123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- package gopp
- import (
- "surdeus.su/core/xgo/v2"
- "fmt"
- "io"
- "context"
- "bytes"
- "strconv"
- )
- type CompiledMap map[string] *Compiled
- type Script = xgo.Script
- type Compiled = xgo.Compiled
- // The type describes
- // customizable way to evaluate
- // files.
- type XGo struct {
- // Functions to modify
- // preprocessor for
- // more specific purposes.
- preCode func(context.Context) []byte
- postCode func(context.Context) []byte
- preCompile func(context.Context, *Script)
- }
- // Returns the new XGo preprocessor
- // with
- func NewXGo() *XGo {
- ret := &XGo{}
- return ret
- }
- func (xgo *XGo) SetPreCode(fn func(context.Context) []byte) *XGo {
- xgo.preCode = fn
- return xgo
- }
- func (xgo *XGo) SetPostCode(fn func(context.Context) []byte) *XGo {
- xgo.postCode = fn
- return xgo
- }
- func (xgo *XGo) SetPreCompile(fn func(context.Context, *Script)) *XGo {
- xgo.preCompile = fn
- return xgo
- }
- const (
- ppFilePath = "__pp_file_path__"
- ppPrintf = "__pp_printf__"
- ppPrint = "__pp_print__"
- ppPrintln = "__pp_println__"
- ppWriteRaw = "__pp_write_raw__"
- ppHead = `// The main generated head. Exists in every file.
- context := {}
- pp := immutable({
- fpath:` + ppFilePath + `,
- printf :` + ppPrintf + `,
- print :` + ppPrint + `,
- println :` + ppPrintln + `,
- write_raw :` + ppWriteRaw + `
- })
- `)
- // Render to code.
- func (pp *XGo) Render(
- ctx context.Context,
- parsed *Parsed,
- output io.Writer,
- ) (error) {
- fmt.Fprint(output, ppHead)
- if parsed.PreCode != nil {
- output.Write([]byte("// The parsed precode from file.\n"))
- output.Write(parsed.PreCode)
- }
- if pp.preCode != nil {
- output.Write(pp.preCode(ctx))
- }
- for i, code := range parsed.Codes {
- // Text.
- fmt.Fprintf(
- output,
- "// Text %d\n" +
- ppWriteRaw+"(%q)\n",
- i, parsed.Texts[i],
- )
- // Code.
- fmt.Fprintf(output, "// Code %d\n", i)
- output.Write(code)
- }
- i := len(parsed.Texts)-1
- fmt.Fprintf(
- output,
- "// Text %d\n" +
- ppWriteRaw+"(%q)\n",
- i, parsed.Texts[i],
- )
- if pp.postCode != nil {
- output.Write(pp.postCode(ctx))
- }
- return nil
- }
- // b
- func (pp *XGo) Compile(
- ctx context.Context,
- // Static text pieces.
- parsed *Parsed,
- ) (*Compiled, error) {
- var buf bytes.Buffer
- err := pp.Render(
- ctx, parsed, &buf,
- )
- fullCode := buf.Bytes()
- if err != nil {
- return nil, err
- }
- script := xgo.NewScript(fullCode)
- if pp.preCompile != nil {
- pp.preCompile(ctx, script)
- }
- // Presetting variables before running.
- script.Add(ppFilePath, false)
- script.Add(ppPrintf, false)
- script.Add(ppPrint, false)
- script.Add(ppPrintln, false)
- script.Add(ppWriteRaw, false)
- compiled, err := script.Compile()
- if err != nil {
- return nil, err
- }
- return compiled, nil
- }
- func (pp *XGo) RunContext(
- ctx context.Context,
- compiled *Compiled,
- filePath string,
- output io.Writer,
- ) error {
- var err error
- err = compiled.Set(ppFilePath, filePath)
- if err != nil {return err}
- err = compiled.Set(ppPrintf, &xgo.UserFunction{
- Value: func(args ...xgo.Object) (xgo.Object, error){
- if len(args) < 1 {
- return nil, xgo.ErrWrongNumArguments
- }
- format, ok := xgo.ToString(args[0])
- if !ok {
- return nil, xgo.ErrInvalidArgumentType{
- Expected: "string",
- }
- }
- gargs := make([]any, len(args) - 1)
- for i := range gargs {
- gargs[i] = xgo.ToInterface(args[i+1])
- //fmt.Printf("shit: %q\n", gargs[i])
- }
- fmt.Fprintf(output, format, gargs...)
- return nil, nil
- //return xgo.FromInterface([]byte(str))
- },
- })
- if err != nil {return err}
- err = compiled.Set(ppPrint, &xgo.UserFunction{
- Value: func(args ...xgo.Object) (xgo.Object, error){
- gargs := make([]any, len(args))
- for i := range gargs {
- gargs[i] = xgo.ToInterface(args[i])
- }
- fmt.Fprint(output, gargs...)
- return nil, nil
- //return xgo.FromInterface([]byte(str))
- },
- })
- if err != nil {return err}
- err = compiled.Set(ppPrintln, &xgo.UserFunction{
- Value: func(args ...xgo.Object) (xgo.Object, error){
- gargs := make([]any, len(args))
- for i := range gargs {
- gargs[i] = xgo.ToInterface(args[i])
- }
- fmt.Fprintln(output, gargs...)
- return nil, nil
- //return xgo.FromInterface([]byte(str))
- },
- })
- if err != nil {return err}
- err = compiled.Set(ppWriteRaw, &xgo.UserFunction{
- Value: func(args ...xgo.Object) (xgo.Object, error){
- bt := make([][]byte, len(args))
- for i, o := range args {
- bts, ok := xgo.ToByteSlice(o)
- if !ok {
- return nil, xgo.ErrInvalidArgumentType{
- Name: strconv.Itoa(i),
- Expected: "string/bytes",
- Found: o.TypeName(),
- }
- }
- bt[i] = bts
- }
- for _, b := range bt {
- output.Write(b)
- }
- return nil, nil
- },
- })
- if err != nil {return err}
- return compiled.RunContext(ctx)
- }
|