feat: use bytes instead of string to be able to generate binary files, started implementing PHP-like server.
This commit is contained in:
parent
1eb5e35fa3
commit
34b6f06901
12 changed files with 193 additions and 62 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1 @@
|
||||||
pp
|
/tpp
|
||||||
|
|
2
build.sh
2
build.sh
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
go build ./cmd/pp
|
go build ./cmd/tpp
|
||||||
|
|
25
cmd/tenserve/main.go
Normal file
25
cmd/tenserve/main.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"net/http"
|
||||||
|
"vultras.su/util/tpp/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
addr, src string
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&addr, "http", ":8080", "HTTP address to serve")
|
||||||
|
flag.StringVar(&src, "src", "", "path to the source directory")
|
||||||
|
srv := http.Server{
|
||||||
|
Address: addr,
|
||||||
|
Handler: &server.Handler{
|
||||||
|
SourcePath: src,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatal(srv.ListenAndServe())
|
||||||
|
}
|
||||||
|
|
10
cmd/tpp/main.go
Normal file
10
cmd/tpp/main.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"vultras.su/util/tpp"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
tpp.Tool.Run(os.Args[1:])
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package pp
|
package tpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"errors"
|
//"errors"
|
||||||
|
|
4
eval.go
4
eval.go
|
@ -1,6 +1,6 @@
|
||||||
package pp
|
package tpp
|
||||||
|
|
||||||
type Evaler interface {
|
type Evaler interface {
|
||||||
Tags() [2]string
|
Tags() [2]string
|
||||||
Eval(string, []string) ([]string, error)
|
Eval(filePath string, pieces []string) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module vultras.su/util/pp
|
module vultras.su/util/tpp
|
||||||
|
|
||||||
go 1.21.7
|
go 1.21.7
|
||||||
|
|
||||||
|
|
65
main.go
65
main.go
|
@ -1,43 +1,52 @@
|
||||||
package pp
|
package tpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
//"fmt"
|
||||||
"fmt"
|
"bytes"
|
||||||
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type Preprocessor struct {
|
type Preprocessor struct {
|
||||||
evaler Evaler
|
tengo *Tengo
|
||||||
tags [2]string
|
tags [2][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the new preprocessor with default options.
|
// Get the new preprocessor with default options.
|
||||||
func NewPp(evaler Evaler) *Preprocessor {
|
func New(tengo *Tengo ) *Preprocessor {
|
||||||
pp := &Preprocessor{
|
pp := &Preprocessor{}
|
||||||
tags: evaler.Tags(),
|
pp.tengo = tengo
|
||||||
|
pp.tags = [2][]byte{
|
||||||
|
[]byte("{{"),
|
||||||
|
[]byte("}}"),
|
||||||
}
|
}
|
||||||
pp.evaler = evaler
|
|
||||||
return pp
|
return pp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *Preprocessor) Process(filePath string, data string) (string, error) {
|
func (pp *Preprocessor) Process(
|
||||||
var b strings.Builder
|
ctx context.Context,
|
||||||
|
recompile bool,
|
||||||
|
filePath string,
|
||||||
|
data []byte,
|
||||||
|
) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
last := 0
|
last := 0
|
||||||
texts := []string{}
|
texts := [][]byte{}
|
||||||
codes := []string{}
|
codes := [][]byte{}
|
||||||
for {
|
for {
|
||||||
idxStart := strings.Index(data[last:], pp.tags[0])
|
idxStart := bytes.Index(data[last:], pp.tags[0])
|
||||||
idxEnd := strings.Index(data[last:], pp.tags[1])
|
idxEnd := bytes.Index(data[last:], pp.tags[1])
|
||||||
//fmt.Printf("cock %d %d %d\n", last, idxStart, idxEnd)
|
//fmt.Printf("cock %d %d %d\n", last, idxStart, idxEnd)
|
||||||
if idxStart < 0 {
|
if idxStart < 0 {
|
||||||
if idxEnd >= 0 {
|
if idxEnd >= 0 {
|
||||||
return "", UnexpectedError{
|
return nil, UnexpectedError{
|
||||||
What: "end tag",
|
What: "end tag",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
texts = append(texts, data[last:])
|
texts = append(texts, data[last:])
|
||||||
break
|
break
|
||||||
} else if idxEnd < 0 {
|
} else if idxEnd < 0 {
|
||||||
return "", UnexpectedError{
|
return nil, UnexpectedError{
|
||||||
What: "start tag",
|
What: "start tag",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,17 +61,23 @@ func (pp *Preprocessor) Process(filePath string, data string) (string, error) {
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
codeRets, err := pp.evaler.Eval(filePath, codes)
|
codeRets, err := pp.tengo.Eval(
|
||||||
|
ctx,
|
||||||
|
recompile,
|
||||||
|
filePath,
|
||||||
|
codes,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, codeRet := range codeRets {
|
for i, codeRet := range codeRets {
|
||||||
fmt.Fprint(&b, texts[i])
|
b.Write(texts[i])
|
||||||
fmt.Fprintf(&b, codeRet)
|
b.Write(codeRet)
|
||||||
}
|
}
|
||||||
fmt.Fprint(&b, texts[len(codeRets)])
|
|
||||||
|
b.Write(texts[len(codeRets)])
|
||||||
return b.String(), nil
|
|
||||||
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
server/handler.go
Normal file
26
server/handler.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = http.Handler(&Handler{})
|
||||||
|
|
||||||
|
// The type describes behaviour
|
||||||
|
// of handling stuff like in PHP.
|
||||||
|
type Handler struct {
|
||||||
|
// THe field represents
|
||||||
|
// where we store site's
|
||||||
|
// source files and request handlers.
|
||||||
|
SourcePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) ServeHTTP(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
r *http.Request,
|
||||||
|
) {
|
||||||
|
p := r.URL.Path
|
||||||
|
p = path.Clean(p)
|
||||||
|
}
|
81
tengo.go
81
tengo.go
|
@ -1,32 +1,63 @@
|
||||||
package pp
|
package tpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/d5/tengo/v2"
|
"github.com/d5/tengo/v2"
|
||||||
"github.com/d5/tengo/v2/stdlib"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"context"
|
"context"
|
||||||
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Script = tengo.Script
|
||||||
|
type Compiled = tengo.Compiled
|
||||||
|
|
||||||
type Tengo struct {
|
type Tengo struct {
|
||||||
head string
|
head string
|
||||||
modules *tengo.ModuleMap
|
compiledScripts map[string] *Compiled
|
||||||
|
// Functions to modify
|
||||||
|
// preprocessor for
|
||||||
|
// more specific purposes.
|
||||||
|
preCode func() []byte
|
||||||
|
postCode func() []byte
|
||||||
|
preCompile func(*Script)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the new Tengo preprocessor
|
||||||
|
// with
|
||||||
func NewTengo() *Tengo {
|
func NewTengo() *Tengo {
|
||||||
evaler := &Tengo{}
|
ret := &Tengo{}
|
||||||
evaler.modules = stdlib.GetModuleMap(stdlib.AllModuleNames()...)
|
return ret
|
||||||
return evaler
|
}
|
||||||
|
|
||||||
|
func (tengo *Tengo) SetPreCode(fn func() []byte) *Tengo {
|
||||||
|
tengo.preCode = fn
|
||||||
|
return tengo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tengo *Tengo) SetPostCode(fn func() []byte) *Tengo {
|
||||||
|
tengo.postCode = fn
|
||||||
|
return tengo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tengo *Tengo) SetPreCompile(fn func(*Script)) *Tengo {
|
||||||
|
tengo.preCompile = fn
|
||||||
|
return tengo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *Tengo) Tags() [2]string {
|
func (pp *Tengo) Tags() [2]string {
|
||||||
return [2]string{
|
return [2]string{
|
||||||
"<?",
|
"{{",
|
||||||
"?>",
|
"}}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple Evaler implementation for the Tengo language
|
// Simple Evaler implementation for the Tengo language
|
||||||
func (pp *Tengo) Eval(filePath string, codes []string) ([]string, error) {
|
func (pp *Tengo) Eval(
|
||||||
|
ctx context.Context,
|
||||||
|
recompile bool,
|
||||||
|
filePath string,
|
||||||
|
codes [][]byte,
|
||||||
|
) ([][]byte, error) {
|
||||||
|
var fullCodeBuf bytes.Buffer
|
||||||
const retHead = `
|
const retHead = `
|
||||||
__ret_one__ := ""
|
__ret_one__ := ""
|
||||||
printf := func(format, ...vals) {
|
printf := func(format, ...vals) {
|
||||||
|
@ -43,20 +74,28 @@ func (pp *Tengo) Eval(filePath string, codes []string) ([]string, error) {
|
||||||
`
|
`
|
||||||
|
|
||||||
const retSeparator = `
|
const retSeparator = `
|
||||||
__Separate(__ret_one__)
|
__separate(__ret_one__)
|
||||||
__ret_one__ = ""
|
__ret_one__ = ""
|
||||||
`
|
`
|
||||||
rets := []string{}
|
rets := [][]byte{}
|
||||||
|
|
||||||
|
|
||||||
fullCode := retHead + "\n" + pp.head
|
fmt.Fprint(&fullCodeBuf, retHead)
|
||||||
for _, code := range codes {
|
if pp.preCode != nil {
|
||||||
fullCode += "\n" + code + retSeparator + "\n"
|
fullCodeBuf.Write(pp.preCode())
|
||||||
}
|
}
|
||||||
script := tengo.NewScript([]byte(fullCode))
|
for _, code := range codes {
|
||||||
script.SetImports(pp.modules)
|
fmt.Fprintln(&fullCodeBuf, "\n" + string(code) + retSeparator)
|
||||||
script.EnableFileImport(true)
|
}
|
||||||
script.SetImportDir(".")
|
if pp.postCode != nil {
|
||||||
|
fullCodeBuf.Write(pp.postCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
script := tengo.NewScript(fullCodeBuf.Bytes())
|
||||||
|
if pp.preCompile != nil {
|
||||||
|
pp.preCompile(script)
|
||||||
|
}
|
||||||
|
|
||||||
err := script.Add("sprintf", &tengo.UserFunction{
|
err := script.Add("sprintf", &tengo.UserFunction{
|
||||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -109,12 +148,12 @@ func (pp *Tengo) Eval(filePath string, codes []string) ([]string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = script.Add("__Separate", &tengo.UserFunction{
|
err = script.Add("__separate", &tengo.UserFunction{
|
||||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, tengo.ErrWrongNumArguments
|
return nil, tengo.ErrWrongNumArguments
|
||||||
}
|
}
|
||||||
str, ok := tengo.ToString(args[0])
|
str, ok := tengo.ToByteSlice(args[0])
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, tengo.ErrInvalidArgumentType{
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
}
|
}
|
||||||
|
@ -128,7 +167,7 @@ func (pp *Tengo) Eval(filePath string, codes []string) ([]string, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = script.RunContext(context.Background())
|
_, err = script.RunContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
<?
|
{{
|
||||||
os := import("os")
|
os := import("os")
|
||||||
shit := import("./tests/shit.tengo")
|
shit := import("./tests/shit.tengo")
|
||||||
newVar := "'this is gen shita'"
|
newVar := "'this is gen shita'"
|
||||||
println(shit.some_func())
|
println(shit.some_func())
|
||||||
?># The index testing
|
}}# The index testing
|
||||||
1 + 1 = <? print(1+1) ?>
|
1 + 1 = {{ print(1+1) }}
|
||||||
cock <? print(newVar, "and cock")?>
|
cock {{ print(newVar, "and cock") }}
|
||||||
|
|
||||||
## The shit after
|
## The shit after
|
||||||
|
|
||||||
checking <? printf(newVar) ?>
|
checking {{ printf(newVar) }}
|
||||||
|
|
||||||
## File contents
|
## File contents
|
||||||
|
|
||||||
<?
|
{{
|
||||||
bts := os.read_file("tests/somefile")
|
bts := os.read_file("tests/somefile")
|
||||||
printf("%v\n%s", bts, bts)
|
printf("%v\n%s", bts, bts)
|
||||||
?>
|
}}
|
||||||
|
|
||||||
|
|
26
tool.go
26
tool.go
|
@ -1,15 +1,26 @@
|
||||||
package pp
|
package tpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
//"github.com/d5/tengo/v2"
|
||||||
|
"github.com/d5/tengo/v2/stdlib"
|
||||||
"vultras.su/core/cli/mtool"
|
"vultras.su/core/cli/mtool"
|
||||||
"fmt"
|
//"fmt"
|
||||||
"os"
|
"os"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Tool = mtool.T("pp").Func(func(flags *mtool.Flags){
|
var Tool = mtool.T("pp").Func(func(flags *mtool.Flags){
|
||||||
pp := NewPp(NewTengo())
|
t := NewTengo().
|
||||||
|
SetPreCompile(func(s *Script){
|
||||||
|
s.SetImports(stdlib.GetModuleMap(
|
||||||
|
stdlib.AllModuleNames()...,
|
||||||
|
))
|
||||||
|
s.EnableFileImport(true)
|
||||||
|
s.SetImportDir(".")
|
||||||
|
})
|
||||||
|
pp := New(t)
|
||||||
filePaths := flags.Parse()
|
filePaths := flags.Parse()
|
||||||
for _, filePath := range filePaths {
|
for _, filePath := range filePaths {
|
||||||
pth := filepath.FromSlash(filePath)
|
pth := filepath.FromSlash(filePath)
|
||||||
|
@ -18,11 +29,16 @@ var Tool = mtool.T("pp").Func(func(flags *mtool.Flags){
|
||||||
log.Println("read error:", err)
|
log.Println("read error:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
str, err := pp.Process(pth, string(bts))
|
out, err := pp.Process(
|
||||||
|
context.Background(),
|
||||||
|
true,
|
||||||
|
pth,
|
||||||
|
bts,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("pp error:", err)
|
log.Println("pp error:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Print(str)
|
os.Stdout.Write(out)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue