diff --git a/.gitignore b/.gitignore index 3d72576..4befed3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .DS_Store -.idea \ No newline at end of file +.idea diff --git a/constructors.go b/constructors.go index f6d20e9..828ef69 100644 --- a/constructors.go +++ b/constructors.go @@ -86,6 +86,10 @@ func (t *Template) newNil(pos Pos) *NilNode { return &NilNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeNil, Pos: pos}} } +func (t *Template) newUndefined(pos Pos) *UndefinedNode { + return &UndefinedNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeUndefined, Pos: pos}} +} + func (t *Template) newField(pos Pos, ident string) *FieldNode { return &FieldNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeField, Pos: pos}, Ident: strings.Split(ident[1:], ".")} //[1:] to drop leading period } @@ -114,6 +118,18 @@ func (t *Template) newElse(pos Pos, line int) *elseNode { return &elseNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: nodeElse, Pos: pos, Line: line}} } +func (t *Template) newFilter(pos Pos, line int, set *SetNode, pipe Expression, list, elseList *ListNode) *FilterNode { + return &FilterNode{BranchNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeFilter, Pos: pos, Line: line}, Set: set, Expression: pipe, List: list, ElseList: elseList}} +} + +func (t *Template) newCase(pos Pos, line int, pipe Expression, list *ListNode) *CaseNode { + return &CaseNode{BranchNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeCase, Pos: pos, Line: line}, Expression: pipe, List: list}} +} + +func (t *Template) newSwitch(pos Pos, line int, set *SetNode, pipe Expression, list, elseList *ListNode) *SwitchNode { + return &SwitchNode{BranchNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeSwitch, Pos: pos, Line: line}, Set: set, Expression: pipe, List: list, ElseList: elseList}} +} + func (t *Template) newIf(pos Pos, line int, set *SetNode, pipe Expression, list, elseList *ListNode) *IfNode { return &IfNode{BranchNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeIf, Pos: pos, Line: line}, Set: set, Expression: pipe, List: list, ElseList: elseList}} } diff --git a/default.go b/default.go index fc26062..3669caa 100644 --- a/default.go +++ b/default.go @@ -52,6 +52,14 @@ func init() { "unsafe": reflect.ValueOf(SafeWriter(unsafePrinter)), "writeJson": reflect.ValueOf(jsonRenderer), "json": reflect.ValueOf(json.Marshal), + "default": reflect.ValueOf(Func(func(a Arguments) reflect.Value { + a.RequireNumOfArguments("default", 1, -1) + return a.Get(0) + })), + "format": reflect.ValueOf(Func(func(a Arguments) reflect.Value { + a.RequireNumOfArguments("format", 1, -1) + return a.Get(0) + })), "isset": reflect.ValueOf(Func(func(a Arguments) reflect.Value { a.RequireNumOfArguments("isset", 1, -1) for i := 0; i < len(a.argExpr); i++ { @@ -76,7 +84,7 @@ func init() { return reflect.ValueOf(expression.NumField()) } - a.Panicf("inválid value type %s in len builtin", expression.Type()) + a.Panicf("invalid value type %s in len builtin", expression.Type()) return reflect.Value{} })), "includeIfExists": reflect.ValueOf(Func(func(a Arguments) reflect.Value { diff --git a/eval.go b/eval.go index b4fe5ad..4997cf9 100644 --- a/eval.go +++ b/eval.go @@ -15,11 +15,13 @@ package jet import ( + "errors" "fmt" "io" "reflect" "runtime" "strconv" + "strings" "sync" "github.com/CloudyKit/fastprinter" @@ -237,7 +239,10 @@ func (st *Runtime) recover(err *error) { } } -func (st *Runtime) executeSet(left Expression, right reflect.Value) { +func (st *Runtime) executeSet(left Expression, right reflect.Value, isdefault bool) { + if isdefault == true && st.evalDefaultPrimaryExpression(left) == false { + return + } typ := left.Type() if typ == NodeIdentifier { st.setValue(left.(*IdentifierNode).Ident, right) @@ -277,37 +282,47 @@ RESTART: } } -func (st *Runtime) executeSetList(set *SetNode) { +func (st *Runtime) executeSetList(set *SetNode, isdefault bool) { if set.IndexExprGetLookup { value := st.evalPrimaryExpressionGroup(set.Right[0]) - st.executeSet(set.Left[0], value) + st.executeSet(set.Left[0], value, isdefault) if value.IsValid() { - st.executeSet(set.Left[1], valueBoolTRUE) + st.executeSet(set.Left[1], valueBoolTRUE, isdefault) } else { - st.executeSet(set.Left[1], valueBoolFALSE) + st.executeSet(set.Left[1], valueBoolFALSE, isdefault) } } else { for i := 0; i < len(set.Left); i++ { - st.executeSet(set.Left[i], st.evalPrimaryExpressionGroup(set.Right[i])) + st.executeSet(set.Left[i], st.evalPrimaryExpressionGroup(set.Right[i]), isdefault) } } } +func (st *Runtime) executeLet(key Expression, value reflect.Value, isdefault bool) { + if isdefault == true && st.evalDefaultPrimaryExpression(key) == false { + return + } + if st.variables == nil { + st.variables = make(VarMap) + } + st.variables[key.(*IdentifierNode).Ident] = value +} + func (st *Runtime) executeLetList(set *SetNode) { if set.IndexExprGetLookup { value := st.evalPrimaryExpressionGroup(set.Right[0]) - st.variables[set.Left[0].(*IdentifierNode).Ident] = value + st.executeLet(set.Left[0], value, false) if value.IsValid() { - st.variables[set.Left[1].(*IdentifierNode).Ident] = valueBoolTRUE + st.executeLet(set.Left[1], valueBoolTRUE, false) } else { - st.variables[set.Left[1].(*IdentifierNode).Ident] = valueBoolFALSE + st.executeLet(set.Left[1], valueBoolFALSE, false) } } else { for i := 0; i < len(set.Left); i++ { - st.variables[set.Left[i].(*IdentifierNode).Ident] = st.evalPrimaryExpressionGroup(set.Right[i]) + st.executeLet(set.Left[i], st.evalPrimaryExpressionGroup(set.Right[i]), false) } } } @@ -372,18 +387,120 @@ func (st *Runtime) executeYieldBlock(block *BlockNode, blockParam, yieldParam *B } } +type FilterType int + +const ( + FilterUndefined FilterType = iota //Plain text. + FilterFormat +) + +type TextFilter struct { + action FilterType + value string + text []byte +} + +var optionText *TextFilter = NewTextFilter() + +func NewTextFilter() *TextFilter { + var ot TextFilter + ot.Reset() + return &ot +} + +func (ot *TextFilter) isEnabled() bool { + return ot.action != FilterUndefined +} + +func (ot *TextFilter) Reset() { + ot.action = FilterUndefined + ot.value = "" + ot.text = []byte{} +} + +func (ot *TextFilter) SetText(src []byte) { + ot.text = append(ot.text, src...) +} + +func (ot *TextFilter) SetValue(value reflect.Value) { + src := value.String() + if src != "" { + ot.action = FilterFormat + ot.value = src + } +} + +func (ot *TextFilter) FormatOutput() []byte { + var value interface{} + var out []byte + var err error + + for _, line := range strings.Split(strings.TrimSuffix(string(ot.text), "\n"), "\n") { + mytext := line + line = strings.Replace(line, " ", "", -1) + line = strings.Replace(line, "\t", "", -1) + if line != "" { + if value, err = strconv.Atoi(mytext); err != nil { + value, err = strconv.ParseFloat(mytext, 64) + } + if err != nil { + value = mytext + } + out = append(out, []byte(fmt.Sprintf(ot.value, value))...) + } + out = append(out, '\n') + } + return out +} + +func (st *Runtime) executeSwitch(list *ListNode, value reflect.Value) { + var defaultNode *CaseNode = nil + var found = false + + for i := 0; i < len(list.Nodes); i++ { + node := list.Nodes[i] + switch node.Type() { + case NodeCase: + node := node.(*CaseNode) + if node.Expression.Type() == NodeUndefined { + defaultNode = node + } else { + myvalue := st.evalPrimaryExpressionGroup(node.Expression) + + left := fmt.Sprintf("%v", value) + right := fmt.Sprintf("%v", myvalue) + + if left == right { + found = true + st.executeList(node.List) + } + } + } + } + if found == false && defaultNode != nil { + st.executeList(defaultNode.List) + } +} + func (st *Runtime) executeList(list *ListNode) { inNewSCOPE := false + if list == nil { + return + } + for i := 0; i < len(list.Nodes); i++ { node := list.Nodes[i] switch node.Type() { - case NodeText: node := node.(*TextNode) - _, err := st.Writer.Write(node.Text) - if err != nil { - node.error(err) + if optionText.isEnabled() == false { + _, err := st.Writer.Write(node.Text) + if err != nil { + node.error(err) + } + } else { + optionText.SetText(node.Text) } case NodeAction: node := node.(*ActionNode) @@ -395,22 +512,52 @@ func (st *Runtime) executeList(list *ListNode) { } st.executeLetList(node.Set) } else { - st.executeSetList(node.Set) + st.executeSetList(node.Set, false) } } if node.Pipe != nil { v, safeWriter := st.evalPipelineExpression(node.Pipe) if !safeWriter && v.IsValid() { - if v.Type().Implements(rendererType) { - v.Interface().(Renderer).Render(st) - } else { - _, err := fastprinter.PrintValue(st.escapeeWriter, v) - if err != nil { - node.error(err) + if optionText.isEnabled() == false { + if v.Type().Implements(rendererType) { + v.Interface().(Renderer).Render(st) + } else { + _, err := fastprinter.PrintValue(st.escapeeWriter, v) + if err != nil { + node.error(err) + } } + } else { + tmp := []byte(fmt.Sprintf("%v", v.Interface())) + optionText.SetText(tmp) } } } + case NodeSwitch: + node := node.(*SwitchNode) + value := st.evalPrimaryExpressionGroup(node.Expression) + st.executeSwitch(node.List, value) + case NodeFilter: + node := node.(*FilterNode) + value := st.evalPrimaryExpressionGroup(node.Expression) + pos := strings.Index(node.Expression.String(), "(") + if pos <= -1 { + node.errorf("unexpected error") + } + funcname := node.Expression.String()[0:pos] + + switch funcname { + case "format": + optionText.SetValue(value) + } + + st.executeList(node.List) + out := optionText.FormatOutput() + _, err := st.Writer.Write(out) + if err != nil { + node.error(err) + } + optionText.Reset() case NodeIf: node := node.(*IfNode) var isLet bool @@ -420,7 +567,7 @@ func (st *Runtime) executeList(list *ListNode) { st.newScope() st.executeLetList(node.Set) } else { - st.executeSetList(node.Set) + st.executeSetList(node.Set, false) } } @@ -467,10 +614,10 @@ func (st *Runtime) executeList(list *ListNode) { } } else { if isKeyVal { - st.executeSet(node.Set.Left[0], indexValue) - st.executeSet(node.Set.Left[1], rangeValue) + st.executeSet(node.Set.Left[0], indexValue, false) + st.executeSet(node.Set.Left[1], rangeValue, false) } else { - st.executeSet(node.Set.Left[0], rangeValue) + st.executeSet(node.Set.Left[0], rangeValue, false) } } } else { @@ -553,6 +700,40 @@ var ( valueBoolFALSE = reflect.ValueOf(false) ) +func (st *Runtime) parseIndexExpr(baseExpression reflect.Value, indexExpression reflect.Value, indexType reflect.Type) (reflect.Value, error) { + switch baseExpression.Kind() { + case reflect.Map: + key := baseExpression.Type().Key() + if !indexType.AssignableTo(key) { + if indexType.ConvertibleTo(key) { + indexExpression = indexExpression.Convert(key) + } else { + return baseExpression, errors.New(indexType.String() + " is not assignable|convertible to map key " + key.String()) + } + } + return baseExpression.MapIndex(indexExpression), nil + case reflect.Array, reflect.String, reflect.Slice: + if canNumber(indexType.Kind()) { + index := int(castInt64(indexExpression)) + if 0 <= index && index < baseExpression.Len() { + return baseExpression.Index(index), nil + } + return baseExpression, fmt.Errorf("%s index out of range (index: %d, len: %d)", baseExpression.Kind().String(), index, baseExpression.Len()) + } + return baseExpression, errors.New("non numeric value in index expression kind " + baseExpression.Kind().String()) + case reflect.Struct: + if canNumber(indexType.Kind()) { + return baseExpression.Field(int(castInt64(indexExpression))), nil + } else if indexType.Kind() == reflect.String { + return getFieldOrMethodValue(indexExpression.String(), baseExpression), nil + } + return baseExpression, errors.New("non numeric value in index expression kind " + baseExpression.Kind().String()) + case reflect.Interface: + return st.parseIndexExpr(reflect.ValueOf(baseExpression.Interface()), indexExpression, indexType) + } + return baseExpression, errors.New("indexing is not supported in value type " + baseExpression.Kind().String()) +} + func (st *Runtime) evalPrimaryExpressionGroup(node Expression) reflect.Value { switch node.Type() { case NodeAdditiveExpr: @@ -595,39 +776,11 @@ func (st *Runtime) evalPrimaryExpressionGroup(node Expression) reflect.Value { baseExpression = baseExpression.Elem() } - switch baseExpression.Kind() { - case reflect.Map: - key := baseExpression.Type().Key() - if !indexType.AssignableTo(key) { - if indexType.ConvertibleTo(key) { - indexExpression = indexExpression.Convert(key) - } else { - node.errorf("%s is not assignable|convertible to map key %s", indexType.String(), key.String()) - } - } - return baseExpression.MapIndex(indexExpression) - case reflect.Array, reflect.String, reflect.Slice: - if canNumber(indexType.Kind()) { - index := int(castInt64(indexExpression)) - if 0 <= index && index < baseExpression.Len() { - return baseExpression.Index(index) - } else { - node.errorf("%s index out of range (index: %d, len: %d)", baseExpression.Kind().String(), index, baseExpression.Len()) - } - } else { - node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String()) - } - case reflect.Struct: - if canNumber(indexType.Kind()) { - return baseExpression.Field(int(castInt64(indexExpression))) - } else if indexType.Kind() == reflect.String { - return getFieldOrMethodValue(indexExpression.String(), baseExpression) - } else { - node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String()) - } - default: - node.errorf("indexing is not supported in value type %s", baseExpression.Kind().String()) + ret, err := st.parseIndexExpr(baseExpression, indexExpression, indexType) + if err != nil { + node.errorf(err.Error()) } + return ret case NodeSliceExpr: node := node.(*SliceExprNode) baseExpression := st.evalPrimaryExpressionGroup(node.Base) @@ -672,6 +825,40 @@ func notNil(v reflect.Value) bool { } } +func (st *Runtime) parseByType(baseExpression reflect.Value, indexExpression reflect.Value, indexType reflect.Type) (bool, error) { + switch baseExpression.Kind() { + case reflect.Map: + key := baseExpression.Type().Key() + if !indexType.AssignableTo(key) { + if indexType.ConvertibleTo(key) { + indexExpression = indexExpression.Convert(key) + } else { + return false, errors.New(indexType.String() + " is not assignable|convertible to map key " + key.String()) + } + } + return notNil(baseExpression.MapIndex(indexExpression)), nil + case reflect.Array, reflect.String, reflect.Slice: + if canNumber(indexType.Kind()) { + i := int(castInt64(indexExpression)) + return i >= 0 && i < baseExpression.Len(), nil + } else { + return false, errors.New("non numeric value in index expression kind " + baseExpression.Kind().String()) + } + case reflect.Struct: + if canNumber(indexType.Kind()) { + i := int(castInt64(indexExpression)) + return i >= 0 && i < baseExpression.NumField(), nil + } else if indexType.Kind() == reflect.String { + return notNil(getFieldOrMethodValue(indexExpression.String(), baseExpression)), nil + } else { + return false, errors.New("non numeric value in index expression kind " + baseExpression.Kind().String()) + } + case reflect.Interface: + return st.parseByType(reflect.ValueOf(baseExpression.Interface()), indexExpression, indexType) + } + return false, errors.New("indexing is not supported in value type " + baseExpression.Kind().String()) +} + func (st *Runtime) isSet(node Node) bool { nodeType := node.Type() @@ -694,39 +881,12 @@ func (st *Runtime) isSet(node Node) bool { baseExpression = baseExpression.Elem() } - switch baseExpression.Kind() { - case reflect.Map: - key := baseExpression.Type().Key() - if !indexType.AssignableTo(key) { - if indexType.ConvertibleTo(key) { - indexExpression = indexExpression.Convert(key) - } else { - node.errorf("%s is not assignable|convertible to map key %s", indexType.String(), key.String()) - } - } - value := baseExpression.MapIndex(indexExpression) - return notNil(value) - case reflect.Array, reflect.String, reflect.Slice: - if canNumber(indexType.Kind()) { - i := int(castInt64(indexExpression)) - return i >= 0 && i < baseExpression.Len() - } else { - node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String()) - } - case reflect.Struct: - if canNumber(indexType.Kind()) { - i := int(castInt64(indexExpression)) - return i >= 0 && i < baseExpression.NumField() - } else if indexType.Kind() == reflect.String { - fieldValue := getFieldOrMethodValue(indexExpression.String(), baseExpression) - return notNil(fieldValue) - - } else { - node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String()) - } - default: - node.errorf("indexing is not supported in value type %s", baseExpression.Kind().String()) + ret, err := st.parseByType(baseExpression, indexExpression, indexType) + if err != nil { + node.errorf(err.Error()) } + return ret + case NodeIdentifier: value := st.Resolve(node.String()) return notNil(value) @@ -991,86 +1151,59 @@ func (st *Runtime) evalMultiplicativeExpression(node *MultiplicativeExprNode) re return left } +func getInterfaceIntFloatAsString(src reflect.Value) (string, error) { + value := fmt.Sprintf("%v", src.Interface()) + if _, err := strconv.Atoi(value); err == nil { + return value, nil + } else if _, err := strconv.ParseFloat(value, 64); err == nil { + return value, nil + } + return "", errors.New("a non numeric value") +} + func (st *Runtime) evalAdditiveExpression(node *AdditiveExprNode) reflect.Value { isAdditive := node.Operator.typ == itemAdd if node.Left == nil { right := st.evalPrimaryExpressionGroup(node.Right) - kind := right.Kind() - // todo: optimize - // todo: - if isInt(kind) { - if isAdditive { - return reflect.ValueOf(+right.Int()) - } else { - return reflect.ValueOf(-right.Int()) - } - } else if isUint(kind) { - if isAdditive { - return right - } else { - return reflect.ValueOf(-int64(right.Uint())) - } - } else if isFloat(kind) { - if isAdditive { - return reflect.ValueOf(+right.Float()) - } else { - return reflect.ValueOf(-right.Float()) + + if rightValue, err := getInterfaceIntFloatAsString(right); err == nil { + if rightRes, err := strconv.ParseFloat(rightValue, 64); err == nil { + if isAdditive { + return reflect.ValueOf(+rightRes) + } else { + return reflect.ValueOf(-rightRes) + } } } + node.Left.errorf("a non numeric value in additive expression") } left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right) - kind := left.Kind() - // if the left value is not a float and the right is, we need to promote the left value to a float before the calculation - // this is necessary for expressions like 4+1.23 - needFloatPromotion := !isFloat(kind) && kind != reflect.String && isFloat(right.Kind()) - if needFloatPromotion { - if isInt(kind) { - if isAdditive { - left = reflect.ValueOf(float64(left.Int()) + right.Float()) - } else { - left = reflect.ValueOf(float64(left.Int()) - right.Float()) - } - } else if isUint(kind) { + leftValue, errLeft := getInterfaceIntFloatAsString(left) + rightValue, errRight := getInterfaceIntFloatAsString(right) + + if errLeft == nil && errRight == nil { + leftRes, errLeftFloat := strconv.ParseFloat(leftValue, 64) + rightRes, errRightFloat := strconv.ParseFloat(rightValue, 64) + if errLeftFloat == nil && errRightFloat == nil { if isAdditive { - left = reflect.ValueOf(float64(left.Uint()) + right.Float()) + return reflect.ValueOf(leftRes + rightRes) } else { - left = reflect.ValueOf(float64(left.Uint()) - right.Float()) + return reflect.ValueOf(leftRes - rightRes) } - } else { - node.Left.errorf("a non numeric value in additive expression") } } else { - if isInt(kind) { - if isAdditive { - left = reflect.ValueOf(left.Int() + toInt(right)) - } else { - left = reflect.ValueOf(left.Int() - toInt(right)) - } - } else if isFloat(kind) { - if isAdditive { - left = reflect.ValueOf(left.Float() + toFloat(right)) - } else { - left = reflect.ValueOf(left.Float() - toFloat(right)) - } - } else if isUint(kind) { - if isAdditive { - left = reflect.ValueOf(left.Uint() + toUint(right)) - } else { - left = reflect.ValueOf(left.Uint() - toUint(right)) - } - } else if kind == reflect.String { - if isAdditive { - left = reflect.ValueOf(left.String() + fmt.Sprint(right)) - } else { - node.Right.errorf("minus signal is not allowed with strings") - } + leftRes := fmt.Sprintf("%v", left.Interface()) + rightRes := fmt.Sprintf("%v", right.Interface()) + if isAdditive { + return reflect.ValueOf(leftRes + rightRes) } else { - node.Left.errorf("a non numeric value in additive expression") + node.Left.errorf("two strings in substraction") } } + node.Left.errorf("unhandled value in additive expression") return left } @@ -1218,8 +1351,40 @@ func (st *Runtime) evalCommandPipeExpression(node *CommandNode, value reflect.Va return term, false } +func (st *Runtime) evalDefaultPrimaryExpression(myexpr Expression) (ret bool) { + defer func() { + if r := recover(); r != nil { + ret = true + } + }() + st.evalPrimaryExpressionGroup(myexpr) + return false +} + func (st *Runtime) evalPipelineExpression(node *PipeNode) (value reflect.Value, safeWriter bool) { - value, safeWriter = st.evalCommandExpression(node.Cmds[0]) + + for i := 0; i < len(node.Cmds); i++ { + if strings.HasPrefix(node.Cmds[i].BaseExpr.String(), "default") { + if i < 1 && value.IsValid() == false { + node.errorf("wrong default order, value should be placed before") + } + if value.IsValid() == false && st.evalDefaultPrimaryExpression(node.Cmds[i-1].BaseExpr) == false { + value = st.evalPrimaryExpressionGroup(node.Cmds[i-1].BaseExpr) + node.Cmds = append(node.Cmds[:i-1], node.Cmds[i+1:]...) + } else { + if value.IsValid() == false { + value = st.evalPrimaryExpressionGroup(node.Cmds[i].BaseExpr) + } + node.Cmds = append(node.Cmds[:i], node.Cmds[i+1:]...) + } + i = 0 + } + } + + if value.IsValid() == false { + value, safeWriter = st.evalCommandExpression(node.Cmds[0]) + } + for i := 1; i < len(node.Cmds); i++ { if safeWriter { node.Cmds[i].errorf("unexpected command %s, writer command should be the last command", node.Cmds[i]) @@ -1360,10 +1525,10 @@ func checkEquality(v1, v2 reflect.Value) bool { } return true case reflect.Interface: - if v1.IsNil() || v2.IsNil() { + if kind == v2.Kind() && (v1.IsNil() || v2.IsNil()) { return v1.IsNil() == v2.IsNil() } - return checkEquality(v1.Elem(), v2.Elem()) + return checkEquality(reflect.ValueOf(v1.Interface()), reflect.ValueOf(v2.Interface())) case reflect.Ptr: return v1.Pointer() == v2.Pointer() case reflect.Struct: @@ -1425,6 +1590,8 @@ func castBoolean(v reflect.Value) bool { return true case reflect.Map, reflect.Slice, reflect.String: return v.Len() > 0 + case reflect.Interface: + return castBoolean(reflect.ValueOf(v.Interface())) default: if isInt(kind) { return v.Int() > 0 diff --git a/eval_test.go b/eval_test.go index d9dc5f7..6dfb1a7 100644 --- a/eval_test.go +++ b/eval_test.go @@ -174,7 +174,7 @@ func TestEvalActionNode(t *testing.T) { RunJetTest(t, data, nil, "actionNode_Add3Minus", `{{ 2+1+4-3 }}`, fmt.Sprint(2+1+4-3)) RunJetTest(t, data, nil, "actionNode_AddIntString", `{{ 2+"1" }}`, "3") - RunJetTest(t, data, nil, "actionNode_AddStringInt", `{{ "1"+2 }}`, "12") + RunJetTest(t, data, nil, "actionNode_AddStringInt", `{{ "1"+2 }}`, "3") RunJetTest(t, data, nil, "actionNode_NumberNegative", `{{ -5 }}`, "-5") RunJetTest(t, data, nil, "actionNode_NumberNegative_1", `{{ 1 + -5 }}`, fmt.Sprint(1+-5)) @@ -293,6 +293,16 @@ func TestEvalRangeNode(t *testing.T) { RunJetTest(t, data, nil, "Range_ExpressionValueIf", `{{range i, user:=users}}