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

Implement basic cut operator #375

Closed
wants to merge 1 commit into from
Closed
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
4 changes: 2 additions & 2 deletions peg-macros/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl<'a> LeftRecursionVisitor<'a> {

LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => false,

PositionExpr => true,
PositionExpr | CutExpr => true,
}
}
}
Expand Down Expand Up @@ -273,7 +273,7 @@ impl<'a> LoopNullabilityVisitor<'a> {
}

LiteralExpr(_) | PatternExpr(_) | MethodExpr(_, _) | FailExpr(_) | MarkerExpr(_) => false,
PositionExpr => true,
PositionExpr | CutExpr => true,
}
}
}
1 change: 1 addition & 0 deletions peg-macros/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub enum Expr {
levels: Vec<PrecedenceLevel>,
},
MarkerExpr(bool),
CutExpr,
}

impl Expr {
Expand Down
303 changes: 215 additions & 88 deletions peg-macros/grammar.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion peg-macros/grammar.rustpeg
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ rule labeled() -> TaggedExpr
{ TaggedExpr{ name: label, expr: expression } }

rule suffixed() -> SpannedExpr
= e:prefixed() sp:sp() "?" { OptionalExpr(Box::new(e)).at(sp) }
= sp:sp() "~>" { CutExpr.at(sp) }
/ e:prefixed() sp:sp() "?" { OptionalExpr(Box::new(e)).at(sp) }
/ e:prefixed() sp:sp() "**" count:repeatcount() sep:primary() { Repeat { inner: Box::new(e), bound: count, sep: Some(Box::new(sep)) }.at(sp) }
/ e:prefixed() sp:sp() "++" sep:primary() { Repeat { inner: Box::new(e), bound: BoundedRepeat::Plus, sep: Some(Box::new(sep)) }.at(sp )}
/ e:prefixed() sp:sp() "*" count:repeatcount() { Repeat { inner: Box::new(e), bound: count, sep: None }.at(sp) }
Expand Down
7 changes: 7 additions & 0 deletions peg-macros/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ fn ordered_choice(span: Span, mut rs: impl DoubleEndedIterator<Item = TokenStrea
let __choice_res = #preferred;
match __choice_res {
::peg::RuleResult::Matched(__pos, __value) => ::peg::RuleResult::Matched(__pos, __value),
::peg::RuleResult::Failed if __err_state.is_cut => ::peg::RuleResult::Failed,
::peg::RuleResult::Failed => #fallback
}
}}
Expand Down Expand Up @@ -1017,5 +1018,11 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
MarkerExpr { .. } => {
report_error(span, "`@` is only allowed in `precedence!{}`".to_string())
}
CutExpr => {
quote_spanned!{ span => {
__err_state.is_cut = true;
::peg::RuleResult::Matched(__pos, __pos)
}}
}
}
}
4 changes: 4 additions & 0 deletions peg-runtime/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ pub struct ErrorState {
/// Non-zero => yes, to support nested blocks.
pub suppress_fail: usize,

/// Are we cut from other choices?
pub is_cut: bool,

/// Are we reparsing after a failure? If so, compute and store expected set of all alternative expectations
/// when we are at offset `max_err_pos`.
pub reparsing_on_error: bool,
Expand All @@ -92,6 +95,7 @@ impl ErrorState {
ErrorState {
max_err_pos: initial_pos,
suppress_fail: 0,
is_cut: false,
reparsing_on_error: false,
expected: ExpectedSet {
expected: BTreeSet::new(),
Expand Down
16 changes: 16 additions & 0 deletions tests/run-pass/cut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
extern crate peg;

peg::parser!( grammar test_grammar() for str {
pub rule cut() -> Option<&'input str>
= "a" ending:$("a") {Some(ending)}
/ "a" ~> ending:$("b") {Some(ending)}
/ "a" ending:$("c") {Some(ending)}
});

use self::test_grammar::*;

fn main() {
assert_eq!(cut("aa"), Ok(Some("a")));
assert_eq!(cut("ab"), Ok(Some("b")));
assert!(cut("ac").is_err());
}
Loading