require "./parser.cr" module Parcom # `FirstOf` is a `Parser` that accepts multiple parsers, and tries to parse # with all of them, in order. As soon as one of the parsers succeeds, # that parser's result is returned. If none of the parsers are successful, # the parsing fails. class FirstOf(T, V) < Parser(T, V) @p : Parser(T, V) # Accepts the parsers to use. Raises an `ArgumentError` if # no parsers are provided. def initialize(ps : Iterable(Parser(T, V))) ps_iter = ps.each p = ps_iter.next if p.is_a?(Iterator::Stop) raise ArgumentError.new("FirstOf requires atleast one parser, got none") end # Combine all the parsers into one by wrapping them with `Alt`. @p = p loop do p = ps_iter.next break if p.is_a?(Iterator::Stop) @p = @p | p end end # Tries to parse with each of the given parsers. Either returns the first # successful result or fails if no parsers succeed. def parse(tokens : Tokens(T)) : Result(T, V) @p.parse(tokens) rescue ex : ParserFail raise ParserFail.new("FirstOf: #{ex.message}") end end end