package main import ( "bytes" "fmt" "log" "strconv" "strings" //"regexp" "io/ioutil" "path/filepath" "reflect" "text/template/parse" ) // TODO: Turn this file into a library var ctemplates []string var tmplPtrMap = make(map[string]interface{}) var textOverlapList = make(map[string]int) // nolint type VarItem struct { Name string Destination string Type string } type VarItemReflect struct { Name string Destination string Value reflect.Value } type CTemplateSet struct { tlist map[string]*parse.Tree dir string funcMap map[string]interface{} importMap map[string]string Fragments map[string]int FragmentCursor map[string]int FragOut string varList map[string]VarItem localVars map[string]map[string]VarItemReflect stats map[string]int pVarList string pVarPosition int previousNode parse.NodeType currentNode parse.NodeType nextNode parse.NodeType //tempVars map[string]string doImports bool expectsInt interface{} } func (c *CTemplateSet) compileTemplate(name string, dir string, expects string, expectsInt interface{}, varList map[string]VarItem) (out string, err error) { if dev.DebugMode { fmt.Println("Compiling template '" + name + "'") } c.dir = dir c.doImports = true c.funcMap = map[string]interface{}{ "and": "&&", "not": "!", "or": "||", "eq": true, "ge": true, "gt": true, "le": true, "lt": true, "ne": true, "add": true, "subtract": true, "multiply": true, "divide": true, } c.importMap = map[string]string{ "net/http": "net/http", } c.varList = varList //c.pVarList = "" //c.pVarPosition = 0 c.stats = make(map[string]int) c.expectsInt = expectsInt holdreflect := reflect.ValueOf(expectsInt) res, err := ioutil.ReadFile(dir + name) if err != nil { return "", err } content := string(res) if config.MinifyTemplates { content = minify(content) } tree := parse.New(name, c.funcMap) var treeSet = make(map[string]*parse.Tree) tree, err = tree.Parse(content, "{{", "}}", treeSet, c.funcMap) if err != nil { return "", err } if dev.TemplateDebug { fmt.Println(name) } out = "" fname := strings.TrimSuffix(name, filepath.Ext(name)) c.tlist = make(map[string]*parse.Tree) c.tlist[fname] = tree varholder := "tmpl_" + fname + "_vars" if dev.TemplateDebug { fmt.Println(c.tlist) } c.localVars = make(map[string]map[string]VarItemReflect) c.localVars[fname] = make(map[string]VarItemReflect) c.localVars[fname]["."] = VarItemReflect{".", varholder, holdreflect} if c.Fragments == nil { c.Fragments = make(map[string]int) } c.FragmentCursor = make(map[string]int) c.FragmentCursor[fname] = 0 subtree := c.tlist[fname] if dev.TemplateDebug { fmt.Println(subtree.Root) } treeLength := len(subtree.Root.Nodes) for index, node := range subtree.Root.Nodes { if dev.TemplateDebug { fmt.Println("Node: " + node.String()) } c.previousNode = c.currentNode c.currentNode = node.Type() if treeLength != (index + 1) { c.nextNode = subtree.Root.Nodes[index+1].Type() } out += c.compileSwitch(varholder, holdreflect, fname, node) } var importList string if c.doImports { for _, item := range c.importMap { importList += "import \"" + item + "\"\n" } } var varString string for _, varItem := range c.varList { varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n" } fout := "// +build !no_templategen\n\n// Code generated by Gosora. More below:\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n" fout += "package main\n" + importList + c.pVarList + "\n" fout += "// nolint\nfunc init() {\n\ttemplate_" + fname + "_handle = template_" + fname + "\n\t//o_template_" + fname + "_handle = template_" + fname + "\n\tctemplates = append(ctemplates,\"" + fname + "\")\n\ttmplPtrMap[\"" + fname + "\"] = &template_" + fname + "_handle\n\ttmplPtrMap[\"o_" + fname + "\"] = template_" + fname + "\n}\n\n" fout += "// nolint\nfunc template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w http.ResponseWriter) error {\n" + varString + out + "\treturn nil\n}\n" fout = strings.Replace(fout, `)) w.Write([]byte(`, " + ", -1) fout = strings.Replace(fout, "` + `", "", -1) //spstr := "`([:space:]*)`" //whitespace_writes := regexp.MustCompile(`(?s)w.Write\(\[\]byte\(`+spstr+`\)\)`) //fout = whitespace_writes.ReplaceAllString(fout,"") if dev.DebugMode { for index, count := range c.stats { fmt.Println(index + ": " + strconv.Itoa(count)) } fmt.Println(" ") } if dev.TemplateDebug { fmt.Println("Output!") fmt.Println(fout) } return fout, nil } func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value, templateName string, node interface{}) (out string) { if dev.TemplateDebug { fmt.Println("in compileSwitch") } switch node := node.(type) { case *parse.ActionNode: if dev.TemplateDebug { fmt.Println("Action Node") } if node.Pipe == nil { break } for _, cmd := range node.Pipe.Cmds { out += c.compileSubswitch(varholder, holdreflect, templateName, cmd) } return out case *parse.IfNode: if dev.TemplateDebug { fmt.Println("If Node:") fmt.Println("node.Pipe", node.Pipe) } var expr string for _, cmd := range node.Pipe.Cmds { if dev.TemplateDebug { fmt.Println("If Node Bit:", cmd) fmt.Println("If Node Bit Type:", reflect.ValueOf(cmd).Type().Name()) } expr += c.compileVarswitch(varholder, holdreflect, templateName, cmd) if dev.TemplateDebug { fmt.Println("If Node Expression Step:", c.compileVarswitch(varholder, holdreflect, templateName, cmd)) } } if dev.TemplateDebug { fmt.Println("If Node Expression:", expr) } c.previousNode = c.currentNode c.currentNode = parse.NodeList c.nextNode = -1 if node.ElseList == nil { if dev.TemplateDebug { fmt.Println("Selected Branch 1") } return "if " + expr + " {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.List) + "}\n" } if dev.TemplateDebug { fmt.Println("Selected Branch 2") } return "if " + expr + " {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.List) + "} else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}\n" case *parse.ListNode: if dev.TemplateDebug { fmt.Println("List Node") } for _, subnode := range node.Nodes { out += c.compileSwitch(varholder, holdreflect, templateName, subnode) } return out case *parse.RangeNode: if dev.TemplateDebug { fmt.Println("Range Node!") fmt.Println(node.Pipe) } var outVal reflect.Value for _, cmd := range node.Pipe.Cmds { if dev.TemplateDebug { fmt.Println("Range Bit:", cmd) } out, outVal = c.compileReflectswitch(varholder, holdreflect, templateName, cmd) } if dev.TemplateDebug { fmt.Println("Returned:", out) fmt.Println("Range Kind Switch!") } switch outVal.Kind() { case reflect.Map: var item reflect.Value for _, key := range outVal.MapKeys() { item = outVal.MapIndex(key) } if dev.DebugMode { fmt.Println("Range item:", item) } if !item.IsValid() { panic("item" + "^\n" + "Invalid map. Maybe, it doesn't have any entries for the template engine to analyse?") } if node.ElseList != nil { out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n} else {\n" + c.compileSwitch("item", item, templateName, node.ElseList) + "}\n" } else { out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n}" } case reflect.Slice: if outVal.Len() == 0 { panic("The sample data needs at-least one or more elements for the slices. We're looking into removing this requirement at some point!") } item := outVal.Index(0) out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n}" case reflect.Invalid: return "" } if node.ElseList != nil { out += " else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}\n" } else { out += "\n" } return out case *parse.TemplateNode: return c.compileSubtemplate(varholder, holdreflect, node) case *parse.TextNode: c.previousNode = c.currentNode c.currentNode = node.Type() c.nextNode = 0 tmpText := bytes.TrimSpace(node.Text) if len(tmpText) == 0 { return "" } //return "w.Write([]byte(`" + string(node.Text) + "`))\n" fragmentName := templateName + "_" + strconv.Itoa(c.FragmentCursor[templateName]) _, ok := c.Fragments[fragmentName] if !ok { c.Fragments[fragmentName] = len(node.Text) c.FragOut += "var " + fragmentName + " = []byte(`" + string(node.Text) + "`)\n" } c.FragmentCursor[templateName] = c.FragmentCursor[templateName] + 1 return "w.Write(" + fragmentName + ")\n" default: panic("Unknown Node in main switch") } return "" } func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string) { if dev.TemplateDebug { fmt.Println("in compileSubswitch") } firstWord := node.Args[0] switch n := firstWord.(type) { case *parse.FieldNode: if dev.TemplateDebug { fmt.Println("Field Node:", n.Ident) } /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Variable declarations are coming soon! */ cur := holdreflect var varbit string if cur.Kind() == reflect.Interface { cur = cur.Elem() varbit += ".(" + cur.Type().Name() + ")" } for _, id := range n.Ident { if dev.TemplateDebug { fmt.Println("Data Kind:", cur.Kind().String()) fmt.Println("Field Bit:", id) } if cur.Kind() == reflect.Ptr { if dev.TemplateDebug { fmt.Println("Looping over pointer") } for cur.Kind() == reflect.Ptr { cur = cur.Elem() } if dev.TemplateDebug { fmt.Println("Data Kind:", cur.Kind().String()) fmt.Println("Field Bit:", id) } } if !cur.IsValid() { if dev.DebugMode { fmt.Println("Debug Data:") fmt.Println("Holdreflect:", holdreflect) fmt.Println("Holdreflect.Kind()", holdreflect.Kind()) if !dev.TemplateDebug { fmt.Println("cur.Kind():", cur.Kind().String()) } fmt.Println("") } panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?") } cur = cur.FieldByName(id) if cur.Kind() == reflect.Interface { cur = cur.Elem() // TODO: Surely, there's a better way of detecting this? /*if cur.Kind() == reflect.String && cur.Type().Name() != "string" { varbit = "string(" + varbit + "." + id + ")"*/ //if cur.Kind() == reflect.String && cur.Type().Name() != "string" { if cur.Type().PkgPath() != "main" && cur.Type().PkgPath() != "" { c.importMap["html/template"] = "html/template" varbit += "." + id + ".(" + strings.TrimPrefix(cur.Type().PkgPath(), "html/") + "." + cur.Type().Name() + ")" } else { varbit += "." + id + ".(" + cur.Type().Name() + ")" } } else { varbit += "." + id } if dev.TemplateDebug { fmt.Println("End Cycle") } } out = c.compileVarsub(varholder+varbit, cur) for _, varItem := range c.varList { if strings.HasPrefix(out, varItem.Destination) { out = strings.Replace(out, varItem.Destination, varItem.Name, 1) } } return out case *parse.DotNode: if dev.TemplateDebug { fmt.Println("Dot Node:", node.String()) } return c.compileVarsub(varholder, holdreflect) case *parse.NilNode: panic("Nil is not a command x.x") case *parse.VariableNode: if dev.TemplateDebug { fmt.Println("Variable Node:", n.String()) fmt.Println(n.Ident) } varname, reflectVal := c.compileIfVarsub(n.String(), varholder, templateName, holdreflect) return c.compileVarsub(varname, reflectVal) case *parse.StringNode: return n.Quoted case *parse.IdentifierNode: if dev.TemplateDebug { fmt.Println("Identifier Node:", node) fmt.Println("Identifier Node Args:", node.Args) } return c.compileVarsub(c.compileIdentswitch(varholder, holdreflect, templateName, node)) default: fmt.Println("Unknown Kind:", reflect.ValueOf(firstWord).Elem().Kind()) fmt.Println("Unknown Type:", reflect.ValueOf(firstWord).Elem().Type().Name()) panic("I don't know what node this is") } } func (c *CTemplateSet) compileVarswitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string) { if dev.TemplateDebug { fmt.Println("in compile_varswitch") } firstWord := node.Args[0] switch n := firstWord.(type) { case *parse.FieldNode: if dev.TemplateDebug { fmt.Println("Field Node:", n.Ident) for _, id := range n.Ident { fmt.Println("Field Bit:", id) } } /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ return c.compileBoolsub(n.String(), varholder, templateName, holdreflect) case *parse.ChainNode: if dev.TemplateDebug { fmt.Println("Chain Node:", n.Node) fmt.Println("Chain Node Args:", node.Args) } break case *parse.IdentifierNode: if dev.TemplateDebug { fmt.Println("Identifier Node:", node) fmt.Println("Identifier Node Args:", node.Args) } return c.compileIdentswitchN(varholder, holdreflect, templateName, node) case *parse.DotNode: return varholder case *parse.VariableNode: if dev.TemplateDebug { fmt.Println("Variable Node:", n.String()) fmt.Println("Variable Node Identifier:", n.Ident) } out, _ = c.compileIfVarsub(n.String(), varholder, templateName, holdreflect) return out case *parse.NilNode: panic("Nil is not a command x.x") case *parse.PipeNode: if dev.TemplateDebug { fmt.Println("Pipe Node!") fmt.Println(n) fmt.Println("Args:", node.Args) } out += c.compileIdentswitchN(varholder, holdreflect, templateName, node) if dev.TemplateDebug { fmt.Println("Out:", out) } return out default: fmt.Println("Unknown Kind:", reflect.ValueOf(firstWord).Elem().Kind()) fmt.Println("Unknown Type:", reflect.ValueOf(firstWord).Elem().Type().Name()) panic("I don't know what node this is! Grr...") } return "" } func (c *CTemplateSet) compileIdentswitchN(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string) { if dev.TemplateDebug { fmt.Println("in compile_identswitch_n") } out, _ = c.compileIdentswitch(varholder, holdreflect, templateName, node) return out } func (c *CTemplateSet) compileIdentswitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string, val reflect.Value) { if dev.TemplateDebug { fmt.Println("in compileIdentswitch") } //var outbuf map[int]string ArgLoop: for pos := 0; pos < len(node.Args); pos++ { id := node.Args[pos] if dev.TemplateDebug { fmt.Println("pos:", pos) fmt.Println("ID:", id) } switch id.String() { case "not": out += "!" case "or": if dev.TemplateDebug { fmt.Println("Building or function") } if pos == 0 { fmt.Println("pos:", pos) panic("or is missing a left operand") } if len(node.Args) <= pos { fmt.Println("post pos:", pos) fmt.Println("len(node.Args):", len(node.Args)) panic("or is missing a right operand") } left := c.compileBoolsub(node.Args[pos-1].String(), varholder, templateName, holdreflect) _, funcExists := c.funcMap[node.Args[pos+1].String()] var right string if !funcExists { right = c.compileBoolsub(node.Args[pos+1].String(), varholder, templateName, holdreflect) } out += left + " || " + right if dev.TemplateDebug { fmt.Println("Left operand:", node.Args[pos-1]) fmt.Println("Right operand:", node.Args[pos+1]) } if !funcExists { pos++ } if dev.TemplateDebug { fmt.Println("pos:", pos) fmt.Println("len(node.Args):", len(node.Args)) } case "and": if dev.TemplateDebug { fmt.Println("Building and function") } if pos == 0 { fmt.Println("pos:", pos) panic("and is missing a left operand") } if len(node.Args) <= pos { fmt.Println("post pos:", pos) fmt.Println("len(node.Args):", len(node.Args)) panic("and is missing a right operand") } left := c.compileBoolsub(node.Args[pos-1].String(), varholder, templateName, holdreflect) _, funcExists := c.funcMap[node.Args[pos+1].String()] var right string if !funcExists { right = c.compileBoolsub(node.Args[pos+1].String(), varholder, templateName, holdreflect) } out += left + " && " + right if dev.TemplateDebug { fmt.Println("Left operand:", node.Args[pos-1]) fmt.Println("Right operand:", node.Args[pos+1]) } if !funcExists { pos++ } if dev.TemplateDebug { fmt.Println("pos:", pos) fmt.Println("len(node.Args):", len(node.Args)) } case "le": out += c.compileIfVarsubN(node.Args[pos+1].String(), varholder, templateName, holdreflect) + " <= " + c.compileIfVarsubN(node.Args[pos+2].String(), varholder, templateName, holdreflect) if dev.TemplateDebug { fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "lt": out += c.compileIfVarsubN(node.Args[pos+1].String(), varholder, templateName, holdreflect) + " < " + c.compileIfVarsubN(node.Args[pos+2].String(), varholder, templateName, holdreflect) if dev.TemplateDebug { fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "gt": out += c.compileIfVarsubN(node.Args[pos+1].String(), varholder, templateName, holdreflect) + " > " + c.compileIfVarsubN(node.Args[pos+2].String(), varholder, templateName, holdreflect) if dev.TemplateDebug { fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "ge": out += c.compileIfVarsubN(node.Args[pos+1].String(), varholder, templateName, holdreflect) + " >= " + c.compileIfVarsubN(node.Args[pos+2].String(), varholder, templateName, holdreflect) if dev.TemplateDebug { fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "eq": out += c.compileIfVarsubN(node.Args[pos+1].String(), varholder, templateName, holdreflect) + " == " + c.compileIfVarsubN(node.Args[pos+2].String(), varholder, templateName, holdreflect) if dev.TemplateDebug { fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "ne": out += c.compileIfVarsubN(node.Args[pos+1].String(), varholder, templateName, holdreflect) + " != " + c.compileIfVarsubN(node.Args[pos+2].String(), varholder, templateName, holdreflect) if dev.TemplateDebug { fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "add": param1, val2 := c.compileIfVarsub(node.Args[pos+1].String(), varholder, templateName, holdreflect) param2, val3 := c.compileIfVarsub(node.Args[pos+2].String(), varholder, templateName, holdreflect) if val2.IsValid() { val = val2 } else if val3.IsValid() { val = val3 } else { numSample := 1 val = reflect.ValueOf(numSample) } out += param1 + " + " + param2 if dev.TemplateDebug { fmt.Println("add") fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "subtract": param1, val2 := c.compileIfVarsub(node.Args[pos+1].String(), varholder, templateName, holdreflect) param2, val3 := c.compileIfVarsub(node.Args[pos+2].String(), varholder, templateName, holdreflect) if val2.IsValid() { val = val2 } else if val3.IsValid() { val = val3 } else { numSample := 1 val = reflect.ValueOf(numSample) } out += param1 + " - " + param2 if dev.TemplateDebug { fmt.Println("subtract") fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "divide": param1, val2 := c.compileIfVarsub(node.Args[pos+1].String(), varholder, templateName, holdreflect) param2, val3 := c.compileIfVarsub(node.Args[pos+2].String(), varholder, templateName, holdreflect) if val2.IsValid() { val = val2 } else if val3.IsValid() { val = val3 } else { numSample := 1 val = reflect.ValueOf(numSample) } out += param1 + " / " + param2 if dev.TemplateDebug { fmt.Println("divide") fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop case "multiply": param1, val2 := c.compileIfVarsub(node.Args[pos+1].String(), varholder, templateName, holdreflect) param2, val3 := c.compileIfVarsub(node.Args[pos+2].String(), varholder, templateName, holdreflect) if val2.IsValid() { val = val2 } else if val3.IsValid() { val = val3 } else { numSample := 1 val = reflect.ValueOf(numSample) } out += param1 + " * " + param2 if dev.TemplateDebug { fmt.Println("multiply") fmt.Println("node.Args[pos + 1]", node.Args[pos+1]) fmt.Println("node.Args[pos + 2]", node.Args[pos+2]) } break ArgLoop default: if dev.TemplateDebug { fmt.Println("Variable!") } if len(node.Args) > (pos + 1) { nextNode := node.Args[pos+1].String() if nextNode == "or" || nextNode == "and" { continue } } out += c.compileIfVarsubN(id.String(), varholder, templateName, holdreflect) } } //for _, outval := range outbuf { // out += outval //} return out, val } func (c *CTemplateSet) compileReflectswitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string, outVal reflect.Value) { if dev.TemplateDebug { fmt.Println("in compileReflectswitch") } firstWord := node.Args[0] switch n := firstWord.(type) { case *parse.FieldNode: if dev.TemplateDebug { fmt.Println("Field Node:", n.Ident) for _, id := range n.Ident { fmt.Println("Field Bit:", id) } } /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ return c.compileIfVarsub(n.String(), varholder, templateName, holdreflect) case *parse.ChainNode: if dev.TemplateDebug { fmt.Println("Chain Node:", n.Node) fmt.Println("node.Args", node.Args) } return "", outVal case *parse.DotNode: return varholder, holdreflect case *parse.NilNode: panic("Nil is not a command x.x") default: //panic("I don't know what node this is") } return "", outVal } func (c *CTemplateSet) compileIfVarsubN(varname string, varholder string, templateName string, cur reflect.Value) (out string) { if dev.TemplateDebug { fmt.Println("in compileIfVarsubN") } out, _ = c.compileIfVarsub(varname, varholder, templateName, cur) return out } func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templateName string, cur reflect.Value) (out string, val reflect.Value) { if dev.TemplateDebug { fmt.Println("in compileIfVarsub") } if varname[0] != '.' && varname[0] != '$' { return varname, cur } bits := strings.Split(varname, ".") if varname[0] == '$' { var res VarItemReflect if varname[1] == '.' { res = c.localVars[templateName]["."] } else { res = c.localVars[templateName][strings.TrimPrefix(bits[0], "$")] } out += res.Destination cur = res.Value if cur.Kind() == reflect.Interface { cur = cur.Elem() } } else { if cur.Kind() == reflect.Interface { cur = cur.Elem() out += varholder + ".(" + cur.Type().Name() + ")" } else { out += varholder } } bits[0] = strings.TrimPrefix(bits[0], "$") if dev.TemplateDebug { fmt.Println("Cur Kind:", cur.Kind()) fmt.Println("Cur Type:", cur.Type().Name()) } for _, bit := range bits { if dev.TemplateDebug { fmt.Println("Variable Field:", bit) } if bit == "" { continue } // TODO: Fix this up so that it works for regular pointers and not just struct pointers. Ditto for the other cur.Kind() == reflect.Ptr we have in this file if cur.Kind() == reflect.Ptr { if dev.TemplateDebug { fmt.Println("Looping over pointer") } for cur.Kind() == reflect.Ptr { cur = cur.Elem() } if dev.TemplateDebug { fmt.Println("Data Kind:", cur.Kind().String()) fmt.Println("Field Bit:", bit) } } cur = cur.FieldByName(bit) if cur.Kind() == reflect.Interface { cur = cur.Elem() out += "." + bit + ".(" + cur.Type().Name() + ")" } else { out += "." + bit } if !cur.IsValid() { panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?") } if dev.TemplateDebug { fmt.Println("Data Kind:", cur.Kind()) fmt.Println("Data Type:", cur.Type().Name()) } } if dev.TemplateDebug { fmt.Println("Out Value:", out) fmt.Println("Out Kind:", cur.Kind()) fmt.Println("Out Type:", cur.Type().Name()) } for _, varItem := range c.varList { if strings.HasPrefix(out, varItem.Destination) { out = strings.Replace(out, varItem.Destination, varItem.Name, 1) } } if dev.TemplateDebug { fmt.Println("Out Value:", out) fmt.Println("Out Kind:", cur.Kind()) fmt.Println("Out Type:", cur.Type().Name()) } _, ok := c.stats[out] if ok { c.stats[out]++ } else { c.stats[out] = 1 } return out, cur } func (c *CTemplateSet) compileBoolsub(varname string, varholder string, templateName string, val reflect.Value) string { if dev.TemplateDebug { fmt.Println("in compileBoolsub") } out, val := c.compileIfVarsub(varname, varholder, templateName, val) // TODO: What if it's a pointer or an interface? I *think* we've got pointers handled somewhere, but not interfaces which we don't know the types of at compile time switch val.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: out += " > 0" case reflect.Bool: // Do nothing case reflect.String: out += " != \"\"" case reflect.Slice, reflect.Map: out = "len(" + out + ") != 0" default: fmt.Println("Variable Name:", varname) fmt.Println("Variable Holder:", varholder) fmt.Println("Variable Kind:", val.Kind()) panic("I don't know what this variable's type is o.o\n") } return out } func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value) string { if dev.TemplateDebug { fmt.Println("in compileVarsub") } for _, varItem := range c.varList { if strings.HasPrefix(varname, varItem.Destination) { varname = strings.Replace(varname, varItem.Destination, varItem.Name, 1) } } _, ok := c.stats[varname] if ok { c.stats[varname]++ } else { c.stats[varname] = 1 } if val.Kind() == reflect.Interface { val = val.Elem() } switch val.Kind() { case reflect.Int: c.importMap["strconv"] = "strconv" return "w.Write([]byte(strconv.Itoa(" + varname + ")))\n" case reflect.Bool: return "if " + varname + " {\nw.Write([]byte(\"true\"))} else {\nw.Write([]byte(\"false\"))\n}\n" case reflect.String: if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") { return "w.Write([]byte(string(" + varname + ")))\n" } return "w.Write([]byte(" + varname + "))\n" case reflect.Int64: c.importMap["strconv"] = "strconv" return "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))" default: if !val.IsValid() { panic(varname + "^\n" + "Invalid value. Maybe, it doesn't exist?") } fmt.Println("Unknown Variable Name:", varname) fmt.Println("Unknown Kind:", val.Kind()) fmt.Println("Unknown Type:", val.Type().Name()) panic("// I don't know what this variable's type is o.o\n") } } func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) { if dev.TemplateDebug { fmt.Println("in compileSubtemplate") fmt.Println("Template Node:", node.Name) } fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name)) varholder := "tmpl_" + fname + "_vars" var holdreflect reflect.Value if node.Pipe != nil { for _, cmd := range node.Pipe.Cmds { firstWord := cmd.Args[0] switch firstWord.(type) { case *parse.DotNode: varholder = pvarholder holdreflect = pholdreflect break case *parse.NilNode: panic("Nil is not a command x.x") default: out = "var " + varholder + " := false\n" out += c.compileCommand(cmd) } } } // TODO: Cascade errors back up the tree to the caller? res, err := ioutil.ReadFile(c.dir + node.Name) if err != nil { log.Fatal(err) } content := string(res) if config.MinifyTemplates { content = minify(content) } tree := parse.New(node.Name, c.funcMap) var treeSet = make(map[string]*parse.Tree) tree, err = tree.Parse(content, "{{", "}}", treeSet, c.funcMap) if err != nil { log.Fatal(err) } c.tlist[fname] = tree subtree := c.tlist[fname] if dev.TemplateDebug { fmt.Println("subtree.Root", subtree.Root) } c.localVars[fname] = make(map[string]VarItemReflect) c.localVars[fname]["."] = VarItemReflect{".", varholder, holdreflect} c.FragmentCursor[fname] = 0 treeLength := len(subtree.Root.Nodes) for index, node := range subtree.Root.Nodes { if dev.TemplateDebug { fmt.Println("Node:", node.String()) } c.previousNode = c.currentNode c.currentNode = node.Type() if treeLength != (index + 1) { c.nextNode = subtree.Root.Nodes[index+1].Type() } out += c.compileSwitch(varholder, holdreflect, fname, node) } return out } func (c *CTemplateSet) compileCommand(*parse.CommandNode) (out string) { panic("Uh oh! Something went wrong!") } // TODO: Write unit tests for this func minify(data string) string { data = strings.Replace(data, "\t", "", -1) data = strings.Replace(data, "\v", "", -1) data = strings.Replace(data, "\n", "", -1) data = strings.Replace(data, "\r", "", -1) data = strings.Replace(data, " ", " ", -1) return data } // TODO: Strip comments // TODO: Handle CSS nested in