From c5f71afa5c17247356f94f493362d8dd93210a0c Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Wed, 9 Apr 2025 02:38:29 +0300 Subject: [PATCH] Daily update. I was almost sure that I have a woring idea, but no, I forgot to pass reference to source code tree to runtime. How do I even do that? --- mtgott/src/dirsearch.rs | 8 +- mtgott/src/lambda_compilation.rs | 273 +++++++++++++++++++++++++++++-- mtgott/src/runtime.rs | 42 +++-- 3 files changed, 295 insertions(+), 28 deletions(-) diff --git a/mtgott/src/dirsearch.rs b/mtgott/src/dirsearch.rs index e1e3ceb..9c4b384 100644 --- a/mtgott/src/dirsearch.rs +++ b/mtgott/src/dirsearch.rs @@ -3,7 +3,7 @@ use std::fs::{read_dir, metadata, canonicalize}; use std::path::PathBuf; use std::error::Error; use super::charclasses::{escape_for_html, is_bad_name}; -use super::runtime::{HigherLevelFunc, Value, DebugState}; +use super::runtime::{HigherLevelFunc, Value, RuntimeEnv}; use super::parser::{parse_one_file_simplified, parse_one_file_packed}; use super::lambda_compilation::{plemege_to_value}; use std::collections::HashMap; @@ -105,7 +105,7 @@ pub fn get_all_templates(p: &str, plain_ext: &str) -> Result Result Result>{ get_all_templates_plus_builtins(p, ".html", Box::new( - |d_state: &DebugState, _: &Value, arg: &Value| -> Result> { + |d_state: &RuntimeEnv, _: &Value, arg: &Value, _: u32| -> Result { let _g = d_state.register("In sanitizer".into()); match arg { Value::Str(s) => Ok(Value::Str(escape_for_html(&s))), diff --git a/mtgott/src/lambda_compilation.rs b/mtgott/src/lambda_compilation.rs index c38fec6..335cdec 100644 --- a/mtgott/src/lambda_compilation.rs +++ b/mtgott/src/lambda_compilation.rs @@ -2,30 +2,283 @@ use super::runtime::*; use super::parser::*; use std::error::Error; use std::collections::HashMap; +use std::ops::Deref; -fn cat_vec(arr: Vec) -> String { - let total_len: usize = arr.iter().map(|s| { s.len() }).sum(); - arr.into_iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc }) +struct ElementPartsCollector { + res: String, cur_col: usize, } -fn compile(el: &[SubElement]) -> Result, Box> { - return Err("sd".into()) +impl ElementPartsCollector { + fn without_pad(&mut self, thp: &str) { + self.res += thp; + for ch in thp.chars() { + if ch == '\n' { self.cur_col = 0; } else { self.cur_col += 1; } + } + } + + fn add_padded_element_piece(&mut self, offset: usize, thp: &str){ + for ch in thp.chars() { + self.res.push(ch); + if ch == '\n' { + self.res.push_str(&" ".repeat(offset)); + self.cur_col = offset; + } else { + self.cur_col += 1; + } + } + } + + fn add_single_padded_element_piece(&mut self, thp: &str) { + self.add_padded_element_piece(self.cur_col, thp); + } +} + +impl RuntimeEnv { + fn value_to_bool(&self, val: &Value) -> Result { + match val { + Value::Int(num) => Ok(*num != 0), + Value::Str(str) => Ok(str.len() > 0), + Value::Arr(arr) => Ok(arr.len() > 0), + Value::Dict(dict) => Ok(dict.len() > 0), + Value::Fn(_) => Err(self.make_error("Can't convert function to bool")), + _ => panic!("Get rid of runtime strings in expressions") + } + } + + fn get_needed_if_block<'i>( + &self, if_sub_el: &'i IfSubElement, root: &Value, arg_stack: &[&Value], recc: u32 + ) -> Result, String> { + let n = if_sub_el.conditions.len(); + for i in 0..n { + let expr_res = self.execute_expression(&if_sub_el.conditions[i], root, arg_stack, recc - 1)?; + if self.value_to_bool(expr_res.deref())? { + return Ok(Some(&if_sub_el.branches[i])); + } + } + if if_sub_el.branches.len() > n { + Ok(Some(&if_sub_el.branches[n])) + } else { + Ok(None) + } + } + + fn execute_element_block<'r, 'aa>( + &self, el: &[SubElement], root: &'r Value, arg_stack: &[&'aa Value], recc: u32 + ) -> Result { + if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) } + let mut res = ElementPartsCollector{res: String::new(), cur_col: 0}; + for se in el { + match se { + SubElement::Static(ps) => { + res.without_pad(ps); + }, + SubElement::If(if_sub_el) => + if let Some(branch_el) = self.get_needed_if_block(if_sub_el, root, arg_stack, recc - 1)? { + res.add_single_padded_element_piece( + &self.execute_element_block( + &branch_el.sub_elements, root, arg_stack, recc - 1)?) + } + , + SubElement::InsertExpr(expr) => res.add_single_padded_element_piece( + match self.execute_expression(expr, root, arg_stack, recc - 1)?.deref() { + Value::Str(str) => str, + _ => return Err(self.make_error("Cannot insert non-string. Try using {{}}")), + }), + SubElement::For(ForSubElement{iterable, core, join}) => { + let _g = self.register("In {%for%}".into()); + let expr_val = self.execute_expression(iterable, root, arg_stack, recc - 1)?; + let an = arg_stack.len(); + let saved_offset = res.cur_col; + match expr_val.deref() { + Value::Dict(dict) => { + let mut it = dict.iter(); + for i in 0usize.. { + if let Some((key, value)) = it.next() { + let itv = Value::Str(key.clone()); + let mut arg_stack_updated: Vec<&Value> = Vec::with_capacity(an + 2); + arg_stack_updated.extend_from_slice(arg_stack); + arg_stack_updated.push(&itv); + arg_stack_updated.push(value); + if i > 0 {res.add_padded_element_piece(saved_offset, join); } + res.add_padded_element_piece( + saved_offset, &self.execute_element_block( + &core.sub_elements, root, arg_stack, recc - 1)?); + } else { break } + } + } + Value::Arr(array) => { + for i in 0..array.len() { + let itv = Value::Int(i as u64); + let mut arg_stack_updated: Vec<&Value> = Vec::with_capacity(an + 2); + arg_stack_updated.extend_from_slice(arg_stack); + arg_stack_updated.push(&itv); + arg_stack_updated.push(&array[i]); + if i > 0 { res.add_padded_element_piece(saved_offset, join); } + res.add_padded_element_piece( + saved_offset, &self.execute_element_block( + &core.sub_elements, root, arg_stack, recc - 1)?); + } + } + _ => return Err(self.make_error(&format!("Can't iterate over {}", expr_val.deref().stringify_type()))) + } + } + SubElement::Let(expr, core) => { + let expr_val = self.execute_expression(expr, root, arg_stack, recc - 1)?; + let mut arg_stack_updated = Vec::with_capacity(arg_stack.len() + 1); + arg_stack_updated.extend_from_slice(arg_stack); + arg_stack_updated.push(expr_val.deref()); + res.add_single_padded_element_piece(&self.execute_element_block( + &core.sub_elements, root, arg_stack, recc - 1)?); + } + }; + } + Ok(res.res) + } +} + +enum ExpressionResult<'l> { + LocalRef(&'l Value), + Owned(Value), +} + + +impl<'l> Deref for ExpressionResult<'l> { + type Target = Value; + + fn deref(&self) -> &Self::Target { + match self { + ExpressionResult::LocalRef(val) => val, + ExpressionResult::Owned(val) => val, + } + } +} + +impl RuntimeEnv { + fn in_expression_index_by_key<'l>(&self, mut base: ExpressionResult<'l>, key: &String) -> Result, String> { + match base { + ExpressionResult::LocalRef(Value::Dict(dictionary )) => { + if let Some(el) = dictionary.get(key) { + Ok(ExpressionResult::LocalRef(el)) + } else { + Err(self.make_error(&format!("Dictionary doesn't have key {}", key))) + } + }, + ExpressionResult::Owned(Value::Dict(mut dictionary)) => { + /* I can't think of a single use case for this */ + if let Some(el) = dictionary.get_mut(key) { + Ok(ExpressionResult::Owned(std::mem::take(el))) + } else { + Err(self.make_error(&format!("Locally created dictionary doesn't have key {}", key))) + } + } + _ => Err(self.make_error(&format!("Can't take attribute {} (or really any attribute) from {}", key, base.deref().stringify_type()))) + } + } + + fn execute_expression<'l>( + &self, expr: &Expression, root: &'l Value, arg_stack: &[&'l Value], recc: u32 + ) -> Result, String> { + if recc == 0 { return Err(self.make_error("Recursion limit exceeded".into())) } + let f1: ExpressionResult<'l> = match expr { + Expression::Root => ExpressionResult::LocalRef(root), + Expression::Argument(id) => ExpressionResult::LocalRef(arg_stack[*id as usize]), + Expression::Get(e1, e2) => { + let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, root, arg_stack, recc - 1)?; + let r2: ExpressionResult = self.execute_expression(&e2, root, arg_stack, recc - 1)?; + match (r1, r2.deref()) { + (ExpressionResult::LocalRef(Value::Arr(arr)), &Value::Int(index)) => { + if index as usize > arr.len() { + return Err(self.make_error(&format!( + "Indexing array of size {} at position {}", arr.len(), index))) + } + ExpressionResult::LocalRef(&arr[index as usize]) + }, + (ExpressionResult::Owned(Value::Arr(mut arr)), &Value::Int(index)) => { + /* I can't think of a single use case for this */ + if index as usize > arr.len() { + return Err(self.make_error(&format!( + "Indexing locally created array of size {} at position {}", arr.len(), index))) + } + ExpressionResult::Owned(std::mem::take(&mut arr[index as usize])) + }, + (r1, Value::Str(key)) => self.in_expression_index_by_key(r1, key)?, + (r1, r2) => return Err(self.make_error(&format!( + "Trying to index {} with {}", r1.deref().stringify_type(), r2.stringify_type()))) + } + } + Expression::Attribute(e1, key) => { + self.in_expression_index_by_key( + self.execute_expression(&e1, root, arg_stack, recc - 1)?, key)? + } + Expression::Call(e1, e2) => { + let r1: ExpressionResult = self.execute_expression(e1, root, arg_stack, recc - 1)?; + let r2: ExpressionResult = self.execute_expression(e2, root, arg_stack, recc - 1)?; + match r1.deref() { + Value::Fn(func) => ExpressionResult::Owned(func(self, root, r2.deref(), recc - 1)?), + _ => return Err(self.make_error(&format!( + "Can't pass argument to {}", r1.deref().stringify_type()))) + } + } + Expression::Int(num ) => ExpressionResult::Owned(Value::Int(*num)), + /* Absurd, and yet possible case */ + Expression::None => ExpressionResult::Owned(Value::Int(0)), + }; + Ok(match f1.deref() { + Value::RuntimeStr(fnc) => ExpressionResult::Owned( + Value::Str(fnc(self, root, recc - 1)?) + ), + _ => f1 + }) + } +} + +/* We construct intermediate closures for elements at runtime */ +fn construct_element_closure(prev: Vec<&Value>, el: Element, cr_recc: u32) -> Result, String> { + if cr_recc == 0 { return Err("Recursion limit exceeded".into()) } + Ok(if prev.len() + 1 == el.argc { + Box::new(move |re: &RuntimeEnv, root: &Value, arg: &Value, ecc: u32| -> Result { + if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) } + let mut new = prev.clone(); new.push(arg); + re.execute_element_block(&el.sub_elements, root, &new, ecc - 1).map(Value::Str) + }) + } else { + Box::new(move |re: &RuntimeEnv, root: &Value, arg: &Value, ecc: u32| -> Result { + if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) } + let mut new = prev.clone(); new.push(arg); + construct_element_closure(new, el, cr_recc - 1).map(Value::Fn) + }) + }) } /* This function is supposed to compile all elements in one particular file */ -pub fn plemege_to_value(mut x: Plemege, file_path: &str) -> Result> { +pub fn plemege_to_value(x: Plemege, file_path: &str, recc: u32) -> Result { + if recc == 0 { return Err("Recursion limit exceeded".into()) } match x { Plemege::Package(map) => { let mut new_dict: HashMap = HashMap::new(); for (key, thing) in map { - new_dict.insert(key, plemege_to_value(thing, file_path)?); + new_dict.insert(key, plemege_to_value(thing, file_path, recc - 1)?); } Ok(Value::Dict(new_dict)) }, Plemege::Element(el) => { - // Rc::new(|d_state: &DebugState, _: &Value, _: &Value, args: &[&Value]|) - // todo - Ok(Value::default()) + /* The only optimization we ever make (we could do more, but sorry, my life is finite) */ + if el.sub_elements.iter().all(|se| matches!(se, SubElement::Static(_))) { + return Ok(Value::Str(el.sub_elements.into_iter() + .fold(String::new(), |mut acc, p| { + let SubElement::Static(s) = p else { panic!() }; + acc.push_str(&s); acc + }))) + } + if el.argc == 0 { + Ok(Value::RuntimeStr(Box::new( + move |re: &RuntimeEnv, root: &Value, recc: u32| -> Result { + re.execute_element_block(&el.sub_elements, root, &[], recc) + }))) + } else { + let c1 = construct_element_closure(Vec::new(), el, recc).map(Value::Fn); + c1 + } } } } \ No newline at end of file diff --git a/mtgott/src/runtime.rs b/mtgott/src/runtime.rs index d38d016..f5459dd 100644 --- a/mtgott/src/runtime.rs +++ b/mtgott/src/runtime.rs @@ -10,25 +10,33 @@ pub struct DebugStateGuard<'a> ( impl<'a> Drop for DebugStateGuard<'a> { fn drop(&mut self) { - assert!(matches!(self.0.borrow_mut().pop(), Some(_))); + self.0.borrow_mut().pop(); } } -pub struct DebugState ( +pub struct RuntimeEnv( RefCell> ); -impl<'a> DebugState { +impl<'a> RuntimeEnv { pub fn register(&'a self, msg: String) -> DebugStateGuard<'a> { self.0.borrow_mut().push(msg); DebugStateGuard(&self.0) } } -/* Arguments are: stack trace for debug, root, actual function argument */ -pub type HigherLevelFunc = dyn Fn(&DebugState, &Value, &Value) -> Result>; -/* Arguments are: stack trace for debug, root */ -pub type OopsForgotToPrecompileThis = dyn Fn(&DebugState, &Value) -> Result>; +impl RuntimeEnv { + pub fn make_error(&self, err: &str) -> String { + let total_len: usize = self.0.borrow().iter().map(|s| { s.len() }).sum(); + let mut res = self.0.borrow().iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc }); + res + err + } +} + +/* Arguments are: stack trace for debug, root, actual function argument, recursion counter */ +pub type HigherLevelFunc = dyn Fn(&RuntimeEnv, &Value, &Value, u32) -> Result; +/* Arguments are: stack trace for debug, root, recursion counter */ +pub type OopsForgotToPrecompileThis = dyn Fn(&RuntimeEnv, &Value, u32) -> Result; pub enum Value { Str(String), @@ -45,9 +53,16 @@ pub enum Value { } impl Value { - fn as_dict(&self) -> &HashMap { + pub fn as_dict(&self) -> &HashMap { match self { Value::Dict(s) => s, _ => panic!(), } } + + pub fn stringify_type(&self) -> &'static str { + match self { + Value::Str(_) => "String", Value::Int(_) => "Int", Value::Arr(_) => "Array", + Value::Dict(_) => "Dictionary", Value::Fn(_) => "Function", _ => panic!() + } + } } impl Default for Value { @@ -69,7 +84,7 @@ impl Debug for Value { write!(f, "Dict({{{}}})", dict_debug.join(", ")) } Value::Fn(_) => write!(f, "Fn()"), - Value::RuntimeStr(_) => write!(f, "C-comp-pilat_onArrrr%%#^#}}&_)") + Value::RuntimeStr(_) => write!(f, "C-comp-pilat_onArrrrr%%#^#}}&_)") } } } @@ -81,7 +96,6 @@ impl<'t> PartialEq for Value { (Value::Int(i1), Value::Int(i2)) => i1 == i2, (Value::Arr(a1), Value::Arr(a2)) => a1 == a2, (Value::Dict(d1), Value::Dict(d2)) => d1 == d2, - (Value::Fn(_), Value::Fn(_)) => false, _ => false, } } @@ -119,11 +133,11 @@ pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) - }; match main { Value::Fn(main_func) => { - let d_stack = DebugState(RefCell::new(Vec::new())); + let d_stack = RuntimeEnv(RefCell::new(Vec::new())); let Some(guest_root) = guest_root else { return Err(format!("Need guest argument root for template {name}").into()) }; - let rv = main_func(&d_stack, root, guest_root)?; + let rv = main_func(&d_stack, root, guest_root, 150)?; match rv { Value::Str(answer) => Ok(answer), _ => Err(format!("template {name} returned not a string").into()) @@ -131,8 +145,8 @@ pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) - } Value::Str(str) => Ok(str.clone()), Value::RuntimeStr(str_fnc) => { - let mut d_stack = DebugState(RefCell::new(Vec::new())); - Ok(str_fnc(&mut d_stack, root)?) + let mut d_stack = RuntimeEnv(RefCell::new(Vec::new())); + Ok(str_fnc(&mut d_stack, root, 150)?) } _ => Err(format!("Called {name} template that is not a function / string").into()) }