func txSaveWriter(st *State) { st.StackPush(st.output) buf := rbpool.Get() st.StackPush(buf) st.output = bufio.NewWriter(buf) st.Advance() }
// MarshalBinary is used to serialize an Op into a binary form. This // is used to cache the ByteCode func (o op) MarshalBinary() ([]byte, error) { buf := rbpool.Get() defer rbpool.Release(buf) // Write the code/opcode if err := binary.Write(buf, binary.LittleEndian, int64(o.OpType)); err != nil { return nil, errors.Wrap(err, "failed to marshal op to binary") } // If this has args, we need to encode the args tArg := reflect.TypeOf(o.uArg) hasArg := tArg != nil if hasArg { binary.Write(buf, binary.LittleEndian, int8(1)) } else { binary.Write(buf, binary.LittleEndian, int8(0)) } if hasArg { switch tArg.Kind() { case reflect.Int: binary.Write(buf, binary.LittleEndian, int64(2)) binary.Write(buf, binary.LittleEndian, int64(o.uArg.(int))) case reflect.Int64: binary.Write(buf, binary.LittleEndian, int64(2)) binary.Write(buf, binary.LittleEndian, int64(o.uArg.(int64))) case reflect.Slice: if tArg.Elem().Kind() != reflect.Uint8 { panic("Slice of what?") } binary.Write(buf, binary.LittleEndian, int64(5)) binary.Write(buf, binary.LittleEndian, int64(len(o.uArg.([]byte)))) for _, v := range o.uArg.([]byte) { binary.Write(buf, binary.LittleEndian, v) } case reflect.String: binary.Write(buf, binary.LittleEndian, int64(6)) binary.Write(buf, binary.LittleEndian, int64(len(o.uArg.(string)))) for _, v := range []byte(o.uArg.(string)) { binary.Write(buf, binary.LittleEndian, v) } default: panic("Unknown type " + tArg.String()) } } v := o.comment hasComment := v != "" if hasComment { binary.Write(buf, binary.LittleEndian, int8(1)) binary.Write(buf, binary.LittleEndian, v) } else { binary.Write(buf, binary.LittleEndian, int8(0)) } return buf.Bytes(), nil }
// Render loads the template specified by the given name string. // By default Xslate looks for files in the local file system, and caches // the generated bytecode too. // // If you wish to, for example, load the templates from a database, you can // change the generated loader object by providing a `ConfigureLoader` // parameter in the xslate.New() function: // // xslate.New(Args { // "ConfigureLoader": func(tx *Xslate, args Args) { // tx.Loader = .... // your custom loader // }, // }) // // `Render()` returns the resulting text from processing the template. // `err` is nil on success, otherwise it contains an `error` value. func (tx Xslate) Render(name string, vars Vars) (string, error) { buf := rbpool.Get() defer rbpool.Release(buf) err := tx.RenderInto(buf, name, vars) if err != nil { return "", errors.Wrap(err, "failed to render template") } return buf.String(), nil }
// String returns the textual representation of this AST func (ast *AST) String() string { buf := rbpool.Get() defer rbpool.Release(buf) c := ast.Visit() k := 0 for v := range c { k++ fmt.Fprintf(buf, "%03d. %s\n", k, v) } return buf.String() }
// RenderString takes a string argument and treats it as the template // content. Like `Render()`, this template is parsed and compiled. Because // there's no way to establish template "freshness", the resulting bytecode // from `RenderString()` is not cached for reuse. // // If you *really* want to change this behavior, it's not impossible to // bend Xslate's Loader mechanism to cache strings as well, but the main // Xslate library will probably not adopt this feature. func (tx *Xslate) RenderString(template string, vars Vars) (string, error) { _, file, line, _ := runtime.Caller(1) bc, err := tx.Loader.LoadString(fmt.Sprintf("%s:%d", file, line), template) if err != nil { return "", errors.Wrap(err, "failed to parse template string") } buf := rbpool.Get() defer rbpool.Release(buf) tx.VM.Run(bc, vm.Vars(vars), buf) return buf.String(), nil }
// String returns the textual representation of this ByteCode func (b *ByteCode) String() string { buf := rbpool.Get() defer rbpool.Release(buf) fmt.Fprintf(buf, "// Bytecode for '%s'\n// Generated On: %s\n", b.Name, b.GeneratedOn, ) for k, v := range b.OpList { fmt.Fprintf(buf, "%03d. %s\n", k+1, v) } return buf.String() }
func txInclude(st *State) { // st.sa should contain the include target // st.sb should contain the map[interface{}]interface{} // object that gets passed to the included template vars := Vars(rvpool.Get()) defer rvpool.Release(vars) defer vars.Reset() if x := st.Vars(); x != nil { for k, v := range x { vars.Set(k, v) } } if x := st.sb; x != nil { hash := x.(map[interface{}]interface{}) // Need to covert this to Vars (map[string]interface{}) for k, v := range hash { vars.Set(interfaceToString(k), v) } } target := interfaceToString(st.sa) bc, err := st.LoadByteCode(target) if err != nil { panic(fmt.Sprintf("Include: Failed to compile %s: %s", target, err)) } buf := rbpool.Get() defer rbpool.Release(buf) vm := NewVM() vm.Run(bc, vars, buf) st.AppendOutputString(buf.String()) st.Advance() }