blob: 521455ba8b2dd81b9db24893bd98d18902abc6ed [file] [log] [blame]
Sketch🕴️b05c53f2026-02-28 19:13:36 +04001package parser
2
3import (
4 "matheval/ast"
5 "matheval/token"
6 "testing"
7)
8
9// helper: tokenize inline for concise tests
10func tokens(toks ...token.Token) []token.Token {
11 return toks
12}
13
14func tok(typ token.Type, lit string, pos int) token.Token {
15 return token.Token{Type: typ, Literal: lit, Pos: pos}
16}
17
18// --- Success cases ---
19
20func TestParseSingleNumber(t *testing.T) {
21 toks := tokens(
22 tok(token.Number, "42", 0),
23 tok(token.EOF, "", 2),
24 )
25 node, err := Parse(toks)
26 if err != nil {
27 t.Fatalf("unexpected error: %v", err)
28 }
29 num, ok := node.(*ast.NumberLit)
30 if !ok {
31 t.Fatalf("expected *ast.NumberLit, got %T", node)
32 }
33 if num.Value != 42 {
34 t.Fatalf("expected 42, got %f", num.Value)
35 }
36}
37
38func TestParseDecimalNumber(t *testing.T) {
39 toks := tokens(
40 tok(token.Number, "3.14", 0),
41 tok(token.EOF, "", 4),
42 )
43 node, err := Parse(toks)
44 if err != nil {
45 t.Fatalf("unexpected error: %v", err)
46 }
47 num, ok := node.(*ast.NumberLit)
48 if !ok {
49 t.Fatalf("expected *ast.NumberLit, got %T", node)
50 }
51 if num.Value != 3.14 {
52 t.Fatalf("expected 3.14, got %f", num.Value)
53 }
54}
55
56func TestParseAddition(t *testing.T) {
57 // 1 + 2
58 toks := tokens(
59 tok(token.Number, "1", 0),
60 tok(token.Plus, "+", 2),
61 tok(token.Number, "2", 4),
62 tok(token.EOF, "", 5),
63 )
64 node, err := Parse(toks)
65 if err != nil {
66 t.Fatalf("unexpected error: %v", err)
67 }
68 expr, ok := node.(*ast.BinaryExpr)
69 if !ok {
70 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
71 }
72 if expr.Op != token.Plus {
73 t.Fatalf("expected Plus, got %v", expr.Op)
74 }
75 assertNumber(t, expr.Left, 1)
76 assertNumber(t, expr.Right, 2)
77}
78
79func TestParseSubtraction(t *testing.T) {
80 // 5 - 3
81 toks := tokens(
82 tok(token.Number, "5", 0),
83 tok(token.Minus, "-", 2),
84 tok(token.Number, "3", 4),
85 tok(token.EOF, "", 5),
86 )
87 node, err := Parse(toks)
88 if err != nil {
89 t.Fatalf("unexpected error: %v", err)
90 }
91 expr, ok := node.(*ast.BinaryExpr)
92 if !ok {
93 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
94 }
95 if expr.Op != token.Minus {
96 t.Fatalf("expected Minus, got %v", expr.Op)
97 }
98 assertNumber(t, expr.Left, 5)
99 assertNumber(t, expr.Right, 3)
100}
101
102func TestParseMultiplication(t *testing.T) {
103 // 2 * 3
104 toks := tokens(
105 tok(token.Number, "2", 0),
106 tok(token.Star, "*", 2),
107 tok(token.Number, "3", 4),
108 tok(token.EOF, "", 5),
109 )
110 node, err := Parse(toks)
111 if err != nil {
112 t.Fatalf("unexpected error: %v", err)
113 }
114 expr, ok := node.(*ast.BinaryExpr)
115 if !ok {
116 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
117 }
118 if expr.Op != token.Star {
119 t.Fatalf("expected Star, got %v", expr.Op)
120 }
121 assertNumber(t, expr.Left, 2)
122 assertNumber(t, expr.Right, 3)
123}
124
125func TestParseDivision(t *testing.T) {
126 // 6 / 2
127 toks := tokens(
128 tok(token.Number, "6", 0),
129 tok(token.Slash, "/", 2),
130 tok(token.Number, "2", 4),
131 tok(token.EOF, "", 5),
132 )
133 node, err := Parse(toks)
134 if err != nil {
135 t.Fatalf("unexpected error: %v", err)
136 }
137 expr, ok := node.(*ast.BinaryExpr)
138 if !ok {
139 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
140 }
141 if expr.Op != token.Slash {
142 t.Fatalf("expected Slash, got %v", expr.Op)
143 }
144 assertNumber(t, expr.Left, 6)
145 assertNumber(t, expr.Right, 2)
146}
147
148func TestParsePrecedence(t *testing.T) {
149 // 1 + 2 * 3 → 1 + (2 * 3)
150 toks := tokens(
151 tok(token.Number, "1", 0),
152 tok(token.Plus, "+", 2),
153 tok(token.Number, "2", 4),
154 tok(token.Star, "*", 6),
155 tok(token.Number, "3", 8),
156 tok(token.EOF, "", 9),
157 )
158 node, err := Parse(toks)
159 if err != nil {
160 t.Fatalf("unexpected error: %v", err)
161 }
162 // Root should be Plus
163 expr, ok := node.(*ast.BinaryExpr)
164 if !ok {
165 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
166 }
167 if expr.Op != token.Plus {
168 t.Fatalf("expected Plus at root, got %v", expr.Op)
169 }
170 assertNumber(t, expr.Left, 1)
171 // Right should be Star
172 right, ok := expr.Right.(*ast.BinaryExpr)
173 if !ok {
174 t.Fatalf("expected right to be *ast.BinaryExpr, got %T", expr.Right)
175 }
176 if right.Op != token.Star {
177 t.Fatalf("expected Star, got %v", right.Op)
178 }
179 assertNumber(t, right.Left, 2)
180 assertNumber(t, right.Right, 3)
181}
182
183func TestParsePrecedenceMulFirst(t *testing.T) {
184 // 2 * 3 + 1 → (2 * 3) + 1
185 toks := tokens(
186 tok(token.Number, "2", 0),
187 tok(token.Star, "*", 2),
188 tok(token.Number, "3", 4),
189 tok(token.Plus, "+", 6),
190 tok(token.Number, "1", 8),
191 tok(token.EOF, "", 9),
192 )
193 node, err := Parse(toks)
194 if err != nil {
195 t.Fatalf("unexpected error: %v", err)
196 }
197 expr, ok := node.(*ast.BinaryExpr)
198 if !ok {
199 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
200 }
201 if expr.Op != token.Plus {
202 t.Fatalf("expected Plus at root, got %v", expr.Op)
203 }
204 left, ok := expr.Left.(*ast.BinaryExpr)
205 if !ok {
206 t.Fatalf("expected left to be *ast.BinaryExpr, got %T", expr.Left)
207 }
208 if left.Op != token.Star {
209 t.Fatalf("expected Star, got %v", left.Op)
210 }
211 assertNumber(t, left.Left, 2)
212 assertNumber(t, left.Right, 3)
213 assertNumber(t, expr.Right, 1)
214}
215
216func TestParseLeftAssociativity(t *testing.T) {
217 // 1 - 2 - 3 → (1 - 2) - 3
218 toks := tokens(
219 tok(token.Number, "1", 0),
220 tok(token.Minus, "-", 2),
221 tok(token.Number, "2", 4),
222 tok(token.Minus, "-", 6),
223 tok(token.Number, "3", 8),
224 tok(token.EOF, "", 9),
225 )
226 node, err := Parse(toks)
227 if err != nil {
228 t.Fatalf("unexpected error: %v", err)
229 }
230 // Root: (1 - 2) - 3
231 expr, ok := node.(*ast.BinaryExpr)
232 if !ok {
233 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
234 }
235 if expr.Op != token.Minus {
236 t.Fatalf("expected Minus at root, got %v", expr.Op)
237 }
238 assertNumber(t, expr.Right, 3)
239 left, ok := expr.Left.(*ast.BinaryExpr)
240 if !ok {
241 t.Fatalf("expected left to be *ast.BinaryExpr, got %T", expr.Left)
242 }
243 if left.Op != token.Minus {
244 t.Fatalf("expected Minus, got %v", left.Op)
245 }
246 assertNumber(t, left.Left, 1)
247 assertNumber(t, left.Right, 2)
248}
249
250func TestParseParentheses(t *testing.T) {
251 // (1 + 2) * 3
252 toks := tokens(
253 tok(token.LParen, "(", 0),
254 tok(token.Number, "1", 1),
255 tok(token.Plus, "+", 3),
256 tok(token.Number, "2", 5),
257 tok(token.RParen, ")", 6),
258 tok(token.Star, "*", 8),
259 tok(token.Number, "3", 10),
260 tok(token.EOF, "", 11),
261 )
262 node, err := Parse(toks)
263 if err != nil {
264 t.Fatalf("unexpected error: %v", err)
265 }
266 expr, ok := node.(*ast.BinaryExpr)
267 if !ok {
268 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
269 }
270 if expr.Op != token.Star {
271 t.Fatalf("expected Star at root, got %v", expr.Op)
272 }
273 assertNumber(t, expr.Right, 3)
274 left, ok := expr.Left.(*ast.BinaryExpr)
275 if !ok {
276 t.Fatalf("expected left to be *ast.BinaryExpr, got %T", expr.Left)
277 }
278 if left.Op != token.Plus {
279 t.Fatalf("expected Plus, got %v", left.Op)
280 }
281 assertNumber(t, left.Left, 1)
282 assertNumber(t, left.Right, 2)
283}
284
285func TestParseNestedParentheses(t *testing.T) {
286 // ((1 + 2))
287 toks := tokens(
288 tok(token.LParen, "(", 0),
289 tok(token.LParen, "(", 1),
290 tok(token.Number, "1", 2),
291 tok(token.Plus, "+", 4),
292 tok(token.Number, "2", 6),
293 tok(token.RParen, ")", 7),
294 tok(token.RParen, ")", 8),
295 tok(token.EOF, "", 9),
296 )
297 node, err := Parse(toks)
298 if err != nil {
299 t.Fatalf("unexpected error: %v", err)
300 }
301 expr, ok := node.(*ast.BinaryExpr)
302 if !ok {
303 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
304 }
305 if expr.Op != token.Plus {
306 t.Fatalf("expected Plus, got %v", expr.Op)
307 }
308 assertNumber(t, expr.Left, 1)
309 assertNumber(t, expr.Right, 2)
310}
311
312func TestParseComplexExpression(t *testing.T) {
313 // 1 + 2 * 3 - 4 / 2 → (1 + (2*3)) - (4/2)
314 toks := tokens(
315 tok(token.Number, "1", 0),
316 tok(token.Plus, "+", 2),
317 tok(token.Number, "2", 4),
318 tok(token.Star, "*", 5),
319 tok(token.Number, "3", 6),
320 tok(token.Minus, "-", 8),
321 tok(token.Number, "4", 10),
322 tok(token.Slash, "/", 11),
323 tok(token.Number, "2", 12),
324 tok(token.EOF, "", 13),
325 )
326 node, err := Parse(toks)
327 if err != nil {
328 t.Fatalf("unexpected error: %v", err)
329 }
330 // Root: (1 + (2*3)) - (4/2)
331 root, ok := node.(*ast.BinaryExpr)
332 if !ok {
333 t.Fatalf("expected *ast.BinaryExpr, got %T", node)
334 }
335 if root.Op != token.Minus {
336 t.Fatalf("expected Minus at root, got %v", root.Op)
337 }
338 // Left: 1 + (2*3)
339 left, ok := root.Left.(*ast.BinaryExpr)
340 if !ok {
341 t.Fatalf("expected left to be *ast.BinaryExpr, got %T", root.Left)
342 }
343 if left.Op != token.Plus {
344 t.Fatalf("expected Plus, got %v", left.Op)
345 }
346 assertNumber(t, left.Left, 1)
347 mul, ok := left.Right.(*ast.BinaryExpr)
348 if !ok {
349 t.Fatalf("expected *ast.BinaryExpr, got %T", left.Right)
350 }
351 if mul.Op != token.Star {
352 t.Fatalf("expected Star, got %v", mul.Op)
353 }
354 assertNumber(t, mul.Left, 2)
355 assertNumber(t, mul.Right, 3)
356 // Right: 4/2
357 div, ok := root.Right.(*ast.BinaryExpr)
358 if !ok {
359 t.Fatalf("expected right to be *ast.BinaryExpr, got %T", root.Right)
360 }
361 if div.Op != token.Slash {
362 t.Fatalf("expected Slash, got %v", div.Op)
363 }
364 assertNumber(t, div.Left, 4)
365 assertNumber(t, div.Right, 2)
366}
367
368// --- Error cases ---
369
370func TestParseEmptyInput(t *testing.T) {
371 toks := tokens(
372 tok(token.EOF, "", 0),
373 )
374 _, err := Parse(toks)
375 if err == nil {
376 t.Fatal("expected error for empty input")
377 }
378}
379
380func TestParseMissingRParen(t *testing.T) {
381 // (1 + 2
382 toks := tokens(
383 tok(token.LParen, "(", 0),
384 tok(token.Number, "1", 1),
385 tok(token.Plus, "+", 3),
386 tok(token.Number, "2", 5),
387 tok(token.EOF, "", 6),
388 )
389 _, err := Parse(toks)
390 if err == nil {
391 t.Fatal("expected error for missing right paren")
392 }
393}
394
395func TestParseUnexpectedRParen(t *testing.T) {
396 // ) 1
397 toks := tokens(
398 tok(token.RParen, ")", 0),
399 tok(token.Number, "1", 2),
400 tok(token.EOF, "", 3),
401 )
402 _, err := Parse(toks)
403 if err == nil {
404 t.Fatal("expected error for unexpected right paren")
405 }
406}
407
408func TestParseTrailingOperator(t *testing.T) {
409 // 1 +
410 toks := tokens(
411 tok(token.Number, "1", 0),
412 tok(token.Plus, "+", 2),
413 tok(token.EOF, "", 3),
414 )
415 _, err := Parse(toks)
416 if err == nil {
417 t.Fatal("expected error for trailing operator")
418 }
419}
420
421func TestParseTrailingTokens(t *testing.T) {
422 // 1 2
423 toks := tokens(
424 tok(token.Number, "1", 0),
425 tok(token.Number, "2", 2),
426 tok(token.EOF, "", 3),
427 )
428 _, err := Parse(toks)
429 if err == nil {
430 t.Fatal("expected error for trailing tokens")
431 }
432}
433
434func TestParseConsecutiveOperators(t *testing.T) {
435 // 1 + * 2
436 toks := tokens(
437 tok(token.Number, "1", 0),
438 tok(token.Plus, "+", 2),
439 tok(token.Star, "*", 4),
440 tok(token.Number, "2", 6),
441 tok(token.EOF, "", 7),
442 )
443 _, err := Parse(toks)
444 if err == nil {
445 t.Fatal("expected error for consecutive operators")
446 }
447}
448
449func TestParseEmptyParens(t *testing.T) {
450 // ()
451 toks := tokens(
452 tok(token.LParen, "(", 0),
453 tok(token.RParen, ")", 1),
454 tok(token.EOF, "", 2),
455 )
456 _, err := Parse(toks)
457 if err == nil {
458 t.Fatal("expected error for empty parentheses")
459 }
460}
461
462// --- Helper ---
463
464func assertNumber(t *testing.T, node ast.Node, expected float64) {
465 t.Helper()
466 num, ok := node.(*ast.NumberLit)
467 if !ok {
468 t.Fatalf("expected *ast.NumberLit, got %T", node)
469 }
470 if num.Value != expected {
471 t.Fatalf("expected %f, got %f", expected, num.Value)
472 }
473}