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

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

View File

@ -4,9 +4,10 @@ use std::env;
use std::process;
use 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}")
}

View File

@ -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("'", "&#39;").replace("\"", "&quot;")
}
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"
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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;
} else {
return Err(self.new_unexpected_char_error(expected_pack_opening_tag_end_or_assignment_operator))
}
self.p += 2;
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();
@ -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)?;
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_roughinsert_tag_end_after_expression, self.p - 2, self.p))
return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p));
}
self.p += 2;
if !matches!(expr, Expression::None){
res.push(SubElement::InsertExpr(expr));
}
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 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)?))
}
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));
}
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())
} else {
Expression::Argument(0)
};
self.fn_parse_expression_l2_trail(arg_names, bg, recc - 1)
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 {
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 {
return Ok(Expression::None)
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)),
}

View File

@ -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,70 +90,55 @@ 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;
let parts: Vec<String> = name.split(".").map(String::from).collect();
for i in 0usize..parts.len() {
match cur {
Value::Dict(hashmap) => {
match hashmap.get(&parts[i]) {
Some(nxt ) => { cur = nxt; }
None => return Err(format!("No such root element: {}", parts[..=i].join("/")))
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 {
Value::Dict(hashmap) => {
match hashmap.get(&parts[i]) {
Some(nxt ) => { cur = nxt; }
None => return Err(format!("No such root element: {}", parts[..=i].join("/")))
}
}
_ => return Err(format!("Not a dictionary: {}", parts[..i].join("/")))
}
_ => return Err(format!("Not a dictionary: {}", parts[..i].join("/")))
}
Ok(cur)
}
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())
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())
};
main_func(&d_stack, self, guest_root, recursion_limit)?
}
}
_ => main,
};
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())
Value::Str(me) => Value::Ref(main),
Value::RuntimeConst(zero_arg_fnc) => {
let mut d_stack = RuntimeEnv(RefCell::new(Vec::new()));
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())
}
Value::Str(str) => Ok(str.clone()),
Value::RuntimeStr(str_fnc) => {
let mut d_stack = RuntimeEnv(RefCell::new(Vec::new()));
Ok(str_fnc(&mut d_stack, root, 150)?)
}
_ => Err(format!("Called {name} template that is not a function / string").into())
}
}

View File

@ -1,4 +1,3 @@
use yyyi_ru::mtgott::parser::*;
use std::collections::HashMap;
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![]})
// })))
}