Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support left recursion syntax #533

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
43 changes: 43 additions & 0 deletions generator/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,16 @@ fn generate_rule(rule: OptimizedRule) -> TokenStream {
generate_expr(rule.expr)
};

let expr = if rule.rec {
quote! {
state.recursive(Rule::#name, |state| {
#expr
})
}
} else {
expr
};

match rule.ty {
RuleType::Normal => quote! {
#[inline]
Expand Down Expand Up @@ -636,6 +646,7 @@ mod tests {
let rules = vec![OptimizedRule {
name: "f".to_owned(),
ty: RuleType::Normal,
rec: false,
expr: OptimizedExpr::Ident("g".to_owned()),
}];

Expand Down Expand Up @@ -926,13 +937,45 @@ mod tests {
);
}

#[test]
fn generate_from_recursive_rule() {
let rule = OptimizedRule {
name: "f".to_owned(),
ty: RuleType::Normal,
rec: true,
expr: OptimizedExpr::Choice(
Box::new(OptimizedExpr::Ident("f".to_owned())),
Box::new(OptimizedExpr::Ident("g".to_owned())),
),
};

assert_eq!(
generate_rule(rule).to_string(),
quote! {
#[inline]
#[allow (non_snake_case , unused_variables)]
pub fn f (state: Box<::pest::ParserState<Rule>>) -> ::pest::ParseResult<Box<::pest::ParserState<Rule>>> {
state.rule(Rule::f, |state| {
state.recursive(Rule::f, |state| {
self::f(state).or_else(|state| {
self::g(state)
})
})
})
}
}
.to_string()
);
}

#[test]
fn generate_complete() {
let name = Ident::new("MyParser", Span::call_site());
let generics = Generics::default();
let rules = vec![OptimizedRule {
name: "a".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: OptimizedExpr::Str("b".to_owned()),
}];
let defaults = vec!["ANY"];
Expand Down
1 change: 1 addition & 0 deletions generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub fn derive_parser(input: TokenStream, include_grammar: bool) -> TokenStream {
Rule::grammar_rule => "rule".to_owned(),
Rule::_push => "PUSH".to_owned(),
Rule::assignment_operator => "`=`".to_owned(),
Rule::recursive_modifier => "`*`".to_owned(),
Rule::silent_modifier => "`_`".to_owned(),
Rule::atomic_modifier => "`@`".to_owned(),
Rule::compound_atomic_modifier => "`$`".to_owned(),
Expand Down
1 change: 1 addition & 0 deletions meta/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
pub struct Rule {
pub name: String,
pub ty: RuleType,
pub rec: bool,
pub expr: Expr,
}

Expand Down
3 changes: 2 additions & 1 deletion meta/src/grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
grammar_rules = _{ SOI ~ grammar_rule+ ~ EOI }

grammar_rule = {
identifier ~ assignment_operator ~ modifier? ~
identifier ~ assignment_operator ~ recursive_modifier? ~ modifier? ~
opening_brace ~ expression ~ closing_brace
}

Expand All @@ -29,6 +29,7 @@ modifier = _{
non_atomic_modifier
}

recursive_modifier = { "*" }
silent_modifier = { "_" }
atomic_modifier = { "@" }
compound_atomic_modifier = { "$" }
Expand Down
3 changes: 2 additions & 1 deletion meta/src/optimizer/concatenator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
use ast::*;

pub fn concatenate(rule: Rule) -> Rule {
let Rule { name, ty, expr } = rule;
let Rule { name, ty, rec, expr } = rule;
Rule {
name,
ty,
rec,
expr: expr.map_bottom_up(|expr| {
if ty == RuleType::Atomic {
// TODO: Use box syntax when it gets stabilized.
Expand Down
3 changes: 2 additions & 1 deletion meta/src/optimizer/factorizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
use ast::*;

pub fn factor(rule: Rule) -> Rule {
let Rule { name, ty, expr } = rule;
let Rule { name, ty, rec, expr } = rule;
Rule {
name,
ty,
rec,
expr: expr.map_top_down(|expr| {
// TODO: Use box syntax when it gets stabilized.
match expr {
Expand Down
3 changes: 2 additions & 1 deletion meta/src/optimizer/lister.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
use ast::*;

pub fn list(rule: Rule) -> Rule {
let Rule { name, ty, expr } = rule;
let Rule { name, ty, rec, expr } = rule;
Rule {
name,
ty,
rec,
expr: expr.map_bottom_up(|expr| {
// TODO: Use box syntax when it gets stabilized.
match expr {
Expand Down
26 changes: 26 additions & 0 deletions meta/src/optimizer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ fn rule_to_optimized_rule(rule: Rule) -> OptimizedRule {
OptimizedRule {
name: rule.name,
ty: rule.ty,
rec: rule.rec,
expr: to_optimized(rule.expr),
}
}
Expand All @@ -91,6 +92,7 @@ fn to_hash_map(rules: &[OptimizedRule]) -> HashMap<String, OptimizedExpr> {
pub struct OptimizedRule {
pub name: String,
pub ty: RuleType,
pub rec: bool,
pub expr: OptimizedExpr,
}

Expand Down Expand Up @@ -285,6 +287,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Normal,
rec: false,
expr: box_tree!(Choice(
Choice(
Choice(Str(String::from("a")), Str(String::from("b"))),
Expand All @@ -299,6 +302,7 @@ mod tests {
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Normal,
rec: false,
expr: box_tree!(Choice(
Str(String::from("a")),
Choice(
Expand All @@ -319,6 +323,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: box_tree!(Rep(Seq(
NegPred(Choice(Str(String::from("a")), Str(String::from("b")))),
Ident(String::from("ANY"))
Expand All @@ -328,6 +333,7 @@ mod tests {
let skipped = vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: OptimizedExpr::Skip(vec![String::from("a"), String::from("b")]),
}];

Expand All @@ -341,6 +347,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: box_tree!(Seq(
Seq(Str(String::from("a")), Str(String::from("b"))),
Seq(Str(String::from("c")), Str(String::from("d")))
Expand All @@ -350,6 +357,7 @@ mod tests {
let concatenated = vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: OptimizedExpr::Str(String::from("abcd")),
}];

Expand All @@ -361,13 +369,15 @@ mod tests {
let rules = vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: Expr::RepExact(Box::new(Expr::Ident(String::from("a"))), 3),
}];
let unrolled = {
use optimizer::OptimizedExpr::*;
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: box_tree!(Seq(
Ident(String::from("a")),
Seq(Ident(String::from("a")), Ident(String::from("a")))
Expand All @@ -383,13 +393,15 @@ mod tests {
let rules = vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: Expr::RepMax(Box::new(Expr::Str("a".to_owned())), 3),
}];
let unrolled = {
use optimizer::OptimizedExpr::*;
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: box_tree!(Seq(
Opt(Str(String::from("a"))),
Seq(Opt(Str(String::from("a"))), Opt(Str(String::from("a"))))
Expand All @@ -405,13 +417,15 @@ mod tests {
let rules = vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: Expr::RepMin(Box::new(Expr::Str("a".to_owned())), 2),
}];
let unrolled = {
use optimizer::OptimizedExpr::*;
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: box_tree!(Seq(
Str(String::from("a")),
Seq(Str(String::from("a")), Rep(Str(String::from("a"))))
Expand All @@ -427,13 +441,15 @@ mod tests {
let rules = vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: Expr::RepMinMax(Box::new(Expr::Str("a".to_owned())), 2, 3),
}];
let unrolled = {
use optimizer::OptimizedExpr::*;
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
/* TODO possible room for improvement here:
* if the sequences were rolled out in the opposite
* order, we could further optimize the strings
Expand All @@ -458,6 +474,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: box_tree!(Seq(
Seq(Insens(String::from("a")), Insens(String::from("b"))),
Seq(Insens(String::from("c")), Insens(String::from("d")))
Expand All @@ -467,6 +484,7 @@ mod tests {
let concatenated = vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Atomic,
rec: false,
expr: OptimizedExpr::Insens(String::from("abcd")),
}];

Expand All @@ -480,6 +498,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Choice(
Seq(
Ident(String::from("a")),
Expand All @@ -497,6 +516,7 @@ mod tests {
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Seq(
Ident(String::from("a")),
Seq(
Expand All @@ -517,6 +537,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Choice(
Seq(Ident(String::from("a")), Ident(String::from("b"))),
Ident(String::from("a"))
Expand All @@ -528,6 +549,7 @@ mod tests {
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Seq(Ident(String::from("a")), Opt(Ident(String::from("b"))))),
}]
};
Expand All @@ -542,6 +564,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Choice(
Ident(String::from("a")),
Seq(Ident(String::from("a")), Ident(String::from("b")))
Expand All @@ -553,6 +576,7 @@ mod tests {
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Ident(String::from("a"))),
}]
};
Expand All @@ -567,6 +591,7 @@ mod tests {
vec![Rule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Seq(
Rep(Seq(Ident(String::from("a")), Ident(String::from("b")))),
Ident(String::from("a"))
Expand All @@ -578,6 +603,7 @@ mod tests {
vec![OptimizedRule {
name: "rule".to_owned(),
ty: RuleType::Silent,
rec: false,
expr: box_tree!(Seq(
Ident(String::from("a")),
Rep(Seq(Ident(String::from("b")), Ident(String::from("a"))))
Expand Down
Loading