Expr's functions now support parameters with default value
This commit is contained in:
parent
f66cd1fdb1
commit
9bba40f155
@ -168,11 +168,11 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
|
|
||||||
func ImportMathFuncs(ctx ExprContext) {
|
func ImportMathFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{
|
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{
|
||||||
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
|
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(0)),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{
|
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{
|
||||||
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 1),
|
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(1)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
104
func-os.go
104
func-os.go
@ -49,55 +49,8 @@ func (h *osReader) getFile() *os.File {
|
|||||||
return h.fh
|
return h.fh
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func errMissingFilePath(funcName string) error {
|
||||||
var filePath string
|
return fmt.Errorf("%s(): missing or invalid file path", funcName)
|
||||||
if len(args) > 0 {
|
|
||||||
filePath, _ = args[0].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(filePath) > 0 {
|
|
||||||
var fh *os.File
|
|
||||||
if fh, err = os.Create(filePath); err == nil {
|
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s(): missing the file path", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|
||||||
var filePath string
|
|
||||||
if len(args) > 0 {
|
|
||||||
filePath, _ = args[0].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(filePath) > 0 {
|
|
||||||
var fh *os.File
|
|
||||||
if fh, err = os.Open(filePath); err == nil {
|
|
||||||
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s(): missing the file path", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|
||||||
var filePath string
|
|
||||||
if len(args) > 0 {
|
|
||||||
filePath, _ = args[0].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(filePath) > 0 {
|
|
||||||
var fh *os.File
|
|
||||||
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
|
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s(): missing the file path", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func errInvalidFileHandle(funcName string, v any) error {
|
func errInvalidFileHandle(funcName string, v any) error {
|
||||||
@ -108,16 +61,50 @@ func errInvalidFileHandle(funcName string, v any) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Create(filePath); err == nil {
|
||||||
|
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = errMissingFilePath("createFile")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Open(filePath); err == nil {
|
||||||
|
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = errMissingFilePath("openFile")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
|
||||||
|
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = errMissingFilePath("openFile")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle osHandle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
|
|
||||||
if len(args) > 0 {
|
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if handle, ok = args[0].(osHandle); !ok {
|
if handle, ok = args[0].(osHandle); !ok {
|
||||||
invalidFileHandle = args[0]
|
invalidFileHandle = args[0]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if fh := handle.getFile(); fh != nil {
|
if fh := handle.getFile(); fh != nil {
|
||||||
@ -140,13 +127,11 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
|
|||||||
func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle osHandle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
|
|
||||||
if len(args) > 0 {
|
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if handle, ok = args[0].(osHandle); !ok {
|
if handle, ok = args[0].(osHandle); !ok {
|
||||||
invalidFileHandle = args[0]
|
invalidFileHandle = args[0]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if w, ok := handle.(*osWriter); ok {
|
if w, ok := handle.(*osWriter); ok {
|
||||||
@ -165,24 +150,21 @@ func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
|
|||||||
func readFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func readFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle osHandle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
|
var ok bool
|
||||||
|
|
||||||
result = nil
|
result = nil
|
||||||
if len(args) > 0 {
|
|
||||||
var ok bool
|
|
||||||
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
|
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
|
||||||
invalidFileHandle = args[0]
|
invalidFileHandle = args[0]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if r, ok := handle.(*osReader); ok {
|
if r, ok := handle.(*osReader); ok {
|
||||||
var limit byte = '\n'
|
var limit byte = '\n'
|
||||||
var v string
|
var v string
|
||||||
if len(args) > 1 {
|
|
||||||
if s, ok := args[1].(string); ok && len(s) > 0 {
|
if s, ok := args[1].(string); ok && len(s) > 0 {
|
||||||
limit = s[0]
|
limit = s[0]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if v, err = r.reader.ReadString(limit); err == nil {
|
if v, err = r.reader.ReadString(limit); err == nil {
|
||||||
if len(v) > 0 && v[len(v)-1] == limit {
|
if len(v) > 0 && v[len(v)-1] == limit {
|
||||||
result = v[0 : len(v)-1]
|
result = v[0 : len(v)-1]
|
||||||
@ -220,7 +202,7 @@ func ImportOsFuncs(ctx ExprContext) {
|
|||||||
})
|
})
|
||||||
ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{
|
ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{
|
||||||
newFuncParam(typeHandle),
|
newFuncParam(typeHandle),
|
||||||
newFuncParamFlagDef("limitCh", pfOptional, "\\n"),
|
newFuncParamFlagDef("limitCh", pfOptional, "\n"),
|
||||||
})
|
})
|
||||||
ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{
|
ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{
|
||||||
newFuncParam(typeHandle),
|
newFuncParam(typeHandle),
|
||||||
|
@ -65,19 +65,19 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
|
|||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[0].(string); !ok {
|
||||||
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
||||||
}
|
}
|
||||||
if len(args) > 1 {
|
|
||||||
if start, err = toInt(args[1], name+"()"); err != nil {
|
if start, err = toInt(args[1], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(args) > 2 {
|
|
||||||
if count, err = toInt(args[2], name+"()"); err != nil {
|
if count, err = toInt(args[2], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = len(source) + start
|
start = len(source) + start
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if count < 0 {
|
if count < 0 {
|
||||||
count = len(source) - start
|
count = len(source) - start
|
||||||
}
|
}
|
||||||
@ -152,18 +152,17 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
|
|||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[0].(string); !ok {
|
||||||
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
||||||
}
|
}
|
||||||
if len(args) >= 2 {
|
|
||||||
if sep, ok = args[1].(string); !ok {
|
if sep, ok = args[1].(string); !ok {
|
||||||
return nil, fmt.Errorf("separator param must be string, got %T (%v)", args[1], args[1])
|
return nil, fmt.Errorf("separator param must be string, got %T (%v)", args[1], args[1])
|
||||||
}
|
}
|
||||||
if len(args) >= 3 {
|
|
||||||
if count64, ok := args[2].(int64); ok { // TODO replace type assertion with toInt()
|
if count64, ok := args[2].(int64); ok { // TODO replace type assertion with toInt()
|
||||||
count = int(count64)
|
count = int(count64)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("part count must be integer, got %T (%v)", args[2], args[2])
|
return nil, fmt.Errorf("part count must be integer, got %T (%v)", args[2], args[2])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
parts = strings.SplitN(source, sep, count)
|
parts = strings.SplitN(source, sep, count)
|
||||||
} else if count < 0 {
|
} else if count < 0 {
|
||||||
@ -190,14 +189,14 @@ func ImportStringFuncs(ctx ExprContext) {
|
|||||||
|
|
||||||
ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{
|
ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{
|
||||||
newFuncParam(paramSource),
|
newFuncParam(paramSource),
|
||||||
newFuncParamFlagDef(paramStart, pfOptional, 0),
|
newFuncParamFlagDef(paramStart, pfOptional, int64(0)),
|
||||||
newFuncParamFlagDef(paramCount, pfOptional, -1),
|
newFuncParamFlagDef(paramCount, pfOptional, int64(-1)),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{
|
ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{
|
||||||
newFuncParam(paramSource),
|
newFuncParam(paramSource),
|
||||||
newFuncParamFlagDef(paramSeparator, pfOptional, ""),
|
newFuncParamFlagDef(paramSeparator, pfOptional, ""),
|
||||||
newFuncParamFlagDef(paramCount, pfOptional, -1),
|
newFuncParamFlagDef(paramCount, pfOptional, int64(-1)),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{
|
ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{
|
||||||
|
14
function.go
14
function.go
@ -59,12 +59,16 @@ func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (
|
|||||||
// ---- Linking with Expr functions
|
// ---- Linking with Expr functions
|
||||||
type exprFunctor struct {
|
type exprFunctor struct {
|
||||||
baseFunctor
|
baseFunctor
|
||||||
params []string
|
params []ExprFuncParam
|
||||||
expr Expr
|
expr Expr
|
||||||
defCtx ExprContext
|
defCtx ExprContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
|
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
|
||||||
|
// return &exprFunctor{expr: e, params: params, defCtx: ctx}
|
||||||
|
// }
|
||||||
|
|
||||||
|
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
|
||||||
return &exprFunctor{expr: e, params: params, defCtx: ctx}
|
return &exprFunctor{expr: e, params: params, defCtx: ctx}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,12 +83,12 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
|
|||||||
if funcArg, ok := arg.(Functor); ok {
|
if funcArg, ok := arg.(Functor); ok {
|
||||||
// ctx.RegisterFunc(p, functor, 0, -1)
|
// ctx.RegisterFunc(p, functor, 0, -1)
|
||||||
paramSpecs := funcArg.GetParams()
|
paramSpecs := funcArg.GetParams()
|
||||||
ctx.RegisterFunc(p, funcArg, typeAny, paramSpecs)
|
ctx.RegisterFunc(p.Name(), funcArg, typeAny, paramSpecs)
|
||||||
} else {
|
} else {
|
||||||
ctx.UnsafeSetVar(p, arg)
|
ctx.UnsafeSetVar(p.Name(), arg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.UnsafeSetVar(p, nil)
|
ctx.UnsafeSetVar(p.Name(), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result, err = functor.expr.eval(ctx, false)
|
result, err = functor.expr.eval(ctx, false)
|
||||||
|
@ -22,13 +22,22 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func call
|
// -------- eval func call
|
||||||
func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
|
||||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
||||||
if info.MinArgs() > len(params) {
|
passedCount := len(*varParams)
|
||||||
err = errTooFewParams(name, info.MinArgs(), info.MaxArgs(), len(params))
|
if info.MinArgs() > passedCount {
|
||||||
|
err = errTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
||||||
}
|
}
|
||||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
for i, p := range info.Params() {
|
||||||
err = errTooMuchParams(name, info.MaxArgs(), len(params))
|
if i >= passedCount {
|
||||||
|
if !p.IsOptional() {
|
||||||
|
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 {
|
if err == nil && owner != ctx {
|
||||||
// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
||||||
@ -44,7 +53,7 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
|||||||
ctx := cloneContext(parentCtx)
|
ctx := cloneContext(parentCtx)
|
||||||
name, _ := self.tk.Value.(string)
|
name, _ := self.tk.Value.(string)
|
||||||
// fmt.Printf("Call %s(), context: %p\n", name, ctx)
|
// fmt.Printf("Call %s(), context: %p\n", name, ctx)
|
||||||
params := make([]any, len(self.children))
|
params := make([]any, len(self.children), len(self.children)+5)
|
||||||
for i, tree := range self.children {
|
for i, tree := range self.children {
|
||||||
var param any
|
var param any
|
||||||
if param, err = tree.compute(ctx); err != nil {
|
if param, err = tree.compute(ctx); err != nil {
|
||||||
@ -52,8 +61,9 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
params[i] = param
|
params[i] = param
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if err = checkFunctionCall(ctx, name, params); err == nil {
|
if err = checkFunctionCall(ctx, name, ¶ms); err == nil {
|
||||||
if v, err = ctx.Call(name, params); err == nil {
|
if v, err = ctx.Call(name, params); err == nil {
|
||||||
exportObjects(parentCtx, ctx)
|
exportObjects(parentCtx, ctx)
|
||||||
}
|
}
|
||||||
@ -75,12 +85,35 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func def
|
// -------- eval func def
|
||||||
|
// func _evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
// bodySpec := self.value()
|
||||||
|
// if expr, ok := bodySpec.(*ast); ok {
|
||||||
|
// paramList := make([]string, 0, len(self.children))
|
||||||
|
// for _, param := range self.children {
|
||||||
|
// paramList = append(paramList, param.source())
|
||||||
|
// }
|
||||||
|
// v = newExprFunctor(expr, paramList, ctx)
|
||||||
|
// } else {
|
||||||
|
// err = errors.New("invalid function definition: the body specification must be an expression")
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
||||||
bodySpec := self.value()
|
bodySpec := self.value()
|
||||||
if expr, ok := bodySpec.(*ast); ok {
|
if expr, ok := bodySpec.(*ast); ok {
|
||||||
paramList := make([]string, 0, len(self.children))
|
paramList := make([]ExprFuncParam, 0, len(self.children))
|
||||||
for _, param := range self.children {
|
for _, param := range self.children {
|
||||||
paramList = append(paramList, param.source())
|
var defValue any
|
||||||
|
flags := paramFlags(0)
|
||||||
|
if len(param.children) > 0 {
|
||||||
|
flags |= pfOptional
|
||||||
|
if defValue, err = param.children[0].compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info := newFuncParamFlagDef(param.source(), flags, defValue)
|
||||||
|
paramList = append(paramList, info)
|
||||||
}
|
}
|
||||||
v = newExprFunctor(expr, paramList, ctx)
|
v = newExprFunctor(expr, paramList, ctx)
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,8 +10,6 @@ import "fmt"
|
|||||||
func newVarTerm(tk *Token) *term {
|
func newVarTerm(tk *Token) *term {
|
||||||
t := &term{
|
t := &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classVar,
|
|
||||||
// kind: kindUnknown,
|
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: nil,
|
children: nil,
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
|
@ -36,12 +36,9 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
// ctx.RegisterFuncInfo(info)
|
// ctx.RegisterFuncInfo(info)
|
||||||
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
|
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
|
||||||
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
||||||
paramSpecs := ForAll(funcDef.params, newFuncParam)
|
// paramSpecs := ForAll(funcDef.params, newFuncParam)
|
||||||
// paramCount := len(funcDef.params)
|
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p })
|
||||||
// paramSpecs := make([]ExprFuncParam, paramCount)
|
|
||||||
// for i := range paramSpecs {
|
|
||||||
// paramSpecs[i] = newFuncParam(funcDef.params[i])
|
|
||||||
// }
|
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, typeAny, paramSpecs)
|
ctx.RegisterFunc(leftTerm.source(), functor, typeAny, paramSpecs)
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("unknown function %s()", funcName)
|
err = self.Errorf("unknown function %s()", funcName)
|
||||||
|
101
parser.go
101
parser.go
@ -23,34 +23,28 @@ func NewParser(ctx ExprContext) (p *parser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
||||||
// name, _ := tk.Value.(string)
|
|
||||||
// funcObj := self.ctx.GetFuncInfo(name)
|
|
||||||
// if funcObj == nil {
|
|
||||||
// err = fmt.Errorf("unknown function %s()", name)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// maxArgs := funcObj.MaxArgs()
|
|
||||||
// if maxArgs < 0 {
|
|
||||||
// maxArgs = funcObj.MinArgs() + 10
|
|
||||||
// }
|
|
||||||
// args := make([]*term, 0, maxArgs)
|
|
||||||
args := make([]*term, 0, 10)
|
args := make([]*term, 0, 10)
|
||||||
|
itemExpected := false
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
||||||
if subTree.root != nil {
|
|
||||||
args = append(args, subTree.root)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
prev := scanner.Previous()
|
||||||
|
if subTree.root != nil {
|
||||||
|
args = append(args, subTree.root)
|
||||||
|
} else if itemExpected {
|
||||||
|
err = prev.ErrorExpectedGot("function-param-value")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
itemExpected = prev.Sym == SymComma
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
|
||||||
if lastSym != SymClosedRound {
|
if lastSym != SymClosedRound {
|
||||||
err = errors.New("unterminate arguments list")
|
err = errors.New("unterminated arguments list")
|
||||||
} else {
|
} else {
|
||||||
tree = newFuncCallTerm(tk, args)
|
tree = newFuncCallTerm(tk, args)
|
||||||
}
|
}
|
||||||
@ -58,62 +52,12 @@ func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|
||||||
// // Example: "add = func(x,y) {x+y}
|
|
||||||
// var body *ast
|
|
||||||
// args := make([]*term, 0)
|
|
||||||
// lastSym := SymUnknown
|
|
||||||
// itemExpected := false
|
|
||||||
// tk := scanner.Previous()
|
|
||||||
// for lastSym != SymClosedRound && lastSym != SymEos {
|
|
||||||
// var subTree *ast
|
|
||||||
// if subTree, err = self.parseItem(scanner, true, SymComma, SymClosedRound); err == nil {
|
|
||||||
// if subTree.root != nil {
|
|
||||||
// if subTree.root.symbol() == SymIdentifier {
|
|
||||||
// args = append(args, subTree.root)
|
|
||||||
// } else {
|
|
||||||
// err = tk.ErrorExpectedGotString("param-name", subTree.root.String())
|
|
||||||
// }
|
|
||||||
// } else if itemExpected {
|
|
||||||
// prev := scanner.Previous()
|
|
||||||
// err = prev.ErrorExpectedGot("function-param")
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// lastSym = scanner.Previous().Sym
|
|
||||||
// itemExpected = lastSym == SymComma
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if err == nil && lastSym != SymClosedRound {
|
|
||||||
// err = tk.ErrorExpectedGot(")")
|
|
||||||
// }
|
|
||||||
// if err == nil {
|
|
||||||
// tk = scanner.Next()
|
|
||||||
// if tk.Sym == SymOpenBrace {
|
|
||||||
// body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
|
|
||||||
// } else {
|
|
||||||
// err = tk.ErrorExpectedGot("{")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if err == nil {
|
|
||||||
// // TODO Check arguments
|
|
||||||
// if scanner.Previous().Sym != SymClosedBrace {
|
|
||||||
// err = scanner.Previous().ErrorExpectedGot("}")
|
|
||||||
// } else {
|
|
||||||
// tk = scanner.makeValueToken(SymExpression, "", body)
|
|
||||||
// tree = newFuncDefTerm(tk, args)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||||
// Example: "add = func(x,y) {x+y}
|
// Example: "add = func(x,y) {x+y}
|
||||||
var body *ast
|
var body *ast
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
|
defaultParamsStarted := false
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
@ -122,9 +66,20 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
param := newTerm(tk)
|
param := newTerm(tk)
|
||||||
args = append(args, param)
|
args = append(args, param)
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
|
if tk.Sym == SymEqual {
|
||||||
|
var paramExpr *ast
|
||||||
|
defaultParamsStarted = true
|
||||||
|
if paramExpr, err = self.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
param.forceChild(paramExpr.root)
|
||||||
|
} else if defaultParamsStarted {
|
||||||
|
err = tk.Errorf("can't mix default and non-default parameters")
|
||||||
|
break
|
||||||
|
}
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.ErrorExpectedGot("function-param")
|
err = prev.ErrorExpectedGot("function-param-spec")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
@ -143,7 +98,6 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
|
||||||
if scanner.Previous().Sym != SymClosedBrace {
|
if scanner.Previous().Sym != SymClosedBrace {
|
||||||
err = scanner.Previous().ErrorExpectedGot("}")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
@ -225,7 +179,6 @@ func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *t
|
|||||||
itemExpected = lastSym == SymComma
|
itemExpected = lastSym == SymComma
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
|
||||||
if lastSym != SymClosedRound {
|
if lastSym != SymClosedRound {
|
||||||
err = scanner.Previous().ErrorExpectedGot(")")
|
err = scanner.Previous().ErrorExpectedGot(")")
|
||||||
} else {
|
} else {
|
||||||
@ -252,7 +205,6 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e
|
|||||||
key = tk.Value
|
key = tk.Value
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// err = tk.Errorf("expected dictionary key or closed brace, got %q", tk)
|
|
||||||
err = tk.ErrorExpectedGot("dictionary-key or }")
|
err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -290,7 +242,6 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
|
|||||||
itemExpected = lastSym == SymComma
|
itemExpected = lastSym == SymComma
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
|
||||||
if lastSym != SymClosedBrace {
|
if lastSym != SymClosedBrace {
|
||||||
err = scanner.Previous().ErrorExpectedGot("}")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
@ -394,6 +345,8 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
if allowForest {
|
if allowForest {
|
||||||
tree.ToForest()
|
tree.ToForest()
|
||||||
firstToken = true
|
firstToken = true
|
||||||
|
currentTerm = nil
|
||||||
|
selectorTerm = nil
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source)
|
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source)
|
||||||
|
@ -5,21 +5,16 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExpr(t *testing.T) {
|
func TestExpr(t *testing.T) {
|
||||||
type inputType struct {
|
section := "Expr"
|
||||||
source string
|
|
||||||
wantResult any
|
|
||||||
wantErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`0?{}`, nil, nil},
|
/* 1 */ {`0?{}`, nil, nil},
|
||||||
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
||||||
/* 3 */ {`f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil},
|
/* 3 */ {`builtin "os.file"; f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil},
|
||||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||||
/* 6 */ {`
|
/* 6 */ {`
|
||||||
@ -35,48 +30,8 @@ func TestExpr(t *testing.T) {
|
|||||||
it++
|
it++
|
||||||
`, int64(1), nil},
|
`, int64(1), nil},
|
||||||
}
|
}
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
succeeded := 0
|
// parserTestSpec(t, section, inputs, 3)
|
||||||
failed := 0
|
parserTest(t, section, inputs)
|
||||||
|
|
||||||
for i, input := range inputs {
|
|
||||||
var expr Expr
|
|
||||||
var gotResult any
|
|
||||||
var gotErr error
|
|
||||||
|
|
||||||
ctx := NewSimpleStore()
|
|
||||||
// ImportMathFuncs(ctx)
|
|
||||||
// ImportImportFunc(ctx)
|
|
||||||
ImportOsFuncs(ctx)
|
|
||||||
parser := NewParser(ctx)
|
|
||||||
|
|
||||||
logTest(t, i+1, "Expr", input.source, input.wantResult, input.wantErr)
|
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
|
||||||
|
|
||||||
good := true
|
|
||||||
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
|
|
||||||
gotResult, gotErr = expr.Eval(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if gotResult != input.wantResult {
|
|
||||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if gotErr != input.wantErr {
|
|
||||||
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
|
||||||
t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if good {
|
|
||||||
succeeded++
|
|
||||||
} else {
|
|
||||||
failed++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)
|
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 14 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil},
|
/* 14 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil},
|
||||||
/* 15 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil},
|
/* 15 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil},
|
||||||
/* 16 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`endsWithStr(): too few params -- expected 2 or more, got 1`)},
|
/* 16 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`endsWithStr(): too few params -- expected 2 or more, got 1`)},
|
||||||
/* 17 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil},
|
/* 17 */ {`builtin "string"; splitStr("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
||||||
/* 18 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got int64 (1)`)},
|
/* 18 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got int64 (1)`)},
|
||||||
/* 19 */ {`builtin "string"; joinStr()`, nil, errors.New(`joinStr(): too few params -- expected 1 or more, got 0`)},
|
/* 19 */ {`builtin "string"; joinStr()`, nil, errors.New(`joinStr(): too few params -- expected 1 or more, got 0`)},
|
||||||
|
|
||||||
|
@ -26,11 +26,14 @@ func TestFuncs(t *testing.T) {
|
|||||||
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
||||||
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
|
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
|
||||||
/* 14 */ {`two=func(){2}; two(123)`, nil, errors.New(`two(): too much params -- expected 0, got 1`)},
|
/* 14 */ {`two=func(){2}; two(123)`, nil, errors.New(`two(): too much params -- expected 0, got 1`)},
|
||||||
|
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
|
||||||
|
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, errors.New(`[1:16] can't mix default and non-default parameters`)},
|
||||||
|
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
// parserTestSpec(t, section, inputs, 17)
|
||||||
parserTest(t, section, inputs)
|
parserTest(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
term.go
9
term.go
@ -229,3 +229,12 @@ func (self *term) evalPrefix(ctx ExprContext) (childValue any, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE Temporary solution to support function parameters with default value
|
||||||
|
|
||||||
|
func (self *term) forceChild(c *term) {
|
||||||
|
if self.children == nil {
|
||||||
|
self.children = make([]*term, 0, 1)
|
||||||
|
}
|
||||||
|
self.children = append(self.children, c)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user