diff options
| -rw-r--r-- | src/parcom.cr | 35 | ||||
| -rw-r--r-- | src/parcom/plus.cr | 54 |
2 files changed, 54 insertions, 35 deletions
diff --git a/src/parcom.cr b/src/parcom.cr index 6c17596..acb0503 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -81,41 +81,6 @@ module Parcom end end - class Phrase(T, V) < Parser(T, V) - @p : Map(T, {V, Nil}, V) - - def initialize(p : Parser(T, V)) - @p = (p + EOF(T).new).map &.first - end - - def parse(tokens : Tokens(T)) : Result(T, V) - @p.parse(tokens) - rescue ex : ParserFail - raise ParserFail.new("Phrase: #{ex.message}") - end - end - - class Plus(T, V, U) < Parser(T, {V, U}) - def initialize(@p1 : Parser(T, V), @p2 : Parser(T, U)) - end - - def parse(tokens : Tokens(T)) : Result(T, {V, U}) - begin - r1 = @p1.parse(tokens) - rescue ex : ParserFail - raise ParserFail.new("Plus (left): #{ex.message}") - end - - begin - r2 = @p2.parse(r1.tokens) - rescue ex : ParserFail - raise ParserFail.new("Plus (right): #{ex.message}") - end - - Result.new(r2.tokens, {r1.value, r2.value}) - end - end - class Left(T, V, U) < Parser(T, V) @p : Map(T, {V, U}, V) diff --git a/src/parcom/plus.cr b/src/parcom/plus.cr new file mode 100644 index 0000000..53c9b4f --- /dev/null +++ b/src/parcom/plus.cr @@ -0,0 +1,54 @@ +require "./parser.cr" + +module Parcom + # `Plus` is a parser that tries to parse with two different + # parsers in succession and fails if either of the two parsers fails. + # The return type of this parser is `{V, U}`, where `V` is the return + # type of the first parser and `U` is the return type of the second. + # + # Example: + # ``` + # parse1 = Token.new('1') + # parse2 = Token.new('2') + # tokens = Tokens.from_string("12") + # result = (parse1 + parse2).parse(tokens) # succeeds + # result = (parse2 + parse1).parse(tokens) # fails, wrong order + # ``` + # + # `Plus` parsers can be chained together, but the resulting return type + # can become unweildly with too many combined parsers: + # ``` + # letter_a = Token.new('a') + # a5 = letter_a + letter_a + letter_a + letter_a + letter_a + # tokens = Tokens.from_string("aaaaa") + # a5.parse(tokens) # succeeds + # # `a5` has a return type of { { { {Char, Char}, Char}, Char}, Char} + # ``` + # + # If you need to parse more than two things in this manner, + # consider using `Many`, `Some`, `Sequence`, or `TokenSeq` instead. + class Plus(T, V, U) < Parser(T, {V, U}) + # Accepts the two parsers to use, in order. + def initialize(@p1 : Parser(T, V), @p2 : Parser(T, U)) + end + + # Tries to parse with the two given parsers, and returns + # their results in a tuple if the both succeed. + def parse(tokens : Tokens(T)) : Result(T, {V, U}) + begin + r1 = @p1.parse(tokens) + rescue ex : ParserFail + raise ParserFail.new("Plus (left): #{ex.message}") + end + + begin + r2 = @p2.parse(r1.tokens) + rescue ex : ParserFail + raise ParserFail.new("Plus (right): #{ex.message}") + end + + Result.new(r2.tokens, {r1.value, r2.value}) + end + end +end + |
