Skip to content

Commit

Permalink
Merge branch 'allow-nonexhaustive-rules' of https://github.com/dario2…
Browse files Browse the repository at this point in the history
…3/rust-peg into dario23-allow-nonexhaustive-rules
  • Loading branch information
kevinmehall committed Apr 4, 2021
2 parents 7d03e42 + 996ac15 commit 12448fe
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 68 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Helpful `rustc` error messages for errors in the grammar definition or the Rust
code embedded within it
* Rule-level tracing to debug grammars
* Opt-in nonexhaustive parsing

## Example

Expand Down
1 change: 1 addition & 0 deletions peg-macros/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub struct Rule {
pub doc: Option<TokenStream>,
pub visibility: Option<TokenStream>,
pub cached: bool,
pub nonexhaustive: bool,
}

#[derive(Debug)]
Expand Down
135 changes: 72 additions & 63 deletions peg-macros/grammar.rs

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions peg-macros/grammar.rustpeg
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@ 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() visibility:rust_visibility()
= doc:rust_doc_comment() cached:cacheflag() visibility:rust_visibility() nonexhaustive:nonexhaustive_flag()
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, cached } }
{ Rule { span, doc, name:header.0, ty_params:header.1, params:header.2, expr, ret_type, visibility, nonexhaustive, cached } }

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

rule nonexhaustive_flag() -> bool = "nonexhaustive" { true } / { false }

rule rust_ty_params() -> Vec<TokenStream>
= "<" p:(($(IDENT() / LIFETIME())) ++ ",") ">" { p }

Expand Down
9 changes: 7 additions & 2 deletions peg-macros/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream {

let extra_args_def = &context.extra_args_def;
let extra_args_call = &context.extra_args_call;
let eof_check = if rule.nonexhaustive {
quote_spanned!{ span => true }
} else {
quote_spanned!{ span => ::peg::Parse::is_eof(__input, __pos) }
};

quote_spanned! { span =>
#doc
Expand All @@ -297,7 +302,7 @@ fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream {
let mut __state = ParseState::new();
match #parse_fn(__input, &mut __state, &mut __err_state, ::peg::Parse::start(__input) #extra_args_call #(, #rule_params_call)*) {
::peg::RuleResult::Matched(__pos, __value) => {
if ::peg::Parse::is_eof(__input, __pos) {
if #eof_check {
return Ok(__value)
} else {
__err_state.mark_failure(__pos, "EOF");
Expand All @@ -311,7 +316,7 @@ fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream {

match #parse_fn(__input, &mut __state, &mut __err_state, ::peg::Parse::start(__input) #extra_args_call #(, #rule_params_call)*) {
::peg::RuleResult::Matched(__pos, __value) => {
if ::peg::Parse::is_eof(__input, __pos) {
if #eof_check {
panic!("Parser is nondeterministic: succeeded when reparsing for error position");
} else {
__err_state.mark_failure(__pos, "EOF");
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@
//! rule beginning and ending with `@` is an infix expression. Prefix and postfix rules have one
//! `@` at the beginning or end, and atoms do not include `@`.
//!
//! ### Exhaustiveness
//!
//! Normally, parsers fail if not all of the input is consumed. To opt in to non exhaustive
//! parsing, add the `nonexhaustive` modifier before the `rule` keyword. Note that you should take
//! care to not miss a malformed `thing` at the last position if you have a `thing()*` repeat
//! parser.
//!
//! ## Input types
//!
//! The first line of the grammar declares an input type. This is normally
Expand Down
2 changes: 1 addition & 1 deletion tests/compile-fail/syntax_error.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: expected one of "#", "crate", "pub", "rule", "use", "}"
error: expected one of "#", "crate", "nonexhaustive", "pub", "rule", "use", "}"
--> $DIR/syntax_error.rs:4:5
|
4 | fn asdf() {} //~ ERROR expected one of "#", "crate", "pub", "rule", "use", "}"
Expand Down
12 changes: 12 additions & 0 deletions tests/run-pass/nonexhaustive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
extern crate peg;
use peg::parser;

parser!{
pub grammar nonexhaustive() for [u8] {
pub nonexhaustive rule foo() = "foo"
}
}

fn main() {
assert_eq!(nonexhaustive::foo(b"foobar"), Ok(()));
}

0 comments on commit 12448fe

Please sign in to comment.