aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--spec/parcom_spec.cr15
-rw-r--r--src/parcom/parser.cr26
2 files changed, 41 insertions, 0 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr
index 76be4c2..c586a7c 100644
--- a/spec/parcom_spec.cr
+++ b/spec/parcom_spec.cr
@@ -409,6 +409,21 @@ describe Parser do
end
end
+ describe "#and_then" do
+ p_string = ->(s : String) do
+ Parser.token_sequence(s.chars).map(&.join)
+ end
+ p_any_word = Parser(Char, Array(Char)).satisfy(&.letter?).some.map(&.join)
+ space = Parser(Char, Array(Char)).satisfy(&.whitespace?).some
+ # Parses two of the same word, with whitespace between:
+ two_words = (p_any_word << space).and_then(p_string)
+
+ tokens = Tokens.from_string("foo foo")
+ result = two_words.parse(tokens)
+
+ result.value.should eq("foo")
+ end
+
describe "#many" do
p = Parser(Char, Char).token('a').many
diff --git a/src/parcom/parser.cr b/src/parcom/parser.cr
index d91ac84..fd161f9 100644
--- a/src/parcom/parser.cr
+++ b/src/parcom/parser.cr
@@ -266,6 +266,32 @@ module Parcom
(p1 + p2).map(&.last).named("#{p1.name} >> #{p2.name}")
end
+ # Creates a new parser from `self` and a function based on
+ # the result of `self`. `#and_then` is the monadic combinator
+ # and works like `and_then` in Rust, or like `>>=` in Haskell.
+ # Example:
+ # ```
+ # p_string = ->(s : String) do
+ # Parser.token_sequence(s.chars).map(&.join)
+ # end
+ # p_any_word = Parser(Char, Array(Char)).satisfy(&.letter?).some.map(&.join)
+ # space = Parser(Char, Array(Char).satisfy(&.whitespace?).some
+ # # Parses two of the same word, with whitespace between:
+ # two_words = (p_any_word << space).and_then(p_string)
+ # ```
+ def and_then(f : U -> Parser(T, V)) : Parser(T, V) forall V
+ p = self
+ Parser(T, V).new("#{p.name} and_then..") do |tokens|
+ r = p.parse(tokens)
+ f.call(r.value).parse(r.tokens)
+ end
+ end
+
+ # :ditto:
+ def and_then(&block : U -> Parser(T, V)) : Parser(T, V) forall V
+ and_then(block)
+ end
+
# Creates a new parser from `self` that parses with `self` as many times
# as possible. Returns an empty list if it never succeeds.
def many : Parser(T, Array(U))