aboutsummaryrefslogtreecommitdiff
path: root/src/__OLD_parcom/assert.cr
blob: 5a3e62162d04e2dd312b67b5227d10644d7b7dfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
require "./parser.cr"

module Parcom
  # `Assert` is a `Parser` that runs another `Parser` and then
  # performs a test on its result. The parser will then either fail if
  # the result does not pass the test, or forward it on if it does.
  #
  # Example:
  # ```
  # letter = Assert.new(AnyToken(Char).new) { |x| x.letter? }
  # non_letter = Assert.new(AnyToken(Char).new) { |x| !x.letter? }
  # input = Tokens.from_string("hi")
  # letter.parse(input) # this succeeds
  # non_letter.parse(input) # this fails
  # ```
  #
  # `Assert` instance can also be created by calling
  # `Parser#assert` on any parser:
  # ```
  # # This parser is identical to the above example.
  # letter = AnyToken.new(Char).assert { |x| x.letter? }
  # ```
  class Assert(T, V) < Parser(T, V)
    # Accepts the parser to run and a `Bool`-retuning block
    # containing the test to perform on the other parser's result.
    def initialize(@p : Parser(T, V), &block : V -> Bool)
      @f = block
    end 

    # Runs the parser it was initialized with, and returns that parser's
    # result if it passes the provided test. Raises `ParserFail` otherwise.
    def parse(tokens : Tokens(T)) : Result(T, V)
      result = @p.parse(tokens)
    rescue ex : ParserFail
      raise ParserFail.new("Assert (pre-assertion): #{ex.message}")
    else
      unless @f.call(result.value)
        raise ParserFail.new("Assert: predicate failed for <#{result.value}>")
      end

      return result
    end
  end
end