Exemple #1
0
// 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
}
Exemple #2
0
func txRestoreWriter(st *State) {
	st.output.(*bufio.Writer).Flush()
	buf := st.StackPop().(*bytes.Buffer)
	st.output = st.StackPop().(io.Writer)

	st.StackPush(buf.String())
	rbpool.Release(buf)

	st.Advance()
}
Exemple #3
0
// 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
}
Exemple #4
0
// 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()
}
Exemple #5
0
// 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
}
Exemple #6
0
// 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()
}
Exemple #7
0
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()
}