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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
require_relative "../test_helper"

require "lox/scanner"

class TestScanner < Lox::Test
  def setup
    @scanner = Lox::Scanner.new
  end

  def test_basic_tokens
    %w[( LEFT_PAREN
       ) RIGHT_PAREN
       { LEFT_BRACE
       } RIGHT_BRACE
       , COMMA
       . DOT
       - MINUS
       + PLUS
       ; SEMICOLON
       * STAR
       != BANG_EQUAL
       ! BANG
       == EQUAL_EQUAL
       = EQUAL
       <= LESS_EQUAL
       < LESS
       >= GREATER_EQUAL
       > GREATER
       / SLASH].each_slice(2).to_h.transform_values(&:to_sym).each do |str, token_type|
         assert_equal token_type.to_sym, @scanner.scan(str)[0].type
       end
  end

  def test_comments_and_whitespace
    tokens = @scanner.scan(<<~SRC)
      (\t) // here lies a comment
      . //
    SRC

    assert_equal %i[LEFT_PAREN RIGHT_PAREN DOT EOF], tokens.map(&:type)
  end

  def test_line_numbers
    tokens = @scanner.scan(<<~SRC)
      (
      )
    SRC

    assert_equal [1, 2, 3], tokens.map(&:line)
  end

  def test_strings
    assert_equal [
      Lox::Token.new(:STRING, '""', "", 1),
      Lox::Token.new(:EOF, "", nil, 1),
    ], @scanner.scan('""')

    assert_raises do
      @scanner.scan('"')
    end

    assert_equal [
      Lox::Token.new(:STRING, '"foo"', "foo", 1),
      Lox::Token.new(:EOF, "", nil, 1),
    ], @scanner.scan('"foo"')

    assert_equal [
      Lox::Token.new(:STRING, "\"foo\nbar\"", "foo\nbar", 2),
      Lox::Token.new(:EOF, "", nil, 2),
    ], @scanner.scan("\"foo\nbar\"")
  end

  def test_numbers
    assert_equal [
      Lox::Token.new(:NUMBER, "123", 123.0, 1),
      Lox::Token.new(:NUMBER, "123.4", 123.4, 1),
      Lox::Token.new(:EOF, "", nil, 1),
    ], @scanner.scan("123 123.4")
  end

  def test_identifiers
    assert_equal [
      Lox::Token.new(:OR, "or", nil, 1),
      Lox::Token.new(:IDENTIFIER, "orchid", nil, 1),
      Lox::Token.new(:IDENTIFIER, "o", nil, 1),
      Lox::Token.new(:EOF, "", nil, 1),
    ], @scanner.scan("or orchid o")
  end

  def test_block_comments
    tokens = @scanner.scan(<<~SRC)
      foo
      /* here lies a /* nested */ block comment
      with newlines */
      bar
    SRC

    assert_equal [
      Lox::Token.new(:IDENTIFIER, "foo", nil, 1),
      Lox::Token.new(:IDENTIFIER, "bar", nil, 4),
      Lox::Token.new(:EOF, "", nil, 5),
    ], tokens

    assert_raises do
      @scanner.scan("/*")
    end
  end
end