Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/site_1.0/component/title.ds
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

func holder = use("component/title_holder")

return (str title) element {
return (str title) html {
return : h1.imported {
@{holder(title)}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/site_1.0/component/title_holder.ds
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
str url = meta.url

return (str title) str {
return std.format("{} - {}", title, url)
return format("{} - {}", title, url)
}
2 changes: 1 addition & 1 deletion examples/site_1.0/src/index.ds
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ meta {


func title = () str {
return std.format("{} - {}", "Hello world!", meta.url)
return format("{} - {}", "Hello world!", meta.url)
}

func h1 = use("component/title")
Expand Down
19 changes: 13 additions & 6 deletions examples/site_1.0/src/test.ds
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use("assets/main.scss")
str line_content = "This is a list item"
str css = use("assets/main.scss")

element line = : li > @{line_content}
html line = : li > @{line_content}

element lines = : ul {
html lines = : ul {
@{line}
@{line}
@{line}
Expand All @@ -22,18 +22,25 @@ map titles = {
str C = "Title C"
}

std.println("Titles map: ")
println("Titles map: ")
list title_keys = keys(titles)
num title_count = len(title_keys)
for num i = 0; i < title_count; i = i + 1 {
println(i)
println(titles[title_keys[i]])
}

for key in titles {
std.println(key)
std.println(titles[key])
println(key)
println(titles[key])
}

list x = [
"hello",
"world"
]
for _, value in x {
std.println(value)
println(value)
}

return : html {
Expand Down
160 changes: 160 additions & 0 deletions src/ast/environment/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use super::{Type, Value};
use crate::ast::statement::Statement;
use crate::context::Context;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

pub type StackValue = Rc<RefCell<(Type, Value)>>;
pub type Scope = Rc<RefCell<HashMap<String, usize>>>;
pub type SharedEnvironment = Rc<RefCell<Environment>>;

pub trait CallableSharedEnvironment {
fn within_scope<T, F>(&mut self, lambda: F) -> T
where
F: FnOnce(&mut SharedEnvironment) -> T;
}

impl CallableSharedEnvironment for SharedEnvironment {
fn within_scope<T, F>(&mut self, lambda: F) -> T
where
F: FnOnce(&mut SharedEnvironment) -> T,
{
let self_clone = self.clone();
let mut env = self_clone.borrow_mut();
env.increase_scope();
let result: T = lambda(self);
env.decrease_scope();
return result;
}
}

#[derive(Clone)]
pub struct Environment {
stack: Vec<StackValue>,
scopes: Vec<Scope>,
current_scope: usize,
}

impl Environment {
pub fn new() -> SharedEnvironment {
Rc::new(RefCell::new(Environment {
stack: Vec::new(),
scopes: vec![Rc::new(RefCell::new(HashMap::new()))],
current_scope: 0,
}))
}

pub fn current_scope(&self) -> Rc<RefCell<HashMap<String, usize>>> {
self.scopes[self.current_scope].clone()
}

pub fn get_stack_value(&self, index: usize) -> Option<Rc<RefCell<(Type, Value)>>> {
self.stack.get(index).map(|s| s.clone()).or_else(|| None)
}

pub fn as_map(&mut self) -> HashMap<String, StackValue> {
let mut map = HashMap::new();
for scope in &self.scopes {
for (name, index) in scope.borrow().iter() {
if let Some(value) = self.get_stack_value(*index) {
map.insert(name.clone(), value);
}
}
}
map
}

pub fn as_vec(&mut self) -> Vec<StackValue> {
self.as_map().into_iter().map(|(_, value)| value).collect()
}

pub fn set(&mut self, type_: Type, name: String, value: Value) {
if !Type::matches(&type_, &value) {
panic!("Type mismatch: expected {}, got {}", type_, value);
}
self.current_scope()
.borrow_mut()
.insert(name, self.stack.len());
}

pub fn exists(&mut self, name: &str) -> Option<usize> {
for (_, scope) in self.scopes.iter().enumerate().rev() {
if let Some(index) = scope.borrow().get(name) {
return Some(*index);
}
}
None
}

pub fn define(&mut self, type_: Type, name: String, value: Value) {
if self.current_scope().borrow().contains_key(&name) {
panic!("Value {} already defined in this scope", name);
}
self.set(type_, name, value);
}

pub fn get(&mut self, name: &str) -> Option<StackValue> {
if let Some(index) = self.exists(name) {
return self.get_stack_value(index);
}
None
}

pub fn assign(&mut self, name: String, value: Value) {
if let Some(stackvalue) = self.get(&name) {
let (type_, mut var) = stackvalue.borrow_mut().clone();
if !Type::matches(&type_, &value) {
panic!("Type mismatch: expected {}, got {}", type_, value);
}

var.set_value(value);
} else {
panic!("Value {} not found in any scope", name);
}
}

pub fn increase_scope(&mut self) {
self.current_scope += 1;
for _ in self.scopes.len()..=self.current_scope {
self.scopes.push(Rc::new(RefCell::new(HashMap::new())));
}
}

pub fn decrease_scope(&mut self) {
if self.current_scope > 0 {
self.current_scope -= 1;
self.collect_garbage();
} else {
panic!("Cannot decrease scope below 0");
}
}

pub fn collect_garbage(&mut self) {
let length = self.scopes.len();

if self.current_scope < length - 1 {
self.scopes.truncate(self.current_scope + 1);
}

let in_use: Vec<usize> = self.current_scope().borrow().values().cloned().collect();
for i in (0..self.stack.len()).rev() {
if !in_use.contains(&i) {
self.stack.remove(i);
}
}
}

pub fn define_builtin_function(
&mut self,
name: String,
func: fn(&mut Context, &Vec<Statement>, &Vec<Value>, &mut Scope) -> Value,
return_type: Type,
) {
self.define(
Type::Function,
name,
Value::Function(func, vec![].into(), return_type, vec![].into()),
);
}
}
1 change: 0 additions & 1 deletion src/ast/environment/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ macro_rules! impl_try_into {
}
}

#[derive(Clone)]
pub enum Value {
String(String),
Number(i64),
Expand Down
1 change: 0 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ pub mod function;
pub mod html;
pub mod node;
pub mod statement;
pub mod strings;
13 changes: 13 additions & 0 deletions src/ast2/builtin/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::{environment::Value, Environment};
use crate::prelude::*;
use ::std::sync::Arc;

mod std;

pub fn populate(env: &mut Environment) {
define_function!(env, "use", std::Use);
define_function!(env, "format", std::Format);
define_function!(env, "println", std::PrintLn);
define_function!(env, "len", std::Len);
define_function!(env, "keys", std::Keys);
}
41 changes: 41 additions & 0 deletions src/ast2/builtin/std/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::{
ast2::{environment::Value, functions::FunctionRunner, Build, Environment, Expression},
context::Context,
};

pub struct Format;

impl FunctionRunner for Format {
#[allow(unused_variables)]
fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec<Expression>) -> Value {
if inputs.len() == 0 {
panic!("format() requires at least one argument");
}

if let Value::String(src) = &inputs[0](ctx, env) {
let mut result = src.clone();

for (i, input) in inputs.iter().enumerate().skip(1) {
let value = input(ctx, env);
match value {
Value::Map(map) => {
let map = map.as_map();
for (key, val) in map {
result = result.replace(
&format!("{{{}}}", key),
val.borrow().1.build(ctx, env).as_str(),
);
}
}
_ => {
result = result.replacen("{}", value.build(ctx, env).as_str(), 1);
}
}
}

return Value::String(result);
} else {
panic!("format() first argument must be a string");
}
}
}
36 changes: 36 additions & 0 deletions src/ast2/builtin/std/keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::{
ast2::{
environment::{Type, Value},
functions::FunctionRunner,
statement::Result,
Environment, Expression,
},
context::{
resource::{Resource, ResourceListExt},
Context,
},
};

pub struct Keys;

impl FunctionRunner for Keys {
#[allow(unused_variables)]
fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec<Expression>) -> Value {
if inputs.len() != 1 {
panic!("keys() expects exactly one argument");
}

if let Value::Map(map) = &inputs[0](ctx, env) {
let mut list = Environment::new();
for (i, key) in map.as_map().keys().enumerate() {
list.set(
i.to_string().as_str(),
Type::String.with_value(Value::String(key.to_string())),
);
}
Value::List(list)
} else {
panic!("keys() expects a list as argument");
}
}
}
26 changes: 26 additions & 0 deletions src/ast2/builtin/std/len.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::{
ast2::{
environment::Value, functions::FunctionRunner, statement::Result, Environment, Expression,
},
context::{
resource::{Resource, ResourceListExt},
Context,
},
};

pub struct Len;

impl FunctionRunner for Len {
#[allow(unused_variables)]
fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec<Expression>) -> Value {
if inputs.len() != 1 {
panic!("len() expects exactly one argument");
}

if let Value::List(list) = &inputs[0](ctx, env) {
Value::Number(list.as_vec().len() as i64)
} else {
panic!("len() expects a list as argument");
}
}
}
7 changes: 7 additions & 0 deletions src/ast2/builtin/std/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::prelude::*;

inherits!(r#use, [Use]);
inherits!(format, [Format]);
inherits!(println, [PrintLn]);
inherits!(len, [Len]);
inherits!(keys, [Keys]);
22 changes: 22 additions & 0 deletions src/ast2/builtin/std/println.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::{
ast2::{environment::Value, functions::FunctionRunner, Build, Environment, Expression},
context::Context,
};

pub struct PrintLn;

impl FunctionRunner for PrintLn {
#[allow(unused_variables)]
fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec<Expression>) -> Value {
if inputs.len() == 0 {
panic!("println() requires at least one argument");
}

for input in inputs {
let value = input(ctx, env);
print!("{}", value.build(ctx, env));
}
println!();
Value::Nil
}
}
Loading