Daily update. Doing big rewrite of everything. Rewrote parser.rs. Smart pointers are back, yay
This commit is contained in:
parent
c5f71afa5c
commit
a9023c6a8c
@ -4,9 +4,10 @@ use std::env;
|
||||
use std::process;
|
||||
use crate::mtgott::charclasses::is_bad_name;
|
||||
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 mtgott::dirsearch::add_path_with_dots_to_root;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn usage() -> ! {
|
||||
eprintln!("Usage: program <path> [-D name value]...");
|
||||
@ -40,11 +41,11 @@ fn main() {
|
||||
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 {
|
||||
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 res = generate_template(&dir_root, &guest_root, &template_name).unwrap();
|
||||
let res = dir_root.render(guest_root, &template_name, 500).unwrap();
|
||||
print!("{res}")
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ pub fn is_digit(ch: char) -> bool {
|
||||
|
||||
pub fn is_normal_word_constituent(ch: char) -> bool {
|
||||
('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch)
|
||||
|| '-' == ch || '_' == ch
|
||||
|| "-_+/|&~!^%*".contains(ch)
|
||||
}
|
||||
|
||||
pub fn is_normal_word(s: &str) -> bool {
|
||||
@ -26,10 +26,6 @@ pub fn escape_for_html(s: &str) -> String {
|
||||
.replace("'", "'").replace("\"", """)
|
||||
}
|
||||
|
||||
pub fn is_bad_name(s: &str) -> bool {
|
||||
is_illegal_name(s) || s == "_"
|
||||
}
|
||||
|
||||
pub fn is_illegal_name(s: &str) -> bool {
|
||||
s == "" || s == "root" || s == "self" || s == "super"
|
||||
pub fn is_special_name(s: &str) -> bool {
|
||||
s.chars().any(|ch| "+'/|&~!^%*".contains(ch)) || s.ends_with("-") || s == "this"
|
||||
}
|
||||
|
||||
@ -2,15 +2,17 @@ use std::{fs};
|
||||
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, RuntimeEnv};
|
||||
use super::charclasses::{escape_for_html, is_special_name};
|
||||
use super::runtime::{Value, RuntimeEnv, MTGOTT};
|
||||
use super::parser::{parse_one_file_simplified, parse_one_file_packed};
|
||||
use super::lambda_compilation::{plemege_to_value};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn search_dir_rec_helper<F: Fn(&str) -> bool>(
|
||||
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>> {
|
||||
if recc == 0 { return Err("Recursion limit exceeded".into()); }
|
||||
let fs_path = canonicalize(fs_path)?;
|
||||
@ -42,7 +44,8 @@ pub fn search_dir_rec_helper<F: Fn(&str) -> bool>(
|
||||
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>> {
|
||||
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)?;
|
||||
@ -60,7 +63,7 @@ pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result<MtgottDirContent, B
|
||||
&(String::from(".mtgott") + plain_ext),
|
||||
&(String::from(".imtgott") + plain_ext),
|
||||
plain_ext
|
||||
], &is_bad_name)?;
|
||||
], is_special_name)?;
|
||||
Ok(MtgottDirContent{
|
||||
mtgott: std::mem::take(&mut all[0]),
|
||||
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)
|
||||
}
|
||||
|
||||
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 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 {
|
||||
let path = format!("{cut_path}.mtgott{plain_ext}");
|
||||
let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
|
||||
let text = std::str::from_utf8(&text_bytes)?;
|
||||
let plemege = parse_one_file_packed(text)?;
|
||||
let compiled = plemege_to_value(plemege, &path, 150)?;
|
||||
add_path_to_root(&mut res, &cut_path, '/', compiled)?
|
||||
let plemege = parse_one_file_packed(text, &cut_path, &mut res.source_expressions)?;
|
||||
let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?;
|
||||
add_path_to_root(&mut res.root, &cut_path, '/', compiled)?
|
||||
}
|
||||
for cut_path in source.imtgott {
|
||||
let path = format!("{cut_path}.imtgott{plain_ext}");
|
||||
let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
|
||||
let text = std::str::from_utf8(&text_bytes)?;
|
||||
let plemege = parse_one_file_simplified(text)?;
|
||||
let compiled = plemege_to_value(plemege, &path, 150)?;
|
||||
add_path_to_root(&mut res, &cut_path, '/', compiled)?
|
||||
let plemege = parse_one_file_simplified(text, &cut_path, &mut res.source_expressions)?;
|
||||
let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?;
|
||||
add_path_to_root(&mut res.root, &cut_path, '/', compiled)?
|
||||
}
|
||||
for cut_path in source.plain {
|
||||
let path = format!("{cut_path}{plain_ext}");
|
||||
let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
|
||||
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)
|
||||
}
|
||||
|
||||
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()))?;
|
||||
add_path_with_slashes_to_root(&mut root, "write_tag_start", Value::Str("{{".into()))?;
|
||||
add_path_with_slashes_to_root(&mut root, "roughinsert_tag_start", Value::Str("{[".into()))?;
|
||||
add_path_with_slashes_to_root(&mut root, "magic_block_ending_tag", Value::Str("{%}".into()))?;
|
||||
add_path_with_slashes_to_root(&mut root, "element_ending_tag", Value::Str("{@}".into()))?;
|
||||
Ok(root)
|
||||
pub fn get_all_templates_plus_builtins(
|
||||
p: &str, plain_ext: &str,
|
||||
sanitize: fn(&str) -> String,
|
||||
) -> Result<MTGOTT, Box<dyn Error>>{
|
||||
let mut res = get_all_templates(p, plain_ext)?;
|
||||
add_path_with_slashes_to_root(&mut res.root, "sanitize", Value::Fn(Rc::new(
|
||||
/* One obvious problem with this sanitizer is that it makes copy even when it
|
||||
* takes full ownership of some string and does not have to replace any characters*/
|
||||
|d_state: &RuntimeEnv, _: &MTGOTT, arg: Value, _: u32| -> Result<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>>{
|
||||
get_all_templates_plus_builtins(p, ".html", Box::new(
|
||||
|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))))
|
||||
}
|
||||
}))
|
||||
pub fn get_root_html(p: &str) -> Result<MTGOTT, Box<dyn Error>>{
|
||||
get_all_templates_plus_builtins(p, ".html", escape_for_html)
|
||||
}
|
||||
|
||||
@ -46,11 +46,11 @@ impl RuntimeEnv {
|
||||
}
|
||||
|
||||
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> {
|
||||
let n = if_sub_el.conditions.len();
|
||||
for i in 0..n {
|
||||
let expr_res = self.execute_expression(&if_sub_el.conditions[i], root, arg_stack, recc - 1)?;
|
||||
let expr_res = self.execute_expression(&if_sub_el.conditions[i], mtgott, arg_stack, recc - 1)?;
|
||||
if self.value_to_bool(expr_res.deref())? {
|
||||
return Ok(Some(&if_sub_el.branches[i]));
|
||||
}
|
||||
@ -62,8 +62,8 @@ impl RuntimeEnv {
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_element_block<'r, 'aa>(
|
||||
&self, el: &[SubElement], root: &'r Value, arg_stack: &[&'aa Value], recc: u32
|
||||
fn execute_element_block(
|
||||
&self, el: &[SubElement], mtgott: &MTGOTT, arg_stack: &[& Value], recc: u32
|
||||
) -> Result<String, String> {
|
||||
if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) }
|
||||
let mut res = ElementPartsCollector{res: String::new(), cur_col: 0};
|
||||
@ -73,20 +73,20 @@ impl RuntimeEnv {
|
||||
res.without_pad(ps);
|
||||
},
|
||||
SubElement::If(if_sub_el) =>
|
||||
if let Some(branch_el) = self.get_needed_if_block(if_sub_el, root, arg_stack, recc - 1)? {
|
||||
if let Some(branch_el) = self.get_needed_if_block(if_sub_el, mtgott, arg_stack, recc - 1)? {
|
||||
res.add_single_padded_element_piece(
|
||||
&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(
|
||||
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,
|
||||
_ => return Err(self.make_error("Cannot insert non-string. Try using {{}}")),
|
||||
}),
|
||||
SubElement::For(ForSubElement{iterable, core, join}) => {
|
||||
let _g = self.register("In {%for%}".into());
|
||||
let expr_val = self.execute_expression(iterable, root, arg_stack, recc - 1)?;
|
||||
let expr_val = self.execute_expression(iterable, mtgott, arg_stack, recc - 1)?;
|
||||
let an = arg_stack.len();
|
||||
let saved_offset = res.cur_col;
|
||||
match expr_val.deref() {
|
||||
@ -102,7 +102,7 @@ impl RuntimeEnv {
|
||||
if i > 0 {res.add_padded_element_piece(saved_offset, join); }
|
||||
res.add_padded_element_piece(
|
||||
saved_offset, &self.execute_element_block(
|
||||
&core.sub_elements, root, arg_stack, recc - 1)?);
|
||||
&core.sub_elements, mtgott, arg_stack, recc - 1)?);
|
||||
} else { break }
|
||||
}
|
||||
}
|
||||
@ -116,19 +116,19 @@ impl RuntimeEnv {
|
||||
if i > 0 { res.add_padded_element_piece(saved_offset, join); }
|
||||
res.add_padded_element_piece(
|
||||
saved_offset, &self.execute_element_block(
|
||||
&core.sub_elements, root, arg_stack, recc - 1)?);
|
||||
&core.sub_elements, mtgott, arg_stack, recc - 1)?);
|
||||
}
|
||||
}
|
||||
_ => return Err(self.make_error(&format!("Can't iterate over {}", expr_val.deref().stringify_type())))
|
||||
}
|
||||
}
|
||||
SubElement::Let(expr, core) => {
|
||||
let expr_val = self.execute_expression(expr, 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);
|
||||
arg_stack_updated.extend_from_slice(arg_stack);
|
||||
arg_stack_updated.push(expr_val.deref());
|
||||
res.add_single_padded_element_piece(&self.execute_element_block(
|
||||
&core.sub_elements, root, arg_stack, recc - 1)?);
|
||||
&core.sub_elements, mtgott, arg_stack, recc - 1)?);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -136,13 +136,13 @@ impl RuntimeEnv {
|
||||
}
|
||||
}
|
||||
|
||||
enum ExpressionResult<'l> {
|
||||
LocalRef(&'l Value),
|
||||
Owned(Value),
|
||||
enum ExpressionResult<'l, 't> {
|
||||
LocalRef(&'l Value<'l>),
|
||||
Owned(Value<'t>),
|
||||
}
|
||||
|
||||
|
||||
impl<'l> Deref for ExpressionResult<'l> {
|
||||
impl<'l, 't> Deref for ExpressionResult<'l> {
|
||||
type Target = Value;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -176,15 +176,15 @@ impl RuntimeEnv {
|
||||
}
|
||||
|
||||
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> {
|
||||
if recc == 0 { return Err(self.make_error("Recursion limit exceeded".into())) }
|
||||
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::Get(e1, e2) => {
|
||||
let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, root, arg_stack, recc - 1)?;
|
||||
let r2: ExpressionResult = self.execute_expression(&e2, root, arg_stack, recc - 1)?;
|
||||
let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, mtgott, arg_stack, recc - 1)?;
|
||||
let r2: ExpressionResult = self.execute_expression(&e2, mtgott, arg_stack, recc - 1)?;
|
||||
match (r1, r2.deref()) {
|
||||
(ExpressionResult::LocalRef(Value::Arr(arr)), &Value::Int(index)) => {
|
||||
if index as usize > arr.len() {
|
||||
@ -208,13 +208,13 @@ impl RuntimeEnv {
|
||||
}
|
||||
Expression::Attribute(e1, 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) => {
|
||||
let r1: ExpressionResult = self.execute_expression(e1, root, arg_stack, recc - 1)?;
|
||||
let r2: ExpressionResult = self.execute_expression(e2, root, arg_stack, recc - 1)?;
|
||||
let r1: ExpressionResult = self.execute_expression(e1, mtgott, arg_stack, recc - 1)?;
|
||||
let r2: ExpressionResult = self.execute_expression(e2, mtgott, arg_stack, recc - 1)?;
|
||||
match r1.deref() {
|
||||
Value::Fn(func) => ExpressionResult::Owned(func(self, root, r2.deref(), recc - 1)?),
|
||||
Value::Fn(func) => ExpressionResult::Owned(func(self, mtgott, r2.deref(), recc - 1)?),
|
||||
_ => return Err(self.make_error(&format!(
|
||||
"Can't pass argument to {}", r1.deref().stringify_type())))
|
||||
}
|
||||
@ -225,7 +225,7 @@ impl RuntimeEnv {
|
||||
};
|
||||
Ok(match f1.deref() {
|
||||
Value::RuntimeStr(fnc) => ExpressionResult::Owned(
|
||||
Value::Str(fnc(self, root, recc - 1)?)
|
||||
Value::Str(fnc(self, mtgott, recc - 1)?)
|
||||
),
|
||||
_ => f1
|
||||
})
|
||||
@ -233,31 +233,38 @@ impl RuntimeEnv {
|
||||
}
|
||||
|
||||
/* 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()) }
|
||||
Ok(if prev.len() + 1 == el.argc {
|
||||
Box::new(move |re: &RuntimeEnv, root: &Value, arg: &Value, ecc: u32| -> Result<Value, String> {
|
||||
Ok(if prev.len() + 1 == argc {
|
||||
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())) }
|
||||
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 {
|
||||
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())) }
|
||||
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 */
|
||||
pub fn plemege_to_value(x: Plemege, file_path: &str, recc: u32) -> Result<Value, String> {
|
||||
/* This function is supposed to compile all elements in one particular file.
|
||||
Due to how bad mtgott type system is, Some elements require me to save source tree into
|
||||
a storage*/
|
||||
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()) }
|
||||
match x {
|
||||
Plemege::Package(map) => {
|
||||
let mut new_dict: HashMap<String, Value> = HashMap::new();
|
||||
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))
|
||||
},
|
||||
@ -272,12 +279,13 @@ pub fn plemege_to_value(x: Plemege, file_path: &str, recc: u32) -> Result<Value,
|
||||
}
|
||||
if el.argc == 0 {
|
||||
Ok(Value::RuntimeStr(Box::new(
|
||||
move |re: &RuntimeEnv, root: &Value, recc: u32| -> Result<String, String> {
|
||||
re.execute_element_block(&el.sub_elements, root, &[], recc)
|
||||
move |re: &RuntimeEnv, mtgott: &MTGOTT, recc: u32| -> Result<String, String> {
|
||||
re.execute_element_block(&el.sub_elements, mtgott, &[], recc)
|
||||
})))
|
||||
} else {
|
||||
let c1 = construct_element_closure(Vec::new(), el, recc).map(Value::Fn);
|
||||
c1
|
||||
let argc = el.argc;
|
||||
source.push(el);
|
||||
construct_element_closure(Vec::new(), argc, source.len() - 1, recc).map(Value::Fn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +1,38 @@
|
||||
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::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 {
|
||||
Root,
|
||||
Argument(u64),
|
||||
Local(usize),
|
||||
Get(Box<Expression>, Box<Expression>),
|
||||
Attribute(Box<Expression>, String),
|
||||
Call(Box<Expression>, Box<Expression>),
|
||||
Lambda(NewLambdaExpression),
|
||||
Int(u64),
|
||||
None,
|
||||
Str(String),
|
||||
Arr(Vec<Expression>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct IfSubElement {
|
||||
pub branches: Vec<Element>,
|
||||
pub conditions: Vec<Expression>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ForSubElement {
|
||||
pub iterable: Expression,
|
||||
pub core: Element,
|
||||
@ -28,7 +40,7 @@ pub struct ForSubElement {
|
||||
pub join: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum SubElement {
|
||||
Static(String),
|
||||
// ======== Other are dynamic ========
|
||||
@ -39,19 +51,19 @@ pub enum SubElement {
|
||||
Let(Expression, Element),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Element {
|
||||
pub argc: usize,
|
||||
pub sub_elements: Vec<SubElement>,
|
||||
}
|
||||
pub type Element = Vec<SubElement>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
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>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum FileParsingErrorKind {
|
||||
expected_pack_opening_or_element_opening_or_pack_ending,
|
||||
expected_pack_opening_or_element_opening_or_eof,
|
||||
@ -59,7 +71,8 @@ pub enum FileParsingErrorKind {
|
||||
expected_pack_name,
|
||||
illegal_pack_name,
|
||||
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,
|
||||
incorrect_block_ending_tag_expected_end_element,
|
||||
illegal_element_name,
|
||||
@ -78,7 +91,7 @@ pub enum FileParsingErrorKind {
|
||||
expected_cmd_tag_end_after_expression,
|
||||
expected_comma_or_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_endif_or_else_or_else_if,
|
||||
incorrect_block_ending_tag_expected_normal_or_endif,
|
||||
@ -86,23 +99,24 @@ pub enum FileParsingErrorKind {
|
||||
expected_closing_round_bracket,
|
||||
cant_start_word_immediately_after_digit,
|
||||
integer_parsing_error,
|
||||
illegal_root_attribute_name,
|
||||
expected_attribute_name_after_dot,
|
||||
illegal_attribute_name,
|
||||
illegal_lambda_argument_name,
|
||||
expected_closing_square_bracket,
|
||||
empty_expression_inside_round_brackets,
|
||||
empty_expression_inside_square_brackets,
|
||||
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,
|
||||
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::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct FileParsingError {
|
||||
pub kind: FileParsingErrorKind,
|
||||
pub p1: usize,
|
||||
@ -131,7 +145,13 @@ impl Error for FileParsingError {}
|
||||
|
||||
struct Parser<'a> {
|
||||
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> {
|
||||
@ -201,7 +221,9 @@ impl<'a> Parser<'a> {
|
||||
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)?;
|
||||
let mut res: HashMap<String, Plemege> = HashMap::new();
|
||||
loop {
|
||||
@ -229,18 +251,30 @@ impl<'a> Parser<'a> {
|
||||
return Err(self.new_unexpected_char_error(expected_pack_name))
|
||||
}
|
||||
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))
|
||||
}
|
||||
if let Some(_) = res.get(child_name) {
|
||||
return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p))
|
||||
}
|
||||
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;
|
||||
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("{@") {
|
||||
self.p += 2;
|
||||
self.skip_whitespace();
|
||||
@ -250,15 +284,15 @@ impl<'a> Parser<'a> {
|
||||
return Err(FileParsingError::new(expected_element_name, 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))
|
||||
}
|
||||
if let Some(_) = res.get(child_name) {
|
||||
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();
|
||||
if self.is_char_ahead('$') {
|
||||
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))
|
||||
}
|
||||
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))
|
||||
}
|
||||
if arg_names.iter().any(|b: &&str| *b == arg_name) {
|
||||
@ -289,11 +323,12 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
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) {
|
||||
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 {
|
||||
return Err(self.new_unexpected_char_error(if top {
|
||||
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> {
|
||||
/* 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>, 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)?;
|
||||
let mut res: Vec<SubElement> = Vec::new();
|
||||
let mut tp1 = self.p;
|
||||
@ -422,9 +459,10 @@ impl<'a> Parser<'a> {
|
||||
};
|
||||
|
||||
/* 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);
|
||||
Ok((Element{ argc: arg_names.len(), sub_elements: res }, ree))
|
||||
Ok((res, ree))
|
||||
};
|
||||
|
||||
loop {
|
||||
@ -433,31 +471,31 @@ impl<'a> Parser<'a> {
|
||||
return finishing_touches(ReasonOfElementEnd{p1: self.p, cmd: BlockEndingTag::EOF}, res);
|
||||
} else if self.is_ahead("{{") {
|
||||
fin_static(self, tp1, &mut res);
|
||||
self.p += 2;
|
||||
let expr: Expression = self.parse_expression(arg_names, recc)?;
|
||||
self.skip_whitespace();
|
||||
if !self.is_ahead("}}") {
|
||||
return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p));
|
||||
}
|
||||
self.p += 2;
|
||||
if !matches!(expr, Expression::None){
|
||||
let expr: Expression = self.parse_expression(
|
||||
arg_names, &mut (0..arg_names.len()).collect(), recc)?;
|
||||
res.push(SubElement::InsertExpr(
|
||||
Expression::Call(
|
||||
Box::new(Expression::Attribute(Box::new(Expression::Root), "sanitize".into())),
|
||||
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;
|
||||
} else if self.is_ahead("{[") {
|
||||
fin_static(self, tp1, &mut res);
|
||||
self.p += 2;
|
||||
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))
|
||||
}
|
||||
self.p += 2;
|
||||
if !matches!(expr, Expression::None){
|
||||
self.skip_whitespace();
|
||||
let expr: Expression = self.parse_expression(
|
||||
arg_names, &mut (0..arg_names.len()).collect(), recc)?;
|
||||
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;
|
||||
} else if self.is_ahead("{@}") {
|
||||
fin_static(self, tp1, &mut res);
|
||||
@ -481,7 +519,9 @@ impl<'a> Parser<'a> {
|
||||
let cmd = &self.text[pb..self.p];
|
||||
|
||||
/* 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();
|
||||
if !pelf.is_ahead("%}") {
|
||||
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),
|
||||
"endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res),
|
||||
"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)?),
|
||||
"for" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?),
|
||||
"if" => res.push(self.parse_if(arg_names, source_expr, recc - 1)?),
|
||||
"let" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?),
|
||||
_ => return Err(FileParsingError::new(illegal_command_name, pb, 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> {
|
||||
let p1 = self.p;
|
||||
let expr: Expression = self.parse_expression(arg_names, 150)?;
|
||||
if matches!(expr, Expression::None) {
|
||||
return Err(FileParsingError::new(expected_nonempty_expression, p1, self.p))
|
||||
}
|
||||
fn parse_expression_at_cmd_tag_end(
|
||||
&mut self, arg_names: &Vec<&str>, recc: u32
|
||||
) -> Result<Expression, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let expr: Expression = self.parse_expression(arg_names, &mut (0..arg_names.len()).collect(), recc - 1)?;
|
||||
if !self.is_ahead("%}"){
|
||||
return Err(self.new_unexpected_char_error(expected_cmd_tag_end_after_expression));
|
||||
}
|
||||
self.p += 2;
|
||||
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 {%} */
|
||||
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)?;
|
||||
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, recc - 1)?;
|
||||
let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?;
|
||||
let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names, source_expr, recc - 1)?;
|
||||
conditions.push(expr);
|
||||
match ending_tag.cmd {
|
||||
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)),
|
||||
}
|
||||
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){
|
||||
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}))
|
||||
}
|
||||
|
||||
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.skip_whitespace();
|
||||
let p1 = self.p;
|
||||
@ -571,7 +613,7 @@ impl<'a> Parser<'a> {
|
||||
return Err(FileParsingError::new(expected_variable_name, 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));
|
||||
}
|
||||
self.skip_whitespace();
|
||||
@ -579,17 +621,17 @@ impl<'a> Parser<'a> {
|
||||
return Err(self.new_unexpected_char_error(expected_assignment_operator));
|
||||
}
|
||||
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();
|
||||
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) {
|
||||
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p));
|
||||
}
|
||||
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();
|
||||
let t1 = self.p;
|
||||
self.skip_normal_word();
|
||||
@ -597,28 +639,28 @@ impl<'a> Parser<'a> {
|
||||
return Err(self.new_unexpected_char_error(expected_variable_name));
|
||||
}
|
||||
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));
|
||||
}
|
||||
if name != "_" && arg_names_extended.iter().find(|&&b| b == name).is_some() {
|
||||
return Err(FileParsingError::new(forloop_variable_cant_take_occupied_name, t1, self.p));
|
||||
}
|
||||
Ok(name)
|
||||
Ok((name, t1))
|
||||
}
|
||||
|
||||
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)?;
|
||||
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);
|
||||
|
||||
let mut name2 = "";
|
||||
self.skip_whitespace();
|
||||
if self.is_char_ahead(',') {
|
||||
let mut name2= if self.is_char_ahead(',') {
|
||||
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);
|
||||
|
||||
if !self.is_char_ahead(':'){
|
||||
@ -628,8 +670,9 @@ 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, recc - 1)?;
|
||||
let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?;
|
||||
let (inner_block, ending) = self.parse_element_plus_ending_tag(
|
||||
&arg_names_extended, source_expr, recc - 1)?;
|
||||
let separator: String = String::from(match ending.cmd {
|
||||
BlockEndingTag::NOGAP => "",
|
||||
BlockEndingTag::GAP => " ",
|
||||
@ -642,10 +685,13 @@ impl<'a> Parser<'a> {
|
||||
|
||||
/* Checks for ]} }} and %}. May actually return NoneOfThose */
|
||||
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)?;
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
@ -658,22 +704,18 @@ impl<'a> Parser<'a> {
|
||||
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) {
|
||||
if is_special_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)?;
|
||||
let sub_expr = self.parse_expression(arg_names, used_local_consts, 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
|
||||
@ -682,25 +724,42 @@ impl<'a> Parser<'a> {
|
||||
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 */
|
||||
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.skip_whitespace();
|
||||
if self.is_char_ahead('(') {
|
||||
let p1 = self.p;
|
||||
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();
|
||||
if !self.is_char_ahead(')') {
|
||||
return Err(self.new_unexpected_char_error(expected_closing_round_bracket))
|
||||
}
|
||||
self.p += 1;
|
||||
if matches!(expr, Expression::None){
|
||||
return Err(FileParsingError::new(empty_expression_inside_round_brackets, p1, self.p))
|
||||
}
|
||||
return Ok(expr);
|
||||
}
|
||||
if self.is_digit_ahead() {
|
||||
Ok(Some(expr))
|
||||
} else if self.is_digit_ahead() {
|
||||
let p1 = self.p;
|
||||
loop {
|
||||
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))
|
||||
} else {
|
||||
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)),
|
||||
};
|
||||
}
|
||||
@ -718,49 +777,107 @@ impl<'a> Parser<'a> {
|
||||
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_root_attribute_name, p1, self.p));
|
||||
}
|
||||
let bg: Expression = match arg_names.iter().rposition(|&n| n == toplevel_name) {
|
||||
Some(i) => Expression::Argument(i as u64),
|
||||
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));
|
||||
let p2 = self.p;
|
||||
self.skip_whitespace();
|
||||
if self.is_char_ahead(':') {
|
||||
if is_special_name(toplevel_name) {
|
||||
return Err(FileParsingError::new(illegal_lambda_argument_name, p1, p2))
|
||||
}
|
||||
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 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));
|
||||
let dollar_level_name = &self.text[p1..self.p];
|
||||
if is_special_name(dollar_level_name) {
|
||||
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 {
|
||||
Expression::Argument(0)
|
||||
};
|
||||
self.fn_parse_expression_l2_trail(arg_names, bg, recc - 1)
|
||||
res.push(self.parse_expression(arg_names, used_local_consts, recc - 1)?);
|
||||
if self.is_char_ahead(',') {
|
||||
self.p += 1;
|
||||
self.skip_whitespace();
|
||||
} else if self.is_char_ahead(']') {
|
||||
self.p += 1;
|
||||
break
|
||||
} 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)?;
|
||||
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. If there are no arguments, e1 does not get turn into
|
||||
Expression::Call */
|
||||
let mut e1: Expression = self.parse_expression_l2(arg_names, used_local_consts, recc - 1)?
|
||||
.ok_or(self.new_unexpected_char_error(expected_round_brackets_open_or_dollar_or_word_or_int_or_string_or_square_bracket_open))?;
|
||||
loop {
|
||||
let arg_expr: Expression = self.parse_expression_l2(arg_names, recc - 1)?;
|
||||
if matches!(arg_expr, Expression::None) {
|
||||
break
|
||||
}
|
||||
e1 = Expression::Call(Box::new(e1), Box::new(arg_expr));
|
||||
let arg_expr: Option<Expression> = self.parse_expression_l2(arg_names, used_local_consts,recc - 1)?;
|
||||
if matches!(arg_expr, None) { break }
|
||||
e1 = Expression::Call(Box::new(e1), Box::new(arg_expr.unwrap()));
|
||||
}
|
||||
Ok(e1)
|
||||
}
|
||||
@ -770,18 +887,29 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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, 150)
|
||||
/* file_path is a path without extension and separated by '/' */
|
||||
pub fn make_expression_for_this(file_path: &str) -> Expression {
|
||||
file_path.split("/").fold(Expression::Root, |b, s| {
|
||||
Expression::Attribute(Box::new(b), String::from(s))
|
||||
})
|
||||
}
|
||||
|
||||
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!["$"], 150)?;
|
||||
/* Parses a file treating it like a package (where other packages and elements could be located)
|
||||
* file_path is a path of file, separated by '/' and without mtgott extension */
|
||||
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 {
|
||||
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)),
|
||||
_ => Err(FileParsingError::new(unmatched_magic_block_ending_tag, tt.p1, parser.p)),
|
||||
}
|
||||
|
||||
@ -3,6 +3,8 @@ use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::error::Error;
|
||||
use std::cell::RefCell;
|
||||
use parser::{Element, Expression};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct DebugStateGuard<'a> (
|
||||
&'a RefCell<Vec<String>>
|
||||
@ -33,34 +35,38 @@ impl RuntimeEnv {
|
||||
}
|
||||
}
|
||||
|
||||
/* Arguments are: stack trace for debug, root, actual function argument, recursion counter */
|
||||
pub type HigherLevelFunc = dyn Fn(&RuntimeEnv, &Value, &Value, u32) -> Result<Value, String>;
|
||||
/* Arguments are: stack trace for debug, root, recursion counter */
|
||||
pub type OopsForgotToPrecompileThis = dyn Fn(&RuntimeEnv, &Value, u32) -> Result<String, String>;
|
||||
pub struct MTGOTT{
|
||||
pub root: Value<'static>,
|
||||
pub source_elements: Vec<Element>,
|
||||
pub source_expressions: Vec<Expression>
|
||||
}
|
||||
|
||||
pub enum Value {
|
||||
Str(String),
|
||||
pub enum Value<'t> {
|
||||
Str(Rc<String>),
|
||||
Int(u64),
|
||||
Arr(Vec<Value>),
|
||||
Dict(HashMap<String, Value>),
|
||||
Fn(Box<HigherLevelFunc>),
|
||||
Arr(Rc<Vec<Value<'t>>>),
|
||||
Dict(Rc<HashMap<String, Value<'t>>>),
|
||||
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
|
||||
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>)
|
||||
Value::RuntimeConst. At runtime, this compilation artifact is the same thing as
|
||||
Value\{::RuntimeConst}.
|
||||
Expression evaluator automatically converts them as soon as possible. */
|
||||
RuntimeConst(Box<dyn Fn(&'t RuntimeEnv, &'t MTGOTT, u32) -> Result<Value<'t>, String>>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn as_dict(&self) -> &HashMap<String, Value> {
|
||||
match self { Value::Dict(s) => s, _ => panic!(), }
|
||||
}
|
||||
|
||||
pub fn stringify_type(&self) -> &'static str {
|
||||
impl<'t> Value<'t> {
|
||||
pub fn stringify_type(&self) -> String {
|
||||
match self {
|
||||
Value::Str(_) => "String", Value::Int(_) => "Int", Value::Arr(_) => "Array",
|
||||
Value::Dict(_) => "Dictionary", Value::Fn(_) => "Function", _ => panic!()
|
||||
Value::Str(_) => "String".into(),
|
||||
Value::Int(u64) => "Int".into(),
|
||||
Value::Arr(_) => "Array".into(),
|
||||
Value::Dict(_) => "Dictionary".into(),
|
||||
Value::Fn(_) => "Function".into(),
|
||||
Value::Ref(i) => format!("Reference to {}", i.stringify_type()),
|
||||
Value::RuntimeConst(_) => "Compilation Artifact".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,25 +90,15 @@ impl Debug for Value {
|
||||
write!(f, "Dict({{{}}})", dict_debug.join(", "))
|
||||
}
|
||||
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 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(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;
|
||||
impl MTGOTT {
|
||||
fn get_sub_value<'a>(&'a self, name: &str) -> Result<&'a Value, String> {
|
||||
let mut cur: &Value = &self.root;
|
||||
let parts: Vec<String> = name.split(".").map(String::from).collect();
|
||||
for i in 0usize..parts.len() {
|
||||
match cur {
|
||||
@ -116,38 +112,33 @@ fn get_sub_value<'a>(root: &'a Value, name: &str) -> Result<&'a Value, String> {
|
||||
}
|
||||
}
|
||||
Ok(cur)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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: 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(format!("Called {name} template is a dictionary without `main()`").into())
|
||||
}
|
||||
}
|
||||
_ => main,
|
||||
};
|
||||
match main {
|
||||
pub fn render(&self, guest_root: Value, name: &str, recursion_limit: u32) -> Result<String, Box<dyn Error>> {
|
||||
let main = self.get_sub_value(name)?;
|
||||
let res = match main {
|
||||
Value::Fn(main_func) => {
|
||||
let d_stack = RuntimeEnv(RefCell::new(Vec::new()));
|
||||
let Some(guest_root) = guest_root else {
|
||||
return Err(format!("Need guest argument root for template {name}").into())
|
||||
};
|
||||
let rv = main_func(&d_stack, root, guest_root, 150)?;
|
||||
match rv {
|
||||
Value::Str(answer) => Ok(answer),
|
||||
_ => Err(format!("template {name} returned not a string").into())
|
||||
main_func(&d_stack, self, guest_root, recursion_limit)?
|
||||
}
|
||||
}
|
||||
Value::Str(str) => Ok(str.clone()),
|
||||
Value::RuntimeStr(str_fnc) => {
|
||||
Value::Str(me) => Value::Ref(main),
|
||||
Value::RuntimeConst(zero_arg_fnc) => {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
use yyyi_ru::mtgott::parser::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn generate_strings(
|
||||
@ -25,9 +24,9 @@ fn test_parse_file_with_all_combinations() {
|
||||
|
||||
generate_strings(&mut String::new(), target_length, &alphabet, &mut |s| {
|
||||
println!("Parsing {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 %} a{[111 . 2332]]dasd {%} {% as s e1e %} adas {{}}d {%} ") + s.as_str()).as_str());
|
||||
// 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 %} a{[111 . 2332]]dasd {%} {% as s e1e %} adas {{}}d {%} ") + s.as_str()).as_str());
|
||||
});
|
||||
}
|
||||
|
||||
@ -42,8 +41,8 @@ macro_rules! string_map {
|
||||
|
||||
#[test]
|
||||
fn resulting_package() {
|
||||
assert_eq!(parse_one_file_packed("{@ x @}{@}"),
|
||||
Ok(Plemege::Package(string_map! {
|
||||
"x" => Plemege::Element(Element{argc: 0, sub_elements: vec![]})
|
||||
})))
|
||||
// assert_eq!(parse_one_file_packed("{@ x @}{@}"),
|
||||
// Ok(Plemege::Package(string_map! {
|
||||
// "x" => Plemege::Element(Element{argc: 0, sub_elements: vec![]})
|
||||
// })))
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user