Skip to content

Commit

Permalink
Merge pull request #371 from A4-Tacks/master
Browse files Browse the repository at this point in the history
Add where clause for rule fn, allow some rust syntax
  • Loading branch information
kevinmehall authored Apr 21, 2024
2 parents d51f5c4 + 211bb7c commit 08cb3b3
Show file tree
Hide file tree
Showing 6 changed files with 867 additions and 260 deletions.
1 change: 1 addition & 0 deletions peg-macros/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub struct Rule {
pub params: Vec<RuleParam>,
pub expr: SpannedExpr,
pub ret_type: Option<TokenStream>,
pub where_clause: Option<TokenStream>,
pub doc: Option<TokenStream>,
pub visibility: Option<TokenStream>,
pub cache: Option<Cache>,
Expand Down
1,029 changes: 782 additions & 247 deletions peg-macros/grammar.rs

Large diffs are not rendered by default.

29 changes: 18 additions & 11 deletions peg-macros/grammar.rustpeg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub rule peg_grammar() -> Grammar
{ Grammar { doc, visibility, name, lifetime_params, args, input_type, items } }

rule rust_lifetime_params() -> Vec<TokenStream>
= "<" p:(($(LIFETIME())) ++ ",") ">" { p }
= "<" p:(($(LIFETIME())) ++ ",") ","? ">" { p }

rule grammar_args() -> Vec<(Ident, TokenStream)>
= "(" args:((i:IDENT() ":" t:$(rust_type()) { (i, t) })**",") ","? ")" { args }
Expand All @@ -23,8 +23,9 @@ rule peg_rule() -> Rule
/ name:IDENT() ty_params:rust_ty_params()? params:rule_params() { (name, ty_params, params) }
)
ret_type:("->" t:$(rust_type()) {t})?
where_clause:$(rust_where_clause())?
"=" expr:expression() ";"?
{ Rule { span, doc, name:header.0, ty_params:header.1, params:header.2, expr, ret_type, visibility, no_eof, cache } }
{ Rule { span, doc, name:header.0, ty_params:header.1, params:header.2, expr, ret_type, where_clause, visibility, no_eof, cache } }

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

Expand All @@ -35,7 +36,7 @@ rule peg_rule() -> Rule
/ t:$(rust_type()) { RuleParamTy::Rust(t) }

rule rule_params() -> Vec<RuleParam>
= "(" params:(name:IDENT() ":" ty:rule_param_ty() { RuleParam { name, ty} }) ** "," ")" { params }
= "(" params:(x:(name:IDENT() ":" ty:rule_param_ty() { RuleParam { name, ty} }) ++ "," ","? {x})? ")" { params.unwrap_or_default() }

rule item() -> Item
= u:rust_use() { Item::Use(u) }
Expand All @@ -48,7 +49,7 @@ rule rust_visibility() -> Option<TokenStream> = $("pub" PAREN_GROUP()?)?
rule rust_use() -> TokenStream
= v:$("use" rust_path() (
"::" "*"
/ "::" "{" ((IDENT() ("as" IDENT())?) ++ ",") "}"
/ "::" "{" ((IDENT() ("as" IDENT())?) ++ "," ","?) "}"
/ ("as" IDENT())?
) ";") { v.to_owned() }

Expand All @@ -58,16 +59,22 @@ rule rust_path()
rule rust_type()
= BRACKET_GROUP()
/ "&" LIFETIME()? "mut"? rust_type()
/ "dyn" rust_type()
/ "impl" rust_type()
/ "(" rust_type() ** "," ")"
/ rust_ty_path()
/ "dyn" rust_type() ++ "+"
/ "impl" rust_type() ++ "+"
/ "(" (rust_type() ++ "," ","?)? ")"
/ ("<" rust_type() ("as" rust_ty_path())? ">")? rust_ty_path()

rule rust_ty_path()
= "::"? (IDENT() ("::"? "<" (LIFETIME() / rust_type() / BRACE_GROUP() / LITERAL()) ++ "," ">")?) ++ "::"
= "::"? (IDENT() ("::"? ("<" (LIFETIME() / rust_type() / BRACE_GROUP() / LITERAL()) ++ "," ","? ">" / PAREN_GROUP() ("->" rust_type())?))?) ++ "::"

rule rust_ty_params() -> Vec<TokenStream>
= "<" p:($(rust_generic_param()) ++ ",") ">" { p }
= "<" p:($(rust_generic_param()) ++ ",") ","? ">" { p }

rule rust_where_clause()
= "where" (
LIFETIME() (":" LIFETIME() ++ "+")?
/ ("for" rust_ty_params())? rust_type() ":" (LIFETIME() / "?"? rust_ty_path()) ++ "+"
) ** "," ","?

rule rust_generic_param()
= LIFETIME() (":" LIFETIME() ++ "+")?
Expand Down Expand Up @@ -147,7 +154,7 @@ rule precedence_op() -> PrecedenceOperator
{ PrecedenceOperator{ span, elements, action } }

rule sp() -> Span = ##next_span()
rule KEYWORD() = "pub" / "rule" / "use" / "type"
rule KEYWORD() = "pub" / "rule" / "use" / "type" / "where"
rule IDENT() -> Ident = !KEYWORD() i:##ident() {i}
rule LITERAL() -> Literal = ##literal()
rule PAREN_GROUP() -> Group = ##group(Delimiter::Parenthesis)
Expand Down
20 changes: 18 additions & 2 deletions peg-macros/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ fn compile_rule(context: &Context, rule: &Rule) -> TokenStream {
let name = format_ident!("__parse_{}", rule.name, span = span);
let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
let ty_params = ty_params_slice(&rule.ty_params);
let where_clause = rule.where_clause.as_ref().into_iter();

let Context {
input_ty,
Expand Down Expand Up @@ -319,7 +320,14 @@ fn compile_rule(context: &Context, rule: &Rule) -> TokenStream {
};

quote_spanned! { span =>
fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(__input: #input_ty, __state: #parse_state_ty, __err_state: &mut ::peg::error::ErrorState, __pos: usize #extra_args_def #(, #rule_params)*) -> ::peg::RuleResult<#ret_ty> {
fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(
__input: #input_ty,
__state: #parse_state_ty,
__err_state: &mut ::peg::error::ErrorState,
__pos: usize #extra_args_def #(, #rule_params)*,
) -> ::peg::RuleResult<#ret_ty>
#(#where_clause)*
{
#![allow(non_snake_case, unused, clippy::redundant_closure_call)]
#fn_body
}
Expand All @@ -340,6 +348,7 @@ fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream {
let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
let parse_fn = format_ident!("__parse_{}", rule.name.to_string(), span = name.span());
let ty_params = ty_params_slice(&rule.ty_params);
let where_clause = rule.where_clause.as_ref().into_iter();
let rule_params = rule_params_list(context, rule);
let rule_params_call: Vec<TokenStream> = rule
.params
Expand Down Expand Up @@ -369,7 +378,14 @@ fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream {

quote_spanned! { span =>
#doc
#visibility fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(__input: #input_ty #extra_args_def #(, #rule_params)*) -> ::core::result::Result<#ret_ty, ::peg::error::ParseError<PositionRepr<#(#grammar_lifetime_params),*>>> {
#visibility fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(
__input: #input_ty #extra_args_def #(, #rule_params)*
) -> ::core::result::Result<
#ret_ty,
::peg::error::ParseError<PositionRepr<#(#grammar_lifetime_params),*>>
>
#(#where_clause)*
{
#![allow(non_snake_case, unused)]

let mut __err_state = ::peg::error::ErrorState::new(::peg::Parse::start(__input));
Expand Down
21 changes: 21 additions & 0 deletions tests/run-pass/generic_fn_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
extern crate peg;

peg::parser!(
grammar parser() for str {
pub rule foo<F: Fn(&str) -> u32 + Copy>(f: F) -> u32
= s:$(['0'..='9']+) { f(s) }
pub rule bar(f: impl Fn(&str) -> u32 + Copy,) -> u32
= s:$(['0'..='9']+) { f(s) }
pub rule baz(f: fn(&str) -> u32) -> u32
= s:$(['0'..='9']+) { f(s) }
}
);

fn main() {
let n = parser::foo("123", |s| s.parse().unwrap()).unwrap();
assert_eq!(n, 123);
let n = parser::bar("123", |s| s.parse().unwrap()).unwrap();
assert_eq!(n, 123);
let n = parser::baz("123", |s| s.parse().unwrap()).unwrap();
assert_eq!(n, 123);
}
27 changes: 27 additions & 0 deletions tests/run-pass/rule_where_clause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
extern crate peg;
use std::{
str::FromStr,
fmt::Debug,
};

peg::parser!(
grammar parser() for str {
use std::cell::Cell;
pub rule nums<C, T>() -> C
where C: Default + Extend<T>,
T: FromStr,
<T as FromStr>::Err: Debug,
= c:({ Cell::new(C::default()) })
(ch:$(['0'..='9']) {
let mut mutc = c.take();
mutc.extend(Some(ch.parse::<T>().unwrap()));
c.set(mutc);
})+
{ c.take() }
}
);

fn main() {
assert_eq!(parser::nums::<Vec<u8>, u8>("3729"), Ok(vec![3, 7, 2, 9]));
}

0 comments on commit 08cb3b3

Please sign in to comment.