From c387e18d755c43d6ab4aa6cc86b6729e1fcdf04b Mon Sep 17 00:00:00 2001 From: meir Date: Wed, 20 Aug 2025 16:12:00 +0200 Subject: [PATCH 1/6] working on a refactor --- src/ast/environment/environment.rs | 160 ++++++++++++++++++++++++++ src/ast/mod.rs | 1 - src/ast2/builtin/mod.rs | 0 src/ast2/environment/mod.rs | 119 +++++++++++++++++++ src/ast2/environment/scope.rs | 32 ++++++ src/ast2/environment/stack.rs | 59 ++++++++++ src/ast2/environment/type.rs | 32 ++++++ src/ast2/environment/value.rs | 53 +++++++++ src/ast2/expression/mod.rs | 6 + src/ast2/functions/mod.rs | 27 +++++ src/ast2/mod.rs | 16 +++ src/ast2/node/html.rs | 77 +++++++++++++ src/ast2/node/insertion.rs | 8 ++ src/ast2/node/logic.rs | 20 ++++ src/ast2/node/mod.rs | 16 +++ src/ast2/node/text.rs | 5 + src/ast2/statement/mod.rs | 13 +++ src/context/mod.rs | 5 +- src/grammar.lalrpop | 66 +++++------ src/main.rs | 12 +- src/{ast/strings.rs => parser/mod.rs} | 0 src/resolver/file.rs | 3 +- src/resolver/mod.rs | 65 ++++++----- src/resolver/resource.rs | 7 +- 24 files changed, 727 insertions(+), 75 deletions(-) create mode 100644 src/ast/environment/environment.rs create mode 100644 src/ast2/builtin/mod.rs create mode 100644 src/ast2/environment/mod.rs create mode 100644 src/ast2/environment/scope.rs create mode 100644 src/ast2/environment/stack.rs create mode 100644 src/ast2/environment/type.rs create mode 100644 src/ast2/environment/value.rs create mode 100644 src/ast2/expression/mod.rs create mode 100644 src/ast2/functions/mod.rs create mode 100644 src/ast2/mod.rs create mode 100644 src/ast2/node/html.rs create mode 100644 src/ast2/node/insertion.rs create mode 100644 src/ast2/node/logic.rs create mode 100644 src/ast2/node/mod.rs create mode 100644 src/ast2/node/text.rs create mode 100644 src/ast2/statement/mod.rs rename src/{ast/strings.rs => parser/mod.rs} (100%) diff --git a/src/ast/environment/environment.rs b/src/ast/environment/environment.rs new file mode 100644 index 0000000..f32d6b1 --- /dev/null +++ b/src/ast/environment/environment.rs @@ -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>; +pub type Scope = Rc>>; +pub type SharedEnvironment = Rc>; + +pub trait CallableSharedEnvironment { + fn within_scope(&mut self, lambda: F) -> T + where + F: FnOnce(&mut SharedEnvironment) -> T; +} + +impl CallableSharedEnvironment for SharedEnvironment { + fn within_scope(&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, + scopes: Vec, + 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>> { + self.scopes[self.current_scope].clone() + } + + pub fn get_stack_value(&self, index: usize) -> Option>> { + self.stack.get(index).map(|s| s.clone()).or_else(|| None) + } + + pub fn as_map(&mut self) -> HashMap { + 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 { + 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 { + 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 { + 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 = 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, &Vec, &mut Scope) -> Value, + return_type: Type, + ) { + self.define( + Type::Function, + name, + Value::Function(func, vec![].into(), return_type, vec![].into()), + ); + } +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 02d46f6..961c152 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -5,4 +5,3 @@ pub mod function; pub mod html; pub mod node; pub mod statement; -pub mod strings; diff --git a/src/ast2/builtin/mod.rs b/src/ast2/builtin/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/ast2/environment/mod.rs b/src/ast2/environment/mod.rs new file mode 100644 index 0000000..7362e0c --- /dev/null +++ b/src/ast2/environment/mod.rs @@ -0,0 +1,119 @@ +mod scope; +mod stack; +mod r#type; +mod value; + +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +pub use r#type::Type; +use scope::QueryScopes; +use scope::Scope; +use scope::ScopeList; +use scope::UseScope; +use stack::Stack; +use stack::UseStack; +pub use value::CheckTypeValue; +pub use value::TypeValue; +pub use value::Value; + +use crate::context::Context; + +use super::Build; + +#[derive(Clone)] +pub struct Environment { + stack: Stack, + scopes: ScopeList, + current_scope: usize, +} + +impl Environment { + pub fn new() -> Self { + Environment { + stack: Rc::new(RefCell::new(Vec::new())), + scopes: Rc::new(RefCell::new(vec![Rc::new(RefCell::new(HashMap::new()))])), + current_scope: 0, + } + } + + pub fn current_scope(&self) -> Scope { + self.scopes.borrow()[self.current_scope].clone() + } + + pub fn increase_scope(&mut self) { + self.scopes + .borrow_mut() + .push(Rc::new(RefCell::new(HashMap::new()))); + self.current_scope += 1; + } + + pub fn decrease_scope(&mut self) { + if self.current_scope > 0 { + self.scopes.borrow_mut().pop(); + self.current_scope -= 1; + self.clean(); + } else { + panic!("Cannot decrease scope, already at the global scope."); + } + } + + pub fn clean(&mut self) { + let in_use: Vec = self + .scopes + .borrow() + .iter() + .flat_map(|scope| scope.borrow().values().cloned().collect::>()) + .clone() + .collect(); + self.stack.clean(in_use); + } + + pub fn define(&mut self, name: &str, value: TypeValue) { + let index = self.stack.push(value); + self.current_scope().define(name, index); + } + + pub fn set(&mut self, name: &str, value: TypeValue) { + let index = self.scopes.exists(name).expect(&format!( + "Variable '{}' not defined in current scope.", + name + )); + self.stack.set(index, value); + } + + pub fn get(&self, name: &str) -> Option { + if let Some(index) = self.scopes.exists(name) { + return self.stack.get(index); + } + None + } + + pub fn as_map(&self) -> HashMap { + let mut map = HashMap::new(); + for (name, index) in self.current_scope().borrow().iter() { + if let Some(value) = self.stack.get(*index) { + map.insert(name.to_string(), value); + } + } + map + } + + pub fn as_vec(&self) -> Vec { + self.as_map().into_values().collect() + } +} + +impl Build for Environment { + fn build(&self, ctx: &Context, env: &Environment) -> String { + self.as_vec() + .iter() + .map(|typevalue| { + let typevalue = typevalue.borrow(); + typevalue.1.build(ctx, env) + }) + .collect::>() + .join("") + } +} diff --git a/src/ast2/environment/scope.rs b/src/ast2/environment/scope.rs new file mode 100644 index 0000000..548913b --- /dev/null +++ b/src/ast2/environment/scope.rs @@ -0,0 +1,32 @@ +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +pub type Scope = Rc, usize>>>; +pub type ScopeList = Rc>>; + +pub trait UseScope { + fn define(&mut self, name: &str, index: usize); +} + +impl UseScope for Scope { + fn define(&mut self, name: &str, index: usize) { + if self.borrow().contains_key(name) { + panic!("Scope: Name '{}' already defined in this scope.", name); + } + self.borrow_mut().insert(name.into(), index); + } +} + +pub trait QueryScopes { + fn exists(&self, name: &str) -> Option; +} + +impl QueryScopes for ScopeList { + fn exists(&self, name: &str) -> Option { + for scope in self.borrow().iter().rev() { + if let Some(index) = scope.borrow().get(name) { + return Some(*index); + } + } + None + } +} diff --git a/src/ast2/environment/stack.rs b/src/ast2/environment/stack.rs new file mode 100644 index 0000000..dc79b3f --- /dev/null +++ b/src/ast2/environment/stack.rs @@ -0,0 +1,59 @@ +use std::{cell::RefCell, rc::Rc}; + +use super::CheckTypeValue; +use super::TypeValue; + +pub type Stack = Rc>>; + +pub trait UseStack { + fn set(&mut self, index: usize, value: TypeValue); + fn get(&self, index: usize) -> Option; + fn push(&mut self, value: TypeValue) -> usize; + fn remove(&mut self, index: usize) -> Option; + fn clean(&mut self, in_use: Vec); +} + +impl UseStack for Stack { + fn set(&mut self, index: usize, value: TypeValue) { + value.check(); + if index < self.borrow().len() { + self.borrow_mut()[index] = value; + } else { + panic!( + "Stack: Index out of bounds {} > {}", + index, + self.borrow().len() + ); + } + } + + fn get(&self, index: usize) -> Option { + if index < self.borrow().len() { + Some(self.borrow()[index].clone()) + } else { + None + } + } + + fn push(&mut self, value: TypeValue) -> usize { + value.check(); + self.borrow_mut().push(value); + self.borrow().len() - 1 + } + + fn remove(&mut self, index: usize) -> Option { + if index < self.borrow().len() { + Some(self.borrow_mut().remove(index)) + } else { + None + } + } + + fn clean(&mut self, in_use: Vec) { + for i in (0..self.borrow().len()).rev() { + if !in_use.contains(&i) { + self.borrow_mut().remove(i); + } + } + } +} diff --git a/src/ast2/environment/type.rs b/src/ast2/environment/type.rs new file mode 100644 index 0000000..19d81d3 --- /dev/null +++ b/src/ast2/environment/type.rs @@ -0,0 +1,32 @@ +use super::Value; + +pub enum Type { + String, + Number, + Float, + Boolean, + Html, + Function, + Map, + List, + Nil, + Any, // not usable in the language, but needed to return any type using "use" +} + +impl Type { + pub fn matches(&self, value: &Value) -> bool { + match (self, value) { + (Type::String, Value::String(_)) => true, + (Type::Number, Value::Number(_)) => true, + (Type::Float, Value::Float(_)) => true, + (Type::Boolean, Value::Boolean(_)) => true, + (Type::Html, Value::Html(_)) => true, + (Type::Function, Value::Function(_)) => true, + (Type::Map, Value::Map(_)) => true, + (Type::List, Value::List(_)) => true, + (_, Value::Nil) => true, + (Type::Any, _) => true, + _ => false, + } + } +} diff --git a/src/ast2/environment/value.rs b/src/ast2/environment/value.rs new file mode 100644 index 0000000..d37c80c --- /dev/null +++ b/src/ast2/environment/value.rs @@ -0,0 +1,53 @@ +use std::{cell::RefCell, rc::Rc}; + +use crate::{ + ast2::{functions::FunctionValue, Build, Node}, + context::Context, +}; + +use super::{Environment, Type}; + +pub type TypeValue = Rc>; + +pub trait CheckTypeValue { + fn check(&self); +} + +impl CheckTypeValue for TypeValue { + fn check(&self) { + let typevalue = self.borrow(); + let t = &typevalue.0; + let value = &typevalue.1; + if !t.matches(value) { + panic!("Type mismatch: expected {}, found {}", "a", "b"); + } + } +} + +pub enum Value { + String(String), + Number(i64), + Float(f64), + Boolean(bool), + Html(Node), + Function(FunctionValue), + Map(Environment), + List(Environment), + Nil, +} + +impl Build for Value { + fn build(&self, ctx: &Context, env: &Environment) -> String { + match self { + Value::String(s) => s.clone(), + Value::Number(n) => n.to_string(), + Value::Float(n) => n.to_string(), + Value::Boolean(b) => b.to_string(), + Value::Html(node) => node(ctx, env), + Value::Function(..) => "function".to_string(), + Value::Map(sub_env) => sub_env.build(ctx, env), + Value::List(sub_env) => sub_env.build(ctx, env), + Value::Nil => "nil".to_string(), + } + } +} diff --git a/src/ast2/expression/mod.rs b/src/ast2/expression/mod.rs new file mode 100644 index 0000000..28ea738 --- /dev/null +++ b/src/ast2/expression/mod.rs @@ -0,0 +1,6 @@ +use crate::context::Context; + +use super::environment::Value; +use super::Environment; + +pub type Expression = Box Value + 'static>; diff --git a/src/ast2/functions/mod.rs b/src/ast2/functions/mod.rs new file mode 100644 index 0000000..e36d453 --- /dev/null +++ b/src/ast2/functions/mod.rs @@ -0,0 +1,27 @@ +use std::rc::Rc; + +use crate::context::Context; + +use super::{ + environment::{Type, Value}, + Environment, Expression, Statement, +}; + +pub type FunctionParameter = Rc)>>; +pub type FunctionBody = Rc>; +pub type FunctionValue = ( + Box, + Rc)>>, + Type, + FunctionBody, +); + +trait FunctionRunner { + fn run( + &self, + ctx: &Context, + env: &Environment, + parameters: Vec, + inputs: Vec, + ) -> Value; +} diff --git a/src/ast2/mod.rs b/src/ast2/mod.rs new file mode 100644 index 0000000..5a5017b --- /dev/null +++ b/src/ast2/mod.rs @@ -0,0 +1,16 @@ +pub mod environment; +pub mod expression; +pub mod functions; +pub mod node; +pub mod statement; + +pub use environment::Environment; +pub use expression::Expression; +pub use node::Node; +pub use statement::Statement; + +use crate::context::Context; + +pub trait Build { + fn build(&self, ctx: &Context, env: &Environment) -> String; +} diff --git a/src/ast2/node/html.rs b/src/ast2/node/html.rs new file mode 100644 index 0000000..4deb52a --- /dev/null +++ b/src/ast2/node/html.rs @@ -0,0 +1,77 @@ +use super::Node; +use crate::ast2::Environment; +use crate::context::Context; +use std::collections::HashMap; + +pub fn html(element: Html) -> Node { + Box::new(move |ctx, env| element.build(ctx, env)) +} + +pub struct Html { + tag: Box, + attributes: HashMap>, + body: Vec, +} + +impl Html { + pub fn new(tag: Box) -> Self { + Html { + tag, + attributes: HashMap::new(), + body: vec![], + } + } + + pub fn with_attributes(mut self, attributes: Vec<(String, Vec)>) -> Self { + for (key, value) in attributes { + match &*key { + "id" => { + self.attributes.insert(key, value); + } + _ => { + self.attributes + .entry(key) + .or_insert_with(Vec::new) + .extend(value); + } + } + } + self + } + + pub fn with_children(mut self, children: Vec) -> Self { + self.body.extend(children); + self + } + + fn build(&self, ctx: &Context, env: &Environment) -> String { + let attributes = self + .attributes + .iter() + .map(|(k, v)| { + let value = v + .iter() + .map(|node| node(ctx, env)) + .collect::>() + .join(" "); + format!("{}=\"{}\"", k, value) + }) + .collect::>() + .join(" "); + + let body = self + .body + .iter() + .map(|node| node(ctx, env)) + .collect::>() + .join(""); + let tag = &*self.tag; + + match (attributes.as_str(), body.as_str()) { + ("", "") => format!("<{}/>", tag), + (_, "") => format!("<{} {} />", tag, attributes), + ("", _) => format!("<{}>{}", tag, body, tag), + _ => format!("<{} {}>{}", tag, attributes, body, tag), + } + } +} diff --git a/src/ast2/node/insertion.rs b/src/ast2/node/insertion.rs new file mode 100644 index 0000000..cd21ccc --- /dev/null +++ b/src/ast2/node/insertion.rs @@ -0,0 +1,8 @@ +use crate::ast2::Build; +use crate::ast2::Expression; + +use super::Node; + +pub fn insertion(expr: Expression) -> Node { + Box::new(move |ctx, env| expr(ctx, env).build(ctx, env)) +} diff --git a/src/ast2/node/logic.rs b/src/ast2/node/logic.rs new file mode 100644 index 0000000..bf93b98 --- /dev/null +++ b/src/ast2/node/logic.rs @@ -0,0 +1,20 @@ +use crate::ast2::statement::Result; +use crate::ast2::{Build, Expression, Statement}; + +use super::Node; + +pub fn logic_statement(logic: Statement) -> Node { + Box::new(move |ctx, env| match logic(ctx, env) { + Result::Collect(value) => value + .iter() + .map(|v| v.build(ctx, env)) + .collect::>() + .join(""), + Result::Return(value) => value.build(ctx, env), + Result::Break | Result::Continue | Result::NOP => String::new(), + }) +} + +pub fn logic_expression(logic: Expression) -> Node { + Box::new(move |ctx, env| logic(ctx, env).build(ctx, env)) +} diff --git a/src/ast2/node/mod.rs b/src/ast2/node/mod.rs new file mode 100644 index 0000000..f4c8155 --- /dev/null +++ b/src/ast2/node/mod.rs @@ -0,0 +1,16 @@ +mod html; +mod insertion; +mod logic; +mod text; + +pub use html::html; +pub use html::Html; +pub use insertion::insertion; +pub use logic::{logic_expression, logic_statement}; +pub use text::text; + +use crate::context::Context; + +use super::Environment; + +pub type Node = Box String + 'static>; diff --git a/src/ast2/node/text.rs b/src/ast2/node/text.rs new file mode 100644 index 0000000..a43a69d --- /dev/null +++ b/src/ast2/node/text.rs @@ -0,0 +1,5 @@ +use super::Node; + +pub fn text(text: String) -> Node { + Box::new(move |_ctx, _env| text.to_string()) +} diff --git a/src/ast2/statement/mod.rs b/src/ast2/statement/mod.rs new file mode 100644 index 0000000..1951b66 --- /dev/null +++ b/src/ast2/statement/mod.rs @@ -0,0 +1,13 @@ +use crate::context::Context; + +use super::{environment::Value, Environment}; + +pub enum Result { + Collect(Vec), + Return(Value), + Break, + Continue, + NOP, +} + +pub type Statement = Box Result + 'static>; diff --git a/src/context/mod.rs b/src/context/mod.rs index 8dc092a..bebe051 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -4,10 +4,11 @@ use crate::{grammar::DaisyParser, resolver::resource::Resource}; use log::warn; use serde::Deserialize; +#[derive(Clone)] pub struct Context { - pub parser: DaisyParser, + pub parser: Rc>, pub resources: Vec>>, - pub config: Config, + pub config: Rc>, } #[derive(Deserialize)] diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index bbf2b5f..e3d0b46 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -1,10 +1,10 @@ use std::str::FromStr; -use crate::ast::environment::{Type, Value}; -use crate::ast::{expression, expression::Expression}; -use crate::ast::{statement, statement::Statement}; -use crate::ast::{node, node::Node}; -use crate::ast::function::default_function; -use crate::ast::strings::{parse_string, parse_multiline_string}; +use crate::ast2::environment::{Type, Value}; +use crate::ast2::{expression, expression::Expression}; +use crate::ast2::{statement, statement::Statement}; +use crate::ast2::{node, node::Node, node::Html}; +use crate::ast2::functions::FunctionValue; +use crate::parser::{parse_string, parse_multiline_string}; grammar; @@ -38,11 +38,11 @@ KeyValueNil = "nil"; KeyTypeString = "str"; KeyTypeNumber = "num"; KeyTypeFloat = "float"; -KeyTypeBool = "bool"; -KeyTypeElement = "element"; +KeyTypeBoolean = "bool"; +KeyTypeHtml = "html"; KeyTypeFunction = "func"; KeyTypeMap = "map"; -KeyTypeArray = "list"; +KeyTypeList = "list"; Keyword = { KeyIf, @@ -62,11 +62,11 @@ Keyword = { KeyTypeString, KeyTypeNumber, KeyTypeFloat, - KeyTypeBool, - KeyTypeElement, + KeyTypeBoolean, + KeyTypeHtml, KeyTypeFunction, KeyTypeMap, - KeyTypeArray, + KeyTypeList, }; KeyVariableName = { @@ -79,21 +79,21 @@ KeyVariableName = { TypeString: Type = KeyTypeString => Type::String; TypeNumber: Type = KeyTypeNumber => Type::Number; TypeFloat: Type = KeyTypeFloat => Type::Float; -TypeBool: Type = KeyTypeBool => Type::Bool; -TypeElement: Type = KeyTypeElement => Type::Element; +TypeBoolean: Type = KeyTypeBoolean => Type::Boolean; +TypeHtml: Type = KeyTypeHtml => Type::Html; TypeFunction: Type = KeyTypeFunction => Type::Function; TypeMap: Type = KeyTypeMap => Type::Map; -TypeArray: Type = KeyTypeArray => Type::Array; +TypeList: Type = KeyTypeList => Type::List; Type: Type = { TypeString, TypeNumber, TypeFloat, - TypeBool, - TypeElement, + TypeBoolean, + TypeHtml, TypeFunction, TypeMap, - TypeArray, + TypeList, }; // Operations @@ -153,7 +153,7 @@ RawValueString: String = { }; RawValueNumber: i64 = => i64::from_str(s).unwrap(); RawValueFloat: f64 = => f64::from_str(s).unwrap(); -RawValueBool: bool = { +RawValueBoolean: bool = { KeyValueTrue => true, KeyValueFalse => false, }; @@ -161,19 +161,19 @@ RawValueBool: bool = { ValueString: Value = => Value::String(s); ValueNumber: Value = => Value::Number(n); ValueFloat: Value = => Value::Float(f); -ValueBool: Value = => Value::Bool(b); +ValueBoolean: Value = => Value::Boolean(b); ValueNil: Value = KeyValueNil => Value::Nil; -ValueFunction: Value = => Value::Function(default_function, function.0.into(), function.1, function.2.into()); -ValueElement: Value = ":" => Value::Element(element.into()); +ValueFunction: Value = => Value::Function((default_function, function.0.into(), function.1, function.2.into())); +ValueHtml: Value = ":" => Value::Html(html); Value: Value = { ValueString, ValueNumber, ValueFloat, - ValueBool, + ValueBoolean, ValueNil, ValueFunction, - ValueElement, + ValueHtml, }; @@ -212,18 +212,18 @@ ExpressionTerm: Expression = { "(" ")", ExpressionValue, ExpressionMap, - ExpressionArray, + ExpressionList, ExpressionIdentifier, ExpressionScript, }; ExpressionValue: Expression = => expression::value(value); ExpressionMap: Expression = "{" "}" => expression::map(definitions); -ExpressionArray: Expression = "[" ",")*> "]" => { +ExpressionList: Expression = "[" ",")*> "]" => { if let Some(last) = last { entries.push(last); } - expression::array(entries) + expression::list(entries) }; ExpressionScopeEntry: Expression = "[" "]" => expression::scope_entry(scope.into(), entry.into()); ExpressionIdentifier: Expression = )*> => expression::identifier({ @@ -279,10 +279,10 @@ Statement: Statement = { // html -NodeElement: Node = { - "{" "}" => node::element(identifier.into(), attributes, children), - ">" => node::element(identifier.into(), attributes, vec![child]), - ";" => node::element(identifier.into(), attributes, vec![]), +NodeHtml: Node = { + "{" "}" => node::html(Html::new(identifier.into()).with_attributes(attributes).with_children(children)), + ">" => node::html(Html::new(identifier.into()).with_attributes(attributes).with_children(vec![child])), + ";" => node::html(Html::new(identifier.into()).with_attributes(attributes)), }; NodeAttribute: (String, Vec) = { @@ -295,14 +295,14 @@ NodeAttribute: (String, Vec) = { NodeText: Node = => node::text(string); -NodeInsert: Node = "@" "{" "}" => node::insert(expression.into()); +NodeInsert: Node = "@" "(" "}" => node::insertion(expression.into()); Node: Node = { => node::logic_statement(for_loop), => node::logic_statement(iter_loop), => node::logic_statement(if_statement), - NodeElement, + NodeHtml, NodeInsert, NodeText, } diff --git a/src/main.rs b/src/main.rs index 09a5933..10ee5cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,16 +2,20 @@ use env_logger::Env; use lalrpop_util::lalrpop_mod; mod ast; -mod cli; +mod ast2; +use ast2::environment::Environment; +// mod cli; mod context; -mod resolver; +mod parser; +// mod resolver; lalrpop_mod!(grammar); fn main() { let env = Env::default().filter_or("DAISY_LOG", "trace"); env_logger::init_from_env(env); - let mut ctx = context::Context::load_config(); + let ctx = context::Context::load_config(); + _ = ctx; - cli::run(&mut ctx); + // cli::run(&mut ctx); } diff --git a/src/ast/strings.rs b/src/parser/mod.rs similarity index 100% rename from src/ast/strings.rs rename to src/parser/mod.rs diff --git a/src/resolver/file.rs b/src/resolver/file.rs index b23326c..06218c7 100644 --- a/src/resolver/file.rs +++ b/src/resolver/file.rs @@ -21,12 +21,13 @@ pub struct File { } impl File { - pub fn load_absolute>(ctx: &mut Context, src: P) -> File { + pub fn load_absolute>(ctx: &Context, src: P) -> File { let content = fs::read_to_string(&src).unwrap_or_else(|_| { panic!("Failed to read file: {:?}", src.as_ref()); }); let ast = ctx .parser + .borrow() .parse(content.as_str()) .unwrap_or_else(|err| Self::error_message(src.as_ref(), err, &content)); diff --git a/src/resolver/mod.rs b/src/resolver/mod.rs index 8523e3b..f2f7d4c 100644 --- a/src/resolver/mod.rs +++ b/src/resolver/mod.rs @@ -13,39 +13,41 @@ use crate::context::Context; pub mod file; pub mod resource; -pub fn load_dir(ctx: &mut Context) { - WalkDir::new(format!( - "{}/{}", - ctx.config.paths.workdir, ctx.config.paths.pages - )) - .into_iter() - .filter_map(|entry| entry.ok()) - .filter(|entry| entry.file_type().is_file() && entry.path().extension() == Some("ds".as_ref())) - .for_each(|entry| { - let path = entry.path(); - let file = get_file(ctx, path.to_str().unwrap().to_string()).unwrap_or_else(|err| { - panic!("Failed to load file {}: {}", path.display(), err); - }); +pub fn load_dir(ctx: Context) { + let config = ctx.config.borrow(); + WalkDir::new(format!("{}/{}", config.paths.workdir, config.paths.pages)) + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.file_type().is_file() && entry.path().extension() == Some("ds".as_ref()) + }) + .for_each(|entry| { + let path = entry.path(); + let file = + get_file(ctx.clone(), path.to_str().unwrap().to_string()).unwrap_or_else(|err| { + panic!("Failed to load file {}: {}", path.display(), err); + }); - let mut resource = file.borrow_mut(); + let mut resource = file.borrow_mut(); - if let Resource::File(file) = &mut *resource { - file.is_page = true; - } else { - panic!( - "Expected a File resource, got {}", - entry.path().to_str().unwrap() - ); - } - }); + if let Resource::File(file) = &mut *resource { + file.is_page = true; + } else { + panic!( + "Expected a File resource, got {}", + entry.path().to_str().unwrap() + ); + } + }); } pub fn get_all(ctx: &mut Context) -> Vec>> { ctx.resources.iter().cloned().collect() } -pub fn get_file(ctx: &mut Context, src: String) -> Result>, String> { - let src = Path::new(ctx.config.paths.workdir.as_str()).join(src); +pub fn get_file(ctx: &Context, src: String) -> Result>, String> { + let config = ctx.config.borrow(); + let src = Path::new(config.paths.workdir.as_str()).join(src); if let Some(rs) = ctx.resources.iter().find(|rs| match &*rs.borrow() { Resource::File(file) => file.src == src, Resource::SCSS(src_file, _, _) => src_file == src.to_str().unwrap(), @@ -55,10 +57,11 @@ pub fn get_file(ctx: &mut Context, src: String) -> Result>, } else { match src.extension().and_then(|ext| ext.to_str()) { Some("ds") => { - let file = file::File::load_absolute(ctx, src.to_str().unwrap()); + let file = file::File::load_absolute(&ctx, src.to_str().unwrap()); let rc = Rc::new(RefCell::new(Resource::File(file))); - ctx.resources.push(rc.clone()); + let mut resources = ctx.resources.borrow_mut(); + resources.push(rc.clone()); Ok(rc) } Some("scss") => { @@ -73,7 +76,7 @@ pub fn get_file(ctx: &mut Context, src: String) -> Result>, content.hash(&mut hasher); let hash = hasher.finish(); let path = - Resource::get_output_path(ctx, format!("{}-{}.css", name, hash).as_str()) + Resource::get_output_path(&ctx, format!("{}-{}.css", name, hash).as_str()) .unwrap(); let rc = Rc::new(RefCell::new(Resource::SCSS( @@ -87,13 +90,13 @@ pub fn get_file(ctx: &mut Context, src: String) -> Result>, _ => { let with_ext = src.with_extension("ds"); if with_ext.exists() { - get_file(ctx, with_ext.to_str().unwrap().to_string()) + get_file(&ctx, with_ext.to_str().unwrap().to_string()) } else { let relative_path = - Resource::get_relative_path_from_root(ctx, src.to_str().unwrap()) + Resource::get_relative_path_from_root(&ctx, src.to_str().unwrap()) .map_err(|err| format!("Failed to get relative path: {}", err))?; - let mut output = Resource::get_output_path(ctx, &relative_path) + let mut output = Resource::get_output_path(&ctx, &relative_path) .map_err(|err| format!("Failed to get output path: {}", err))?; if src.extension().is_none() { diff --git a/src/resolver/resource.rs b/src/resolver/resource.rs index 43447e5..60cd0cb 100644 --- a/src/resolver/resource.rs +++ b/src/resolver/resource.rs @@ -14,7 +14,7 @@ pub enum Resource { } impl Resource { - pub fn get_output_path(ctx: &mut Context, src: &str) -> Result { + pub fn get_output_path(ctx: &Context, src: &str) -> Result { let mut path = Path::new(src); path = path.strip_prefix(ctx.get_page_path()).unwrap_or(path); @@ -52,8 +52,9 @@ impl Resource { } } - pub fn get_relative_path_from_root(ctx: &mut Context, src: &str) -> Result { - if let Some(relative_path) = src.strip_prefix(&ctx.config.paths.workdir) { + pub fn get_relative_path_from_root(ctx: &Context, src: &str) -> Result { + let config = ctx.config.borrow(); + if let Some(relative_path) = src.strip_prefix(&config.paths.workdir) { return Ok(format!("/{}", relative_path)); } else { panic!("Failed to strip workdir from path: {}", src); From c4d5fff00fb310baf1d2f32843d47558bac1fdbb Mon Sep 17 00:00:00 2001 From: meir Date: Wed, 27 Aug 2025 11:40:13 +0200 Subject: [PATCH 2/6] wip --- src/ast/environment/value.rs | 1 - src/ast2/environment/mod.rs | 41 ++++--- src/ast2/environment/type.rs | 50 ++++++-- src/ast2/environment/value.rs | 48 +++++++- src/ast2/expression/calculus/add.rs | 19 ++++ src/ast2/expression/calculus/divide.rs | 29 +++++ src/ast2/expression/calculus/mod.rs | 6 + src/ast2/expression/calculus/multiply.rs | 19 ++++ src/ast2/expression/calculus/subtract.rs | 19 ++++ src/ast2/expression/comparisons/and.rs | 19 ++++ src/ast2/expression/comparisons/equal.rs | 11 ++ src/ast2/expression/comparisons/greater.rs | 16 +++ .../expression/comparisons/greater_equal.rs | 16 +++ src/ast2/expression/comparisons/lesser.rs | 16 +++ .../expression/comparisons/lesser_equal.rs | 16 +++ src/ast2/expression/comparisons/mod.rs | 10 ++ src/ast2/expression/comparisons/notequal.rs | 11 ++ src/ast2/expression/comparisons/or.rs | 19 ++++ src/ast2/expression/mod.rs | 19 +++- src/ast2/functions/mod.rs | 65 +++++++++-- src/ast2/node/html.rs | 7 +- src/ast2/node/mod.rs | 18 +-- src/ast2/statement/assign.rs | 10 ++ src/ast2/statement/call.rs | 17 +++ src/ast2/statement/define.rs | 17 +++ src/ast2/statement/mod.rs | 6 +- src/context/config/assets.rs | 15 +++ src/context/config/mod.rs | 45 ++++++++ src/context/config/paths.rs | 19 ++++ src/context/mod.rs | 107 ++---------------- src/context/parser.rs | 0 src/context/resolver.rs | 0 src/context/resources.rs | 0 src/grammar.lalrpop | 27 +++-- src/lib.rs | 17 +++ src/main.rs | 6 +- src/prelude.rs | 2 + 37 files changed, 594 insertions(+), 169 deletions(-) create mode 100644 src/ast2/expression/calculus/add.rs create mode 100644 src/ast2/expression/calculus/divide.rs create mode 100644 src/ast2/expression/calculus/mod.rs create mode 100644 src/ast2/expression/calculus/multiply.rs create mode 100644 src/ast2/expression/calculus/subtract.rs create mode 100644 src/ast2/expression/comparisons/and.rs create mode 100644 src/ast2/expression/comparisons/equal.rs create mode 100644 src/ast2/expression/comparisons/greater.rs create mode 100644 src/ast2/expression/comparisons/greater_equal.rs create mode 100644 src/ast2/expression/comparisons/lesser.rs create mode 100644 src/ast2/expression/comparisons/lesser_equal.rs create mode 100644 src/ast2/expression/comparisons/mod.rs create mode 100644 src/ast2/expression/comparisons/notequal.rs create mode 100644 src/ast2/expression/comparisons/or.rs create mode 100644 src/ast2/statement/assign.rs create mode 100644 src/ast2/statement/call.rs create mode 100644 src/ast2/statement/define.rs create mode 100644 src/context/config/assets.rs create mode 100644 src/context/config/mod.rs create mode 100644 src/context/config/paths.rs create mode 100644 src/context/parser.rs create mode 100644 src/context/resolver.rs create mode 100644 src/context/resources.rs create mode 100644 src/lib.rs create mode 100644 src/prelude.rs diff --git a/src/ast/environment/value.rs b/src/ast/environment/value.rs index feed5b9..cd4eede 100644 --- a/src/ast/environment/value.rs +++ b/src/ast/environment/value.rs @@ -19,7 +19,6 @@ macro_rules! impl_try_into { } } -#[derive(Clone)] pub enum Value { String(String), Number(i64), diff --git a/src/ast2/environment/mod.rs b/src/ast2/environment/mod.rs index 7362e0c..b75c98f 100644 --- a/src/ast2/environment/mod.rs +++ b/src/ast2/environment/mod.rs @@ -1,26 +1,22 @@ -mod scope; -mod stack; -mod r#type; -mod value; - +use super::Build; +use crate::context::Context; +use crate::prelude::*; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -pub use r#type::Type; +inherits!(r#type, [Type]); +inherits!(value, [Value, TypeValue, CheckTypeValue]); + +mod scope; use scope::QueryScopes; use scope::Scope; use scope::ScopeList; use scope::UseScope; + +mod stack; use stack::Stack; use stack::UseStack; -pub use value::CheckTypeValue; -pub use value::TypeValue; -pub use value::Value; - -use crate::context::Context; - -use super::Build; #[derive(Clone)] pub struct Environment { @@ -59,6 +55,16 @@ impl Environment { } } + pub fn subscope(&mut self, lambda: F) -> T + where + F: FnOnce() -> T, + { + self.increase_scope(); + let result: T = lambda(); + self.decrease_scope(); + result + } + pub fn clean(&mut self) { let in_use: Vec = self .scopes @@ -76,6 +82,15 @@ impl Environment { } pub fn set(&mut self, name: &str, value: TypeValue) { + let index = self.scopes.exists(name); + if let Some(index) = index { + self.stack.set(index, value); + } else { + self.define(name, value); + } + } + + pub fn assign(&mut self, name: &str, value: TypeValue) { let index = self.scopes.exists(name).expect(&format!( "Variable '{}' not defined in current scope.", name diff --git a/src/ast2/environment/type.rs b/src/ast2/environment/type.rs index 19d81d3..ceb2a93 100644 --- a/src/ast2/environment/type.rs +++ b/src/ast2/environment/type.rs @@ -1,5 +1,11 @@ -use super::Value; +use super::{TypeValue, Value}; +use std::{ + cell::RefCell, + fmt::{Display, Formatter, Result}, + rc::Rc, +}; +#[derive(Clone)] pub enum Type { String, Number, @@ -14,19 +20,41 @@ pub enum Type { } impl Type { + pub fn with_value(&self, value: Value) -> TypeValue { + Rc::new(RefCell::new((self.clone(), value))) + } + pub fn matches(&self, value: &Value) -> bool { - match (self, value) { - (Type::String, Value::String(_)) => true, - (Type::Number, Value::Number(_)) => true, - (Type::Float, Value::Float(_)) => true, - (Type::Boolean, Value::Boolean(_)) => true, - (Type::Html, Value::Html(_)) => true, - (Type::Function, Value::Function(_)) => true, - (Type::Map, Value::Map(_)) => true, - (Type::List, Value::List(_)) => true, - (_, Value::Nil) => true, + let t = value.type_of(); + match (self, t) { + (Type::String, Type::String) => true, + (Type::Number, Type::Number) => true, + (Type::Float, Type::Float) => true, + (Type::Boolean, Type::Boolean) => true, + (Type::Html, Type::Html) => true, + (Type::Function, Type::Function) => true, + (Type::Map, Type::Map) => true, + (Type::List, Type::List) => true, + (_, Type::Nil) => true, (Type::Any, _) => true, _ => false, } } } + +impl Display for Type { + fn fmt(&self, f: &mut Formatter) -> Result { + match self { + Type::String => write!(f, "String"), + Type::Number => write!(f, "Number"), + Type::Float => write!(f, "Float"), + Type::Boolean => write!(f, "Boolean"), + Type::Html => write!(f, "Html"), + Type::Function => write!(f, "Function"), + Type::Map => write!(f, "Map"), + Type::List => write!(f, "List"), + Type::Nil => write!(f, "Nil"), + Type::Any => write!(f, "Any"), + } + } +} diff --git a/src/ast2/environment/value.rs b/src/ast2/environment/value.rs index d37c80c..e91c0f8 100644 --- a/src/ast2/environment/value.rs +++ b/src/ast2/environment/value.rs @@ -1,7 +1,9 @@ +use std::fmt::{Display, Formatter, Result}; use std::{cell::RefCell, rc::Rc}; +use crate::ast2::functions::FunctionRunner; use crate::{ - ast2::{functions::FunctionValue, Build, Node}, + ast2::{Build, Node}, context::Context, }; @@ -30,12 +32,33 @@ pub enum Value { Float(f64), Boolean(bool), Html(Node), - Function(FunctionValue), + Function(Box), Map(Environment), List(Environment), Nil, } +impl Value { + pub fn type_of(&self) -> Type { + match self { + Value::String(_) => Type::String, + Value::Number(_) => Type::Number, + Value::Float(_) => Type::Float, + Value::Boolean(_) => Type::Boolean, + Value::Html(_) => Type::Html, + Value::Function(_) => Type::Function, + Value::Map(_) => Type::Map, + Value::List(_) => Type::List, + Value::Nil => Type::Nil, + } + } + + pub fn as_typevalue(self) -> TypeValue { + let t = self.type_of(); + Rc::new(RefCell::new((t, self))) + } +} + impl Build for Value { fn build(&self, ctx: &Context, env: &Environment) -> String { match self { @@ -51,3 +74,24 @@ impl Build for Value { } } } + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Value::String(l), Value::String(r)) => l == r, + (Value::Number(l), Value::Number(r)) => l == r, + (Value::Float(l), Value::Float(r)) => l == r, + (Value::Boolean(l), Value::Boolean(r)) => l == r, + (Value::Nil, Value::Nil) => true, + + _ => std::mem::discriminant(self) == std::mem::discriminant(other), + } + } +} + +impl Display for Value { + fn fmt(&self, formatter: &mut Formatter) -> Result { + let t = self.type_of(); + write!(formatter, "{}", t) + } +} diff --git a/src/ast2/expression/calculus/add.rs b/src/ast2/expression/calculus/add.rs new file mode 100644 index 0000000..7d9daf9 --- /dev/null +++ b/src/ast2/expression/calculus/add.rs @@ -0,0 +1,19 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn add(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (&left_value, &right_value) { + (Value::Number(l), Value::Number(r)) => Value::Number(l + r), + (Value::Float(l), Value::Float(r)) => Value::Float(l + r), + (Value::String(l), Value::String(r)) => Value::String(format!("{}{}", l, r)), + _ => panic!( + "Type mismatch in addition: {} + {}", + left_value, right_value + ), + } + }) +} diff --git a/src/ast2/expression/calculus/divide.rs b/src/ast2/expression/calculus/divide.rs new file mode 100644 index 0000000..9768798 --- /dev/null +++ b/src/ast2/expression/calculus/divide.rs @@ -0,0 +1,29 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn divide(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (&left_value, &right_value) { + (Value::Number(l), Value::Number(r)) => { + if *r == 0 { + panic!("Division by zero"); + } + Value::Number(l / r) + } + (Value::Float(l), Value::Float(r)) => { + if *r == 0.0 { + panic!("Division by zero"); + } + Value::Float(l / r) + } + (Value::String(l), Value::String(r)) => Value::String(format!("{}{}", l, r)), + _ => panic!( + "Type mismatch in addition: {} + {}", + left_value, right_value + ), + } + }) +} diff --git a/src/ast2/expression/calculus/mod.rs b/src/ast2/expression/calculus/mod.rs new file mode 100644 index 0000000..5357268 --- /dev/null +++ b/src/ast2/expression/calculus/mod.rs @@ -0,0 +1,6 @@ +use crate::prelude::*; + +inherit!(add); +inherit!(subtract); +inherit!(multiply); +inherit!(divide); diff --git a/src/ast2/expression/calculus/multiply.rs b/src/ast2/expression/calculus/multiply.rs new file mode 100644 index 0000000..5a4cc96 --- /dev/null +++ b/src/ast2/expression/calculus/multiply.rs @@ -0,0 +1,19 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn multiply(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (&left_value, &right_value) { + (Value::Number(l), Value::Number(r)) => Value::Number(l * r), + (Value::Float(l), Value::Float(r)) => Value::Float(l * r), + (Value::String(l), Value::String(r)) => Value::String(format!("{}{}", l, r)), + _ => panic!( + "Type mismatch in addition: {} + {}", + left_value, right_value + ), + } + }) +} diff --git a/src/ast2/expression/calculus/subtract.rs b/src/ast2/expression/calculus/subtract.rs new file mode 100644 index 0000000..43b1fac --- /dev/null +++ b/src/ast2/expression/calculus/subtract.rs @@ -0,0 +1,19 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn subtract(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (&left_value, &right_value) { + (Value::Number(l), Value::Number(r)) => Value::Number(l - r), + (Value::Float(l), Value::Float(r)) => Value::Float(l - r), + (Value::String(l), Value::String(r)) => Value::String(format!("{}{}", l, r)), + _ => panic!( + "Type mismatch in addition: {} + {}", + left_value, right_value + ), + } + }) +} diff --git a/src/ast2/expression/comparisons/and.rs b/src/ast2/expression/comparisons/and.rs new file mode 100644 index 0000000..bda10c2 --- /dev/null +++ b/src/ast2/expression/comparisons/and.rs @@ -0,0 +1,19 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn and(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (&left_value, &right_value) { + (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(*l && *r), + _ => { + panic!( + "Type mismatch in logical AND: {} && {}", + left_value, right_value + ); + } + } + }) +} diff --git a/src/ast2/expression/comparisons/equal.rs b/src/ast2/expression/comparisons/equal.rs new file mode 100644 index 0000000..1c70c73 --- /dev/null +++ b/src/ast2/expression/comparisons/equal.rs @@ -0,0 +1,11 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn equal(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + Value::Boolean(left_value == right_value) + }) +} diff --git a/src/ast2/expression/comparisons/greater.rs b/src/ast2/expression/comparisons/greater.rs new file mode 100644 index 0000000..a5492a2 --- /dev/null +++ b/src/ast2/expression/comparisons/greater.rs @@ -0,0 +1,16 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn greater(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (left_value, right_value) { + (Value::Number(l), Value::Number(r)) => Value::Boolean(l > r), + (Value::String(l), Value::String(r)) => Value::Boolean(l > r), + (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l > r), + _ => panic!("Cannot compare values of different types or unsupported types"), + } + }) +} diff --git a/src/ast2/expression/comparisons/greater_equal.rs b/src/ast2/expression/comparisons/greater_equal.rs new file mode 100644 index 0000000..14b13a6 --- /dev/null +++ b/src/ast2/expression/comparisons/greater_equal.rs @@ -0,0 +1,16 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn greater_equal(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (left_value, right_value) { + (Value::Number(l), Value::Number(r)) => Value::Boolean(l >= r), + (Value::String(l), Value::String(r)) => Value::Boolean(l >= r), + (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l >= r), + _ => panic!("Cannot compare values of different types or unsupported types"), + } + }) +} diff --git a/src/ast2/expression/comparisons/lesser.rs b/src/ast2/expression/comparisons/lesser.rs new file mode 100644 index 0000000..cf2a2a4 --- /dev/null +++ b/src/ast2/expression/comparisons/lesser.rs @@ -0,0 +1,16 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn lesser(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (left_value, right_value) { + (Value::Number(l), Value::Number(r)) => Value::Boolean(l < r), + (Value::String(l), Value::String(r)) => Value::Boolean(l < r), + (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l < r), + _ => panic!("Cannot compare values of different types or unsupported types"), + } + }) +} diff --git a/src/ast2/expression/comparisons/lesser_equal.rs b/src/ast2/expression/comparisons/lesser_equal.rs new file mode 100644 index 0000000..05b46fa --- /dev/null +++ b/src/ast2/expression/comparisons/lesser_equal.rs @@ -0,0 +1,16 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn lesser_equal(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (left_value, right_value) { + (Value::Number(l), Value::Number(r)) => Value::Boolean(l <= r), + (Value::String(l), Value::String(r)) => Value::Boolean(l <= r), + (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l <= r), + _ => panic!("Cannot compare values of different types or unsupported types"), + } + }) +} diff --git a/src/ast2/expression/comparisons/mod.rs b/src/ast2/expression/comparisons/mod.rs new file mode 100644 index 0000000..2b451ac --- /dev/null +++ b/src/ast2/expression/comparisons/mod.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; + +inherit!(equal); +inherit!(notequal); +inherit!(or); +inherit!(and); +inherit!(lesser); +inherit!(lesser_equal); +inherit!(greater); +inherit!(greater_equal); diff --git a/src/ast2/expression/comparisons/notequal.rs b/src/ast2/expression/comparisons/notequal.rs new file mode 100644 index 0000000..1bbaaef --- /dev/null +++ b/src/ast2/expression/comparisons/notequal.rs @@ -0,0 +1,11 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn notequal(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + Value::Boolean(left_value != right_value) + }) +} diff --git a/src/ast2/expression/comparisons/or.rs b/src/ast2/expression/comparisons/or.rs new file mode 100644 index 0000000..3b64ca5 --- /dev/null +++ b/src/ast2/expression/comparisons/or.rs @@ -0,0 +1,19 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn or(left: Expression, right: Expression) -> Expression { + Box::new(move |ctx, scope| { + let left_value = left(ctx, scope); + let right_value = right(ctx, scope); + + match (&left_value, &right_value) { + (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(*l || *r), + _ => { + panic!( + "Type mismatch in logical AND: {} && {}", + left_value, right_value + ); + } + } + }) +} diff --git a/src/ast2/expression/mod.rs b/src/ast2/expression/mod.rs index 28ea738..cd2c35c 100644 --- a/src/ast2/expression/mod.rs +++ b/src/ast2/expression/mod.rs @@ -1,6 +1,21 @@ -use crate::context::Context; - use super::environment::Value; use super::Environment; +use crate::context::Context; +use crate::prelude::*; + +inherits!( + comparisons, + [ + equal, + notequal, + or, + and, + lesser, + lesser_equal, + greater, + greater_equal + ] +); +inherits!(calculus, [add, subtract, multiply, divide]); pub type Expression = Box Value + 'static>; diff --git a/src/ast2/functions/mod.rs b/src/ast2/functions/mod.rs index e36d453..7b45259 100644 --- a/src/ast2/functions/mod.rs +++ b/src/ast2/functions/mod.rs @@ -1,27 +1,72 @@ -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; use crate::context::Context; use super::{ environment::{Type, Value}, + statement::Result, Environment, Expression, Statement, }; pub type FunctionParameter = Rc)>>; pub type FunctionBody = Rc>; pub type FunctionValue = ( - Box, Rc)>>, Type, FunctionBody, ); -trait FunctionRunner { - fn run( - &self, - ctx: &Context, - env: &Environment, - parameters: Vec, - inputs: Vec, - ) -> Value; +pub trait FunctionRunner { + fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec) -> Value; +} + +impl FunctionRunner for FunctionValue { + fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec) -> Value { + let (parameters, return_type, body) = self; + + // init params and override with inputs + for (i, param) in parameters.iter().enumerate() { + let (param_type, param_name, default) = param; + let input = inputs.get(i); + let value = if let Some(input) = input { + input(ctx, env) + } else if let Some(default) = default { + default(ctx, env) + } else { + Value::Nil + }; + + env.clone().set( + param_name, + Rc::new(RefCell::new((param_type.clone(), value))), + ); + } + + env.clone().subscope(|| { + for statement in body.iter() { + let value = match statement(ctx, env) { + Result::Return(value) => Some(value), + Result::Collect(value) => { + let list = Environment::new(); + for _ in value { + todo!() + } + Some(Value::List(list)) + } + Result::NOP => None, + _ => panic!("Unexpected result from statement"), + }; + + if let Some(value) = value { + if return_type.matches(&value) { + return value; + } else { + panic!("Type mismatch: expected {}, got {}", return_type, value); + } + } + } + + Value::Nil + }) + } } diff --git a/src/ast2/node/html.rs b/src/ast2/node/html.rs index 4deb52a..d22faa7 100644 --- a/src/ast2/node/html.rs +++ b/src/ast2/node/html.rs @@ -54,7 +54,12 @@ impl Html { .map(|node| node(ctx, env)) .collect::>() .join(" "); - format!("{}=\"{}\"", k, value) + + if value.is_empty() { + k.to_string() + } else { + format!(r#"{}="{}""#, k, value) + } }) .collect::>() .join(" "); diff --git a/src/ast2/node/mod.rs b/src/ast2/node/mod.rs index f4c8155..218c030 100644 --- a/src/ast2/node/mod.rs +++ b/src/ast2/node/mod.rs @@ -1,16 +1,10 @@ -mod html; -mod insertion; -mod logic; -mod text; - -pub use html::html; -pub use html::Html; -pub use insertion::insertion; -pub use logic::{logic_expression, logic_statement}; -pub use text::text; - +use super::Environment; use crate::context::Context; +use crate::prelude::*; -use super::Environment; +inherits!(html, [html, Html]); +inherit!(insertion); +inherits!(logic, [logic_expression, logic_statement]); +inherit!(text); pub type Node = Box String + 'static>; diff --git a/src/ast2/statement/assign.rs b/src/ast2/statement/assign.rs new file mode 100644 index 0000000..a896eaa --- /dev/null +++ b/src/ast2/statement/assign.rs @@ -0,0 +1,10 @@ +use super::{Result, Statement}; +use crate::ast2::expression::Expression; + +pub fn assign(name: String, expression: Expression) -> Statement { + Box::new(move |ctx, env| { + let value = expression(ctx, env); + env.clone().assign(name.as_str(), value.as_typevalue()); + Result::NOP + }) +} diff --git a/src/ast2/statement/call.rs b/src/ast2/statement/call.rs new file mode 100644 index 0000000..f0afdff --- /dev/null +++ b/src/ast2/statement/call.rs @@ -0,0 +1,17 @@ +use super::{Result, Statement}; +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn call(value: Expression, arguments: Vec) -> Statement { + Box::new(move |ctx, env| { + let function = value(ctx, env); + match function { + Value::Function(function) => { + function.call(ctx, env, &arguments); + } + _ => panic!("Expected a function, got {}", function), + } + + Result::NOP + }) +} diff --git a/src/ast2/statement/define.rs b/src/ast2/statement/define.rs new file mode 100644 index 0000000..8d4368d --- /dev/null +++ b/src/ast2/statement/define.rs @@ -0,0 +1,17 @@ +use super::{Result, Statement}; +use crate::ast2::{ + environment::{Type, Value}, + expression::Expression, +}; + +pub fn define(t: Type, name: String, expression: Option) -> Statement { + Box::new(move |ctx, env| { + let value = if let Some(expr) = &expression { + expr(ctx, env) + } else { + Value::Nil + }; + env.clone().define(name.as_str(), t.with_value(value)); + Result::NOP + }) +} diff --git a/src/ast2/statement/mod.rs b/src/ast2/statement/mod.rs index 1951b66..9b37b31 100644 --- a/src/ast2/statement/mod.rs +++ b/src/ast2/statement/mod.rs @@ -1,6 +1,10 @@ +use super::{environment::Value, Environment}; use crate::context::Context; +use crate::prelude::*; -use super::{environment::Value, Environment}; +inherit!(call); +inherit!(assign); +inherit!(define); pub enum Result { Collect(Vec), diff --git a/src/context/config/assets.rs b/src/context/config/assets.rs new file mode 100644 index 0000000..0ba8f5e --- /dev/null +++ b/src/context/config/assets.rs @@ -0,0 +1,15 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +#[serde(default)] +pub struct Assets { + pub folder: String, +} + +impl Default for Assets { + fn default() -> Self { + Assets { + folder: "assets".to_string(), + } + } +} diff --git a/src/context/config/mod.rs b/src/context/config/mod.rs new file mode 100644 index 0000000..2b7207e --- /dev/null +++ b/src/context/config/mod.rs @@ -0,0 +1,45 @@ +use log::warn; +use serde::Deserialize; +use std::fs; + +mod assets; +mod paths; + +use assets::Assets; +use paths::Paths; + +#[derive(Deserialize)] +#[serde(default)] +pub struct Config { + pub paths: Paths, + pub assets: Assets, + pub pretty: bool, +} + +impl Default for Config { + fn default() -> Self { + Config { + paths: Paths::default(), + assets: Assets::default(), + pretty: false, + } + } +} + +const DAISY_CONFIG: &str = "daisy.toml"; + +impl Config { + pub fn new() -> Self { + let config_raw = fs::read_to_string(DAISY_CONFIG).unwrap_or_else(|_| { + warn!("{} not found, using default config", DAISY_CONFIG); + String::new() + }); + + toml::from_str::(&config_raw).unwrap().sanitize() + } + + pub fn sanitize(self) -> Self { + // todo: sanitize paths + self + } +} diff --git a/src/context/config/paths.rs b/src/context/config/paths.rs new file mode 100644 index 0000000..fb0d6b5 --- /dev/null +++ b/src/context/config/paths.rs @@ -0,0 +1,19 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +#[serde(default)] +pub struct Paths { + pub workdir: String, + pub pages: String, + pub output: String, +} + +impl Default for Paths { + fn default() -> Self { + Paths { + workdir: ".".to_string(), + pages: "src".to_string(), + output: "site".to_string(), + } + } +} diff --git a/src/context/mod.rs b/src/context/mod.rs index bebe051..ef2dfe2 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -1,112 +1,21 @@ -use std::{cell::RefCell, fs, path::Path, rc::Rc}; +use std::{cell::RefCell, rc::Rc}; +mod config; +use config::Config; -use crate::{grammar::DaisyParser, resolver::resource::Resource}; -use log::warn; -use serde::Deserialize; +use crate::grammar::DaisyParser; -#[derive(Clone)] pub struct Context { - pub parser: Rc>, + pub parser: DaisyParser, pub resources: Vec>>, - pub config: Rc>, + pub config: Config, } -#[derive(Deserialize)] -#[serde(default)] -pub struct Config { - pub paths: Paths, - pub assets: Assets, - pub pretty: bool, -} - -#[derive(Deserialize)] -#[serde(default)] -pub struct Paths { - pub workdir: String, - pub pages: String, - pub output: String, -} - -#[derive(Deserialize)] -#[serde(default)] -pub struct Assets { - pub folder: String, -} - -impl Default for Config { - fn default() -> Self { - Config { - paths: Paths::default(), - assets: Assets::default(), - pretty: false, - } - } -} - -impl Default for Paths { - fn default() -> Self { - Paths { - workdir: ".".to_string(), - pages: "src".to_string(), - output: "site".to_string(), - } - } -} - -impl Default for Assets { - fn default() -> Self { - Assets { - folder: "assets".to_string(), - } - } -} - -const DAISY_CONFIG: &str = "daisy.toml"; - impl Context { - pub fn load_config() -> Self { - let config_str = std::fs::read_to_string(DAISY_CONFIG).unwrap_or_else(|_| { - warn!("{} not found, using default config", DAISY_CONFIG); - "".to_string() - }); - - let mut cfg: Config = toml::from_str(&config_str).unwrap(); - - let absolute_src = std::fs::canonicalize(&cfg.paths.workdir).unwrap_or_else(|_| { - panic!("{} not found, using default src", cfg.paths.workdir); - }); - cfg.paths.workdir = absolute_src.to_str().unwrap().to_string(); - + pub fn new() -> Self { Context { parser: DaisyParser::new(), resources: vec![], - config: cfg, + config: Config::new(), } } - - pub fn get_output_path(&self) -> String { - format!( - "{}/{}/", - self.config.paths.workdir, self.config.paths.output - ) - } - - pub fn get_page_path(&self) -> String { - format!("{}/{}/", self.config.paths.workdir, self.config.paths.pages) - } - - pub fn save_content(&self, path: &str, content: &str) -> String { - let output_path = Path::new(path); - - fs::create_dir_all(output_path.parent().unwrap()).unwrap_or_else(|err| { - panic!("Failed to create directory: {}: {}", path, err); - }); - - fs::write(&output_path, content).unwrap_or_else(|err| { - println!("{}", content); - panic!("Failed to write file: {}: {}", path, err); - }); - - output_path.to_str().unwrap().to_string() - } } diff --git a/src/context/parser.rs b/src/context/parser.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/context/resolver.rs b/src/context/resolver.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/context/resources.rs b/src/context/resources.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index e3d0b46..2180b35 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -3,7 +3,6 @@ use crate::ast2::environment::{Type, Value}; use crate::ast2::{expression, expression::Expression}; use crate::ast2::{statement, statement::Statement}; use crate::ast2::{node, node::Node, node::Html}; -use crate::ast2::functions::FunctionValue; use crate::parser::{parse_string, parse_multiline_string}; grammar; @@ -102,10 +101,10 @@ OpEqual = "=="; OpNotEqual = "!="; OpOr = "||"; OpAnd = "&&"; -OpLessThan = "<"; -OpLessThanOrEqual = "<="; -OpGreaterThan = ">"; -OpGreaterThanOrEqual = ">="; +OpLesser = "<"; +OpLesserEqual = "<="; +OpGreater = ">"; +OpGreaterEqual = ">="; OpAdd = "+"; OpSubtract = "-"; @@ -163,7 +162,7 @@ ValueNumber: Value = => Value::Number(n); ValueFloat: Value = => Value::Float(f); ValueBoolean: Value = => Value::Boolean(b); ValueNil: Value = KeyValueNil => Value::Nil; -ValueFunction: Value = => Value::Function((default_function, function.0.into(), function.1, function.2.into())); +ValueFunction: Value = => Value::Function(Box::new((function.0.into(), function.1, function.2.into()))); ValueHtml: Value = ":" => Value::Html(html); Value: Value = { @@ -184,10 +183,10 @@ Expression: Expression = { (OpNotEqual) => expression::notequal(left.into(), right.into()), (OpOr) => expression::or(left.into(), right.into()), (OpAnd) => expression::and(left.into(), right.into()), - (OpLessThan) => expression::lessthan(left.into(), right.into()), - (OpLessThanOrEqual) => expression::lessthanorequal(left.into(), right.into()), - (OpGreaterThan) => expression::greaterthan(left.into(), right.into()), - (OpGreaterThanOrEqual) => expression::greaterthanorequal(left.into(), right.into()), + (OpLesser) => expression::lesser(left.into(), right.into()), + (OpLesserEqual) => expression::lesser_equal(left.into(), right.into()), + (OpGreater) => expression::greater(left.into(), right.into()), + (OpGreaterEqual) => expression::greater_equal(left.into(), right.into()), ExpressionCall, ExpressionScopeEntry, @@ -195,15 +194,15 @@ Expression: Expression = { }; ExpressionCalculus: Expression = { - (OpAdd) => expression::addition(left.into(), right.into()), - (OpSubtract) => expression::subtraction(left.into(), right.into()), + (OpAdd) => expression::add(left, right), + (OpSubtract) => expression::subtract(left, right), ExpressionFactor, }; ExpressionFactor: Expression = { - (OpMultiply) => expression::multiplication(left.into(), right.into()), - (OpDivide) => expression::division(left.into(), right.into()), + (OpMultiply) => expression::multiply(left, right), + (OpDivide) => expression::divide(left, right), ExpressionTerm, }; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3c82c64 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,17 @@ +#[macro_export] +macro_rules! inherit { + ($name:ident) => { + pub mod $name; + pub use $name::$name; + }; +} + +#[macro_export] +macro_rules! inherits { + ($name:ident, [$($inherit:ident),+]) => { + pub mod $name; + $( + pub use $name::$inherit; + )+ + }; +} diff --git a/src/main.rs b/src/main.rs index 10ee5cd..2fc94c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ use env_logger::Env; use lalrpop_util::lalrpop_mod; -mod ast; +// mod ast; mod ast2; -use ast2::environment::Environment; // mod cli; mod context; mod parser; +mod prelude; // mod resolver; lalrpop_mod!(grammar); @@ -14,7 +14,7 @@ lalrpop_mod!(grammar); fn main() { let env = Env::default().filter_or("DAISY_LOG", "trace"); env_logger::init_from_env(env); - let ctx = context::Context::load_config(); + let ctx = context::Context::new(); _ = ctx; // cli::run(&mut ctx); diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..d1cc662 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,2 @@ +pub use daisy::inherit; +pub use daisy::inherits; From 78d3b733c6a74968edd3fbb2b95497396a913877 Mon Sep 17 00:00:00 2001 From: meir Date: Wed, 27 Aug 2025 12:58:57 +0200 Subject: [PATCH 3/6] wip --- src/ast2/environment/value.rs | 1 + src/ast2/expression/call.rs | 12 ++++++ src/ast2/expression/identifier.rs | 37 ++++++++++++++++ src/ast2/expression/list.rs | 16 +++++++ src/ast2/expression/map.rs | 22 ++++++++++ src/ast2/expression/mod.rs | 7 +++ src/ast2/expression/object_entry.rs | 35 +++++++++++++++ src/ast2/expression/script.rs | 18 ++++++++ src/ast2/expression/value.rs | 13 ++++++ src/ast2/statement/break.rs | 5 +++ src/ast2/statement/collect.rs | 9 ++++ src/ast2/statement/continue.rs | 5 +++ src/ast2/statement/for.rs | 46 ++++++++++++++++++++ src/ast2/statement/if.rs | 33 +++++++++++++++ src/ast2/statement/iter.rs | 66 +++++++++++++++++++++++++++++ src/ast2/statement/mod.rs | 7 +++ src/ast2/statement/return.rs | 13 ++++++ src/grammar.lalrpop | 20 ++++----- 18 files changed, 355 insertions(+), 10 deletions(-) create mode 100644 src/ast2/expression/call.rs create mode 100644 src/ast2/expression/identifier.rs create mode 100644 src/ast2/expression/list.rs create mode 100644 src/ast2/expression/map.rs create mode 100644 src/ast2/expression/object_entry.rs create mode 100644 src/ast2/expression/script.rs create mode 100644 src/ast2/expression/value.rs create mode 100644 src/ast2/statement/break.rs create mode 100644 src/ast2/statement/collect.rs create mode 100644 src/ast2/statement/continue.rs create mode 100644 src/ast2/statement/for.rs create mode 100644 src/ast2/statement/if.rs create mode 100644 src/ast2/statement/iter.rs create mode 100644 src/ast2/statement/return.rs diff --git a/src/ast2/environment/value.rs b/src/ast2/environment/value.rs index e91c0f8..72db965 100644 --- a/src/ast2/environment/value.rs +++ b/src/ast2/environment/value.rs @@ -26,6 +26,7 @@ impl CheckTypeValue for TypeValue { } } +// #[derive(Clone)] pub enum Value { String(String), Number(i64), diff --git a/src/ast2/expression/call.rs b/src/ast2/expression/call.rs new file mode 100644 index 0000000..8f4fd4b --- /dev/null +++ b/src/ast2/expression/call.rs @@ -0,0 +1,12 @@ +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn call(value: Expression, arguments: Vec) -> Expression { + Box::new(move |ctx, env| { + let function = value(ctx, env); + match function { + Value::Function(function) => function.call(ctx, env, &arguments), + _ => panic!("Expected a function, got {}", function), + } + }) +} diff --git a/src/ast2/expression/identifier.rs b/src/ast2/expression/identifier.rs new file mode 100644 index 0000000..869c3b4 --- /dev/null +++ b/src/ast2/expression/identifier.rs @@ -0,0 +1,37 @@ +use std::rc::Rc; + +use crate::ast2::environment::Value; +use crate::ast2::expression::Expression; + +pub fn identifier(location: Vec) -> Expression { + Box::new(move |_ctx, env| { + let first = location + .first() + .expect("Identifier must have at least one part"); + let mut current = env.get(first); + + for part in location.iter().skip(1) { + current = if let Some(tv) = current { + let borrowed = tv.borrow(); + if let Value::Map(map) = &borrowed.1 { + map.as_map().get(part).cloned() + } else { + None + } + } else { + None + }; + } + + if let Some(tv) = current { + match Rc::try_unwrap(tv) { + Ok(ref_cell) => ref_cell.into_inner().1, + Err(_rc) => { + panic!("Identifier value is still referenced elsewhere"); + } + } + } else { + Value::Nil + } + }) +} diff --git a/src/ast2/expression/list.rs b/src/ast2/expression/list.rs new file mode 100644 index 0000000..4ac83c2 --- /dev/null +++ b/src/ast2/expression/list.rs @@ -0,0 +1,16 @@ +use crate::ast2::environment::{Environment, Value}; + +use super::Expression; + +pub fn list(items: Vec) -> Expression { + Box::new(move |ctx, env| { + let values: Vec = items.iter().map(|item| item(ctx, env)).collect(); + let mut list = Environment::new(); + + for (index, value) in values.into_iter().enumerate() { + list.set(index.to_string().as_str(), value.as_typevalue()); + } + + Value::List(list) + }) +} diff --git a/src/ast2/expression/map.rs b/src/ast2/expression/map.rs new file mode 100644 index 0000000..a3c4d9f --- /dev/null +++ b/src/ast2/expression/map.rs @@ -0,0 +1,22 @@ +use crate::ast2::{ + environment::{Type, Value}, + Environment, +}; + +use super::Expression; + +pub fn map(entries: Vec<(Type, String, Option)>) -> Expression { + Box::new(move |ctx, env| { + let mut map = Environment::new(); + for entry in entries.iter() { + let value = if let Some(expr) = &entry.2 { + expr(ctx, env) + } else { + Value::Nil + }; + map.define(entry.1.as_str(), entry.0.with_value(value)); + } + + Value::Map(map) + }) +} diff --git a/src/ast2/expression/mod.rs b/src/ast2/expression/mod.rs index cd2c35c..50539ef 100644 --- a/src/ast2/expression/mod.rs +++ b/src/ast2/expression/mod.rs @@ -17,5 +17,12 @@ inherits!( ] ); inherits!(calculus, [add, subtract, multiply, divide]); +inherit!(call); +inherit!(identifier); +inherit!(list); +inherit!(map); +inherit!(value); +inherit!(script); +inherit!(object_entry); pub type Expression = Box Value + 'static>; diff --git a/src/ast2/expression/object_entry.rs b/src/ast2/expression/object_entry.rs new file mode 100644 index 0000000..4f22bae --- /dev/null +++ b/src/ast2/expression/object_entry.rs @@ -0,0 +1,35 @@ +use std::rc::Rc; + +use crate::ast2::environment::Value; + +use super::Expression; + +pub fn object_entry(env_obj: Expression, entry: Expression) -> Expression { + Box::new(move |ctx, env| { + let env_value = env_obj(ctx, env); + let key = entry(ctx, env); + let env_ = match env_value { + Value::Map(map_value) => map_value, + Value::List(array_value) => array_value, + _ => { + panic!("Expected a map or array, got {}", env_value); + } + }; + + let key = match key { + Value::String(s) => s, + Value::Number(n) => n.to_string(), + _ => panic!("Expected a string or number as key, got {}", key), + }; + if let Some(value) = env_.get(&key) { + match Rc::try_unwrap(value) { + Ok(ref_cell) => ref_cell.into_inner().1, + Err(_rc) => { + panic!("Identifier value is still referenced elsewhere"); + } + } + } else { + Value::Nil + } + }) +} diff --git a/src/ast2/expression/script.rs b/src/ast2/expression/script.rs new file mode 100644 index 0000000..82c9a12 --- /dev/null +++ b/src/ast2/expression/script.rs @@ -0,0 +1,18 @@ +use std::process::Command; + +use crate::ast2::environment::Value; + +use super::Expression; + +pub fn script(script: Box) -> Expression { + Box::new(move |_ctx, _scope| { + let result = Command::new("bash") + .arg("-c") + .arg(script.to_string()) + .output(); + match result { + Ok(output) => Value::String(String::from_utf8_lossy(&output.stdout).trim().to_string()), + Err(e) => panic!("Failed to execute script '{}': {}", script, e), + } + }) +} diff --git a/src/ast2/expression/value.rs b/src/ast2/expression/value.rs new file mode 100644 index 0000000..9f29194 --- /dev/null +++ b/src/ast2/expression/value.rs @@ -0,0 +1,13 @@ +use std::rc::Rc; + +use crate::ast2::environment::Value; + +use super::Expression; + +pub fn value(value: Value) -> Expression { + let value = Rc::new(value); + Box::new(move |_ctx, _env| { + Rc::try_unwrap(value.clone()) + .unwrap_or_else(|_rc| panic!("Cannot extract value with multiple references")) + }) +} diff --git a/src/ast2/statement/break.rs b/src/ast2/statement/break.rs new file mode 100644 index 0000000..301f21f --- /dev/null +++ b/src/ast2/statement/break.rs @@ -0,0 +1,5 @@ +use super::{Result, Statement}; + +pub fn break_s() -> Statement { + Box::new(move |_ctx, _env| Result::Break) +} diff --git a/src/ast2/statement/collect.rs b/src/ast2/statement/collect.rs new file mode 100644 index 0000000..9615c13 --- /dev/null +++ b/src/ast2/statement/collect.rs @@ -0,0 +1,9 @@ +use super::{Result, Statement}; +use crate::ast2::expression::Expression; + +pub fn collect(expression: Expression) -> Statement { + Box::new(move |ctx, env| { + let value = expression(ctx, env); + Result::Collect(vec![value]) + }) +} diff --git a/src/ast2/statement/continue.rs b/src/ast2/statement/continue.rs new file mode 100644 index 0000000..5faafec --- /dev/null +++ b/src/ast2/statement/continue.rs @@ -0,0 +1,5 @@ +use super::{Result, Statement}; + +pub fn continue_s() -> Statement { + Box::new(move |_ctx, _env| Result::Continue) +} diff --git a/src/ast2/statement/for.rs b/src/ast2/statement/for.rs new file mode 100644 index 0000000..29d1f01 --- /dev/null +++ b/src/ast2/statement/for.rs @@ -0,0 +1,46 @@ +use super::{Result, Statement}; +use crate::ast2::{environment::Value, expression::Expression}; + +pub fn for_s( + init: Statement, + condition: Expression, + increment: Statement, + body: Vec, +) -> Statement { + Box::new(move |ctx, env| { + env.clone().subscope(|| { + init(ctx, env); + + let mut collected_values = vec![]; + 'mainloop: loop { + if let Value::Boolean(false) = condition(ctx, env) { + break; + } + + for stmt in body.iter() { + let result = stmt(ctx, env); + match result { + Result::Continue => { + continue 'mainloop; + } + Result::Collect(values) => { + collected_values.extend(values); + } + Result::NOP => {} + _ => { + return result; + } + } + + increment(ctx, env); + } + } + + if collected_values.is_empty() { + Result::NOP + } else { + Result::Collect(collected_values) + } + }) + }) +} diff --git a/src/ast2/statement/if.rs b/src/ast2/statement/if.rs new file mode 100644 index 0000000..bd9cbb3 --- /dev/null +++ b/src/ast2/statement/if.rs @@ -0,0 +1,33 @@ +use super::{Result, Statement}; +use crate::ast2::{environment::Value, expression::Expression}; + +pub fn if_s(condition: Expression, body: Vec) -> Statement { + Box::new(move |ctx, env| { + let condition_value = condition(ctx, env); + let mut collected_values = vec![]; + let mut result = Result::NOP; + if let Value::Boolean(true) = condition_value { + result = env.clone().subscope(|| { + for stmt in body.iter() { + let result = stmt(ctx, env); + match result { + Result::Collect(values) => { + collected_values.extend(values); + } + Result::NOP => {} + _ => { + return result; + } + } + } + return Result::NOP; + }); + } + + if collected_values.is_empty() { + result + } else { + Result::Collect(collected_values) + } + }) +} diff --git a/src/ast2/statement/iter.rs b/src/ast2/statement/iter.rs new file mode 100644 index 0000000..f1965c4 --- /dev/null +++ b/src/ast2/statement/iter.rs @@ -0,0 +1,66 @@ +use super::{Result, Statement}; +use crate::ast2::{environment::Value, expression::Expression}; + +pub fn iter( + identifiers: (String, Option), + iterable: Expression, + body: Vec, +) -> Statement { + Box::new(move |ctx, env| { + env.clone().subscope(|| { + Result::NOP + // let (key_name, value_name) = identifiers.clone(); + // let var = iterable(ctx, env); + // let (_scope, indices) = match var { + // Value::List(mut list) => { + // let indices = list.get_indices(); + // (list, indices) + // } + // Value::Map(mut map) => { + // let keys = map.get_keys(); + // (map, keys) + // } + // _ => { + // panic!("Expected an array or map, got {}", var); + // } + // }; + // + // let mut collected_values = vec![]; + // env.define(Type::Any, key_name.to_string(), Value::Nil); + // if let Some(value_name) = &value_name { + // env.define(Type::Any, value_name.to_string(), Value::Nil); + // } + // 'mainloop: for index in indices { + // env.set(key_name.to_string(), Value::String(index.clone())); + // if let Some(value_name) = &value_name { + // env.set( + // value_name.to_string(), + // _scope.get(&index).unwrap_or(&Value::Nil).clone(), + // ); + // } + // + // for statement in body.iter() { + // let result = statement(ctx, env); + // match result { + // Result::Continue => { + // continue 'mainloop; + // } + // Result::Collect(values) => { + // collected_values.extend(values); + // } + // Result::NOP => {} + // _ => { + // return result; + // } + // } + // } + // } + // + // if collected_values.is_empty() { + // return Result::NOP; + // } else { + // return Result::Collect(collected_values); + // } + }) + }) +} diff --git a/src/ast2/statement/mod.rs b/src/ast2/statement/mod.rs index 9b37b31..cabd396 100644 --- a/src/ast2/statement/mod.rs +++ b/src/ast2/statement/mod.rs @@ -5,6 +5,13 @@ use crate::prelude::*; inherit!(call); inherit!(assign); inherit!(define); +inherit!(collect); +inherits!(r#return, [return_s]); +inherits!(r#break, [break_s]); +inherits!(r#continue, [continue_s]); +inherits!(r#if, [if_s]); +inherits!(r#for, [for_s]); +inherit!(iter); pub enum Result { Collect(Vec), diff --git a/src/ast2/statement/return.rs b/src/ast2/statement/return.rs new file mode 100644 index 0000000..f92c601 --- /dev/null +++ b/src/ast2/statement/return.rs @@ -0,0 +1,13 @@ +use super::{Result, Statement}; +use crate::ast2::expression::Expression; + +pub fn return_s(expression: Option) -> Statement { + if let Some(expr) = expression { + Box::new(move |ctx, env| { + let value = expr(ctx, env); + Result::Return(value) + }) + } else { + Box::new(|_ctx, _env| Result::Break) + } +} diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index 2180b35..f780195 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -224,7 +224,7 @@ ExpressionList: Expression = "[" ",")*> "[" "]" => expression::scope_entry(scope.into(), entry.into()); +ExpressionScopeEntry: Expression = "[" "]" => expression::object_entry(scope.into(), entry.into()); ExpressionIdentifier: Expression = )*> => expression::identifier({ let mut location = vec![location.to_string()]; location.append(&mut subsequent.iter().map(|s| s.to_string()).collect()); @@ -242,19 +242,19 @@ ExpressionScript: Expression = "$" => expression::s // statements StatementCollect: Statement = (KeyCollect) => statement::collect(expression.into()); -StatementBreak: Statement = KeyBreak => statement::break_statement(); -StatementContinue: Statement = KeyContinue => statement::continue_statement(); +StatementBreak: Statement = KeyBreak => statement::break_s(); +StatementContinue: Statement = KeyContinue => statement::continue_s(); StatementReturn: Statement = { - (KeyReturn) => statement::return_statement(Some(expression)), - (KeyReturn) ";" => statement::return_statement(None), + (KeyReturn) => statement::return_s(Some(expression)), + (KeyReturn) ";" => statement::return_s(None), }; -StatementIf: Statement = (KeyIf) "{" "}" => statement::if_statement(condition.into(), body); +StatementIf: Statement = (KeyIf) "{" "}" => statement::if_s(condition.into(), body); StatementIter: Statement = { - (KeyFor) "in" "{" "}" => statement::iter_statement((key.into(), None), collection, body), - (KeyFor) "," "in" "{" "}" => statement::iter_statement((key.into(), Some(value.into())), collection, body), + (KeyFor) "in" "{" "}" => statement::iter((key.into(), None), collection, body), + (KeyFor) "," "in" "{" "}" => statement::iter((key.into(), Some(value.into())), collection, body), }; -StatementFor: Statement = (KeyFor) ";" ";" "{" "}" => statement::for_statement(init, condition, increment, body); +StatementFor: Statement = (KeyFor) ";" ";" "{" "}" => statement::for_s(init, condition, increment, body); StatementDefinition: Statement = => statement::define(definition.0, definition.1, definition.2); StatementAssign: Statement = "=" => statement::assign(identifier.into(), expression.into()); StatementCall: Statement = "(" ",")*> ")" => { @@ -299,7 +299,7 @@ NodeInsert: Node = "@" "(" "}" => node::insertion(expres Node: Node = { => node::logic_statement(for_loop), => node::logic_statement(iter_loop), - => node::logic_statement(if_statement), + => node::logic_statement(if_s), NodeHtml, NodeInsert, From 11c572faf6e0086a733b1cf01d430e7c444e1fde Mon Sep 17 00:00:00 2001 From: meir Date: Fri, 5 Sep 2025 09:40:51 +0200 Subject: [PATCH 4/6] wip --- examples/site_1.0/src/test.ds | 4 +- src/ast2/environment/value.rs | 8 +- src/ast2/expression/call.rs | 7 +- src/ast2/expression/identifier.rs | 9 +- src/ast2/expression/object_entry.rs | 9 +- src/ast2/expression/value.rs | 8 +- src/ast2/functions/mod.rs | 21 ++++- src/ast2/node/html.rs | 3 +- src/ast2/node/insertion.rs | 4 +- src/ast2/node/logic.rs | 6 +- src/ast2/node/mod.rs | 4 +- src/ast2/node/text.rs | 4 +- src/ast2/statement/call.rs | 4 +- src/cli/build.rs | 125 +++++++++++++++++----------- src/context/config/assets.rs | 2 +- src/context/config/mod.rs | 2 +- src/context/config/paths.rs | 21 ++++- src/context/file.rs | 40 +++++++++ src/context/mod.rs | 21 +++-- src/context/parser.rs | 73 ++++++++++++++++ src/context/resolver.rs | 28 +++++++ src/context/resource.rs | 47 +++++++++++ src/context/resources.rs | 0 src/grammar.lalrpop | 11 ++- src/main.rs | 8 +- 25 files changed, 363 insertions(+), 106 deletions(-) create mode 100644 src/context/file.rs create mode 100644 src/context/resource.rs delete mode 100644 src/context/resources.rs diff --git a/examples/site_1.0/src/test.ds b/examples/site_1.0/src/test.ds index d681534..326b94c 100644 --- a/examples/site_1.0/src/test.ds +++ b/examples/site_1.0/src/test.ds @@ -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} diff --git a/src/ast2/environment/value.rs b/src/ast2/environment/value.rs index 72db965..73c0b10 100644 --- a/src/ast2/environment/value.rs +++ b/src/ast2/environment/value.rs @@ -2,10 +2,8 @@ use std::fmt::{Display, Formatter, Result}; use std::{cell::RefCell, rc::Rc}; use crate::ast2::functions::FunctionRunner; -use crate::{ - ast2::{Build, Node}, - context::Context, -}; +use crate::ast2::Node; +use crate::{ast2::Build, context::Context}; use super::{Environment, Type}; @@ -26,7 +24,7 @@ impl CheckTypeValue for TypeValue { } } -// #[derive(Clone)] +#[derive(Clone)] pub enum Value { String(String), Number(i64), diff --git a/src/ast2/expression/call.rs b/src/ast2/expression/call.rs index 8f4fd4b..69f04c1 100644 --- a/src/ast2/expression/call.rs +++ b/src/ast2/expression/call.rs @@ -1,3 +1,5 @@ +use log::warn; + use crate::ast2::environment::Value; use crate::ast2::expression::Expression; @@ -6,7 +8,10 @@ pub fn call(value: Expression, arguments: Vec) -> Expression { let function = value(ctx, env); match function { Value::Function(function) => function.call(ctx, env, &arguments), - _ => panic!("Expected a function, got {}", function), + _ => { + warn!("Expected a function, got {}", function); + Value::Nil + } } }) } diff --git a/src/ast2/expression/identifier.rs b/src/ast2/expression/identifier.rs index 869c3b4..576d35e 100644 --- a/src/ast2/expression/identifier.rs +++ b/src/ast2/expression/identifier.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use crate::ast2::environment::Value; use crate::ast2::expression::Expression; @@ -24,12 +22,7 @@ pub fn identifier(location: Vec) -> Expression { } if let Some(tv) = current { - match Rc::try_unwrap(tv) { - Ok(ref_cell) => ref_cell.into_inner().1, - Err(_rc) => { - panic!("Identifier value is still referenced elsewhere"); - } - } + tv.borrow().1.clone() } else { Value::Nil } diff --git a/src/ast2/expression/object_entry.rs b/src/ast2/expression/object_entry.rs index 4f22bae..40f8c61 100644 --- a/src/ast2/expression/object_entry.rs +++ b/src/ast2/expression/object_entry.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use crate::ast2::environment::Value; use super::Expression; @@ -22,12 +20,7 @@ pub fn object_entry(env_obj: Expression, entry: Expression) -> Expression { _ => panic!("Expected a string or number as key, got {}", key), }; if let Some(value) = env_.get(&key) { - match Rc::try_unwrap(value) { - Ok(ref_cell) => ref_cell.into_inner().1, - Err(_rc) => { - panic!("Identifier value is still referenced elsewhere"); - } - } + value.borrow().1.clone() } else { Value::Nil } diff --git a/src/ast2/expression/value.rs b/src/ast2/expression/value.rs index 9f29194..6d41a8f 100644 --- a/src/ast2/expression/value.rs +++ b/src/ast2/expression/value.rs @@ -1,13 +1,7 @@ -use std::rc::Rc; - use crate::ast2::environment::Value; use super::Expression; pub fn value(value: Value) -> Expression { - let value = Rc::new(value); - Box::new(move |_ctx, _env| { - Rc::try_unwrap(value.clone()) - .unwrap_or_else(|_rc| panic!("Cannot extract value with multiple references")) - }) + Box::new(move |_ctx, _env| value.clone()) } diff --git a/src/ast2/functions/mod.rs b/src/ast2/functions/mod.rs index 7b45259..4b8f203 100644 --- a/src/ast2/functions/mod.rs +++ b/src/ast2/functions/mod.rs @@ -16,10 +16,29 @@ pub type FunctionValue = ( FunctionBody, ); -pub trait FunctionRunner { +pub trait FunctionRunner: FunctionRunnerClone { fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec) -> Value; } +pub trait FunctionRunnerClone { + fn clone_box(&self) -> Box; +} + +impl FunctionRunnerClone for T +where + T: FunctionRunner + Clone + 'static, +{ + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Clone for Box { + fn clone(&self) -> Box { + self.as_ref().clone_box() + } +} + impl FunctionRunner for FunctionValue { fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec) -> Value { let (parameters, return_type, body) = self; diff --git a/src/ast2/node/html.rs b/src/ast2/node/html.rs index d22faa7..991bdbd 100644 --- a/src/ast2/node/html.rs +++ b/src/ast2/node/html.rs @@ -2,9 +2,10 @@ use super::Node; use crate::ast2::Environment; use crate::context::Context; use std::collections::HashMap; +use std::sync::Arc; pub fn html(element: Html) -> Node { - Box::new(move |ctx, env| element.build(ctx, env)) + Arc::new(move |ctx, env| element.build(ctx, env)) } pub struct Html { diff --git a/src/ast2/node/insertion.rs b/src/ast2/node/insertion.rs index cd21ccc..2798188 100644 --- a/src/ast2/node/insertion.rs +++ b/src/ast2/node/insertion.rs @@ -1,8 +1,10 @@ +use std::sync::Arc; + use crate::ast2::Build; use crate::ast2::Expression; use super::Node; pub fn insertion(expr: Expression) -> Node { - Box::new(move |ctx, env| expr(ctx, env).build(ctx, env)) + Arc::new(move |ctx, env| expr(ctx, env).build(ctx, env)) } diff --git a/src/ast2/node/logic.rs b/src/ast2/node/logic.rs index bf93b98..6f58285 100644 --- a/src/ast2/node/logic.rs +++ b/src/ast2/node/logic.rs @@ -1,10 +1,12 @@ +use std::sync::Arc; + use crate::ast2::statement::Result; use crate::ast2::{Build, Expression, Statement}; use super::Node; pub fn logic_statement(logic: Statement) -> Node { - Box::new(move |ctx, env| match logic(ctx, env) { + Arc::new(move |ctx, env| match logic(ctx, env) { Result::Collect(value) => value .iter() .map(|v| v.build(ctx, env)) @@ -16,5 +18,5 @@ pub fn logic_statement(logic: Statement) -> Node { } pub fn logic_expression(logic: Expression) -> Node { - Box::new(move |ctx, env| logic(ctx, env).build(ctx, env)) + Arc::new(move |ctx, env| logic(ctx, env).build(ctx, env)) } diff --git a/src/ast2/node/mod.rs b/src/ast2/node/mod.rs index 218c030..74d3986 100644 --- a/src/ast2/node/mod.rs +++ b/src/ast2/node/mod.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use super::Environment; use crate::context::Context; use crate::prelude::*; @@ -7,4 +9,4 @@ inherit!(insertion); inherits!(logic, [logic_expression, logic_statement]); inherit!(text); -pub type Node = Box String + 'static>; +pub type Node = Arc String + 'static>; diff --git a/src/ast2/node/text.rs b/src/ast2/node/text.rs index a43a69d..b5e0028 100644 --- a/src/ast2/node/text.rs +++ b/src/ast2/node/text.rs @@ -1,5 +1,7 @@ +use std::sync::Arc; + use super::Node; pub fn text(text: String) -> Node { - Box::new(move |_ctx, _env| text.to_string()) + Arc::new(move |_ctx, _env| text.to_string()) } diff --git a/src/ast2/statement/call.rs b/src/ast2/statement/call.rs index f0afdff..5423524 100644 --- a/src/ast2/statement/call.rs +++ b/src/ast2/statement/call.rs @@ -1,3 +1,5 @@ +use log::warn; + use super::{Result, Statement}; use crate::ast2::environment::Value; use crate::ast2::expression::Expression; @@ -9,7 +11,7 @@ pub fn call(value: Expression, arguments: Vec) -> Statement { Value::Function(function) => { function.call(ctx, env, &arguments); } - _ => panic!("Expected a function, got {}", function), + _ => warn!("Expected a function, got {}", function), } Result::NOP diff --git a/src/cli/build.rs b/src/cli/build.rs index 27ea54b..8e957b2 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -1,63 +1,88 @@ -use std::path::Path; - -use crate::ast::function::default_function; -use crate::context::Context; -use crate::resolver::{self, resource::Resource}; +use crate::ast2::statement::Result; +use crate::ast2::{Build, Environment}; +use crate::context::resource::{Resource, ResourceListExt}; +use crate::{context, context::Context}; pub fn build(ctx: &mut Context) { - resolver::load_dir(ctx); + context::load_dir(ctx); - // Process pages - resolver::get_all(ctx).iter().for_each(|resource| { + ctx.resources.get_pages().iter().for_each(|resource| { if let Resource::File(file) = &*resource.borrow() { - if !file.is_page { - return; - } - - let mut scope = file.get_scope(ctx); - - let output_path = if let Some(meta) = scope.get("meta") { - let meta = meta.clone().try_into_map().unwrap(); - let url = meta.get("url").unwrap(); - let url = url.clone().try_into_string().unwrap(); - - Resource::get_output_path(ctx, url.as_str()).unwrap() + let environment = if let Some(meta) = &file.meta { + let meta = meta(ctx, &Environment::new()); + let mut env = Environment::new(); + env.set("meta", meta.as_typevalue()); + env } else { - Resource::get_output_path(ctx, &file.src.to_str().unwrap()).unwrap() + Environment::new() }; - let content = default_function(ctx, &file.ast, &vec![], &mut scope.clone()); - let content = content.render(ctx, &mut scope); - let output = ctx.save_content(output_path.to_str().unwrap(), content.as_str()); - println!("[DAISY] Built {} -> {}", file.src.to_str().unwrap(), output); + for ast in &file.ast { + let result = ast(ctx, &environment); + match result { + Result::Return(result) => { + let value = result.build(ctx, &environment); + println!("{}", value) + } + _ => {} + } + } } else { panic!("Expected a File resource for page"); } }); - // after pages have been process, new resources have been added, process these resources - resolver::get_all(ctx) - .iter() - .for_each(|resource| match &*resource.borrow() { - Resource::SCSS(src, path, content) => { - let output = ctx.save_content(path, content); - println!("[SCSS] Built SCSS {} -> {}", src, output); - } - Resource::Other(src, output) => { - std::fs::create_dir_all(Path::new(output).parent().unwrap()).unwrap_or_else( - |err| { - panic!("Failed to create directory {}: {}", output, err); - }, - ); - - std::fs::copy(src, output).unwrap_or_else(|err| { - panic!( - "Failed to copy resource from {} to {}: {}", - src, output, err - ); - }); - println!("[ASSET] Copied {} -> {}", src, output); - } - _ => {} - }); + // Process pages + // resolver::get_all(ctx).iter().for_each(|resource| { + // if let Resource::File(file) = &*resource.borrow() { + // if !file.is_page { + // return; + // } + // + // let mut scope = file.get_scope(ctx); + // + // let output_path = if let Some(meta) = scope.get("meta") { + // let meta = meta.clone().try_into_map().unwrap(); + // let url = meta.get("url").unwrap(); + // let url = url.clone().try_into_string().unwrap(); + // + // Resource::get_output_path(ctx, url.as_str()).unwrap() + // } else { + // Resource::get_output_path(ctx, &file.src.to_str().unwrap()).unwrap() + // }; + // + // let content = default_function(ctx, &file.ast, &vec![], &mut scope.clone()); + // let content = content.render(ctx, &mut scope); + // let output = ctx.save_content(output_path.to_str().unwrap(), content.as_str()); + // println!("[DAISY] Built {} -> {}", file.src.to_str().unwrap(), output); + // } else { + // panic!("Expected a File resource for page"); + // } + // }); + // + // // after pages have been process, new resources have been added, process these resources + // resolver::get_all(ctx) + // .iter() + // .for_each(|resource| match &*resource.borrow() { + // Resource::SCSS(src, path, content) => { + // let output = ctx.save_content(path, content); + // println!("[SCSS] Built SCSS {} -> {}", src, output); + // } + // Resource::Other(src, output) => { + // std::fs::create_dir_all(Path::new(output).parent().unwrap()).unwrap_or_else( + // |err| { + // panic!("Failed to create directory {}: {}", output, err); + // }, + // ); + // + // std::fs::copy(src, output).unwrap_or_else(|err| { + // panic!( + // "Failed to copy resource from {} to {}: {}", + // src, output, err + // ); + // }); + // println!("[ASSET] Copied {} -> {}", src, output); + // } + // _ => {} + // }); } diff --git a/src/context/config/assets.rs b/src/context/config/assets.rs index 0ba8f5e..e1ca6cc 100644 --- a/src/context/config/assets.rs +++ b/src/context/config/assets.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] #[serde(default)] pub struct Assets { pub folder: String, diff --git a/src/context/config/mod.rs b/src/context/config/mod.rs index 2b7207e..ee9905d 100644 --- a/src/context/config/mod.rs +++ b/src/context/config/mod.rs @@ -8,7 +8,7 @@ mod paths; use assets::Assets; use paths::Paths; -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] #[serde(default)] pub struct Config { pub paths: Paths, diff --git a/src/context/config/paths.rs b/src/context/config/paths.rs index fb0d6b5..863490b 100644 --- a/src/context/config/paths.rs +++ b/src/context/config/paths.rs @@ -1,6 +1,8 @@ +use std::path::{Path, PathBuf}; + use serde::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, Clone)] #[serde(default)] pub struct Paths { pub workdir: String, @@ -17,3 +19,20 @@ impl Default for Paths { } } } + +impl Paths { + pub fn get_workdir(&self) -> PathBuf { + let path = Path::new(&self.workdir); + path.to_path_buf() + } + + pub fn get_page_path(&self) -> PathBuf { + let path = Path::new(&self.workdir).join(&self.pages); + path.to_path_buf() + } + + pub fn get_output_path(&self) -> PathBuf { + let path = Path::new(&self.workdir).join(&self.output); + path.to_path_buf() + } +} diff --git a/src/context/file.rs b/src/context/file.rs new file mode 100644 index 0000000..78204e8 --- /dev/null +++ b/src/context/file.rs @@ -0,0 +1,40 @@ +use std::{fs::read_to_string, path::PathBuf}; + +use crate::{ + ast2::{Expression, Statement}, + context::parser, +}; + +use super::Context; + +pub struct File { + pub is_page: bool, + + pub meta: Option, + pub ast: Vec, +} + +impl File { + pub fn new(ctx: Context, location: &PathBuf) -> Self { + let parser = ctx.parser.borrow(); + let content = read_to_string(&location); + println!("Loading file: {}", location.to_str().unwrap()); + + let content = if let Ok(content) = content { + content + } else { + panic!("Failed to read file: {}", location.to_str().unwrap()); + }; + + let result = parser.parse(content.as_str()); + if let Ok((meta, ast)) = result { + File { + is_page: false, + meta, + ast, + } + } else { + parser::error_message(location, result.err().unwrap(), &content); + } + } +} diff --git a/src/context/mod.rs b/src/context/mod.rs index ef2dfe2..9b618d0 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -1,20 +1,29 @@ -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; mod config; use config::Config; - +pub mod resource; +use resource::ResourceList; +pub mod parser; +mod resolver; use crate::grammar::DaisyParser; +pub use resolver::load_dir; + +mod file; + +const EXTENSION: &str = "ds"; +#[derive(Clone)] pub struct Context { - pub parser: DaisyParser, - pub resources: Vec>>, + pub parser: Rc>, + pub resources: ResourceList, pub config: Config, } impl Context { pub fn new() -> Self { Context { - parser: DaisyParser::new(), - resources: vec![], + parser: Rc::new(RefCell::new(DaisyParser::new())), + resources: HashMap::new(), config: Config::new(), } } diff --git a/src/context/parser.rs b/src/context/parser.rs index e69de29..b49ea81 100644 --- a/src/context/parser.rs +++ b/src/context/parser.rs @@ -0,0 +1,73 @@ +use std::path::Path; + +use crate::grammar::Token; +use lalrpop_util::ParseError; + +pub fn error_message(src: &Path, err: ParseError, content: &str) -> ! { + match err { + ParseError::InvalidToken { location } => { + let (line, column) = position_to_line_column(&content, location); + panic!( + "Invalid token at {}:{} in file {}", + line, + column, + src.display() + ); + } + ParseError::UnrecognizedEof { location, expected } => { + let (line, column) = position_to_line_column(&content, location); + panic!( + "Unrecognized EOF at {}:{} in file {}. Expected: {:?}", + line, + column, + src.display(), + expected + ); + } + ParseError::UnrecognizedToken { + token: (location, token, _), + expected, + } => { + let (line, column) = position_to_line_column(&content, location); + panic!( + "Unrecognized token '{}' at {}:{} in file {}. Expected: {:?}", + token, + line, + column, + src.display(), + expected + ); + } + ParseError::ExtraToken { token } => { + let (line, column) = position_to_line_column(&content, token.0); + panic!( + "Extra token '{}' at {}:{} in file {}", + token.1, + line, + column, + src.display(), + ); + } + ParseError::User { error } => { + panic!("User error: {} in file {}", error, src.display()); + } + } +} + +fn position_to_line_column(input: &str, pos: usize) -> (usize, usize) { + let mut line = 1; + let mut last_line_start = 0; + + for (i, c) in input.char_indices() { + if i >= pos { + break; + } + if c == '\n' { + line += 1; + last_line_start = i + 1; + } + } + + let column = pos - last_line_start + 1; + (line, column) +} diff --git a/src/context/resolver.rs b/src/context/resolver.rs index e69de29..886556a 100644 --- a/src/context/resolver.rs +++ b/src/context/resolver.rs @@ -0,0 +1,28 @@ +use walkdir::WalkDir; + +use super::resource::ResourceListExt; +use super::{resource::Resource, Context, EXTENSION}; + +pub fn load_dir(ctx: &mut Context) { + let config = &ctx.config; + WalkDir::new(config.paths.get_page_path()) + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.file_type().is_file() && entry.path().extension() == Some(EXTENSION.as_ref()) + }) + .for_each(|entry| { + let path = entry.path(); + let resource = ctx.resources.load(ctx.clone(), path.to_path_buf()); + let mut resource = resource.borrow_mut(); + + if let Resource::File(file) = &mut *resource { + file.is_page = true; + } else { + panic!( + "Expected a File resource, got {}", + entry.path().to_str().unwrap() + ); + } + }); +} diff --git a/src/context/resource.rs b/src/context/resource.rs new file mode 100644 index 0000000..00b38ee --- /dev/null +++ b/src/context/resource.rs @@ -0,0 +1,47 @@ +use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc}; + +use super::{file::File, Context}; + +pub enum Resource { + File(File), + Styling(String, String, String), + Other(String, String), +} + +pub type ResourceList = HashMap>>; + +pub trait ResourceListExt { + fn load(&mut self, ctx: Context, location: PathBuf) -> Rc>; + fn get_pages(&self) -> Vec>>; + fn get(&self) -> Vec>>; +} + +impl ResourceListExt for ResourceList { + fn load(&mut self, ctx: Context, location: PathBuf) -> Rc> { + let absolute_path = location.to_str().unwrap().to_string(); + if self.contains_key(&absolute_path) { + return self.get(&absolute_path).unwrap().clone(); + } + + let resource = Rc::new(RefCell::new(Resource::File(File::new( + ctx.clone(), + &location, + )))); + self.insert(absolute_path, resource.clone()); + resource + } + + fn get_pages(&self) -> Vec>> { + self.values() + .filter(|res| match &*res.borrow() { + Resource::File(file) => file.is_page, + _ => false, + }) + .cloned() + .collect() + } + + fn get(&self) -> Vec>> { + self.values().cloned().collect() + } +} diff --git a/src/context/resources.rs b/src/context/resources.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index f780195..2a61481 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -71,6 +71,7 @@ Keyword = { KeyVariableName = { KeyIdentifier, KeyMeta, + KeyTypeHtml, } // types @@ -163,7 +164,7 @@ ValueFloat: Value = => Value::Float(f); ValueBoolean: Value = => Value::Boolean(b); ValueNil: Value = KeyValueNil => Value::Nil; ValueFunction: Value = => Value::Function(Box::new((function.0.into(), function.1, function.2.into()))); -ValueHtml: Value = ":" => Value::Html(html); +ValueHtml: Value = => Value::Html(html); Value: Value = { ValueString, @@ -279,6 +280,10 @@ Statement: Statement = { // html NodeHtml: Node = { + ":" => node_body, +}; + +NodeBody: Node = { "{" "}" => node::html(Html::new(identifier.into()).with_attributes(attributes).with_children(children)), ">" => node::html(Html::new(identifier.into()).with_attributes(attributes).with_children(vec![child])), ";" => node::html(Html::new(identifier.into()).with_attributes(attributes)), @@ -294,14 +299,14 @@ NodeAttribute: (String, Vec) = { NodeText: Node = => node::text(string); -NodeInsert: Node = "@" "(" "}" => node::insertion(expression.into()); +NodeInsert: Node = "@" "{" "}" => node::insertion(expression.into()); Node: Node = { => node::logic_statement(for_loop), => node::logic_statement(iter_loop), => node::logic_statement(if_s), - NodeHtml, + NodeBody, NodeInsert, NodeText, } diff --git a/src/main.rs b/src/main.rs index 2fc94c5..70bbd1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,19 +3,17 @@ use lalrpop_util::lalrpop_mod; // mod ast; mod ast2; -// mod cli; +mod cli; mod context; mod parser; mod prelude; -// mod resolver; lalrpop_mod!(grammar); fn main() { let env = Env::default().filter_or("DAISY_LOG", "trace"); env_logger::init_from_env(env); - let ctx = context::Context::new(); - _ = ctx; + let mut ctx = context::Context::new(); - // cli::run(&mut ctx); + cli::run(&mut ctx); } From 5de9c81883f02cd2c7a5aad66d233df085128694 Mon Sep 17 00:00:00 2001 From: meir Date: Mon, 8 Sep 2025 14:55:53 +0200 Subject: [PATCH 5/6] wip --- examples/site_1.0/component/title.ds | 2 +- examples/site_1.0/component/title_holder.ds | 2 +- examples/site_1.0/src/index.ds | 2 +- src/ast2/builtin/mod.rs | 10 +++ src/ast2/builtin/std/format.rs | 41 +++++++++++ src/ast2/builtin/std/mod.rs | 4 ++ src/ast2/builtin/std/use.rs | 57 +++++++++++++++ src/ast2/environment/mod.rs | 43 +++++------ src/ast2/environment/scope.rs | 19 +++++ src/ast2/environment/stack.rs | 12 ++++ src/ast2/environment/type.rs | 1 + src/ast2/environment/value.rs | 8 ++- src/ast2/expression/call.rs | 15 ++-- src/ast2/expression/identifier.rs | 3 + src/ast2/expression/value.rs | 8 ++- src/ast2/functions/mod.rs | 21 +----- src/ast2/mod.rs | 1 + src/ast2/statement/call.rs | 10 ++- src/ast2/statement/iter.rs | 10 +-- src/cli/build.rs | 5 +- src/context/config/paths.rs | 44 ++++++++++-- src/context/resource.rs | 80 ++++++++++++++++++--- src/grammar.lalrpop | 3 +- src/lib.rs | 10 +++ src/prelude.rs | 1 + 25 files changed, 333 insertions(+), 79 deletions(-) create mode 100644 src/ast2/builtin/std/format.rs create mode 100644 src/ast2/builtin/std/mod.rs create mode 100644 src/ast2/builtin/std/use.rs diff --git a/examples/site_1.0/component/title.ds b/examples/site_1.0/component/title.ds index e27dc2d..a724479 100644 --- a/examples/site_1.0/component/title.ds +++ b/examples/site_1.0/component/title.ds @@ -1,7 +1,7 @@ func holder = use("component/title_holder") -return (str title) element { +return (str title) html { return : h1.imported { @{holder(title)} } diff --git a/examples/site_1.0/component/title_holder.ds b/examples/site_1.0/component/title_holder.ds index a428f18..2f390aa 100644 --- a/examples/site_1.0/component/title_holder.ds +++ b/examples/site_1.0/component/title_holder.ds @@ -2,5 +2,5 @@ str url = meta.url return (str title) str { - return std.format("{} - {}", title, url) + return format("{} - {}", title, url) } diff --git a/examples/site_1.0/src/index.ds b/examples/site_1.0/src/index.ds index d015dfc..9bd3a2d 100644 --- a/examples/site_1.0/src/index.ds +++ b/examples/site_1.0/src/index.ds @@ -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") diff --git a/src/ast2/builtin/mod.rs b/src/ast2/builtin/mod.rs index e69de29..1615d2e 100644 --- a/src/ast2/builtin/mod.rs +++ b/src/ast2/builtin/mod.rs @@ -0,0 +1,10 @@ +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); +} diff --git a/src/ast2/builtin/std/format.rs b/src/ast2/builtin/std/format.rs new file mode 100644 index 0000000..e983d41 --- /dev/null +++ b/src/ast2/builtin/std/format.rs @@ -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) -> 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"); + } + } +} diff --git a/src/ast2/builtin/std/mod.rs b/src/ast2/builtin/std/mod.rs new file mode 100644 index 0000000..b21b38b --- /dev/null +++ b/src/ast2/builtin/std/mod.rs @@ -0,0 +1,4 @@ +use crate::prelude::*; + +inherits!(r#use, [Use]); +inherits!(format, [Format]); diff --git a/src/ast2/builtin/std/use.rs b/src/ast2/builtin/std/use.rs new file mode 100644 index 0000000..5981728 --- /dev/null +++ b/src/ast2/builtin/std/use.rs @@ -0,0 +1,57 @@ +use crate::{ + ast2::{ + environment::Value, functions::FunctionRunner, statement::Result, Environment, Expression, + }, + context::{ + resource::{Resource, ResourceListExt}, + Context, + }, +}; + +pub struct Use; + +impl FunctionRunner for Use { + #[allow(unused_variables)] + fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec) -> Value { + if inputs.len() != 1 { + panic!("use() expects exactly one argument"); + } + + if let Value::String(import) = &inputs[0](ctx, env) { + let resource = ctx + .clone() + .resources + .load(ctx.clone(), ctx.config.paths.get_workdir().join(import)); + + let resource = &*resource.borrow(); + if let Resource::File(file) = resource { + if !file.is_page { + env.clone().subscope(|| { + for ast in &file.ast { + let result = ast(ctx, env); + match result { + Result::Return(result) => { + return result; + } + _ => {} + } + } + return Value::Nil; + }) + } else { + todo!("allow importing pages to import the output url") + } + } else { + let value = match resource { + Resource::Styling(_, output_path, _) => output_path.clone(), + Resource::Other(_, output_path) => output_path.clone(), + _ => panic!("Non implemented type"), + }; + + Value::String(format!("/{}", value)) + } + } else { + panic!("use() expects a string as argument"); + } + } +} diff --git a/src/ast2/environment/mod.rs b/src/ast2/environment/mod.rs index b75c98f..4dffa35 100644 --- a/src/ast2/environment/mod.rs +++ b/src/ast2/environment/mod.rs @@ -8,7 +8,7 @@ use std::rc::Rc; inherits!(r#type, [Type]); inherits!(value, [Value, TypeValue, CheckTypeValue]); -mod scope; +pub mod scope; use scope::QueryScopes; use scope::Scope; use scope::ScopeList; @@ -22,7 +22,6 @@ use stack::UseStack; pub struct Environment { stack: Stack, scopes: ScopeList, - current_scope: usize, } impl Environment { @@ -30,26 +29,34 @@ impl Environment { Environment { stack: Rc::new(RefCell::new(Vec::new())), scopes: Rc::new(RefCell::new(vec![Rc::new(RefCell::new(HashMap::new()))])), - current_scope: 0, } } + pub fn from_scopes(&self, scopes: ScopeList) -> Self { + Environment { + stack: self.stack.clone(), + scopes, + } + } + + pub fn clone_scopes(&self) -> ScopeList { + Rc::new(RefCell::new(self.scopes.borrow().clone())) + } + pub fn current_scope(&self) -> Scope { - self.scopes.borrow()[self.current_scope].clone() + self.scopes.borrow().last().unwrap().clone() } pub fn increase_scope(&mut self) { self.scopes .borrow_mut() .push(Rc::new(RefCell::new(HashMap::new()))); - self.current_scope += 1; } pub fn decrease_scope(&mut self) { - if self.current_scope > 0 { - self.scopes.borrow_mut().pop(); - self.current_scope -= 1; - self.clean(); + if self.scopes.borrow().len() > 0 { + let _scope = self.scopes.borrow_mut().pop(); + // scope.unwrap().clean(self.stack.clone()); } else { panic!("Cannot decrease scope, already at the global scope."); } @@ -65,17 +72,6 @@ impl Environment { result } - pub fn clean(&mut self) { - let in_use: Vec = self - .scopes - .borrow() - .iter() - .flat_map(|scope| scope.borrow().values().cloned().collect::>()) - .clone() - .collect(); - self.stack.clean(in_use); - } - pub fn define(&mut self, name: &str, value: TypeValue) { let index = self.stack.push(value); self.current_scope().define(name, index); @@ -118,6 +114,13 @@ impl Environment { pub fn as_vec(&self) -> Vec { self.as_map().into_values().collect() } + + #[allow(dead_code)] + pub fn debug(&self) { + println!("Environment:"); + self.current_scope().debug(); + self.stack.debug(); + } } impl Build for Environment { diff --git a/src/ast2/environment/scope.rs b/src/ast2/environment/scope.rs index 548913b..136b8dc 100644 --- a/src/ast2/environment/scope.rs +++ b/src/ast2/environment/scope.rs @@ -1,10 +1,16 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use super::stack::Stack; + pub type Scope = Rc, usize>>>; pub type ScopeList = Rc>>; pub trait UseScope { fn define(&mut self, name: &str, index: usize); + fn clean(&self, stack: Stack); + + #[allow(dead_code)] + fn debug(&self); } impl UseScope for Scope { @@ -14,6 +20,19 @@ impl UseScope for Scope { } self.borrow_mut().insert(name.into(), index); } + + fn clean(&self, stack: Stack) { + for (_, value) in self.borrow().iter() { + stack.borrow_mut().remove(*value); + } + } + + fn debug(&self) { + println!("Scope Debug:"); + for (key, value) in self.borrow().iter() { + println!(" {} => {}", key, value); + } + } } pub trait QueryScopes { diff --git a/src/ast2/environment/stack.rs b/src/ast2/environment/stack.rs index dc79b3f..1bf9434 100644 --- a/src/ast2/environment/stack.rs +++ b/src/ast2/environment/stack.rs @@ -11,6 +11,9 @@ pub trait UseStack { fn push(&mut self, value: TypeValue) -> usize; fn remove(&mut self, index: usize) -> Option; fn clean(&mut self, in_use: Vec); + + #[allow(dead_code)] + fn debug(&self); } impl UseStack for Stack { @@ -56,4 +59,13 @@ impl UseStack for Stack { } } } + + fn debug(&self) { + println!("Stack:"); + for (i, v) in self.borrow().iter().enumerate() { + let t = &v.borrow().0; + let value = &v.borrow().1; + println!(" [{}] {} = {}", i, t, value); + } + } } diff --git a/src/ast2/environment/type.rs b/src/ast2/environment/type.rs index ceb2a93..323afe3 100644 --- a/src/ast2/environment/type.rs +++ b/src/ast2/environment/type.rs @@ -5,6 +5,7 @@ use std::{ rc::Rc, }; +#[allow(dead_code)] #[derive(Clone)] pub enum Type { String, diff --git a/src/ast2/environment/value.rs b/src/ast2/environment/value.rs index 73c0b10..56e817f 100644 --- a/src/ast2/environment/value.rs +++ b/src/ast2/environment/value.rs @@ -1,10 +1,12 @@ use std::fmt::{Display, Formatter, Result}; +use std::sync::Arc; use std::{cell::RefCell, rc::Rc}; use crate::ast2::functions::FunctionRunner; use crate::ast2::Node; use crate::{ast2::Build, context::Context}; +use super::scope::ScopeList; use super::{Environment, Type}; pub type TypeValue = Rc>; @@ -19,7 +21,7 @@ impl CheckTypeValue for TypeValue { let t = &typevalue.0; let value = &typevalue.1; if !t.matches(value) { - panic!("Type mismatch: expected {}, found {}", "a", "b"); + panic!("Type mismatch: expected {}, found {}", t, value); } } } @@ -31,7 +33,7 @@ pub enum Value { Float(f64), Boolean(bool), Html(Node), - Function(Box), + Function(Arc, Option), Map(Environment), List(Environment), Nil, @@ -45,7 +47,7 @@ impl Value { Value::Float(_) => Type::Float, Value::Boolean(_) => Type::Boolean, Value::Html(_) => Type::Html, - Value::Function(_) => Type::Function, + Value::Function(..) => Type::Function, Value::Map(_) => Type::Map, Value::List(_) => Type::List, Value::Nil => Type::Nil, diff --git a/src/ast2/expression/call.rs b/src/ast2/expression/call.rs index 69f04c1..d267188 100644 --- a/src/ast2/expression/call.rs +++ b/src/ast2/expression/call.rs @@ -1,5 +1,3 @@ -use log::warn; - use crate::ast2::environment::Value; use crate::ast2::expression::Expression; @@ -7,10 +5,17 @@ pub fn call(value: Expression, arguments: Vec) -> Expression { Box::new(move |ctx, env| { let function = value(ctx, env); match function { - Value::Function(function) => function.call(ctx, env, &arguments), + Value::Function(function, scopes) => { + let env = if let Some(scopes) = scopes { + env.from_scopes(scopes) + } else { + env.clone() + }; + + function.call(ctx, &env, &arguments) + } _ => { - warn!("Expected a function, got {}", function); - Value::Nil + panic!("Expected a function, got {}", function); } } }) diff --git a/src/ast2/expression/identifier.rs b/src/ast2/expression/identifier.rs index 576d35e..6152ca6 100644 --- a/src/ast2/expression/identifier.rs +++ b/src/ast2/expression/identifier.rs @@ -1,3 +1,5 @@ +use log::warn; + use crate::ast2::environment::Value; use crate::ast2::expression::Expression; @@ -24,6 +26,7 @@ pub fn identifier(location: Vec) -> Expression { if let Some(tv) = current { tv.borrow().1.clone() } else { + warn!("Identifier not found: {}", location.join(".")); Value::Nil } }) diff --git a/src/ast2/expression/value.rs b/src/ast2/expression/value.rs index 6d41a8f..b847cf8 100644 --- a/src/ast2/expression/value.rs +++ b/src/ast2/expression/value.rs @@ -1,7 +1,9 @@ -use crate::ast2::environment::Value; - use super::Expression; +use crate::ast2::environment::Value; pub fn value(value: Value) -> Expression { - Box::new(move |_ctx, _env| value.clone()) + Box::new(move |_ctx, env| match &value { + Value::Function(func, _) => Value::Function(func.clone(), Some(env.clone_scopes())), + _ => value.clone(), + }) } diff --git a/src/ast2/functions/mod.rs b/src/ast2/functions/mod.rs index 4b8f203..7b45259 100644 --- a/src/ast2/functions/mod.rs +++ b/src/ast2/functions/mod.rs @@ -16,29 +16,10 @@ pub type FunctionValue = ( FunctionBody, ); -pub trait FunctionRunner: FunctionRunnerClone { +pub trait FunctionRunner { fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec) -> Value; } -pub trait FunctionRunnerClone { - fn clone_box(&self) -> Box; -} - -impl FunctionRunnerClone for T -where - T: FunctionRunner + Clone + 'static, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Box { - self.as_ref().clone_box() - } -} - impl FunctionRunner for FunctionValue { fn call(&self, ctx: &Context, env: &Environment, inputs: &Vec) -> Value { let (parameters, return_type, body) = self; diff --git a/src/ast2/mod.rs b/src/ast2/mod.rs index 5a5017b..f16cd99 100644 --- a/src/ast2/mod.rs +++ b/src/ast2/mod.rs @@ -1,3 +1,4 @@ +pub mod builtin; pub mod environment; pub mod expression; pub mod functions; diff --git a/src/ast2/statement/call.rs b/src/ast2/statement/call.rs index 5423524..8035e98 100644 --- a/src/ast2/statement/call.rs +++ b/src/ast2/statement/call.rs @@ -8,8 +8,14 @@ pub fn call(value: Expression, arguments: Vec) -> Statement { Box::new(move |ctx, env| { let function = value(ctx, env); match function { - Value::Function(function) => { - function.call(ctx, env, &arguments); + Value::Function(function, scopes) => { + let env = if let Some(scopes) = scopes { + env.from_scopes(scopes) + } else { + env.clone() + }; + + function.call(ctx, &env, &arguments); } _ => warn!("Expected a function, got {}", function), } diff --git a/src/ast2/statement/iter.rs b/src/ast2/statement/iter.rs index f1965c4..1ef63a1 100644 --- a/src/ast2/statement/iter.rs +++ b/src/ast2/statement/iter.rs @@ -1,12 +1,12 @@ use super::{Result, Statement}; -use crate::ast2::{environment::Value, expression::Expression}; +use crate::ast2::expression::Expression; pub fn iter( - identifiers: (String, Option), - iterable: Expression, - body: Vec, + _identifiers: (String, Option), + _iterable: Expression, + _body: Vec, ) -> Statement { - Box::new(move |ctx, env| { + Box::new(move |_ctx, env| { env.clone().subscope(|| { Result::NOP // let (key_name, value_name) = identifiers.clone(); diff --git a/src/cli/build.rs b/src/cli/build.rs index 8e957b2..816377d 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -1,5 +1,5 @@ use crate::ast2::statement::Result; -use crate::ast2::{Build, Environment}; +use crate::ast2::{builtin, Build, Environment}; use crate::context::resource::{Resource, ResourceListExt}; use crate::{context, context::Context}; @@ -8,7 +8,7 @@ pub fn build(ctx: &mut Context) { ctx.resources.get_pages().iter().for_each(|resource| { if let Resource::File(file) = &*resource.borrow() { - let environment = if let Some(meta) = &file.meta { + let mut environment = if let Some(meta) = &file.meta { let meta = meta(ctx, &Environment::new()); let mut env = Environment::new(); env.set("meta", meta.as_typevalue()); @@ -16,6 +16,7 @@ pub fn build(ctx: &mut Context) { } else { Environment::new() }; + builtin::populate(&mut environment); for ast in &file.ast { let result = ast(ctx, &environment); diff --git a/src/context/config/paths.rs b/src/context/config/paths.rs index 863490b..049f3b9 100644 --- a/src/context/config/paths.rs +++ b/src/context/config/paths.rs @@ -22,17 +22,49 @@ impl Default for Paths { impl Paths { pub fn get_workdir(&self) -> PathBuf { - let path = Path::new(&self.workdir); - path.to_path_buf() + let mut current_dir = std::env::current_dir().unwrap(); + current_dir = current_dir.join(&self.workdir); + current_dir + .to_path_buf() + .canonicalize() + .unwrap_or(current_dir.to_path_buf()) } pub fn get_page_path(&self) -> PathBuf { - let path = Path::new(&self.workdir).join(&self.pages); - path.to_path_buf() + let path = self.get_workdir().join(&self.pages); + path.canonicalize().unwrap_or(path) } pub fn get_output_path(&self) -> PathBuf { - let path = Path::new(&self.workdir).join(&self.output); - path.to_path_buf() + let path = self.get_workdir().join(&self.output); + path.canonicalize().unwrap_or(path) + } + + pub fn get_absolute_filepath(&self, file: &PathBuf) -> PathBuf { + let path = self.get_workdir().join(file); + path.canonicalize().unwrap_or(path) + } + + pub fn generate_output_path(&self, file: &PathBuf) -> PathBuf { + let relative_path = file + .strip_prefix(&self.get_page_path()) + .unwrap_or(file) + .strip_prefix(&self.get_workdir()) + .unwrap_or(file); + + let relative_parent = relative_path.parent().unwrap_or(Path::new("")); + let file_stem = relative_path.file_stem().unwrap_or_default(); + + match file.extension().and_then(|ext| ext.to_str()) { + Some("ds") => { + if file_stem == "index" { + relative_parent.join("index.html") + } else { + relative_parent.join(file_stem).join("index.html") + } + } + Some(ext) => relative_parent.join(format!("{}.{}", file_stem.to_str().unwrap(), ext)), + None => self.get_output_path().join(relative_path).join(file_stem), + } } } diff --git a/src/context/resource.rs b/src/context/resource.rs index 00b38ee..07d8919 100644 --- a/src/context/resource.rs +++ b/src/context/resource.rs @@ -1,4 +1,10 @@ -use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc}; +use std::{ + cell::RefCell, + collections::HashMap, + hash::{DefaultHasher, Hash, Hasher}, + path::PathBuf, + rc::Rc, +}; use super::{file::File, Context}; @@ -18,16 +24,72 @@ pub trait ResourceListExt { impl ResourceListExt for ResourceList { fn load(&mut self, ctx: Context, location: PathBuf) -> Rc> { - let absolute_path = location.to_str().unwrap().to_string(); - if self.contains_key(&absolute_path) { - return self.get(&absolute_path).unwrap().clone(); + let absolute_path = ctx.config.paths.get_absolute_filepath(&location); + if self.contains_key(absolute_path.to_str().unwrap()) { + return self.get(absolute_path.to_str().unwrap()).unwrap().clone(); } - let resource = Rc::new(RefCell::new(Resource::File(File::new( - ctx.clone(), - &location, - )))); - self.insert(absolute_path, resource.clone()); + let build_location = ctx.config.paths.generate_output_path(&absolute_path); + + let resource = match &location.extension().and_then(|ext| ext.to_str()) { + Some("ds") => Rc::new(RefCell::new(Resource::File(File::new( + ctx.clone(), + &location, + )))), + Some("scss") | Some("sass") => { + let content = std::fs::read_to_string(&absolute_path) + .map_err(|_| { + format!( + "Failed to read SCSS file: {}", + absolute_path.to_str().unwrap() + ) + }) + .unwrap(); + let css = grass::from_string(content.clone(), &grass::Options::default()) + .map_err(|err| { + format!( + "Failed to compile SCSS file: {}: {}", + absolute_path.to_str().unwrap(), + err + ) + }) + .unwrap(); + let name = location.file_stem().unwrap().to_str().unwrap(); + let mut hasher = DefaultHasher::new(); + content.hash(&mut hasher); + let hash = hasher.finish(); + let name = format!("{}-{}.css", name, hash); + + Rc::new(RefCell::new(Resource::Styling( + absolute_path.to_str().unwrap().to_string(), + format!( + "{}/{}", + build_location.parent().unwrap().to_str().unwrap(), + name + ), + css.to_string(), + ))) + } + Some(_) => Rc::new(RefCell::new(Resource::Other( + absolute_path.to_str().unwrap().to_string(), + build_location.to_str().unwrap().to_string(), + ))), + None => { + if location.with_extension("ds").exists() { + self.load(ctx, location.with_extension("ds")) + } else { + Rc::new(RefCell::new(Resource::Other( + absolute_path.to_str().unwrap().to_string(), + build_location.to_str().unwrap().to_string(), + ))) + } + } + }; + + self.insert( + absolute_path.to_str().unwrap().to_string(), + resource.clone(), + ); resource } diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index 2a61481..3d89126 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -1,4 +1,5 @@ use std::str::FromStr; +use std::sync::Arc; use crate::ast2::environment::{Type, Value}; use crate::ast2::{expression, expression::Expression}; use crate::ast2::{statement, statement::Statement}; @@ -163,7 +164,7 @@ ValueNumber: Value = => Value::Number(n); ValueFloat: Value = => Value::Float(f); ValueBoolean: Value = => Value::Boolean(b); ValueNil: Value = KeyValueNil => Value::Nil; -ValueFunction: Value = => Value::Function(Box::new((function.0.into(), function.1, function.2.into()))); +ValueFunction: Value = => Value::Function(Arc::new((function.0.into(), function.1, function.2.into())), None); ValueHtml: Value = => Value::Html(html); Value: Value = { diff --git a/src/lib.rs b/src/lib.rs index 3c82c64..9ff2ccf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,3 +15,13 @@ macro_rules! inherits { )+ }; } + +#[macro_export] +macro_rules! define_function { + ($environment:ident, $name:expr, $function:expr) => { + $environment.set( + $name, + Value::Function(Arc::new($function), None).as_typevalue(), + ); + }; +} diff --git a/src/prelude.rs b/src/prelude.rs index d1cc662..cce3fd5 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,2 +1,3 @@ +pub use daisy::define_function; pub use daisy::inherit; pub use daisy::inherits; From 4cc7231d8b4b30764d1efe5894775a0f9272f47b Mon Sep 17 00:00:00 2001 From: meir Date: Tue, 30 Sep 2025 14:59:07 +0200 Subject: [PATCH 6/6] fix node scope issue --- examples/site_1.0/src/test.ds | 15 +++++++--- src/ast2/builtin/mod.rs | 3 ++ src/ast2/builtin/std/keys.rs | 36 +++++++++++++++++++++++ src/ast2/builtin/std/len.rs | 26 ++++++++++++++++ src/ast2/builtin/std/mod.rs | 3 ++ src/ast2/builtin/std/println.rs | 22 ++++++++++++++ src/ast2/environment/mod.rs | 4 +++ src/ast2/environment/value.rs | 14 +++++++-- src/ast2/expression/comparisons/lesser.rs | 7 +++-- src/ast2/expression/value.rs | 1 + src/ast2/statement/for.rs | 5 ++-- src/cli/build.rs | 24 ++------------- src/context/config/paths.rs | 2 +- src/context/file.rs | 34 ++++++++++++++++++++- src/grammar.lalrpop | 2 +- 15 files changed, 163 insertions(+), 35 deletions(-) create mode 100644 src/ast2/builtin/std/keys.rs create mode 100644 src/ast2/builtin/std/len.rs create mode 100644 src/ast2/builtin/std/println.rs diff --git a/examples/site_1.0/src/test.ds b/examples/site_1.0/src/test.ds index 326b94c..2bf699c 100644 --- a/examples/site_1.0/src/test.ds +++ b/examples/site_1.0/src/test.ds @@ -22,10 +22,17 @@ 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 = [ @@ -33,7 +40,7 @@ list x = [ "world" ] for _, value in x { - std.println(value) + println(value) } return : html { diff --git a/src/ast2/builtin/mod.rs b/src/ast2/builtin/mod.rs index 1615d2e..289e529 100644 --- a/src/ast2/builtin/mod.rs +++ b/src/ast2/builtin/mod.rs @@ -7,4 +7,7 @@ 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); } diff --git a/src/ast2/builtin/std/keys.rs b/src/ast2/builtin/std/keys.rs new file mode 100644 index 0000000..7b05d79 --- /dev/null +++ b/src/ast2/builtin/std/keys.rs @@ -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) -> 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"); + } + } +} diff --git a/src/ast2/builtin/std/len.rs b/src/ast2/builtin/std/len.rs new file mode 100644 index 0000000..a6f4b4c --- /dev/null +++ b/src/ast2/builtin/std/len.rs @@ -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) -> 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"); + } + } +} diff --git a/src/ast2/builtin/std/mod.rs b/src/ast2/builtin/std/mod.rs index b21b38b..3e6f4cd 100644 --- a/src/ast2/builtin/std/mod.rs +++ b/src/ast2/builtin/std/mod.rs @@ -2,3 +2,6 @@ use crate::prelude::*; inherits!(r#use, [Use]); inherits!(format, [Format]); +inherits!(println, [PrintLn]); +inherits!(len, [Len]); +inherits!(keys, [Keys]); diff --git a/src/ast2/builtin/std/println.rs b/src/ast2/builtin/std/println.rs new file mode 100644 index 0000000..171404f --- /dev/null +++ b/src/ast2/builtin/std/println.rs @@ -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) -> 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 + } +} diff --git a/src/ast2/environment/mod.rs b/src/ast2/environment/mod.rs index 4dffa35..73fa615 100644 --- a/src/ast2/environment/mod.rs +++ b/src/ast2/environment/mod.rs @@ -111,6 +111,10 @@ impl Environment { map } + pub fn exists(&self, name: &str) -> bool { + self.scopes.exists(name).is_some() + } + pub fn as_vec(&self) -> Vec { self.as_map().into_values().collect() } diff --git a/src/ast2/environment/value.rs b/src/ast2/environment/value.rs index 56e817f..663aa2d 100644 --- a/src/ast2/environment/value.rs +++ b/src/ast2/environment/value.rs @@ -32,7 +32,7 @@ pub enum Value { Number(i64), Float(f64), Boolean(bool), - Html(Node), + Html(Node, Option), Function(Arc, Option), Map(Environment), List(Environment), @@ -46,7 +46,7 @@ impl Value { Value::Number(_) => Type::Number, Value::Float(_) => Type::Float, Value::Boolean(_) => Type::Boolean, - Value::Html(_) => Type::Html, + Value::Html(..) => Type::Html, Value::Function(..) => Type::Function, Value::Map(_) => Type::Map, Value::List(_) => Type::List, @@ -67,7 +67,15 @@ impl Build for Value { Value::Number(n) => n.to_string(), Value::Float(n) => n.to_string(), Value::Boolean(b) => b.to_string(), - Value::Html(node) => node(ctx, env), + Value::Html(node, scopes) => { + let env = if let Some(scopes) = scopes { + env.from_scopes(scopes.clone()) + } else { + env.clone() + }; + + node(ctx, &env) + } Value::Function(..) => "function".to_string(), Value::Map(sub_env) => sub_env.build(ctx, env), Value::List(sub_env) => sub_env.build(ctx, env), diff --git a/src/ast2/expression/comparisons/lesser.rs b/src/ast2/expression/comparisons/lesser.rs index cf2a2a4..edc29c5 100644 --- a/src/ast2/expression/comparisons/lesser.rs +++ b/src/ast2/expression/comparisons/lesser.rs @@ -6,11 +6,14 @@ pub fn lesser(left: Expression, right: Expression) -> Expression { let left_value = left(ctx, scope); let right_value = right(ctx, scope); - match (left_value, right_value) { + match (&left_value, &right_value) { (Value::Number(l), Value::Number(r)) => Value::Boolean(l < r), (Value::String(l), Value::String(r)) => Value::Boolean(l < r), (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l < r), - _ => panic!("Cannot compare values of different types or unsupported types"), + _ => panic!( + "Cannot compare values of different types or unsupported types: {} & {}", + left_value, right_value + ), } }) } diff --git a/src/ast2/expression/value.rs b/src/ast2/expression/value.rs index b847cf8..7c474a3 100644 --- a/src/ast2/expression/value.rs +++ b/src/ast2/expression/value.rs @@ -4,6 +4,7 @@ use crate::ast2::environment::Value; pub fn value(value: Value) -> Expression { Box::new(move |_ctx, env| match &value { Value::Function(func, _) => Value::Function(func.clone(), Some(env.clone_scopes())), + Value::Html(node, _) => Value::Html(node.clone(), Some(env.clone_scopes())), _ => value.clone(), }) } diff --git a/src/ast2/statement/for.rs b/src/ast2/statement/for.rs index 29d1f01..70785bc 100644 --- a/src/ast2/statement/for.rs +++ b/src/ast2/statement/for.rs @@ -21,6 +21,7 @@ pub fn for_s( let result = stmt(ctx, env); match result { Result::Continue => { + increment(ctx, env); continue 'mainloop; } Result::Collect(values) => { @@ -31,9 +32,9 @@ pub fn for_s( return result; } } - - increment(ctx, env); } + + increment(ctx, env); } if collected_values.is_empty() { diff --git a/src/cli/build.rs b/src/cli/build.rs index 816377d..a7cf88f 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -1,4 +1,3 @@ -use crate::ast2::statement::Result; use crate::ast2::{builtin, Build, Environment}; use crate::context::resource::{Resource, ResourceListExt}; use crate::{context, context::Context}; @@ -8,26 +7,9 @@ pub fn build(ctx: &mut Context) { ctx.resources.get_pages().iter().for_each(|resource| { if let Resource::File(file) = &*resource.borrow() { - let mut environment = if let Some(meta) = &file.meta { - let meta = meta(ctx, &Environment::new()); - let mut env = Environment::new(); - env.set("meta", meta.as_typevalue()); - env - } else { - Environment::new() - }; - builtin::populate(&mut environment); - - for ast in &file.ast { - let result = ast(ctx, &environment); - match result { - Result::Return(result) => { - let value = result.build(ctx, &environment); - println!("{}", value) - } - _ => {} - } - } + println!("[DAISY] Building page {}", file.src.to_str().unwrap()); + let built = file.build(ctx); + println!("{}", built); } else { panic!("Expected a File resource for page"); } diff --git a/src/context/config/paths.rs b/src/context/config/paths.rs index 049f3b9..e09d842 100644 --- a/src/context/config/paths.rs +++ b/src/context/config/paths.rs @@ -64,7 +64,7 @@ impl Paths { } } Some(ext) => relative_parent.join(format!("{}.{}", file_stem.to_str().unwrap(), ext)), - None => self.get_output_path().join(relative_path).join(file_stem), + None => relative_path.to_path_buf(), } } } diff --git a/src/context/file.rs b/src/context/file.rs index 78204e8..241a1fe 100644 --- a/src/context/file.rs +++ b/src/context/file.rs @@ -1,5 +1,8 @@ use std::{fs::read_to_string, path::PathBuf}; +use crate::ast2::statement::Result; +use crate::ast2::{builtin, Build, Environment}; + use crate::{ ast2::{Expression, Statement}, context::parser, @@ -10,6 +13,7 @@ use super::Context; pub struct File { pub is_page: bool, + pub src: PathBuf, pub meta: Option, pub ast: Vec, } @@ -18,7 +22,6 @@ impl File { pub fn new(ctx: Context, location: &PathBuf) -> Self { let parser = ctx.parser.borrow(); let content = read_to_string(&location); - println!("Loading file: {}", location.to_str().unwrap()); let content = if let Ok(content) = content { content @@ -30,6 +33,8 @@ impl File { if let Ok((meta, ast)) = result { File { is_page: false, + + src: location.to_path_buf(), meta, ast, } @@ -37,4 +42,31 @@ impl File { parser::error_message(location, result.err().unwrap(), &content); } } + + pub fn build(&self, ctx: &Context) -> String { + let mut environment = if let Some(meta) = &self.meta { + let meta = meta(ctx, &Environment::new()); + let mut env = Environment::new(); + env.set("meta", meta.as_typevalue()); + env + } else { + Environment::new() + }; + + builtin::populate(&mut environment); + + for ast in &self.ast { + let result = ast(ctx, &environment); + match result { + Result::Return(result) => { + let value = result.build(ctx, &environment); + return value; + } + _ => { + continue; + } + } + } + return "".to_string(); + } } diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index 3d89126..61db209 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -165,7 +165,7 @@ ValueFloat: Value = => Value::Float(f); ValueBoolean: Value = => Value::Boolean(b); ValueNil: Value = KeyValueNil => Value::Nil; ValueFunction: Value = => Value::Function(Arc::new((function.0.into(), function.1, function.2.into())), None); -ValueHtml: Value = => Value::Html(html); +ValueHtml: Value = => Value::Html(html, None); Value: Value = { ValueString,