use std::collections::HashMap; 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, Clone)] pub struct NewLambdaExpression { pub local_var_array: Vec, /* 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 */ pub expr_id: usize, } #[derive(Debug, PartialEq, Clone)] pub enum Expression { Root, Local(usize), Attribute(Box, String), Get(Box, Box), Call(Box, Box), Lambda(NewLambdaExpression), Int(u64), Str(String), Arr(Vec), } #[derive(Debug, PartialEq, Clone)] pub struct IfSubElement { pub branches: Vec, pub conditions: Vec, } #[derive(Debug, PartialEq, Clone)] pub struct ForSubElement { pub iterable: Expression, pub core: Element, // Either "\n", " " or "" pub separator: String, } #[derive(Debug, PartialEq, Clone)] pub enum SubElement { Static(String), // ======== Other are dynamic ======== If(IfSubElement), // Both for {{}} and {[]} InsertExpr(Expression), For(ForSubElement), Let(Expression, Element), } pub type Element = Vec; #[derive(Debug, PartialEq)] pub enum Plemege { 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), } #[derive(Debug, PartialEq)] pub enum FileParsingErrorKind { expected_pack_opening_or_element_opening_or_pack_ending, expected_pack_opening_or_element_opening_or_eof, unmatched_pack_ending_tag, expected_pack_name, illegal_pack_name, pack_member_name_already_occupied, 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, expected_argument_name_or_eldef_opening_tag_end, illegal_argument_name, repeated_argument_name, expected_command_name, incorrect_block_ending_tag_expected_normal, expected_write_tag_end_after_expression, expected_roughinsert_tag_end_after_expression, illegal_command_name, expected_cmd_tag_end, expected_variable_name, illegal_variable_name, expected_assignment_operator, expected_cmd_tag_end_after_expression, expected_comma_or_colon, expected_colon, 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, expected_nonempty_expression, expected_closing_round_bracket, cant_start_word_immediately_after_digit, integer_parsing_error, expected_attribute_name_after_dot, illegal_attribute_name, illegal_lambda_argument_name, expected_closing_square_bracket, 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_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)] pub struct FileParsingError { pub kind: FileParsingErrorKind, pub p1: usize, pub p2: usize, } impl FileParsingError { fn new(kind: FileParsingErrorKind, p1: usize, p2: usize) -> Self { Self{kind, p1, p2} } } impl Display for FileParsingErrorKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } impl Display for FileParsingError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "FileParsingError: {} at positions {} to {}", self.kind, self.p1, self.p2) } } impl Error for FileParsingError {} struct Parser<'a> { text: &'a str, 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 } impl<'a> Parser<'a> { fn here(&self)->Option { self.text[self.p..].chars().next() } fn is_ahead(&self, substr: &str)->bool { self.text[self.p..].starts_with(substr) } fn is_char_ahead(&self, ch: char) -> bool { match self.here() { Some(cha) => cha == ch, None => false, } } fn is_digit_ahead(&self) -> bool { match self.here() { Some(ch) => is_digit(ch), None => false, } } fn is_word_ahead(&self) -> bool { match self.here() { Some(ch) => is_normal_word_constituent(ch), None => false, } } fn advance(&mut self) { self.p += self.text[self.p..].chars().next().unwrap().len_utf8(); } fn skip_whitespace(&mut self) { loop { match self.here() { Some(ch ) => if !is_whitespace(ch) { break } else { self.advance(); } None => break } } } fn skip_normal_word(&mut self){ loop { match self.here() { Some(ch ) => if !is_normal_word_constituent(ch) { break } else { self.advance(); } None => break } } } fn next_p(&self) -> usize { match self.text[self.p..].char_indices().next() { Some((off, _)) => self.p + off, None => self.p, } } fn new_unexpected_char_error(&self, kind: FileParsingErrorKind) -> FileParsingError { FileParsingError::new(kind, self.p, self.next_p()) } fn parse_pack_plus_ending( &mut self, top: bool, recc: u32 ) -> Result { self.check_recursion_limit(recc)?; let mut res: HashMap = HashMap::new(); loop { self.skip_whitespace(); if self.p == self.text.len() { return if top { Ok(Plemege::Package(res)) } else { Err(self.new_unexpected_char_error(expected_pack_opening_or_element_opening_or_pack_ending)) } } if self.is_ahead("{$}") { return if top { Err(FileParsingError::new(unmatched_pack_ending_tag, self.p, self.p + 3)) } else { self.p += 3; Ok(Plemege::Package(res)) }; } else if self.is_ahead("{$") { self.p += 2; self.skip_whitespace(); let p1 = self.p; self.skip_normal_word(); if self.p == p1 { return Err(self.new_unexpected_char_error(expected_pack_name)) } let child_name: &str = &self.text[p1..self.p]; 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_whitespace(); if self.is_ahead("$}"){ self.p += 2; res.insert(String::from(child_name), self.parse_pack_plus_ending(false, 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; } 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(); let p1 = self.p; self.skip_normal_word(); if p1 == self.p { return Err(FileParsingError::new(expected_element_name, p1, self.p)) } let child_name = &self.text[p1..self.p]; 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 functions 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("@}") { self.p += 2; break } let p1 = self.p; self.skip_normal_word(); if 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]; 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) { return Err(FileParsingError::new(repeated_argument_name, p1, self.p)) } arg_names.push(arg_name); } 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)) } 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 } else { expected_pack_opening_or_element_opening_or_pack_ending })); } } } } enum BlockEndingTag { EOF, /* This is {@} */ END_ELEMENT, /* These tags are command tags */ /* {%} */ NORMAL, LF, GAP, NOGAP, ENDLOOP, /* Incomplete tag (ony `{% else if` is included */ ELSE_IF, ELSE, ENDIF } struct ReasonOfElementEnd { p1: usize, cmd: BlockEndingTag, } fn fix_whitespaces_in_element(subels: &mut Vec) { let n = subels.len(); if n > 0 { match &mut subels[0] { SubElement::Static(org) => { let mut ta = 0; for p in 0..org.len(){ if !is_whitespace(org.as_bytes()[p] as char) { ta = p; break } if org.as_bytes()[p] == b'\n' { ta = p + 1; } } *org = String::from(&org[ta..]); }, _ => {}, } match &mut subels[n - 1] { SubElement::Static(org) => { while let Some(ch) = org.chars().last() { if is_whitespace(ch) { org.pop(); } else { break } } }, _ => {}, } } let mut min_offset = usize::MAX; for i in 0..subels.len() { match &mut subels[i] { SubElement::Static(org) => { let mut seen_online = i > 0; let mut line_bg: usize = 0; for p in 0..org.len() { let ch = org.as_bytes()[p] as char; if !is_whitespace(ch) { if !seen_online { seen_online = true; min_offset = std::cmp::min(min_offset, p - line_bg) } } else if ch == '\n' { line_bg = p + 1; seen_online = false; } } /* This won't cause issues on the .last() because we previously rstripped the last part */ if !seen_online{ min_offset = std::cmp::min(min_offset, org.len() - line_bg) } }, _ => {}, } } for i in 0..subels.len() { match &mut subels[i] { SubElement::Static(org) => { let mut res: Vec = Vec::new(); let mut should_ignore_gap = i > 0; let mut line_bg: usize = 0; for p in 0..org.len() { let ch = org.as_bytes()[p]; if ch == b'\n' { line_bg = p + 1; should_ignore_gap = false; /* We handle trailing whitespaces case here */ while let Some(&ch) = res.last() { if is_lnspace(ch as char) { res.pop(); } else { break } } } else if p - line_bg < min_offset && !should_ignore_gap{ continue } res.push(ch); } *org = String::from_utf8(res).unwrap(); }, _ => {}, } } } 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> { self.check_recursion_limit(recc)?; let mut res: Vec = Vec::new(); let mut tp1 = self.p; let fin_static = |p: &Parser, tp1: usize, res: &mut Vec| { res.push(SubElement::Static(String::from(&p.text[tp1..p.p]))) }; /* Fixes whitespaces in static sub-elements */ let finishing_touches = |ree: ReasonOfElementEnd, mut res: Vec| -> Result<(Element, ReasonOfElementEnd), FileParsingError> { fix_whitespaces_in_element(&mut res); Ok((res, ree)) }; loop { if self.p == self.text.len() { fin_static(self, tp1, &mut res); 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; if !self.is_ahead("}}") { 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, &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); self.p += 3; return finishing_touches(ReasonOfElementEnd{p1: self.p - 3, cmd: BlockEndingTag::END_ELEMENT}, res); } else if self.is_ahead("{%}") { fin_static(self, tp1, &mut res); self.p += 3; return finishing_touches(ReasonOfElementEnd{p1: self.p - 3, cmd: BlockEndingTag::NORMAL}, res); } else if self.is_ahead("{%") { fin_static(self, tp1, &mut res); /* Might be needed if this is the ENDING cmd tag */ let p1 = self.p; self.p += 2; self.skip_whitespace(); let pb = self.p; self.skip_normal_word(); if pb == self.p { return Err(self.new_unexpected_char_error(expected_command_name)) } 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 | -> Result<(Element, ReasonOfElementEnd), FileParsingError> { pelf.skip_whitespace(); if !pelf.is_ahead("%}") { return Err(pelf.new_unexpected_char_error(expected_cmd_tag_end)); } pelf.p += 2; finishing_touches(ReasonOfElementEnd{p1, cmd}, res) }; match cmd { "lf" => return just_one_thing(self, BlockEndingTag::LF, res), "gap" => return just_one_thing(self, BlockEndingTag::GAP, res), "nogap" => return just_one_thing(self, BlockEndingTag::NOGAP, res), "else" => { self.skip_whitespace(); let ps = self.p; self.skip_normal_word(); if ps == self.p { return just_one_thing(self, BlockEndingTag::ELSE, res) } else if &self.text[ps..self.p] != "if" { return Err(FileParsingError::new(illegal_command_name, pb, self.p)) } return finishing_touches(ReasonOfElementEnd{p1, cmd: BlockEndingTag::ELSE_IF}, res); } "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)?), _ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)), } tp1 = self.p; } else { self.advance(); } } } fn parse_expression_at_cmd_tag_end( &mut self, arg_names: &Vec<&str>, recc: u32 ) -> Result { 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 parses expr %} block {% else if expr %} block {% else %} block {%} */ fn parse_if( &mut self, arg_names: &Vec<&str>, recc: u32 ) -> Result { self.check_recursion_limit(recc)?; let mut conditions: Vec = Vec::new(); let mut blocks: Vec = Vec::new(); loop { let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?; 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 | BlockEndingTag::ELSE_IF => blocks.push(inner_block), _ => return Err(FileParsingError::new( 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)?; 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)); } blocks.push(else_block); break } else if matches!(ending_tag.cmd, BlockEndingTag::NORMAL | BlockEndingTag::ENDIF) { break } } Ok(SubElement::If(IfSubElement{branches: blocks, conditions})) } fn parse_let(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result { self.check_recursion_limit(recc)?; self.skip_whitespace(); let p1 = self.p; self.skip_normal_word(); if p1 == self.p { // Ironically, these symbols are actually constants return Err(FileParsingError::new(expected_variable_name, p1, self.p)); } let new_variable_name = &self.text[p1..self.p]; if is_special_name(new_variable_name){ return Err(FileParsingError::new(illegal_variable_name, p1, self.p)); } self.skip_whitespace(); if !self.is_char_ahead('=') { return Err(self.new_unexpected_char_error(expected_assignment_operator)); } self.p += 1; 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)?; 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) -> Result<(&'a str, usize), FileParsingError> { self.skip_whitespace(); let t1 = self.p; self.skip_normal_word(); if t1 == self.p { return Err(self.new_unexpected_char_error(expected_variable_name)); } let name = &self.text[t1..self.p]; if is_special_name(name) { return Err(FileParsingError::new(illegal_variable_name, t1, self.p)); } Ok((name, t1)) } fn parse_for(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result { self.check_recursion_limit(recc)?; let mut arg_names_extended = arg_names.clone(); let (name1, _) = self.parse_for_new_variable()?; arg_names_extended.push(name1); self.skip_whitespace(); let mut name2= if self.is_char_ahead(',') { self.p += 1; 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(':'){ return Err(self.new_unexpected_char_error( if name2.len() > 0 { expected_colon } else { expected_comma_or_colon } )); } self.p += 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, recc - 1)?; let separator: String = String::from(match ending.cmd { BlockEndingTag::NOGAP => "", BlockEndingTag::GAP => " ", BlockEndingTag::NORMAL | BlockEndingTag::LF | BlockEndingTag::ENDLOOP => "\n", _ => return Err(FileParsingError::new( incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop, ending.p1, self.p)), }); Ok(SubElement::For(ForSubElement{iterable: expr, core: inner_block, separator})) } /* Checks for ]} }} and %}. May actually return NoneOfThose */ fn is_tag_end_ahead(&self) -> bool { ["]}", "}}", "%}", "$}"].iter().any(|s| self.is_ahead(s)) } fn fn_parse_expression_l2_trail( &mut self, arg_names: &Vec<&str>, mut from: Expression, used_local_consts: &mut Vec, recc: u32 ) -> Result { self.check_recursion_limit(recc)?; loop { self.skip_whitespace(); from = if self.is_char_ahead('.') { self.p += 1; self.skip_whitespace(); let attrp1 = self.p; self.skip_normal_word(); if attrp1 == self.p { return Err(self.new_unexpected_char_error(expected_attribute_name_after_dot)); } let attr_name = &self.text[attrp1..self.p]; if is_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('[') { self.p += 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; Expression::Get(Box::new(from), Box::new(sub_expr)) } else { break } } 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, name_in_expr: &str ) -> Option { 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>, used_local_consts: &mut Vec, recc: u32 ) -> Result, FileParsingError> { self.check_recursion_limit(recc)?; self.skip_whitespace(); if self.is_tag_end_ahead() { Ok(None) } else if self.is_char_ahead('(') { self.p += 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; Ok(Some(expr)) } else if self.is_digit_ahead() { let p1 = self.p; loop { if self.is_digit_ahead() { self.p += 1; } else if self.is_word_ahead() { return Err(self.new_unexpected_char_error(cant_start_word_immediately_after_digit)) } else { return match self.text[p1..self.p].parse::() { Ok(v) => Ok(Some(Expression::Int(v))), Err(_) => Err(FileParsingError::new(integer_parsing_error, p1, self.p)), }; } } } else if self.is_word_ahead() { let p1 = self.p; self.skip_normal_word(); let toplevel_name = &self.text[p1..self.p]; 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 argument_iig = arg_names.len(); let mut arg_names_extended = arg_names.clone(); arg_names_extended.push(toplevel_name); let mut used_by_lambda: Vec = 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) && *iig != argument_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 { if *iig == argument_iig { usize::MAX // We assume usize::MAX never appears as an actual index in used_local_consts } else { 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 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(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 = Vec::new(); loop { if self.is_char_ahead(']') { self.p += 1; break; } else { 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 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>, used_local_consts: &mut Vec, recc: u32 ) -> Result { self.check_recursion_limit(recc)?; 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: Option = 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) } fn check_recursion_limit(&self, recc: u32) -> Result<(), FileParsingError> { if recc == 0 { Err(self.new_unexpected_char_error(recursion_limit_exceeded)) } else { Ok(() )} } } /* 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)) }) } /* 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, ) -> Result { let mut parser: Parser = Parser{text, p: 0, this_meaning: make_expression_for_this(file_path), source_expr}; parser.parse_pack_plus_ending(true, 150) } pub fn parse_one_file_simplified( text: &str, file_path: &str, source_expr: &mut Vec, ) -> Result { 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!["$"], 150)?; match tt.cmd { 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)), } }