Skip to content

Commit

Permalink
Merge pull request #266 from fgasperij/add-left-recursion-support
Browse files Browse the repository at this point in the history
Add left recursion support for rules without args
  • Loading branch information
kevinmehall authored Jul 28, 2021
2 parents 9024f77 + 447dd0b commit 4b146b4
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 63 deletions.
32 changes: 18 additions & 14 deletions peg-macros/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,25 @@ impl<'a> LeftRecursionVisitor<'a> {
RuleExpr(ref rule_ident, _) => {
let name = rule_ident.to_string();

if let Some(loop_start) = self
.stack
.iter()
.position(|caller_name| caller_name == &name)
{
let mut recursive_loop = self.stack[loop_start..].to_vec();
recursive_loop.push(name);
self.errors.push(LeftRecursionError {
path: recursive_loop,
span: rule_ident.span(),
});
return false;
}

if let Some(rule) = self.rules.get(&name) {
if let Some(loop_start) = self
.stack
.iter()
.position(|caller_name| caller_name == &name)
{
let mut recursive_loop = self.stack[loop_start..].to_vec();
recursive_loop.push(name.clone());
match rule.cache {
None | Some(Cache::Simple) =>
self.errors.push(LeftRecursionError {
path: recursive_loop,
span: rule_ident.span(),
}),
_ => ()

}
return false;
}
self.walk_rule(rule)
} else {
// Missing rule would have already been reported
Expand Down
8 changes: 7 additions & 1 deletion peg-macros/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ pub enum Item {
Rule(Rule),
}

#[derive(Debug)]
pub enum Cache {
Simple,
Recursive
}

#[derive(Debug)]
pub struct Rule {
pub span: Span,
Expand All @@ -36,7 +42,7 @@ pub struct Rule {
pub ret_type: Option<TokenStream>,
pub doc: Option<TokenStream>,
pub visibility: Option<TokenStream>,
pub cached: bool,
pub cache: Option<Cache>,
pub no_eof: bool,
}

Expand Down
65 changes: 60 additions & 5 deletions peg-macros/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ pub mod peg {
::peg::RuleResult::Matched(__pos, doc) => {
let __seq_res = __parse_cacheflag(__input, __state, __err_state, __pos);
match __seq_res {
::peg::RuleResult::Matched(__pos, cached) => {
::peg::RuleResult::Matched(__pos, cache) => {
let __seq_res =
__parse_no_eof_flag(__input, __state, __err_state, __pos);
match __seq_res {
Expand Down Expand Up @@ -445,7 +445,7 @@ pub mod peg {
header,
) => {
let __seq_res = match match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "->") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = { let str_start = __pos ; match match __parse_rust_type (__input , __state , __err_state , __pos) { :: peg :: RuleResult :: Matched (pos , _) => :: peg :: RuleResult :: Matched (pos , ()) , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , :: peg :: ParseSlice :: parse_slice (__input , str_start , __newpos)) } , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , t) => { :: peg :: RuleResult :: Matched (__pos , (|| { t }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"->\"") ; :: peg :: RuleResult :: Failed } } { :: peg :: RuleResult :: Matched (__newpos , __value) => { :: peg :: RuleResult :: Matched (__newpos , Some (__value)) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , None) } , } ;
match __seq_res { :: peg :: RuleResult :: Matched (__pos , ret_type) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "=") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_expression (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , expr) => { { let __seq_res = match match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ";") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , __val) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\";\"") ; :: peg :: RuleResult :: Failed } } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , ()) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , ()) } , } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , _) => { :: peg :: RuleResult :: Matched (__pos , (|| { Rule { span , doc , name : header . 0 , ty_params : header . 1 , params : header . 2 , expr , ret_type , visibility , no_eof , cached } }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"=\"") ; :: peg :: RuleResult :: Failed } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , }
match __seq_res { :: peg :: RuleResult :: Matched (__pos , ret_type) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "=") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_expression (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , expr) => { { let __seq_res = match match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ";") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , __val) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\";\"") ; :: peg :: RuleResult :: Failed } } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , ()) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , ()) } , } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , _) => { :: peg :: RuleResult :: Matched (__pos , (|| { Rule { span , doc , name : header . 0 , ty_params : header . 1 , params : header . 2 , expr , ret_type , visibility , no_eof , cache } }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"=\"") ; :: peg :: RuleResult :: Failed } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , }
}
::peg::RuleResult::Failed => {
::peg::RuleResult::Failed
Expand Down Expand Up @@ -482,7 +482,7 @@ pub mod peg {
__state: &mut ParseState<'input>,
__err_state: &mut ::peg::error::ErrorState,
__pos: usize,
) -> ::peg::RuleResult<bool> {
) -> ::peg::RuleResult<Option<Cache>> {
#![allow(non_snake_case, unused, clippy::redundant_closure_call)]
{
let __choice_res = match ::peg::ParseLiteral::parse_string_literal(__input, __pos, "#")
Expand All @@ -497,7 +497,10 @@ pub mod peg {
__input, __pos, "]",
) {
::peg::RuleResult::Matched(__pos, __val) => {
::peg::RuleResult::Matched(__pos, (|| true)())
::peg::RuleResult::Matched(
__pos,
(|| Some(Cache::Simple))(),
)
}
::peg::RuleResult::Failed => {
__err_state.mark_failure(__pos, "\"]\"");
Expand Down Expand Up @@ -526,7 +529,59 @@ pub mod peg {
::peg::RuleResult::Matched(__pos, __value) => {
::peg::RuleResult::Matched(__pos, __value)
}
::peg::RuleResult::Failed => ::peg::RuleResult::Matched(__pos, (|| false)()),
::peg::RuleResult::Failed => {
let __choice_res =
match ::peg::ParseLiteral::parse_string_literal(__input, __pos, "#") {
::peg::RuleResult::Matched(__pos, __val) => {
match ::peg::ParseLiteral::parse_string_literal(__input, __pos, "[")
{
::peg::RuleResult::Matched(__pos, __val) => {
match ::peg::ParseLiteral::parse_string_literal(
__input,
__pos,
"cache_left_rec",
) {
::peg::RuleResult::Matched(__pos, __val) => {
match ::peg::ParseLiteral::parse_string_literal(
__input, __pos, "]",
) {
::peg::RuleResult::Matched(__pos, __val) => {
::peg::RuleResult::Matched(
__pos,
(|| Some(Cache::Recursive))(),
)
}
::peg::RuleResult::Failed => {
__err_state.mark_failure(__pos, "\"]\"");
::peg::RuleResult::Failed
}
}
}
::peg::RuleResult::Failed => {
__err_state
.mark_failure(__pos, "\"cache_left_rec\"");
::peg::RuleResult::Failed
}
}
}
::peg::RuleResult::Failed => {
__err_state.mark_failure(__pos, "\"[\"");
::peg::RuleResult::Failed
}
}
}
::peg::RuleResult::Failed => {
__err_state.mark_failure(__pos, "\"#\"");
::peg::RuleResult::Failed
}
};
match __choice_res {
::peg::RuleResult::Matched(__pos, __value) => {
::peg::RuleResult::Matched(__pos, __value)
}
::peg::RuleResult::Failed => ::peg::RuleResult::Matched(__pos, (|| None)()),
}
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions peg-macros/grammar.rustpeg
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ rule grammar_args() -> Vec<(Ident, TokenStream)>
= "(" args:((i:IDENT() ":" t:$(rust_type()) { (i, t) })**",") ","? ")" { args }

rule peg_rule() -> Rule
= doc:rust_doc_comment() cached:cacheflag() no_eof:no_eof_flag() visibility:rust_visibility()
= doc:rust_doc_comment() cache:cacheflag() no_eof:no_eof_flag() visibility:rust_visibility()
span:sp() "rule"
header:(
&("_" / "__" / "___") name:IDENT() ("(" ")")? { (name, None, Vec::new()) }
/ name:IDENT() ty_params:rust_ty_params()? params:rule_params() { (name, ty_params, params) }
)
ret_type:("->" t:$(rust_type()) {t})?
"=" expr:expression() ";"?
{ Rule { span, doc, name:header.0, ty_params:header.1, params:header.2, expr, ret_type, visibility, no_eof, cached } }
{ Rule { span, doc, name:header.0, ty_params:header.1, params:header.2, expr, ret_type, visibility, no_eof, cache } }

rule cacheflag() -> bool = "#" "[" "cache" "]" {true} / {false}
rule cacheflag() -> Option<Cache> = "#" "[" "cache" "]" {Some(Cache::Simple)} / "#" "[" "cache_left_rec" "]" {Some(Cache::Recursive)} / {None}

rule no_eof_flag() -> bool = "#" "[" "no_eof" "]" {true} / {false}

Expand Down
Loading

0 comments on commit 4b146b4

Please sign in to comment.