Skip to content

Commit

Permalink
Parse match Patterns as a sequence of Pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
egli committed May 23, 2024
1 parent 109e615 commit df4c05c
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 33 deletions.
10 changes: 5 additions & 5 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use search_path::SearchPath;

use self::{
braille::{braille_chars, chars_to_dots, BrailleChars},
match_rule::Patterns,
multipass::Test,
match_rule::Pattern,
};

pub use braille::dots_to_unicode;
Expand Down Expand Up @@ -675,9 +675,9 @@ pub enum Rule {
},

Match {
pre: Pattern,
pre: Patterns,
chars: String,
post: Pattern,
post: Patterns,
dots: Braille,
constraints: Constraints,
matches: Option<WithMatches>,
Expand Down Expand Up @@ -1186,7 +1186,7 @@ impl<'a> RuleParser<'a> {
.map(|s| s.to_string())
}

fn match_pre(&mut self) -> Result<Pattern, ParseError> {
fn match_pre(&mut self) -> Result<Patterns, ParseError> {
self.tokens
.next()
.ok_or(ParseError::MatchPreExpected)
Expand All @@ -1197,7 +1197,7 @@ impl<'a> RuleParser<'a> {
})?
}

fn match_post(&mut self) -> Result<Pattern, ParseError> {
fn match_post(&mut self) -> Result<Patterns, ParseError> {
self.tokens
.next()
.ok_or(ParseError::MatchPostExpected)
Expand Down
72 changes: 44 additions & 28 deletions src/parser/match_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub enum ParseError {
CharExpected { expected: char, found: Option<char> },
#[error("Invalid attribute {0:?}")]
InvalidAttribute(Option<char>),
#[error("Quantifier not allowed without pattern")]
MissingPatternBeforeQuantifier,
#[error("Pattern cannot be empty")]
EmptyPattern,
#[error("Group cannot be empty")]
Expand Down Expand Up @@ -41,7 +43,7 @@ pub enum Pattern {
Either(Box<Pattern>, Box<Pattern>),
}

type Patterns = Vec<Pattern>;
pub type Patterns = Vec<Pattern>;

pub struct PatternParser<'a> {
chars: Peekable<Chars<'a>>,
Expand Down Expand Up @@ -145,9 +147,9 @@ impl<'a> PatternParser<'a> {

fn group(&mut self) -> Result<Pattern, ParseError> {
self.consume('(')?;
let mut patterns: Vec<Pattern> = Vec::new();
let mut patterns: Patterns = Vec::new();
while self.chars.peek() != Some(&')') {
patterns.push(self.pattern()?);
patterns.push(self.pattern_with_quantifier()?);
}
self.consume(')')?;
if patterns.is_empty() {
Expand All @@ -159,7 +161,7 @@ impl<'a> PatternParser<'a> {

fn negate(&mut self) -> Result<Pattern, ParseError> {
self.consume('!')?;
let pattern = self.pattern()?;
let pattern = self.pattern_with_quantifier()?;
Ok(Pattern::Negate(Box::new(pattern)))
}

Expand All @@ -172,12 +174,16 @@ impl<'a> PatternParser<'a> {
Some('!') => self.negate(),
Some('[') => self.characters(),
Some('(') => self.group(),
// FIXME: handle escaped special chars
Some('?') | Some('*') | Some('+') | Some('|') => {
Err(ParseError::MissingPatternBeforeQuantifier)
}
Some(_) => self.characters(),
None => Err(ParseError::EmptyPattern),
}
}

pub fn pattern(&mut self) -> Result<Pattern, ParseError> {
fn pattern_with_quantifier(&mut self) -> Result<Pattern, ParseError> {
let inner = self.inner_pattern()?;
if self.chars.next_if(|&c| c == '?').is_some() {
return Ok(Pattern::Optional(Box::new(inner)));
Expand All @@ -186,11 +192,19 @@ impl<'a> PatternParser<'a> {
} else if self.chars.next_if(|&c| c == '+').is_some() {
return Ok(Pattern::OneOrMore(Box::new(inner)));
} else if self.chars.next_if(|&c| c == '|').is_some() {
let outer = self.pattern()?;
let outer = self.pattern_with_quantifier()?;
return Ok(Pattern::Either(Box::new(inner), Box::new(outer)));
}
Ok(inner)
}

pub fn pattern(&mut self) -> Result<Patterns, ParseError> {
let mut patterns: Patterns = Vec::new();
while self.chars.peek().is_some() {
patterns.push(self.pattern_with_quantifier()?);
}
Ok(patterns)
}
}

#[cfg(test)]
Expand Down Expand Up @@ -275,43 +289,45 @@ mod tests {
fn pattern_test() {
assert_eq!(
PatternParser::new("(abc)").pattern(),
Ok(Pattern::Group(Vec::from([
Ok(vec![Pattern::Group(Vec::from([
Pattern::Characters("a".into()),
Pattern::Characters("b".into()),
Pattern::Characters("c".into())
])))
]))])
);
assert_eq!(
PatternParser::new("(abc)?").pattern(),
Ok(Pattern::Optional(Box::new(Pattern::Group(Vec::from([
Pattern::Characters("a".into()),
Pattern::Characters("b".into()),
Pattern::Characters("c".into())
])))))
Ok(vec![Pattern::Optional(Box::new(Pattern::Group(
Vec::from([
Pattern::Characters("a".into()),
Pattern::Characters("b".into()),
Pattern::Characters("c".into())
])
)))])
);
assert_eq!(
PatternParser::new("(abc)+").pattern(),
Ok(Pattern::OneOrMore(Box::new(Pattern::Group(Vec::from([
Pattern::Characters("a".into()),
Pattern::Characters("b".into()),
Pattern::Characters("c".into())
])))))
Ok(vec![Pattern::OneOrMore(Box::new(Pattern::Group(
Vec::from([
Pattern::Characters("a".into()),
Pattern::Characters("b".into()),
Pattern::Characters("c".into())
])
)))])
);
assert_eq!(
PatternParser::new("(abc)*").pattern(),
Ok(Pattern::ZeroOrMore(Box::new(Pattern::Group(Vec::from([
Pattern::Characters("a".into()),
Pattern::Characters("b".into()),
Pattern::Characters("c".into())
])))))
Ok(vec![Pattern::ZeroOrMore(Box::new(Pattern::Group(
Vec::from([
Pattern::Characters("a".into()),
Pattern::Characters("b".into()),
Pattern::Characters("c".into())
])
)))])
);
assert_eq!(
// FIXME: This test should fail as it contains a lonely *
// that doesn't belong to a pattern
PatternParser::new("a**").pattern(),
Ok(Pattern::ZeroOrMore(Box::new(Pattern::Characters(
"a".into()
))))
Err(ParseError::MissingPatternBeforeQuantifier)
);
}
}

0 comments on commit df4c05c

Please sign in to comment.