Skip to content

Commit

Permalink
Merge pull request #15 from CloudyKit/develop
Browse files Browse the repository at this point in the history
Merge develop branch
  • Loading branch information
jhsx committed Jul 11, 2016
2 parents 6f38150 + 3569bd8 commit 09277d7
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 165 deletions.
2 changes: 1 addition & 1 deletion constructors.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (t *Template) newYield(pos Pos, line int, name string, pipe Expression) *Yi
return &YieldNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeYield, Pos: pos, Line: line}, Name: name, Expression: pipe}
}

func (t *Template) newInclude(pos Pos, line int, name string, pipe Expression) *IncludeNode {
func (t *Template) newInclude(pos Pos, line int, name, pipe Expression) *IncludeNode {
return &IncludeNode{NodeBase: NodeBase{TemplateName: t.Name, NodeType: NodeInclude, Pos: pos, Line: line}, Name: name, Expression: pipe}
}

Expand Down
133 changes: 103 additions & 30 deletions eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
)

var (
stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
rangerType = reflect.TypeOf((*Ranger)(nil)).Elem()
rendererType = reflect.TypeOf((*Renderer)(nil)).Elem()
safeWriterType = reflect.TypeOf(SafeWriter(nil))
Expand Down Expand Up @@ -281,7 +282,7 @@ func (st *Runtime) executeList(list *ListNode) {
}
if node.Pipe != nil {
v, safeWriter := st.evalPipelineExpression(node.Pipe)
if !safeWriter {
if !safeWriter && v.IsValid() {
if v.Type().Implements(rendererType) {
v.Interface().(Renderer).Render(st)
} else {
Expand Down Expand Up @@ -399,7 +400,18 @@ func (st *Runtime) executeList(list *ListNode) {
}
case NodeInclude:
node := node.(*IncludeNode)
t, exists := st.set.getTemplate(node.Name)
var Name string

name := st.evalPrimaryExpressionGroup(node.Name)
if name.Type().Implements(stringerType) {
Name = name.String()
} else if name.Kind() == reflect.String {
Name = name.String()
} else {
node.errorf("unexpected expression type %q in template yielding", getTypeString(name))
}

t, exists := st.set.getTemplate(Name)
if !exists {
node.errorf("template %q was not found!!", node.Name)
} else {
Expand Down Expand Up @@ -477,6 +489,7 @@ func (st *Runtime) evalPrimaryExpressionGroup(node Expression) reflect.Value {
node.errorf("inválid value type %s in len builtin", expression.Type())
case NodeIndexExpr:
node := node.(*IndexExprNode)

baseExpression := st.evalPrimaryExpressionGroup(node.Base)
indexExpression := st.evalPrimaryExpressionGroup(node.Index)
indexType := indexExpression.Type()
Expand Down Expand Up @@ -543,41 +556,101 @@ func (st *Runtime) evalPrimaryExpressionGroup(node Expression) reflect.Value {
case NodeIssetExpr:
node := node.(*BuiltinExprNode)
for i := 0; i < len(node.Args); i++ {
switch node.Args[i].Type() {
case NodeIdentifier:
if st.Resolve(node.Args[i].String()).IsValid() == false {
return valueBoolFALSE
}
case NodeField:
node := node.Args[i].(*FieldNode)
resolved := st.context
for i := 0; i < len(node.Ident); i++ {
resolved = getValue(node.Ident[i], resolved)
if !resolved.IsValid() {
return valueBoolFALSE
}
}
case NodeChain:
node := node.Args[i].(*ChainNode)
var value = st.evalPrimaryExpressionGroup(node.Node)
if !value.IsValid() {
return valueBoolFALSE
}
for i := 0; i < len(node.Field); i++ {
value := getValue(node.Field[i], value)
if !value.IsValid() {
return valueBoolFALSE
}
}
default:
node.Args[i].errorf("unexpected %q node in isset clause", node.Args[i])
if !st.Isset(node.Args[i]) {
return valueBoolFALSE
}
}
return valueBoolTRUE
}
return st.evalBaseExpressionGroup(node)
}

func (st *Runtime) Isset(node Node) bool {
nodeType := node.Type()

switch nodeType {
case NodeIndexExpr:
node := node.(*IndexExprNode)
if !st.Isset(node.Base) {
return false
}

if !st.Isset(node.Index) {
return false
}

baseExpression := st.evalPrimaryExpressionGroup(node.Base)
indexExpression := st.evalPrimaryExpressionGroup(node.Index)

indexType := indexExpression.Type()
if baseExpression.Kind() == reflect.Ptr {
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).IsValid()
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 {
return getValue(indexExpression.String(), baseExpression).IsValid()
} 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())
}
case NodeIdentifier:
if st.Resolve(node.String()).IsValid() == false {
return false
}
case NodeField:
node := node.(*FieldNode)
resolved := st.context
for i := 0; i < len(node.Ident); i++ {
resolved = getValue(node.Ident[i], resolved)
if !resolved.IsValid() {
return false
}
}
case NodeChain:
node := node.(*ChainNode)
var value = st.evalPrimaryExpressionGroup(node.Node)
if !value.IsValid() {
return false
}
for i := 0; i < len(node.Field); i++ {
value := getValue(node.Field[i], value)
if !value.IsValid() {
return false
}
}
default:
//todo: maybe work some edge cases
if !(nodeType > beginExpressions && nodeType < endExpressions) {
node.errorf("unexpected %q node in isset clause", node)
}
}
return true
}

func (st *Runtime) evalNumericComparativeExpression(node *NumericComparativeExprNode) reflect.Value {
left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right)
isTrue := false
Expand Down
9 changes: 8 additions & 1 deletion eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,22 @@ var evalTemplateSet = NewSet()

func evalTestCase(t *testing.T, variables VarMap, context interface{}, testName, testContent, testExpected string) {
buff := bytes.NewBuffer(nil)

tt, err := evalTemplateSet.loadTemplate(testName, testContent)
if err != nil {
t.Errorf("Parsing error: %s %s %s", err.Error(), testName, testContent)
return
}

err = tt.Execute(buff, variables, context)
if err != nil {
t.Errorf("Eval error: %q executing %s", err.Error(), testName)
return
}

result := buff.String()
if result != testExpected {
t.Errorf("Result error: expected %q got %q on %s", testExpected, result, testName)
t.Errorf("Result error: %q expected, got %q on %s", testExpected, result, testName)
}
}

Expand Down Expand Up @@ -195,6 +198,10 @@ func TestEvalIssetAndTernaryExpression(t *testing.T) {
func TestEvalIndexExpression(t *testing.T) {
evalTestCase(t, nil, []string{"111", "222"}, "IndexExpressionSlice_1", `{{.[1]}}`, `222`)
evalTestCase(t, nil, map[string]string{"name": "value"}, "IndexExpressionMap_1", `{{.["name"]}}`, "value")
evalTestCase(t, nil, map[string]string{"name": "value"}, "IndexExpressionMap_2", `{{.["non_existant_key"]}}`, "")
evalTestCase(t, nil, map[string]string{"name": "value"}, "IndexExpressionMap_3", `{{isset(.["non_existant_key"]) ? "key does exist" : "key does not exist"}}`, "key does not exist")
//evalTestCase(t, nil, map[string]string{"name": "value"}, "IndexExpressionMap_4", `{{if v, ok := .["name"]; ok}}key does exist and has the value '{{v}}'{{else}}key does not exist{{end}}`, "key does exist and has the value 'value'")
//evalTestCase(t, nil, map[string]string{"name": "value"}, "IndexExpressionMap_5", `{{if v, ok := .["non_existant_key"]; ok}}key does exist and has the value '{{v}}'{{else}}key does not exist{{end}}`, "key does not exist")
evalTestCase(t, nil, &User{"José Santos", "[email protected]"}, "IndexExpressionStruct_1", `{{.[0]}}`, "José Santos")
evalTestCase(t, nil, &User{"José Santos", "[email protected]"}, "IndexExpressionStruct_2", `{{.["Email"]}}`, "[email protected]")
}
Expand Down
Loading

0 comments on commit 09277d7

Please sign in to comment.