aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Hall <hallmatthew314@gmail.com>2023-03-09 21:32:35 +1300
committerMatthew Hall <hallmatthew314@gmail.com>2023-03-09 21:32:35 +1300
commita26ccb40ca98c82f486f1c57334ccc04534a1a23 (patch)
tree4d44196022f2280ecc47e07802c6575c3df1ac1a
parent9dc06838e3c78e4a77731cab6bc773846eafce99 (diff)
Implement between + improve error handling
-rw-r--r--spec/parcom_spec.cr20
-rw-r--r--src/parcom.cr77
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