Skip to content

Commit

Permalink
Add spread selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
TomWright committed Sep 24, 2024
1 parent c9f0b5c commit 20f73ab
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 11 deletions.
2 changes: 2 additions & 0 deletions selector/lexer/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const (
Colon
OpenBracket
CloseBracket
OpenCurly
CloseCurly
OpenParen
CloseParen
Equal
Expand Down
4 changes: 4 additions & 0 deletions selector/lexer/tokenize.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ func (p *Tokenizer) parseCurRune() (Token, error) {
return NewToken(OpenParen, "(", p.i, 1), nil
case ')':
return NewToken(CloseParen, ")", p.i, 1), nil
case '{':
return NewToken(OpenCurly, "{", p.i, 1), nil
case '}':
return NewToken(CloseCurly, "}", p.i, 1), nil
case '=':
if p.peekRuneEqual(p.i+1, '=') {
return NewToken(Equal, "==", p.i, 2), nil
Expand Down
2 changes: 1 addition & 1 deletion selector/parser/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ type UnexpectedTokenError struct {
}

func (e *UnexpectedTokenError) Error() string {
return fmt.Sprintf("unexpected token: %s at position %d.", e.Token.Value, e.Token.Pos)
return fmt.Sprintf("unexpected token %v %q at position %d.", e.Token.Kind, e.Token.Value, e.Token.Pos)
}
85 changes: 80 additions & 5 deletions selector/parser/parse_symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,91 @@ import (
"github.com/tomwright/dasel/v2/selector/lexer"
)

func parseArray(p *Parser) (ast.Expr, error) {
// parseSquareBrackets parses square bracket array access.
// E.g. [0], [0:1], [0:], [:2]. [...]
func parseSquareBrackets(p *Parser) (ast.Expr, error) {
// Handle index (from bracket)
p.advance()

// todo : handle spread operator
// Spread [...]
if p.current().IsKind(lexer.Dot) && p.peekN(1).IsKind(lexer.Dot) && p.peekN(2).IsKind(lexer.Dot) {
p.advanceN(3)
if err := p.expect(p.current(), lexer.CloseBracket); err != nil {
return nil, err
}
p.advance()
return &ast.CallExpr{
Function: "all",
Args: ast.Expressions{},
}, nil
}

// Range [1:2]
if p.current().IsKind(lexer.Number) && p.peekN(1).IsKind(lexer.Colon) && p.peekN(2).IsKind(lexer.Number) {
from, err := p.parseExpression()
if err != nil {
return nil, err
}
p.advance()
to, err := p.parseExpression()
if err != nil {
return nil, err
}
if err := p.expect(p.current(), lexer.CloseBracket); err != nil {
return nil, err
}
p.advance()
return &ast.CallExpr{
Function: "range",
Args: ast.Expressions{
from, to,
},
}, nil
}

if !p.current().IsKind(lexer.Number) {
return nil, &UnexpectedTokenError{
Token: p.current(),
// Range [:2]
if p.current().IsKind(lexer.Colon) && p.peekN(1).IsKind(lexer.Number) {
from := &ast.NumberIntExpr{Value: -1}
p.advanceN(1)
to, err := p.parseExpression()
if err != nil {
return nil, err
}
if err := p.expect(p.current(), lexer.CloseBracket); err != nil {
return nil, err
}
p.advance()
return &ast.CallExpr{
Function: "range",
Args: ast.Expressions{
from, to,
},
}, nil
}

// Range [1:]
if p.current().IsKind(lexer.Number) && p.peekN(1).IsKind(lexer.Colon) {
from, err := p.parseExpression()
if err != nil {
return nil, err
}
p.advanceN(1)
to := &ast.NumberIntExpr{Value: -1}
if err := p.expect(p.current(), lexer.CloseBracket); err != nil {
return nil, err
}
p.advance()
return &ast.CallExpr{
Function: "range",
Args: ast.Expressions{
from, to,
},
}, nil
}

// Array index [1]
if err := p.expect(p.current(), lexer.Number); err != nil {
return nil, err
}
index, err := p.parseExpression()
if err != nil {
Expand Down
7 changes: 3 additions & 4 deletions selector/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,12 @@ func (p *Parser) parseExpression() (ast.Expr, error) {
case lexer.Symbol:
return parseSymbol(p)
case lexer.OpenBracket:
return parseArray(p)
return parseSquareBrackets(p)
case lexer.Bool:
return parseBoolLiteral(p)
default:
return nil, &PositionalError{
Position: p.current().Pos,
Err: fmt.Errorf("unhandled token: %v", p.current().Kind),
return nil, &UnexpectedTokenError{
Token: p.current(),
}
}
}
Expand Down
43 changes: 42 additions & 1 deletion selector/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestParser_Parse(t *testing.T) {
},
{
name: "single function with various args",
input: "all(\"foo\", 'bar', false, TRUE, 123, 12.3, hello, funcOne(), funcTwo(1, 2, 3), asd[5])",
input: "all(\"foo\", 'bar', false, TRUE, 123, 12.3, hello, funcOne(), funcTwo(1, 2, 3), asd[5], asd[...], asd[0:1], asd[2:], asd[:2])",
expected: &ast.CallExpr{
Function: "all",
Args: ast.Expressions{
Expand Down Expand Up @@ -85,6 +85,47 @@ func TestParser_Parse(t *testing.T) {
&ast.NumberIntExpr{Value: 5},
},
},
&ast.CallExpr{
Function: "property",
Args: ast.Expressions{&ast.StringExpr{Value: "asd"}},
},
&ast.CallExpr{
Function: "all",
Args: ast.Expressions{},
},
&ast.CallExpr{
Function: "property",
Args: ast.Expressions{&ast.StringExpr{Value: "asd"}},
},
&ast.CallExpr{
Function: "range",
Args: ast.Expressions{
&ast.NumberIntExpr{Value: 0},
&ast.NumberIntExpr{Value: 1},
},
},
&ast.CallExpr{
Function: "property",
Args: ast.Expressions{&ast.StringExpr{Value: "asd"}},
},
&ast.CallExpr{
Function: "range",
Args: ast.Expressions{
&ast.NumberIntExpr{Value: 2},
&ast.NumberIntExpr{Value: -1},
},
},
&ast.CallExpr{
Function: "property",
Args: ast.Expressions{&ast.StringExpr{Value: "asd"}},
},
&ast.CallExpr{
Function: "range",
Args: ast.Expressions{
&ast.NumberIntExpr{Value: -1},
&ast.NumberIntExpr{Value: 2},
},
},
},
},
},
Expand Down

0 comments on commit 20f73ab

Please sign in to comment.