diff options
| -rw-r--r-- | spec/parcom_spec.cr | 15 | ||||
| -rw-r--r-- | src/parcom/parser.cr | 26 |
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)) |
