aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Hall <hallmatthew314@gmail.com>2023-03-09 20:48:34 +1300
committerMatthew Hall <hallmatthew314@gmail.com>2023-03-09 20:48:34 +1300
commit9dc06838e3c78e4a77731cab6bc773846eafce99 (patch)
tree2fa1b675b5a384d6ae88e0327aa9240f5cf3cea7
parent1a9792afb92e227adcb564a051b678faa9fe2036 (diff)
Implement AtLeast and AtMost
-rw-r--r--spec/parcom_spec.cr51
-rw-r--r--src/parcom.cr24
2 files changed, 71 insertions, 4 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr
index 713445f..6301c2e 100644
--- a/spec/parcom_spec.cr
+++ b/spec/parcom_spec.cr
@@ -531,10 +531,57 @@ describe Exactly do
end
end
-pending AtLeast do
+describe AtLeast do
+ letter_a = Token.new('a')
+ tokens = TokenStream.from_string("aaaabcd")
+
+ describe "#parse" 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) }
+ end
+
+ it "parses n or more times with the given parser" do
+ p0 = AtLeast.new(0, letter_a)
+ p2 = AtLeast.new(2, letter_a)
+ p4 = AtLeast.new(4, letter_a)
+
+ result0 = p0.parse(tokens)
+ result2 = p2.parse(tokens)
+ result4 = p4.parse(tokens)
+
+ result0.value.should eq("aaaa".chars)
+ result0.tokens.should eq(tokens[4..])
+
+ result2.should eq(result0)
+ result4.should eq(result0)
+ end
+ end
end
-pending AtMost do
+describe AtMost do
+ letter_a = Token.new('a')
+ tokens = TokenStream.from_string("aaaabcd")
+
+ describe "#parse" do
+ it "does not parse more than n times" do
+ p0 = AtMost.new(0, letter_a)
+ p2 = AtMost.new(2, letter_a)
+ p6 = AtMost.new(6, letter_a)
+
+ r0 = p0.parse(tokens)
+ r0.value.empty?.should be_true
+ r0.tokens.should eq(tokens)
+
+ r2 = p2.parse(tokens)
+ r2.value.should eq("aa".chars)
+ r2.tokens.should eq(tokens[2..])
+
+ r6 = p6.parse(tokens)
+ r6.value.should eq("aaaa".chars)
+ r6.tokens.should eq(tokens[4..])
+ end
+ end
end
pending Between do
diff --git a/src/parcom.cr b/src/parcom.cr
index c7f4012..0a97e51 100644
--- a/src/parcom.cr
+++ b/src/parcom.cr
@@ -308,10 +308,30 @@ module Parcom
end
end
- class AtLeast
+ class AtLeast(T, V) < Parser(T, Array(V))
+ @p : Map(T, {Array(V), Array(V)}, Array(V))
+
+ def initialize(i : Int, p : Parser(T, V))
+ @p = (Exactly.new(i, p) + Many.new(p)).map do |tup|
+ tup[0] + tup[1]
+ end
+ end
+
+ def parse(tokens : TokenStream(T)) : Result(T, Array(V))
+ @p.parse(tokens)
+ end
end
- class AtMost
+ class AtMost(T, V) < Parser(T, Array(V))
+ @p : Map(T, Array(V?), Array(V))
+
+ def initialize(i : Int, p : Parser(T, V))
+ @p = Exactly.new(i, Optional.new(p)).map(&.compact)
+ end
+
+ def parse(tokens : TokenStream(T)) : Result(T, Array(V))
+ @p.parse(tokens)
+ end
end
class Between