From 9ce0e602b3190ec0ad080e667cb8270e10ded6c0 Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Wed, 23 Apr 2025 15:14:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=BD=D0=B0=D0=BB=20=D0=BB=D0=B8=20?= =?UTF-8?q?=D1=82=D1=8B,=20=D0=9C=D0=B0=D1=80=D0=BA,=20=D1=87=D1=82=D0=BE?= =?UTF-8?q?=20=D0=B2=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=BC=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BA=D0=B0=D0=BD=D0=B5=20=D0=A4=D1=80=D1=8D=D0=BD=D0=B7=D0=B8?= =?UTF-8?q?-=D0=9A=D0=BE=D1=84=D0=B5=2044=20=D1=87=D0=B0=D0=B9=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=BB=D0=BE=D0=B6=D0=BA=D0=B8=20=D0=93=D0=BE=D1=80?= =?UTF-8?q?=D0=B0=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mtgott/src/bin/mtgott_cli.rs | 27 +- mtgott/src/dirsearch.rs | 62 ++--- mtgott/src/lambda_compilation.rs | 319 ++++++++------------- mtgott/src/parser.rs | 55 ++-- mtgott/src/runtime.rs | 462 ++++++++++++++++++++++++------- 5 files changed, 548 insertions(+), 377 deletions(-) diff --git a/mtgott/src/bin/mtgott_cli.rs b/mtgott/src/bin/mtgott_cli.rs index 06d802a..9ee2b97 100644 --- a/mtgott/src/bin/mtgott_cli.rs +++ b/mtgott/src/bin/mtgott_cli.rs @@ -2,11 +2,11 @@ extern crate mtgott; use std::env; use std::process; -use crate::mtgott::charclasses::is_bad_name; +use crate::mtgott::charclasses::is_special_name; use crate::mtgott::dirsearch::get_root_html; use crate::mtgott::runtime::{Value, MTGOTT}; use std::collections::HashMap; -use mtgott::dirsearch::add_path_with_dots_to_root; +use crate::mtgott::runtime::*; use std::rc::Rc; fn usage() -> ! { @@ -14,6 +14,20 @@ fn usage() -> ! { process::exit(1); } +fn arg_dict_to_value<'t>(x: SharedValue) -> Value<'t> { + match x { + SharedValue::Str(s) => Value::Str(Rc::new(s)), + SharedValue::Dict(d) => { + let mut map: HashMap = HashMap::new(); + for (key, value) in d { + map.insert(key, arg_dict_to_value(value)); + } + Value::Dict(Rc::new(map)) + } + _ => panic!() + } +} + fn main() { let mut args = env::args(); args.next().unwrap(); @@ -31,7 +45,7 @@ fn main() { None => usage() }; for s in name.split('.') { - if is_bad_name(s) { + if is_special_name(s) { eprintln!("Bad name: {}", s); process::exit(1); } @@ -41,11 +55,12 @@ fn main() { usage(); } } - let mut guest_root = Value::Dict(Rc::new(HashMap::new())); + let mut guest_root = SharedValue::Dict(HashMap::new()); for (name_path, val) in defines { - add_path_with_dots_to_root(&mut guest_root, &name_path, Value::Str(Rc::new(val))).unwrap(); + add_path_with_dots_to_root(&mut guest_root, &name_path, SharedValue::Str(val)).unwrap(); } - let dir_root = get_root_html(&path_arg).unwrap(); + let guest_root = arg_dict_to_value(guest_root); + let dir_root: MTGOTT = get_root_html(&path_arg).unwrap(); let res = dir_root.render(guest_root, &template_name, 500).unwrap(); print!("{res}") } diff --git a/mtgott/src/dirsearch.rs b/mtgott/src/dirsearch.rs index a3eb806..c2e676b 100644 --- a/mtgott/src/dirsearch.rs +++ b/mtgott/src/dirsearch.rs @@ -3,13 +3,13 @@ use std::fs::{read_dir, metadata, canonicalize}; use std::path::PathBuf; use std::error::Error; use super::charclasses::{escape_for_html, is_special_name}; -use super::runtime::{Value, RuntimeEnv, MTGOTT}; +use super::runtime::*; use super::parser::{parse_one_file_simplified, parse_one_file_packed}; use super::lambda_compilation::{plemege_to_value}; use std::collections::HashMap; use std::rc::Rc; -pub fn search_dir_rec_helper bool>( +pub fn search_dir_rec_helper( res: &mut Vec>, fs_path: PathBuf, virtual_path: String, allowed_extensions: &[&str], is_valid_name: fn(&str) -> bool, recc: u32 @@ -71,38 +71,9 @@ pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result Result<(), Box> { - let parts: Vec<&str> = unsplit_path.split(splitter).collect(); - let mut cur: &mut Value = root; - assert!(parts.len() > 0); - for i in 0usize..parts.len() { - match cur { - Value::Dict(hashmap) => { - cur = hashmap.entry(String::from(parts[i])).or_insert(Default::default()); - } - _ => return Err(format!("Overlapping root elements {}", parts[..i].join(&String::from(splitter))).into()) - } - } - match cur { - Value::Int(x) if *x == 0 => {}, - _ => return Err(format!("Overlapping root elements {}", unsplit_path).into()), - } - *cur = obj; - Ok(()) -} - -pub fn add_path_with_slashes_to_root(root: &mut Value, unsplit_path: &str, obj: Value) -> Result<(), Box> { - add_path_to_root(root, unsplit_path, '/', obj) -} - -pub fn add_path_with_dots_to_root(root: &mut Value, unsplit_path: &str, obj: Value) -> Result<(), Box> { - add_path_to_root(root, unsplit_path, '.', obj) -} - pub fn get_all_templates(p: &str, plain_ext: &str) -> Result> { let source = search_mtgott_dir(p, plain_ext)?; - let mut res = MTGOTT{root: Value::Dict(Rc::new(HashMap::new())), source_expressions: Vec::new(), source_elements: Vec::new()}; + let mut res = MTGOTT{root: SharedValue::Dict(HashMap::new()), source_expressions: Vec::new(), source_elements: Vec::new()}; for cut_path in source.mtgott { let path = format!("{cut_path}.mtgott{plain_ext}"); let text_bytes = fs::read(PathBuf::from(p).join(&path))?; @@ -123,7 +94,7 @@ pub fn get_all_templates(p: &str, plain_ext: &str) -> Result String, ) -> Result>{ let mut res = get_all_templates(p, plain_ext)?; - add_path_with_slashes_to_root(&mut res.root, "sanitize", Value::Fn(Rc::new( + add_path_with_slashes_to_root(&mut res.root, "sanitize", SharedValue::Fn(Box::new( /* One obvious problem with this sanitizer is that it makes copy even when it * takes full ownership of some string and does not have to replace any characters*/ - |d_state: &RuntimeEnv, _: &MTGOTT, arg: Value, _: u32| -> Result { - let s: &Value = if let Value::Ref(nr) = arg { nr } else { &arg }; - match s { - Value::Str(s) => Ok(Value::Str(Rc::new(sanitize(s.as_ref())))), - Value::Int(num) => Ok(Value::Str(Rc::new(num.to_string()))), - _ => Ok(Value::Str(Rc::new(sanitize(&format!("{:?}", arg))))) + move |d_state: &RuntimeEnv, arg: Value, _: u32| -> Result { + if let Some(s) = arg.if_str_get_ref() { + Ok(Value::Str(Rc::new(sanitize(s)))) + } else if let Some(n) = arg.if_int_get() { + Ok(Value::Str(Rc::new(n.to_string()))) + } else { + Ok(Value::Str(Rc::new(sanitize(&format!("{:?}", arg))))) } })))?; - add_path_with_slashes_to_root(&mut res.root, "cmd_tag_start", Value::Str("{%".into()))?; - add_path_with_slashes_to_root(&mut res.root, "write_tag_start", Value::Str("{{".into()))?; - add_path_with_slashes_to_root(&mut res.root, "roughinsert_tag_start", Value::Str("{[".into()))?; - add_path_with_slashes_to_root(&mut res.root, "magic_block_ending_tag", Value::Str("{%}".into()))?; - add_path_with_slashes_to_root(&mut res.root, "element_ending_tag", Value::Str("{@}".into()))?; + add_path_with_slashes_to_root(&mut res.root, "cmd_tag_start", SharedValue::Str("{%".into()))?; + add_path_with_slashes_to_root(&mut res.root, "write_tag_start", SharedValue::Str("{{".into()))?; + add_path_with_slashes_to_root(&mut res.root, "roughinsert_tag_start", SharedValue::Str("{[".into()))?; + add_path_with_slashes_to_root(&mut res.root, "magic_block_ending_tag", SharedValue::Str("{%}".into()))?; + add_path_with_slashes_to_root(&mut res.root, "element_ending_tag", SharedValue::Str("{@}".into()))?; Ok(res) } diff --git a/mtgott/src/lambda_compilation.rs b/mtgott/src/lambda_compilation.rs index 9a3039a..234147e 100644 --- a/mtgott/src/lambda_compilation.rs +++ b/mtgott/src/lambda_compilation.rs @@ -1,8 +1,7 @@ -use super::runtime::*; -use super::parser::*; -use std::error::Error; +use runtime::*; +use parser::*; use std::collections::HashMap; -use std::ops::Deref; +use std::rc::Rc; struct ElementPartsCollector { res: String, cur_col: usize, @@ -33,25 +32,14 @@ impl ElementPartsCollector { } } -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") - } - } - +impl<'m> RuntimeEnv<'m> { fn get_needed_if_block<'i>( - &self, if_sub_el: &'i IfSubElement, mtgott: &MTGOTT, arg_stack: &[&Value], recc: u32 + &self, if_sub_el: &'i IfSubElement, local: &[Value<'m>], 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], mtgott, arg_stack, recc - 1)?; - if self.value_to_bool(expr_res.deref())? { + let expr_res = self.execute_expression(&if_sub_el.conditions[i], local, recc - 1)?; + if expr_res.to_bool()? { return Ok(Some(&if_sub_el.branches[i])); } } @@ -62,8 +50,9 @@ impl RuntimeEnv { } } + /* if Ok, local remains unchanged after return, but if Err, don't use local anymore */ fn execute_element_block( - &self, el: &[SubElement], mtgott: &MTGOTT, arg_stack: &[& Value], recc: u32 + &self, el: &[SubElement], local: &mut Vec>, recc: u32 ) -> Result { if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) } let mut res = ElementPartsCollector{res: String::new(), cur_col: 0}; @@ -72,63 +61,66 @@ impl RuntimeEnv { 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, mtgott, arg_stack, recc - 1)? { + SubElement::If(if_sub_el) => if let Some(branch_el) = + self.get_needed_if_block(if_sub_el, local, recc - 1)? { res.add_single_padded_element_piece( - &self.execute_element_block( - &branch_el.sub_elements, mtgott, arg_stack, recc - 1)?) + &self.execute_element_block(branch_el, local, recc - 1)?) + }, + SubElement::InsertExpr(expr) => { + let val = self.execute_expression(expr, local, recc - 1)?; + match val.if_str_get_ref() { + Some(s) => res.add_single_padded_element_piece(s), + None => return Err(self.make_error(&format!( + "Can't insert {} into template. Consider using {{{{}}}}", val.type_name() + ))) } - , - SubElement::InsertExpr(expr) => res.add_single_padded_element_piece( - match self.execute_expression(expr, mtgott, 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}) => { + }, + SubElement::For(ForSubElement{iterable, core, separator}) => { let _g = self.register("In {%for%}".into()); - let expr_val = self.execute_expression(iterable, mtgott, arg_stack, recc - 1)?; - let an = arg_stack.len(); + let r = self.execute_expression(iterable, local, recc - 1)?; 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, mtgott, arg_stack, recc - 1)?); - } else { break } - } + let k = local.len(); + local.push(Value::Int(0)); local.push(Value::Int(0)); + let collected: Vec = match r { + Value::Dict(dict) => dict.as_ref().iter() + .map(|(key ,value)| -> Result { + local[k] = Value::Str(Rc::new(key.clone())); + local[k + 1] = value.clone(); + self.execute_element_block(core, local, recc - 1) + }).collect::, String>>()?, + Value::Ref(SharedValue::Dict(dict)) => dict.iter() + .map(|(key, sh_val)| -> Result { + local[k] = Value::Str(Rc::new(key.clone())); + local[k + 1] = Value::Ref(sh_val); + self.execute_element_block(core, local, recc - 1) + }).collect::, String>>()?, + Value::Arr(arr) => arr.as_ref().iter().enumerate() + .map(|(ind ,value)| -> Result { + local[k] = Value::Int(ind as u64); + local[k + 1] = value.clone(); + self.execute_element_block(core, local, recc - 1) + }).collect::, String>>()?, + Value::Ref(SharedValue::Arr(arr)) => arr.iter().enumerate() + .map(|(ind, sh_val)| -> Result { + local[k] = Value::Int(ind as u64); + local[k + 1] = Value::Ref(sh_val); + self.execute_element_block(core, local, recc - 1) + }).collect::, String>>()?, + _ => return Err(self.make_error(&format!("Can't iterate over {}", r.type_name()))) + }; + if !collected.is_empty() { + res.add_padded_element_piece(saved_offset, &collected[0]); + for s in &collected[1..]{ + res.add_padded_element_piece(saved_offset, separator); + res.add_padded_element_piece(saved_offset, s); } - 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, mtgott, 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, mtgott, 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()); + let r = self.execute_expression(expr, local, recc - 1)?; + local.push(r); res.add_single_padded_element_piece(&self.execute_element_block( - &core.sub_elements, mtgott, arg_stack, recc - 1)?); + &core, local, recc - 1)?); } }; } @@ -136,157 +128,88 @@ impl RuntimeEnv { } } -enum ExpressionResult<'l, 't> { - LocalRef(&'l Value<'l>), - Owned(Value<'t>), -} - - -impl<'l, 't> Deref for ExpressionResult<'l> { - type Target = Value; - - fn deref(&self) -> &Self::Target { - match self { - ExpressionResult::LocalRef(val) => val, - ExpressionResult::Owned(val) => val, +/* We construct intermediate closures for elements at runtime + * argc >= 1 */ +impl<'m> RuntimeEnv<'m> { + fn construct_result_of_element_lambda( + &self, mut prev: Vec>, argc: usize, el_source_pos: usize, recc: u32, + ) -> Result, String> { + if prev.len() == argc { + self.execute_element_block(&self.mtgott.source_elements[el_source_pos], &mut prev, recc) + .map(|s| Value::Str(Rc::new(s))) + } else { + Ok(Value::Fn(Rc::new(move |re, arg, counter| -> Result, String> { + re.check_recc(counter)?; + let mut prev = prev.clone(); + prev.push(arg); + re.construct_result_of_element_lambda(prev, argc, el_source_pos, counter - 1) + } ))) } } } -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, mtgott: &'l MTGOTT, 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(&mtgott.root), - Expression::Argument(id) => ExpressionResult::LocalRef(arg_stack[*id as usize]), - Expression::Get(e1, e2) => { - let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, mtgott, arg_stack, recc - 1)?; - let r2: ExpressionResult = self.execute_expression(&e2, mtgott, 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, mtgott, arg_stack, recc - 1)?, key)? - } - Expression::Call(e1, e2) => { - let r1: ExpressionResult = self.execute_expression(e1, mtgott, arg_stack, recc - 1)?; - let r2: ExpressionResult = self.execute_expression(e2, mtgott, arg_stack, recc - 1)?; - match r1.deref() { - Value::Fn(func) => ExpressionResult::Owned(func(self, mtgott, 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, mtgott, recc - 1)?) - ), - _ => f1 - }) - } -} - -/* We construct intermediate closures for elements at runtime */ -fn construct_element_closure<'a>( - prev: Vec<&'a Value>, argc: usize, el_source_pos: usize, cr_recc: u32 -) -> Result Result + 'a>, String> { - if cr_recc == 0 { return Err("Recursion limit exceeded".into()) } - Ok(if prev.len() + 1 == argc { - Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, 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(&mtgott.source[el_source_pos].sub_elements, mtgott, &new, ecc - 1).map(Value::Str) - }) - } else { - Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, 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, argc, el_source_pos, cr_recc - 1).map(Value::Fn) - }) - }) -} - /* This function is supposed to compile all elements in one particular file. - Due to how bad mtgott type system is, Some elements require me to save source tree into - a storage*/ + Due to how bad mtgott type system is, some elements require me to save source tree into + a storage */ pub fn plemege_to_value( x: Plemege, file_path: &str, source_elements: &mut Vec, source_expr: &mut Vec, recc: u32 -) -> Result { +) -> Result { if recc == 0 { return Err("Recursion limit exceeded".into()) } match x { Plemege::Package(map) => { - let mut new_dict: HashMap = HashMap::new(); + let mut new_dict: HashMap = HashMap::new(); for (key, thing) in map { - new_dict.insert(key, plemege_to_value(thing, file_path, source, recc - 1)?); + new_dict.insert(key, plemege_to_value(thing, file_path, source_elements, source_expr, recc - 1)?); } - Ok(Value::Dict(new_dict)) + Ok(SharedValue::Dict(new_dict)) }, - Plemege::Element(el) => { - /* 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() + Plemege::Element(argc, el) => { + if argc == 0 && el.iter().all(|se| matches!(se, SubElement::Static(_))) { + /* The only optimization we ever make (we could do more, but sorry, my life is finite) */ + Ok(SharedValue::Str(el.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, mtgott: &MTGOTT, recc: u32| -> Result { - re.execute_element_block(&el.sub_elements, mtgott, &[], recc) + } else if argc == 0 { + Ok(SharedValue::RuntimeConst(Box::new( + move |re: &RuntimeEnv, counter: u32| -> Result { + re.check_recc(counter)?; + /* No need to save element into source vector. We move it here */ + re.execute_element_block(&el, &mut Vec::new(), counter).map(|s| Value::Str(Rc::new(s))) }))) } else { - let argc = el.argc; - source.push(el); - construct_element_closure(Vec::new(), argc, source.len() - 1, recc).map(Value::Fn) + let body_element_id = source_elements.len(); + source_elements.push(el); + Ok(SharedValue::Fn(Box::new( + move |re, x, counter| -> Result { + re.check_recc(counter)?; + re.construct_result_of_element_lambda(vec![x], argc, body_element_id, counter - 1) + } + ))) } } + Plemege::Expression(expr) => match expr { + Expression::Int(n) => Ok(SharedValue::Int(n)), + Expression::Str(s) => Ok(SharedValue::Str(s)), + Expression::Lambda(NewLambdaExpression{local_var_array, expr_id}) => { + assert_eq!(local_var_array, vec![usize::MAX]); + Ok(SharedValue::Fn(Box::new( + move |re, arg, counter| -> Result { + re.check_recc(counter)?; + re.execute_expression( + &re.mtgott.source_expressions[expr_id], + &vec![arg], counter - 1) + })) + ) + }, + _ => Ok(SharedValue::RuntimeConst(Box::new( + move |re, counter| -> Result { + re.check_recc(counter)?; + /* No need to save expression into source vector. We move it here */ + re.execute_expression(&expr, &Vec::new(), counter - 1) + }))) + } } } \ No newline at end of file diff --git a/mtgott/src/parser.rs b/mtgott/src/parser.rs index 10e42c7..ff710fb 100644 --- a/mtgott/src/parser.rs +++ b/mtgott/src/parser.rs @@ -6,19 +6,19 @@ use std::error::Error; #[derive(Debug, PartialEq, Clone)] pub struct NewLambdaExpression { - local_var_array: Vec, + pub local_var_array: Vec, /* We store all expression trees in giant vector in MTGOTT object. * We fill this global (to all the files) vector at compile time, and it can be used * to access expressions (parsed source code) from any dynamically created lambda */ - expr_id: usize, + pub expr_id: usize, } #[derive(Debug, PartialEq, Clone)] pub enum Expression { Root, Local(usize), - Get(Box, Box), Attribute(Box, String), + Get(Box, Box), Call(Box, Box), Lambda(NewLambdaExpression), Int(u64), @@ -37,7 +37,7 @@ pub struct ForSubElement { pub iterable: Expression, pub core: Element, // Either "\n", " " or "" - pub join: String, + pub separator: String, } #[derive(Debug, PartialEq, Clone)] @@ -222,7 +222,7 @@ impl<'a> Parser<'a> { } fn parse_pack_plus_ending( - &mut self, top: bool, source_expr: &mut Vec, recc: u32 + &mut self, top: bool, recc: u32 ) -> Result { self.check_recursion_limit(recc)?; let mut res: HashMap = HashMap::new(); @@ -261,7 +261,7 @@ impl<'a> Parser<'a> { if self.is_ahead("$}"){ self.p += 2; - res.insert(String::from(child_name), self.parse_pack_plus_ending(false, source_expr, recc - 1)?); + res.insert(String::from(child_name), self.parse_pack_plus_ending(false, recc - 1)?); } else if self.is_char_ahead('=') { self.p += 1; self.skip_whitespace(); @@ -324,7 +324,7 @@ impl<'a> Parser<'a> { arg_names.push(arg_name); } let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag( - &arg_names, source_expr, recc - 1)?; + &arg_names, recc - 1)?; if !matches!(end_cmd.cmd, BlockEndingTag::END_ELEMENT) { return Err(FileParsingError::new(incorrect_block_ending_tag_expected_end_element, end_cmd.p1, self.p)) } @@ -446,7 +446,7 @@ impl<'a> Parser<'a> { /* If BlockEndingCmdTag::ELSE_IF is returned, the ending tag won't be read completely, * But in other case it would be read to the end */ fn parse_element_plus_ending_tag ( - &mut self, arg_names: &Vec<&str>, source_expr: &mut Vec, recc: u32 + &mut self, arg_names: &Vec<&str>, recc: u32 ) -> Result<(Element, ReasonOfElementEnd), FileParsingError> { self.check_recursion_limit(recc)?; let mut res: Vec = Vec::new(); @@ -547,9 +547,9 @@ impl<'a> Parser<'a> { } "endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res), "endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res), - "for" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?), - "if" => res.push(self.parse_if(arg_names, source_expr, recc - 1)?), - "let" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?), + "for" => res.push(self.parse_let(arg_names, recc - 1)?), + "if" => res.push(self.parse_if(arg_names, recc - 1)?), + "let" => res.push(self.parse_let(arg_names, recc - 1)?), _ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)), } tp1 = self.p; @@ -574,14 +574,14 @@ impl<'a> Parser<'a> { /* * It parses expr %} block {% else if expr %} block {% else %} block {%} */ fn parse_if( - &mut self, arg_names: &Vec<&str>, source_expr: &mut Vec, recc: u32 + &mut self, arg_names: &Vec<&str>, recc: u32 ) -> Result { self.check_recursion_limit(recc)?; let mut conditions: Vec = Vec::new(); let mut blocks: Vec = Vec::new(); loop { let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?; - let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names, source_expr, recc - 1)?; + let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names, recc - 1)?; conditions.push(expr); match ending_tag.cmd { BlockEndingTag::ELSE | BlockEndingTag::NORMAL | BlockEndingTag::ENDIF | @@ -590,7 +590,7 @@ impl<'a> Parser<'a> { incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if, ending_tag.p1, self.p)), } if matches!(ending_tag.cmd, BlockEndingTag::ELSE) { - let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names, source_expr, recc - 1)?; + let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names, recc - 1)?; if !matches!(the_end.cmd, BlockEndingTag::NORMAL | BlockEndingTag::ENDIF){ return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal_or_endif, the_end.p1, self.p)); } @@ -603,7 +603,7 @@ impl<'a> Parser<'a> { Ok(SubElement::If(IfSubElement{branches: blocks, conditions})) } - fn parse_let(&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec, recc: u32) -> Result { + fn parse_let(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result { self.check_recursion_limit(recc)?; self.skip_whitespace(); let p1 = self.p; @@ -624,7 +624,7 @@ impl<'a> Parser<'a> { let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?; let mut arg_names_extended = arg_names.clone(); arg_names_extended.push(new_variable_name); - let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, source_expr, recc - 1)?; + let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, recc - 1)?; if !matches!(ending.cmd, BlockEndingTag::NORMAL) { return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p)); } @@ -645,7 +645,7 @@ impl<'a> Parser<'a> { Ok((name, t1)) } - fn parse_for(&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec, recc: u32) -> Result { + fn parse_for(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result { self.check_recursion_limit(recc)?; let mut arg_names_extended = arg_names.clone(); @@ -672,7 +672,7 @@ impl<'a> Parser<'a> { let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?; let (inner_block, ending) = self.parse_element_plus_ending_tag( - &arg_names_extended, source_expr, recc - 1)?; + &arg_names_extended, recc - 1)?; let separator: String = String::from(match ending.cmd { BlockEndingTag::NOGAP => "", BlockEndingTag::GAP => " ", @@ -680,7 +680,7 @@ impl<'a> Parser<'a> { _ => return Err(FileParsingError::new( incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop, ending.p1, self.p)), }); - Ok(SubElement::For(ForSubElement{iterable: expr, core: inner_block, join: separator})) + Ok(SubElement::For(ForSubElement{iterable: expr, core: inner_block, separator})) } /* Checks for ]} }} and %}. May actually return NoneOfThose */ @@ -726,7 +726,7 @@ impl<'a> Parser<'a> { /* Suppose we use some toplevel name in expression. There is a chance it is a local constant. * If so, we search for its index I in arg_names and if name_in_expr belongs to arg_names - * we return the index of number I in used_local_consts (pushing I to the right if needed) + * we return the index of number I in used_local_consts (pushing I to the right if needed). * If name_in_expr is not a local constant, we return None */ fn expression_parsing_include_local_const( arg_names: &Vec<&str>, used_local_consts: &mut Vec, name_in_expr: &str @@ -784,19 +784,26 @@ impl<'a> Parser<'a> { return Err(FileParsingError::new(illegal_lambda_argument_name, p1, p2)) } self.p += 1; + let argument_iig = arg_names.len(); let mut arg_names_extended = arg_names.clone(); arg_names_extended.push(toplevel_name); let mut used_by_lambda: Vec = Vec::new(); let lambda_body = self.parse_expression( &arg_names_extended, &mut used_by_lambda, recc - 1)?; for iig in &used_by_lambda { - if !used_local_consts.contains(iig) { used_local_consts.push(*iig); } + if !used_local_consts.contains(iig) && *iig != argument_iig { + used_local_consts.push(*iig); + } } let expr_id = self.source_expr.len(); self.source_expr.push(lambda_body); Ok(Some(Expression::Lambda(NewLambdaExpression{ local_var_array: used_by_lambda.iter().map( |iig| -> usize { - used_local_consts.iter().position(|iig_of_this| iig_of_this == iig).unwrap() + if *iig == argument_iig { + usize::MAX + } else { + used_local_consts.iter().position(|iig_of_this| iig_of_this == iig).unwrap() + } }).collect(), expr_id}))) } else if toplevel_name == "this" { Ok(Some(self.fn_parse_expression_l2_trail( @@ -900,14 +907,14 @@ pub fn parse_one_file_packed( text: &str, file_path: &str, source_expr: &mut Vec, ) -> Result { let mut parser: Parser = Parser{text, p: 0, this_meaning: make_expression_for_this(file_path), source_expr}; - parser.parse_pack_plus_ending(true, source_expr, 150) + parser.parse_pack_plus_ending(true, 150) } pub fn parse_one_file_simplified( text: &str, file_path: &str, source_expr: &mut Vec, ) -> Result { let mut parser: Parser = Parser{text, p: 0, this_meaning: make_expression_for_this(file_path), source_expr}; - let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&vec!["$"], source_expr, 150)?; + let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&vec!["$"], 150)?; match tt.cmd { BlockEndingTag::EOF => Ok(Plemege::Element(1, el)), BlockEndingTag::END_ELEMENT => Err(FileParsingError::new(unmatched_element_ending_tag, tt.p1, parser.p)), diff --git a/mtgott/src/runtime.rs b/mtgott/src/runtime.rs index 1fe4b1c..743e852 100644 --- a/mtgott/src/runtime.rs +++ b/mtgott/src/runtime.rs @@ -1,10 +1,9 @@ -use std::collections::HashMap; -use std::fmt; -use std::fmt::Debug; -use std::error::Error; -use std::cell::RefCell; -use parser::{Element, Expression}; use std::rc::Rc; +use std::collections::HashMap; +use std::error::Error; +use std::fmt::{Debug, Formatter}; +use std::cell::RefCell; +use parser::{Element, Expression, NewLambdaExpression}; pub struct DebugStateGuard<'a> ( &'a RefCell> @@ -16,129 +15,384 @@ impl<'a> Drop for DebugStateGuard<'a> { } } -pub struct RuntimeEnv( - RefCell> -); - -impl<'a> RuntimeEnv { - pub fn register(&'a self, msg: String) -> DebugStateGuard<'a> { - self.0.borrow_mut().push(msg); - DebugStateGuard(&self.0) - } +pub enum SharedValue { + Int(u64), + Str(String), + Arr(Vec), + Dict(HashMap), + Fn(Box Fn(& RuntimeEnv<'m>, Value<'m>, u32) -> Result, String> + Send + Sync>), + RuntimeConst(Box Fn(& RuntimeEnv<'m>, u32) -> Result, String> + Send + Sync>), } -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 - } -} - -pub struct MTGOTT{ - pub root: Value<'static>, +pub struct MTGOTT { + pub root: SharedValue, pub source_elements: Vec, pub source_expressions: Vec } -pub enum Value<'t> { - Str(Rc), - Int(u64), - Arr(Rc>>), - Dict(Rc>>), - Fn(Rc, u32) -> Result, String>>), - Ref(&'t Value<'t>), - /* Some may call it a crutch (and I am definitely agree with them): my current mtgott - compiler does not perform precomputational optimization for elements with no arguments. - They cannot be turned into Fn, as it would be a type error, so they get compiled into - Value::RuntimeConst. At runtime, this compilation artifact is the same thing as - Value\{::RuntimeConst}. - Expression evaluator automatically converts them as soon as possible. */ - RuntimeConst(Box Result, String>>), +pub struct RuntimeEnv<'m> { + pub stack: RefCell>, + pub mtgott: &'m MTGOTT, } -impl<'t> Value<'t> { - pub fn stringify_type(&self) -> String { +impl<'a> RuntimeEnv<'a> { + pub fn new(mtgott: &'a MTGOTT) -> Self { + RuntimeEnv{stack: RefCell::new(Vec::new()), mtgott} + } + + pub fn register(&'a self, mut msg: String) -> DebugStateGuard<'a> { + msg.push('\n'); + self.stack.borrow_mut().push(msg); + DebugStateGuard(&self.stack) + } +} + +impl<'m> RuntimeEnv<'m> { + pub fn make_error(&self, err: &str) -> String { + let total_len: usize = self.stack.borrow().iter().map(|s| { s.len() }).sum(); + let mut res = self.stack.borrow().iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc }); + res + err + } +} + + +#[derive(Clone)] +pub enum Value<'m> { + Int(u64), + Str(Rc), + Arr(Rc>>), + Dict(Rc>>), + Fn(Rc, Value<'m>, u32) -> Result, String> + 'm>), + Ref(&'m SharedValue), +} + +impl Debug for SharedValue { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { + SharedValue::Int(n) => write!(f, "Int({n})"), + SharedValue::Str(s) => write!(f, "Str({})", s), + SharedValue::Arr(arr) => write!(f, "Arr({:?})", arr), + SharedValue::Dict(dict) => write!(f, "Dict({:?})", dict), + SharedValue::Fn(_) => write!(f, "Fn()"), + SharedValue::RuntimeConst(_) => write!(f, "RuntimeConst()") + } + } +} + +impl SharedValue { + pub fn type_name(&self) -> String { + match self { + SharedValue::Int(_) => "Integer".into(), + SharedValue::Str(_) => "String".into(), + SharedValue::Arr(_) => "Array".into(), + SharedValue::Dict(_) => "Dictionary".into(), + SharedValue::Fn(_) => "Function".into(), + SharedValue::RuntimeConst(_) => "Runtime Const".into(), + } + } +} + +impl<'m> Debug for Value<'m> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Value::Int(n) => write!(f, "Int({n})"), + Value::Str(s) => write!(f, "Str({})", s.as_ref()), + Value::Arr(arr) => write!(f, "Arr({:?})", arr.as_ref()), + Value::Dict(dict) => write!(f, "Dict({:?})", dict.as_ref()), + Value::Fn(_) => write!(f, "Fn()"), + Value::Ref(shv) => write!(f, "Ref({shv:?})"), + } + } +} + +impl SharedValue { + pub fn is_empty_dictionary(&self) -> bool { + if let SharedValue::Dict(d) = self { d.is_empty() } else { false } + } +} + +impl<'m> Value<'m> { + pub fn if_int_get(&self) -> Option { + match self { + Value::Int(n) => Some(*n), + Value::Ref(SharedValue::Int(n)) => Some(*n), + _ => None, + } + } + + pub fn if_str_get_ref(&self) -> Option<& String> { + match self { + Value::Str(s) => Some(s.as_ref()), + Value::Ref(SharedValue::Str(s)) => Some(s), + _ => None, + } + } + + pub fn is_str(&self) -> bool { + match self { + Value::Str(_) => true, Value::Ref(SharedValue::Str(_)) => true, + _ => false, + } + } + + pub fn get_str(self) -> String { + match self { + Value::Str(s) => Rc::try_unwrap(s).unwrap_or_else(|s| s.as_ref().clone()), + Value::Ref(SharedValue::Str(s)) => s.clone(), + _ => panic!("Not a string"), + } + } + + pub fn is_arr(&self) -> bool { + match self {Value::Arr(_) => true, Value::Ref(SharedValue::Arr(_)) => true, _ => false} + } + + pub fn is_dict(&self) -> bool { + match self {Value::Dict(_) => true, Value::Ref(SharedValue::Dict(_)) => true, _ => false} + } + + pub fn access_arr(&self, ind: usize) -> Option> { + match self { + Value::Arr(array) => array.get(ind).map(|r| r.clone()), + Value::Ref(SharedValue::Arr(array)) => array.get(ind) + .map(|rshv| Value::Ref(rshv)), + _ => panic!("Not an array") + } + } + + pub fn arr_len(&self) -> usize { + match self { + Value::Arr(array) => array.len(), + Value::Ref(SharedValue::Arr(array)) => array.len(), + _ => panic!("Not an array") + } + } + + pub fn access_dict(&self, key: &String) -> Option> { + match self { + Value::Dict(map) => map.get(key).map(|r| r.clone()), + Value::Ref(SharedValue::Dict(map)) => map.get(key) + .map(|rshv| Value::Ref(rshv)), + _ => panic!("Not a dictionary") + } + } + + pub fn dict_is_empty(&self) -> bool { + match self { + Value::Dict(dict) => dict.as_ref().is_empty(), + Value::Ref(SharedValue::Dict(dict)) => dict.is_empty(), + _ => panic!("Not a dictionary") + } + } + + pub fn type_name(&self) -> String { + match self { + Value::Int(_) => "Integer".into(), Value::Str(_) => "String".into(), - Value::Int(u64) => "Int".into(), Value::Arr(_) => "Array".into(), Value::Dict(_) => "Dictionary".into(), Value::Fn(_) => "Function".into(), - Value::Ref(i) => format!("Reference to {}", i.stringify_type()), - Value::RuntimeConst(_) => "Compilation Artifact".into(), + Value::Ref(r) => format!("Reference to {}", r.type_name()) + } + } + + pub fn to_bool(&self) -> Result { + if let Some(n) = self.if_int_get() { + Ok(n != 0) + } else if let Some(s) = self.if_str_get_ref() { + Ok(!s.is_empty()) + } else if self.is_arr() { + Ok(self.arr_len() > 0) + } else if self.is_dict() { + Ok(self.dict_is_empty()) + } else { + Err("Can't convert Function to bool".into()) } } } -impl Default for Value { - fn default() -> Self { - Value::Int(0) - } -} - -impl Debug for Value { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl<'a, 'm> Value<'m> { + pub fn if_fn_get(&'a self) -> Option<&'a (dyn Fn(&RuntimeEnv<'m>, Value<'m>, u32) -> Result, String> + 'm) > { match self { - Value::Str(s) => write!(f, "Str({})", s), - Value::Int(i) => write!(f, "Int({})", i), - Value::Arr(a) => write!(f, "Arr({:?})", a), - Value::Dict(d) => { - let dict_debug: Vec = d.iter() - .map(|(k, v)| format!("{}: {:?}", k, v)) - .collect(); - write!(f, "Dict({{{}}})", dict_debug.join(", ")) - } - Value::Fn(_) => write!(f, "Fn()"), - Value::Ref(&b) => write!(f, "Ref({b:?})"), - Value::RuntimeConst(_) => write!(f, "C-comp-pilat_onArrrrr%%#^#}}&_)"), + Value::Fn(func) => Some(func.as_ref()), + Value::Ref(SharedValue::Fn(func)) => Some(func), + _ => None } } } +impl<'m> RuntimeEnv<'m> { + pub fn call_fn(&'m self, f: Value<'m>, x: Value<'m>, recc: u32) -> Result, String> { + match f { + Value::Fn(func) => func.as_ref()(self, x, recc), + Value::Ref(SharedValue::Fn(func)) => func.as_ref()(self, x, recc), + _ => panic!(), + } + } +} + +impl<'m> RuntimeEnv<'m> { + /* Might change it into a macros one day */ + pub fn check_recc(&self, recc: u32) -> Result<(), String> { + if recc == 0 { Err("Recursion limit exceeded".into()) } else { Ok(()) } + } + + pub fn clean_rrc(& self, x: Value<'m>, recc: u32) -> Result, String> { + self.check_recc(recc)?; + match x { + Value::Ref(SharedValue::RuntimeConst(zero_arg_fn)) => + Ok(zero_arg_fn(self, recc - 1)?), + _ => Ok(x) + } + } + + pub fn execute_expression(&self, expr: &Expression, local: &[Value<'m>], recc: u32) -> Result, String> { + self.check_recc(recc)?; + match expr { + Expression::Root => Ok(Value::Ref(&self.mtgott.root)), + Expression::Local(i) => Ok(local[*i].clone()), + Expression::Int(n) => Ok(Value::Int(*n)), + /* Yes, I could have avoided cloning. But who cares anyway, consider using + * Element syntax for long text */ + Expression::Str(s) => Ok(Value::Str(Rc::new(s.clone()))), + Expression::Arr(arr) => Ok(Value::Arr(Rc::new(arr.iter() + .map(|e| -> Result { + self.execute_expression(e, local, recc - 1) + }).collect::, String>>()?))), + Expression::Attribute(e1, key) => { + let r: Value = self.execute_expression(e1, local, recc - 1)?; + if !r.is_dict() { return Err(format!("Taking attribute of {}", r.type_name())); } + match r.access_dict(key) { + Some(v) => self.clean_rrc(v, recc - 1), + None => Err(format!("Dictionary has no attribute {}", key)) + } + }, + Expression::Get(e1, e2) => { + let r1: Value = self.execute_expression(e1, local, recc - 1)?; + let r2: Value = self.execute_expression(e2, local, recc - 1)?; + if r1.is_arr(){ + match r2.if_int_get() { + Some(ind) => match r1.access_arr(ind as usize) { + Some(v) => self.clean_rrc(v, recc - 1), + None => Err(format!( + "Out of bounds: accessing array of size {} with index {}", + r1.arr_len(), ind)) + }, + None => Err(format!("Trying to access array using {}", r2.type_name())) + } + } else if r1.is_dict() { + match r2.if_str_get_ref() { + Some(key) => match r1.access_dict(key) { + Some(v) => self.clean_rrc(v, recc - 1), + None => Err(format!("Dictionary doesn't have key {}", key)) + }, + None => Err(format!("Trying to access dictionary using {}", r2.type_name())) + } + } else { + Err(format!("Operator [] does not work on {}", r1.type_name())) + } + } + Expression::Call(e1, e2) => { + let r1: Value = self.execute_expression(e1, local, recc - 1)?; + let r2: Value = self.execute_expression(e2, local, recc - 1)?; + match r1.if_fn_get() { + /* All functions must return Value-RRC */ + Some(f) => f(self, r2, recc - 1), + None => Err(format!("Can't call {}", r1.type_name())) + } + } + Expression::Lambda(NewLambdaExpression{ local_var_array: captured, expr_id: body_expr_id}) => { + // let mut where_arg_goes = usize::MAX; + let local_var_array: Vec = captured.iter().map(|oid| { + if *oid == usize::MAX { Value::Int(0) } else { local[*oid].clone() } + }).collect(); + let where_arg_goes = captured.iter().position(|oid| *oid == usize::MAX).unwrap(); + let body_expr_id: usize = *body_expr_id; + Ok(Value::Fn(Rc::new(move |re, x: Value, counter| -> Result { + let mut local_var_array: Vec = local_var_array.clone(); + local_var_array[where_arg_goes] = x; + re.execute_expression(&re.mtgott.source_expressions[body_expr_id], &local_var_array, counter - 1) + }))) + } + } + } +} + +/* If we have to add two paths and one is a prefix of another, like here: + * aaa/bbb > Dict{...} + * aaa > String("...") + * we should add objects with short path first, because they would otherwise override + * nested dictionaries containing objects with long paths */ +pub fn add_path_to_root(root: &mut SharedValue, path: &str, splitter: char, x: SharedValue) -> Result<(), Box>{ + let path: Vec = path.split(splitter).map(String::from).collect(); + let mut cur: &mut SharedValue = root; + assert!(path.len() > 0); + for i in 0..path.len() { + match cur { + SharedValue::Dict(dict) => { + cur = dict.entry(path[i].clone()).or_insert(SharedValue::Dict(HashMap::new())); + } + _ => return Err(format!("Overlapping root paths: {}", path[0..i].join(&splitter.to_string())).into()) + } + } + if !cur.is_empty_dictionary() { + return Err(format!("Overlapping root paths: {}", path.join(&splitter.to_string())).into()) + } + *cur = x; + Ok(()) +} + +pub fn add_path_with_slashes_to_root(root: &mut SharedValue, path: &str, x: SharedValue) -> Result<(), Box> { + add_path_to_root(root, path, '/', x) +} + +pub fn add_path_with_dots_to_root(root: &mut SharedValue, path: &str, x: SharedValue) -> Result<(), Box> { + add_path_to_root(root, path, '.', x) +} + +pub fn access_root_by_path<'m>(root: &'m SharedValue, path: &str, splitter: char) -> Result<&'m SharedValue, Box> { + let path: Vec = path.split(splitter).map(String::from).collect(); + let mut cur = root; + for i in 0..path.len() { + cur = match cur { + SharedValue::Dict(d) => match d.get(&path[i]) { + Some(nxt) => nxt, + None => return Err( + format!("No such path in root: {}", path[0..i+1].join(&splitter.to_string())).into()) + } + _ => return Err( + format!("Can't access {}, {} is not a dictionary", + path[0..i+1].join(&splitter.to_string()), path[0..i].join(&splitter.to_string())).into()) + } + } + Ok(cur) +} + +pub fn access_root_by_path_with_slashes<'m>(root: &'m SharedValue, path: &str) -> Result<&'m SharedValue, Box> { + access_root_by_path(root, path, '/') +} + +pub fn access_root_by_path_with_dots<'m>(root: &'m SharedValue, path: &str) -> Result<&'m SharedValue, Box> { + access_root_by_path(root, path, '.') +} + + impl MTGOTT { - fn get_sub_value<'a>(&'a self, name: &str) -> Result<&'a Value, String> { - let mut cur: &Value = &self.root; - let parts: Vec = name.split(".").map(String::from).collect(); - for i in 0usize..parts.len() { - match cur { - Value::Dict(hashmap) => { - match hashmap.get(&parts[i]) { - Some(nxt ) => { cur = nxt; } - None => return Err(format!("No such root element: {}", parts[..=i].join("/"))) - } - } - _ => return Err(format!("Not a dictionary: {}", parts[..i].join("/"))) - } - } - Ok(cur) - } - - pub fn render(&self, guest_root: Value, name: &str, recursion_limit: u32) -> Result> { - let main = self.get_sub_value(name)?; - let res = match main { - Value::Fn(main_func) => { - 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()) - }; - main_func(&d_stack, self, guest_root, recursion_limit)? - } - Value::Str(me) => Value::Ref(main), - Value::RuntimeConst(zero_arg_fnc) => { - let mut d_stack = RuntimeEnv(RefCell::new(Vec::new())); - zero_arg_fnc(&mut d_stack, self, recursion_limit)? - }, - _ => return Err(format!("Called {name} template that is not a function / string, but {}", main.stringify_type()).into()) - }; - match res { - Value::Ref(Value::Str(s1)) => Ok(s1.as_ref().clone()), - Value::Str(answer) => if Rc::strong_count(&answer) == 1 { - Ok(Rc::try_unwrap(answer).unwrap()) + /* name is a path with dots (and obviously without extension) */ + pub fn render<'t>(&'t self, guest_root: Value<'t>, name: &str, recursion_limit: u32) -> Result> { + let re = RuntimeEnv::new(self); + let main: Value = re.clean_rrc(Value::Ref(access_root_by_path_with_dots(&self.root, name)?), recursion_limit)?; + if let Some(func_main) = main.if_fn_get() { + let result = func_main(&re, guest_root, recursion_limit)?; + if result.is_str(){ + Ok(result.get_str()) } else { - Ok(answer.as_ref().clone()) + Err(format!("Function {name} returned {}", result.type_name()).into()) } - _ => Err(format!("Template {name} returned {} instead of string", res.stringify_type()).into()) + } else if main.is_str() { + Ok(main.get_str()) + } else { + Err(format!("Path {name} leads to {}", main.type_name()).into()) } } } \ No newline at end of file