require "./parser.cr" module Parcom # `Optional` is a `Parser` that tries to parse with another parser, # but does not fail the parser chain if the wrapped parser fails. # If the wrapped parser fails, an `Optional` will not modify the input # stream, and return `nil` instead. # # Example: # ``` # digit = Satisfy(Char).new { |c| c.digit? } # digits = Many.new(digit) # plus_sign = Optional.new(Token.new('+')) # positive_int = plus_sign >> digits # # input1 = Tokens.from_string("+12345") # input2 = Tokens.from_string("12345") # # # Both of these has the same result # positive_int.parse(input1) # positive_int.parse(input2) # ``` class Optional(T, V) < Parser(T, V?) # Accepts the parser to use. def initialize(@p : Parser(T, V)) end # Tries to parse with the given parser, succeeds with `nil` # instead of failing. This parser does not consume input if # the wrapped parser fails. def parse(tokens : Tokens(T)) : Result(T, V?) r = @p.parse?(tokens) new_tokens = r.nil? ? tokens : r.tokens new_value = r.nil? ? nil : r.value Result.new(new_tokens, new_value) end end end