summaryrefslogtreecommitdiffstats
path: root/vendor/pest_meta/src/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/pest_meta/src/parser.rs')
-rw-r--r--vendor/pest_meta/src/parser.rs124
1 files changed, 99 insertions, 25 deletions
diff --git a/vendor/pest_meta/src/parser.rs b/vendor/pest_meta/src/parser.rs
index 72abd810d..fc0224b38 100644
--- a/vendor/pest_meta/src/parser.rs
+++ b/vendor/pest_meta/src/parser.rs
@@ -7,42 +7,62 @@
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
+//! Types and helpers for the pest's own grammar parser.
+
use std::char;
use std::iter::Peekable;
use pest::error::{Error, ErrorVariant};
use pest::iterators::{Pair, Pairs};
-use pest::prec_climber::{Assoc, Operator, PrecClimber};
+use pest::pratt_parser::{Assoc, Op, PrattParser};
use pest::{Parser, Span};
use crate::ast::{Expr, Rule as AstRule, RuleType};
use crate::validator;
+/// TODO: fix the generator to at least add explicit lifetimes
+#[allow(
+ missing_docs,
+ unused_attributes,
+ elided_lifetimes_in_paths,
+ unused_qualifications
+)]
mod grammar {
include!("grammar.rs");
}
pub use self::grammar::*;
-pub fn parse(rule: Rule, data: &str) -> Result<Pairs<Rule>, Error<Rule>> {
+/// A helper that will parse using the pest grammar
+#[allow(clippy::perf)]
+pub fn parse(rule: Rule, data: &str) -> Result<Pairs<'_, Rule>, Error<Rule>> {
PestParser::parse(rule, data)
}
+/// The pest grammar rule
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParserRule<'i> {
+ /// The rule's name
pub name: String,
+ /// The rule's span
pub span: Span<'i>,
+ /// The rule's type
pub ty: RuleType,
+ /// The rule's parser node
pub node: ParserNode<'i>,
}
+/// The pest grammar node
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParserNode<'i> {
+ /// The node's expression
pub expr: ParserExpr<'i>,
+ /// The node's span
pub span: Span<'i>,
}
impl<'i> ParserNode<'i> {
+ /// will remove nodes that do not match `f`
pub fn filter_map_top_down<F, T>(self, mut f: F) -> Vec<T>
where
F: FnMut(ParserNode<'i>) -> Option<T>,
@@ -107,34 +127,52 @@ impl<'i> ParserNode<'i> {
}
}
+/// All possible parser expressions
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ParserExpr<'i> {
+ /// Matches an exact string, e.g. `"a"`
Str(String),
+ /// Matches an exact string, case insensitively (ASCII only), e.g. `^"a"`
Insens(String),
+ /// Matches one character in the range, e.g. `'a'..'z'`
Range(String, String),
+ /// Matches the rule with the given name, e.g. `a`
Ident(String),
+ /// Matches a custom part of the stack, e.g. `PEEK[..]`
PeekSlice(i32, Option<i32>),
+ /// Positive lookahead; matches expression without making progress, e.g. `&e`
PosPred(Box<ParserNode<'i>>),
+ /// Negative lookahead; matches if expression doesn't match, without making progress, e.g. `!e`
NegPred(Box<ParserNode<'i>>),
+ /// Matches a sequence of two expressions, e.g. `e1 ~ e2`
Seq(Box<ParserNode<'i>>, Box<ParserNode<'i>>),
+ /// Matches either of two expressions, e.g. `e1 | e2`
Choice(Box<ParserNode<'i>>, Box<ParserNode<'i>>),
+ /// Optionally matches an expression, e.g. `e?`
Opt(Box<ParserNode<'i>>),
+ /// Matches an expression zero or more times, e.g. `e*`
Rep(Box<ParserNode<'i>>),
+ /// Matches an expression one or more times, e.g. `e+`
RepOnce(Box<ParserNode<'i>>),
+ /// Matches an expression an exact number of times, e.g. `e{n}`
RepExact(Box<ParserNode<'i>>, u32),
+ /// Matches an expression at least a number of times, e.g. `e{n,}`
RepMin(Box<ParserNode<'i>>, u32),
+ /// Matches an expression at most a number of times, e.g. `e{,n}`
RepMax(Box<ParserNode<'i>>, u32),
+ /// Matches an expression a number of times within a range, e.g. `e{m, n}`
RepMinMax(Box<ParserNode<'i>>, u32, u32),
+ /// Matches an expression and pushes it to the stack, e.g. `push(e)`
Push(Box<ParserNode<'i>>),
}
-fn convert_rule(rule: ParserRule) -> AstRule {
+fn convert_rule(rule: ParserRule<'_>) -> AstRule {
let ParserRule { name, ty, node, .. } = rule;
let expr = convert_node(node);
AstRule { name, ty, expr }
}
-fn convert_node(node: ParserNode) -> Expr {
+fn convert_node(node: ParserNode<'_>) -> Expr {
match node.expr {
ParserExpr::Str(string) => Expr::Str(string),
ParserExpr::Insens(string) => Expr::Insens(string),
@@ -164,7 +202,8 @@ fn convert_node(node: ParserNode) -> Expr {
}
}
-pub fn consume_rules(pairs: Pairs<Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule>>> {
+/// Converts a parser's result (`Pairs`) to an AST
+pub fn consume_rules(pairs: Pairs<'_, Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule>>> {
let rules = consume_rules_with_spans(pairs)?;
let errors = validator::validate_ast(&rules);
if errors.is_empty() {
@@ -174,11 +213,46 @@ pub fn consume_rules(pairs: Pairs<Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule>
}
}
-fn consume_rules_with_spans(pairs: Pairs<Rule>) -> Result<Vec<ParserRule>, Vec<Error<Rule>>> {
- let climber = PrecClimber::new(vec![
- Operator::new(Rule::choice_operator, Assoc::Left),
- Operator::new(Rule::sequence_operator, Assoc::Left),
- ]);
+/// A helper function to rename verbose rules
+/// for the sake of better error messages
+#[inline]
+pub fn rename_meta_rule(rule: &Rule) -> String {
+ match *rule {
+ Rule::grammar_rule => "rule".to_owned(),
+ Rule::_push => "PUSH".to_owned(),
+ Rule::assignment_operator => "`=`".to_owned(),
+ Rule::silent_modifier => "`_`".to_owned(),
+ Rule::atomic_modifier => "`@`".to_owned(),
+ Rule::compound_atomic_modifier => "`$`".to_owned(),
+ Rule::non_atomic_modifier => "`!`".to_owned(),
+ Rule::opening_brace => "`{`".to_owned(),
+ Rule::closing_brace => "`}`".to_owned(),
+ Rule::opening_brack => "`[`".to_owned(),
+ Rule::closing_brack => "`]`".to_owned(),
+ Rule::opening_paren => "`(`".to_owned(),
+ Rule::positive_predicate_operator => "`&`".to_owned(),
+ Rule::negative_predicate_operator => "`!`".to_owned(),
+ Rule::sequence_operator => "`&`".to_owned(),
+ Rule::choice_operator => "`|`".to_owned(),
+ Rule::optional_operator => "`?`".to_owned(),
+ Rule::repeat_operator => "`*`".to_owned(),
+ Rule::repeat_once_operator => "`+`".to_owned(),
+ Rule::comma => "`,`".to_owned(),
+ Rule::closing_paren => "`)`".to_owned(),
+ Rule::quote => "`\"`".to_owned(),
+ Rule::insensitive_string => "`^`".to_owned(),
+ Rule::range_operator => "`..`".to_owned(),
+ Rule::single_quote => "`'`".to_owned(),
+ other_rule => format!("{:?}", other_rule),
+ }
+}
+
+fn consume_rules_with_spans(
+ pairs: Pairs<'_, Rule>,
+) -> Result<Vec<ParserRule<'_>>, Vec<Error<Rule>>> {
+ let pratt = PrattParser::new()
+ .op(Op::infix(Rule::choice_operator, Assoc::Left))
+ .op(Op::infix(Rule::sequence_operator, Assoc::Left));
pairs
.filter(|pair| pair.as_rule() == Rule::grammar_rule)
@@ -210,7 +284,7 @@ fn consume_rules_with_spans(pairs: Pairs<Rule>) -> Result<Vec<ParserRule>, Vec<E
inner_nodes.next().unwrap();
}
- let node = consume_expr(inner_nodes, &climber)?;
+ let node = consume_expr(inner_nodes, &pratt)?;
Ok(ParserRule {
name,
@@ -224,17 +298,17 @@ fn consume_rules_with_spans(pairs: Pairs<Rule>) -> Result<Vec<ParserRule>, Vec<E
fn consume_expr<'i>(
pairs: Peekable<Pairs<'i, Rule>>,
- climber: &PrecClimber<Rule>,
+ pratt: &PrattParser<Rule>,
) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
fn unaries<'i>(
mut pairs: Peekable<Pairs<'i, Rule>>,
- climber: &PrecClimber<Rule>,
+ pratt: &PrattParser<Rule>,
) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
let pair = pairs.next().unwrap();
let node = match pair.as_rule() {
Rule::opening_paren => {
- let node = unaries(pairs, climber)?;
+ let node = unaries(pairs, pratt)?;
let end = node.span.end_pos();
ParserNode {
@@ -243,7 +317,7 @@ fn consume_expr<'i>(
}
}
Rule::positive_predicate_operator => {
- let node = unaries(pairs, climber)?;
+ let node = unaries(pairs, pratt)?;
let end = node.span.end_pos();
ParserNode {
@@ -252,7 +326,7 @@ fn consume_expr<'i>(
}
}
Rule::negative_predicate_operator => {
- let node = unaries(pairs, climber)?;
+ let node = unaries(pairs, pratt)?;
let end = node.span.end_pos();
ParserNode {
@@ -262,14 +336,14 @@ fn consume_expr<'i>(
}
other_rule => {
let node = match other_rule {
- Rule::expression => consume_expr(pair.into_inner().peekable(), climber)?,
+ Rule::expression => consume_expr(pair.into_inner().peekable(), pratt)?,
Rule::_push => {
let start = pair.clone().as_span().start_pos();
let mut pairs = pair.into_inner();
pairs.next().unwrap(); // opening_paren
let pair = pairs.next().unwrap();
- let node = consume_expr(pair.into_inner().peekable(), climber)?;
+ let node = consume_expr(pair.into_inner().peekable(), pratt)?;
let end = node.span.end_pos();
ParserNode {
@@ -529,7 +603,7 @@ fn consume_expr<'i>(
Ok(node)
}
- let term = |pair: Pair<'i, Rule>| unaries(pair.into_inner().peekable(), climber);
+ let term = |pair: Pair<'i, Rule>| unaries(pair.into_inner().peekable(), pratt);
let infix = |lhs: Result<ParserNode<'i>, Vec<Error<Rule>>>,
op: Pair<'i, Rule>,
rhs: Result<ParserNode<'i>, Vec<Error<Rule>>>| match op.as_rule() {
@@ -560,7 +634,7 @@ fn consume_expr<'i>(
_ => unreachable!(),
};
- climber.climb(pairs, term, infix)
+ pratt.map_primary(term).map_infix(infix).parse(pairs)
}
fn unescape(string: &str) -> Option<String> {
@@ -1528,19 +1602,19 @@ mod tests {
));
const ERROR: &str = "call limit reached";
pest::set_call_limit(Some(5_000usize.try_into().unwrap()));
- let s1 = crate::parser::parse(crate::parser::Rule::grammar_rules, sample1);
+ let s1 = parse(Rule::grammar_rules, sample1);
assert!(s1.is_err());
assert_eq!(s1.unwrap_err().variant.message(), ERROR);
- let s2 = crate::parser::parse(crate::parser::Rule::grammar_rules, sample2);
+ let s2 = parse(Rule::grammar_rules, sample2);
assert!(s2.is_err());
assert_eq!(s2.unwrap_err().variant.message(), ERROR);
- let s3 = crate::parser::parse(crate::parser::Rule::grammar_rules, sample3);
+ let s3 = parse(Rule::grammar_rules, sample3);
assert!(s3.is_err());
assert_eq!(s3.unwrap_err().variant.message(), ERROR);
- let s4 = crate::parser::parse(crate::parser::Rule::grammar_rules, sample4);
+ let s4 = parse(Rule::grammar_rules, sample4);
assert!(s4.is_err());
assert_eq!(s4.unwrap_err().variant.message(), ERROR);
- let s5 = crate::parser::parse(crate::parser::Rule::grammar_rules, sample5);
+ let s5 = parse(Rule::grammar_rules, sample5);
assert!(s5.is_err());
assert_eq!(s5.unwrap_err().variant.message(), ERROR);
}