diff options
| author | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-18 22:47:02 +1300 |
|---|---|---|
| committer | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-18 22:47:02 +1300 |
| commit | 6406766e5a175c87dfc20c1ff1374c8c8a163517 (patch) | |
| tree | 4c8e841b0d312ce46245f9748bd4057fb38581b1 | |
| parent | 467660e024bad8e2e084aa703686d0856a7e88b9 (diff) | |
Many and Some
| -rw-r--r-- | spec/parcom_spec.cr | 35 | ||||
| -rw-r--r-- | src/parcom/parser.cr | 23 |
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 |
