xgo.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. package gopp
  2. import (
  3. "surdeus.su/core/xgo/v2"
  4. "fmt"
  5. "io"
  6. "context"
  7. "bytes"
  8. "strconv"
  9. )
  10. type CompiledMap map[string] *Compiled
  11. type Script = xgo.Script
  12. type Compiled = xgo.Compiled
  13. // The type describes
  14. // customizable way to evaluate
  15. // files.
  16. type XGo struct {
  17. // Functions to modify
  18. // preprocessor for
  19. // more specific purposes.
  20. preCode func(context.Context) []byte
  21. postCode func(context.Context) []byte
  22. preCompile func(context.Context, *Script)
  23. }
  24. // Returns the new XGo preprocessor
  25. // with
  26. func NewXGo() *XGo {
  27. ret := &XGo{}
  28. return ret
  29. }
  30. func (xgo *XGo) SetPreCode(fn func(context.Context) []byte) *XGo {
  31. xgo.preCode = fn
  32. return xgo
  33. }
  34. func (xgo *XGo) SetPostCode(fn func(context.Context) []byte) *XGo {
  35. xgo.postCode = fn
  36. return xgo
  37. }
  38. func (xgo *XGo) SetPreCompile(fn func(context.Context, *Script)) *XGo {
  39. xgo.preCompile = fn
  40. return xgo
  41. }
  42. const (
  43. ppFilePath = "__pp_file_path__"
  44. ppPrintf = "__pp_printf__"
  45. ppPrint = "__pp_print__"
  46. ppPrintln = "__pp_println__"
  47. ppWriteRaw = "__pp_write_raw__"
  48. ppHead = `// The main generated head. Exists in every file.
  49. context := {}
  50. pp := immutable({
  51. fpath:` + ppFilePath + `,
  52. printf :` + ppPrintf + `,
  53. print :` + ppPrint + `,
  54. println :` + ppPrintln + `,
  55. write_raw :` + ppWriteRaw + `
  56. })
  57. `)
  58. // Render to code.
  59. func (pp *XGo) Render(
  60. ctx context.Context,
  61. parsed *Parsed,
  62. output io.Writer,
  63. ) (error) {
  64. fmt.Fprint(output, ppHead)
  65. if parsed.PreCode != nil {
  66. output.Write([]byte("// The parsed precode from file.\n"))
  67. output.Write(parsed.PreCode)
  68. }
  69. if pp.preCode != nil {
  70. output.Write(pp.preCode(ctx))
  71. }
  72. for i, code := range parsed.Codes {
  73. // Text.
  74. fmt.Fprintf(
  75. output,
  76. "// Text %d\n" +
  77. ppWriteRaw+"(%q)\n",
  78. i, parsed.Texts[i],
  79. )
  80. // Code.
  81. fmt.Fprintf(output, "// Code %d\n", i)
  82. output.Write(code)
  83. }
  84. i := len(parsed.Texts)-1
  85. fmt.Fprintf(
  86. output,
  87. "// Text %d\n" +
  88. ppWriteRaw+"(%q)\n",
  89. i, parsed.Texts[i],
  90. )
  91. if pp.postCode != nil {
  92. output.Write(pp.postCode(ctx))
  93. }
  94. return nil
  95. }
  96. // b
  97. func (pp *XGo) Compile(
  98. ctx context.Context,
  99. // Static text pieces.
  100. parsed *Parsed,
  101. ) (*Compiled, error) {
  102. var buf bytes.Buffer
  103. err := pp.Render(
  104. ctx, parsed, &buf,
  105. )
  106. fullCode := buf.Bytes()
  107. if err != nil {
  108. return nil, err
  109. }
  110. script := xgo.NewScript(fullCode)
  111. if pp.preCompile != nil {
  112. pp.preCompile(ctx, script)
  113. }
  114. // Presetting variables before running.
  115. script.Add(ppFilePath, false)
  116. script.Add(ppPrintf, false)
  117. script.Add(ppPrint, false)
  118. script.Add(ppPrintln, false)
  119. script.Add(ppWriteRaw, false)
  120. compiled, err := script.Compile()
  121. if err != nil {
  122. return nil, err
  123. }
  124. return compiled, nil
  125. }
  126. func (pp *XGo) RunContext(
  127. ctx context.Context,
  128. compiled *Compiled,
  129. filePath string,
  130. output io.Writer,
  131. ) error {
  132. var err error
  133. err = compiled.Set(ppFilePath, filePath)
  134. if err != nil {return err}
  135. err = compiled.Set(ppPrintf, &xgo.UserFunction{
  136. Value: func(args ...xgo.Object) (xgo.Object, error){
  137. if len(args) < 1 {
  138. return nil, xgo.ErrWrongNumArguments
  139. }
  140. format, ok := xgo.ToString(args[0])
  141. if !ok {
  142. return nil, xgo.ErrInvalidArgumentType{
  143. Expected: "string",
  144. }
  145. }
  146. gargs := make([]any, len(args) - 1)
  147. for i := range gargs {
  148. gargs[i] = xgo.ToInterface(args[i+1])
  149. //fmt.Printf("shit: %q\n", gargs[i])
  150. }
  151. fmt.Fprintf(output, format, gargs...)
  152. return nil, nil
  153. //return xgo.FromInterface([]byte(str))
  154. },
  155. })
  156. if err != nil {return err}
  157. err = compiled.Set(ppPrint, &xgo.UserFunction{
  158. Value: func(args ...xgo.Object) (xgo.Object, error){
  159. gargs := make([]any, len(args))
  160. for i := range gargs {
  161. gargs[i] = xgo.ToInterface(args[i])
  162. }
  163. fmt.Fprint(output, gargs...)
  164. return nil, nil
  165. //return xgo.FromInterface([]byte(str))
  166. },
  167. })
  168. if err != nil {return err}
  169. err = compiled.Set(ppPrintln, &xgo.UserFunction{
  170. Value: func(args ...xgo.Object) (xgo.Object, error){
  171. gargs := make([]any, len(args))
  172. for i := range gargs {
  173. gargs[i] = xgo.ToInterface(args[i])
  174. }
  175. fmt.Fprintln(output, gargs...)
  176. return nil, nil
  177. //return xgo.FromInterface([]byte(str))
  178. },
  179. })
  180. if err != nil {return err}
  181. err = compiled.Set(ppWriteRaw, &xgo.UserFunction{
  182. Value: func(args ...xgo.Object) (xgo.Object, error){
  183. bt := make([][]byte, len(args))
  184. for i, o := range args {
  185. bts, ok := xgo.ToByteSlice(o)
  186. if !ok {
  187. return nil, xgo.ErrInvalidArgumentType{
  188. Name: strconv.Itoa(i),
  189. Expected: "string/bytes",
  190. Found: o.TypeName(),
  191. }
  192. }
  193. bt[i] = bts
  194. }
  195. for _, b := range bt {
  196. output.Write(b)
  197. }
  198. return nil, nil
  199. },
  200. })
  201. if err != nil {return err}
  202. return compiled.RunContext(ctx)
  203. }