aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--spec/parcom_spec.cr31
-rw-r--r--src/parcom/parser.cr21
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))