aboutsummaryrefslogtreecommitdiff
path: root/spec/parser_chain_spec.cr
blob: 3f1e3a0b104a1ead10701b108e56acdc9f0ad74c (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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
require "./spec_helper"

include Parcom

describe "parser_chain" do
  it "works with zero intermediate steps" do
    p = parser_chain "test", Char, Char, finally: Parser(Char, Char).pure('x')
    tokens = Tokens.from_string("abcd")
    result = p.parse(tokens)

    result.value.should eq('x')
    result.tokens.should eq(tokens)
  end

  it "works with one intermediate step" do
    p = parser_chain "test", Char, Char,
      {c, Parser(Char, Char).any_token},
      finally: Parser(Char, Char).pure(c)
    tokens = Tokens.from_string("abcd")
    result = p.parse(tokens)

    result.value.should eq('a')
    result.tokens.should eq(tokens[1..])
  end

  it "works with many intermediate steps" do
    digit = Parser(Char, Char).satisfy(&.number?)
    p = parser_chain "float", Char, Float64,
      {sign,  Parser.token('-').map_const(-1).recover(1)},
      {front, digit.many},
      {point, Parser.token('.').optional},
      {back,  digit.many},
      finally: case {front.empty?, point.nil?, back.empty?}
          when {false, _, true}
            Parser(Char, Float64).pure(front.join.to_f64 * sign)
          when {true, false, false}
            Parser(Char, Float64).pure("0.#{back.join}".to_f64 * sign)
          when {false, false, false}
            Parser(Char, Float64).pure("#{front.join}.#{back.join}".to_f64 * sign)
          else
            Parser(Char, Float64).flunk
        end
    {
      {"1",     1_f64},
      {"-1",   -1_f64},
      {"2.",    2_f64},
      {"-2.",  -2_f64},
      {".3",    0.3_f64},
      {"-.3",  -0.3_f64},
      {"0.4",   0.4_f64},
      {"-0.4", -0.4_f64},
    }.each do |s, v|
      tokens = Tokens.from_string(s)
      result = p.parse(tokens)

      result.value.should eq(v)
      result.tokens.empty?.should be_true
    end
  end

  it "allows ignoring results with underscores" do
    any_word = Parser(Char, String).satisfy(&.letter?).some.map(&.join)
    ws = Parser(Char, Array(Char)).satisfy(&.whitespace?).many
    two_words = parser_chain "two words", Char, {String, String},
      {word, any_word},
      {_,    ws},
      finally: Parser.token_sequence(word.chars).map_const({word, word})

    tokens = Tokens.from_string("foo  \n  foo")
    result = two_words.parse(tokens)
    result.value.should eq({"foo", "foo"})

    tokens = Tokens.from_string("foo   bar")
    expect_raises(ParserFail) { two_words.parse(tokens) }
    tokens = Tokens.from_string("foofoo")
    expect_raises(ParserFail) { two_words.parse(tokens) }
  end
end