blob: c7be53b1b42097a027a2b381e34cec9498545f7a [file] [log] [blame]
Sketch🕴️b05c53f2026-02-28 19:13:36 +04001package parser
2
3import (
4 "fmt"
5 "matheval/ast"
6 "matheval/token"
7 "strconv"
8)
9
10// Parse converts a slice of tokens into an AST.
11// Returns an error for malformed expressions.
12func Parse(tokens []token.Token) (ast.Node, error) {
13 p := &parser{tokens: tokens}
14 node, err := p.expr()
15 if err != nil {
16 return nil, err
17 }
18 // Ensure all tokens have been consumed (only EOF remains).
19 if p.current().Type != token.EOF {
20 tok := p.current()
21 return nil, fmt.Errorf("unexpected token %v at position %d", tok.Type, tok.Pos)
22 }
23 return node, nil
24}
25
26// parser holds the state for a single parse operation.
27type parser struct {
28 tokens []token.Token
29 pos int
30}
31
32// current returns the token at the current position.
33func (p *parser) current() token.Token {
34 if p.pos >= len(p.tokens) {
35 return token.Token{Type: token.EOF}
36 }
37 return p.tokens[p.pos]
38}
39
40// advance moves to the next token and returns the previous one.
41func (p *parser) advance() token.Token {
42 tok := p.current()
43 p.pos++
44 return tok
45}
46
47// expect consumes a token of the given type or returns an error.
48func (p *parser) expect(typ token.Type) (token.Token, error) {
49 tok := p.current()
50 if tok.Type != typ {
51 return tok, fmt.Errorf("expected %v but got %v at position %d", typ, tok.Type, tok.Pos)
52 }
53 p.advance()
54 return tok, nil
55}
56
57// expr → term (('+' | '-') term)*
58func (p *parser) expr() (ast.Node, error) {
59 left, err := p.term()
60 if err != nil {
61 return nil, err
62 }
63 for p.current().Type == token.Plus || p.current().Type == token.Minus {
64 op := p.advance()
65 right, err := p.term()
66 if err != nil {
67 return nil, err
68 }
69 left = &ast.BinaryExpr{
70 Op: op.Type,
71 Left: left,
72 Right: right,
73 }
74 }
75 return left, nil
76}
77
78// term → factor (('*' | '/') factor)*
79func (p *parser) term() (ast.Node, error) {
80 left, err := p.factor()
81 if err != nil {
82 return nil, err
83 }
84 for p.current().Type == token.Star || p.current().Type == token.Slash {
85 op := p.advance()
86 right, err := p.factor()
87 if err != nil {
88 return nil, err
89 }
90 left = &ast.BinaryExpr{
91 Op: op.Type,
92 Left: left,
93 Right: right,
94 }
95 }
96 return left, nil
97}
98
99// factor → NUMBER | '(' expr ')'
100func (p *parser) factor() (ast.Node, error) {
101 tok := p.current()
102 switch tok.Type {
103 case token.Number:
104 p.advance()
105 val, err := strconv.ParseFloat(tok.Literal, 64)
106 if err != nil {
107 return nil, fmt.Errorf("invalid number %q at position %d: %w", tok.Literal, tok.Pos, err)
108 }
109 return &ast.NumberLit{Value: val}, nil
110
111 case token.LParen:
112 p.advance() // consume '('
113 node, err := p.expr()
114 if err != nil {
115 return nil, err
116 }
117 if _, err := p.expect(token.RParen); err != nil {
118 return nil, fmt.Errorf("missing closing parenthesis at position %d", p.current().Pos)
119 }
120 return node, nil
121
122 default:
123 return nil, fmt.Errorf("unexpected token %v at position %d", tok.Type, tok.Pos)
124 }
125}