package pp import ( "github.com/d5/tengo/v2" "github.com/d5/tengo/v2/stdlib" "context" "strings" "fmt" ) type Preprocessor struct { // The string will be inserted in the // beginning of processed files. head string tags [2]string modules *tengo.ModuleMap } // Evaluate the expression and return the value // it would print. func (pp *Preprocessor) Eval(data string) (string, error) { const retHead = ` printf := func(format, ...vals) { __ret__ += __Sprintf(format, vals...) } ` script := tengo.NewScript([]byte(retHead + pp.head + "\n" + data)) err := script.Add("__Sprintf", &tengo.UserFunction{ Value: func(args ...tengo.Object) (tengo.Object, error){ if len(args) < 1 { return nil, tengo.ErrWrongNumArguments } format, ok := tengo.ToString(args[0]) if !ok { return nil, tengo.ErrInvalidArgumentType{ Expected: "string", } } gargs := make([]any, len(args) - 1) for i := range gargs { gargs[i] = tengo.ToInterface(args[i+1]) //fmt.Printf("shit: %q\n", gargs[i]) } str := fmt.Sprintf(format, gargs...) return tengo.FromInterface(str) }, }, ) if err != nil { return "", err } err = script.Add("__ret__", "") if err != nil{ return "", err } compiled, err := script.RunContext(context.Background()) if err != nil { return "", err } return compiled.Get("__ret__").String(), nil } // Get the new preprocessor with default options. func NewPp() *Preprocessor { pp := &Preprocessor{ tags: [2]string{ "", }, modules: stdlib.GetModuleMap(stdlib.AllModuleNames()...) } return pp } func (pp *Preprocessor) Process(data string) (string, error) { var b strings.Builder last := 0 for { idxStart := strings.Index(data[last:], pp.tags[0]) idxEnd := strings.Index(data[last:], pp.tags[1]) //fmt.Printf("cock %d %d %d\n", last, idxStart, idxEnd) if idxStart < 0 { if idxEnd >= 0 { return "", UnexpectedError{ What: "end tag", } } fmt.Fprint(&b, data[last:]) break } else if idxEnd < 0 { return "", UnexpectedError{ What: "start tag", } } fmt.Fprint(&b, data[last:idxStart]) code := data[idxStart+len(pp.tags[0]):idxEnd] str, err := pp.Eval(code) if err != nil { return "", err } fmt.Fprint(&b, str) data = data[idxEnd + len(pp.tags[1])+1:] } return b.String(), nil }