Daily update. One passing test :)

This commit is contained in:
Андреев Григорий 2025-04-05 23:22:07 +03:00
parent 0294b3e623
commit 00b00cf26a
3 changed files with 79 additions and 46 deletions

View File

@ -1,11 +1,11 @@
use std::ops::RangeBounds; use std::ops::RangeBounds;
pub fn is_lnspace(ch: char) -> bool { pub fn is_lnspace(ch: char) -> bool {
ch == '\t' && ch == ' ' && ch == '\r' ch == '\t' || ch == ' ' || ch == '\r'
} }
pub fn is_whitespace(ch: char) -> bool { pub fn is_whitespace(ch: char) -> bool {
is_lnspace(ch) && ch == '\n' is_lnspace(ch) || ch == '\n'
} }
pub fn is_digit(ch: char) -> bool { pub fn is_digit(ch: char) -> bool {

View File

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::mtgott::charclasses::*; use crate::mtgott::charclasses::*;
#[derive(Debug, PartialEq, Eq)]
pub enum Expression { pub enum Expression {
Root, Root,
Argument(u64), Argument(u64),
@ -11,38 +12,44 @@ pub enum Expression {
None, None,
} }
#[derive(Debug, PartialEq, Eq)]
pub struct IfSubElement { pub struct IfSubElement {
branches: Vec<Element>, pub branches: Vec<Element>,
conditions: Vec<Expression> pub conditions: Vec<Expression>,
} }
#[derive(Debug, PartialEq, Eq)]
pub struct ForSubElement { pub struct ForSubElement {
iterable: Expression, pub iterable: Expression,
core: Element, pub core: Element,
/* Either "\n", " " or "" */ // Either "\n", " " or ""
join: String, pub join: String,
} }
pub enum SubElement{ #[derive(Debug, PartialEq, Eq)]
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),
} }
#[derive(Debug, PartialEq, Eq)]
pub struct Element { pub struct Element {
argc: usize, pub argc: usize,
sub_elements: Vec<SubElement> pub sub_elements: Vec<SubElement>,
} }
#[derive(Debug, PartialEq, Eq)]
pub enum Plemege { pub enum Plemege {
Element(Element), Element(Element),
Package(Box<HashMap<String, Plemege>>), Package(HashMap<String, Plemege>),
} }
#[derive(Debug, PartialEq, Eq)]
pub enum FileParsingErrorKind { pub enum FileParsingErrorKind {
expected_pack_opening_or_element_opening_or_pack_ending, expected_pack_opening_or_element_opening_or_pack_ending,
expected_pack_opening_or_element_opening_or_eof, expected_pack_opening_or_element_opening_or_eof,
@ -52,6 +59,7 @@ pub enum FileParsingErrorKind {
pack_member_name_already_occupied, pack_member_name_already_occupied,
expected_pack_opening_tag_end, expected_pack_opening_tag_end,
expected_element_name, expected_element_name,
incorrect_block_ending_tag_expected_end_element,
illegal_element_name, illegal_element_name,
expected_argument_name_or_eldef_opening_tag_end, expected_argument_name_or_eldef_opening_tag_end,
illegal_argument_name, illegal_argument_name,
@ -87,10 +95,11 @@ pub enum FileParsingErrorKind {
use FileParsingErrorKind::*; use FileParsingErrorKind::*;
use crate::mtgott::charclasses::{is_bad_name, is_digit, is_illegal_name, is_lnspace, is_normal_word_constituent, is_whitespace}; use crate::mtgott::charclasses::{is_bad_name, is_digit, is_illegal_name, is_lnspace, is_normal_word_constituent, is_whitespace};
#[derive(Debug, PartialEq, Eq)]
pub struct FileParsingError { pub struct FileParsingError {
kind: FileParsingErrorKind, pub kind: FileParsingErrorKind,
p1: usize, pub p1: usize,
p2: usize, pub p2: usize,
} }
impl FileParsingError { impl FileParsingError {
@ -135,7 +144,7 @@ impl<'a> Parser<'a> {
} }
fn advance(&mut self) { fn advance(&mut self) {
self.p += self.text[self.p..].char_indices().next().unwrap().0; self.p += self.text[self.p..].chars().next().unwrap().len_utf8();
} }
fn skip_whitespace(&mut self) { fn skip_whitespace(&mut self) {
@ -173,7 +182,7 @@ impl<'a> Parser<'a> {
self.skip_whitespace(); self.skip_whitespace();
if self.p == self.text.len() { if self.p == self.text.len() {
return if top { return if top {
Ok(Plemege::Package(Box::new(res))) Ok(Plemege::Package(res))
} else { } else {
Err(self.new_unexpected_char_error(expected_pack_opening_or_element_opening_or_pack_ending)) Err(self.new_unexpected_char_error(expected_pack_opening_or_element_opening_or_pack_ending))
} }
@ -183,7 +192,7 @@ impl<'a> Parser<'a> {
Err(FileParsingError::new(unmatched_pack_ending_tag, self.p, self.p + 3)) Err(FileParsingError::new(unmatched_pack_ending_tag, self.p, self.p + 3))
} else { } else {
self.p += 3; self.p += 3;
Ok(Plemege::Package(Box::new(res))) Ok(Plemege::Package(res))
}; };
} else if self.is_ahead("{$") { } else if self.is_ahead("{$") {
self.p += 2; self.p += 2;
@ -243,8 +252,8 @@ impl<'a> Parser<'a> {
arg_names.push(arg_name); arg_names.push(arg_name);
} }
let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(&arg_names)?; let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(&arg_names)?;
if !matches!(end_cmd.cmd, BlockEndingCmdTag::NORMAL) { if !matches!(end_cmd.cmd, BlockEndingTag::END_ELEMENT) {
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, end_cmd.p1, self.p)) 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(child_el));
} else { } else {
@ -258,12 +267,17 @@ impl<'a> Parser<'a> {
} }
} }
enum BlockEndingCmdTag { enum BlockEndingTag {
/* This is {@} */
END_ELEMENT,
/* These tags are command tags */
/* {%} */
NORMAL, NORMAL,
LF, LF,
GAP, GAP,
NOGAP, NOGAP,
ENDLOOP, ENDLOOP,
/* Incomplete tag (ony `{% else if` is included */
ELSE_IF, ELSE_IF,
ELSE, ELSE,
ENDIF ENDIF
@ -271,7 +285,7 @@ enum BlockEndingCmdTag {
struct ReasonOfElementEnd { struct ReasonOfElementEnd {
p1: usize, p1: usize,
cmd: BlockEndingCmdTag, cmd: BlockEndingTag,
} }
fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) { fn fix_whitespaces_in_element(subels: &mut Vec<SubElement>) {
@ -404,10 +418,14 @@ impl<'a> Parser<'a> {
res.push(SubElement::InsertExpr(expr)); res.push(SubElement::InsertExpr(expr));
} }
tp1 = self.p; 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("{%}") { } else if self.is_ahead("{%}") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
self.p += 3; self.p += 3;
return finishing_touches(ReasonOfElementEnd{p1: self.p - 3, cmd: BlockEndingCmdTag::NORMAL}, res); return finishing_touches(ReasonOfElementEnd{p1: self.p - 3, cmd: BlockEndingTag::NORMAL}, res);
} else if self.is_ahead("{%") { } else if self.is_ahead("{%") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
/* Might be needed if this is the ENDING cmd tag */ /* Might be needed if this is the ENDING cmd tag */
@ -422,7 +440,7 @@ impl<'a> Parser<'a> {
let cmd = &self.text[pb..self.p]; let cmd = &self.text[pb..self.p];
/* Read space + expect %} and do finishing_touches */ /* Read space + expect %} and do finishing_touches */
let just_one_thing = |pelf: &mut Parser, cmd: BlockEndingCmdTag, 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(); pelf.skip_whitespace();
if !pelf.is_ahead("%}") { if !pelf.is_ahead("%}") {
return Err(pelf.new_unexpected_char_error(expected_cmd_tag_end)); return Err(pelf.new_unexpected_char_error(expected_cmd_tag_end));
@ -432,22 +450,22 @@ impl<'a> Parser<'a> {
}; };
match cmd { match cmd {
"lf" => return just_one_thing(self, BlockEndingCmdTag::LF, res), "lf" => return just_one_thing(self, BlockEndingTag::LF, res),
"gap" => return just_one_thing(self, BlockEndingCmdTag::GAP, res), "gap" => return just_one_thing(self, BlockEndingTag::GAP, res),
"nogap" => return just_one_thing(self, BlockEndingCmdTag::NOGAP, res), "nogap" => return just_one_thing(self, BlockEndingTag::NOGAP, res),
"else" => { "else" => {
self.skip_whitespace(); self.skip_whitespace();
let ps = self.p; let ps = self.p;
self.skip_normal_word(); self.skip_normal_word();
if ps == self.p { if ps == self.p {
return just_one_thing(self, BlockEndingCmdTag::ELSE, res) return just_one_thing(self, BlockEndingTag::ELSE, res)
} else if &self.text[ps..self.p] != "if" { } else if &self.text[ps..self.p] != "if" {
return Err(FileParsingError::new(illegal_command_name, pb, self.p)) return Err(FileParsingError::new(illegal_command_name, pb, self.p))
} }
return finishing_touches(ReasonOfElementEnd{p1, cmd: BlockEndingCmdTag::ELSE_IF}, res); return finishing_touches(ReasonOfElementEnd{p1, cmd: BlockEndingTag::ELSE_IF}, res);
} }
"endif" => return just_one_thing(self, BlockEndingCmdTag::ENDIF, res), "endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res),
"endloop" => return just_one_thing(self, BlockEndingCmdTag::ENDLOOP, res), "endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res),
"for" => res.push(self.parse_let(arg_names)?), "for" => res.push(self.parse_let(arg_names)?),
"if" => res.push(self.parse_if(arg_names)?), "if" => res.push(self.parse_if(arg_names)?),
"let" => res.push(self.parse_let(arg_names)?), "let" => res.push(self.parse_let(arg_names)?),
@ -482,19 +500,19 @@ impl<'a> Parser<'a> {
let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names)?; let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names)?;
conditions.push(expr); conditions.push(expr);
match ending_tag.cmd { match ending_tag.cmd {
BlockEndingCmdTag::ELSE | BlockEndingCmdTag::NORMAL | BlockEndingCmdTag::ENDIF | BlockEndingTag::ELSE | BlockEndingTag::NORMAL | BlockEndingTag::ENDIF |
BlockEndingCmdTag::ELSE_IF => blocks.push(inner_block), BlockEndingTag::ELSE_IF => blocks.push(inner_block),
_ => return Err(FileParsingError::new( _ => return Err(FileParsingError::new(
incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if, ending_tag.p1, self.p)), incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if, ending_tag.p1, self.p)),
} }
if matches!(ending_tag.cmd, BlockEndingCmdTag::ELSE) { if matches!(ending_tag.cmd, BlockEndingTag::ELSE) {
let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names)?; let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names)?;
if !matches!(the_end.cmd, BlockEndingCmdTag::NORMAL | BlockEndingCmdTag::ENDIF){ 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)); return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal_or_endif, the_end.p1, self.p));
} }
blocks.push(else_block); blocks.push(else_block);
break break
} else if matches!(ending_tag.cmd, BlockEndingCmdTag::NORMAL | BlockEndingCmdTag::ENDIF) { } else if matches!(ending_tag.cmd, BlockEndingTag::NORMAL | BlockEndingTag::ENDIF) {
break break
} }
} }
@ -522,7 +540,7 @@ impl<'a> Parser<'a> {
let mut arg_names_extended = arg_names.clone(); let mut arg_names_extended = arg_names.clone();
arg_names_extended.push(new_variable_name); arg_names_extended.push(new_variable_name);
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended)?; let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended)?;
if !matches!(ending.cmd, BlockEndingCmdTag::NORMAL) { if !matches!(ending.cmd, BlockEndingTag::NORMAL) {
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p)); return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p));
} }
Ok(SubElement::Let(expr, inner_block)) Ok(SubElement::Let(expr, inner_block))
@ -569,9 +587,9 @@ impl<'a> Parser<'a> {
let expr = self.parse_expression_at_cmd_tag_end(arg_names)?; let expr = self.parse_expression_at_cmd_tag_end(arg_names)?;
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended)?; let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended)?;
let separator: String = String::from(match ending.cmd { let separator: String = String::from(match ending.cmd {
BlockEndingCmdTag::NOGAP => "", BlockEndingTag::NOGAP => "",
BlockEndingCmdTag::GAP => " ", BlockEndingTag::GAP => " ",
BlockEndingCmdTag::NORMAL | BlockEndingCmdTag::LF | BlockEndingCmdTag::ENDLOOP => "\n", BlockEndingTag::NORMAL | BlockEndingTag::LF | BlockEndingTag::ENDLOOP => "\n",
_ => return Err(FileParsingError::new( _ => return Err(FileParsingError::new(
incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop, ending.p1, self.p)), incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop, ending.p1, self.p)),
}); });

View File

@ -1,4 +1,5 @@
use yyyi_ru::mtgott::parser::*; use yyyi_ru::mtgott::parser::*;
use std::collections::HashMap;
fn generate_strings( fn generate_strings(
prefix: &mut String, prefix: &mut String,
@ -20,15 +21,29 @@ fn generate_strings(
#[test] #[test]
fn test_parse_file_with_all_combinations() { fn test_parse_file_with_all_combinations() {
let alphabet = [' ', '{', '%', '}', '$', 'a']; let alphabet = [' ', '{', '%', '}', '$', 'a'];
let target_length = 3; let target_length = 5 ;
generate_strings(&mut String::new(), target_length, &alphabet, &mut |s| { generate_strings(&mut String::new(), target_length, &alphabet, &mut |s| {
println!("Parsing {s}"); println!("Parsing {s}");
parse_one_file(&s); parse_one_file(&s);
parse_one_file((String::from("{% as s e1e %} adasd {%}") + s.as_str()).as_str());
parse_one_file((String::from("{% as s e1e %} a{[111 . 2332]]dasd {%} {% as s e1e %} adas {{}}d {%} ") + s.as_str()).as_str());
}); });
} }
#[test] macro_rules! string_map {
fn t1(){ ($($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("{@ x @}{@}"),
Ok(Plemege::Package(string_map! {
"x" => Plemege::Element(Element{argc: 0, sub_elements: vec![]})
})))
} }