Skip to content

Commit

Permalink
Get initial binary expressions working
Browse files Browse the repository at this point in the history
  • Loading branch information
TomWright committed Oct 1, 2024
1 parent daef53f commit 1657e5d
Show file tree
Hide file tree
Showing 17 changed files with 805 additions and 32 deletions.
48 changes: 47 additions & 1 deletion execution/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func exprExecutor(expr ast.Expr) (expressionExecutor, error) {
return indexExprExecutor(e)
case ast.PropertyExpr:
return propertyExprExecutor(e)
case ast.VariableExpr:
return variableExprExecutor(e)
case ast.NumberIntExpr:
return numberIntExprExecutor(e)
case ast.NumberFloatExpr:
Expand All @@ -78,7 +80,41 @@ func exprExecutor(expr ast.Expr) (expressionExecutor, error) {

func binaryExprExecutor(e ast.BinaryExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
panic("not implemented")
left, err := ExecuteAST(e.Left, data)
if err != nil {
return nil, fmt.Errorf("error evaluating left expression: %w", err)
}
right, err := ExecuteAST(e.Right, data)
if err != nil {
return nil, fmt.Errorf("error evaluating right expression: %w", err)
}

switch e.Operator.Kind {
case lexer.Plus:
return left.Add(right)
case lexer.Dash:
return left.Subtract(right)
case lexer.Star:
return left.Multiply(right)
case lexer.Slash:
return left.Divide(right)
case lexer.Percent:
return left.Modulo(right)
case lexer.GreaterThan:
return left.GreaterThan(right)
case lexer.GreaterThanOrEqual:
return left.GreaterThanOrEqual(right)
case lexer.LessThan:
return left.LessThan(right)
case lexer.LessThanOrEqual:
return left.LessThanOrEqual(right)
case lexer.Equal:
return left.Equal(right)
case lexer.NotEqual:
return left.NotEqual(right)
default:
return nil, fmt.Errorf("unhandled operator: %s", e.Operator.Value)
}
}, nil
}

Expand Down Expand Up @@ -155,3 +191,13 @@ func propertyExprExecutor(e ast.PropertyExpr) (expressionExecutor, error) {
return data.GetMapKey(keyStr)
}, nil
}

func variableExprExecutor(e ast.VariableExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
varName := e.Name
if varName == "this" {
return data, nil
}
return nil, fmt.Errorf("variable %s not found", varName)
}, nil
}
69 changes: 69 additions & 0 deletions execution/execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,75 @@ func TestExecuteSelector_HappyPath(t *testing.T) {
}
}

t.Run("binary expressions", func(t *testing.T) {
t.Run("math", func(t *testing.T) {
t.Run("literals", func(t *testing.T) {
t.Run("addition", runTest(testCase{
in: model.NewValue(nil),
s: `1 + 2`,
out: model.NewIntValue(3),
}))
t.Run("subtraction", runTest(testCase{
in: model.NewValue(nil),
s: `5 - 2`,
out: model.NewIntValue(3),
}))
t.Run("multiplication", runTest(testCase{
in: model.NewValue(nil),
s: `5 * 2`,
out: model.NewIntValue(10),
}))
t.Run("division", runTest(testCase{
in: model.NewValue(nil),
s: `10 / 2`,
out: model.NewIntValue(5),
}))
t.Run("modulus", runTest(testCase{
in: model.NewValue(nil),
s: `10 % 3`,
out: model.NewIntValue(1),
}))
t.Run("ordering", runTest(testCase{
in: model.NewValue(nil),
s: `45.2 + 5 * 4 - 2 / 2`, // 45.2 + (5 * 4) - (2 / 2) = 45.2 + 20 - 1
out: model.NewFloatValue(64.2),
}))
})
})
t.Run("comparison", func(t *testing.T) {
t.Run("equal", runTest(testCase{
in: model.NewValue(nil),
s: `1 == 1`,
out: model.NewBoolValue(true),
}))
t.Run("not equal", runTest(testCase{
in: model.NewValue(nil),
s: `1 != 1`,
out: model.NewBoolValue(false),
}))
t.Run("greater than", runTest(testCase{
in: model.NewValue(nil),
s: `2 > 1`,
out: model.NewBoolValue(true),
}))
t.Run("greater than or equal", runTest(testCase{
in: model.NewValue(nil),
s: `2 >= 2`,
out: model.NewBoolValue(true),
}))
t.Run("less than", runTest(testCase{
in: model.NewValue(nil),
s: `1 < 2`,
out: model.NewBoolValue(true),
}))
t.Run("less than or equal", runTest(testCase{
in: model.NewValue(nil),
s: `2 <= 2`,
out: model.NewBoolValue(true),
}))
})
})

t.Run("literal", func(t *testing.T) {
t.Run("string", runTest(testCase{
in: model.NewValue(nil),
Expand Down
153 changes: 153 additions & 0 deletions model/value_comparison.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package model

func (v *Value) Equal(other *Value) (*Value, error) {
if v.IsInt() && other.IsInt() {
a, err := v.IntValue()
if err != nil {
return nil, err
}
b, err := other.IntValue()
if err != nil {
return nil, err
}
return NewValue(a == b), nil
}
if v.IsFloat() && other.IsFloat() {
a, err := v.FloatValue()
if err != nil {
return nil, err
}
b, err := other.FloatValue()
if err != nil {
return nil, err
}
return NewValue(a == b), nil
}
if v.IsInt() && other.IsFloat() {
a, err := v.IntValue()
if err != nil {
return nil, err
}
b, err := other.FloatValue()
if err != nil {
return nil, err
}
return NewValue(float64(a) == b), nil
}
if v.IsFloat() && other.IsInt() {
a, err := v.FloatValue()
if err != nil {
return nil, err
}
b, err := other.IntValue()
if err != nil {
return nil, err
}
return NewValue(a == float64(b)), nil
}
return nil, &ErrIncompatibleTypes{A: v, B: other}
}

func (v *Value) NotEqual(other *Value) (*Value, error) {
equals, err := v.Equal(other)
if err != nil {
return nil, err
}
boolValue, err := equals.BoolValue()
if err != nil {
return nil, err
}
return NewValue(!boolValue), nil
}

func (v *Value) LessThan(other *Value) (*Value, error) {
if v.IsInt() && other.IsInt() {
a, err := v.IntValue()
if err != nil {
return nil, err
}
b, err := other.IntValue()
if err != nil {
return nil, err
}
return NewValue(a < b), nil
}
if v.IsFloat() && other.IsFloat() {
a, err := v.FloatValue()
if err != nil {
return nil, err
}
b, err := other.FloatValue()
if err != nil {
return nil, err
}
return NewValue(a < b), nil
}
if v.IsInt() && other.IsFloat() {
a, err := v.IntValue()
if err != nil {
return nil, err
}
b, err := other.FloatValue()
if err != nil {
return nil, err
}
return NewValue(float64(a) < b), nil
}
if v.IsFloat() && other.IsInt() {
a, err := v.FloatValue()
if err != nil {
return nil, err
}
b, err := other.IntValue()
if err != nil {
return nil, err
}
return NewValue(a < float64(b)), nil
}
return nil, &ErrIncompatibleTypes{A: v, B: other}
}

func (v *Value) LessThanOrEqual(other *Value) (*Value, error) {
lessThan, err := v.LessThan(other)
if err != nil {
return nil, err
}
boolValue, err := lessThan.BoolValue()
if err != nil {
return nil, err
}
equals, err := v.Equal(other)
if err != nil {
return nil, err
}
boolEquals, err := equals.BoolValue()
if err != nil {
return nil, err
}
return NewValue(boolValue || boolEquals), nil
}

func (v *Value) GreaterThan(other *Value) (*Value, error) {
lessThanOrEqual, err := v.LessThanOrEqual(other)
if err != nil {
return nil, err
}
boolValue, err := lessThanOrEqual.BoolValue()
if err != nil {
return nil, err
}
return NewValue(!boolValue), nil
}

func (v *Value) GreaterThanOrEqual(other *Value) (*Value, error) {
lessThan, err := v.LessThan(other)
if err != nil {
return nil, err
}
boolValue, err := lessThan.BoolValue()
if err != nil {
return nil, err
}
return NewValue(!boolValue), nil
}
Loading

0 comments on commit 1657e5d

Please sign in to comment.