blob: 94a1627e7788969c7fb7fdd3338372c890107615 [file] [log] [blame]
Sketch🕴️cdbb1892026-02-28 19:10:35 +04001package lexer
2
3import (
4 "matheval/token"
5 "testing"
6)
7
8func TestTokenizeEmpty(t *testing.T) {
9 tokens, err := Tokenize("")
10 if err != nil {
11 t.Fatalf("unexpected error: %v", err)
12 }
13 if len(tokens) != 1 || tokens[0].Type != token.EOF {
14 t.Fatalf("expected single EOF token, got %v", tokens)
15 }
16}
17
18func TestTokenizeWhitespaceOnly(t *testing.T) {
19 tokens, err := Tokenize(" \t\n ")
20 if err != nil {
21 t.Fatalf("unexpected error: %v", err)
22 }
23 if len(tokens) != 1 || tokens[0].Type != token.EOF {
24 t.Fatalf("expected single EOF token, got %v", tokens)
25 }
26}
27
28func TestTokenizeSingleNumber(t *testing.T) {
29 tokens, err := Tokenize("42")
30 if err != nil {
31 t.Fatalf("unexpected error: %v", err)
32 }
33 expect := []token.Token{
34 {Type: token.Number, Literal: "42", Pos: 0},
35 {Type: token.EOF, Literal: "", Pos: 2},
36 }
37 assertTokens(t, expect, tokens)
38}
39
40func TestTokenizeDecimalNumber(t *testing.T) {
41 tokens, err := Tokenize("3.14")
42 if err != nil {
43 t.Fatalf("unexpected error: %v", err)
44 }
45 expect := []token.Token{
46 {Type: token.Number, Literal: "3.14", Pos: 0},
47 {Type: token.EOF, Literal: "", Pos: 4},
48 }
49 assertTokens(t, expect, tokens)
50}
51
52func TestTokenizeLeadingDotNumber(t *testing.T) {
53 tokens, err := Tokenize(".5")
54 if err != nil {
55 t.Fatalf("unexpected error: %v", err)
56 }
57 expect := []token.Token{
58 {Type: token.Number, Literal: ".5", Pos: 0},
59 {Type: token.EOF, Literal: "", Pos: 2},
60 }
61 assertTokens(t, expect, tokens)
62}
63
64func TestTokenizeOperators(t *testing.T) {
65 tokens, err := Tokenize("+-*/")
66 if err != nil {
67 t.Fatalf("unexpected error: %v", err)
68 }
69 expect := []token.Token{
70 {Type: token.Plus, Literal: "+", Pos: 0},
71 {Type: token.Minus, Literal: "-", Pos: 1},
72 {Type: token.Star, Literal: "*", Pos: 2},
73 {Type: token.Slash, Literal: "/", Pos: 3},
74 {Type: token.EOF, Literal: "", Pos: 4},
75 }
76 assertTokens(t, expect, tokens)
77}
78
79func TestTokenizeParens(t *testing.T) {
80 tokens, err := Tokenize("()")
81 if err != nil {
82 t.Fatalf("unexpected error: %v", err)
83 }
84 expect := []token.Token{
85 {Type: token.LParen, Literal: "(", Pos: 0},
86 {Type: token.RParen, Literal: ")", Pos: 1},
87 {Type: token.EOF, Literal: "", Pos: 2},
88 }
89 assertTokens(t, expect, tokens)
90}
91
92func TestTokenizeFullExpression(t *testing.T) {
93 tokens, err := Tokenize("(1 + 2.5) * 3")
94 if err != nil {
95 t.Fatalf("unexpected error: %v", err)
96 }
97 expect := []token.Token{
98 {Type: token.LParen, Literal: "(", Pos: 0},
99 {Type: token.Number, Literal: "1", Pos: 1},
100 {Type: token.Plus, Literal: "+", Pos: 3},
101 {Type: token.Number, Literal: "2.5", Pos: 5},
102 {Type: token.RParen, Literal: ")", Pos: 8},
103 {Type: token.Star, Literal: "*", Pos: 10},
104 {Type: token.Number, Literal: "3", Pos: 12},
105 {Type: token.EOF, Literal: "", Pos: 13},
106 }
107 assertTokens(t, expect, tokens)
108}
109
110func TestTokenizeNoSpaces(t *testing.T) {
111 tokens, err := Tokenize("1+2")
112 if err != nil {
113 t.Fatalf("unexpected error: %v", err)
114 }
115 expect := []token.Token{
116 {Type: token.Number, Literal: "1", Pos: 0},
117 {Type: token.Plus, Literal: "+", Pos: 1},
118 {Type: token.Number, Literal: "2", Pos: 2},
119 {Type: token.EOF, Literal: "", Pos: 3},
120 }
121 assertTokens(t, expect, tokens)
122}
123
124func TestTokenizeInvalidCharacter(t *testing.T) {
125 _, err := Tokenize("1 + @")
126 if err == nil {
127 t.Fatal("expected error for invalid character")
128 }
129}
130
131func TestTokenizeMultipleInvalidCharacters(t *testing.T) {
132 _, err := Tokenize("1 & 2")
133 if err == nil {
134 t.Fatal("expected error for invalid character")
135 }
136}
137
138func TestTokenizeMultipleDecimals(t *testing.T) {
139 // "1.2.3" — the lexer should read "1.2" as a number, then ".3" as another number
140 tokens, err := Tokenize("1.2.3")
141 if err != nil {
142 t.Fatalf("unexpected error: %v", err)
143 }
144 expect := []token.Token{
145 {Type: token.Number, Literal: "1.2", Pos: 0},
146 {Type: token.Number, Literal: ".3", Pos: 3},
147 {Type: token.EOF, Literal: "", Pos: 5},
148 }
149 assertTokens(t, expect, tokens)
150}
151
152// assertTokens is a test helper that compares two token slices.
153func assertTokens(t *testing.T, want, got []token.Token) {
154 t.Helper()
155 if len(want) != len(got) {
156 t.Fatalf("token count: want %d, got %d\nwant: %v\ngot: %v", len(want), len(got), want, got)
157 }
158 for i := range want {
159 if want[i].Type != got[i].Type {
160 t.Errorf("token[%d].Type: want %v, got %v", i, want[i].Type, got[i].Type)
161 }
162 if want[i].Literal != got[i].Literal {
163 t.Errorf("token[%d].Literal: want %q, got %q", i, want[i].Literal, got[i].Literal)
164 }
165 if want[i].Pos != got[i].Pos {
166 t.Errorf("token[%d].Pos: want %d, got %d", i, want[i].Pos, got[i].Pos)
167 }
168 }
169}