Skip to content

Commit bbc873f

Browse files
committed
Add coalesce operator and unstable flag
1 parent 4ef342c commit bbc873f

23 files changed

+606
-170
lines changed

execution/execute.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package execution
22

33
import (
4+
"errors"
45
"fmt"
6+
"reflect"
7+
"slices"
58

69
"github.com/tomwright/dasel/v3/model"
710
"github.com/tomwright/dasel/v3/selector"
@@ -64,7 +67,16 @@ func ExecuteAST(expr ast.Expr, value *model.Value, options *Options) (*model.Val
6467
return res, nil
6568
}
6669

70+
var unstableAstTypes = []reflect.Type{
71+
reflect.TypeFor[ast.BranchExpr](),
72+
}
73+
6774
func exprExecutor(opts *Options, expr ast.Expr) (expressionExecutor, error) {
75+
if !opts.Unstable && (slices.Contains(unstableAstTypes, reflect.TypeOf(expr)) ||
76+
slices.Contains(unstableAstTypes, reflect.ValueOf(expr).Type())) {
77+
return nil, errors.New("unstable ast types are not enabled. to enable them use --unstable")
78+
}
79+
6880
switch e := expr.(type) {
6981
case ast.BinaryExpr:
7082
return binaryExprExecutor(opts, e)

execution/execute_binary.go

Lines changed: 154 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,39 @@
11
package execution
22

33
import (
4+
"errors"
45
"fmt"
56

67
"github.com/tomwright/dasel/v3/model"
78
"github.com/tomwright/dasel/v3/selector/ast"
89
"github.com/tomwright/dasel/v3/selector/lexer"
910
)
1011

11-
func binaryExprExecutor(opts *Options, e ast.BinaryExpr) (expressionExecutor, error) {
12-
return func(data *model.Value) (*model.Value, error) {
13-
left, err := ExecuteAST(e.Left, data, opts)
12+
type binaryExpressionExecutorFn func(expr ast.BinaryExpr, value *model.Value, options *Options) (*model.Value, error)
13+
14+
func basicBinaryExpressionExecutorFn(handler func(left *model.Value, right *model.Value, e ast.BinaryExpr) (*model.Value, error)) binaryExpressionExecutorFn {
15+
return func(expr ast.BinaryExpr, value *model.Value, options *Options) (*model.Value, error) {
16+
left, err := ExecuteAST(expr.Left, value, options)
1417
if err != nil {
1518
return nil, fmt.Errorf("error evaluating left expression: %w", err)
1619
}
1720

18-
var doOperation func(a *model.Value, b *model.Value) (*model.Value, error)
19-
20-
switch e.Operator.Kind {
21-
case lexer.Plus:
22-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
23-
return a.Add(b)
24-
}
25-
case lexer.Dash:
26-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
27-
return a.Subtract(b)
28-
}
29-
case lexer.Star:
30-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
31-
return a.Multiply(b)
32-
}
33-
case lexer.Slash:
34-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
35-
return a.Divide(b)
36-
}
37-
case lexer.Percent:
38-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
39-
return a.Modulo(b)
40-
}
41-
case lexer.GreaterThan:
42-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
43-
return a.GreaterThan(b)
44-
}
45-
case lexer.GreaterThanOrEqual:
46-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
47-
return a.GreaterThanOrEqual(b)
48-
}
49-
case lexer.LessThan:
50-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
51-
return a.LessThan(b)
52-
}
53-
case lexer.LessThanOrEqual:
54-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
55-
return a.LessThanOrEqual(b)
56-
}
57-
case lexer.Equal:
58-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
59-
return a.Equal(b)
60-
}
61-
case lexer.NotEqual:
62-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
63-
return a.NotEqual(b)
64-
}
65-
case lexer.Equals:
66-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
67-
err := a.Set(b)
68-
if err != nil {
69-
return nil, fmt.Errorf("error setting value: %w", err)
70-
}
71-
switch a.Type() {
72-
case model.TypeMap:
73-
return a, nil
74-
case model.TypeSlice:
75-
return a, nil
76-
default:
77-
return b, nil
78-
}
79-
}
80-
case lexer.And:
81-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
82-
leftBool, err := a.BoolValue()
83-
if err != nil {
84-
return nil, fmt.Errorf("error getting left bool value: %w", err)
85-
}
86-
rightBool, err := b.BoolValue()
87-
if err != nil {
88-
return nil, fmt.Errorf("error getting right bool value: %w", err)
89-
}
90-
return model.NewBoolValue(leftBool && rightBool), nil
91-
}
92-
case lexer.Or:
93-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
94-
leftBool, err := a.BoolValue()
95-
if err != nil {
96-
return nil, fmt.Errorf("error getting left bool value: %w", err)
97-
}
98-
rightBool, err := b.BoolValue()
99-
if err != nil {
100-
return nil, fmt.Errorf("error getting right bool value: %w", err)
101-
}
102-
return model.NewBoolValue(leftBool || rightBool), nil
103-
}
104-
case lexer.Like, lexer.NotLike:
105-
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
106-
leftStr, err := a.StringValue()
107-
if err != nil {
108-
return nil, fmt.Errorf("like requires left side to be a string, got %s", left.Type().String())
109-
}
110-
rightPatt, ok := e.Right.(ast.RegexExpr)
111-
if !ok {
112-
return nil, fmt.Errorf("like requires right side to be a regex pattern")
113-
}
114-
res := rightPatt.Regex.MatchString(leftStr)
115-
if e.Operator.Kind == lexer.NotLike {
116-
res = !res
117-
}
118-
return model.NewBoolValue(res), nil
119-
}
120-
default:
121-
return nil, fmt.Errorf("unhandled operator: %s", e.Operator.Value)
122-
}
123-
124-
if doOperation == nil {
125-
return nil, fmt.Errorf("missing operation for operator %s", e.Operator.Value)
126-
}
127-
12821
if !left.IsBranch() {
129-
right, err := ExecuteAST(e.Right, data, opts)
22+
right, err := ExecuteAST(expr.Right, value, options)
13023
if err != nil {
13124
return nil, fmt.Errorf("error evaluating right expression: %w", err)
13225
}
133-
return doOperation(left, right)
26+
return handler(left, right, expr)
13427
}
13528

13629
res := model.NewSliceValue()
13730
res.MarkAsBranch()
13831
if err := left.RangeSlice(func(i int, v *model.Value) error {
139-
right, err := ExecuteAST(e.Right, v, opts)
32+
right, err := ExecuteAST(expr.Right, v, options)
14033
if err != nil {
14134
return fmt.Errorf("error evaluating right expression: %w", err)
14235
}
143-
144-
r, err := doOperation(v, right)
36+
r, err := handler(v, right, expr)
14537
if err != nil {
14638
return err
14739
}
@@ -153,5 +45,149 @@ func binaryExprExecutor(opts *Options, e ast.BinaryExpr) (expressionExecutor, er
15345
return nil, err
15446
}
15547
return res, nil
48+
}
49+
}
50+
51+
var binaryExpressionExecutors = map[lexer.TokenKind]binaryExpressionExecutorFn{}
52+
53+
func binaryExprExecutor(opts *Options, e ast.BinaryExpr) (expressionExecutor, error) {
54+
return func(data *model.Value) (*model.Value, error) {
55+
56+
exec, ok := binaryExpressionExecutors[e.Operator.Kind]
57+
if !ok {
58+
return nil, fmt.Errorf("unhandled operator: %s", e.Operator.Value)
59+
}
60+
61+
return exec(e, data, opts)
15662
}, nil
15763
}
64+
65+
func init() {
66+
binaryExpressionExecutors[lexer.Plus] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
67+
return left.Add(right)
68+
})
69+
binaryExpressionExecutors[lexer.Dash] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
70+
return left.Subtract(right)
71+
})
72+
binaryExpressionExecutors[lexer.Star] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
73+
return left.Multiply(right)
74+
})
75+
binaryExpressionExecutors[lexer.Slash] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
76+
return left.Divide(right)
77+
})
78+
binaryExpressionExecutors[lexer.Percent] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
79+
return left.Modulo(right)
80+
})
81+
binaryExpressionExecutors[lexer.GreaterThan] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
82+
return left.GreaterThan(right)
83+
})
84+
binaryExpressionExecutors[lexer.GreaterThanOrEqual] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
85+
return left.GreaterThanOrEqual(right)
86+
})
87+
binaryExpressionExecutors[lexer.LessThan] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
88+
return left.LessThan(right)
89+
})
90+
binaryExpressionExecutors[lexer.LessThanOrEqual] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
91+
return left.LessThanOrEqual(right)
92+
})
93+
binaryExpressionExecutors[lexer.Equal] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
94+
return left.Equal(right)
95+
})
96+
binaryExpressionExecutors[lexer.NotEqual] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
97+
return left.NotEqual(right)
98+
})
99+
binaryExpressionExecutors[lexer.Equals] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
100+
err := left.Set(right)
101+
if err != nil {
102+
return nil, fmt.Errorf("error setting value: %w", err)
103+
}
104+
switch left.Type() {
105+
case model.TypeMap:
106+
return left, nil
107+
case model.TypeSlice:
108+
return left, nil
109+
default:
110+
return right, nil
111+
}
112+
})
113+
binaryExpressionExecutors[lexer.And] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
114+
leftBool, err := left.BoolValue()
115+
if err != nil {
116+
return nil, fmt.Errorf("error getting left bool value: %w", err)
117+
}
118+
rightBool, err := right.BoolValue()
119+
if err != nil {
120+
return nil, fmt.Errorf("error getting right bool value: %w", err)
121+
}
122+
return model.NewBoolValue(leftBool && rightBool), nil
123+
})
124+
binaryExpressionExecutors[lexer.Or] = basicBinaryExpressionExecutorFn(func(left *model.Value, right *model.Value, _ ast.BinaryExpr) (*model.Value, error) {
125+
leftBool, err := left.BoolValue()
126+
if err != nil {
127+
return nil, fmt.Errorf("error getting left bool value: %w", err)
128+
}
129+
rightBool, err := right.BoolValue()
130+
if err != nil {
131+
return nil, fmt.Errorf("error getting right bool value: %w", err)
132+
}
133+
return model.NewBoolValue(leftBool || rightBool), nil
134+
})
135+
binaryExpressionExecutors[lexer.Like] = basicBinaryExpressionExecutorFn(func(left *model.Value, _ *model.Value, e ast.BinaryExpr) (*model.Value, error) {
136+
leftStr, err := left.StringValue()
137+
if err != nil {
138+
return nil, fmt.Errorf("like requires left side to be a string, got %s", left.Type().String())
139+
}
140+
rightPatt, ok := e.Right.(ast.RegexExpr)
141+
if !ok {
142+
return nil, fmt.Errorf("like requires right side to be a regex pattern")
143+
}
144+
res := rightPatt.Regex.MatchString(leftStr)
145+
return model.NewBoolValue(res), nil
146+
})
147+
binaryExpressionExecutors[lexer.NotLike] = basicBinaryExpressionExecutorFn(func(left *model.Value, _ *model.Value, e ast.BinaryExpr) (*model.Value, error) {
148+
leftStr, err := left.StringValue()
149+
if err != nil {
150+
return nil, fmt.Errorf("like requires left side to be a string, got %s", left.Type().String())
151+
}
152+
rightPatt, ok := e.Right.(ast.RegexExpr)
153+
if !ok {
154+
return nil, fmt.Errorf("like requires right side to be a regex pattern")
155+
}
156+
res := rightPatt.Regex.MatchString(leftStr)
157+
return model.NewBoolValue(!res), nil
158+
})
159+
binaryExpressionExecutors[lexer.DoubleQuestionMark] = func(expr ast.BinaryExpr, value *model.Value, options *Options) (*model.Value, error) {
160+
left, err := ExecuteAST(expr.Left, value, options)
161+
162+
if err == nil && !left.IsNull() {
163+
return left, nil
164+
}
165+
166+
if err != nil {
167+
handleErrs := []any{
168+
model.ErrIncompatibleTypes{},
169+
model.ErrUnexpectedType{},
170+
model.ErrUnexpectedTypes{},
171+
model.SliceIndexOutOfRange{},
172+
model.MapKeyNotFound{},
173+
}
174+
for _, e := range handleErrs {
175+
if errors.As(err, &e) {
176+
err = nil
177+
break
178+
}
179+
}
180+
181+
if err != nil {
182+
return nil, fmt.Errorf("error evaluating left expression: %w", err)
183+
}
184+
}
185+
186+
// Do we need to handle branches here?
187+
right, err := ExecuteAST(expr.Right, value, options)
188+
if err != nil {
189+
return nil, fmt.Errorf("error evaluating right expression: %w", err)
190+
}
191+
return right, nil
192+
}
193+
}

0 commit comments

Comments
 (0)