From 3d5af952121c600fff51c5464100e5d77b31dcdd Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Sun, 27 Apr 2025 01:21:47 +0300 Subject: [PATCH] Some changes to syntax. Operator % is no more... --- mtgott/src/charclasses.rs | 2 +- mtgott/src/dirsearch.rs | 6 +- mtgott/src/lambda_compilation.rs | 1 + mtgott/src/parser.rs | 130 ++++++++--------- mtgott/tests/april_24.rs | 234 +++++++++++++++++++++++++++++++ mtgott/tests/parsing_test.rs | 48 ------- 6 files changed, 299 insertions(+), 122 deletions(-) delete mode 100644 mtgott/tests/parsing_test.rs diff --git a/mtgott/src/charclasses.rs b/mtgott/src/charclasses.rs index 51cb54c..d63c462 100644 --- a/mtgott/src/charclasses.rs +++ b/mtgott/src/charclasses.rs @@ -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) - || "-_+/|&~!^%*".contains(ch) + || "-_+/|&~!^*".contains(ch) } pub fn is_normal_word(s: &str) -> bool { diff --git a/mtgott/src/dirsearch.rs b/mtgott/src/dirsearch.rs index 8470a50..32d3c0c 100644 --- a/mtgott/src/dirsearch.rs +++ b/mtgott/src/dirsearch.rs @@ -2,7 +2,7 @@ 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_special_name}; +use super::charclasses::{escape_for_html, is_normal_word, is_special_name}; use super::runtime::*; use super::parser::{parse_one_file_simplified, parse_one_file_packed}; use super::lambda_compilation::{plemege_to_value}; @@ -63,7 +63,7 @@ pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result match expr { Expression::Int(n) => Ok(SharedValue::Int(n)), Expression::Str(s) => Ok(SharedValue::Str(s)), + // todo: optimize arrays Expression::Lambda(NewLambdaExpression{local_var_array, expr_id}) => { assert_eq!(local_var_array, vec![usize::MAX]); Ok(SharedValue::Fn(Box::new( diff --git a/mtgott/src/parser.rs b/mtgott/src/parser.rs index b2e94bf..006658e 100644 --- a/mtgott/src/parser.rs +++ b/mtgott/src/parser.rs @@ -45,7 +45,7 @@ pub enum SubElement { Static(String), // ======== Other are dynamic ======== If(IfSubElement), - // Both for {{}} and {[]} + // Both for {{}} and {##} InsertExpr(Expression), For(ForSubElement), Let(Expression, Element), @@ -365,79 +365,67 @@ struct ReasonOfElementEnd { 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; - } + if let SubElement::Static(org) = &mut subels[0] { + let mut ta = 0; + for p in 0..org.len(){ + if !is_whitespace(org.as_bytes()[p] as char) { + break } - *org = String::from(&org[ta..]); - }, - _ => {}, + 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 } - } - }, - _ => {}, + if let SubElement::Static(org) = &mut subels[n - 1] { + 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; + if let SubElement::Static(org) = &mut subels[i] { + 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) - } - }, - _ => {}, + } + /* 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 + if let SubElement::Static(org) = &mut subels[i] { + 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 } } - res.push(ch); + } else if p - line_bg < min_offset && !should_ignore_gap{ + continue } - *org = String::from_utf8(res).unwrap(); - }, - _ => {}, + res.push(ch); + } + *org = String::from_utf8(res).unwrap(); } } } @@ -545,7 +533,7 @@ 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)?), + "for" => res.push(self.parse_for(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)), @@ -648,22 +636,24 @@ impl<'a> Parser<'a> { 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(',') { + let (name_key, name_value): (&str, &str) = 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); + (name1, name) + } else { + ("", name1) + }; + arg_names_extended.push(name_key); + arg_names_extended.push(name_value); if !self.is_char_ahead(':'){ return Err(self.new_unexpected_char_error( - if name2.len() > 0 { expected_colon } else { expected_comma_or_colon } + if name_key.len() > 0 { expected_colon } else { expected_comma_or_colon } )); } self.p += 1; @@ -681,9 +671,9 @@ impl<'a> Parser<'a> { Ok(SubElement::For(ForSubElement{iterable: expr, core: inner_block, separator})) } - /* Checks for ]} }} and %}. May actually return NoneOfThose */ + /* Checks for #} }} and %}. May actually return NoneOfThose */ fn is_tag_end_ahead(&self) -> bool { - ["]}", "}}", "%}", "$}"].iter().any(|s| self.is_ahead(s)) + ["#}", "}}", "%}", "$}"].iter().any(|s| self.is_ahead(s)) } fn fn_parse_expression_l2_trail( diff --git a/mtgott/tests/april_24.rs b/mtgott/tests/april_24.rs index 934c572..4c852d8 100644 --- a/mtgott/tests/april_24.rs +++ b/mtgott/tests/april_24.rs @@ -222,3 +222,237 @@ fn t017(){ "index", 50 ).unwrap(), "0"); } + +#[test] +fn t018(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "__".into(), text: "{$arr = [0, 10, 20, 30]$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: " {{ (x:y:z:z.arr[x]) 2 \"Lol\" __ }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + Value::Int(0), "index", 50 + ).unwrap(), "20"); +} + + +#[test] +fn t019(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "__".into(), text: "{$arr = [0, 10, 20, 30]$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: " {{ (x:y:z:w:w x z) 2 \"Lol\" __ x:z:z.arr[x]}}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + Value::Int(0), "index", 50 + ).unwrap(), "20"); +} + + +#[test] +fn t020(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "__".into(), text: "{$arr = [0, 10, 20, 30]$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: " {{ (x:y:z:w:w x z) __ \"Lol\" 2 x:z:x.arr[z]}}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + Value::Int(0), "index", 50 + ).unwrap(), "20"); +} + + +#[test] +fn t021(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "__".into(), text: "{$arr = [\" zero\", 10, 20,]$} {$dict$} {$lol = \"HELLO\"$} {@ cat a b @}{[a]}{[b]}{@} {$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: " {{ __.dict.cat ((a:b:c:c[b][a]) \"lol\" \"dict\" __) __.arr[0] }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + Value::Int(0), "index", 50 + ).unwrap(), "HELLO zero"); +} + +#[test] +fn t022(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "__".into(), text: "{$arr = [\" zero\", 10, 20,]$} {$dict$} {$lol = \"HELLO\"$} {@ cat a b @}{[a]}{[b]}{@} {$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: " {{ __.dict.cat ((a:b:c:c[a][b]) \"dict\" \"lol\" __) __.arr[0] }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + Value::Int(0), "index", 50 + ).unwrap(), "HELLO zero"); +} + +#[test] +fn t023(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "aaa".into(), text: "{@ el arr @} + + A + + B + {@} ".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: " {[ aaa.el ([1, 2, 3]) ]}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + Value::Int(0), "index", 50 + ).unwrap(), "A\n\nB"); +} + +macro_rules! valarr { + ($($el:expr),* $(,)?) => { + Value::Arr(std::rc::Rc::new( + vec![$($el),*] + )) + }; +} + +macro_rules! valdict { + ($($el:literal => $val:expr),* $(,)?) => {{ + let mut hashmap = std::collections::HashMap::new(); + $(hashmap.insert($el.to_string(), $val);)* + Value::Dict(Rc::new(hashmap)) + }} +} + +#[test] +fn t024(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% let x = $[2]%} \n {{x}} \n{%} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + valarr![Value::Int(10), Value::Int(11), Value::Int(12)], + "index", 50 + ).unwrap(), "12"); +} + +#[test] +fn t025(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% let l1 = $[2]%} {%let l2 = l1 [ 0]%}\n\n {{ l2}} \n {%} \n{%} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + valarr![Value::Int(0), Value::Int(1), valarr![Value::Int(69), Value::Int(228)]], + "index", 50 + ).unwrap(), "69"); +} + +#[test] +fn t026(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% for v: $ %}{{v}}{%nogap %} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + valarr![Value::Int(10), Value::Int(11), Value::Int(12)], + "index", 50 + ).unwrap(), "101112"); +} + + +#[test] +fn t027(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% for v: $ %}{{v}}{% gap %} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + valarr![Value::Int(10), Value::Int(11), Value::Int(12)], + "index", 50 + ).unwrap(), "10 11 12"); +} + + +#[test] +fn t028(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% for v: $ %} \n {{v}} \n {% lf %} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + valarr![Value::Int(10), Value::Int(11), Value::Int(12)], + "index", 50 + ).unwrap(), "10\n11\n12"); +} + +#[test] +fn t029(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% for k, v: $ %} {{k }}->{{ v}} {% lf %} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + valarr![Value::Int(10), Value::Int(11), Value::Int(12)], + "index", 50 + ).unwrap(), "0->10\n1->11\n2->12"); +} + + +#[test] +fn t030(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% for k, v: $ %} {{k }}->{{ v}} {% lf %} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render( + valarr![Value::Int(10), Value::Int(11), Value::Int(12)], + "index", 50 + ).unwrap(), "0->10\n1->11\n2->12"); +} + + +#[test] +fn t031(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{% for k, v: $ %} {{k }}:{{ v}} {% gap %} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + let a = r.render( + valdict!["a" => Value::Int(1), "b" => Value::Int(2)], + "index", 50 + ).unwrap(); + assert!(["a:1 b:2".to_string(), "b:2 a:1".to_string()].contains(&a)); +} + +#[test] +fn t032(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "A".into(), text: "".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{%for v: $ %} ->{{v}} {%gap%} ".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + let a = r.render( + valdict!["a" => Value::Int(1), "b" => Value::Int(2)], + "index", 50 + ).unwrap(); + assert!(["->1 ->2".to_string(), "->2 ->1".to_string()].contains(&a)); +} \ No newline at end of file diff --git a/mtgott/tests/parsing_test.rs b/mtgott/tests/parsing_test.rs deleted file mode 100644 index 290b4fd..0000000 --- a/mtgott/tests/parsing_test.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::collections::HashMap; - -fn generate_strings( - prefix: &mut String, - target_length: usize, - alphabet: &[char], - f: &mut impl Fn(String), -) { - if prefix.len() == target_length { - f(prefix.clone()); - } else { - for &c in alphabet { - prefix.push(c); - generate_strings(prefix, target_length, alphabet, f); - prefix.pop(); - } - } -} - -#[test] -fn test_parse_file_with_all_combinations() { - let alphabet = [' ', '{', '%', '}', '$', 'a']; - let target_length = 5 ; - - 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()); - }); -} - -macro_rules! string_map { - ($($key:expr => $val:expr),* $(,)?) => {{ - let mut map = HashMap::new(); - $( map.insert($key.to_string(), $val); )* - 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![]}) - // }))) -}