From 495e0f660d569c51178f40f42fca263fa2c5f0d0 Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Tue, 8 Apr 2025 13:26:26 +0300 Subject: [PATCH] Right now, I finally know what to do. I see it all --- mtgott/src/dirsearch.rs | 13 +- mtgott/src/lambda_compilation.rs | 4 + mtgott/src/parser.rs | 198 +++++++++++++++++++------------ mtgott/src/runtime.rs | 45 ++++--- 4 files changed, 160 insertions(+), 100 deletions(-) diff --git a/mtgott/src/dirsearch.rs b/mtgott/src/dirsearch.rs index 0b74868..e1e3ceb 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, val_lambda_check_argc}; +use super::runtime::{HigherLevelFunc, Value, DebugState}; use super::parser::{parse_one_file_simplified, parse_one_file_packed}; use super::lambda_compilation::{plemege_to_value}; use std::collections::HashMap; @@ -78,7 +78,7 @@ fn add_path_to_root(root: &mut Value, unsplit_path: &str, splitter: char, obj: V Value::Dict(hashmap) => { cur = hashmap.entry(String::from(parts[i])).or_insert(Default::default()); } - _ => return Err(format!("Overlapping root elements {}", parts[..i].join(&*splitter)).into()) + _ => return Err(format!("Overlapping root elements {}", parts[..i].join(&String::from(splitter))).into()) } } match cur { @@ -125,7 +125,7 @@ pub fn get_all_templates(p: &str, plain_ext: &str) -> Result Result>{ +pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: Box) -> Result>{ let mut root = get_all_templates(p, plain_ext)?; add_path_with_slashes_to_root(&mut root, "sanitize", Value::Fn(sanitize))?; add_path_with_slashes_to_root(&mut root, "cmd_tag_start", Value::Str("{%".into()))?; @@ -138,13 +138,12 @@ pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: Highe pub fn get_root_html(p: &str) -> Result>{ get_all_templates_plus_builtins(p, ".html", Box::new( - |d_state: &DebugState, _: &Value, _: &Value, args: &[&Value]| -> Result> { + |d_state: &DebugState, _: &Value, arg: &Value| -> Result> { let _g = d_state.register("In sanitizer".into()); - val_lambda_check_argc(args, 1)?; - match args[0]{ + match arg { Value::Str(s) => Ok(Value::Str(escape_for_html(&s))), Value::Int(num) => Ok(Value::Str(num.to_string())), - _ => Ok(Value::Str(escape_for_html(&format!("{:?}", args[0])))) + _ => Ok(Value::Str(escape_for_html(&format!("{:?}", arg)))) } })) } diff --git a/mtgott/src/lambda_compilation.rs b/mtgott/src/lambda_compilation.rs index ccbd730..c38fec6 100644 --- a/mtgott/src/lambda_compilation.rs +++ b/mtgott/src/lambda_compilation.rs @@ -8,6 +8,10 @@ fn cat_vec(arr: Vec) -> String { arr.into_iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc }) } +fn compile(el: &[SubElement]) -> Result, Box> { + return Err("sd".into()) +} + /* This function is supposed to compile all elements in one particular file */ pub fn plemege_to_value(mut x: Plemege, file_path: &str) -> Result> { match x { diff --git a/mtgott/src/parser.rs b/mtgott/src/parser.rs index fec6e87..9a24deb 100644 --- a/mtgott/src/parser.rs +++ b/mtgott/src/parser.rs @@ -9,7 +9,7 @@ pub enum Expression { Argument(u64), Get(Box, Box), Attribute(Box, String), - Call(Box, Vec), + Call(Box, Box), Int(u64), None, } @@ -86,7 +86,7 @@ pub enum FileParsingErrorKind { expected_closing_round_bracket, cant_start_word_immediately_after_digit, integer_parsing_error, - illegal_object_name, + illegal_root_attribute_name, expected_attribute_name_after_dot, illegal_attribute_name, expected_closing_square_bracket, @@ -95,6 +95,9 @@ pub enum FileParsingErrorKind { unmatched_element_ending_tag, unmatched_magic_block_ending_tag, recursion_limit_exceeded, + leave_space_between_dollar_argument_and_other_arguments, + cant_use_dollar_in_expression_of_element_without_dollar_argument, + illegal_dollar_toplevel_attribute_name, } use self::FileParsingErrorKind::*; @@ -187,14 +190,19 @@ impl<'a> Parser<'a> { } } - fn new_unexpected_char_error(&self, kind: FileParsingErrorKind) -> FileParsingError { + fn next_p(&self) -> usize { match self.text[self.p..].char_indices().next() { - Some((off, _)) => FileParsingError::new(kind, self.p, self.p + off), - None => FileParsingError::new(kind, self.p, self.p), + Some((off, _)) => self.p + off, + None => self.p, } } - fn parse_pack_plus_ending(&mut self, top: bool) -> Result { + fn new_unexpected_char_error(&self, kind: FileParsingErrorKind) -> FileParsingError { + FileParsingError::new(kind, self.p, self.next_p()) + } + + fn parse_pack_plus_ending(&mut self, top: bool, recc: u32) -> Result { + self.check_recursion_limit(recc)?; let mut res: HashMap = HashMap::new(); loop { self.skip_whitespace(); @@ -232,7 +240,7 @@ impl<'a> Parser<'a> { return Err(self.new_unexpected_char_error(expected_pack_opening_tag_end)) } self.p += 2; - res.insert(String::from(child_name), self.parse_pack_plus_ending(false)?); + res.insert(String::from(child_name), self.parse_pack_plus_ending(false, recc - 1)?); } else if self.is_ahead("{@") { self.p += 2; self.skip_whitespace(); @@ -249,6 +257,18 @@ impl<'a> Parser<'a> { return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p)) } let mut arg_names: Vec<&str> = Vec::new(); + + /* Some function have $ as their first argument, we will let them have it */ + self.skip_whitespace(); + if self.is_char_ahead('$') { + arg_names.push("$"); + self.p += 1; + if self.is_word_ahead() { + return Err(FileParsingError::new(leave_space_between_dollar_argument_and_other_arguments, + self.p - 1, self.next_p())); + } + } + loop { self.skip_whitespace(); if self.is_ahead("@}") { @@ -269,7 +289,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)?; + let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(&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)) } @@ -390,7 +410,8 @@ fn fix_whitespaces_in_element(subels: &mut Vec) { 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>) -> Result<(Element, ReasonOfElementEnd), FileParsingError> { + fn parse_element_plus_ending_tag(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<(Element, ReasonOfElementEnd), FileParsingError> { + self.check_recursion_limit(recc)?; let mut res: Vec = Vec::new(); let mut tp1 = self.p; @@ -413,7 +434,7 @@ impl<'a> Parser<'a> { } else if self.is_ahead("{{") { fin_static(self, tp1, &mut res); self.p += 2; - let expr: Expression = self.parse_expression(arg_names)?; + let expr: Expression = self.parse_expression(arg_names, recc)?; if !self.is_ahead("}}") { return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p)); } @@ -421,15 +442,15 @@ impl<'a> Parser<'a> { if !matches!(expr, Expression::None){ res.push(SubElement::InsertExpr( Expression::Call( - Box::new(Expression::ToplevelAttribute("sanitize".into())), - vec![expr]) + Box::new(Expression::Attribute(Box::new(Expression::Root), "sanitize".into())), + Box::new(expr)) )); } tp1 = self.p; } else if self.is_ahead("{[") { fin_static(self, tp1, &mut res); self.p += 2; - let expr: Expression = self.parse_expression(arg_names)?; + let expr: Expression = self.parse_expression(arg_names, recc)?; if !self.is_ahead("]}") { return Err(FileParsingError::new(expected_roughinsert_tag_end_after_expression, self.p - 2, self.p)) } @@ -486,9 +507,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)?), - "if" => res.push(self.parse_if(arg_names)?), - "let" => res.push(self.parse_let(arg_names)?), + "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; @@ -500,7 +521,7 @@ impl<'a> Parser<'a> { fn parse_expression_at_cmd_tag_end(&mut self, arg_names: &Vec<&str>) -> Result { let p1 = self.p; - let expr: Expression = self.parse_expression(arg_names)?; + let expr: Expression = self.parse_expression(arg_names, 150)?; if matches!(expr, Expression::None) { return Err(FileParsingError::new(expected_nonempty_expression, p1, self.p)) } @@ -512,12 +533,13 @@ impl<'a> Parser<'a> { /* It turned out to be so complex I put it in a separate function. * It parses expr %} block {% else if expr %} block {% else %} block {%} */ - fn parse_if(&mut self, arg_names: &Vec<&str>) -> Result { + fn parse_if(&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)?; - let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names)?; + 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 | @@ -526,7 +548,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)?; + 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)); } @@ -539,7 +561,8 @@ impl<'a> Parser<'a> { Ok(SubElement::If(IfSubElement{branches: blocks, conditions})) } - fn parse_let(&mut self, arg_names: &Vec<&str>) -> 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; self.skip_normal_word(); @@ -559,7 +582,7 @@ impl<'a> Parser<'a> { let expr = self.parse_expression_at_cmd_tag_end(arg_names)?; 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)?; + 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)); } @@ -583,7 +606,8 @@ impl<'a> Parser<'a> { Ok(name) } - fn parse_for(&mut self, arg_names: &Vec<&str>) -> 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(); let name1 = self.parse_for_new_variable(&arg_names_extended)?; @@ -605,7 +629,7 @@ impl<'a> Parser<'a> { self.p += 1; let expr = self.parse_expression_at_cmd_tag_end(arg_names)?; - let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended)?; + let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, recc - 1)?; let separator: String = String::from(match ending.cmd { BlockEndingTag::NOGAP => "", BlockEndingTag::GAP => " ", @@ -621,13 +645,51 @@ impl<'a> Parser<'a> { self.is_ahead("]}") || self.is_ahead("}}") || self.is_ahead("%}") } + fn fn_parse_expression_l2_trail(&mut self, arg_names: &Vec<&str>, mut from: Expression, recc: u32) -> Result { + self.check_recursion_limit(recc)?; + loop { + self.skip_whitespace(); + from = if self.is_char_ahead('.') { + self.p += 1; + self.skip_whitespace(); + let attrp1 = self.p; + self.skip_normal_word(); + if attrp1 == self.p { + return Err(self.new_unexpected_char_error(expected_attribute_name_after_dot)); + } + let attr_name = &self.text[attrp1..self.p]; + if is_bad_name(attr_name) { + return Err(FileParsingError::new(illegal_attribute_name, attrp1, self.p)); + } + Expression::Attribute(Box::new(from), String::from(attr_name)) + } else if self.is_char_ahead('[') { + let sqbrp1 = self.p; + self.p += 1; + let sub_expr = self.parse_expression(arg_names, recc - 1)?; + self.skip_whitespace(); + if !self.is_char_ahead(']') { + return Err(self.new_unexpected_char_error(expected_closing_square_bracket)) + } + self.p += 1; + if matches!(sub_expr, Expression::None) { + return Err(FileParsingError::new(empty_expression_inside_square_brackets, sqbrp1, self.p)) + } + Expression::Get(Box::new(from), Box::new(sub_expr)) + } else { + break + } + } + Ok(from) + } + /* l1 expression = l2 space l2 space ... space l2 */ - fn parse_expression_l2(&mut self, arg_names: &Vec<&str>) -> Result { + fn parse_expression_l2(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result { + self.check_recursion_limit(recc)?; self.skip_whitespace(); if self.is_char_ahead('(') { let p1 = self.p; self.p += 1; - let expr = self.parse_expression(arg_names)?; + let expr = self.parse_expression(arg_names, recc - 1)?; self.skip_whitespace(); if !self.is_char_ahead(')') { return Err(self.new_unexpected_char_error(expected_closing_round_bracket)) @@ -657,81 +719,67 @@ impl<'a> Parser<'a> { self.skip_normal_word(); let toplevel_name = &self.text[p1..self.p]; if is_bad_name(toplevel_name) { - return Err(FileParsingError::new(illegal_object_name, p1, self.p)); + return Err(FileParsingError::new(illegal_root_attribute_name, p1, self.p)); } - let mut bg: Expression = match arg_names.iter().rposition(|&n| n == toplevel_name) { + let bg: Expression = match arg_names.iter().rposition(|&n| n == toplevel_name) { Some(i) => Expression::Argument(i as u64), - None => Expression::ToplevelAttribute(toplevel_name.into()) + None => Expression::Attribute(Box::new(Expression::Root), toplevel_name.into()) }; - loop { - self.skip_whitespace(); - bg = if self.is_char_ahead('.') { - self.p += 1; - self.skip_whitespace(); - let attrp1 = self.p; - self.skip_normal_word(); - if attrp1 == self.p { - return Err(self.new_unexpected_char_error(expected_attribute_name_after_dot)); - } - let attr_name = &self.text[attrp1..self.p]; - if is_bad_name(attr_name) { - return Err(FileParsingError::new(illegal_attribute_name, attrp1, self.p)); - } - Expression::Attribute(Box::new(bg), String::from(attr_name)) - } else if self.is_char_ahead('[') { - let sqbrp1 = self.p; - self.p += 1; - let sub_expr = self.parse_expression(arg_names)?; - self.skip_whitespace(); - if !self.is_char_ahead(']') { - return Err(self.new_unexpected_char_error(expected_closing_square_bracket)) - } - self.p += 1; - if matches!(sub_expr, Expression::None) { - return Err(FileParsingError::new(empty_expression_inside_square_brackets, sqbrp1, self.p)) - } - Expression::Get(Box::new(bg), Box::new(sub_expr)) - } else { - break - } + self.fn_parse_expression_l2_trail(arg_names, bg, recc - 1) + } else if self.is_char_ahead('$') { + if arg_names.len() < 1 || arg_names[0] != "$" { + return Err(self.new_unexpected_char_error(cant_use_dollar_in_expression_of_element_without_dollar_argument)); } - return Ok(bg) + self.p += 1; + let bg: Expression = if self.is_word_ahead() { + let p1 = self.p; + self.skip_normal_word(); + let toplevel_name = &self.text[p1..self.p]; + if is_bad_name(toplevel_name) { + return Err(FileParsingError::new(illegal_dollar_toplevel_attribute_name, p1, self.p)); + } + Expression::Attribute(Box::new(Expression::Argument(0)), toplevel_name.into()) + } else { + Expression::Argument(0) + }; + self.fn_parse_expression_l2_trail(arg_names, bg, recc - 1) } else { return Ok(Expression::None) } } - fn parse_expression(&mut self, arg_names: &Vec<&str>) -> Result { - let e1: Expression = self.parse_expression_l2(arg_names)?; - let mut call_args: Vec = Vec::new(); + fn parse_expression(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result { + self.check_recursion_limit(recc)?; + let mut e1: Expression = self.parse_expression_l2(arg_names, recc - 1)?; /* It is okay to enter call_args reading loop even when e1 is None. If parse_expression_l2 returned None, subsequent call to parse_expression_l2 - is guaranteed to return None. Arg list will be empty and the final `if` will choose - the second branch, which will return Expression::None */ + is guaranteed to return None. If there are no arguments, e1 does not get turn into + Expression::Call */ loop { - let arg_expr: Expression = self.parse_expression_l2(arg_names)?; + let arg_expr: Expression = self.parse_expression_l2(arg_names, recc - 1)?; if matches!(arg_expr, Expression::None) { break } - call_args.push(arg_expr) + e1 = Expression::Call(Box::new(e1), Box::new(arg_expr)); } - Ok(if call_args.len() > 0 { - Expression::Call(Box::new(e1), call_args) - } else { - e1 - }) + Ok(e1) + } + + fn check_recursion_limit(&self, recc: u32) -> Result<(), FileParsingError> { + if recc == 0 { Err(self.new_unexpected_char_error(recursion_limit_exceeded)) } else { Ok(() )} } } + /* Parses a file treating it like a package (where other packages and elements could be located) */ pub fn parse_one_file_packed(text: &str) -> Result { let mut parser: Parser = Parser{text, p: 0}; - parser.parse_pack_plus_ending(true) + parser.parse_pack_plus_ending(true, 150) } pub fn parse_one_file_simplified(text: &str) -> Result { let mut parser: Parser = Parser{text, p: 0}; - let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&Vec::new())?; + let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&vec!["$"], 150)?; match tt.cmd { BlockEndingTag::EOF => Ok(Plemege::Element(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 86a7c89..d38d016 100644 --- a/mtgott/src/runtime.rs +++ b/mtgott/src/runtime.rs @@ -25,14 +25,23 @@ impl<'a> DebugState { } } -pub type HigherLevelFunc = Box Result>>; +/* 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>; pub enum Value { Str(String), Int(u64), Arr(Vec), Dict(HashMap), - Fn(HigherLevelFunc), + Fn(Box), + /* 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::RuntimeStr. At runtime, this compilation artifact is the same thing as + Value::Str */ + RuntimeStr(Box) } impl Value { @@ -41,15 +50,6 @@ impl Value { } } -/* Useful utility to put in every function you see */ -pub fn val_lambda_check_argc(argv: &[&Value], argc_required: usize) -> Result<(), String> { - if argv.len() == argc_required { - Ok(()) - } else { - Err(format!("Function takes {argc_required} arguments, but {} were given", argv.len())) - } -} - impl Default for Value { fn default() -> Self { Value::Int(0) @@ -69,6 +69,7 @@ impl Debug for Value { write!(f, "Dict({{{}}})", dict_debug.join(", ")) } Value::Fn(_) => write!(f, "Fn()"), + Value::RuntimeStr(_) => write!(f, "C-comp-pilat_onArrrr%%#^#}}&_)") } } } @@ -88,7 +89,7 @@ impl<'t> PartialEq for Value { fn get_sub_value<'a>(root: &'a Value, name: &str) -> Result<&'a Value, String> { let mut cur: &Value = root; - let parts = name.split(".").map(String::from).collect(); + let parts: Vec = name.split(".").map(String::from).collect(); for i in 0usize..parts.len() { match cur { Value::Dict(hashmap) => { @@ -105,26 +106,34 @@ fn get_sub_value<'a>(root: &'a Value, name: &str) -> Result<&'a Value, String> { /* If some `top-level-attribute` was not found in root, we search for it in guest_root. * This is our way of passing arguments for template */ -pub fn generate_template(root: &Value, guest_root: &Value, name: &str) -> Result> { +pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) -> Result> { let main = get_sub_value(root, name)?; let main = match main { Value::Dict(p) => { match p.get("main".into()) { Some(v2) => v2, - None => return Err(Box::new(format!("Called {name} template is a dictionary without `main()`"))) + None => return Err(format!("Called {name} template is a dictionary without `main()`").into()) } } _ => main, }; - match main { + match main { Value::Fn(main_func) => { let d_stack = DebugState(RefCell::new(Vec::new())); - let rv = main_func(&d_stack, root, &[guest_root])?; + 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)?; match rv { Value::Str(answer) => Ok(answer), - _ => Err(Box::new(format!("template {name} returned not a string"))) + _ => Err(format!("template {name} returned not a string").into()) } } - _ => Err(Box::new(format!("Called {name} template that is not a function"))) + 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)?) + } + _ => Err(format!("Called {name} template that is not a function / string").into()) } }