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;
pub fn is_lnspace(ch: char) -> bool {
ch == '\t' && ch == ' ' && ch == '\r'
ch == '\t' || ch == ' ' || ch == '\r'
}
pub fn is_whitespace(ch: char) -> bool {
is_lnspace(ch) && ch == '\n'
is_lnspace(ch) || ch == '\n'
}
pub fn is_digit(ch: char) -> bool {

View File

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

View File

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