diff options
| -rw-r--r-- | spec/parcom_spec.cr | 50 | ||||
| -rw-r--r-- | src/parcom.cr | 31 |
2 files changed, 77 insertions, 4 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr index 937c43c..57c5024 100644 --- a/spec/parcom_spec.cr +++ b/spec/parcom_spec.cr @@ -402,10 +402,56 @@ describe Tokens do end end -pending Many do +describe Many do + p = Many.new(Token.new('a')) + + describe "#parse" do + it "returns an empty array if the wrapped parser never succeeds" do + tokens = TokenStream.from_string("bb") + result = p.parse(tokens) + + result.value.empty?.should be_true + result.tokens.should eq(tokens) + end + + it "stops parsing when the wrapped parser fails, returns all successes" do + tokens = TokenStream.from_string("aaabcd") + result = p.parse(tokens) + + result.value.should eq("aaa".chars) + result.tokens.should eq(tokens[3..]) + + tokens = TokenStream.from_string("aaa") + result = p.parse(tokens) + + result.value.should eq("aaa".chars) + result.tokens.should eq(tokens[3..]) + end + end end -pending Some do +describe Some do + p = Some.new(Token.new('a')) + describe "#parse" do + it "fails if the wrapped parser never succeeds" do + tokens = TokenStream.from_string("") + expect_raises(ParserException) { p.parse(tokens) } + end + + it "stops parsing when the wrapped parser fails, returns all successes" do + tokens = TokenStream.from_string("aaabcd") + result = p.parse(tokens) + + result.value.should eq("aaa".chars) + result.tokens.should eq(tokens[3..]) + + tokens = TokenStream.from_string("aaa") + result = p.parse(tokens) + + result.value.should eq("aaa".chars) + result.tokens.should eq(tokens[3..]) + end + end end pending Exactly do diff --git a/src/parcom.cr b/src/parcom.cr index 760a450..8d7c754 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -246,10 +246,37 @@ module Parcom end end - class Many + class Many(T, V) < Parser(T, Array(V)) + def initialize(@p : Parser(T, V)) + end + + def parse(tokens : TokenStream(T)) : Result(T, Array(V)) + parsed = [] of V + + loop do + result = @p.parse?(tokens) + if result.nil? + break + else + parsed << result.value + tokens = result.tokens + end + end + + Result.new(tokens, parsed) + end end - class Some + class Some(T, V) < Parser(T, Array(V)) + @p : Assert(T, Array(V)) + + def initialize(p : Parser(T, V)) + @p = Many.new(p).assert { |arr| !arr.empty? } + end + + def parse(tokens : TokenStream(T)) : Result(T, Array(V)) + @p.parse(tokens) + end end class Exactly |
