diff --git a/peg-macros/analysis.rs b/peg-macros/analysis.rs index ffdac50..9157394 100644 --- a/peg-macros/analysis.rs +++ b/peg-macros/analysis.rs @@ -52,7 +52,10 @@ impl LeftRecursionError { } impl<'a> LeftRecursionVisitor<'a> { - fn check(grammar: &'a Grammar, rules: &HashMap) -> (HashMap, Vec) { + fn check( + grammar: &'a Grammar, + rules: &HashMap, + ) -> (HashMap, Vec) { let mut visitor = LeftRecursionVisitor { rules, errors: Vec::new(), @@ -64,7 +67,9 @@ impl<'a> LeftRecursionVisitor<'a> { for rule in grammar.iter_rules() { let nullable = visitor.walk_rule(rule); debug_assert!(visitor.stack.is_empty()); - rule_nullability.entry(rule.name.to_string()).or_insert(nullable); + rule_nullability + .entry(rule.name.to_string()) + .or_insert(nullable); } (rule_nullability, visitor.errors) @@ -98,20 +103,18 @@ impl<'a> LeftRecursionVisitor<'a> { 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(), - }), - _ => () - + 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 - false + false } } @@ -138,7 +141,11 @@ impl<'a> LeftRecursionVisitor<'a> { true } - Repeat { ref inner, ref bound, .. } => { + Repeat { + ref inner, + ref bound, + .. + } => { let inner_nullable = self.walk_expr(inner); inner_nullable | !bound.has_lower_bound() } @@ -161,10 +168,12 @@ impl<'a> LeftRecursionVisitor<'a> { } } - nullable + nullable } - LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => false, + LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => { + false + } PositionExpr => true, } @@ -188,9 +197,11 @@ impl LoopNullabilityError { } } - impl<'a> LoopNullabilityVisitor<'a> { - fn check(grammar: &'a Grammar, rule_nullability: &HashMap) -> Vec { + fn check( + grammar: &'a Grammar, + rule_nullability: &HashMap, + ) -> Vec { let mut visitor = LoopNullabilityVisitor { rule_nullability, errors: Vec::new(), @@ -203,7 +214,6 @@ impl<'a> LoopNullabilityVisitor<'a> { visitor.errors } - /// Walk an expr and its children analyzing the nullability of loop bodies. /// /// Returns true if the rule is known to match completely without consuming @@ -220,7 +230,7 @@ impl<'a> LoopNullabilityVisitor<'a> { let name = rule_ident.to_string(); *self.rule_nullability.get(&name).unwrap_or(&false) } - + ActionExpr(ref elems, ..) => { let mut nullable = true; for elem in elems { @@ -242,15 +252,21 @@ impl<'a> LoopNullabilityVisitor<'a> { true } - Repeat { ref inner, ref bound, ref sep } => { + Repeat { + ref inner, + ref bound, + ref sep, + } => { let inner_nullable = self.walk_expr(inner); let sep_nullable = sep.as_ref().map_or(true, |sep| self.walk_expr(sep)); // The entire purpose of this analysis: report errors if the loop body is nullable if inner_nullable && sep_nullable && !bound.has_upper_bound() { - self.errors.push(LoopNullabilityError { span: this_expr.span }); + self.errors.push(LoopNullabilityError { + span: this_expr.span, + }); } - + inner_nullable | !bound.has_lower_bound() } @@ -269,10 +285,12 @@ impl<'a> LoopNullabilityVisitor<'a> { } } - nullable + nullable } - LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => false, + LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => { + false + } PositionExpr => true, } } diff --git a/peg-macros/ast.rs b/peg-macros/ast.rs index 24ff565..18b9b5d 100644 --- a/peg-macros/ast.rs +++ b/peg-macros/ast.rs @@ -28,8 +28,8 @@ pub enum Item { #[derive(Debug)] pub enum Cache { - Simple, - Recursive + Simple(Option), + Recursive(Option), } #[derive(Debug)] @@ -77,7 +77,11 @@ pub enum Expr { MethodExpr(Ident, TokenStream), ChoiceExpr(Vec), OptionalExpr(Box), - Repeat { inner: Box, bound: BoundedRepeat, sep: Option> }, + Repeat { + inner: Box, + bound: BoundedRepeat, + sep: Option>, + }, PosAssertExpr(Box), NegAssertExpr(Box), ActionExpr(Vec, Option), @@ -93,7 +97,10 @@ pub enum Expr { impl Expr { pub fn at(self, sp: Span) -> SpannedExpr { - SpannedExpr { expr: self, span:sp } + SpannedExpr { + expr: self, + span: sp, + } } } @@ -127,14 +134,14 @@ impl BoundedRepeat { pub fn has_lower_bound(&self) -> bool { match self { BoundedRepeat::None | BoundedRepeat::Both(None, _) => false, - BoundedRepeat::Plus | BoundedRepeat::Exact(_) | BoundedRepeat::Both(Some(_), _) => true + BoundedRepeat::Plus | BoundedRepeat::Exact(_) | BoundedRepeat::Both(Some(_), _) => true, } } pub fn has_upper_bound(&self) -> bool { match self { - BoundedRepeat::None | BoundedRepeat::Plus | BoundedRepeat::Both(_, None) => false, - BoundedRepeat::Exact(_) | BoundedRepeat::Both(_, Some(_)) => true + BoundedRepeat::None | BoundedRepeat::Plus | BoundedRepeat::Both(_, None) => false, + BoundedRepeat::Exact(_) | BoundedRepeat::Both(_, Some(_)) => true, } } } diff --git a/peg-macros/grammar.rs b/peg-macros/grammar.rs index 3014270..b84e53f 100644 --- a/peg-macros/grammar.rs +++ b/peg-macros/grammar.rs @@ -13,7 +13,7 @@ pub mod peg { fn new() -> ParseState<'input> { ParseState { _phantom: ::core::marker::PhantomData, - primary_cache: ::std::collections::HashMap::new(), + primary_cache: ::core::default::Default::default(), } } } @@ -494,19 +494,71 @@ pub mod peg { match ::peg::ParseLiteral::parse_string_literal(__input, __pos, "cache") { ::peg::RuleResult::Matched(__pos, __val) => { - match ::peg::ParseLiteral::parse_string_literal( - __input, __pos, "]", - ) { - ::peg::RuleResult::Matched(__pos, __val) => { - ::peg::RuleResult::Matched( - __pos, - (|| Some(Cache::Simple))(), - ) - } - ::peg::RuleResult::Failed => { - __err_state.mark_failure(__pos, "\"]\""); - ::peg::RuleResult::Failed + 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) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ")") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , (|| { t }) ()) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\")\"") ; :: peg :: RuleResult :: Failed } } } :: 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, ty) => { + match ::peg::ParseLiteral::parse_string_literal( + __input, __pos, "]", + ) { + ::peg::RuleResult::Matched(__pos, __val) => { + ::peg::RuleResult::Matched( + __pos, + (|| Some(Cache::Simple(ty)))(), + ) + } + ::peg::RuleResult::Failed => { + __err_state.mark_failure(__pos, "\"]\""); + ::peg::RuleResult::Failed + } + } } + ::peg::RuleResult::Failed => ::peg::RuleResult::Failed, } } ::peg::RuleResult::Failed => { @@ -531,51 +583,60 @@ pub mod peg { ::peg::RuleResult::Matched(__pos, __value) } ::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) => { + 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) => { + 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) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ")") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , (|| { t }) ()) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\")\"") ; :: peg :: RuleResult :: Failed } } } :: 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, ty) => { + match ::peg::ParseLiteral::parse_string_literal( + __input, __pos, "]", + ) { ::peg::RuleResult::Matched( __pos, - (|| Some(Cache::Recursive))(), - ) - } - ::peg::RuleResult::Failed => { - __err_state.mark_failure(__pos, "\"]\""); - ::peg::RuleResult::Failed + __val, + ) => ::peg::RuleResult::Matched( + __pos, + (|| Some(Cache::Recursive(ty)))(), + ), + ::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 => { + ::peg::RuleResult::Failed + } } } - } - ::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 - } - }; + } + ::peg::RuleResult::Failed => { + __err_state.mark_failure(__pos, "\"#\""); + ::peg::RuleResult::Failed + } + }; match __choice_res { ::peg::RuleResult::Matched(__pos, __value) => { ::peg::RuleResult::Matched(__pos, __value) diff --git a/peg-macros/grammar.rustpeg b/peg-macros/grammar.rustpeg index 9e55c90..31ef1e5 100644 --- a/peg-macros/grammar.rustpeg +++ b/peg-macros/grammar.rustpeg @@ -26,7 +26,10 @@ rule peg_rule() -> Rule "=" expr:expression() ";"? { Rule { span, doc, name:header.0, ty_params:header.1, params:header.2, expr, ret_type, visibility, no_eof, cache } } - rule cacheflag() -> Option = "#" "[" "cache" "]" {Some(Cache::Simple)} / "#" "[" "cache_left_rec" "]" {Some(Cache::Recursive)} / {None} + rule cacheflag() -> Option = + "#" "[" "cache" ty:("(" t:$(rust_type()) ")" { t })? "]" { Some(Cache::Simple(ty)) } / + "#" "[" "cache_left_rec" ty:("(" t:$(rust_type()) ")" { t })? "]" { Some(Cache::Recursive(ty)) } / + { None } rule no_eof_flag() -> bool = "#" "[" "no_eof" "]" {true} / {false} diff --git a/peg-macros/translate.rs b/peg-macros/translate.rs index 3afbe18..18d3774 100644 --- a/peg-macros/translate.rs +++ b/peg-macros/translate.rs @@ -158,13 +158,24 @@ fn make_parse_state(grammar: &Grammar) -> TokenStream { let mut cache_fields_def: Vec = Vec::new(); let mut cache_fields: Vec = Vec::new(); for rule in grammar.iter_rules() { - if rule.cache.is_some() && rule.params.is_empty() && rule.ty_params.is_none() { - let name = format_ident!("{}_cache", rule.name); - let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(())); - cache_fields_def.push( - quote_spanned! { span => #name: ::std::collections::HashMap> }, - ); - cache_fields.push(name); + match &rule.cache { + Some(cache) if rule.params.is_empty() && rule.ty_params.is_none() => { + let name = format_ident!("{}_cache", rule.name); + let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(())); + + let cache_type = match cache { + Cache::Simple(None) | Cache::Recursive(None) => { + quote! { ::std::collections::HashMap } + } + Cache::Simple(Some(ty)) | Cache::Recursive(Some(ty)) => ty.clone(), + }; + + cache_fields_def.push( + quote_spanned! { span => #name: #cache_type> }, + ); + cache_fields.push(name); + } + _ => {} } } @@ -179,7 +190,7 @@ fn make_parse_state(grammar: &Grammar) -> TokenStream { fn new() -> ParseState<'input #(, #grammar_lifetime_params)*> { ParseState { _phantom: ::core::marker::PhantomData, - #(#cache_fields: ::std::collections::HashMap::new()),* + #(#cache_fields: ::core::default::Default::default()),* } } } @@ -274,7 +285,7 @@ fn compile_rule(context: &Context, rule: &Rule) -> TokenStream { }; match cache_type { - Cache::Simple => quote_spanned! { span => + Cache::Simple(_) => quote_spanned! { span => if let Some(entry) = __state.#cache_field.get(&__pos) { #cache_trace return entry.clone(); @@ -284,7 +295,7 @@ fn compile_rule(context: &Context, rule: &Rule) -> TokenStream { __state.#cache_field.insert(__pos, __rule_result.clone()); __rule_result }, - Cache::Recursive => + Cache::Recursive(_) => // `#[cache_left_rec] support for recursive rules using the technique described here: // { diff --git a/tests/run-pass/grammar_with_args_and_cache_and_type.rs b/tests/run-pass/grammar_with_args_and_cache_and_type.rs new file mode 100644 index 0000000..209d5ec --- /dev/null +++ b/tests/run-pass/grammar_with_args_and_cache_and_type.rs @@ -0,0 +1,16 @@ +#![no_std] + +use alloc::collections::BTreeMap; + +extern crate peg; +extern crate alloc; + +peg::parser! { + grammar lol(config: bool) for str { + #[cache_left_rec(BTreeMap)] + rule one() -> () + = one() / "foo" + } +} + +fn main() {} diff --git a/tests/run-pass/memoization_with_cache_type.rs b/tests/run-pass/memoization_with_cache_type.rs new file mode 100644 index 0000000..8f84321 --- /dev/null +++ b/tests/run-pass/memoization_with_cache_type.rs @@ -0,0 +1,21 @@ +#![no_std] + +use alloc::collections::BTreeMap; +use alloc::vec; + +extern crate peg; +extern crate alloc; + +peg::parser!{ grammar memo() for str { + #[cache(BTreeMap)] + rule r() -> &'input str + = s:$(['a'..='z']+) { s } + + pub rule parse() + = r() "+" r() { () } + / r() " " r() { () } +}} + +fn main() { + assert_eq!(memo::parse("abc zzz"), Ok(())); +}