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

Add where clause for rule fn, allow some rust syntax #371

Merged
merged 1 commit into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]));
}

Loading