feat: added the sjson module.
This commit is contained in:
parent
c5b303bd15
commit
784b2dbfdf
14 changed files with 736 additions and 1 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/exe/
|
3
build.sh
Executable file
3
build.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
go build -o ./exe/ ./
|
97
htmlx/element.go
Normal file
97
htmlx/element.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package htmlx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/v2"
|
||||||
|
"strings"
|
||||||
|
"html"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RawTag = "raw"
|
||||||
|
|
||||||
|
// The type implements basic
|
||||||
|
// way to structrize HTML elements.
|
||||||
|
type Element struct {
|
||||||
|
tengo.ObjectImpl
|
||||||
|
Tag string
|
||||||
|
Attr map[string] string
|
||||||
|
Children []*Element
|
||||||
|
// The value makes sense only if
|
||||||
|
// the tag is the "raw"
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *Element) TypeName() string {
|
||||||
|
return "*HTMLElement"
|
||||||
|
}
|
||||||
|
|
||||||
|
// The method renders the element to it's
|
||||||
|
// HTML representation.
|
||||||
|
func (el *Element) String() string {
|
||||||
|
if el.Tag == RawTag {
|
||||||
|
return html.EscapeString(el.Content)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
|
||||||
|
fmt.Fprintf(&b, "<%s", el.Tag)
|
||||||
|
for k, v := range el.Attr {
|
||||||
|
fmt.Fprintf(&b, " %s=%q", k, v)
|
||||||
|
}
|
||||||
|
fmt.Fprint(&b, ">")
|
||||||
|
|
||||||
|
for _, child := range el.Children {
|
||||||
|
if child == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprint(&b, child.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(&b, "</%s>", el.Tag)
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *Element) Body(els ...*Element) *Element {
|
||||||
|
el.Children = els
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *Element) IndexGet(
|
||||||
|
index tengo.Object,
|
||||||
|
) (tengo.Object, error) {
|
||||||
|
arg, ok := tengo.ToString(index)
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidIndexValueType
|
||||||
|
}
|
||||||
|
|
||||||
|
switch arg {
|
||||||
|
case "body" :
|
||||||
|
return &tengo.UserFunction{
|
||||||
|
Name: "Element.Body",
|
||||||
|
Value: func(
|
||||||
|
args ...tengo.Object,
|
||||||
|
) (tengo.Object, error) {
|
||||||
|
s := []*Element{}
|
||||||
|
for _, arg := range args {
|
||||||
|
el, ok := arg.(*Element)
|
||||||
|
if !ok {
|
||||||
|
str, ok := tengo.ToString(arg)
|
||||||
|
if ok {
|
||||||
|
s = append(s, &Element{
|
||||||
|
Tag: RawTag,
|
||||||
|
Content: str,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s = append(s, el)
|
||||||
|
}
|
||||||
|
return el.Body(s...), nil
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
86
htmlx/html.go
Normal file
86
htmlx/html.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package htmlx
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/v2"
|
||||||
|
|
||||||
|
type HTML struct{
|
||||||
|
tengo.ObjectImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
html.div({
|
||||||
|
id: "some-el-id",
|
||||||
|
value: "shit value"
|
||||||
|
}).body(
|
||||||
|
html.raw("cock "),
|
||||||
|
html.strong("something")
|
||||||
|
)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (html *HTML) IndexGet(
|
||||||
|
index tengo.Object,
|
||||||
|
) (tengo.Object, error) {
|
||||||
|
str, ok := tengo.ToString(index)
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidIndexValueType
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := func(args ...tengo.Object) (tengo.Object, error) {
|
||||||
|
if len(args) > 1 {
|
||||||
|
return nil, tengo.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
var arg tengo.Object
|
||||||
|
if len(args) == 1 {
|
||||||
|
arg = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg == nil {
|
||||||
|
return &Element{
|
||||||
|
Tag: str,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if can := arg.CanIterate() ; !can {
|
||||||
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "first",
|
||||||
|
Expected: "iterable",
|
||||||
|
Found: arg.TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attr := map[string] string{}
|
||||||
|
iter := arg.Iterate()
|
||||||
|
for iter.Next() {
|
||||||
|
key, val := iter.Key(), iter.Value()
|
||||||
|
skey, ok := tengo.ToString(key)
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "attribute(key)",
|
||||||
|
Expected: "stringer",
|
||||||
|
Found: key.TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sval, ok := tengo.ToString(val)
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "attribute(value)",
|
||||||
|
Expected: "stringer",
|
||||||
|
Found: val.TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attr[skey] = sval
|
||||||
|
}
|
||||||
|
return &Element{
|
||||||
|
Tag: str,
|
||||||
|
Attr: attr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tengo.UserFunction{
|
||||||
|
Name: str,
|
||||||
|
Value: fn,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
191
httpx/handler.go
Normal file
191
httpx/handler.go
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
package httpx
|
||||||
|
|
||||||
|
import (
|
||||||
|
//"github.com/d5/tengo/v2"
|
||||||
|
"surdeus.su/util/tpp/mdx"
|
||||||
|
"surdeus.su/util/tpp/htmlx"
|
||||||
|
"surdeus.su/util/tpp"
|
||||||
|
"path/filepath"
|
||||||
|
"net/http"
|
||||||
|
"context"
|
||||||
|
"path"
|
||||||
|
"mime"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
// Preprocessor must be set by user
|
||||||
|
// to be able to bring custom features in.
|
||||||
|
pp *tpp.Preprocessor
|
||||||
|
// Aditional extension. ".tpp" by default.
|
||||||
|
// For example "file.html.tpp" will be
|
||||||
|
// first preprocessed TPP and sent back as simple HTML.
|
||||||
|
ext string
|
||||||
|
|
||||||
|
// The global map accessable from all the
|
||||||
|
// request so you can keep the states
|
||||||
|
// between requests.
|
||||||
|
global any
|
||||||
|
md *mdx.Markdown
|
||||||
|
html *htmlx.HTML
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the new Handler with
|
||||||
|
// specified preprocessor, source path,
|
||||||
|
// preprocessor extension and the global value.
|
||||||
|
func NewHandler(
|
||||||
|
pp *tpp.Preprocessor,
|
||||||
|
src, ext string,
|
||||||
|
global any,
|
||||||
|
) *Handler {
|
||||||
|
ret := &Handler{}
|
||||||
|
ret.sourcePath = src
|
||||||
|
ret.ext = ext
|
||||||
|
ret.pp = pp
|
||||||
|
ret.global = global
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the default tpp.Preprocessor suitable for
|
||||||
|
// the most needs.
|
||||||
|
func DefaultPP(mod string) *tpp.Preprocessor {
|
||||||
|
t := tpp.NewTengo().SetPreCompile(func(
|
||||||
|
ctx context.Context,
|
||||||
|
s *tpp.Script,
|
||||||
|
) {
|
||||||
|
s.SetImportDir(mod)
|
||||||
|
s.SetImports(&ModuleGetter{})
|
||||||
|
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({
|
||||||
|
request : __http_request__
|
||||||
|
})
|
||||||
|
html := __html__
|
||||||
|
context.http = http
|
||||||
|
context.pp = pp
|
||||||
|
context.global = __global__
|
||||||
|
context.html = html
|
||||||
|
import("./pre")(context)
|
||||||
|
`)
|
||||||
|
}).SetPostCode(func(ctx context.Context) []byte {
|
||||||
|
return []byte(`
|
||||||
|
import("./post")(context)
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
return tpp.New(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
r *http.Request,
|
||||||
|
) {
|
||||||
|
shouldProcess := true
|
||||||
|
urlPath := r.URL.Path
|
||||||
|
// Cleaning URL path to prevent injections.
|
||||||
|
urlPath = path.Clean(urlPath)
|
||||||
|
urlExt := path.Ext(urlPath)
|
||||||
|
|
||||||
|
filePath := filepath.Join(
|
||||||
|
filepath.FromSlash(h.sourcePath),
|
||||||
|
filepath.FromSlash(urlPath),
|
||||||
|
)
|
||||||
|
filePathTpp := filePath + h.ext
|
||||||
|
|
||||||
|
//log.Println("pth:", urlPath, filePathTpp)
|
||||||
|
file, err := os.Open(filePathTpp)
|
||||||
|
if err != nil {
|
||||||
|
shouldProcess = false
|
||||||
|
file, err = os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//process := true
|
||||||
|
fileData, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.WithValue(
|
||||||
|
r.Context(),
|
||||||
|
KeyRequest,
|
||||||
|
&Request{
|
||||||
|
Request: r,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = context.WithValue(
|
||||||
|
ctx,
|
||||||
|
KeyGlobal,
|
||||||
|
h.global,
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = context.WithValue(
|
||||||
|
ctx,
|
||||||
|
KeyMarkdown,
|
||||||
|
h.md,
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = context.WithValue(
|
||||||
|
ctx,
|
||||||
|
KeyHTML,
|
||||||
|
h.html,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Setting before the code to let it change own
|
||||||
|
// content type.
|
||||||
|
contentType := mime.TypeByExtension(urlExt)
|
||||||
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
processedData := fileData
|
||||||
|
if shouldProcess {
|
||||||
|
processedData, err = h.pp.Process(
|
||||||
|
ctx,
|
||||||
|
true,
|
||||||
|
filePathTpp,
|
||||||
|
fileData,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
log.Printf(
|
||||||
|
"Error: pp.Process(...): %s\n",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(processedData)
|
||||||
|
}
|
2
httpx/http.go
Normal file
2
httpx/http.go
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
package httpx
|
||||||
|
|
24
httpx/module.go
Normal file
24
httpx/module.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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)
|
||||||
|
}
|
49
httpx/request.go
Normal file
49
httpx/request.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
52
httpx/tool.go
Normal file
52
httpx/tool.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package httpx
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"github.com/d5/tengo/v2"
|
||||||
|
//"github.com/d5/tengo/v2/stdlib"
|
||||||
|
"surdeus.su/util/tpp/mdx"
|
||||||
|
"surdeus.su/core/cli/mtool"
|
||||||
|
"net/http"
|
||||||
|
"log"
|
||||||
|
//"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context key type for internal usage.
|
||||||
|
type CKey string
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyRequest CKey = "http-request"
|
||||||
|
KeyGlobal = "global"
|
||||||
|
KeyMarkdown = "markdown"
|
||||||
|
KeyHTML = "html"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Simple PHP-like server implementation.
|
||||||
|
var Tool = mtool.T("tht").Func(func(flags *mtool.Flags) {
|
||||||
|
var (
|
||||||
|
addr, ext, src, mod 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.Parse()
|
||||||
|
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: addr,
|
||||||
|
Handler: NewHandler(
|
||||||
|
DefaultPP(mod),
|
||||||
|
src, ext,
|
||||||
|
map[string] tengo.Object{},
|
||||||
|
).SetMD(mdx.MakeDefaultMarkdown()),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Listening on %q\n", addr)
|
||||||
|
err := srv.ListenAndServe()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error: srv.ListenAndServe(...): %s\n", err)
|
||||||
|
}
|
||||||
|
})
|
76
httpx/url.go
Normal file
76
httpx/url.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
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
|
||||||
|
}
|
106
sjson/dec.go
Normal file
106
sjson/dec.go
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package sjson
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/v2"
|
||||||
|
import "encoding/json"
|
||||||
|
import "io"
|
||||||
|
import "errors"
|
||||||
|
import tjson "github.com/d5/tengo/v2/stdlib/json"
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
type Decoder struct {
|
||||||
|
tengo.ObjectImpl
|
||||||
|
dec *json.Decoder
|
||||||
|
end bool
|
||||||
|
inputName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) TypeName() string {
|
||||||
|
return "*sjson.Decoder"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) String() string {
|
||||||
|
return "sjson.Decoder{...}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecoder(args ...tengo.Object) (tengo.Object, error) {
|
||||||
|
var inputName string
|
||||||
|
if len(args) == 0 {
|
||||||
|
inputName = StrStdin
|
||||||
|
} else if len(args) > 1 {
|
||||||
|
return nil, tengo.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
inputObject := args[0]
|
||||||
|
inputName, ok := tengo.ToString(inputObject)
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "first",
|
||||||
|
Expected: "stringer",
|
||||||
|
Found: inputObject.TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var reader io.Reader
|
||||||
|
var err error
|
||||||
|
switch {
|
||||||
|
case inputName == StrStdin :
|
||||||
|
reader = os.Stdin
|
||||||
|
default:
|
||||||
|
reader, err = os.Open(inputName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := &Decoder{}
|
||||||
|
ret.dec = json.NewDecoder(reader)
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) IndexGet(
|
||||||
|
index tengo.Object,
|
||||||
|
) (tengo.Object, error) {
|
||||||
|
key, ok := tengo.ToString(index)
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidIndexValueType
|
||||||
|
}
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "decode" :
|
||||||
|
return &tengo.UserFunction{
|
||||||
|
Name: "decode",
|
||||||
|
Value: d.Decode,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing found.
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) Decode(
|
||||||
|
args ...tengo.Object,
|
||||||
|
) (tengo.Object, error) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return nil, tengo.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
if d.end {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var v any
|
||||||
|
err := d.dec.Decode(&v)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
d.end = true
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tjson.Decode(bts)
|
||||||
|
}
|
33
sjson/sjson.go
Normal file
33
sjson/sjson.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package sjson
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/v2"
|
||||||
|
import "io"
|
||||||
|
import "encoding/json"
|
||||||
|
//import "github.com/d5/tengo/v2/stdlib"
|
||||||
|
|
||||||
|
const (
|
||||||
|
StrStdin = "<stdin>"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Module = map[string]tengo.Object{
|
||||||
|
"__check": &tengo.BuiltinFunction{
|
||||||
|
Name: "__check",
|
||||||
|
Value: func(
|
||||||
|
args ...tengo.Object,
|
||||||
|
) (tengo.Object, error) {
|
||||||
|
return tengo.FromInterface("Hello, Check!")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"new_decoder": &tengo.BuiltinFunction{
|
||||||
|
Name: "new_decoder",
|
||||||
|
Value: NewDecoder,
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encoder struct {
|
||||||
|
enc *json.Encoder
|
||||||
|
output io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
//func
|
13
tests/sjson.xgo
Normal file
13
tests/sjson.xgo
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
sjson := import("sjson")
|
||||||
|
fmt := import("fmt")
|
||||||
|
fmt.println(sjson.__check())
|
||||||
|
|
||||||
|
dec := sjson.new_decoder("<stdin>")
|
||||||
|
|
||||||
|
for {
|
||||||
|
v := dec.decode()
|
||||||
|
if !v {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.println(v)
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
||||||
"github.com/d5/tengo/v2"
|
"github.com/d5/tengo/v2"
|
||||||
"github.com/d5/tengo/v2/parser"
|
"github.com/d5/tengo/v2/parser"
|
||||||
"github.com/d5/tengo/v2/stdlib"
|
"github.com/d5/tengo/v2/stdlib"
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
import "surdeus.su/core/cli/mtool"
|
import "surdeus.su/core/cli/mtool"
|
||||||
|
import "surdeus.su/core/xgo/sjson"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sourceFileExt = ".xgo"
|
sourceFileExt = ".xgo"
|
||||||
|
@ -50,8 +52,8 @@ func Run(flags *mtool.Flags) {
|
||||||
fmt.Println(version)
|
fmt.Println(version)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
modules := stdlib.GetModuleMap(stdlib.AllModuleNames()...)
|
modules := stdlib.GetModuleMap(stdlib.AllModuleNames()...)
|
||||||
|
modules.AddBuiltinModule("sjson", sjson.Module)
|
||||||
if len(iargs) == 0 {
|
if len(iargs) == 0 {
|
||||||
// REPL
|
// REPL
|
||||||
RunREPL(modules, os.Stdin, os.Stdout)
|
RunREPL(modules, os.Stdin, os.Stdout)
|
||||||
|
|
Loading…
Reference in a new issue