Skip to content

Commit 7d722c9

Browse files
committed
feat: support for Array
1 parent d45a7f8 commit 7d722c9

File tree

3 files changed

+109
-4
lines changed

3 files changed

+109
-4
lines changed

ast.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,30 @@ type CellExpr struct {
222222
func (c *CellExpr) String() string {
223223
return fmt.Sprintf("CellExpr(%s)", c.Ident.Raw)
224224
}
225+
226+
type ArrayExpr struct {
227+
*baseNode
228+
BraceOpen *Token // The opening brace token {
229+
Elements [][]Node // [row][column] of expressions
230+
BraceClose *Token // The closing brace token }
231+
}
232+
233+
func (a *ArrayExpr) String() string {
234+
var sb strings.Builder
235+
sb.WriteString("ArrayExpr(")
236+
for i, row := range a.Elements {
237+
if i > 0 {
238+
sb.WriteString(", ")
239+
}
240+
sb.WriteString("[")
241+
for j, col := range row {
242+
if j > 0 {
243+
sb.WriteString(", ")
244+
}
245+
sb.WriteString(col.String())
246+
}
247+
sb.WriteString("]")
248+
}
249+
sb.WriteString(")")
250+
return sb.String()
251+
}

parser.go

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (p *Parser) Parse() (Node, error) {
3535
return nil, err
3636
}
3737
if p.token != nil {
38-
return nil, newParseError(p.token.Start, "unexpected token at end %s %v", p.token.Raw, p.token.Type)
38+
return nil, newParseError(p.token.Start, "unexpected token at end %s type=%v", p.token.Raw, p.token.Type)
3939
}
4040
return res, nil
4141
}
@@ -201,7 +201,7 @@ func (p *Parser) percent() (Node, error) {
201201
}
202202

203203
func (p *Parser) negation() (Node, error) {
204-
if p.token != nil && p.token.Type == Minus {
204+
if p.token != nil && (p.token.Type == Minus || p.token.Type == Plus) {
205205
var op = p.token
206206
if err := p.advance(); err != nil { // consume the operator token
207207
return nil, err
@@ -210,12 +210,22 @@ func (p *Parser) negation() (Node, error) {
210210
if err != nil {
211211
return nil, err
212212
}
213+
if lit, ok := right.(*LiteralExpr); ok && lit.Value.Type == Number {
214+
if isDigit([]rune(lit.Value.Raw)[0]) { // combine '-' / '+' with number literal
215+
lit.start = op.Start // Adjust start position to the operator
216+
lit.Value.Raw = op.Raw + lit.Value.Raw // Prepend the operator to the literal value
217+
return lit, nil
218+
}
219+
}
213220
return &UnaryExpr{
214221
baseNode: newBaseNode(op.Start, op.End),
215222
Operator: op,
216223
Operand: right,
217224
}, nil
218225
}
226+
if p.token != nil && p.token.Type == BraceOpen {
227+
return p.arrayExpr()
228+
}
219229
return p.rangeExpr()
220230
}
221231

@@ -312,7 +322,7 @@ func (p *Parser) primary() (Node, error) {
312322
switch p.token.Type {
313323
case ParenOpen:
314324
return p.parenthesized()
315-
case String, Number, BoolLiteral:
325+
case String, Number, BoolLiteral, EValue:
316326
if err := p.advance(); err != nil { // consume the token
317327
return nil, err
318328
}
@@ -384,8 +394,72 @@ func (p *Parser) primary() (Node, error) {
384394
ColAbsolute: true,
385395
}, nil
386396
default:
387-
return nil, newParseError(tk.Start, "unexpected token %s", tk.Raw)
397+
return nil, newParseError(tk.Start, "unexpected token %s (type=%v)", tk.Raw, tk.Type)
398+
}
399+
}
400+
401+
func (p *Parser) arrayExpr() (Node, error) {
402+
var braceOpen = p.token
403+
if err := p.advance(); err != nil { // consume the '{' token
404+
return nil, err
405+
}
406+
var row_index = 0
407+
var values = [][]Node{}
408+
for p.token != nil && p.token.Type != BraceClose {
409+
if row_index > 0 {
410+
// expected a semicolon as row separator
411+
if p.token.Type != Semicolon {
412+
return nil, newParseError(p.token.Start, "expected a semicolon to separate rows in array, got %s (type=%v)", p.token.Raw, p.token.Type)
413+
}
414+
if err := p.advance(); err != nil { // consume the ';' token
415+
return nil, err
416+
}
417+
if p.token == nil {
418+
break
419+
}
420+
}
421+
var col_index = 0
422+
var row_values = []Node{}
423+
for p.token != nil && (p.token.Type != Semicolon && p.token.Type != BraceClose) {
424+
if col_index > 0 {
425+
// expected a comma as col separator
426+
if p.token.Type != Comma {
427+
return nil, newParseError(p.token.Start, "expected a comma to separate columns in array, got %s (type=%v)", p.token.Raw, p.token.Type)
428+
}
429+
if err := p.advance(); err != nil { // consume the ',' token
430+
return nil, err
431+
}
432+
if p.token == nil {
433+
break
434+
}
435+
}
436+
value, err := p.negation()
437+
if err != nil {
438+
return nil, err
439+
}
440+
row_values = append(row_values, value)
441+
col_index += 1
442+
}
443+
values = append(values, row_values)
444+
row_index += 1
388445
}
446+
// expect a '}' token to close the array literal
447+
if p.token == nil {
448+
return nil, newParseError(braceOpen.Start, "unexpected end of input, expected '}' to close array literal")
449+
}
450+
if p.token.Type != BraceClose {
451+
return nil, newParseError(p.lexer.pos, "expected '}' to close array literal, got %s (type=%v)", p.token.Raw, p.token.Type)
452+
}
453+
var braceClose = p.token
454+
if err := p.advance(); err != nil { // consume the '}' token
455+
return nil, err
456+
}
457+
return &ArrayExpr{
458+
baseNode: newBaseNode(braceOpen.Start, braceClose.End),
459+
BraceOpen: braceOpen,
460+
Elements: values,
461+
BraceClose: braceClose,
462+
}, nil
389463
}
390464

391465
func (p *Parser) functionCall() (Node, error) {

parser_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ func TestParse(t *testing.T) {
77
src string
88
expected string
99
}{
10+
{"={1,2;3,4}", "ArrayExpr([LiteralExpr(Value: 1), LiteralExpr(Value: 2)], [LiteralExpr(Value: 3), LiteralExpr(Value: 4)])"},
11+
{"=-{-1}", "UnaryExpr(Operator: -, Operand: ArrayExpr([LiteralExpr(Value: -1)]))"},
12+
{"=--1", "UnaryExpr(Operator: -, Operand: LiteralExpr(Value: -1))"},
1013
{"=-A1", "UnaryExpr(Operator: -, Operand: CellExpr(A1))"},
14+
{"=\"abcd\"", "LiteralExpr(Value: \"abcd\")"},
1115
{"=10%", "UnaryExpr(Operator: %, Operand: LiteralExpr(Value: 10))"},
1216
{"=A1^B1", "BinaryExpr(Left: CellExpr(A1), Operator: ^, Right: CellExpr(B1))"},
1317
{"=A1&B1", "BinaryExpr(Left: CellExpr(A1), Operator: &, Right: CellExpr(B1))"},

0 commit comments

Comments
 (0)