Daily update. One passing test :)
This commit is contained in:
parent
0294b3e623
commit
00b00cf26a
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum SubElement {
|
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)),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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![]})
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user