diff options
| -rw-r--r-- | spec/parcom_spec.cr | 23 | ||||
| -rw-r--r-- | src/parcom.cr | 21 |
2 files changed, 41 insertions, 3 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr index 5be59b9..937c43c 100644 --- a/spec/parcom_spec.cr +++ b/spec/parcom_spec.cr @@ -378,7 +378,28 @@ describe Optional do end end -pending Tokens do +describe Tokens do + p = Tokens.new("test".chars) + + describe "#parse" do + it "fails if the input stream is too short" do + input = TokenStream.from_string("") + expect_raises(ParserException) { p.parse(input) } + end + + it "fails if it encounters an unexpected token" do + input = TokenStream.from_string("text") + expect_raises(ParserException) { p.parse(input) } + end + + it "succeeds if the input starts with the expected tokens" do + input = TokenStream.from_string("testing") + result = p.parse(input) + + result.tokens.should eq(input[4..]) + result.value.should eq("test".chars) + end + end end pending Many do diff --git a/src/parcom.cr b/src/parcom.cr index 14d2e44..760a450 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -133,7 +133,7 @@ module Parcom result = @p.parse(tokens) unless @f.call(result.value) - raise ParserException.new("Assert: predicate failed") + raise ParserException.new("Assert: predicate failed for <#{result.value}>") end result @@ -153,6 +153,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, T) @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Token <#{@expected}>: #{ex.message}") end end @@ -226,7 +228,22 @@ module Parcom end end - class Tokens + class Tokens(T) < Parser(T, Array(T)) + def initialize(@expected : Iterable(T)) + end + + # TODO: this can probably be optimised more for Arrays + # TODO: might be better to use #zip? + def parse(tokens : TokenStream(T)) : Result(T, Array(T)) + parsed_tokens = [] of T + + @expected.each_with_index do |t, i| + r = Token.new(t).parse(tokens[i..]) + parsed_tokens << r.value + end + + Result.new(tokens[parsed_tokens.size..], parsed_tokens) + end end class Many |
