Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed #164's bug by focusing on the operator instead of the value #166

Merged
merged 2 commits into from
Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 29 additions & 22 deletions compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,30 +451,29 @@ func (c *compiler) evalIdentifier(node *ast.Identifier) (interface{}, error) {
func (c *compiler) evalInfixExpression(node *ast.InfixExpression) (interface{}, error) {

lres, err := c.evalExpression(node.Left)
if err != nil {
if _, ok := err.(*ErrUnknownIdentifier); ok {
lres = false
} else {
return nil, err
}
}
if node.Operator == "&&" {
if !c.isTruthy(lres) {
return false, nil
}
if err != nil && node.Operator != "==" && node.Operator != "!=" {
return nil, err
} // nil lres is acceptable only for '==' and '!='

switch { // fast return
case node.Operator == "&&" && !c.isTruthy(lres):
return false, nil
case node.Operator == "||" && c.isTruthy(lres):
return true, nil
}

rres, err := c.evalExpression(node.Right)
if err != nil {
if _, ok := err.(*ErrUnknownIdentifier); !ok {
return nil, err
} else {
rres = false
}
}
if err != nil && node.Operator != "==" && node.Operator != "!=" {
return nil, err
} // nil rres is acceptable only for '==' and '!='

switch node.Operator {
case "&&", "||":
return c.boolsOperator(lres, rres, node.Operator)
return c.isTruthy(rres), nil
} // fast return or this. '&&' and '||' end here

if nil == lres || nil == rres {
return c.simpleOperator(lres, rres, node.Operator)
}

switch t := lres.(type) {
Expand All @@ -493,10 +492,7 @@ func (c *compiler) evalInfixExpression(node *ast.InfixExpression) (interface{},
return c.floatsOperator(t, r, node.Operator)
}
case bool:

return c.boolsOperator(lres, rres, node.Operator)
case nil:
return nil, nil
}
return nil, fmt.Errorf("unable to operate (%s) on %T and %T ", node.Operator, lres, rres)
}
Expand Down Expand Up @@ -605,6 +601,17 @@ func (c *compiler) stringsOperator(l string, r interface{}, op string) (interfac
return nil, fmt.Errorf("unknown operator for string %s", op)
}

func (c *compiler) simpleOperator(l interface{}, r interface{}, op string) (interface{}, error) {
switch op {
case "!=":
return l != r, nil
case "==":
return l == r, nil
default:
return nil, fmt.Errorf("unknown operator '%s' on '%T' and '%T' ", op, l, r)
}
}

func (c *compiler) evalCallExpression(node *ast.CallExpression) (interface{}, error) {
var rv reflect.Value
if node.Callee != nil {
Expand Down
15 changes: 15 additions & 0 deletions if_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,18 @@ func Test_Render_If_Variable_Not_Set_But_Or_Condition_Right_Node_Is_True(t *test
r.NoError(err)
r.Equal("hi", s)
}

func Test_Condition_UnsetIsNil(t *testing.T) {
r := require.New(t)
ctx := NewContext()

input := `<%= paths == nil %>`
s, err := Render(input, ctx)
r.NoError(err)
r.Equal("true", s)

input = `<%= nil == paths %>`
s, err = Render(input, ctx)
r.NoError(err)
r.Equal("true", s)
}
41 changes: 41 additions & 0 deletions math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,47 @@ func Test_Render_String_Math(t *testing.T) {
}
}

func Test_Render_Operator_UndefinedVar(t *testing.T) {
tests := []struct {
operator string
result interface{}
errorExpected bool
}{
{"+", "", true},
{"-", "", true},
{"/", "", true},
{"*", "", true},
{">", "", true},
{">=", "", true},
{"<=", "", true},
{"<", "", true},
{"==", "false", false},
{"!=", "true", false},
}
for _, tc := range tests {
t.Run(tc.operator, func(t *testing.T) {
r := require.New(t)
input := fmt.Sprintf("<%%= undefined %s 3 %%>", tc.operator)
s, err := Render(input, NewContext())
if tc.errorExpected {
r.Error(err, "undefined %s 3 --> '%v'", tc.operator, tc.result)
} else {
r.NoError(err, "undefined %s 3 --> '%v'", tc.operator, tc.result)
}
r.Equal(tc.result, s, "undefined %s 3", tc.operator)

input = fmt.Sprintf("<%%= 3 %s unknown %%>", tc.operator)
s, err = Render(input, NewContext())
if tc.errorExpected {
r.Error(err, "3 %s undefined --> '%v'", tc.operator, tc.result)
} else {
r.NoError(err, "3 %s undefined --> '%v'", tc.operator, tc.result)
}
r.Equal(tc.result, s, "undefined %s 3", tc.operator)
})
}
}

func Test_Render_String_Concat_Multiple(t *testing.T) {
r := require.New(t)

Expand Down