Right now, I finally know what to do. I see it all

This commit is contained in:
Андреев Григорий 2025-04-08 13:26:26 +03:00
parent 374d2aef06
commit 495e0f660d
4 changed files with 160 additions and 100 deletions

View File

@ -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<Value, Box<dyn Err
Ok(res)
}
pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: HigherLevelFunc) -> Result<Value, Box<dyn Error>>{
pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: Box<HigherLevelFunc>) -> Result<Value, Box<dyn Error>>{
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<Value, Box<dyn Error>>{
get_all_templates_plus_builtins(p, ".html", Box::new(
|d_state: &DebugState, _: &Value, _: &Value, args: &[&Value]| -> Result<Value, Box<dyn Error>> {
|d_state: &DebugState, _: &Value, arg: &Value| -> Result<Value, Box<dyn Error>> {
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))))
}
}))
}

View File

@ -8,6 +8,10 @@ fn cat_vec(arr: Vec<String>) -> 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<HigherLevelFunc>, Box<dyn Error>> {
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<Value, Box<dyn Error>> {
match x {

View File

@ -9,7 +9,7 @@ pub enum Expression {
Argument(u64),
Get(Box<Expression>, Box<Expression>),
Attribute(Box<Expression>, String),
Call(Box<Expression>, Vec<Expression>),
Call(Box<Expression>, Box<Expression>),
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<Plemege, FileParsingError> {
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<Plemege, FileParsingError> {
self.check_recursion_limit(recc)?;
let mut res: HashMap<String, Plemege> = 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<SubElement>) {
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<SubElement> = 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<Expression, FileParsingError> {
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<SubElement, FileParsingError> {
fn parse_if(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
self.check_recursion_limit(recc)?;
let mut conditions: Vec<Expression> = Vec::new();
let mut blocks: Vec<Element> = 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<SubElement, FileParsingError> {
fn parse_let(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
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<SubElement, FileParsingError> {
fn parse_for(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
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<Expression, FileParsingError> {
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<Expression, FileParsingError> {
fn parse_expression_l2(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<Expression, FileParsingError> {
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<Expression, FileParsingError> {
let e1: Expression = self.parse_expression_l2(arg_names)?;
let mut call_args: Vec<Expression> = Vec::new();
fn parse_expression(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<Expression, FileParsingError> {
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<Plemege, FileParsingError> {
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<Plemege, FileParsingError> {
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)),

View File

@ -25,14 +25,23 @@ impl<'a> DebugState {
}
}
pub type HigherLevelFunc = Box<dyn Fn(&DebugState, &Value, &[&Value]) -> Result<Value, Box<dyn Error>>>;
/* Arguments are: stack trace for debug, root, actual function argument */
pub type HigherLevelFunc = dyn Fn(&DebugState, &Value, &Value) -> Result<Value, Box<dyn Error>>;
/* Arguments are: stack trace for debug, root */
pub type OopsForgotToPrecompileThis = dyn Fn(&DebugState, &Value) -> Result<String, Box<dyn Error>>;
pub enum Value {
Str(String),
Int(u64),
Arr(Vec<Value>),
Dict(HashMap<String, Value>),
Fn(HigherLevelFunc),
Fn(Box<HigherLevelFunc>),
/* 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<OopsForgotToPrecompileThis>)
}
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(<function>)"),
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<String> = 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<String, Box<dyn Error>> {
pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) -> Result<String, Box<dyn Error>> {
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())
}
}