Skip to content

Commit b300610

Browse files
authored
Merge pull request #395 from kevinmehall/custom-expr
Add `#{|input, pos| ... }` custom expr syntax to replace undocumented `##method()`
2 parents fc7d27f + 51e4a3a commit b300610

File tree

8 files changed

+92
-21
lines changed

8 files changed

+92
-21
lines changed

peg-macros/analysis.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,12 @@ impl<'a> LeftRecursionVisitor<'a> {
164164
nullable
165165
}
166166

167-
LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => false,
167+
| LiteralExpr(_)
168+
| PatternExpr(_)
169+
| MethodExpr(_, _)
170+
| CustomExpr(_)
171+
| FailExpr(_)
172+
| MarkerExpr(_) => false,
168173

169174
PositionExpr => true,
170175
}
@@ -220,7 +225,7 @@ impl<'a> LoopNullabilityVisitor<'a> {
220225
let name = rule_ident.to_string();
221226
*self.rule_nullability.get(&name).unwrap_or(&false)
222227
}
223-
228+
224229
ActionExpr(ref elems, ..) => {
225230
let mut nullable = true;
226231
for elem in elems {
@@ -250,7 +255,7 @@ impl<'a> LoopNullabilityVisitor<'a> {
250255
if inner_nullable && sep_nullable && !bound.has_upper_bound() {
251256
self.errors.push(LoopNullabilityError { span: this_expr.span });
252257
}
253-
258+
254259
inner_nullable | !bound.has_lower_bound()
255260
}
256261

@@ -269,10 +274,16 @@ impl<'a> LoopNullabilityVisitor<'a> {
269274
}
270275
}
271276

272-
nullable
277+
nullable
273278
}
274279

275-
LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => false,
280+
| LiteralExpr(_)
281+
| PatternExpr(_)
282+
| MethodExpr(_, _)
283+
| CustomExpr(_)
284+
| FailExpr(_)
285+
| MarkerExpr(_) => false,
286+
276287
PositionExpr => true,
277288
}
278289
}

peg-macros/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub enum Expr {
7676
PatternExpr(Group),
7777
RuleExpr(Ident, Vec<RuleArg>),
7878
MethodExpr(Ident, TokenStream),
79+
CustomExpr(Group),
7980
ChoiceExpr(Vec<SpannedExpr>),
8081
OptionalExpr(Box<SpannedExpr>),
8182
Repeat { inner: Box<SpannedExpr>, bound: BoundedRepeat, sep: Option<Box<SpannedExpr>> },

peg-macros/grammar.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3440,7 +3440,7 @@ pub mod peg {
34403440
);
34413441
match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { { let __seq_res = __parse_BRACKET_GROUP (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , p) => { :: peg :: RuleResult :: Matched (__pos , (|| { PatternExpr (p) . at (sp) }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , }
34423442
};
3443-
match __choice_res { :: peg :: RuleResult :: Matched (__pos , __value) => :: peg :: RuleResult :: Matched (__pos , __value) , :: peg :: RuleResult :: Failed => { let __choice_res = match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "(") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_sp (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { 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) => { :: peg :: RuleResult :: Matched (__pos , (|| { MarkerExpr (true) . at (sp) }) ()) } :: 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 => :: 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 => { let __choice_res = { let __seq_res = __parse_sp (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "@") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , (|| { MarkerExpr (false) . at (sp) }) ()) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"@\"") ; :: peg :: RuleResult :: Failed } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } ; match __choice_res { :: peg :: RuleResult :: Matched (__pos , __value) => :: peg :: RuleResult :: Matched (__pos , __value) , :: peg :: RuleResult :: Failed => { let __choice_res = { let __seq_res = __parse_sp (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "##") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_IDENT (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , method) => { { let __seq_res = __parse_PAREN_GROUP (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , args) => { :: peg :: RuleResult :: Matched (__pos , (|| { MethodExpr (method , args . stream ()) . at (sp) }) ()) } :: 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 __choice_res { :: peg :: RuleResult :: Matched (__pos , __value) => :: peg :: RuleResult :: Matched (__pos , __value) , :: peg :: RuleResult :: Failed => 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 , expression) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ")") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , (|| { expression }) ()) } :: 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 } } } } } } } } }
3443+
match __choice_res { :: peg :: RuleResult :: Matched (__pos , __value) => :: peg :: RuleResult :: Matched (__pos , __value) , :: peg :: RuleResult :: Failed => { let __choice_res = match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "(") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_sp (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { 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) => { :: peg :: RuleResult :: Matched (__pos , (|| { MarkerExpr (true) . at (sp) }) ()) } :: 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 => :: 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 => { let __choice_res = { let __seq_res = __parse_sp (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "@") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , (|| { MarkerExpr (false) . at (sp) }) ()) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"@\"") ; :: peg :: RuleResult :: Failed } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } ; match __choice_res { :: peg :: RuleResult :: Matched (__pos , __value) => :: peg :: RuleResult :: Matched (__pos , __value) , :: peg :: RuleResult :: Failed => { let __choice_res = { let __seq_res = __parse_sp (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "##") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_IDENT (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , method) => { { let __seq_res = __parse_PAREN_GROUP (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , args) => { :: peg :: RuleResult :: Matched (__pos , (|| { MethodExpr (method , args . stream ()) . at (sp) }) ()) } :: 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 __choice_res { :: peg :: RuleResult :: Matched (__pos , __value) => :: peg :: RuleResult :: Matched (__pos , __value) , :: peg :: RuleResult :: Failed => { let __choice_res = { let __seq_res = __parse_sp (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , sp) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "#") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_BRACE_GROUP (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , code) => { :: peg :: RuleResult :: Matched (__pos , (|| { CustomExpr (code) . at (sp) }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"#\"") ; :: peg :: RuleResult :: Failed } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } ; match __choice_res { :: peg :: RuleResult :: Matched (__pos , __value) => :: peg :: RuleResult :: Matched (__pos , __value) , :: peg :: RuleResult :: Failed => 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 , expression) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ")") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , (|| { expression }) ()) } :: 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 } } } } } } } } } } }
34443444
}
34453445
}
34463446
}
@@ -3503,7 +3503,11 @@ pub mod peg {
35033503
let mut __repeat_value = vec![];
35043504
loop {
35053505
let __pos = __repeat_pos;
3506-
let __step_res = __input.eat_until(__pos, ',');
3506+
let __step_res = ::peg::call_custom_closure(
3507+
(|input, pos| input.eat_until(pos, ',')),
3508+
__input,
3509+
__pos,
3510+
);
35073511
match __step_res {
35083512
::peg::RuleResult::Matched(__newpos, __value) => {
35093513
__repeat_pos = __newpos;
@@ -3639,7 +3643,7 @@ pub mod peg {
36393643
__pos: usize,
36403644
) -> ::peg::RuleResult<Span> {
36413645
#![allow(non_snake_case, unused, clippy::redundant_closure_call)]
3642-
__input.next_span(__pos)
3646+
::peg::call_custom_closure((|input, pos| input.next_span(pos)), __input, __pos)
36433647
}
36443648
fn __parse_KEYWORD<'input>(
36453649
__input: &'input Input,
@@ -3755,7 +3759,8 @@ pub mod peg {
37553759
};
37563760
match __seq_res {
37573761
::peg::RuleResult::Matched(__pos, _) => {
3758-
let __seq_res = __input.ident(__pos);
3762+
let __seq_res =
3763+
::peg::call_custom_closure((|input, pos| input.ident(pos)), __input, __pos);
37593764
match __seq_res {
37603765
::peg::RuleResult::Matched(__pos, i) => {
37613766
::peg::RuleResult::Matched(__pos, (|| i)())
@@ -3774,7 +3779,7 @@ pub mod peg {
37743779
__pos: usize,
37753780
) -> ::peg::RuleResult<Literal> {
37763781
#![allow(non_snake_case, unused, clippy::redundant_closure_call)]
3777-
__input.literal(__pos)
3782+
::peg::call_custom_closure((|input, pos| input.literal(pos)), __input, __pos)
37783783
}
37793784
fn __parse_PAREN_GROUP<'input>(
37803785
__input: &'input Input,
@@ -3783,7 +3788,11 @@ pub mod peg {
37833788
__pos: usize,
37843789
) -> ::peg::RuleResult<Group> {
37853790
#![allow(non_snake_case, unused, clippy::redundant_closure_call)]
3786-
__input.group(__pos, Delimiter::Parenthesis)
3791+
::peg::call_custom_closure(
3792+
(|input, pos| input.group(pos, Delimiter::Parenthesis)),
3793+
__input,
3794+
__pos,
3795+
)
37873796
}
37883797
fn __parse_BRACE_GROUP<'input>(
37893798
__input: &'input Input,
@@ -3792,7 +3801,11 @@ pub mod peg {
37923801
__pos: usize,
37933802
) -> ::peg::RuleResult<Group> {
37943803
#![allow(non_snake_case, unused, clippy::redundant_closure_call)]
3795-
__input.group(__pos, Delimiter::Brace)
3804+
::peg::call_custom_closure(
3805+
(|input, pos| input.group(pos, Delimiter::Brace)),
3806+
__input,
3807+
__pos,
3808+
)
37963809
}
37973810
fn __parse_BRACKET_GROUP<'input>(
37983811
__input: &'input Input,
@@ -3801,7 +3814,11 @@ pub mod peg {
38013814
__pos: usize,
38023815
) -> ::peg::RuleResult<Group> {
38033816
#![allow(non_snake_case, unused, clippy::redundant_closure_call)]
3804-
__input.group(__pos, Delimiter::Bracket)
3817+
::peg::call_custom_closure(
3818+
(|input, pos| input.group(pos, Delimiter::Bracket)),
3819+
__input,
3820+
__pos,
3821+
)
38053822
}
38063823
fn __parse_LIFETIME<'input>(
38073824
__input: &'input Input,

peg-macros/grammar.rustpeg

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,12 @@ rule primary() -> SpannedExpr
139139
/ "(" sp:sp() "@" ")" { MarkerExpr(true).at(sp) }
140140
/ sp:sp() "@" { MarkerExpr(false).at(sp) }
141141
/ sp:sp() "##" method:IDENT() args:PAREN_GROUP() { MethodExpr(method, args.stream()).at(sp) }
142+
/ sp:sp() "#" code:BRACE_GROUP() { CustomExpr(code).at(sp) }
142143
/ "(" expression:expression() ")" { expression }
143144

144145
rule rule_arg() -> RuleArg
145146
= "<" e:expression() ">" { RuleArg::Peg(e) }
146-
/ tt:$( ##eat_until(',')+ ) { RuleArg::Rust(tt) }
147+
/ tt:$( #{|input, pos| input.eat_until(pos, ',')}+ ) { RuleArg::Rust(tt) }
147148

148149
rule precedence_level() -> PrecedenceLevel
149150
= operators:precedence_op()+
@@ -153,13 +154,13 @@ rule precedence_op() -> PrecedenceOperator
153154
= span:sp() elements:labeled()* action:BRACE_GROUP()
154155
{ PrecedenceOperator{ span, elements, action } }
155156

156-
rule sp() -> Span = ##next_span()
157+
rule sp() -> Span = #{|input, pos| input.next_span(pos)}
157158
rule KEYWORD() = "pub" / "rule" / "use" / "type" / "where"
158-
rule IDENT() -> Ident = !KEYWORD() i:##ident() {i}
159-
rule LITERAL() -> Literal = ##literal()
160-
rule PAREN_GROUP() -> Group = ##group(Delimiter::Parenthesis)
161-
rule BRACE_GROUP() -> Group = ##group(Delimiter::Brace)
162-
rule BRACKET_GROUP() -> Group = ##group(Delimiter::Bracket)
159+
rule IDENT() -> Ident = !KEYWORD() i:#{|input, pos| input.ident(pos)} {i}
160+
rule LITERAL() -> Literal = #{|input, pos| input.literal(pos)}
161+
rule PAREN_GROUP() -> Group = #{|input, pos| input.group(pos, Delimiter::Parenthesis)}
162+
rule BRACE_GROUP() -> Group = #{|input, pos| input.group(pos, Delimiter::Brace)}
163+
rule BRACKET_GROUP() -> Group = #{|input, pos| input.group(pos, Delimiter::Bracket)}
163164
rule LIFETIME() = "'" IDENT()
164165
rule INTEGER() = LITERAL()
165166

peg-macros/translate.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,11 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
626626
quote_spanned! { span=> __input.#method(__pos, #args) }
627627
}
628628

629+
CustomExpr(ref code) => {
630+
let code = code.stream();
631+
quote_spanned! { span=> ::peg::call_custom_closure((#code), __input, __pos) }
632+
}
633+
629634
ChoiceExpr(ref exprs) => ordered_choice(
630635
span,
631636
exprs

peg-runtime/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ pub mod str;
1010
/// The result type used internally in the parser.
1111
///
1212
/// You'll only need this if implementing the `Parse*` traits for a custom input
13-
/// type. The public API of a parser adapts errors to `std::result::Result`.
13+
/// type, or using the `#{}` syntax to embed a custom Rust snippet within the parser.
14+
///
15+
/// The public API of a parser adapts errors to `std::result::Result` instead of using this type.
1416
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
1517
pub enum RuleResult<T> {
1618
/// Success, with final location
@@ -58,3 +60,10 @@ pub trait ParseSlice<'input>: Parse {
5860
extern crate alloc;
5961
#[cfg(not(feature = "std"))]
6062
extern crate core as std;
63+
64+
// needed for type inference on the `#{|input, pos| ..}` closure, since there
65+
// are different type inference rules on closures in function args.
66+
#[doc(hidden)]
67+
pub fn call_custom_closure<I, T>(f: impl FnOnce(I, usize) -> RuleResult<T>, input: I, pos: usize) -> RuleResult<T> {
68+
f(input, pos)
69+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@
125125
//! at the current location.
126126
//! * `precedence!{ ... }` - Parse infix, prefix, or postfix expressions by precedence climbing.
127127
//! [(details)](#precedence-climbing)
128+
//! * `#{|input, pos| ... }` - _Custom:_ The provided closure is passed the full input and current
129+
//! parse position, and returns a [`RuleResult`].
128130
//!
129131
//! ## Expression details
130132
//!

tests/run-pass/custom_expr.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use peg::RuleResult;
2+
3+
peg::parser!( grammar test() for str {
4+
rule position() -> usize = #{|input, pos| RuleResult::Matched(pos, pos)}
5+
pub rule test1() -> usize = ['a']* p1:position() ['b']* { p1 }
6+
7+
pub rule fail() -> usize = #{|input, pos| RuleResult::Failed}
8+
9+
rule custom_literal(literal: &str) = #{|input, pos| {
10+
let l = literal.len();
11+
if input.len() >= pos + l && &input.as_bytes()[pos..pos + l] == literal.as_bytes() {
12+
RuleResult::Matched(pos + l, ())
13+
} else {
14+
RuleResult::Failed
15+
}
16+
}}
17+
pub rule test2() = custom_literal("foo") "_" custom_literal("bar")
18+
});
19+
20+
fn main() {
21+
assert_eq!(test::test1("aaaabb"), Ok(4));
22+
assert_eq!(test::fail("aaaabb").unwrap_err().location.offset, 0);
23+
24+
assert_eq!(test::test2("foo_bar"), Ok(()));
25+
}

0 commit comments

Comments
 (0)