diff options
| -rw-r--r-- | spec/parcom_spec.cr | 20 | ||||
| -rw-r--r-- | src/parcom.cr | 77 |
2 files changed, 87 insertions, 10 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr index 6301c2e..fd4b470 100644 --- a/spec/parcom_spec.cr +++ b/spec/parcom_spec.cr @@ -539,6 +539,7 @@ describe AtLeast do it "fails if there are not enough matching tokens to parse" do p = AtLeast.new(5, letter_a) expect_raises(ParserException) { p.parse(tokens) } + #expect_raises(ParserException) { raise ParserException.new("sdgseg") } end it "parses n or more times with the given parser" do @@ -584,7 +585,24 @@ describe AtMost do end end -pending Between do +describe Between do + letter_a = Token.new('a') + tokens = TokenStream.from_string("aaaabcd") + + describe "#parse" do + it "parses at least i times, up to a limit of j times" do + p0_4 = Between.new(0, 4, letter_a) + r0_4 = p0_4.parse(tokens) + + r0_4.value.should eq("aaaa".chars) + r0_4.tokens.should eq(tokens[4..]) + end + + it "fails if there are not enough parser successes" do + p = Between.new(5, 6, letter_a) + expect_raises(ParserException) { p.parse(tokens) } + end + end end pending StopAt do diff --git a/src/parcom.cr b/src/parcom.cr index 0a97e51..c1ac147 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -121,6 +121,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, V) result = @p.parse(tokens) Result.new(tokens, result.value) + rescue ex : ParserException + raise ParserException.new("Peek: #{ex.message}") end end @@ -131,18 +133,28 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, V) result = @p.parse(tokens) - + rescue ex : ParserException + raise ParserException.new("Assert (pre-assertion): #{ex.message}") + else unless @f.call(result.value) raise ParserException.new("Assert: predicate failed for <#{result.value}>") end - result + return result end end - class Satisfy(T) < Assert(T, T) + class Satisfy(T) < Parser(T, T) + @p : Assert(T, T) + def initialize(&block : T -> Bool) - super(AnyToken(T).new, &block) + @p = AnyToken(T).new.assert(&block) + end + + def parse(tokens : TokenStream(T)) : Result(T, T) + @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Satisfy: #{ex.message}") end end @@ -164,8 +176,12 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, V) @p1.parse(tokens) - rescue ParserException - @p2.parse(tokens) + rescue ex1 : ParserException + begin + @p2.parse(tokens) + rescue ex2 : ParserException + raise ParserException.new("Alt (#{ex1.message}), (#{ex2.message})") + end end end @@ -177,6 +193,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, U) result = @p.parse(tokens) Result.new(result.tokens, @f.call(result.value)) + rescue ex : ParserException + raise ParserException.new("Map: #{ex.message}") end end @@ -189,6 +207,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, V) @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Phrase: #{ex.message}") end end @@ -197,8 +217,18 @@ module Parcom end def parse(tokens : TokenStream(T)) : Result(T, {V, U}) - r1 = @p1.parse(tokens) - r2 = @p2.parse(r1.tokens) + begin + r1 = @p1.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Plus (left): #{ex.message}") + end + + begin + r2 = @p2.parse(r1.tokens) + rescue ex : ParserException + raise ParserException.new("Plus (right): #{ex.message}") + end + Result.new(r2.tokens, {r1.value, r2.value}) end end @@ -244,6 +274,8 @@ module Parcom end Result.new(tokens, parsed) + rescue ex : ParserException + raise ParserException.new("Sequence: #{ex.message}") end end @@ -259,6 +291,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, Array(T)) @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Tokens: #{ex.message}") end end @@ -280,6 +314,8 @@ module Parcom end Result.new(tokens, parsed) + rescue ex : ParserException + raise ParserException.new("Many: #{ex.message}") end end @@ -292,6 +328,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, Array(V)) @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Some: #{ex.message}") end end @@ -305,6 +343,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, Array(V)) @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Exactly: #{ex.message}") end end @@ -319,6 +359,8 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, Array(V)) @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("AtLeast: #{ex.message}") end end @@ -331,10 +373,27 @@ module Parcom def parse(tokens : TokenStream(T)) : Result(T, Array(V)) @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("AtMost: #{ex.message}") end end - class Between + class Between(T, V) < Parser(T, Array(V)) + @p : Map(T, {Array(V), Array(V)}, Array(V)) + + def initialize(i : Int, j : Int, p : Parser(T, V)) + lower = i < j ? i : j + upper = (i - j).abs + @p = (Exactly.new(lower, p) + AtMost.new(upper, p)).map do |tup| + tup[0] + tup[1] + end + end + + def parse(tokens : TokenStream(T)) : Result(T, Array(V)) + @p.parse(tokens) + rescue ex : ParserException + raise ParserException.new("Between: #{ex.message}") + end end class StopAt |
