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
|
||||
#
|
||||
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 (
|
||||
//"errors"
|
||||
|
|
4
eval.go
4
eval.go
|
@ -1,6 +1,6 @@
|
|||
package pp
|
||||
package tpp
|
||||
|
||||
type Evaler interface {
|
||||
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
|
||||
|
||||
|
|
61
main.go
61
main.go
|
@ -1,43 +1,52 @@
|
|||
package pp
|
||||
package tpp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"fmt"
|
||||
//"fmt"
|
||||
"bytes"
|
||||
"context"
|
||||
)
|
||||
|
||||
|
||||
type Preprocessor struct {
|
||||
evaler Evaler
|
||||
tags [2]string
|
||||
tengo *Tengo
|
||||
tags [2][]byte
|
||||
}
|
||||
|
||||
// Get the new preprocessor with default options.
|
||||
func NewPp(evaler Evaler) *Preprocessor {
|
||||
pp := &Preprocessor{
|
||||
tags: evaler.Tags(),
|
||||
func New(tengo *Tengo ) *Preprocessor {
|
||||
pp := &Preprocessor{}
|
||||
pp.tengo = tengo
|
||||
pp.tags = [2][]byte{
|
||||
[]byte("{{"),
|
||||
[]byte("}}"),
|
||||
}
|
||||
pp.evaler = evaler
|
||||
return pp
|
||||
}
|
||||
|
||||
func (pp *Preprocessor) Process(filePath string, data string) (string, error) {
|
||||
var b strings.Builder
|
||||
func (pp *Preprocessor) Process(
|
||||
ctx context.Context,
|
||||
recompile bool,
|
||||
filePath string,
|
||||
data []byte,
|
||||
) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
last := 0
|
||||
texts := []string{}
|
||||
codes := []string{}
|
||||
texts := [][]byte{}
|
||||
codes := [][]byte{}
|
||||
for {
|
||||
idxStart := strings.Index(data[last:], pp.tags[0])
|
||||
idxEnd := strings.Index(data[last:], pp.tags[1])
|
||||
idxStart := bytes.Index(data[last:], pp.tags[0])
|
||||
idxEnd := bytes.Index(data[last:], pp.tags[1])
|
||||
//fmt.Printf("cock %d %d %d\n", last, idxStart, idxEnd)
|
||||
if idxStart < 0 {
|
||||
if idxEnd >= 0 {
|
||||
return "", UnexpectedError{
|
||||
return nil, UnexpectedError{
|
||||
What: "end tag",
|
||||
}
|
||||
}
|
||||
texts = append(texts, data[last:])
|
||||
break
|
||||
} else if idxEnd < 0 {
|
||||
return "", UnexpectedError{
|
||||
return nil, UnexpectedError{
|
||||
What: "start tag",
|
||||
}
|
||||
}
|
||||
|
@ -52,17 +61,23 @@ func (pp *Preprocessor) Process(filePath string, data string) (string, error) {
|
|||
data = data[1:]
|
||||
}*/
|
||||
}
|
||||
codeRets, err := pp.evaler.Eval(filePath, codes)
|
||||
codeRets, err := pp.tengo.Eval(
|
||||
ctx,
|
||||
recompile,
|
||||
filePath,
|
||||
codes,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, codeRet := range codeRets {
|
||||
fmt.Fprint(&b, texts[i])
|
||||
fmt.Fprintf(&b, codeRet)
|
||||
b.Write(texts[i])
|
||||
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 (
|
||||
"github.com/d5/tengo/v2"
|
||||
"github.com/d5/tengo/v2/stdlib"
|
||||
"fmt"
|
||||
"context"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type Script = tengo.Script
|
||||
type Compiled = tengo.Compiled
|
||||
|
||||
type Tengo struct {
|
||||
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 {
|
||||
evaler := &Tengo{}
|
||||
evaler.modules = stdlib.GetModuleMap(stdlib.AllModuleNames()...)
|
||||
return evaler
|
||||
ret := &Tengo{}
|
||||
return ret
|
||||
}
|
||||
|
||||
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 {
|
||||
return [2]string{
|
||||
"<?",
|
||||
"?>",
|
||||
"{{",
|
||||
"}}",
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = `
|
||||
__ret_one__ := ""
|
||||
printf := func(format, ...vals) {
|
||||
|
@ -43,20 +74,28 @@ func (pp *Tengo) Eval(filePath string, codes []string) ([]string, error) {
|
|||
`
|
||||
|
||||
const retSeparator = `
|
||||
__Separate(__ret_one__)
|
||||
__separate(__ret_one__)
|
||||
__ret_one__ = ""
|
||||
`
|
||||
rets := []string{}
|
||||
rets := [][]byte{}
|
||||
|
||||
|
||||
fullCode := retHead + "\n" + pp.head
|
||||
for _, code := range codes {
|
||||
fullCode += "\n" + code + retSeparator + "\n"
|
||||
fmt.Fprint(&fullCodeBuf, retHead)
|
||||
if pp.preCode != nil {
|
||||
fullCodeBuf.Write(pp.preCode())
|
||||
}
|
||||
script := tengo.NewScript([]byte(fullCode))
|
||||
script.SetImports(pp.modules)
|
||||
script.EnableFileImport(true)
|
||||
script.SetImportDir(".")
|
||||
for _, code := range codes {
|
||||
fmt.Fprintln(&fullCodeBuf, "\n" + string(code) + retSeparator)
|
||||
}
|
||||
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{
|
||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||
if len(args) < 1 {
|
||||
|
@ -109,12 +148,12 @@ func (pp *Tengo) Eval(filePath string, codes []string) ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = script.Add("__Separate", &tengo.UserFunction{
|
||||
err = script.Add("__separate", &tengo.UserFunction{
|
||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||
if len(args) < 1 {
|
||||
return nil, tengo.ErrWrongNumArguments
|
||||
}
|
||||
str, ok := tengo.ToString(args[0])
|
||||
str, ok := tengo.ToByteSlice(args[0])
|
||||
if !ok {
|
||||
return nil, tengo.ErrInvalidArgumentType{
|
||||
}
|
||||
|
@ -128,7 +167,7 @@ func (pp *Tengo) Eval(filePath string, codes []string) ([]string, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
_, err = script.RunContext(context.Background())
|
||||
_, err = script.RunContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<?
|
||||
{{
|
||||
os := import("os")
|
||||
shit := import("./tests/shit.tengo")
|
||||
newVar := "'this is gen shita'"
|
||||
println(shit.some_func())
|
||||
?># The index testing
|
||||
1 + 1 = <? print(1+1) ?>
|
||||
cock <? print(newVar, "and cock")?>
|
||||
}}# The index testing
|
||||
1 + 1 = {{ print(1+1) }}
|
||||
cock {{ print(newVar, "and cock") }}
|
||||
|
||||
## The shit after
|
||||
|
||||
checking <? printf(newVar) ?>
|
||||
checking {{ printf(newVar) }}
|
||||
|
||||
## File contents
|
||||
|
||||
<?
|
||||
{{
|
||||
bts := os.read_file("tests/somefile")
|
||||
printf("%v\n%s", bts, bts)
|
||||
?>
|
||||
}}
|
||||
|
||||
|
|
26
tool.go
26
tool.go
|
@ -1,15 +1,26 @@
|
|||
package pp
|
||||
package tpp
|
||||
|
||||
import (
|
||||
//"github.com/d5/tengo/v2"
|
||||
"github.com/d5/tengo/v2/stdlib"
|
||||
"vultras.su/core/cli/mtool"
|
||||
"fmt"
|
||||
//"fmt"
|
||||
"os"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"context"
|
||||
)
|
||||
|
||||
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()
|
||||
for _, filePath := range filePaths {
|
||||
pth := filepath.FromSlash(filePath)
|
||||
|
@ -18,11 +29,16 @@ var Tool = mtool.T("pp").Func(func(flags *mtool.Flags){
|
|||
log.Println("read error:", err)
|
||||
continue
|
||||
}
|
||||
str, err := pp.Process(pth, string(bts))
|
||||
out, err := pp.Process(
|
||||
context.Background(),
|
||||
true,
|
||||
pth,
|
||||
bts,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println("pp error:", err)
|
||||
continue
|
||||
}
|
||||
fmt.Print(str)
|
||||
os.Stdout.Write(out)
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue