Some changes to syntax. Operator % is no more...

This commit is contained in:
Андреев Григорий 2025-04-27 01:21:47 +03:00
parent 0d24c782e3
commit 3d5af95212
6 changed files with 299 additions and 122 deletions

View File

@ -14,7 +14,7 @@ pub fn is_digit(ch: char) -> bool {
pub fn is_normal_word_constituent(ch: char) -> bool { pub fn is_normal_word_constituent(ch: char) -> bool {
('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) ('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch)
|| "-_+/|&~!^%*".contains(ch) || "-_+/|&~!^*".contains(ch)
} }
pub fn is_normal_word(s: &str) -> bool { pub fn is_normal_word(s: &str) -> bool {

View File

@ -2,7 +2,7 @@ use std::{fs};
use std::fs::{read_dir, metadata, canonicalize}; use std::fs::{read_dir, metadata, canonicalize};
use std::path::PathBuf; use std::path::PathBuf;
use std::error::Error; 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::runtime::*;
use super::parser::{parse_one_file_simplified, parse_one_file_packed}; use super::parser::{parse_one_file_simplified, parse_one_file_packed};
use super::lambda_compilation::{plemege_to_value}; use super::lambda_compilation::{plemege_to_value};
@ -63,7 +63,7 @@ pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result<MtgottDirFiles, Box
&(String::from(".mtgott") + plain_ext), &(String::from(".mtgott") + plain_ext),
&(String::from(".imtgott") + plain_ext), &(String::from(".imtgott") + plain_ext),
plain_ext plain_ext
], &(|s| !is_special_name(s)))?; ], &(|s| is_normal_word(s) && !is_special_name(s)))?;
Ok(MtgottDirFiles { Ok(MtgottDirFiles {
mtgott: std::mem::take(&mut all[0]), mtgott: std::mem::take(&mut all[0]),
imtgott: std::mem::take(&mut all[1]), imtgott: std::mem::take(&mut all[1]),
@ -140,7 +140,7 @@ pub fn get_all_templates_plus_builtins_from_dir_text(
})))?; })))?;
add_path_with_slashes_to_root(&mut res.root, "cmd_tag_start", SharedValue::Str("{%".into()))?; add_path_with_slashes_to_root(&mut res.root, "cmd_tag_start", SharedValue::Str("{%".into()))?;
add_path_with_slashes_to_root(&mut res.root, "write_tag_start", SharedValue::Str("{{".into()))?; add_path_with_slashes_to_root(&mut res.root, "write_tag_start", SharedValue::Str("{{".into()))?;
add_path_with_slashes_to_root(&mut res.root, "roughinsert_tag_start", SharedValue::Str("{[".into()))?; add_path_with_slashes_to_root(&mut res.root, "roughinsert_tag_start", SharedValue::Str("{#".into()))?;
add_path_with_slashes_to_root(&mut res.root, "magic_block_ending_tag", SharedValue::Str("{%}".into()))?; add_path_with_slashes_to_root(&mut res.root, "magic_block_ending_tag", SharedValue::Str("{%}".into()))?;
add_path_with_slashes_to_root(&mut res.root, "element_ending_tag", SharedValue::Str("{@}".into()))?; add_path_with_slashes_to_root(&mut res.root, "element_ending_tag", SharedValue::Str("{@}".into()))?;
Ok(res) Ok(res)

View File

@ -193,6 +193,7 @@ pub fn plemege_to_value(
Plemege::Expression(expr) => match expr { Plemege::Expression(expr) => match expr {
Expression::Int(n) => Ok(SharedValue::Int(n)), Expression::Int(n) => Ok(SharedValue::Int(n)),
Expression::Str(s) => Ok(SharedValue::Str(s)), Expression::Str(s) => Ok(SharedValue::Str(s)),
// todo: optimize arrays
Expression::Lambda(NewLambdaExpression{local_var_array, expr_id}) => { Expression::Lambda(NewLambdaExpression{local_var_array, expr_id}) => {
assert_eq!(local_var_array, vec![usize::MAX]); assert_eq!(local_var_array, vec![usize::MAX]);
Ok(SharedValue::Fn(Box::new( Ok(SharedValue::Fn(Box::new(

View File

@ -45,7 +45,7 @@ pub enum SubElement {
Static(String), Static(String),
// ======== Other are dynamic ======== // ======== Other are dynamic ========
If(IfSubElement), If(IfSubElement),
// Both for {{}} and {[]} // Both for {{}} and {##}
InsertExpr(Expression), InsertExpr(Expression),
For(ForSubElement), For(ForSubElement),
Let(Expression, Element), Let(Expression, Element),
@ -365,34 +365,27 @@ struct ReasonOfElementEnd {
fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) { fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) {
let n = subels.len(); let n = subels.len();
if n > 0 { if n > 0 {
match &mut subels[0] { if let SubElement::Static(org) = &mut subels[0] {
SubElement::Static(org) => {
let mut ta = 0; let mut ta = 0;
for p in 0..org.len(){ for p in 0..org.len(){
if !is_whitespace(org.as_bytes()[p] as char) { if !is_whitespace(org.as_bytes()[p] as char) {
ta = p; break break
} }
if org.as_bytes()[p] == b'\n' { if org.as_bytes()[p] == b'\n' {
ta = p + 1; ta = p + 1;
} }
} }
*org = String::from(&org[ta..]); *org = String::from(&org[ta..]);
},
_ => {},
} }
match &mut subels[n - 1] { if let SubElement::Static(org) = &mut subels[n - 1] {
SubElement::Static(org) => {
while let Some(ch) = org.chars().last() { while let Some(ch) = org.chars().last() {
if is_whitespace(ch) { org.pop(); } else { break } if is_whitespace(ch) { org.pop(); } else { break }
} }
},
_ => {},
} }
} }
let mut min_offset = usize::MAX; let mut min_offset = usize::MAX;
for i in 0..subels.len() { for i in 0..subels.len() {
match &mut subels[i] { if let SubElement::Static(org) = &mut subels[i] {
SubElement::Static(org) => {
let mut seen_online = i > 0; let mut seen_online = i > 0;
let mut line_bg: usize = 0; let mut line_bg: usize = 0;
for p in 0..org.len() { for p in 0..org.len() {
@ -411,13 +404,10 @@ fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) {
if !seen_online{ if !seen_online{
min_offset = std::cmp::min(min_offset, org.len() - line_bg) min_offset = std::cmp::min(min_offset, org.len() - line_bg)
} }
},
_ => {},
} }
} }
for i in 0..subels.len() { for i in 0..subels.len() {
match &mut subels[i] { if let SubElement::Static(org) = &mut subels[i] {
SubElement::Static(org) => {
let mut res: Vec<u8> = Vec::new(); let mut res: Vec<u8> = Vec::new();
let mut should_ignore_gap = i > 0; let mut should_ignore_gap = i > 0;
let mut line_bg: usize = 0; let mut line_bg: usize = 0;
@ -436,8 +426,6 @@ fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) {
res.push(ch); res.push(ch);
} }
*org = String::from_utf8(res).unwrap(); *org = String::from_utf8(res).unwrap();
},
_ => {},
} }
} }
} }
@ -545,7 +533,7 @@ impl<'a> Parser<'a> {
} }
"endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res), "endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res),
"endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, 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)?), "if" => res.push(self.parse_if(arg_names, recc - 1)?),
"let" => res.push(self.parse_let(arg_names, recc - 1)?), "let" => res.push(self.parse_let(arg_names, recc - 1)?),
_ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)), _ => 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 mut arg_names_extended = arg_names.clone();
let (name1, _) = self.parse_for_new_variable()?; let (name1, _) = self.parse_for_new_variable()?;
arg_names_extended.push(name1);
self.skip_whitespace(); 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; self.p += 1;
let (name, pt2) = self.parse_for_new_variable()?; let (name, pt2) = self.parse_for_new_variable()?;
if name == name1 { if name == name1 {
return Err(FileParsingError::new(forloop_key_and_val_cant_have_same_name, pt2, self.p)) return Err(FileParsingError::new(forloop_key_and_val_cant_have_same_name, pt2, self.p))
} }
name (name1, name)
} else { "" }; } else {
arg_names_extended.push(name2); ("", name1)
};
arg_names_extended.push(name_key);
arg_names_extended.push(name_value);
if !self.is_char_ahead(':'){ if !self.is_char_ahead(':'){
return Err(self.new_unexpected_char_error( 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; self.p += 1;
@ -681,9 +671,9 @@ impl<'a> Parser<'a> {
Ok(SubElement::For(ForSubElement{iterable: expr, core: inner_block, separator})) 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 { 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( fn fn_parse_expression_l2_trail(

View File

@ -222,3 +222,237 @@ fn t017(){
"index", 50 "index", 50
).unwrap(), "0"); ).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));
}

View File

@ -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![]})
// })))
}