Compare commits
8 Commits
24e6a293b0
...
866de759dd
Author | SHA1 | Date | |
---|---|---|---|
866de759dd | |||
b1d6b6de44 | |||
7e357eea62 | |||
d6b4c79736 | |||
d066344af8 | |||
f41dba069e | |||
703ecf6829 | |||
ba479a1b99 |
@ -43,16 +43,16 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
|
||||
return
|
||||
}
|
||||
|
||||
func CallExprFunction(parentCtx ExprContext, funcName string, params ...any) (v any, err error) {
|
||||
ctx := cloneContext(parentCtx)
|
||||
ctx.SetParent(parentCtx)
|
||||
// func CallExprFunction(parentCtx ExprContext, funcName string, params ...any) (v any, err error) {
|
||||
// ctx := cloneContext(parentCtx)
|
||||
// ctx.SetParent(parentCtx)
|
||||
|
||||
if err == nil {
|
||||
if err = checkFunctionCall(ctx, funcName, ¶ms); err == nil {
|
||||
if v, err = ctx.Call(funcName, params); err == nil {
|
||||
exportObjects(parentCtx, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// if err == nil {
|
||||
// if err = checkFunctionCall(ctx, funcName, ¶ms); err == nil {
|
||||
// if v, err = ctx.Call(funcName, params); err == nil {
|
||||
// exportObjects(parentCtx, ctx)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
@ -68,7 +68,7 @@ func createFileFunc(ctx ExprContext, name string, args []any) (result any, err e
|
||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||
}
|
||||
} else {
|
||||
err = errMissingFilePath("createFile")
|
||||
err = errMissingFilePath(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -80,7 +80,7 @@ func openFileFunc(ctx ExprContext, name string, args []any) (result any, err err
|
||||
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
||||
}
|
||||
} else {
|
||||
err = errMissingFilePath("openFile")
|
||||
err = errMissingFilePath(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -92,7 +92,7 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
|
||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||
}
|
||||
} else {
|
||||
err = errMissingFilePath("appendFile")
|
||||
err = errMissingFilePath(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -118,13 +118,13 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
|
||||
}
|
||||
}
|
||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||
err = errInvalidFileHandle("closeFileFunc", handle)
|
||||
err = errInvalidFileHandle(name, handle)
|
||||
}
|
||||
result = err == nil
|
||||
return
|
||||
}
|
||||
|
||||
func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
var handle osHandle
|
||||
var invalidFileHandle any
|
||||
var ok bool
|
||||
@ -142,12 +142,12 @@ func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
|
||||
}
|
||||
|
||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||
err = errInvalidFileHandle("writeFileFunc", invalidFileHandle)
|
||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
var handle osHandle
|
||||
var invalidFileHandle any
|
||||
var ok bool
|
||||
@ -165,23 +165,50 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
|
||||
limit = s[0]
|
||||
}
|
||||
|
||||
if v, err = r.reader.ReadString(limit); err == nil {
|
||||
if len(v) > 0 && v[len(v)-1] == limit {
|
||||
v, err = r.reader.ReadString(limit)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
if len(v) > 0 {
|
||||
if v[len(v)-1] == limit {
|
||||
result = v[0 : len(v)-1]
|
||||
} else {
|
||||
result = v
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
} else {
|
||||
invalidFileHandle = handle
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||
err = errInvalidFileHandle("readFileFunc", invalidFileHandle)
|
||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
var handle osHandle
|
||||
var invalidFileHandle any
|
||||
var ok bool
|
||||
|
||||
result = nil
|
||||
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
|
||||
invalidFileHandle = args[0]
|
||||
}
|
||||
|
||||
if handle != nil {
|
||||
if r, ok := handle.(*osReader); ok {
|
||||
var b []byte
|
||||
b, err = io.ReadAll(r.reader)
|
||||
result = string(b)
|
||||
} else {
|
||||
invalidFileHandle = handle
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -190,21 +217,30 @@ func ImportOsFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
})
|
||||
ctx.RegisterFunc("fileWrite", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
|
||||
|
||||
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
|
||||
NewFuncParam(TypeHandle),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
|
||||
NewFuncParam(TypeHandle),
|
||||
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
|
||||
})
|
||||
ctx.RegisterFunc("fileRead", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
|
||||
|
||||
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(TypeHandle),
|
||||
NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
|
||||
})
|
||||
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
|
||||
|
||||
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(TypeHandle),
|
||||
})
|
||||
}
|
||||
|
@ -41,3 +41,9 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func exportObjectsToParent(sourceCtx ExprContext) {
|
||||
if parentCtx := sourceCtx.GetParent(); parentCtx != nil {
|
||||
exportObjects(parentCtx, sourceCtx)
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ type ExprContext interface {
|
||||
SetParent(ctx ExprContext)
|
||||
GetParent() (ctx ExprContext)
|
||||
GetVar(varName string) (value any, exists bool)
|
||||
GetLast() any
|
||||
SetVar(varName string, value any)
|
||||
UnsafeSetVar(varName string, value any)
|
||||
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||
|
39
function.go
39
function.go
@ -199,3 +199,42 @@ func (info *funcInfo) MaxArgs() int {
|
||||
func (info *funcInfo) Functor() Functor {
|
||||
return info.functor
|
||||
}
|
||||
|
||||
// ----- Call a function ---
|
||||
|
||||
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
|
||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
||||
passedCount := len(*varParams)
|
||||
if info.MinArgs() > passedCount {
|
||||
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
||||
}
|
||||
for i, p := range info.Params() {
|
||||
if i >= passedCount {
|
||||
if !p.IsDefault() {
|
||||
break
|
||||
}
|
||||
*varParams = append(*varParams, p.DefaultValue())
|
||||
}
|
||||
}
|
||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
|
||||
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
|
||||
}
|
||||
if err == nil && owner != ctx {
|
||||
ctx.RegisterFuncInfo(info)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown function %s()", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CallFunction(parentCtx ExprContext, name string, params []any) (result any, err error) {
|
||||
ctx := cloneContext(parentCtx)
|
||||
ctx.SetParent(parentCtx)
|
||||
|
||||
if err = checkFunctionCall(ctx, name, ¶ms); err == nil {
|
||||
result, err = ctx.Call(name, params)
|
||||
exportObjectsToParent(ctx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// -------- function call term
|
||||
@ -22,35 +21,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||
}
|
||||
|
||||
// -------- eval func call
|
||||
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
|
||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
||||
passedCount := len(*varParams)
|
||||
if info.MinArgs() > passedCount {
|
||||
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
||||
}
|
||||
for i, p := range info.Params() {
|
||||
if i >= passedCount {
|
||||
if !p.IsDefault() {
|
||||
break
|
||||
}
|
||||
*varParams = append(*varParams, p.DefaultValue())
|
||||
}
|
||||
}
|
||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
|
||||
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
|
||||
}
|
||||
if err == nil && owner != ctx {
|
||||
ctx.RegisterFuncInfo(info)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown function %s()", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
||||
ctx := cloneContext(parentCtx)
|
||||
ctx.SetParent(parentCtx)
|
||||
func evalFuncCall(ctx ExprContext, self *term) (v any, err error) {
|
||||
name, _ := self.tk.Value.(string)
|
||||
params := make([]any, len(self.children), len(self.children)+5)
|
||||
for i, tree := range self.children {
|
||||
@ -62,11 +33,7 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if err = checkFunctionCall(ctx, name, ¶ms); err == nil {
|
||||
if v, err = ctx.Call(name, params); err == nil {
|
||||
exportObjects(parentCtx, ctx)
|
||||
}
|
||||
}
|
||||
v, err = CallFunction(ctx, name, params)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -87,13 +87,14 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
|
||||
v = string(unboxedValue[index])
|
||||
}
|
||||
case *DictType:
|
||||
var ok bool
|
||||
/* var ok bool
|
||||
var indexValue any
|
||||
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
||||
if v, ok = (*unboxedValue)[indexValue]; !ok {
|
||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||
}
|
||||
}
|
||||
} */
|
||||
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
||||
default:
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
@ -113,6 +114,29 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
|
||||
default:
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
} else if IsDict(leftValue) {
|
||||
d := leftValue.(*DictType)
|
||||
|
||||
/* var ok bool
|
||||
var indexValue any
|
||||
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
||||
if v, ok = (*d)[indexValue]; !ok {
|
||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||
}
|
||||
}*/
|
||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue any) (v any, err error) {
|
||||
var ok bool
|
||||
var indexValue any
|
||||
|
||||
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
||||
if v, ok = (*d)[indexValue]; !ok {
|
||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func pluginExists(name string) (exists bool) {
|
||||
|
||||
func makePluginName(name string) (decorated string) {
|
||||
var template string
|
||||
if execName, err := os.Executable(); err != nil || !strings.HasSuffix(execName, ".debug") {
|
||||
if execName, err := os.Executable(); err != nil || strings.Index(execName, "debug") < 0 {
|
||||
template = "expr-%s-plugin.so"
|
||||
} else {
|
||||
template = "expr-%s-plugin.so.debug"
|
||||
|
@ -7,7 +7,7 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
// "strings"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
type SimpleStore struct {
|
||||
@ -50,6 +50,7 @@ func (ctx *SimpleStore) Merge(src ExprContext) {
|
||||
ctx.funcStore[name], _ = src.GetFuncInfo(name)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
||||
sb.WriteString("vars: {\n")
|
||||
@ -165,6 +166,11 @@ func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ctx *SimpleStore) GetLast() (v any) {
|
||||
v = ctx.varStore["last"]
|
||||
return
|
||||
}
|
||||
|
||||
func (ctx *SimpleStore) UnsafeSetVar(varName string, value any) {
|
||||
// fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value)
|
||||
ctx.varStore[varName] = value
|
||||
|
@ -16,10 +16,10 @@ func TestFuncOs(t *testing.T) {
|
||||
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
|
||||
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
|
||||
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
|
||||
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWrite(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
||||
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileRead(handle, "-"); fileClose(handle);word`, "bye", nil},
|
||||
/* 7 */ {`builtin "os.file"; word=fileRead(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
|
||||
/* 7 */ {`builtin "os.file"; fileWrite(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
|
||||
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
||||
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
|
||||
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)},
|
||||
/* 7 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
@ -31,6 +31,7 @@ func TestDictParser(t *testing.T) {
|
||||
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, map[any]any{"a": 9, "b": 2}, nil},
|
||||
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
|
||||
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
|
||||
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
|
||||
}
|
||||
|
||||
succeeded := 0
|
||||
|
@ -14,7 +14,7 @@ func TestExpr(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`0?{}`, nil, nil},
|
||||
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileRead(f); fileClose(f); line`, "uno", nil},
|
||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||
/* 6 */ {`
|
||||
|
@ -14,9 +14,9 @@ func TestIteratorParser(t *testing.T) {
|
||||
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
|
||||
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
|
||||
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil},
|
||||
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
|
||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
||||
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
|
||||
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
|
||||
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
||||
}
|
||||
// inputs1 := []inputType{
|
||||
|
@ -1,23 +1,22 @@
|
||||
builtin ["os.file", "base"];
|
||||
|
||||
readInt=func(fh){
|
||||
line=fileRead(fh);
|
||||
line=fileReadText(fh);
|
||||
line ? [nil] {nil} :: {int(line)}
|
||||
};
|
||||
|
||||
ds={
|
||||
"init":func(filename){
|
||||
fh=fileOpen(filename);
|
||||
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
|
||||
fh ? [nil] {nil} :: { @current=readInt(fh) };
|
||||
fh
|
||||
},
|
||||
"current":func(){
|
||||
prev
|
||||
current
|
||||
},
|
||||
"next":func(fh){
|
||||
current ?
|
||||
[nil] {current}
|
||||
:: {@prev=current; @current=readInt(fh) but current}
|
||||
@current=readInt(fh);
|
||||
current
|
||||
},
|
||||
"clean":func(fh){
|
||||
fileClose(fh)
|
||||
@ -25,9 +24,9 @@ ds={
|
||||
}
|
||||
|
||||
//;f=$(ds, "int.list")
|
||||
/*
|
||||
;f++
|
||||
;f++
|
||||
;f++
|
||||
*/
|
||||
|
||||
//;f++
|
||||
//;f++
|
||||
//;f++
|
||||
//*/
|
||||
//;add(f)
|
||||
|
Loading…
Reference in New Issue
Block a user