aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Hall <hallmatthew314@gmail.com>2023-03-18 22:47:02 +1300
committerMatthew Hall <hallmatthew314@gmail.com>2023-03-18 22:47:02 +1300
commit6406766e5a175c87dfc20c1ff1374c8c8a163517 (patch)
tree4c8e841b0d312ce46245f9748bd4057fb38581b1
parent467660e024bad8e2e084aa703686d0856a7e88b9 (diff)
Many and Some
-rw-r--r--spec/parcom_spec.cr35
-rw-r--r--src/parcom/parser.cr23
2 files changed, 58 insertions, 0 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr
index 86ad93a..870b0a2 100644
--- a/spec/parcom_spec.cr
+++ b/spec/parcom_spec.cr
@@ -380,5 +380,40 @@ describe Parser do
result.value.should eq('b')
end
end
+
+ describe "#many" do
+ p = Parser(Char, Char).token('a').many
+
+ it "returns no results if it never succeeds" do
+ ["", "bb"].each do |s|
+ tokens = Tokens.from_string(s)
+ result = p.parse(tokens)
+
+ result.value.empty?.should be_true
+ result.tokens.should eq(tokens)
+ end
+ end
+
+ it "parses as many times as possible" do
+ ["a", "aaa", "aaaaa"].each do |s|
+ tokens = Tokens.from_string(s)
+ result = p.parse(tokens)
+
+ result.value.should eq(s.chars)
+ result.tokens.empty?.should be_true
+ end
+ end
+ end
+
+ describe "#some" do
+ p = Parser(Char, Char).token('a').some
+
+ it "fails if it cannot parse at least once" do
+ ["", "bb"].each do |s|
+ tokens = Tokens.from_string(s)
+ expect_raises(ParserFail) { p.parse(tokens) }
+ end
+ end
+ end
end
diff --git a/src/parcom/parser.cr b/src/parcom/parser.cr
index fcfe292..8ee3f68 100644
--- a/src/parcom/parser.cr
+++ b/src/parcom/parser.cr
@@ -241,6 +241,29 @@ module Parcom
p1 = self
(p1 + p2).map(&.last).named("#{p1.name} >> #{p2.name}")
end
+
+ # Creates a new parser from `self` that parses with `self` as many times
+ # as possible. If it never succeeds, returns an empty list.
+ def many : Parser(T, Array(U))
+ p = self
+ Parser(T, Array(U)).new("Many: #{p.name}") do |tokens|
+ values = [] of U
+ r = p.parse?(tokens)
+ until r.nil?
+ break unless tokens != r.tokens
+ tokens = r.tokens
+ values << r.value
+ r = p.parse?(tokens)
+ end
+ Result.new(tokens, values)
+ end
+ end
+
+ # Creates a new parser from `self` that parses with `self` as many times
+ # as possible. Raises `ParserFail` it never succeeds.
+ def some : Parser(T, Array(U))
+ many.assert { |arr| !arr.empty? }.named("Some: #{@name}")
+ end
end
end