feat: simplified evaling.

This commit is contained in:
Andrey Parhomenko 2024-06-09 16:51:03 +05:00
parent 5a82d721bd
commit 0c7f2d9df0
20 changed files with 247 additions and 301 deletions

View file

@ -1,10 +1,10 @@
package main
import (
"surdeus.su/util/tpp/httpx"
"surdeus.su/util/tpp/server"
"os"
)
func main() {
httpx.Tool.Run(os.Args[1:])
server.Tool.Run(os.Args[1:])
}

6
go.mod
View file

@ -1,9 +1,11 @@
module surdeus.su/util/tpp
go 1.21.7
go 1.22.3
require (
github.com/d5/tengo/v2 v2.17.0
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2
surdeus.su/core/cli v0.0.2
surdeus.su/core/cli v0.1.2
)
require surdeus.su/core/xgo v0.5.0 // indirect

4
go.sum
View file

@ -4,3 +4,7 @@ github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 h1:yEt5djSYb4i
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
surdeus.su/core/cli v0.0.2 h1:RdHHk3/Fhwxz9PjaE+vTlCuF9KmhrmNUb5y4oqulrYI=
surdeus.su/core/cli v0.0.2/go.mod h1:UKwCmcSX+x7XX9aF3gOaaAaJcJA3gtUmL4vdnM43+fM=
surdeus.su/core/cli v0.1.2 h1:qPzjawqPyZsO4Z5SaA1u141recVE65yioA83Qs7Jecs=
surdeus.su/core/cli v0.1.2/go.mod h1:r9JtQz3aEJzpYzMaNUNQHJoYkoWKNPi047qhd5uGlmA=
surdeus.su/core/xgo v0.5.0 h1:/Rk3scfFkoSb0qjHRlkUNOp9sr/fd7wAvCiT4fBRo+U=
surdeus.su/core/xgo v0.5.0/go.mod h1:6C/AHbjfvAMvt3TOzLB4eIZ40eU3ahJXtdY+kr4yXoc=

View file

@ -1,2 +0,0 @@
package httpx

View file

@ -1,24 +0,0 @@
package httpx
import "github.com/d5/tengo/v2"
import "github.com/d5/tengo/v2/stdlib"
import "surdeus.su/util/tpp/paths"
var Modules = map[string]tengo.Importable{
"paths": paths.Module,
}
var Stdlib = stdlib.GetModuleMap(stdlib.AllModuleNames()...)
type ModuleGetter struct{}
func (m *ModuleGetter) Get(
name string,
) tengo.Importable {
module, exist := Modules[name]
if exist {
return module
}
return Stdlib.Get(name)
}

View file

@ -1,49 +0,0 @@
package httpx
import (
"github.com/d5/tengo/v2"
"net/http"
"io"
//"log"
)
type Request struct {
tengo.ObjectImpl
*http.Request
}
func (r *Request) TypeName() string {
return "*http.Request"
}
func (r *Request) String() string {
return "*http.Request{...}"
}
func (r *Request) IndexGet(
index tengo.Object,
) (tengo.Object, error) {
key, ok := tengo.ToString(index)
if !ok {
return nil, tengo.ErrInvalidIndexValueType
}
switch key {
case "url" :
return &URL{
URL: r.URL,
}, nil
case "body" :
bts, err := io.ReadAll(r.Body)
if err != nil {
return nil, err
}
return &tengo.Bytes{
Value: bts,
}, nil
}
// Nothing found.
return nil, nil
}

View file

@ -1,76 +0,0 @@
package httpx
import (
"github.com/d5/tengo/v2"
"net/url"
"fmt"
)
var _ = tengo.Object(&Values{})
type Values struct {
tengo.ObjectImpl
url.Values
}
func (vs *Values) TypeName() string {
return "*url.Values"
}
func (vs *Values) String() string {
return fmt.Sprintf("%v", vs.Values)
}
func (vs *Values) IndexGet(
index tengo.Object,
) (tengo.Object, error) {
key, ok := tengo.ToString(index)
if !ok {
return nil, tengo.ErrInvalidIndexValueType
}
val, ok := vs.Values[key]
if !ok {
return nil, nil
}
arr := make([]tengo.Object, len(val))
for i, v := range val {
arr[i], _ = tengo.FromInterface(v)
}
return &tengo.Array{Value: arr}, nil
}
type URL struct {
tengo.ObjectImpl
*url.URL
}
func (u *URL) TypeName() string {
return "<URL>"
}
func (u *URL) String() string {
return u.URL.String()
}
func (u *URL) IndexGet(
index tengo.Object,
) (tengo.Object, error) {
key, ok := tengo.ToString(index)
if !ok {
return nil, tengo.ErrInvalidIndexValueType
}
switch key {
case "path" :
return tengo.FromInterface(u.Path)
case "query" :
return &Values{
Values: u.Query(),
}, nil
}
// Nothing found.
return nil, nil
}

3
install.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
go install ./cmd/tpp/ ./cmd/tht/

19
main.go
View file

@ -1,7 +1,6 @@
package tpp
import (
//"fmt"
"bytes"
"context"
)
@ -31,7 +30,7 @@ func (pp *Preprocessor) Process(
filePath string,
data []byte,
) ([]byte, error) {
var b bytes.Buffer
//var b bytes.Buffer
preCode := []byte(nil)
texts := [][]byte{}
codes := [][]byte{}
@ -72,15 +71,13 @@ func (pp *Preprocessor) Process(
codes = append(codes, code)
data = data[idxEnd + len(pp.tags[1]):]
/*if len(data) > 0 && data[0] == '\n' {
data = data[1:]
}*/
}
codeRets, err := pp.tengo.Eval(
ret, err := pp.tengo.Eval(
ctx,
recompile,
filePath,
texts,
preCode,
codes,
)
@ -88,14 +85,6 @@ func (pp *Preprocessor) Process(
return nil, err
}
for i := range codeRets {
b.Write(texts[i])
b.Write(codeRets[i])
}
if len(texts) > len(codeRets) {
b.Write(texts[len(codeRets)])
}
return b.Bytes(), nil
return ret, nil
}

View file

@ -4,9 +4,8 @@ htmExt := func(c){
if c.is_compo {
return
}
c.pp.print(`
</body></html>
`)
c.pp.print(`</body></html>`)
}
exts := {
@ -15,8 +14,10 @@ exts := {
}
export func(c){
url_path := c.http.request.url.path
ext := paths.ext(url_path)
ext := paths.ext(c.pp.filepath)
ext = paths.ext(
c.pp.filepath[:len(c.pp.filepath) - len(ext)]
)
cl := exts[ext]
if !is_undefined(cl) {

View file

@ -1,22 +1,25 @@
fmt := import("fmt")
paths := import("paths")
html := import("html").new_render()
htmExt := func(c){
if c.is_compo {
return
}
fmt.println("shit")
c.pp.print(`
<!doctype html>
<html><head>
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
`)
if c.title {
c.pp.printf(`<title>%s</title>`, c.title)
}
c.pp.print(`
</head><body>
`)
c.pp.print(`<!doctype html><html>`)
c.pp.print(html.head().body(
func(){
if c.title {
return html.title().body(c.title)
}
}(),
html.script({
src: `https://unpkg.com/htmx.org@1.9.2`
})
))
c.pp.print(`<body>`)
}
exts := {
@ -25,8 +28,10 @@ exts := {
}
export func(c){
url_path := c.http.request.url.path
ext := paths.ext(url_path)
ext := paths.ext(c.pp.filepath)
ext = paths.ext(
c.pp.filepath[:len(c.pp.filepath) - len(ext)]
)
cl := exts[ext]
if cl {

View file

@ -1,9 +1,11 @@
package httpx
package server
import (
//"github.com/d5/tengo/v2"
"surdeus.su/util/tpp/mdx"
"surdeus.su/util/tpp/htmlx"
//"surdeus.su/core/xgo/xmodules/htmlx"
"surdeus.su/core/xgo/xmodules/httpx"
"surdeus.su/core/xgo/xmodules"
"surdeus.su/util/tpp"
"path/filepath"
"net/http"
@ -37,7 +39,7 @@ type Handler struct {
// between requests.
global any
md *mdx.Markdown
html *htmlx.HTML
indexSuffix string
}
// Returns the new Handler with
@ -45,7 +47,7 @@ type Handler struct {
// preprocessor extension and the global value.
func NewHandler(
pp *tpp.Preprocessor,
src, ext string,
src, ext, index string,
global any,
) *Handler {
ret := &Handler{}
@ -53,6 +55,7 @@ func NewHandler(
ret.ext = ext
ret.pp = pp
ret.global = global
ret.indexSuffix = index
return ret
}
@ -64,26 +67,23 @@ func DefaultPP(mod string) *tpp.Preprocessor {
s *tpp.Script,
) {
s.SetImportDir(mod)
s.SetImports(&ModuleGetter{})
s.SetImports(xmodules.Modules)
s.EnableFileImport(true)
s.Add("__http_request__", ctx.Value(KeyRequest))
s.Add("__global__", ctx.Value(KeyGlobal))
s.Add("__markdown__", ctx.Value(KeyMarkdown))
s.Add("__html__", ctx.Value(KeyHTML))
}).SetPreCode(func(ctx context.Context) []byte {
return []byte(`
markdown := func(...args) {
pp.write_raw(__markdown__(args...))
}
http := immutable({
__http__ := immutable({
request : __http_request__
})
html := __html__
context.http = http
context.http = __http__
context.pp = pp
context.global = __global__
context.html = html
import("./pre")(context)
`)
}).SetPostCode(func(ctx context.Context) []byte {
@ -99,10 +99,6 @@ func (h *Handler) SetMD(md *mdx.Markdown) *Handler {
h.md = md
return h
}
func (h *Handler) SetHTML(html *htmlx.HTML) *Handler {
h.html = html
return h
}
func (h *Handler) ServeHTTP(
w http.ResponseWriter,
@ -118,8 +114,17 @@ func (h *Handler) ServeHTTP(
filepath.FromSlash(h.sourcePath),
filepath.FromSlash(urlPath),
)
filePathTpp := filePath + h.ext
fileStat, err := os.Stat(filePath)
if err == nil {
if fileStat.IsDir() {
filePath = filepath.Join(
filePath,
h.indexSuffix,
)
}
}
filePathTpp := filePath + h.ext
//log.Println("pth:", urlPath, filePathTpp)
file, err := os.Open(filePathTpp)
if err != nil {
@ -141,7 +146,7 @@ func (h *Handler) ServeHTTP(
ctx := context.WithValue(
r.Context(),
KeyRequest,
&Request{
&httpx.Request{
Request: r,
},
)
@ -158,17 +163,10 @@ func (h *Handler) ServeHTTP(
h.md,
)
ctx = context.WithValue(
ctx,
KeyHTML,
h.html,
)
// Setting before the code to let it change own
// content type.
// content type?
contentType := mime.TypeByExtension(urlExt)
w.Header().Set("Content-Type", contentType)
processedData := fileData
if shouldProcess {
processedData, err = h.pp.Process(

View file

@ -1,4 +1,4 @@
package httpx
package server
import (
@ -24,13 +24,14 @@ const (
// Simple PHP-like server implementation.
var Tool = mtool.T("tht").Func(func(flags *mtool.Flags) {
var (
addr, ext, src, mod string
addr, ext, src, mod, index string
)
flags.StringVar(&addr, "addr", ":3000", "address to serve at")
flags.StringVar(&mod, "mod", "./mod", "path to store Tengo modules")
flags.StringVar(&src, "src", "./src", "directory with source files")
flags.StringVar(&ext, "ext", ".tpp", "extension for TPP files")
flags.StringVar(&index, "index", "index.htm", "index file name")
flags.Parse()
@ -39,7 +40,7 @@ var Tool = mtool.T("tht").Func(func(flags *mtool.Flags) {
Addr: addr,
Handler: NewHandler(
DefaultPP(mod),
src, ext,
src, ext, index,
map[string] tengo.Object{},
).SetMD(mdx.MakeDefaultMarkdown()),
}

5
src/check.htm.tpp Normal file
View file

@ -0,0 +1,5 @@
<main>shit</main>
{{
pp.print("cock")
}}
<main>later</main>

12
src/check/index.htm.tpp Normal file
View file

@ -0,0 +1,12 @@
{{#
html := import("html").new_render()
}}
{{
h := html.main().body(
html.p().body(
"some paragraph shit"
)
)
pp.print(h)
}}

11
src/incorrect.htm.tpp Normal file
View file

@ -0,0 +1,11 @@
html := import("html").new_render()
pp.print(html.html().body(
html.head().body(
html.title().body(
"some title"
)
),
html.body().body(
)
))

78
src/index.htm.tpp Normal file
View file

@ -0,0 +1,78 @@
{{#
context.title = "The example.site main page"
fmt := import("fmt")
html := import("html").new_render()
}}
<h1>This is the example page for the THT</h1>
{{
/*if !context.global.int {
context.global.int = 0
} else {
context.global.int++
}*/
if !context.global.val {
context.global.val = 1
} else {
context.global.val += 1
}
fmt.println("global: ", context.global)
req := context.http.request
q := req.url.query
// List checking.
list := []
rng := int(q.range[0])
fmt.println("range:", q.range)
if rng {
pp.print(`<ul name="range">`)
for i:=0 ; i < rng ; i++ {
pp.printf("<li>%d</li>", i)
}
pp.print("</ul>")
}
if q.name {
pp.print("<div id=\"name\">", q.name[0], "</div>")
}
}}
<div name="counter">
{{ pp.print(context.global.val) }}
</div>
<div>
Hello, Cock!
</div>
<div check>
{{
pp.print(req.url.path)
}}
</div>
<ul>{{
for v in list {
pp.print("<li>", v, "</li>")
}
}}</ul>
{{
vals := ["die", "with", "them", "as", "you", 1, "could", "do"]
pp.print(html.div({
id: "the-uniq-shit"
}).body(
html.ul(
).body(
func(){
ret := []
for i:=0 ; i<len(vals) ; i++{
ret += [html.li({
class: "someclass"
}).body(i, ": ", vals[i])]
}
return ret
}()...
)
))
}}
<script src="/script.js" ></script>
<div></div>

View file

@ -1,27 +1,31 @@
{{#
context.title = "The example.site main page"
fmt := import("fmt")
html := import("html").new_render()
}}
<h1>This is the example page for the THT</h1>
{{
/*if !context.global.int {
context.global.int = 0
} else {
context.global.int++
}*/
pp.print(
html.h1().body("This is the example page for THT")
)
fmt := import("fmt")
if !context.global.val {
context.global.val = 1
} else {
context.global.val += 1
}
fmt.println("global: ", context.global)
req := http.request
req
("global: ", context.global)
}}
{{
req := context.http.request
q := req.url.query
// List checking.
list := []
rng := int(q.range[0])
fmt.println("range:", q.range)
if rng {
@ -35,43 +39,3 @@
pp.print("<div id=\"name\">", q.name[0], "</div>")
}
}}
<div name="counter">
{{ pp.print(context.global.val) }}
</div>
<div>
Hello, Cock!
</div>
<div check>
{{
pp.print(req.url.path)
}}
</div>
<ul>{{
for v in list {
pp.print("<li>", v, "</li>")
}
}}</ul>
{{
vals := ["die", "with", "them", "as", "you", 1, "could", "do"]
pp.print(html.div({
id: "the-uniq-shit"
}).body(
html.ul(
).body(
func(){
ret := []
for i:=0 ; i<len(vals) ; i++{
ret += [html.li({
class: "someclass"
}).body(i, ": ", vals[i])]
}
return ret
}()...
)
))
}}
<script src="/script.js" ></script>
<div></div>

View file

@ -16,7 +16,7 @@ This is the markdown example shit.
And even more text
> Cheap is talk, show the code.
> Cheap is talk, show me the code.
(c) Linus Torvalds

View file

@ -48,39 +48,30 @@ func (pp *Tengo) Eval(
ctx context.Context,
recompile bool,
filePath string,
texts [][]byte,
preCode []byte,
codes [][]byte,
) ([][]byte, error) {
var fullCodeBuf bytes.Buffer
) ([]byte, error) {
var fullCodeBuf, retBuf bytes.Buffer
const retHead = `
context := {}
__ret_one__ := bytes("")
pp := immutable({
filepath: __filepath__,
printf : func(format, ...vals) {
__ret_one__ += __sprintf__(format, vals...)
},
print : func(...vals) {
__ret_one__ += __sprint__(vals...)
},
println : func(...vals) {
__ret_one__ += __sprintln__(vals...)
},
write_raw : func(bts) {
__ret_one__ += bytes(bts)
}
filepath: __pp_filepath__,
printf : __pp_printf__,
print : __pp_print__,
println : __pp_println__,
write_raw : __pp_write_raw__
})
`
const retSeparator = `
__separate__(__ret_one__)
__ret_one__ = ""
__separate__()
`
rets := [][]byte{}
//rets := [][]byte{}
fmt.Fprint(&fullCodeBuf, retHead)
if preCode != nil {
fmt.Fprintln(
&fullCodeBuf,
@ -93,12 +84,22 @@ func (pp *Tengo) Eval(
fullCodeBuf.Write([]byte(retSeparator))
}
for _, code := range codes {
for i, code := range codes {
fmt.Fprintf(
&fullCodeBuf,
"\n__pp_write_raw__(%q)\n",
texts[i],
)
fmt.Fprintln(
&fullCodeBuf,
"\n" + string(code) + retSeparator,
)
}
fmt.Fprintf(
&fullCodeBuf,
"\n__pp_write_raw__(%q)\n",
texts[len(texts)-1],
)
if pp.postCode != nil {
fullCodeBuf.Write(pp.postCode(ctx))
@ -110,8 +111,8 @@ func (pp *Tengo) Eval(
pp.preCompile(ctx, script)
}
script.Add("__filepath__", filePath)
script.Add("__sprintf__", &tengo.UserFunction{
script.Add("__pp_filepath__", filePath)
script.Add("__pp_printf__", &tengo.UserFunction{
Value: func(args ...tengo.Object) (tengo.Object, error){
if len(args) < 1 {
return nil, tengo.ErrWrongNumArguments
@ -127,41 +128,64 @@ func (pp *Tengo) Eval(
gargs[i] = tengo.ToInterface(args[i+1])
//fmt.Printf("shit: %q\n", gargs[i])
}
str := fmt.Sprintf(format, gargs...)
return tengo.FromInterface([]byte(str))
fmt.Fprintf(&retBuf, format, gargs...)
return nil, nil
//return tengo.FromInterface([]byte(str))
},
})
script.Add("__sprint__", &tengo.UserFunction{
script.Add("__pp_print__", &tengo.UserFunction{
Value: func(args ...tengo.Object) (tengo.Object, error){
gargs := make([]any, len(args))
for i := range gargs {
gargs[i] = tengo.ToInterface(args[i])
}
str := fmt.Sprint(gargs...)
return tengo.FromInterface([]byte(str))
fmt.Fprint(&retBuf, gargs...)
return nil, nil
//return tengo.FromInterface([]byte(str))
},
})
script.Add("__sprintln__", &tengo.UserFunction{
script.Add("__pp_println__", &tengo.UserFunction{
Value: func(args ...tengo.Object) (tengo.Object, error){
gargs := make([]any, len(args))
for i := range gargs {
gargs[i] = tengo.ToInterface(args[i])
}
str := fmt.Sprintln(gargs...)
return tengo.FromInterface([]byte(str))
fmt.Fprintln(&retBuf, gargs...)
return nil, nil
//return tengo.FromInterface([]byte(str))
},
})
script.Add("__pp_write_raw__", &tengo.UserFunction{
Value: func(args ...tengo.Object) (tengo.Object, error){
bt := make([][]byte, len(args))
for i, o := range args {
bts, ok := tengo.ToByteSlice(o)
if !ok {
return nil, tengo.ErrInvalidArgumentType{
Expected: "string/bytes",
}
}
bt[i] = bts
}
for _, b := range bt {
retBuf.Write(b)
}
return nil, nil
},
})
script.Add("__separate__", &tengo.UserFunction{
Value: func(args ...tengo.Object) (tengo.Object, error){
if len(args) < 1 {
return nil, nil
/*if len(args) < 1 {
return nil, tengo.ErrWrongNumArguments
}
bts, ok := tengo.ToByteSlice(args[0])
if !ok {
return nil, tengo.ErrInvalidArgumentType{
}
}
rets = append(rets, bts)
}*/
//rets = append(rets, retBuf.Bytes())
//retBuf.Reset()
return nil, nil
},
})
@ -171,5 +195,5 @@ func (pp *Tengo) Eval(
return nil, err
}
return rets, nil
return retBuf.Bytes(), nil
}