Daily update. Doing big rewrite of everything. Rewrote parser.rs. Smart pointers are back, yay

This commit is contained in:
Андреев Григорий 2025-04-20 00:04:01 +03:00
parent c5f71afa5c
commit a9023c6a8c
7 changed files with 435 additions and 304 deletions

View File

@ -4,9 +4,10 @@ use std::env;
use std::process; use std::process;
use crate::mtgott::charclasses::is_bad_name; use crate::mtgott::charclasses::is_bad_name;
use crate::mtgott::dirsearch::get_root_html; use crate::mtgott::dirsearch::get_root_html;
use crate::mtgott::runtime::{Value, generate_template}; use crate::mtgott::runtime::{Value, MTGOTT};
use std::collections::HashMap; use std::collections::HashMap;
use mtgott::dirsearch::add_path_with_dots_to_root; use mtgott::dirsearch::add_path_with_dots_to_root;
use std::rc::Rc;
fn usage() -> ! { fn usage() -> ! {
eprintln!("Usage: program <path> [-D name value]..."); eprintln!("Usage: program <path> [-D name value]...");
@ -40,11 +41,11 @@ fn main() {
usage(); usage();
} }
} }
let mut guest_root = Value::Dict(HashMap::new()); let mut guest_root = Value::Dict(Rc::new(HashMap::new()));
for (name_path, val) in defines { for (name_path, val) in defines {
add_path_with_dots_to_root(&mut guest_root, &name_path, Value::Str(val)).unwrap(); add_path_with_dots_to_root(&mut guest_root, &name_path, Value::Str(Rc::new(val))).unwrap();
} }
let dir_root = get_root_html(&path_arg).unwrap(); let dir_root = get_root_html(&path_arg).unwrap();
let res = generate_template(&dir_root, &guest_root, &template_name).unwrap(); let res = dir_root.render(guest_root, &template_name, 500).unwrap();
print!("{res}") print!("{res}")
} }

View File

@ -14,7 +14,7 @@ pub fn is_digit(ch: char) -> bool {
pub fn is_normal_word_constituent(ch: char) -> bool { pub fn is_normal_word_constituent(ch: char) -> bool {
('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) ('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch)
|| '-' == ch || '_' == ch || "-_+/|&~!^%*".contains(ch)
} }
pub fn is_normal_word(s: &str) -> bool { pub fn is_normal_word(s: &str) -> bool {
@ -26,10 +26,6 @@ pub fn escape_for_html(s: &str) -> String {
.replace("'", "&#39;").replace("\"", "&quot;") .replace("'", "&#39;").replace("\"", "&quot;")
} }
pub fn is_bad_name(s: &str) -> bool { pub fn is_special_name(s: &str) -> bool {
is_illegal_name(s) || s == "_" s.chars().any(|ch| "+'/|&~!^%*".contains(ch)) || s.ends_with("-") || s == "this"
}
pub fn is_illegal_name(s: &str) -> bool {
s == "" || s == "root" || s == "self" || s == "super"
} }

View File

@ -2,15 +2,17 @@ use std::{fs};
use std::fs::{read_dir, metadata, canonicalize}; use std::fs::{read_dir, metadata, canonicalize};
use std::path::PathBuf; use std::path::PathBuf;
use std::error::Error; use std::error::Error;
use super::charclasses::{escape_for_html, is_bad_name}; use super::charclasses::{escape_for_html, is_special_name};
use super::runtime::{HigherLevelFunc, Value, RuntimeEnv}; use super::runtime::{Value, RuntimeEnv, MTGOTT};
use super::parser::{parse_one_file_simplified, parse_one_file_packed}; use super::parser::{parse_one_file_simplified, parse_one_file_packed};
use super::lambda_compilation::{plemege_to_value}; use super::lambda_compilation::{plemege_to_value};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
pub fn search_dir_rec_helper<F: Fn(&str) -> bool>( pub fn search_dir_rec_helper<F: Fn(&str) -> bool>(
res: &mut Vec<Vec<String>>, res: &mut Vec<Vec<String>>,
fs_path: PathBuf, virtual_path: String, allowed_extensions: &[&str], is_valid_name: &F, recc: u32 fs_path: PathBuf, virtual_path: String,
allowed_extensions: &[&str], is_valid_name: fn(&str) -> bool, recc: u32
)-> Result<(), Box<dyn Error>> { )-> Result<(), Box<dyn Error>> {
if recc == 0 { return Err("Recursion limit exceeded".into()); } if recc == 0 { return Err("Recursion limit exceeded".into()); }
let fs_path = canonicalize(fs_path)?; let fs_path = canonicalize(fs_path)?;
@ -42,7 +44,8 @@ pub fn search_dir_rec_helper<F: Fn(&str) -> bool>(
Ok(()) Ok(())
} }
pub fn search_dir<F: Fn(&str) -> bool>(p: &str, allowed_extensions: &[&str], is_valid_name: &F) pub fn search_dir(
p: &str, allowed_extensions: &[&str], is_valid_name: fn(&str) -> bool)
-> Result<Vec<Vec<String>>, Box<dyn Error>> { -> Result<Vec<Vec<String>>, Box<dyn Error>> {
let mut res: Vec<Vec<String>> = (0..allowed_extensions.len()).map(|_| Vec::new()).collect(); let mut res: Vec<Vec<String>> = (0..allowed_extensions.len()).map(|_| Vec::new()).collect();
search_dir_rec_helper(&mut res, PathBuf::from(p), String::new(), allowed_extensions, is_valid_name, 100)?; search_dir_rec_helper(&mut res, PathBuf::from(p), String::new(), allowed_extensions, is_valid_name, 100)?;
@ -60,7 +63,7 @@ pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result<MtgottDirContent, B
&(String::from(".mtgott") + plain_ext), &(String::from(".mtgott") + plain_ext),
&(String::from(".imtgott") + plain_ext), &(String::from(".imtgott") + plain_ext),
plain_ext plain_ext
], &is_bad_name)?; ], is_special_name)?;
Ok(MtgottDirContent{ Ok(MtgottDirContent{
mtgott: std::mem::take(&mut all[0]), mtgott: std::mem::take(&mut all[0]),
imtgott: std::mem::take(&mut all[1]), imtgott: std::mem::take(&mut all[1]),
@ -97,53 +100,58 @@ pub fn add_path_with_dots_to_root(root: &mut Value, unsplit_path: &str, obj: Val
add_path_to_root(root, unsplit_path, '.', obj) add_path_to_root(root, unsplit_path, '.', obj)
} }
pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<Value, Box<dyn Error>> { pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<MTGOTT, Box<dyn Error>> {
let source = search_mtgott_dir(p, plain_ext)?; let source = search_mtgott_dir(p, plain_ext)?;
let mut res: Value = Value::Dict(HashMap::new()); let mut res = MTGOTT{root: Value::Dict(Rc::new(HashMap::new())), source_expressions: Vec::new(), source_elements: Vec::new()};
for cut_path in source.mtgott { for cut_path in source.mtgott {
let path = format!("{cut_path}.mtgott{plain_ext}"); let path = format!("{cut_path}.mtgott{plain_ext}");
let text_bytes = fs::read(PathBuf::from(p).join(&path))?; let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
let text = std::str::from_utf8(&text_bytes)?; let text = std::str::from_utf8(&text_bytes)?;
let plemege = parse_one_file_packed(text)?; let plemege = parse_one_file_packed(text, &cut_path, &mut res.source_expressions)?;
let compiled = plemege_to_value(plemege, &path, 150)?; let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?;
add_path_to_root(&mut res, &cut_path, '/', compiled)? add_path_to_root(&mut res.root, &cut_path, '/', compiled)?
} }
for cut_path in source.imtgott { for cut_path in source.imtgott {
let path = format!("{cut_path}.imtgott{plain_ext}"); let path = format!("{cut_path}.imtgott{plain_ext}");
let text_bytes = fs::read(PathBuf::from(p).join(&path))?; let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
let text = std::str::from_utf8(&text_bytes)?; let text = std::str::from_utf8(&text_bytes)?;
let plemege = parse_one_file_simplified(text)?; let plemege = parse_one_file_simplified(text, &cut_path, &mut res.source_expressions)?;
let compiled = plemege_to_value(plemege, &path, 150)?; let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?;
add_path_to_root(&mut res, &cut_path, '/', compiled)? add_path_to_root(&mut res.root, &cut_path, '/', compiled)?
} }
for cut_path in source.plain { for cut_path in source.plain {
let path = format!("{cut_path}{plain_ext}"); let path = format!("{cut_path}{plain_ext}");
let text_bytes = fs::read(PathBuf::from(p).join(&path))?; let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
let text = String::from_utf8(text_bytes)?; let text = String::from_utf8(text_bytes)?;
add_path_to_root(&mut res, &cut_path, '/', Value::Str(text))? add_path_to_root(&mut res.root, &cut_path, '/', Value::Str(Rc::new(text)))?
} }
Ok(res) Ok(res)
} }
pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: Box<HigherLevelFunc>) -> Result<Value, Box<dyn Error>>{ pub fn get_all_templates_plus_builtins(
let mut root = get_all_templates(p, plain_ext)?; p: &str, plain_ext: &str,
add_path_with_slashes_to_root(&mut root, "sanitize", Value::Fn(sanitize))?; sanitize: fn(&str) -> String,
add_path_with_slashes_to_root(&mut root, "cmd_tag_start", Value::Str("{%".into()))?; ) -> Result<MTGOTT, Box<dyn Error>>{
add_path_with_slashes_to_root(&mut root, "write_tag_start", Value::Str("{{".into()))?; let mut res = get_all_templates(p, plain_ext)?;
add_path_with_slashes_to_root(&mut root, "roughinsert_tag_start", Value::Str("{[".into()))?; add_path_with_slashes_to_root(&mut res.root, "sanitize", Value::Fn(Rc::new(
add_path_with_slashes_to_root(&mut root, "magic_block_ending_tag", Value::Str("{%}".into()))?; /* One obvious problem with this sanitizer is that it makes copy even when it
add_path_with_slashes_to_root(&mut root, "element_ending_tag", Value::Str("{@}".into()))?; * takes full ownership of some string and does not have to replace any characters*/
Ok(root) |d_state: &RuntimeEnv, _: &MTGOTT, arg: Value, _: u32| -> Result<Value, String> {
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)))))
}
})))?;
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()))?;
Ok(res)
} }
pub fn get_root_html(p: &str) -> Result<Value, Box<dyn Error>>{ pub fn get_root_html(p: &str) -> Result<MTGOTT, Box<dyn Error>>{
get_all_templates_plus_builtins(p, ".html", Box::new( get_all_templates_plus_builtins(p, ".html", escape_for_html)
|d_state: &RuntimeEnv, _: &Value, arg: &Value, _: u32| -> Result<Value, String> {
let _g = d_state.register("In sanitizer".into());
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!("{:?}", arg))))
}
}))
} }

View File

@ -46,11 +46,11 @@ impl RuntimeEnv {
} }
fn get_needed_if_block<'i>( fn get_needed_if_block<'i>(
&self, if_sub_el: &'i IfSubElement, root: &Value, arg_stack: &[&Value], recc: u32 &self, if_sub_el: &'i IfSubElement, mtgott: &MTGOTT, arg_stack: &[&Value], recc: u32
) -> Result<Option<&'i Element>, String> { ) -> Result<Option<&'i Element>, String> {
let n = if_sub_el.conditions.len(); let n = if_sub_el.conditions.len();
for i in 0..n { for i in 0..n {
let expr_res = self.execute_expression(&if_sub_el.conditions[i], root, arg_stack, recc - 1)?; let expr_res = self.execute_expression(&if_sub_el.conditions[i], mtgott, arg_stack, recc - 1)?;
if self.value_to_bool(expr_res.deref())? { if self.value_to_bool(expr_res.deref())? {
return Ok(Some(&if_sub_el.branches[i])); return Ok(Some(&if_sub_el.branches[i]));
} }
@ -62,8 +62,8 @@ impl RuntimeEnv {
} }
} }
fn execute_element_block<'r, 'aa>( fn execute_element_block(
&self, el: &[SubElement], root: &'r Value, arg_stack: &[&'aa Value], recc: u32 &self, el: &[SubElement], mtgott: &MTGOTT, arg_stack: &[& Value], recc: u32
) -> Result<String, String> { ) -> Result<String, String> {
if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) } if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) }
let mut res = ElementPartsCollector{res: String::new(), cur_col: 0}; let mut res = ElementPartsCollector{res: String::new(), cur_col: 0};
@ -73,20 +73,20 @@ impl RuntimeEnv {
res.without_pad(ps); res.without_pad(ps);
}, },
SubElement::If(if_sub_el) => SubElement::If(if_sub_el) =>
if let Some(branch_el) = self.get_needed_if_block(if_sub_el, root, arg_stack, recc - 1)? { if let Some(branch_el) = self.get_needed_if_block(if_sub_el, mtgott, arg_stack, recc - 1)? {
res.add_single_padded_element_piece( res.add_single_padded_element_piece(
&self.execute_element_block( &self.execute_element_block(
&branch_el.sub_elements, root, arg_stack, recc - 1)?) &branch_el.sub_elements, mtgott, arg_stack, recc - 1)?)
} }
, ,
SubElement::InsertExpr(expr) => res.add_single_padded_element_piece( SubElement::InsertExpr(expr) => res.add_single_padded_element_piece(
match self.execute_expression(expr, root, arg_stack, recc - 1)?.deref() { match self.execute_expression(expr, mtgott, arg_stack, recc - 1)?.deref() {
Value::Str(str) => str, Value::Str(str) => str,
_ => return Err(self.make_error("Cannot insert non-string. Try using {{}}")), _ => return Err(self.make_error("Cannot insert non-string. Try using {{}}")),
}), }),
SubElement::For(ForSubElement{iterable, core, join}) => { SubElement::For(ForSubElement{iterable, core, join}) => {
let _g = self.register("In {%for%}".into()); let _g = self.register("In {%for%}".into());
let expr_val = self.execute_expression(iterable, root, arg_stack, recc - 1)?; let expr_val = self.execute_expression(iterable, mtgott, arg_stack, recc - 1)?;
let an = arg_stack.len(); let an = arg_stack.len();
let saved_offset = res.cur_col; let saved_offset = res.cur_col;
match expr_val.deref() { match expr_val.deref() {
@ -102,7 +102,7 @@ impl RuntimeEnv {
if i > 0 {res.add_padded_element_piece(saved_offset, join); } if i > 0 {res.add_padded_element_piece(saved_offset, join); }
res.add_padded_element_piece( res.add_padded_element_piece(
saved_offset, &self.execute_element_block( saved_offset, &self.execute_element_block(
&core.sub_elements, root, arg_stack, recc - 1)?); &core.sub_elements, mtgott, arg_stack, recc - 1)?);
} else { break } } else { break }
} }
} }
@ -116,19 +116,19 @@ impl RuntimeEnv {
if i > 0 { res.add_padded_element_piece(saved_offset, join); } if i > 0 { res.add_padded_element_piece(saved_offset, join); }
res.add_padded_element_piece( res.add_padded_element_piece(
saved_offset, &self.execute_element_block( saved_offset, &self.execute_element_block(
&core.sub_elements, root, arg_stack, recc - 1)?); &core.sub_elements, mtgott, arg_stack, recc - 1)?);
} }
} }
_ => return Err(self.make_error(&format!("Can't iterate over {}", expr_val.deref().stringify_type()))) _ => return Err(self.make_error(&format!("Can't iterate over {}", expr_val.deref().stringify_type())))
} }
} }
SubElement::Let(expr, core) => { SubElement::Let(expr, core) => {
let expr_val = self.execute_expression(expr, root, arg_stack, recc - 1)?; let expr_val = self.execute_expression(expr, mtgott, arg_stack, recc - 1)?;
let mut arg_stack_updated = Vec::with_capacity(arg_stack.len() + 1); let mut arg_stack_updated = Vec::with_capacity(arg_stack.len() + 1);
arg_stack_updated.extend_from_slice(arg_stack); arg_stack_updated.extend_from_slice(arg_stack);
arg_stack_updated.push(expr_val.deref()); arg_stack_updated.push(expr_val.deref());
res.add_single_padded_element_piece(&self.execute_element_block( res.add_single_padded_element_piece(&self.execute_element_block(
&core.sub_elements, root, arg_stack, recc - 1)?); &core.sub_elements, mtgott, arg_stack, recc - 1)?);
} }
}; };
} }
@ -136,13 +136,13 @@ impl RuntimeEnv {
} }
} }
enum ExpressionResult<'l> { enum ExpressionResult<'l, 't> {
LocalRef(&'l Value), LocalRef(&'l Value<'l>),
Owned(Value), Owned(Value<'t>),
} }
impl<'l> Deref for ExpressionResult<'l> { impl<'l, 't> Deref for ExpressionResult<'l> {
type Target = Value; type Target = Value;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -176,15 +176,15 @@ impl RuntimeEnv {
} }
fn execute_expression<'l>( fn execute_expression<'l>(
&self, expr: &Expression, root: &'l Value, arg_stack: &[&'l Value], recc: u32 &self, expr: &Expression, mtgott: &'l MTGOTT, arg_stack: &[&'l Value], recc: u32
) -> Result<ExpressionResult<'l>, String> { ) -> Result<ExpressionResult<'l>, String> {
if recc == 0 { return Err(self.make_error("Recursion limit exceeded".into())) } if recc == 0 { return Err(self.make_error("Recursion limit exceeded".into())) }
let f1: ExpressionResult<'l> = match expr { let f1: ExpressionResult<'l> = match expr {
Expression::Root => ExpressionResult::LocalRef(root), Expression::Root => ExpressionResult::LocalRef(&mtgott.root),
Expression::Argument(id) => ExpressionResult::LocalRef(arg_stack[*id as usize]), Expression::Argument(id) => ExpressionResult::LocalRef(arg_stack[*id as usize]),
Expression::Get(e1, e2) => { Expression::Get(e1, e2) => {
let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, root, arg_stack, recc - 1)?; let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, mtgott, arg_stack, recc - 1)?;
let r2: ExpressionResult = self.execute_expression(&e2, root, arg_stack, recc - 1)?; let r2: ExpressionResult = self.execute_expression(&e2, mtgott, arg_stack, recc - 1)?;
match (r1, r2.deref()) { match (r1, r2.deref()) {
(ExpressionResult::LocalRef(Value::Arr(arr)), &Value::Int(index)) => { (ExpressionResult::LocalRef(Value::Arr(arr)), &Value::Int(index)) => {
if index as usize > arr.len() { if index as usize > arr.len() {
@ -208,13 +208,13 @@ impl RuntimeEnv {
} }
Expression::Attribute(e1, key) => { Expression::Attribute(e1, key) => {
self.in_expression_index_by_key( self.in_expression_index_by_key(
self.execute_expression(&e1, root, arg_stack, recc - 1)?, key)? self.execute_expression(&e1, mtgott, arg_stack, recc - 1)?, key)?
} }
Expression::Call(e1, e2) => { Expression::Call(e1, e2) => {
let r1: ExpressionResult = self.execute_expression(e1, root, arg_stack, recc - 1)?; let r1: ExpressionResult = self.execute_expression(e1, mtgott, arg_stack, recc - 1)?;
let r2: ExpressionResult = self.execute_expression(e2, root, arg_stack, recc - 1)?; let r2: ExpressionResult = self.execute_expression(e2, mtgott, arg_stack, recc - 1)?;
match r1.deref() { match r1.deref() {
Value::Fn(func) => ExpressionResult::Owned(func(self, root, r2.deref(), recc - 1)?), Value::Fn(func) => ExpressionResult::Owned(func(self, mtgott, r2.deref(), recc - 1)?),
_ => return Err(self.make_error(&format!( _ => return Err(self.make_error(&format!(
"Can't pass argument to {}", r1.deref().stringify_type()))) "Can't pass argument to {}", r1.deref().stringify_type())))
} }
@ -225,7 +225,7 @@ impl RuntimeEnv {
}; };
Ok(match f1.deref() { Ok(match f1.deref() {
Value::RuntimeStr(fnc) => ExpressionResult::Owned( Value::RuntimeStr(fnc) => ExpressionResult::Owned(
Value::Str(fnc(self, root, recc - 1)?) Value::Str(fnc(self, mtgott, recc - 1)?)
), ),
_ => f1 _ => f1
}) })
@ -233,31 +233,38 @@ impl RuntimeEnv {
} }
/* We construct intermediate closures for elements at runtime */ /* We construct intermediate closures for elements at runtime */
fn construct_element_closure(prev: Vec<&Value>, el: Element, cr_recc: u32) -> Result<Box<HigherLevelFunc>, String> { fn construct_element_closure<'a>(
prev: Vec<&'a Value>, argc: usize, el_source_pos: usize, cr_recc: u32
) -> Result<Box<dyn Fn(&RuntimeEnv, &MTGOTT, &'a Value, u32) -> Result<Value, String> + 'a>, String> {
if cr_recc == 0 { return Err("Recursion limit exceeded".into()) } if cr_recc == 0 { return Err("Recursion limit exceeded".into()) }
Ok(if prev.len() + 1 == el.argc { Ok(if prev.len() + 1 == argc {
Box::new(move |re: &RuntimeEnv, root: &Value, arg: &Value, ecc: u32| -> Result<Value, String> { Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, arg: &Value, ecc: u32| -> Result<Value, String> {
if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) } if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) }
let mut new = prev.clone(); new.push(arg); let mut new = prev.clone(); new.push(arg);
re.execute_element_block(&el.sub_elements, root, &new, ecc - 1).map(Value::Str) re.execute_element_block(&mtgott.source[el_source_pos].sub_elements, mtgott, &new, ecc - 1).map(Value::Str)
}) })
} else { } else {
Box::new(move |re: &RuntimeEnv, root: &Value, arg: &Value, ecc: u32| -> Result<Value, String> { Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, arg: &Value, ecc: u32| -> Result<Value, String> {
if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) } if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) }
let mut new = prev.clone(); new.push(arg); let mut new = prev.clone(); new.push(arg);
construct_element_closure(new, el, cr_recc - 1).map(Value::Fn) 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 */ /* This function is supposed to compile all elements in one particular file.
pub fn plemege_to_value(x: Plemege, file_path: &str, recc: u32) -> Result<Value, String> { 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<Element>, source_expr: &mut Vec<Expression>, recc: u32
) -> Result<Value, String> {
if recc == 0 { return Err("Recursion limit exceeded".into()) } if recc == 0 { return Err("Recursion limit exceeded".into()) }
match x { match x {
Plemege::Package(map) => { Plemege::Package(map) => {
let mut new_dict: HashMap<String, Value> = HashMap::new(); let mut new_dict: HashMap<String, Value> = HashMap::new();
for (key, thing) in map { for (key, thing) in map {
new_dict.insert(key, plemege_to_value(thing, file_path, recc - 1)?); new_dict.insert(key, plemege_to_value(thing, file_path, source, recc - 1)?);
} }
Ok(Value::Dict(new_dict)) Ok(Value::Dict(new_dict))
}, },
@ -272,12 +279,13 @@ pub fn plemege_to_value(x: Plemege, file_path: &str, recc: u32) -> Result<Value,
} }
if el.argc == 0 { if el.argc == 0 {
Ok(Value::RuntimeStr(Box::new( Ok(Value::RuntimeStr(Box::new(
move |re: &RuntimeEnv, root: &Value, recc: u32| -> Result<String, String> { move |re: &RuntimeEnv, mtgott: &MTGOTT, recc: u32| -> Result<String, String> {
re.execute_element_block(&el.sub_elements, root, &[], recc) re.execute_element_block(&el.sub_elements, mtgott, &[], recc)
}))) })))
} else { } else {
let c1 = construct_element_closure(Vec::new(), el, recc).map(Value::Fn); let argc = el.argc;
c1 source.push(el);
construct_element_closure(Vec::new(), argc, source.len() - 1, recc).map(Value::Fn)
} }
} }
} }

View File

@ -1,26 +1,38 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::charclasses::{is_bad_name, is_digit, is_illegal_name, is_lnspace, is_normal_word_constituent, is_whitespace}; use crate::charclasses::{is_special_name, is_digit, is_lnspace,
is_normal_word_constituent, is_whitespace};
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::error::Error; use std::error::Error;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Clone)]
pub struct NewLambdaExpression {
local_var_array: Vec<usize>,
/* 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,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Expression { pub enum Expression {
Root, Root,
Argument(u64), Local(usize),
Get(Box<Expression>, Box<Expression>), Get(Box<Expression>, Box<Expression>),
Attribute(Box<Expression>, String), Attribute(Box<Expression>, String),
Call(Box<Expression>, Box<Expression>), Call(Box<Expression>, Box<Expression>),
Lambda(NewLambdaExpression),
Int(u64), Int(u64),
None, Str(String),
Arr(Vec<Expression>),
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Clone)]
pub struct IfSubElement { pub struct IfSubElement {
pub branches: Vec<Element>, pub branches: Vec<Element>,
pub conditions: Vec<Expression>, pub conditions: Vec<Expression>,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Clone)]
pub struct ForSubElement { pub struct ForSubElement {
pub iterable: Expression, pub iterable: Expression,
pub core: Element, pub core: Element,
@ -28,7 +40,7 @@ pub struct ForSubElement {
pub join: String, pub join: String,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Clone)]
pub enum SubElement { pub enum SubElement {
Static(String), Static(String),
// ======== Other are dynamic ======== // ======== Other are dynamic ========
@ -39,19 +51,19 @@ pub enum SubElement {
Let(Expression, Element), Let(Expression, Element),
} }
#[derive(Debug, PartialEq, Eq)] pub type Element = Vec<SubElement>;
pub struct Element {
pub argc: usize,
pub sub_elements: Vec<SubElement>,
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq)]
pub enum Plemege { pub enum Plemege {
Element(Element), Expression(Expression),
/* .0 is argc, .1 is the actual body
* I could compile Element into Expression "on-the-run", but I consider this
* bad for performance + not that good for improving loc count */
Element(usize, Element),
Package(HashMap<String, Plemege>), Package(HashMap<String, Plemege>),
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq)]
pub enum FileParsingErrorKind { pub enum FileParsingErrorKind {
expected_pack_opening_or_element_opening_or_pack_ending, expected_pack_opening_or_element_opening_or_pack_ending,
expected_pack_opening_or_element_opening_or_eof, expected_pack_opening_or_element_opening_or_eof,
@ -59,7 +71,8 @@ pub enum FileParsingErrorKind {
expected_pack_name, expected_pack_name,
illegal_pack_name, illegal_pack_name,
pack_member_name_already_occupied, pack_member_name_already_occupied,
expected_pack_opening_tag_end, expected_pack_tag_end,
expected_pack_opening_tag_end_or_assignment_operator,
expected_element_name, expected_element_name,
incorrect_block_ending_tag_expected_end_element, incorrect_block_ending_tag_expected_end_element,
illegal_element_name, illegal_element_name,
@ -78,7 +91,7 @@ pub enum FileParsingErrorKind {
expected_cmd_tag_end_after_expression, expected_cmd_tag_end_after_expression,
expected_comma_or_colon, expected_comma_or_colon,
expected_colon, expected_colon,
forloop_variable_cant_take_occupied_name, forloop_key_and_val_cant_have_same_name,
incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop, incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop,
incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if, incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if,
incorrect_block_ending_tag_expected_normal_or_endif, incorrect_block_ending_tag_expected_normal_or_endif,
@ -86,23 +99,24 @@ pub enum FileParsingErrorKind {
expected_closing_round_bracket, expected_closing_round_bracket,
cant_start_word_immediately_after_digit, cant_start_word_immediately_after_digit,
integer_parsing_error, integer_parsing_error,
illegal_root_attribute_name,
expected_attribute_name_after_dot, expected_attribute_name_after_dot,
illegal_attribute_name, illegal_attribute_name,
illegal_lambda_argument_name,
expected_closing_square_bracket, expected_closing_square_bracket,
empty_expression_inside_round_brackets,
empty_expression_inside_square_brackets,
unmatched_element_ending_tag, unmatched_element_ending_tag,
unmatched_magic_block_ending_tag, unmatched_magic_block_ending_tag,
recursion_limit_exceeded, recursion_limit_exceeded,
leave_space_between_dollar_argument_and_other_arguments, leave_space_between_dollar_argument_and_other_arguments,
cant_use_dollar_in_expression_of_element_without_dollar_argument, cant_use_dollar_in_expression_of_element_without_dollar_argument,
illegal_dollar_toplevel_attribute_name, illegal_dollar_level_attribute_name,
expected_round_brackets_open_or_dollar_or_word_or_int_or_string_or_square_bracket_open,
unmatched_double_quotes,
unmatched_closing_square_bracket,
} }
use self::FileParsingErrorKind::*; use self::FileParsingErrorKind::*;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq)]
pub struct FileParsingError { pub struct FileParsingError {
pub kind: FileParsingErrorKind, pub kind: FileParsingErrorKind,
pub p1: usize, pub p1: usize,
@ -131,7 +145,13 @@ impl Error for FileParsingError {}
struct Parser<'a> { struct Parser<'a> {
text: &'a str, text: &'a str,
p: usize p: usize,
/* We can use `this` keyword that refers to current file
* (current file can be either package or element), it does not matter, this_meaning contains expression
* that extracts object representing this file from root.
* Here we abuse the fact that Parser corresponds to one file */
this_meaning: Expression,
source_expr: &'a mut Vec<Expression>
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
@ -201,7 +221,9 @@ impl<'a> Parser<'a> {
FileParsingError::new(kind, self.p, self.next_p()) FileParsingError::new(kind, self.p, self.next_p())
} }
fn parse_pack_plus_ending(&mut self, top: bool, recc: u32) -> Result<Plemege, FileParsingError> { fn parse_pack_plus_ending(
&mut self, top: bool, source_expr: &mut Vec<Expression>, recc: u32
) -> Result<Plemege, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut res: HashMap<String, Plemege> = HashMap::new(); let mut res: HashMap<String, Plemege> = HashMap::new();
loop { loop {
@ -229,18 +251,30 @@ impl<'a> Parser<'a> {
return Err(self.new_unexpected_char_error(expected_pack_name)) return Err(self.new_unexpected_char_error(expected_pack_name))
} }
let child_name: &str = &self.text[p1..self.p]; let child_name: &str = &self.text[p1..self.p];
if !is_bad_name(child_name) { if !is_special_name(child_name) {
return Err(FileParsingError::new(illegal_pack_name, p1, self.p)) return Err(FileParsingError::new(illegal_pack_name, p1, self.p))
} }
if let Some(_) = res.get(child_name) { if let Some(_) = res.get(child_name) {
return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p)) return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p))
} }
self.skip_normal_word(); self.skip_normal_word();
if !self.is_ahead("$}") {
return Err(self.new_unexpected_char_error(expected_pack_opening_tag_end)) if self.is_ahead("$}"){
self.p += 2;
res.insert(String::from(child_name), self.parse_pack_plus_ending(false, source_expr, recc - 1)?);
} else if self.is_char_ahead('=') {
self.p += 1;
self.skip_whitespace();
res.insert(String::from(child_name), Plemege::Expression(
self.parse_expression(&vec![], &mut vec![],recc - 1)?
));
if !self.is_ahead("$}"){
return Err(self.new_unexpected_char_error(expected_pack_tag_end))
} }
self.p += 2; self.p += 2;
res.insert(String::from(child_name), self.parse_pack_plus_ending(false, recc - 1)?); } else {
return Err(self.new_unexpected_char_error(expected_pack_opening_tag_end_or_assignment_operator))
}
} else if self.is_ahead("{@") { } else if self.is_ahead("{@") {
self.p += 2; self.p += 2;
self.skip_whitespace(); self.skip_whitespace();
@ -250,15 +284,15 @@ impl<'a> Parser<'a> {
return Err(FileParsingError::new(expected_element_name, p1, self.p)) return Err(FileParsingError::new(expected_element_name, p1, self.p))
} }
let child_name = &self.text[p1..self.p]; let child_name = &self.text[p1..self.p];
if is_bad_name(child_name) { if is_special_name(child_name) {
return Err(FileParsingError::new(illegal_element_name, p1, self.p)) return Err(FileParsingError::new(illegal_element_name, p1, self.p))
} }
if let Some(_) = res.get(child_name) { if let Some(_) = res.get(child_name) {
return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p)) 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 */ let mut arg_names: Vec<&str> = Vec::new();
/* Some functions have $ as their first argument, we will let them have it */
self.skip_whitespace(); self.skip_whitespace();
if self.is_char_ahead('$') { if self.is_char_ahead('$') {
arg_names.push("$"); arg_names.push("$");
@ -281,7 +315,7 @@ impl<'a> Parser<'a> {
return Err(FileParsingError::new(expected_argument_name_or_eldef_opening_tag_end, p1, self.p)) return Err(FileParsingError::new(expected_argument_name_or_eldef_opening_tag_end, p1, self.p))
} }
let arg_name: &str = &self.text[p1..self.p]; let arg_name: &str = &self.text[p1..self.p];
if is_bad_name(arg_name) { if is_special_name(arg_name) {
return Err(FileParsingError::new(illegal_argument_name, p1, self.p)) return Err(FileParsingError::new(illegal_argument_name, p1, self.p))
} }
if arg_names.iter().any(|b: &&str| *b == arg_name) { if arg_names.iter().any(|b: &&str| *b == arg_name) {
@ -289,11 +323,12 @@ impl<'a> Parser<'a> {
} }
arg_names.push(arg_name); arg_names.push(arg_name);
} }
let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(&arg_names, recc - 1)?; let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(
&arg_names, source_expr, recc - 1)?;
if !matches!(end_cmd.cmd, BlockEndingTag::END_ELEMENT) { if !matches!(end_cmd.cmd, BlockEndingTag::END_ELEMENT) {
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_end_element, end_cmd.p1, self.p)) return Err(FileParsingError::new(incorrect_block_ending_tag_expected_end_element, end_cmd.p1, self.p))
} }
res.insert(String::from(child_name), Plemege::Element(child_el)); res.insert(String::from(child_name), Plemege::Element(arg_names.len(), child_el));
} else { } else {
return Err(self.new_unexpected_char_error(if top { return Err(self.new_unexpected_char_error(if top {
expected_pack_opening_or_element_opening_or_eof expected_pack_opening_or_element_opening_or_eof
@ -410,7 +445,9 @@ fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) {
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/* If BlockEndingCmdTag::ELSE_IF is returned, the ending tag won't be read completely, /* 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 */ * But in other case it would be read to the end */
fn parse_element_plus_ending_tag(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<(Element, ReasonOfElementEnd), FileParsingError> { fn parse_element_plus_ending_tag (
&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32
) -> Result<(Element, ReasonOfElementEnd), FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut res: Vec<SubElement> = Vec::new(); let mut res: Vec<SubElement> = Vec::new();
let mut tp1 = self.p; let mut tp1 = self.p;
@ -422,9 +459,10 @@ impl<'a> Parser<'a> {
}; };
/* Fixes whitespaces in static sub-elements */ /* Fixes whitespaces in static sub-elements */
let finishing_touches = |ree: ReasonOfElementEnd, mut res: Vec<SubElement>| -> Result<(Element, ReasonOfElementEnd), FileParsingError> { let finishing_touches = |ree: ReasonOfElementEnd, mut res: Vec<SubElement>|
-> Result<(Element, ReasonOfElementEnd), FileParsingError> {
fix_whitespaces_in_element(&mut res); fix_whitespaces_in_element(&mut res);
Ok((Element{ argc: arg_names.len(), sub_elements: res }, ree)) Ok((res, ree))
}; };
loop { loop {
@ -433,31 +471,31 @@ impl<'a> Parser<'a> {
return finishing_touches(ReasonOfElementEnd{p1: self.p, cmd: BlockEndingTag::EOF}, res); return finishing_touches(ReasonOfElementEnd{p1: self.p, cmd: BlockEndingTag::EOF}, res);
} else if self.is_ahead("{{") { } else if self.is_ahead("{{") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
self.p += 2; self.skip_whitespace();
let expr: Expression = self.parse_expression(arg_names, recc)?;
if !self.is_ahead("}}") { if !self.is_ahead("}}") {
return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p)); let expr: Expression = self.parse_expression(
} arg_names, &mut (0..arg_names.len()).collect(), recc)?;
self.p += 2;
if !matches!(expr, Expression::None){
res.push(SubElement::InsertExpr( res.push(SubElement::InsertExpr(
Expression::Call( Expression::Call(
Box::new(Expression::Attribute(Box::new(Expression::Root), "sanitize".into())), Box::new(Expression::Attribute(Box::new(Expression::Root), "sanitize".into())),
Box::new(expr)) Box::new(expr))
)); ));
if !self.is_ahead("}}") {
return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p));
} }
}
self.p += 2;
tp1 = self.p; tp1 = self.p;
} else if self.is_ahead("{[") { } else if self.is_ahead("{[") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
self.p += 2; self.skip_whitespace();
let expr: Expression = self.parse_expression(arg_names, recc)?; let expr: Expression = self.parse_expression(
if !self.is_ahead("]}") { arg_names, &mut (0..arg_names.len()).collect(), recc)?;
return Err(FileParsingError::new(expected_roughinsert_tag_end_after_expression, self.p - 2, self.p))
}
self.p += 2;
if !matches!(expr, Expression::None){
res.push(SubElement::InsertExpr(expr)); res.push(SubElement::InsertExpr(expr));
if !self.is_ahead("]}") {
return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p));
} }
self.p += 2;
tp1 = self.p; tp1 = self.p;
} else if self.is_ahead("{@}") { } else if self.is_ahead("{@}") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
@ -481,7 +519,9 @@ impl<'a> Parser<'a> {
let cmd = &self.text[pb..self.p]; let cmd = &self.text[pb..self.p];
/* Read space + expect %} and do finishing_touches */ /* Read space + expect %} and do finishing_touches */
let just_one_thing = |pelf: &mut Parser, cmd: BlockEndingTag, res: Vec<SubElement>| -> Result<(Element, ReasonOfElementEnd), FileParsingError> { let just_one_thing = |
pelf: &mut Parser, cmd: BlockEndingTag, res: Vec<SubElement>
| -> Result<(Element, ReasonOfElementEnd), FileParsingError> {
pelf.skip_whitespace(); pelf.skip_whitespace();
if !pelf.is_ahead("%}") { if !pelf.is_ahead("%}") {
return Err(pelf.new_unexpected_char_error(expected_cmd_tag_end)); return Err(pelf.new_unexpected_char_error(expected_cmd_tag_end));
@ -507,9 +547,9 @@ impl<'a> Parser<'a> {
} }
"endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res), "endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res),
"endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res), "endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res),
"for" => res.push(self.parse_let(arg_names, recc - 1)?), "for" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?),
"if" => res.push(self.parse_if(arg_names, recc - 1)?), "if" => res.push(self.parse_if(arg_names, source_expr, recc - 1)?),
"let" => res.push(self.parse_let(arg_names, recc - 1)?), "let" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?),
_ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)), _ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)),
} }
tp1 = self.p; tp1 = self.p;
@ -519,27 +559,29 @@ impl<'a> Parser<'a> {
} }
} }
fn parse_expression_at_cmd_tag_end(&mut self, arg_names: &Vec<&str>) -> Result<Expression, FileParsingError> { fn parse_expression_at_cmd_tag_end(
let p1 = self.p; &mut self, arg_names: &Vec<&str>, recc: u32
let expr: Expression = self.parse_expression(arg_names, 150)?; ) -> Result<Expression, FileParsingError> {
if matches!(expr, Expression::None) { self.check_recursion_limit(recc)?;
return Err(FileParsingError::new(expected_nonempty_expression, p1, self.p)) let expr: Expression = self.parse_expression(arg_names, &mut (0..arg_names.len()).collect(), recc - 1)?;
}
if !self.is_ahead("%}"){ if !self.is_ahead("%}"){
return Err(self.new_unexpected_char_error(expected_cmd_tag_end_after_expression)); return Err(self.new_unexpected_char_error(expected_cmd_tag_end_after_expression));
} }
self.p += 2;
Ok(expr) Ok(expr)
} }
/* It turned out to be so complex I put it in a separate function. /*
* It parses expr %} block {% else if expr %} block {% else %} block {%} */ * It parses expr %} block {% else if expr %} block {% else %} block {%} */
fn parse_if(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> { fn parse_if(
&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32
) -> Result<SubElement, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut conditions: Vec<Expression> = Vec::new(); let mut conditions: Vec<Expression> = Vec::new();
let mut blocks: Vec<Element> = Vec::new(); let mut blocks: Vec<Element> = Vec::new();
loop { loop {
let expr = self.parse_expression_at_cmd_tag_end(arg_names)?; 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, recc - 1)?; let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names, source_expr, recc - 1)?;
conditions.push(expr); conditions.push(expr);
match ending_tag.cmd { match ending_tag.cmd {
BlockEndingTag::ELSE | BlockEndingTag::NORMAL | BlockEndingTag::ENDIF | BlockEndingTag::ELSE | BlockEndingTag::NORMAL | BlockEndingTag::ENDIF |
@ -548,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)), 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) { if matches!(ending_tag.cmd, BlockEndingTag::ELSE) {
let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names, recc - 1)?; let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names, source_expr, recc - 1)?;
if !matches!(the_end.cmd, BlockEndingTag::NORMAL | BlockEndingTag::ENDIF){ 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)); return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal_or_endif, the_end.p1, self.p));
} }
@ -561,7 +603,7 @@ impl<'a> Parser<'a> {
Ok(SubElement::If(IfSubElement{branches: blocks, conditions})) Ok(SubElement::If(IfSubElement{branches: blocks, conditions}))
} }
fn parse_let(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> { fn parse_let(&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32) -> Result<SubElement, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
self.skip_whitespace(); self.skip_whitespace();
let p1 = self.p; let p1 = self.p;
@ -571,7 +613,7 @@ impl<'a> Parser<'a> {
return Err(FileParsingError::new(expected_variable_name, p1, self.p)); return Err(FileParsingError::new(expected_variable_name, p1, self.p));
} }
let new_variable_name = &self.text[p1..self.p]; let new_variable_name = &self.text[p1..self.p];
if is_bad_name(new_variable_name){ if is_special_name(new_variable_name){
return Err(FileParsingError::new(illegal_variable_name, p1, self.p)); return Err(FileParsingError::new(illegal_variable_name, p1, self.p));
} }
self.skip_whitespace(); self.skip_whitespace();
@ -579,17 +621,17 @@ impl<'a> Parser<'a> {
return Err(self.new_unexpected_char_error(expected_assignment_operator)); return Err(self.new_unexpected_char_error(expected_assignment_operator));
} }
self.p += 1; self.p += 1;
let expr = self.parse_expression_at_cmd_tag_end(arg_names)?; let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?;
let mut arg_names_extended = arg_names.clone(); let mut arg_names_extended = arg_names.clone();
arg_names_extended.push(new_variable_name); arg_names_extended.push(new_variable_name);
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, recc - 1)?; let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, source_expr, recc - 1)?;
if !matches!(ending.cmd, BlockEndingTag::NORMAL) { if !matches!(ending.cmd, BlockEndingTag::NORMAL) {
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p)); return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p));
} }
Ok(SubElement::Let(expr, inner_block)) Ok(SubElement::Let(expr, inner_block))
} }
fn parse_for_new_variable(&mut self, arg_names_extended: &Vec<&str>) -> Result<&'a str, FileParsingError> { fn parse_for_new_variable(&mut self) -> Result<(&'a str, usize), FileParsingError> {
self.skip_whitespace(); self.skip_whitespace();
let t1 = self.p; let t1 = self.p;
self.skip_normal_word(); self.skip_normal_word();
@ -597,28 +639,28 @@ impl<'a> Parser<'a> {
return Err(self.new_unexpected_char_error(expected_variable_name)); return Err(self.new_unexpected_char_error(expected_variable_name));
} }
let name = &self.text[t1..self.p]; let name = &self.text[t1..self.p];
if is_illegal_name(name) { if is_special_name(name) {
return Err(FileParsingError::new(illegal_variable_name, t1, self.p)); return Err(FileParsingError::new(illegal_variable_name, t1, self.p));
} }
if name != "_" && arg_names_extended.iter().find(|&&b| b == name).is_some() { Ok((name, t1))
return Err(FileParsingError::new(forloop_variable_cant_take_occupied_name, t1, self.p));
}
Ok(name)
} }
fn parse_for(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> { fn parse_for(&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32) -> Result<SubElement, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut arg_names_extended = arg_names.clone(); let mut arg_names_extended = arg_names.clone();
let name1 = self.parse_for_new_variable(&arg_names_extended)?; let (name1, _) = self.parse_for_new_variable()?;
arg_names_extended.push(name1); arg_names_extended.push(name1);
let mut name2 = "";
self.skip_whitespace(); self.skip_whitespace();
if self.is_char_ahead(',') { let mut name2= if self.is_char_ahead(',') {
self.p += 1; self.p += 1;
name2 = self.parse_for_new_variable(&arg_names_extended)?; let (name, pt2) = self.parse_for_new_variable()?;
if name == name1 {
return Err(FileParsingError::new(forloop_key_and_val_cant_have_same_name, pt2, self.p))
} }
name
} else { "" };
arg_names_extended.push(name2); arg_names_extended.push(name2);
if !self.is_char_ahead(':'){ if !self.is_char_ahead(':'){
@ -628,8 +670,9 @@ impl<'a> Parser<'a> {
} }
self.p += 1; self.p += 1;
let expr = self.parse_expression_at_cmd_tag_end(arg_names)?; 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, recc - 1)?; let (inner_block, ending) = self.parse_element_plus_ending_tag(
&arg_names_extended, source_expr, recc - 1)?;
let separator: String = String::from(match ending.cmd { let separator: String = String::from(match ending.cmd {
BlockEndingTag::NOGAP => "", BlockEndingTag::NOGAP => "",
BlockEndingTag::GAP => " ", BlockEndingTag::GAP => " ",
@ -642,10 +685,13 @@ impl<'a> Parser<'a> {
/* Checks for ]} }} and %}. May actually return NoneOfThose */ /* Checks for ]} }} and %}. May actually return NoneOfThose */
fn is_tag_end_ahead(&self) -> bool { fn is_tag_end_ahead(&self) -> bool {
self.is_ahead("]}") || self.is_ahead("}}") || self.is_ahead("%}") ["]}", "}}", "%}", "$}"].iter().any(|s| self.is_ahead(s))
} }
fn fn_parse_expression_l2_trail(&mut self, arg_names: &Vec<&str>, mut from: Expression, recc: u32) -> Result<Expression, FileParsingError> { fn fn_parse_expression_l2_trail(
&mut self, arg_names: &Vec<&str>, mut from: Expression,
used_local_consts: &mut Vec<usize>, recc: u32
) -> Result<Expression, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
loop { loop {
self.skip_whitespace(); self.skip_whitespace();
@ -658,22 +704,18 @@ impl<'a> Parser<'a> {
return Err(self.new_unexpected_char_error(expected_attribute_name_after_dot)); return Err(self.new_unexpected_char_error(expected_attribute_name_after_dot));
} }
let attr_name = &self.text[attrp1..self.p]; let attr_name = &self.text[attrp1..self.p];
if is_bad_name(attr_name) { if is_special_name(attr_name) {
return Err(FileParsingError::new(illegal_attribute_name, attrp1, self.p)); return Err(FileParsingError::new(illegal_attribute_name, attrp1, self.p));
} }
Expression::Attribute(Box::new(from), String::from(attr_name)) Expression::Attribute(Box::new(from), String::from(attr_name))
} else if self.is_char_ahead('[') { } else if self.is_char_ahead('[') {
let sqbrp1 = self.p;
self.p += 1; self.p += 1;
let sub_expr = self.parse_expression(arg_names, recc - 1)?; let sub_expr = self.parse_expression(arg_names, used_local_consts, recc - 1)?;
self.skip_whitespace(); self.skip_whitespace();
if !self.is_char_ahead(']') { if !self.is_char_ahead(']') {
return Err(self.new_unexpected_char_error(expected_closing_square_bracket)) return Err(self.new_unexpected_char_error(expected_closing_square_bracket))
} }
self.p += 1; 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)) Expression::Get(Box::new(from), Box::new(sub_expr))
} else { } else {
break break
@ -682,25 +724,42 @@ impl<'a> Parser<'a> {
Ok(from) Ok(from)
} }
/* 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)
* 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<usize>, name_in_expr: &str
) -> Option<usize> {
match arg_names.iter().rposition(|&n| n == name_in_expr) {
Some(i) => {
let lci = match used_local_consts.iter().position(|iig| *iig == i) {
Some(lci) => lci,
None => {used_local_consts.push(i); used_local_consts.len() - 1},
};
Some(lci)
}
None => None
}
}
/* l1 expression = l2 space l2 space ... space l2 */ /* l1 expression = l2 space l2 space ... space l2 */
fn parse_expression_l2(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<Expression, FileParsingError> { fn parse_expression_l2(
&mut self, arg_names: &Vec<&str>,
used_local_consts: &mut Vec<usize>, recc: u32
) -> Result<Option<Expression>, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
self.skip_whitespace(); self.skip_whitespace();
if self.is_char_ahead('(') { if self.is_char_ahead('(') {
let p1 = self.p;
self.p += 1; self.p += 1;
let expr = self.parse_expression(arg_names, recc - 1)?; let expr = self.parse_expression(arg_names, used_local_consts, recc - 1)?;
self.skip_whitespace(); self.skip_whitespace();
if !self.is_char_ahead(')') { if !self.is_char_ahead(')') {
return Err(self.new_unexpected_char_error(expected_closing_round_bracket)) return Err(self.new_unexpected_char_error(expected_closing_round_bracket))
} }
self.p += 1; self.p += 1;
if matches!(expr, Expression::None){ Ok(Some(expr))
return Err(FileParsingError::new(empty_expression_inside_round_brackets, p1, self.p)) } else if self.is_digit_ahead() {
}
return Ok(expr);
}
if self.is_digit_ahead() {
let p1 = self.p; let p1 = self.p;
loop { loop {
if self.is_digit_ahead() { if self.is_digit_ahead() {
@ -709,7 +768,7 @@ impl<'a> Parser<'a> {
return Err(self.new_unexpected_char_error(cant_start_word_immediately_after_digit)) return Err(self.new_unexpected_char_error(cant_start_word_immediately_after_digit))
} else { } else {
return match self.text[p1..self.p].parse::<u64>() { return match self.text[p1..self.p].parse::<u64>() {
Ok(v) => Ok(Expression::Int(v)), Ok(v) => Ok(Some(Expression::Int(v))),
Err(_) => Err(FileParsingError::new(integer_parsing_error, p1, self.p)), Err(_) => Err(FileParsingError::new(integer_parsing_error, p1, self.p)),
}; };
} }
@ -718,49 +777,107 @@ impl<'a> Parser<'a> {
let p1 = self.p; let p1 = self.p;
self.skip_normal_word(); self.skip_normal_word();
let toplevel_name = &self.text[p1..self.p]; let toplevel_name = &self.text[p1..self.p];
if is_bad_name(toplevel_name) { let p2 = self.p;
return Err(FileParsingError::new(illegal_root_attribute_name, p1, self.p)); self.skip_whitespace();
} if self.is_char_ahead(':') {
let bg: Expression = match arg_names.iter().rposition(|&n| n == toplevel_name) { if is_special_name(toplevel_name) {
Some(i) => Expression::Argument(i as u64), return Err(FileParsingError::new(illegal_lambda_argument_name, p1, p2))
None => Expression::Attribute(Box::new(Expression::Root), toplevel_name.into())
};
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));
} }
self.p += 1; self.p += 1;
let mut arg_names_extended = arg_names.clone();
arg_names_extended.push(toplevel_name);
let mut used_by_lambda: Vec<usize> = 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); }
}
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()
}).collect(), expr_id})))
} else if toplevel_name == "this" {
Ok(Some(self.fn_parse_expression_l2_trail(
arg_names, self.this_meaning.clone(), used_local_consts, recc - 1)?))
} else {
let bg: Expression = match Self::expression_parsing_include_local_const(
arg_names, used_local_consts, toplevel_name
) {
Some(n) => Expression::Local(n),
None => Expression::Attribute(Box::new(Expression::Root), toplevel_name.into()),
};
Ok(Some(self.fn_parse_expression_l2_trail(arg_names, bg, used_local_consts, recc - 1)?))
}
} else if self.is_char_ahead('$') {
self.p += 1;
let bg_dollar: Expression = match Self::expression_parsing_include_local_const(
arg_names, used_local_consts, "$"
) {
Some(n) => Expression::Local(n),
None => return Err(self.new_unexpected_char_error(cant_use_dollar_in_expression_of_element_without_dollar_argument)),
};
let bg: Expression = if self.is_word_ahead() { let bg: Expression = if self.is_word_ahead() {
let p1 = self.p; let p1 = self.p;
self.skip_normal_word(); self.skip_normal_word();
let toplevel_name = &self.text[p1..self.p]; let dollar_level_name = &self.text[p1..self.p];
if is_bad_name(toplevel_name) { if is_special_name(dollar_level_name) {
return Err(FileParsingError::new(illegal_dollar_toplevel_attribute_name, p1, self.p)); return Err(FileParsingError::new(illegal_dollar_level_attribute_name, p1, self.p));
} }
Expression::Attribute(Box::new(Expression::Argument(0)), toplevel_name.into()) Expression::Attribute(Box::new(bg_dollar), dollar_level_name.into())
} else { bg_dollar };
Ok(Some(self.fn_parse_expression_l2_trail(arg_names, bg, used_local_consts, recc - 1)?))
} else if self.is_char_ahead('"'){
self.p += 1;
let sb = self.p;
loop {
match self.here() {
Some(ch ) => if ch == '"' { break } else { self.advance(); }
None => return Err(self.new_unexpected_char_error(unmatched_double_quotes))
}
}
let se = self.p;
self.p += 1;
Ok(Some(Expression::Str(String::from(&self.text[sb..se]))))
} else if self.is_char_ahead('['){
self.p += 1;
self.skip_whitespace();
let mut res: Vec<Expression> = Vec::new();
loop {
if self.is_char_ahead(']') {
self.p += 1;
break;
} else { } else {
Expression::Argument(0) res.push(self.parse_expression(arg_names, used_local_consts, recc - 1)?);
}; if self.is_char_ahead(',') {
self.fn_parse_expression_l2_trail(arg_names, bg, recc - 1) self.p += 1;
self.skip_whitespace();
} else if self.is_char_ahead(']') {
self.p += 1;
break
} else { } else {
return Ok(Expression::None) return Err(self.new_unexpected_char_error(unmatched_closing_square_bracket))
}
}
}
Ok(Some(Expression::Arr(res)))
} else {
Ok(None)
} }
} }
fn parse_expression(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<Expression, FileParsingError> { fn parse_expression(
&mut self, arg_names: &Vec<&str>,
used_local_consts: &mut Vec<usize>, recc: u32
) -> Result<Expression, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut e1: Expression = self.parse_expression_l2(arg_names, recc - 1)?; let mut e1: Expression = self.parse_expression_l2(arg_names, used_local_consts, recc - 1)?
/* It is okay to enter call_args reading loop even when e1 is None. .ok_or(self.new_unexpected_char_error(expected_round_brackets_open_or_dollar_or_word_or_int_or_string_or_square_bracket_open))?;
If parse_expression_l2 returned None, subsequent call to parse_expression_l2
is guaranteed to return None. If there are no arguments, e1 does not get turn into
Expression::Call */
loop { loop {
let arg_expr: Expression = self.parse_expression_l2(arg_names, recc - 1)?; let arg_expr: Option<Expression> = self.parse_expression_l2(arg_names, used_local_consts,recc - 1)?;
if matches!(arg_expr, Expression::None) { if matches!(arg_expr, None) { break }
break e1 = Expression::Call(Box::new(e1), Box::new(arg_expr.unwrap()));
}
e1 = Expression::Call(Box::new(e1), Box::new(arg_expr));
} }
Ok(e1) Ok(e1)
} }
@ -770,18 +887,29 @@ impl<'a> Parser<'a> {
} }
} }
/* file_path is a path without extension and separated by '/' */
/* Parses a file treating it like a package (where other packages and elements could be located) */ pub fn make_expression_for_this(file_path: &str) -> Expression {
pub fn parse_one_file_packed(text: &str) -> Result<Plemege, FileParsingError> { file_path.split("/").fold(Expression::Root, |b, s| {
let mut parser: Parser = Parser{text, p: 0}; Expression::Attribute(Box::new(b), String::from(s))
parser.parse_pack_plus_ending(true, 150) })
} }
pub fn parse_one_file_simplified(text: &str) -> Result<Plemege, FileParsingError> { /* Parses a file treating it like a package (where other packages and elements could be located)
let mut parser: Parser = Parser{text, p: 0}; * file_path is a path of file, separated by '/' and without mtgott extension */
let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&vec!["$"], 150)?; pub fn parse_one_file_packed(
text: &str, file_path: &str, source_expr: &mut Vec<Expression>,
) -> Result<Plemege, FileParsingError> {
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)
}
pub fn parse_one_file_simplified(
text: &str, file_path: &str, source_expr: &mut Vec<Expression>,
) -> Result<Plemege, FileParsingError> {
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)?;
match tt.cmd { match tt.cmd {
BlockEndingTag::EOF => Ok(Plemege::Element(el)), BlockEndingTag::EOF => Ok(Plemege::Element(1, el)),
BlockEndingTag::END_ELEMENT => Err(FileParsingError::new(unmatched_element_ending_tag, tt.p1, parser.p)), BlockEndingTag::END_ELEMENT => Err(FileParsingError::new(unmatched_element_ending_tag, tt.p1, parser.p)),
_ => Err(FileParsingError::new(unmatched_magic_block_ending_tag, tt.p1, parser.p)), _ => Err(FileParsingError::new(unmatched_magic_block_ending_tag, tt.p1, parser.p)),
} }

View File

@ -3,6 +3,8 @@ use std::fmt;
use std::fmt::Debug; use std::fmt::Debug;
use std::error::Error; use std::error::Error;
use std::cell::RefCell; use std::cell::RefCell;
use parser::{Element, Expression};
use std::rc::Rc;
pub struct DebugStateGuard<'a> ( pub struct DebugStateGuard<'a> (
&'a RefCell<Vec<String>> &'a RefCell<Vec<String>>
@ -33,34 +35,38 @@ impl RuntimeEnv {
} }
} }
/* Arguments are: stack trace for debug, root, actual function argument, recursion counter */ pub struct MTGOTT{
pub type HigherLevelFunc = dyn Fn(&RuntimeEnv, &Value, &Value, u32) -> Result<Value, String>; pub root: Value<'static>,
/* Arguments are: stack trace for debug, root, recursion counter */ pub source_elements: Vec<Element>,
pub type OopsForgotToPrecompileThis = dyn Fn(&RuntimeEnv, &Value, u32) -> Result<String, String>; pub source_expressions: Vec<Expression>
}
pub enum Value { pub enum Value<'t> {
Str(String), Str(Rc<String>),
Int(u64), Int(u64),
Arr(Vec<Value>), Arr(Rc<Vec<Value<'t>>>),
Dict(HashMap<String, Value>), Dict(Rc<HashMap<String, Value<'t>>>),
Fn(Box<HigherLevelFunc>), Fn(Rc<dyn Fn(&'t RuntimeEnv, &'t MTGOTT, Value<'t>, u32) -> Result<Value<'t>, String>>),
Ref(&'t Value<'t>),
/* Some may call it a crutch (and I am definitely agree with them): my current mtgott /* 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. 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 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::RuntimeConst. At runtime, this compilation artifact is the same thing as
Value::Str */ Value\{::RuntimeConst}.
RuntimeStr(Box<OopsForgotToPrecompileThis>) Expression evaluator automatically converts them as soon as possible. */
RuntimeConst(Box<dyn Fn(&'t RuntimeEnv, &'t MTGOTT, u32) -> Result<Value<'t>, String>>),
} }
impl Value { impl<'t> Value<'t> {
pub fn as_dict(&self) -> &HashMap<String, Value> { pub fn stringify_type(&self) -> String {
match self { Value::Dict(s) => s, _ => panic!(), }
}
pub fn stringify_type(&self) -> &'static str {
match self { match self {
Value::Str(_) => "String", Value::Int(_) => "Int", Value::Arr(_) => "Array", Value::Str(_) => "String".into(),
Value::Dict(_) => "Dictionary", Value::Fn(_) => "Function", _ => panic!() 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(),
} }
} }
} }
@ -84,25 +90,15 @@ impl Debug for Value {
write!(f, "Dict({{{}}})", dict_debug.join(", ")) write!(f, "Dict({{{}}})", dict_debug.join(", "))
} }
Value::Fn(_) => write!(f, "Fn(<function>)"), Value::Fn(_) => write!(f, "Fn(<function>)"),
Value::RuntimeStr(_) => write!(f, "C-comp-pilat_onArrrrr%%#^#}}&_)") Value::Ref(&b) => write!(f, "Ref({b:?})"),
Value::RuntimeConst(_) => write!(f, "C-comp-pilat_onArrrrr%%#^#}}&_)"),
} }
} }
} }
impl<'t> PartialEq for Value { impl MTGOTT {
fn eq(&self, other: &Self) -> bool { fn get_sub_value<'a>(&'a self, name: &str) -> Result<&'a Value, String> {
match (self, other) { let mut cur: &Value = &self.root;
(Value::Str(s1), Value::Str(s2)) => s1 == s2,
(Value::Int(i1), Value::Int(i2)) => i1 == i2,
(Value::Arr(a1), Value::Arr(a2)) => a1 == a2,
(Value::Dict(d1), Value::Dict(d2)) => d1 == d2,
_ => false,
}
}
}
fn get_sub_value<'a>(root: &'a Value, name: &str) -> Result<&'a Value, String> {
let mut cur: &Value = root;
let parts: Vec<String> = name.split(".").map(String::from).collect(); let parts: Vec<String> = name.split(".").map(String::from).collect();
for i in 0usize..parts.len() { for i in 0usize..parts.len() {
match cur { match cur {
@ -116,38 +112,33 @@ fn get_sub_value<'a>(root: &'a Value, name: &str) -> Result<&'a Value, String> {
} }
} }
Ok(cur) Ok(cur)
} }
/* If some `top-level-attribute` was not found in root, we search for it in guest_root. pub fn render(&self, guest_root: Value, name: &str, recursion_limit: u32) -> Result<String, Box<dyn Error>> {
* This is our way of passing arguments for template */ let main = self.get_sub_value(name)?;
pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) -> Result<String, Box<dyn Error>> { let res = match main {
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(format!("Called {name} template is a dictionary without `main()`").into())
}
}
_ => main,
};
match main {
Value::Fn(main_func) => { Value::Fn(main_func) => {
let d_stack = RuntimeEnv(RefCell::new(Vec::new())); let d_stack = RuntimeEnv(RefCell::new(Vec::new()));
let Some(guest_root) = guest_root else { let Some(guest_root) = guest_root else {
return Err(format!("Need guest argument root for template {name}").into()) return Err(format!("Need guest argument root for template {name}").into())
}; };
let rv = main_func(&d_stack, root, guest_root, 150)?; main_func(&d_stack, self, guest_root, recursion_limit)?
match rv {
Value::Str(answer) => Ok(answer),
_ => Err(format!("template {name} returned not a string").into())
} }
} Value::Str(me) => Value::Ref(main),
Value::Str(str) => Ok(str.clone()), Value::RuntimeConst(zero_arg_fnc) => {
Value::RuntimeStr(str_fnc) => {
let mut d_stack = RuntimeEnv(RefCell::new(Vec::new())); let mut d_stack = RuntimeEnv(RefCell::new(Vec::new()));
Ok(str_fnc(&mut d_stack, root, 150)?) 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())
} else {
Ok(answer.as_ref().clone())
}
_ => Err(format!("Template {name} returned {} instead of string", res.stringify_type()).into())
} }
_ => Err(format!("Called {name} template that is not a function / string").into())
} }
} }

View File

@ -1,4 +1,3 @@
use yyyi_ru::mtgott::parser::*;
use std::collections::HashMap; use std::collections::HashMap;
fn generate_strings( fn generate_strings(
@ -25,9 +24,9 @@ fn test_parse_file_with_all_combinations() {
generate_strings(&mut String::new(), target_length, &alphabet, &mut |s| { generate_strings(&mut String::new(), target_length, &alphabet, &mut |s| {
println!("Parsing {s}"); println!("Parsing {s}");
parse_one_file_packed(&s); // parse_one_file_packed(&s);
parse_one_file_packed((String::from("{% as s e1e %} adasd {%}") + s.as_str()).as_str()); // parse_one_file_packed((String::from("{% as s e1e %} adasd {%}") + s.as_str()).as_str());
parse_one_file_packed((String::from("{% as s e1e %} a{[111 . 2332]]dasd {%} {% as s e1e %} adas {{}}d {%} ") + s.as_str()).as_str()); // parse_one_file_packed((String::from("{% as s e1e %} a{[111 . 2332]]dasd {%} {% as s e1e %} adas {{}}d {%} ") + s.as_str()).as_str());
}); });
} }
@ -42,8 +41,8 @@ macro_rules! string_map {
#[test] #[test]
fn resulting_package() { fn resulting_package() {
assert_eq!(parse_one_file_packed("{@ x @}{@}"), // assert_eq!(parse_one_file_packed("{@ x @}{@}"),
Ok(Plemege::Package(string_map! { // Ok(Plemege::Package(string_map! {
"x" => Plemege::Element(Element{argc: 0, sub_elements: vec![]}) // "x" => Plemege::Element(Element{argc: 0, sub_elements: vec![]})
}))) // })))
} }