diff options
| author | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-19 22:13:13 +1300 |
|---|---|---|
| committer | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-19 22:13:13 +1300 |
| commit | 54263a0cfec5ef1adcd1bf6541abc0275d9a98df (patch) | |
| tree | 9ea63fc9aa2463d41e3beedaee833a51109695a4 | |
| parent | acd14e4b4f722f7fe502bbf3aabab16d7d7df396 (diff) | |
first_of
| -rw-r--r-- | spec/parcom_spec.cr | 31 | ||||
| -rw-r--r-- | src/parcom/parser.cr | 21 |
2 files changed, 51 insertions, 1 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr index aae1d1b..af95953 100644 --- a/spec/parcom_spec.cr +++ b/spec/parcom_spec.cr @@ -216,6 +216,36 @@ describe Parser do end end + describe "self.first_of" do + ps = [ + Parser.token('a'), + Parser.token('b'), + Parser.token('c'), + ] + abc = Parser(Char, Char).first_of(ps) + + it "fails to instantiate if array is empty" do + expect_raises(ArgumentError) { Parser.first_of([] of Parser(Char, Char)) } + end + + it "returns the result of the first parser that succeeds" do + {"a", "b", "c"}.each do |s| + tokens = Tokens.from_string(s) + result = abc.parse(tokens) + + result.value.should eq(s[0]) + result.tokens.empty?.should be_true + end + end + + it "fails if none of the parsers succeed" do + {"", "d"}.each do |s| + tokens = Tokens.from_string(s) + expect_raises(ParserFail) { abc.parse(tokens) } + end + end + end + describe "#assert" do p = Parser(Char, Char).any_token.assert { |c| c == 'a' } @@ -621,7 +651,6 @@ describe Parser do end end - # TODO: first_of # TODO: sep_by # TODO: phrase # TODO: peek diff --git a/src/parcom/parser.cr b/src/parcom/parser.cr index 0b09e95..95a2e21 100644 --- a/src/parcom/parser.cr +++ b/src/parcom/parser.cr @@ -112,6 +112,27 @@ module Parcom Parser(T, T).sequence(ps).named("Token Sequence: #{ts}") end + # TODO: Allow support for Iterable(Parser(T, U)) + def self.first_of(ps : Array(Parser(T, U))) : Parser(T, U) + if ps.empty? + raise ArgumentError.new("first_of requires at least one parser") + end + + Parser(T, U).new("First of: #{ps.map(&.name)}") do |tokens| + result = nil + ps.each do |p| + break unless result.nil? + result = result || p.parse?(tokens) + end + + if result.nil? + raise ParserFail.new("No successes from any provided parsers.") + else + result + end + end + end + # Creates a new parser from a `Proc`. # The `Proc` should have the properties outlined above. def initialize(@name : String, @f : Tokens(T) -> Result(T, U)) |
