Right now, I finally know what to do. I see it all
This commit is contained in:
parent
374d2aef06
commit
495e0f660d
@ -3,7 +3,7 @@ use std::fs::{read_dir, metadata, canonicalize};
|
||||
use std::path::PathBuf;
|
||||
use std::error::Error;
|
||||
use super::charclasses::{escape_for_html, is_bad_name};
|
||||
use super::runtime::{HigherLevelFunc, Value, DebugState, val_lambda_check_argc};
|
||||
use super::runtime::{HigherLevelFunc, Value, DebugState};
|
||||
use super::parser::{parse_one_file_simplified, parse_one_file_packed};
|
||||
use super::lambda_compilation::{plemege_to_value};
|
||||
use std::collections::HashMap;
|
||||
@ -78,7 +78,7 @@ fn add_path_to_root(root: &mut Value, unsplit_path: &str, splitter: char, obj: V
|
||||
Value::Dict(hashmap) => {
|
||||
cur = hashmap.entry(String::from(parts[i])).or_insert(Default::default());
|
||||
}
|
||||
_ => return Err(format!("Overlapping root elements {}", parts[..i].join(&*splitter)).into())
|
||||
_ => return Err(format!("Overlapping root elements {}", parts[..i].join(&String::from(splitter))).into())
|
||||
}
|
||||
}
|
||||
match cur {
|
||||
@ -125,7 +125,7 @@ pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<Value, Box<dyn Err
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: HigherLevelFunc) -> Result<Value, Box<dyn Error>>{
|
||||
pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: Box<HigherLevelFunc>) -> Result<Value, Box<dyn Error>>{
|
||||
let mut root = get_all_templates(p, plain_ext)?;
|
||||
add_path_with_slashes_to_root(&mut root, "sanitize", Value::Fn(sanitize))?;
|
||||
add_path_with_slashes_to_root(&mut root, "cmd_tag_start", Value::Str("{%".into()))?;
|
||||
@ -138,13 +138,12 @@ pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: Highe
|
||||
|
||||
pub fn get_root_html(p: &str) -> Result<Value, Box<dyn Error>>{
|
||||
get_all_templates_plus_builtins(p, ".html", Box::new(
|
||||
|d_state: &DebugState, _: &Value, _: &Value, args: &[&Value]| -> Result<Value, Box<dyn Error>> {
|
||||
|d_state: &DebugState, _: &Value, arg: &Value| -> Result<Value, Box<dyn Error>> {
|
||||
let _g = d_state.register("In sanitizer".into());
|
||||
val_lambda_check_argc(args, 1)?;
|
||||
match args[0]{
|
||||
match arg {
|
||||
Value::Str(s) => Ok(Value::Str(escape_for_html(&s))),
|
||||
Value::Int(num) => Ok(Value::Str(num.to_string())),
|
||||
_ => Ok(Value::Str(escape_for_html(&format!("{:?}", args[0]))))
|
||||
_ => Ok(Value::Str(escape_for_html(&format!("{:?}", arg))))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
@ -8,6 +8,10 @@ fn cat_vec(arr: Vec<String>) -> String {
|
||||
arr.into_iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc })
|
||||
}
|
||||
|
||||
fn compile(el: &[SubElement]) -> Result<Box<HigherLevelFunc>, Box<dyn Error>> {
|
||||
return Err("sd".into())
|
||||
}
|
||||
|
||||
/* This function is supposed to compile all elements in one particular file */
|
||||
pub fn plemege_to_value(mut x: Plemege, file_path: &str) -> Result<Value, Box<dyn Error>> {
|
||||
match x {
|
||||
|
||||
@ -9,7 +9,7 @@ pub enum Expression {
|
||||
Argument(u64),
|
||||
Get(Box<Expression>, Box<Expression>),
|
||||
Attribute(Box<Expression>, String),
|
||||
Call(Box<Expression>, Vec<Expression>),
|
||||
Call(Box<Expression>, Box<Expression>),
|
||||
Int(u64),
|
||||
None,
|
||||
}
|
||||
@ -86,7 +86,7 @@ pub enum FileParsingErrorKind {
|
||||
expected_closing_round_bracket,
|
||||
cant_start_word_immediately_after_digit,
|
||||
integer_parsing_error,
|
||||
illegal_object_name,
|
||||
illegal_root_attribute_name,
|
||||
expected_attribute_name_after_dot,
|
||||
illegal_attribute_name,
|
||||
expected_closing_square_bracket,
|
||||
@ -95,6 +95,9 @@ pub enum FileParsingErrorKind {
|
||||
unmatched_element_ending_tag,
|
||||
unmatched_magic_block_ending_tag,
|
||||
recursion_limit_exceeded,
|
||||
leave_space_between_dollar_argument_and_other_arguments,
|
||||
cant_use_dollar_in_expression_of_element_without_dollar_argument,
|
||||
illegal_dollar_toplevel_attribute_name,
|
||||
}
|
||||
|
||||
use self::FileParsingErrorKind::*;
|
||||
@ -187,14 +190,19 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn new_unexpected_char_error(&self, kind: FileParsingErrorKind) -> FileParsingError {
|
||||
fn next_p(&self) -> usize {
|
||||
match self.text[self.p..].char_indices().next() {
|
||||
Some((off, _)) => FileParsingError::new(kind, self.p, self.p + off),
|
||||
None => FileParsingError::new(kind, self.p, self.p),
|
||||
Some((off, _)) => self.p + off,
|
||||
None => self.p,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_pack_plus_ending(&mut self, top: bool) -> Result<Plemege, FileParsingError> {
|
||||
fn new_unexpected_char_error(&self, kind: FileParsingErrorKind) -> FileParsingError {
|
||||
FileParsingError::new(kind, self.p, self.next_p())
|
||||
}
|
||||
|
||||
fn parse_pack_plus_ending(&mut self, top: bool, recc: u32) -> Result<Plemege, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut res: HashMap<String, Plemege> = HashMap::new();
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
@ -232,7 +240,7 @@ impl<'a> Parser<'a> {
|
||||
return Err(self.new_unexpected_char_error(expected_pack_opening_tag_end))
|
||||
}
|
||||
self.p += 2;
|
||||
res.insert(String::from(child_name), self.parse_pack_plus_ending(false)?);
|
||||
res.insert(String::from(child_name), self.parse_pack_plus_ending(false, recc - 1)?);
|
||||
} else if self.is_ahead("{@") {
|
||||
self.p += 2;
|
||||
self.skip_whitespace();
|
||||
@ -249,6 +257,18 @@ impl<'a> Parser<'a> {
|
||||
return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p))
|
||||
}
|
||||
let mut arg_names: Vec<&str> = Vec::new();
|
||||
|
||||
/* Some function have $ as their first argument, we will let them have it */
|
||||
self.skip_whitespace();
|
||||
if self.is_char_ahead('$') {
|
||||
arg_names.push("$");
|
||||
self.p += 1;
|
||||
if self.is_word_ahead() {
|
||||
return Err(FileParsingError::new(leave_space_between_dollar_argument_and_other_arguments,
|
||||
self.p - 1, self.next_p()));
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
if self.is_ahead("@}") {
|
||||
@ -269,7 +289,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
arg_names.push(arg_name);
|
||||
}
|
||||
let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(&arg_names)?;
|
||||
let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(&arg_names, recc - 1)?;
|
||||
if !matches!(end_cmd.cmd, BlockEndingTag::END_ELEMENT) {
|
||||
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_end_element, end_cmd.p1, self.p))
|
||||
}
|
||||
@ -390,7 +410,8 @@ fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) {
|
||||
impl<'a> Parser<'a> {
|
||||
/* If BlockEndingCmdTag::ELSE_IF is returned, the ending tag won't be read completely,
|
||||
* But in other case it would be read to the end */
|
||||
fn parse_element_plus_ending_tag(&mut self, arg_names: &Vec<&str>) -> Result<(Element, ReasonOfElementEnd), FileParsingError> {
|
||||
fn parse_element_plus_ending_tag(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<(Element, ReasonOfElementEnd), FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut res: Vec<SubElement> = Vec::new();
|
||||
let mut tp1 = self.p;
|
||||
|
||||
@ -413,7 +434,7 @@ impl<'a> Parser<'a> {
|
||||
} else if self.is_ahead("{{") {
|
||||
fin_static(self, tp1, &mut res);
|
||||
self.p += 2;
|
||||
let expr: Expression = self.parse_expression(arg_names)?;
|
||||
let expr: Expression = self.parse_expression(arg_names, recc)?;
|
||||
if !self.is_ahead("}}") {
|
||||
return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p));
|
||||
}
|
||||
@ -421,15 +442,15 @@ impl<'a> Parser<'a> {
|
||||
if !matches!(expr, Expression::None){
|
||||
res.push(SubElement::InsertExpr(
|
||||
Expression::Call(
|
||||
Box::new(Expression::ToplevelAttribute("sanitize".into())),
|
||||
vec![expr])
|
||||
Box::new(Expression::Attribute(Box::new(Expression::Root), "sanitize".into())),
|
||||
Box::new(expr))
|
||||
));
|
||||
}
|
||||
tp1 = self.p;
|
||||
} else if self.is_ahead("{[") {
|
||||
fin_static(self, tp1, &mut res);
|
||||
self.p += 2;
|
||||
let expr: Expression = self.parse_expression(arg_names)?;
|
||||
let expr: Expression = self.parse_expression(arg_names, recc)?;
|
||||
if !self.is_ahead("]}") {
|
||||
return Err(FileParsingError::new(expected_roughinsert_tag_end_after_expression, self.p - 2, self.p))
|
||||
}
|
||||
@ -486,9 +507,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
"endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res),
|
||||
"endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res),
|
||||
"for" => res.push(self.parse_let(arg_names)?),
|
||||
"if" => res.push(self.parse_if(arg_names)?),
|
||||
"let" => res.push(self.parse_let(arg_names)?),
|
||||
"for" => res.push(self.parse_let(arg_names, recc - 1)?),
|
||||
"if" => res.push(self.parse_if(arg_names, recc - 1)?),
|
||||
"let" => res.push(self.parse_let(arg_names, recc - 1)?),
|
||||
_ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)),
|
||||
}
|
||||
tp1 = self.p;
|
||||
@ -500,7 +521,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
fn parse_expression_at_cmd_tag_end(&mut self, arg_names: &Vec<&str>) -> Result<Expression, FileParsingError> {
|
||||
let p1 = self.p;
|
||||
let expr: Expression = self.parse_expression(arg_names)?;
|
||||
let expr: Expression = self.parse_expression(arg_names, 150)?;
|
||||
if matches!(expr, Expression::None) {
|
||||
return Err(FileParsingError::new(expected_nonempty_expression, p1, self.p))
|
||||
}
|
||||
@ -512,12 +533,13 @@ impl<'a> Parser<'a> {
|
||||
|
||||
/* It turned out to be so complex I put it in a separate function.
|
||||
* It parses expr %} block {% else if expr %} block {% else %} block {%} */
|
||||
fn parse_if(&mut self, arg_names: &Vec<&str>) -> Result<SubElement, FileParsingError> {
|
||||
fn parse_if(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut conditions: Vec<Expression> = Vec::new();
|
||||
let mut blocks: Vec<Element> = Vec::new();
|
||||
loop {
|
||||
let expr = self.parse_expression_at_cmd_tag_end(arg_names)?;
|
||||
let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names)?;
|
||||
let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names, recc - 1)?;
|
||||
conditions.push(expr);
|
||||
match ending_tag.cmd {
|
||||
BlockEndingTag::ELSE | BlockEndingTag::NORMAL | BlockEndingTag::ENDIF |
|
||||
@ -526,7 +548,7 @@ impl<'a> Parser<'a> {
|
||||
incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if, ending_tag.p1, self.p)),
|
||||
}
|
||||
if matches!(ending_tag.cmd, BlockEndingTag::ELSE) {
|
||||
let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names)?;
|
||||
let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names, recc - 1)?;
|
||||
if !matches!(the_end.cmd, BlockEndingTag::NORMAL | BlockEndingTag::ENDIF){
|
||||
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal_or_endif, the_end.p1, self.p));
|
||||
}
|
||||
@ -539,7 +561,8 @@ impl<'a> Parser<'a> {
|
||||
Ok(SubElement::If(IfSubElement{branches: blocks, conditions}))
|
||||
}
|
||||
|
||||
fn parse_let(&mut self, arg_names: &Vec<&str>) -> Result<SubElement, FileParsingError> {
|
||||
fn parse_let(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
self.skip_whitespace();
|
||||
let p1 = self.p;
|
||||
self.skip_normal_word();
|
||||
@ -559,7 +582,7 @@ impl<'a> Parser<'a> {
|
||||
let expr = self.parse_expression_at_cmd_tag_end(arg_names)?;
|
||||
let mut arg_names_extended = arg_names.clone();
|
||||
arg_names_extended.push(new_variable_name);
|
||||
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended)?;
|
||||
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, recc - 1)?;
|
||||
if !matches!(ending.cmd, BlockEndingTag::NORMAL) {
|
||||
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p));
|
||||
}
|
||||
@ -583,7 +606,8 @@ impl<'a> Parser<'a> {
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn parse_for(&mut self, arg_names: &Vec<&str>) -> Result<SubElement, FileParsingError> {
|
||||
fn parse_for(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut arg_names_extended = arg_names.clone();
|
||||
|
||||
let name1 = self.parse_for_new_variable(&arg_names_extended)?;
|
||||
@ -605,7 +629,7 @@ impl<'a> Parser<'a> {
|
||||
self.p += 1;
|
||||
|
||||
let expr = self.parse_expression_at_cmd_tag_end(arg_names)?;
|
||||
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended)?;
|
||||
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, recc - 1)?;
|
||||
let separator: String = String::from(match ending.cmd {
|
||||
BlockEndingTag::NOGAP => "",
|
||||
BlockEndingTag::GAP => " ",
|
||||
@ -621,13 +645,51 @@ impl<'a> Parser<'a> {
|
||||
self.is_ahead("]}") || self.is_ahead("}}") || self.is_ahead("%}")
|
||||
}
|
||||
|
||||
fn fn_parse_expression_l2_trail(&mut self, arg_names: &Vec<&str>, mut from: Expression, recc: u32) -> Result<Expression, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
from = if self.is_char_ahead('.') {
|
||||
self.p += 1;
|
||||
self.skip_whitespace();
|
||||
let attrp1 = self.p;
|
||||
self.skip_normal_word();
|
||||
if attrp1 == self.p {
|
||||
return Err(self.new_unexpected_char_error(expected_attribute_name_after_dot));
|
||||
}
|
||||
let attr_name = &self.text[attrp1..self.p];
|
||||
if is_bad_name(attr_name) {
|
||||
return Err(FileParsingError::new(illegal_attribute_name, attrp1, self.p));
|
||||
}
|
||||
Expression::Attribute(Box::new(from), String::from(attr_name))
|
||||
} else if self.is_char_ahead('[') {
|
||||
let sqbrp1 = self.p;
|
||||
self.p += 1;
|
||||
let sub_expr = self.parse_expression(arg_names, recc - 1)?;
|
||||
self.skip_whitespace();
|
||||
if !self.is_char_ahead(']') {
|
||||
return Err(self.new_unexpected_char_error(expected_closing_square_bracket))
|
||||
}
|
||||
self.p += 1;
|
||||
if matches!(sub_expr, Expression::None) {
|
||||
return Err(FileParsingError::new(empty_expression_inside_square_brackets, sqbrp1, self.p))
|
||||
}
|
||||
Expression::Get(Box::new(from), Box::new(sub_expr))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
Ok(from)
|
||||
}
|
||||
|
||||
/* l1 expression = l2 space l2 space ... space l2 */
|
||||
fn parse_expression_l2(&mut self, arg_names: &Vec<&str>) -> Result<Expression, FileParsingError> {
|
||||
fn parse_expression_l2(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<Expression, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
self.skip_whitespace();
|
||||
if self.is_char_ahead('(') {
|
||||
let p1 = self.p;
|
||||
self.p += 1;
|
||||
let expr = self.parse_expression(arg_names)?;
|
||||
let expr = self.parse_expression(arg_names, recc - 1)?;
|
||||
self.skip_whitespace();
|
||||
if !self.is_char_ahead(')') {
|
||||
return Err(self.new_unexpected_char_error(expected_closing_round_bracket))
|
||||
@ -657,81 +719,67 @@ impl<'a> Parser<'a> {
|
||||
self.skip_normal_word();
|
||||
let toplevel_name = &self.text[p1..self.p];
|
||||
if is_bad_name(toplevel_name) {
|
||||
return Err(FileParsingError::new(illegal_object_name, p1, self.p));
|
||||
return Err(FileParsingError::new(illegal_root_attribute_name, p1, self.p));
|
||||
}
|
||||
let mut bg: Expression = match arg_names.iter().rposition(|&n| n == toplevel_name) {
|
||||
let bg: Expression = match arg_names.iter().rposition(|&n| n == toplevel_name) {
|
||||
Some(i) => Expression::Argument(i as u64),
|
||||
None => Expression::ToplevelAttribute(toplevel_name.into())
|
||||
None => Expression::Attribute(Box::new(Expression::Root), toplevel_name.into())
|
||||
};
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
bg = if self.is_char_ahead('.') {
|
||||
self.p += 1;
|
||||
self.skip_whitespace();
|
||||
let attrp1 = self.p;
|
||||
self.skip_normal_word();
|
||||
if attrp1 == self.p {
|
||||
return Err(self.new_unexpected_char_error(expected_attribute_name_after_dot));
|
||||
}
|
||||
let attr_name = &self.text[attrp1..self.p];
|
||||
if is_bad_name(attr_name) {
|
||||
return Err(FileParsingError::new(illegal_attribute_name, attrp1, self.p));
|
||||
}
|
||||
Expression::Attribute(Box::new(bg), String::from(attr_name))
|
||||
} else if self.is_char_ahead('[') {
|
||||
let sqbrp1 = self.p;
|
||||
self.p += 1;
|
||||
let sub_expr = self.parse_expression(arg_names)?;
|
||||
self.skip_whitespace();
|
||||
if !self.is_char_ahead(']') {
|
||||
return Err(self.new_unexpected_char_error(expected_closing_square_bracket))
|
||||
}
|
||||
self.p += 1;
|
||||
if matches!(sub_expr, Expression::None) {
|
||||
return Err(FileParsingError::new(empty_expression_inside_square_brackets, sqbrp1, self.p))
|
||||
}
|
||||
Expression::Get(Box::new(bg), Box::new(sub_expr))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
self.fn_parse_expression_l2_trail(arg_names, bg, recc - 1)
|
||||
} else if self.is_char_ahead('$') {
|
||||
if arg_names.len() < 1 || arg_names[0] != "$" {
|
||||
return Err(self.new_unexpected_char_error(cant_use_dollar_in_expression_of_element_without_dollar_argument));
|
||||
}
|
||||
return Ok(bg)
|
||||
self.p += 1;
|
||||
let bg: Expression = if self.is_word_ahead() {
|
||||
let p1 = self.p;
|
||||
self.skip_normal_word();
|
||||
let toplevel_name = &self.text[p1..self.p];
|
||||
if is_bad_name(toplevel_name) {
|
||||
return Err(FileParsingError::new(illegal_dollar_toplevel_attribute_name, p1, self.p));
|
||||
}
|
||||
Expression::Attribute(Box::new(Expression::Argument(0)), toplevel_name.into())
|
||||
} else {
|
||||
Expression::Argument(0)
|
||||
};
|
||||
self.fn_parse_expression_l2_trail(arg_names, bg, recc - 1)
|
||||
} else {
|
||||
return Ok(Expression::None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_expression(&mut self, arg_names: &Vec<&str>) -> Result<Expression, FileParsingError> {
|
||||
let e1: Expression = self.parse_expression_l2(arg_names)?;
|
||||
let mut call_args: Vec<Expression> = Vec::new();
|
||||
fn parse_expression(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<Expression, FileParsingError> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut e1: Expression = self.parse_expression_l2(arg_names, recc - 1)?;
|
||||
/* It is okay to enter call_args reading loop even when e1 is None.
|
||||
If parse_expression_l2 returned None, subsequent call to parse_expression_l2
|
||||
is guaranteed to return None. Arg list will be empty and the final `if` will choose
|
||||
the second branch, which will return Expression::None */
|
||||
is guaranteed to return None. If there are no arguments, e1 does not get turn into
|
||||
Expression::Call */
|
||||
loop {
|
||||
let arg_expr: Expression = self.parse_expression_l2(arg_names)?;
|
||||
let arg_expr: Expression = self.parse_expression_l2(arg_names, recc - 1)?;
|
||||
if matches!(arg_expr, Expression::None) {
|
||||
break
|
||||
}
|
||||
call_args.push(arg_expr)
|
||||
e1 = Expression::Call(Box::new(e1), Box::new(arg_expr));
|
||||
}
|
||||
Ok(if call_args.len() > 0 {
|
||||
Expression::Call(Box::new(e1), call_args)
|
||||
} else {
|
||||
e1
|
||||
})
|
||||
Ok(e1)
|
||||
}
|
||||
|
||||
fn check_recursion_limit(&self, recc: u32) -> Result<(), FileParsingError> {
|
||||
if recc == 0 { Err(self.new_unexpected_char_error(recursion_limit_exceeded)) } else { Ok(() )}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Parses a file treating it like a package (where other packages and elements could be located) */
|
||||
pub fn parse_one_file_packed(text: &str) -> Result<Plemege, FileParsingError> {
|
||||
let mut parser: Parser = Parser{text, p: 0};
|
||||
parser.parse_pack_plus_ending(true)
|
||||
parser.parse_pack_plus_ending(true, 150)
|
||||
}
|
||||
|
||||
pub fn parse_one_file_simplified(text: &str) -> Result<Plemege, FileParsingError> {
|
||||
let mut parser: Parser = Parser{text, p: 0};
|
||||
let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&Vec::new())?;
|
||||
let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&vec!["$"], 150)?;
|
||||
match tt.cmd {
|
||||
BlockEndingTag::EOF => Ok(Plemege::Element(el)),
|
||||
BlockEndingTag::END_ELEMENT => Err(FileParsingError::new(unmatched_element_ending_tag, tt.p1, parser.p)),
|
||||
|
||||
@ -25,14 +25,23 @@ impl<'a> DebugState {
|
||||
}
|
||||
}
|
||||
|
||||
pub type HigherLevelFunc = Box<dyn Fn(&DebugState, &Value, &[&Value]) -> Result<Value, Box<dyn Error>>>;
|
||||
/* Arguments are: stack trace for debug, root, actual function argument */
|
||||
pub type HigherLevelFunc = dyn Fn(&DebugState, &Value, &Value) -> Result<Value, Box<dyn Error>>;
|
||||
/* Arguments are: stack trace for debug, root */
|
||||
pub type OopsForgotToPrecompileThis = dyn Fn(&DebugState, &Value) -> Result<String, Box<dyn Error>>;
|
||||
|
||||
pub enum Value {
|
||||
Str(String),
|
||||
Int(u64),
|
||||
Arr(Vec<Value>),
|
||||
Dict(HashMap<String, Value>),
|
||||
Fn(HigherLevelFunc),
|
||||
Fn(Box<HigherLevelFunc>),
|
||||
/* Some may call it a crutch (and I am definitely agree with them): my current mtgott
|
||||
compiler does not perform precomputational optimization for elements with no arguments.
|
||||
They cannot be turned into Fn, as it would be a type error, so they get compiled into
|
||||
Value::RuntimeStr. At runtime, this compilation artifact is the same thing as
|
||||
Value::Str */
|
||||
RuntimeStr(Box<OopsForgotToPrecompileThis>)
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@ -41,15 +50,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/* Useful utility to put in every function you see */
|
||||
pub fn val_lambda_check_argc(argv: &[&Value], argc_required: usize) -> Result<(), String> {
|
||||
if argv.len() == argc_required {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Function takes {argc_required} arguments, but {} were given", argv.len()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
fn default() -> Self {
|
||||
Value::Int(0)
|
||||
@ -69,6 +69,7 @@ impl Debug for Value {
|
||||
write!(f, "Dict({{{}}})", dict_debug.join(", "))
|
||||
}
|
||||
Value::Fn(_) => write!(f, "Fn(<function>)"),
|
||||
Value::RuntimeStr(_) => write!(f, "C-comp-pilat_onArrrr%%#^#}}&_)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,7 +89,7 @@ impl<'t> PartialEq for Value {
|
||||
|
||||
fn get_sub_value<'a>(root: &'a Value, name: &str) -> Result<&'a Value, String> {
|
||||
let mut cur: &Value = root;
|
||||
let parts = name.split(".").map(String::from).collect();
|
||||
let parts: Vec<String> = name.split(".").map(String::from).collect();
|
||||
for i in 0usize..parts.len() {
|
||||
match cur {
|
||||
Value::Dict(hashmap) => {
|
||||
@ -105,26 +106,34 @@ fn get_sub_value<'a>(root: &'a Value, name: &str) -> Result<&'a Value, String> {
|
||||
|
||||
/* If some `top-level-attribute` was not found in root, we search for it in guest_root.
|
||||
* This is our way of passing arguments for template */
|
||||
pub fn generate_template(root: &Value, guest_root: &Value, name: &str) -> Result<String, Box<dyn Error>> {
|
||||
pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) -> Result<String, Box<dyn Error>> {
|
||||
let main = get_sub_value(root, name)?;
|
||||
let main = match main {
|
||||
Value::Dict(p) => {
|
||||
match p.get("main".into()) {
|
||||
Some(v2) => v2,
|
||||
None => return Err(Box::new(format!("Called {name} template is a dictionary without `main()`")))
|
||||
None => return Err(format!("Called {name} template is a dictionary without `main()`").into())
|
||||
}
|
||||
}
|
||||
_ => main,
|
||||
};
|
||||
match main {
|
||||
match main {
|
||||
Value::Fn(main_func) => {
|
||||
let d_stack = DebugState(RefCell::new(Vec::new()));
|
||||
let rv = main_func(&d_stack, root, &[guest_root])?;
|
||||
let Some(guest_root) = guest_root else {
|
||||
return Err(format!("Need guest argument root for template {name}").into())
|
||||
};
|
||||
let rv = main_func(&d_stack, root, guest_root)?;
|
||||
match rv {
|
||||
Value::Str(answer) => Ok(answer),
|
||||
_ => Err(Box::new(format!("template {name} returned not a string")))
|
||||
_ => Err(format!("template {name} returned not a string").into())
|
||||
}
|
||||
}
|
||||
_ => Err(Box::new(format!("Called {name} template that is not a function")))
|
||||
Value::Str(str) => Ok(str.clone()),
|
||||
Value::RuntimeStr(str_fnc) => {
|
||||
let mut d_stack = DebugState(RefCell::new(Vec::new()));
|
||||
Ok(str_fnc(&mut d_stack, root)?)
|
||||
}
|
||||
_ => Err(format!("Called {name} template that is not a function / string").into())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user