Skip to content

Commit

Permalink
feat(compiler): add support for conditional exprs to compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher Wolff committed Apr 19, 2019
1 parent 7bd1ed6 commit 6fd07a0
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 0 deletions.
19 changes: 19 additions & 0 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,25 @@ func compile(n semantic.Node, typeSol semantic.TypeSolution, builtIns Scope, fun
left: l,
right: r,
}, nil
case *semantic.ConditionalExpression:
test, err := compile(n.Test, typeSol, builtIns, funcExprs)
if err != nil {
return nil, err
}
c, err := compile(n.Consequent, typeSol, builtIns, funcExprs)
if err != nil {
return nil, err
}
a, err := compile(n.Alternate, typeSol, builtIns, funcExprs)
if err != nil {
return nil, err
}
return &conditionalEvaluator{
t: monoType(typeSol.TypeOf(n.Consequent)),
test: test,
consequent: c,
alternate: a,
}, nil
case *semantic.BinaryExpression:
l, err := compile(n.Left, typeSol, builtIns, funcExprs)
if err != nil {
Expand Down
36 changes: 36 additions & 0 deletions compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,42 @@ func TestCompileAndEval(t *testing.T) {
want: values.NewBool(true),
wantErr: false,
},
{
name: "conditional",
fn: &semantic.FunctionExpression{
Block: &semantic.FunctionBlock{
Parameters: &semantic.FunctionParameters{
List: []*semantic.FunctionParameter{
{Key: &semantic.Identifier{Name: "t"}},
{Key: &semantic.Identifier{Name: "c"}},
{Key: &semantic.Identifier{Name: "a"}},
},
},
Body: &semantic.ConditionalExpression{
Test: &semantic.IdentifierExpression{
Name: "t",
},
Consequent: &semantic.IdentifierExpression{
Name: "c",
},
Alternate: &semantic.IdentifierExpression{
Name: "a",
},
},
},
},
inType: semantic.NewObjectType(map[string]semantic.Type{
"t": semantic.Bool,
"c": semantic.String,
"a": semantic.String,
}),
input: values.NewObjectWithValues(map[string]values.Value{
"t": values.NewBool(true),
"c": values.NewString("cats"),
"a": values.NewString("dogs"),
}),
want: values.NewString("cats"),
},
}

for _, tc := range testCases {
Expand Down
102 changes: 102 additions & 0 deletions compiler/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,108 @@ func (e *logicalEvaluator) EvalFunction(scope Scope) (values.Function, error) {
panic(values.UnexpectedKind(e.t.Nature(), semantic.Function))
}

type conditionalEvaluator struct {
t semantic.Type
test Evaluator
consequent Evaluator
alternate Evaluator
}

func (e *conditionalEvaluator) Type() semantic.Type {
return e.t
}

func (e *conditionalEvaluator) eval(scope Scope) (values.Value, error) {
t, err := eval(e.test, scope)
if err != nil {
return nil, err
}

if t.Bool() {
return eval(e.consequent, scope)
} else {
return eval(e.alternate, scope)
}
}

func (e *conditionalEvaluator) EvalString(scope Scope) (string, error) {
v, err := e.eval(scope)
if err != nil {
return "", err
}
return v.Str(), nil
}
func (e *conditionalEvaluator) EvalInt(scope Scope) (int64, error) {
v, err := e.eval(scope)
if err != nil {
return 0, err
}
return v.Int(), nil
}
func (e *conditionalEvaluator) EvalUInt(scope Scope) (uint64, error) {
v, err := e.eval(scope)
if err != nil {
return 0, err
}
return v.UInt(), nil
}
func (e *conditionalEvaluator) EvalFloat(scope Scope) (float64, error) {
v, err := e.eval(scope)
if err != nil {
return 0.0, err
}
return v.Float(), nil
}
func (e *conditionalEvaluator) EvalBool(scope Scope) (bool, error) {
v, err := e.eval(scope)
if err != nil {
return false, err
}
return v.Bool(), nil
}
func (e *conditionalEvaluator) EvalTime(scope Scope) (values.Time, error) {
v, err := e.eval(scope)
if err != nil {
return 0, err
}
return v.Time(), nil
}
func (e *conditionalEvaluator) EvalDuration(scope Scope) (values.Duration, error) {
v, err := e.eval(scope)
if err != nil {
return 0, err
}
return v.Duration(), nil
}
func (e *conditionalEvaluator) EvalRegexp(scope Scope) (*regexp.Regexp, error) {
v, err := e.eval(scope)
if err != nil {
return nil, err
}
return v.Regexp(), nil
}
func (e *conditionalEvaluator) EvalArray(scope Scope) (values.Array, error) {
v, err := e.eval(scope)
if err != nil {
return nil, err
}
return v.Array(), nil
}
func (e *conditionalEvaluator) EvalObject(scope Scope) (values.Object, error) {
v, err := e.eval(scope)
if err != nil {
return nil, err
}
return v.Object(), nil
}
func (e *conditionalEvaluator) EvalFunction(scope Scope) (values.Function, error) {
v, err := e.eval(scope)
if err != nil {
return nil, err
}
return v.Function(), nil
}

type binaryEvaluator struct {
t semantic.Type
left, right Evaluator
Expand Down

0 comments on commit 6fd07a0

Please sign in to comment.