Repository: kevinmehall/rust-peg (language: rust)

Issue:
Grammar input type requires a ".len()" method
The generated parser code calls the `len()` method on the grammar input, but the `Parse` trait doesn't contain such a function. This limits the types that can be used as input for the parser. I can think of two ways to fix this:

1. Add a `len()` function to the `Parse` trait.
2. I believe there are only two places where `len()` is called on the input, and both are conditions in the form of `__pos == __input.len()`. So instead of adding a `len()` trait function, I suggest `fn is_eof(&self, p: usize) -> bool`, which can easily be implemented for any type that already has a `len()` method.

What do you think? I'd be happy to provide a pull request, if desired.


Discussion / hints:
Yeah, all the methods called on input should go through the traits -- thanks for pointing that out.

Option 2 sounds good to me.

Fix patch:
diff --git a/peg-macros/grammar.rs b/peg-macros/grammar.rs
index b6f54bd..ad4d408 100644
--- a/peg-macros/grammar.rs
+++ b/peg-macros/grammar.rs
@@ -33,7 +33,7 @@ pub mod peg {
             ::peg::Parse::start(__input),
         ) {
             ::peg::RuleResult::Matched(__pos, __value) => {
-                if __pos == __input.len() {
+                if ::peg::Parse::is_eof(__input, __pos) {
                     return Ok(__value);
                 } else {
                     __err_state.mark_failure(__pos, "EOF");
@@ -50,7 +50,7 @@ pub mod peg {
             ::peg::Parse::start(__input),
         ) {
             ::peg::RuleResult::Matched(__pos, __value) => {
-                if __pos == __input.len() {
+                if ::peg::Parse::is_eof(__input, __pos) {
                     panic!(
                         "Parser is nondeterministic: succeeded when reparsing for error position"
                     );
diff --git a/peg-macros/tokens.rs b/peg-macros/tokens.rs
index 3a76477..34cd91d 100644
--- a/peg-macros/tokens.rs
+++ b/peg-macros/tokens.rs
@@ -89,6 +89,10 @@ impl Parse for FlatTokenStream {
         0
     }
 
+    fn is_eof(&self, pos: usize) -> bool {
+        pos >= self.len()
+    }
+
     fn position_repr(&self, pos: usize) -> Sp {
         Sp(
             match &self.tokens[pos] {
diff --git a/peg-macros/translate.rs b/peg-macros/translate.rs
index cbb5db4..f7bedfb 100644
--- a/peg-macros/translate.rs
+++ b/peg-macros/translate.rs
@@ -279,7 +279,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 __pos == __input.len() {
+                    if ::peg::Parse::is_eof(__input, __pos) {
                         return Ok(__value)
                     } else {
                         __err_state.mark_failure(__pos, "EOF");
@@ -293,7 +293,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 __pos == __input.len() {
+                    if ::peg::Parse::is_eof(__input, __pos) {
                         panic!("Parser is nondeterministic: succeeded when reparsing for error position");
                     } else {
                         __err_state.mark_failure(__pos, "EOF");
diff --git a/peg-runtime/lib.rs b/peg-runtime/lib.rs
index 4cfa72d..8afdab0 100644
--- a/peg-runtime/lib.rs
+++ b/peg-runtime/lib.rs
@@ -18,6 +18,7 @@ pub enum RuleResult<T> {
 pub trait Parse {
     type PositionRepr: Display;
     fn start<'input>(&'input self) -> usize;
+    fn is_eof<'input>(&'input self, p: usize) -> bool;
     fn position_repr<'input>(&'input self, p: usize) -> Self::PositionRepr;
 }
 
diff --git a/peg-runtime/slice.rs b/peg-runtime/slice.rs
index eb131d1..ff4b066 100644
--- a/peg-runtime/slice.rs
+++ b/peg-runtime/slice.rs
@@ -6,6 +6,10 @@ impl<T> Parse for [T] {
         0
     }
 
+    fn is_eof(&self, pos: usize) -> bool {
+        pos >= self.len()
+    }
+
     fn position_repr(&self, pos: usize) -> usize {
         pos
     }
diff --git a/peg-runtime/str.rs b/peg-runtime/str.rs
index 94a4d82..3b912ce 100644
--- a/peg-runtime/str.rs
+++ b/peg-runtime/str.rs
@@ -28,6 +28,10 @@ impl Parse for str {
         0
     }
 
+    fn is_eof(&self, pos: usize) -> bool {
+        pos >= self.len()
+    }
+
     fn position_repr(&self, pos: usize) -> LineCol {
         let before = &self[..pos];
         let line = before.as_bytes().iter().filter(|&&c| c == b'\n').count() + 1;


Test patch: