From e5f63c3d9d5d74f8a0b5db4057b94f8d18ee27cf Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Fri, 24 May 2024 06:28:48 +0200 Subject: [PATCH] function definition and usage rationalized --- common-type-names.go | 2 +- context-helpers.go | 2 +- context.go | 24 +---- func-base.go | 60 +++++------ func-import.go | 6 +- func-math.go | 6 +- func-os.go | 18 ++-- func-string.go | 12 +-- function.go | 232 +++++++++++++++++++++++++++++++++++++++++++ helpers.go | 4 +- operand-func.go | 55 +++++----- operand-iterator.go | 3 +- operator-assign.go | 17 ++-- operator-coalesce.go | 2 +- simple-store.go | 142 ++------------------------ utils.go | 8 ++ 16 files changed, 342 insertions(+), 251 deletions(-) create mode 100644 function.go diff --git a/common-type-names.go b/common-type-names.go index 9d44c6b..189b4b1 100644 --- a/common-type-names.go +++ b/common-type-names.go @@ -7,7 +7,7 @@ package expr const ( typeAny = "any" typeBoolean = "boolean" - typeFloat = "decimal" + typeFloat = "float" typeFraction = "fraction" typeHandle = "handle" typeInt = "integer" diff --git a/context-helpers.go b/context-helpers.go index d33abca..1bda5a3 100644 --- a/context-helpers.go +++ b/context-helpers.go @@ -24,7 +24,7 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) { } // ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs()) // ctx.RegisterFuncInfo(name, info) - ctx.RegisterFunc2(name, info.Functor(), info.ReturnType(), info.Params()) + ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params()) } func exportObjects(destCtx, sourceCtx ExprContext) { diff --git a/context.go b/context.go index 8190d68..51e03b1 100644 --- a/context.go +++ b/context.go @@ -4,28 +4,11 @@ // context.go package expr -// ---- Function template -type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error) - // ---- Functor interface type Functor interface { Invoke(ctx ExprContext, name string, args []any) (result any, err error) -} - -type simpleFunctor struct { - f FuncTemplate -} - -func newSimpleFunctor(f FuncTemplate) *simpleFunctor { - return &simpleFunctor{f: f} -} - -func (functor *simpleFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { - return functor.f(ctx, name, args) -} - -func (functor *simpleFunctor) ToString(opt FmtOpt) string { - return "func() {}" + SetFunc(info ExprFunc) + GetFunc() ExprFunc } // ---- Function Param Info @@ -39,6 +22,7 @@ type ExprFuncParam interface { // ---- Function Info type ExprFunc interface { + Formatter Name() string MinArgs() int MaxArgs() int @@ -59,5 +43,5 @@ type ExprContext interface { Call(name string, args []any) (result any, err error) // RegisterFunc(name string, f Functor, minArgs, maxArgs int) RegisterFuncInfo(info ExprFunc) - RegisterFunc2(name string, f Functor, returnType string, param []ExprFuncParam) error + RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error } diff --git a/func-base.go b/func-base.go index 6759633..6bb86ef 100644 --- a/func-base.go +++ b/func-base.go @@ -54,6 +54,24 @@ func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err return } +func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) { + switch v := args[0].(type) { + case int64: + result = (v != 0) + case *fraction: + result = v.num != 0 + case float64: + result = v != 0.0 + case bool: + result = v + case string: + result = len(v) > 0 + default: + err = errCantConvert(name, v, "bool") + } + return +} + func intFunc(ctx ExprContext, name string, args []any) (result any, err error) { switch v := args[0].(type) { case int64: @@ -144,34 +162,20 @@ func ImportBuiltinsFuncs(ctx ExprContext) { newFuncParam(paramValue), } - // ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, 1) - ctx.RegisterFunc2("isNil", newSimpleFunctor(isNilFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isInt", &simpleFunctor{f: isIntFunc}, 1, 1) - ctx.RegisterFunc2("isInt", newSimpleFunctor(isIntFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isFloat", &simpleFunctor{f: isFloatFunc}, 1, 1) - ctx.RegisterFunc2("isFloat", newSimpleFunctor(isFloatFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isBool", &simpleFunctor{f: isBoolFunc}, 1, 1) - ctx.RegisterFunc2("isBool", newSimpleFunctor(isBoolFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1) - ctx.RegisterFunc2("isString", newSimpleFunctor(isStringFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1) - //ctx.RegisterFunc2("isFraction", &simpleFunctor{f: isFractionFunc}, typeBoolean, anyParams) - // ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1) - ctx.RegisterFunc2("isFract", newSimpleFunctor(isFractionFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isRational", &simpleFunctor{f: isRationalFunc}, 1, 1) - ctx.RegisterFunc2("isRational", newSimpleFunctor(isRationalFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isList", &simpleFunctor{f: isListFunc}, 1, 1) - ctx.RegisterFunc2("isList", newSimpleFunctor(isListFunc), typeBoolean, anyParams) - // ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1) - //ctx.RegisterFunc2("isDictionary", &simpleFunctor{f: isDictionaryFunc}, typeBoolean, anyParams) - // ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1) - ctx.RegisterFunc2("isDict", newSimpleFunctor(isDictionaryFunc), typeBoolean, anyParams) - //ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, 1) - ctx.RegisterFunc2("int", newSimpleFunctor(intFunc), typeInt, anyParams) - // ctx.RegisterFunc("dec", &simpleFunctor{f: decFunc}, 1, 1) - ctx.RegisterFunc2("dec", newSimpleFunctor(decFunc), typeFloat, anyParams) - // ctx.RegisterFunc("fract", &simpleFunctor{f: fractFunc}, 1, 2) - ctx.RegisterFunc2("fract", newSimpleFunctor(fractFunc), typeFraction, []ExprFuncParam{ + ctx.RegisterFunc("isNil", newGolangFunctor(isNilFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isInt", newGolangFunctor(isIntFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isFloat", newGolangFunctor(isFloatFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isBool", newGolangFunctor(isBoolFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isString", newGolangFunctor(isStringFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isFract", newGolangFunctor(isFractionFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isRational", newGolangFunctor(isRationalFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isList", newGolangFunctor(isListFunc), typeBoolean, anyParams) + ctx.RegisterFunc("isDict", newGolangFunctor(isDictionaryFunc), typeBoolean, anyParams) + + ctx.RegisterFunc("bool", newGolangFunctor(boolFunc), typeBoolean, anyParams) + ctx.RegisterFunc("int", newGolangFunctor(intFunc), typeInt, anyParams) + ctx.RegisterFunc("dec", newGolangFunctor(decFunc), typeFloat, anyParams) + ctx.RegisterFunc("fract", newGolangFunctor(fractFunc), typeFraction, []ExprFuncParam{ newFuncParam(paramValue), newFuncParamFlagDef("denominator", pfOptional, 1), }) diff --git a/func-import.go b/func-import.go index 98bcbc7..3548d39 100644 --- a/func-import.go +++ b/func-import.go @@ -137,12 +137,10 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu } func ImportImportFuncs(ctx ExprContext) { - // ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1) - ctx.RegisterFunc2("import", newSimpleFunctor(importFunc), typeAny, []ExprFuncParam{ + ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{ newFuncParamFlag(typeFilepath, pfRepeat), }) - //ctx.RegisterFunc("importAll", &simpleFunctor{f: importAllFunc}, 1, -1) - ctx.RegisterFunc2("importAll", newSimpleFunctor(importAllFunc), typeAny, []ExprFuncParam{ + ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{ newFuncParamFlag(typeFilepath, pfRepeat), }) } diff --git a/func-math.go b/func-math.go index 058c4ff..84fef10 100644 --- a/func-math.go +++ b/func-math.go @@ -167,13 +167,11 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) { } func ImportMathFuncs(ctx ExprContext) { - //ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1) - ctx.RegisterFunc2("add", &simpleFunctor{f: addFunc}, typeNumber, []ExprFuncParam{ + ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{ newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0), }) - // ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1) - ctx.RegisterFunc2("mul", &simpleFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{ + ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{ newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 1), }) } diff --git a/func-os.go b/func-os.go index 3632455..38fe280 100644 --- a/func-os.go +++ b/func-os.go @@ -158,30 +158,24 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err } func ImportOsFuncs(ctx ExprContext) { - // ctx.RegisterFunc("openFile", &simpleFunctor{f: openFileFunc}, 1, 1) - ctx.RegisterFunc2("openFile", newSimpleFunctor(openFileFunc), typeHandle, []ExprFuncParam{ + ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{ newFuncParam(typeFilepath), }) - // ctx.RegisterFunc("appendFile", &simpleFunctor{f: appendFileFunc}, 1, 1) - ctx.RegisterFunc2("appendFile", newSimpleFunctor(appendFileFunc), typeHandle, []ExprFuncParam{ + ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{ newFuncParam(typeFilepath), }) - // ctx.RegisterFunc("createFile", &simpleFunctor{f: createFileFunc}, 1, 1) - ctx.RegisterFunc2("createFile", newSimpleFunctor(createFileFunc), typeHandle, []ExprFuncParam{ + ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{ newFuncParam(typeFilepath), }) - // ctx.RegisterFunc("writeFile", &simpleFunctor{f: writeFileFunc}, 1, -1) - ctx.RegisterFunc2("writeFile", newSimpleFunctor(writeFileFunc), typeInt, []ExprFuncParam{ + ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{ newFuncParam(typeHandle), newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""), }) - // ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2) - ctx.RegisterFunc2("readFile", newSimpleFunctor(readFileFunc), typeString, []ExprFuncParam{ + ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{ newFuncParam(typeHandle), newFuncParamFlagDef("limitCh", pfOptional, "\\n"), }) - //ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1) - ctx.RegisterFunc2("closeFile", newSimpleFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{ + ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{ newFuncParam(typeHandle), }) } diff --git a/func-string.go b/func-string.go index 03762e1..88d7e16 100644 --- a/func-string.go +++ b/func-string.go @@ -183,33 +183,33 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err // Import above functions in the context func ImportStringFuncs(ctx ExprContext) { - ctx.RegisterFunc2("joinStr", newSimpleFunctor(joinStrFunc), typeString, []ExprFuncParam{ + ctx.RegisterFunc("joinStr", newGolangFunctor(joinStrFunc), typeString, []ExprFuncParam{ newFuncParam(paramSeparator), newFuncParamFlagDef(paramItem, pfOptional|pfRepeat, ""), }) - ctx.RegisterFunc2("subStr", newSimpleFunctor(subStrFunc), typeString, []ExprFuncParam{ + ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{ newFuncParam(paramSource), newFuncParamFlagDef(paramStart, pfOptional, 0), newFuncParamFlagDef(paramCount, pfOptional, -1), }) - ctx.RegisterFunc2("splitStr", newSimpleFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{ + ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{ newFuncParam(paramSource), newFuncParamFlagDef(paramSeparator, pfOptional, ""), newFuncParamFlagDef(paramCount, pfOptional, -1), }) - ctx.RegisterFunc2("trimStr", newSimpleFunctor(trimStrFunc), typeString, []ExprFuncParam{ + ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{ newFuncParam(paramSource), }) - ctx.RegisterFunc2("startsWithStr", newSimpleFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{ + ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{ newFuncParam(paramSource), newFuncParamFlag(paramPrefix, pfRepeat), }) - ctx.RegisterFunc2("endsWithStr", newSimpleFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{ + ctx.RegisterFunc("endsWithStr", newGolangFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{ newFuncParam(paramSource), newFuncParamFlag(paramSuffix, pfRepeat), }) diff --git a/function.go b/function.go new file mode 100644 index 0000000..c09028c --- /dev/null +++ b/function.go @@ -0,0 +1,232 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// function.go +package expr + +import ( + "fmt" + "strings" +) + +// ---- Function template +type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error) + +// ---- Common functor definition +type baseFunctor struct { + info ExprFunc +} + +func (functor *baseFunctor) ToString(opt FmtOpt) (s string) { + if functor.info != nil { + s = functor.info.ToString(opt) + } else { + s = "func() {}" + } + return s +} + +func (functor *baseFunctor) SetFunc(info ExprFunc) { + functor.info = info +} + +func (functor *baseFunctor) GetFunc() ExprFunc { + return functor.info +} + +// ---- Linking with the functions of Go +type golangFunctor struct { + baseFunctor + f FuncTemplate +} + +func newGolangFunctor(f FuncTemplate) *golangFunctor { + return &golangFunctor{f: f} +} + +func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { + return functor.f(ctx, name, args) +} + +// ---- Linking with the functions of Expr +type exprFunctor struct { + baseFunctor + params []string + expr Expr +} + +func newExprFunctor(e Expr, params []string) *exprFunctor { + return &exprFunctor{expr: e, params: params} +} + +func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { + for i, p := range functor.params { + if i < len(args) { + arg := args[i] + if funcArg, ok := arg.(Functor); ok { + // ctx.RegisterFunc(p, functor, 0, -1) + ctx.RegisterFunc(p, funcArg, typeAny, []ExprFuncParam{ + newFuncParam(paramValue), + }) + } else { + ctx.UnsafeSetVar(p, arg) + } + } else { + ctx.UnsafeSetVar(p, nil) + } + } + result, err = functor.expr.eval(ctx, false) + return +} + +// ---- Function Parameters +type paramFlags uint16 + +const ( + pfOptional paramFlags = 1 << iota + pfRepeat +) + +type funcParamInfo struct { + name string + flags paramFlags + defaultValue any +} + +func newFuncParam(name string) ExprFuncParam { + return &funcParamInfo{name: name} +} + +func newFuncParamFlag(name string, flags paramFlags) ExprFuncParam { + return &funcParamInfo{name: name, flags: flags} +} + +func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo { + return &funcParamInfo{name: name, flags: flags, defaultValue: defValue} +} + +func (param *funcParamInfo) Name() string { + return param.name +} + +func (param *funcParamInfo) Type() string { + return "any" +} + +func (param *funcParamInfo) IsOptional() bool { + return (param.flags & pfOptional) != 0 +} + +func (param *funcParamInfo) IsRepeat() bool { + return (param.flags & pfRepeat) != 0 +} + +func (param *funcParamInfo) DefaultValue() any { + return param.defaultValue +} + +// --- Functions +type funcInfo struct { + name string + minArgs int + maxArgs int + functor Functor + params []ExprFuncParam + returnType string +} + +func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) { + var minArgs = 0 + var maxArgs = 0 + if params != nil { + for _, p := range params { + if maxArgs == -1 { + return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) + } + if p.IsOptional() { + maxArgs++ + } else if maxArgs == minArgs { + minArgs++ + maxArgs++ + } else { + return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name()) + } + if p.IsRepeat() { + maxArgs = -1 + } + } + } + info = &funcInfo{ + name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params, + } + functor.SetFunc(info) + return info, nil +} + +func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) { + return newFuncInfo("unnamed", functor, returnType, params) +} + +func (info *funcInfo) Params() []ExprFuncParam { + return info.params +} + +func (info *funcInfo) ReturnType() string { + return info.returnType +} + +func (info *funcInfo) ToString(opt FmtOpt) string { + var sb strings.Builder + if len(info.Name()) == 0 { + sb.WriteString("func") + } else { + sb.WriteString(info.Name()) + } + sb.WriteByte('(') + if info.params != nil { + for i, p := range info.params { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(p.Name()) + + if p.IsOptional() { + sb.WriteByte('=') + if s, ok := p.DefaultValue().(string); ok { + sb.WriteByte('"') + sb.WriteString(s) + sb.WriteByte('"') + } else { + sb.WriteString(fmt.Sprintf("%v", p.DefaultValue())) + } + } + } + } + if info.maxArgs < 0 { + sb.WriteString(" ...") + } + sb.WriteString("): ") + if len(info.returnType) > 0 { + sb.WriteString(info.returnType) + } else { + sb.WriteString(typeAny) + } + sb.WriteString(" {}") + return sb.String() +} + +func (info *funcInfo) Name() string { + return info.name +} + +func (info *funcInfo) MinArgs() int { + return info.minArgs +} + +func (info *funcInfo) MaxArgs() int { + return info.maxArgs +} + +func (info *funcInfo) Functor() Functor { + return info.functor +} diff --git a/helpers.go b/helpers.go index 4ea8cf3..3c3aa1d 100644 --- a/helpers.go +++ b/helpers.go @@ -38,9 +38,9 @@ func EvalStringV(source string, args []Arg) (result any, err error) { for _, arg := range args { if isFunc(arg.Value) { if f, ok := arg.Value.(FuncTemplate); ok { - functor := newSimpleFunctor(f) + functor := newGolangFunctor(f) // ctx.RegisterFunc(arg.Name, functor, 0, -1) - ctx.RegisterFunc2(arg.Name, functor, typeAny, []ExprFuncParam{ + ctx.RegisterFunc(arg.Name, functor, typeAny, []ExprFuncParam{ newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0), }) } else { diff --git a/operand-func.go b/operand-func.go index 8e061f3..4bafbad 100644 --- a/operand-func.go +++ b/operand-func.go @@ -76,30 +76,30 @@ func newFuncDefTerm(tk *Token, args []*term) *term { // -------- eval func def // TODO -type funcDefFunctor struct { - params []string - expr Expr -} +// type funcDefFunctor struct { +// params []string +// expr Expr +// } -func (funcDef *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { - for i, p := range funcDef.params { - if i < len(args) { - arg := args[i] - if functor, ok := arg.(Functor); ok { - // ctx.RegisterFunc(p, functor, 0, -1) - ctx.RegisterFunc2(p, functor, typeAny, []ExprFuncParam{ - newFuncParam(paramValue), - }) - } else { - ctx.UnsafeSetVar(p, arg) - } - } else { - ctx.UnsafeSetVar(p, nil) - } - } - result, err = funcDef.expr.eval(ctx, false) - return -} +// func (funcDef *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { +// for i, p := range funcDef.params { +// if i < len(args) { +// arg := args[i] +// if functor, ok := arg.(Functor); ok { +// // ctx.RegisterFunc(p, functor, 0, -1) +// ctx.RegisterFunc2(p, functor, typeAny, []ExprFuncParam{ +// newFuncParam(paramValue), +// }) +// } else { +// ctx.UnsafeSetVar(p, arg) +// } +// } else { +// ctx.UnsafeSetVar(p, nil) +// } +// } +// result, err = funcDef.expr.eval(ctx, false) +// return +// } func evalFuncDef(ctx ExprContext, self *term) (v any, err error) { bodySpec := self.value() @@ -108,10 +108,11 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) { for _, param := range self.children { paramList = append(paramList, param.source()) } - v = &funcDefFunctor{ - params: paramList, - expr: expr, - } + v = newExprFunctor(expr, paramList) + // v = &funcDefFunctor{ + // params: paramList, + // expr: expr, + // } } else { err = errors.New("invalid function definition: the body specification must be an expression") } diff --git a/operand-iterator.go b/operand-iterator.go index 63e5951..973b83a 100644 --- a/operand-iterator.go +++ b/operand-iterator.go @@ -74,7 +74,8 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map ds = make(map[string]Functor) for keyAny, item := range *dictAny { if key, ok := keyAny.(string); ok { - if functor, ok := item.(*funcDefFunctor); ok { + //if functor, ok := item.(*funcDefFunctor); ok { + if functor, ok := item.(Functor); ok { ds[key] = functor if index := slices.Index(requiredFields, key); index >= 0 { foundFields |= 1 << index diff --git a/operator-assign.go b/operator-assign.go index 84dfd97..81216ad 100644 --- a/operator-assign.go +++ b/operator-assign.go @@ -34,14 +34,15 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) { funcName := rightChild.source() if info, exists, _ := GetFuncInfo(ctx, funcName); exists { // ctx.RegisterFuncInfo(info) - ctx.RegisterFunc2(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params()) - } else if funcDef, ok := functor.(*funcDefFunctor); ok { - paramCount := len(funcDef.params) - paramSpecs := make([]ExprFuncParam, paramCount) - for i := range paramSpecs { - paramSpecs[i] = newFuncParam(funcDef.params[i]) - } - ctx.RegisterFunc2(leftTerm.source(), functor, typeAny, paramSpecs) + ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params()) + } else if funcDef, ok := functor.(*exprFunctor); ok { + paramSpecs := ForAll(funcDef.params, newFuncParam) + // paramCount := len(funcDef.params) + // paramSpecs := make([]ExprFuncParam, paramCount) + // for i := range paramSpecs { + // paramSpecs[i] = newFuncParam(funcDef.params[i]) + // } + ctx.RegisterFunc(leftTerm.source(), functor, typeAny, paramSpecs) } else { err = self.Errorf("unknown function %s()", funcName) } diff --git a/operator-coalesce.go b/operator-coalesce.go index 5fe206b..2acd8b1 100644 --- a/operator-coalesce.go +++ b/operator-coalesce.go @@ -67,7 +67,7 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) { } else if rightValue, err = self.children[1].compute(ctx); err == nil { if functor, ok := rightValue.(Functor); ok { //ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) - ctx.RegisterFunc2(leftTerm.source(), functor, typeAny, []ExprFuncParam{ + ctx.RegisterFunc(leftTerm.source(), functor, typeAny, []ExprFuncParam{ newFuncParamFlag(paramValue, pfOptional|pfRepeat), }) } else { diff --git a/simple-store.go b/simple-store.go index 76ace3a..850be75 100644 --- a/simple-store.go +++ b/simple-store.go @@ -1,7 +1,7 @@ // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. -// simple-func-store.go +// simple-store.go package expr import ( @@ -15,117 +15,6 @@ type SimpleStore struct { funcStore map[string]*funcInfo } -type paramFlags uint16 - -const ( - pfOptional paramFlags = 1 << iota - pfRepeat -) - -type funcParamInfo struct { - name string - flags paramFlags - defaultValue any -} - -func newFuncParam(name string) *funcParamInfo { - return &funcParamInfo{name: name} -} - -func newFuncParamFlag(name string, flags paramFlags) *funcParamInfo { - return &funcParamInfo{name: name, flags: flags} -} - -func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo { - return &funcParamInfo{name: name, flags: flags, defaultValue: defValue} -} - -func (param *funcParamInfo) Name() string { - return param.name -} - -func (param *funcParamInfo) Type() string { - return "any" -} - -func (param *funcParamInfo) IsOptional() bool { - return (param.flags & pfOptional) != 0 -} - -func (param *funcParamInfo) IsRepeat() bool { - return (param.flags & pfRepeat) != 0 -} - -func (param *funcParamInfo) DefaultValue() any { - return param.defaultValue -} - -type funcInfo struct { - name string - minArgs int - maxArgs int - functor Functor - params []ExprFuncParam - returnType string -} - -func (info *funcInfo) Params() []ExprFuncParam { - return info.params -} - -func (info *funcInfo) ReturnType() string { - return info.returnType -} - -func (info *funcInfo) ToString(opt FmtOpt) string { - var sb strings.Builder - sb.WriteByte('(') - if info.params != nil { - for i, p := range info.params { - if i > 0 { - sb.WriteString(", ") - } - sb.WriteString(p.Name()) - if p.IsOptional() { - sb.WriteByte('=') - if s, ok := p.DefaultValue().(string); ok { - sb.WriteByte('"') - sb.WriteString(s) - sb.WriteByte('"') - } else { - sb.WriteString(fmt.Sprintf("%v", p.DefaultValue())) - } - } - } - } - if info.maxArgs < 0 { - sb.WriteString(" ...") - } - sb.WriteString(") -> ") - if len(info.returnType) > 0 { - sb.WriteString(info.returnType) - } else { - sb.WriteString(typeAny) - } - return sb.String() -} - -func (info *funcInfo) Name() string { - return info.name -} - -func (info *funcInfo) MinArgs() int { - return info.minArgs -} - -func (info *funcInfo) MaxArgs() int { - return info.maxArgs -} - -func (info *funcInfo) Functor() Functor { - return info.functor -} - func NewSimpleStore() *SimpleStore { ctx := &SimpleStore{ varStore: make(map[string]any), @@ -251,31 +140,12 @@ func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) { ctx.funcStore[info.Name()], _ = info.(*funcInfo) } -func (ctx *SimpleStore) RegisterFunc2(name string, functor Functor, returnType string, params []ExprFuncParam) error { - var minArgs = 0 - var maxArgs = 0 - if params != nil { - for _, p := range params { - if maxArgs == -1 { - return fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) - } - if p.IsOptional() { - maxArgs++ - } else if maxArgs == minArgs { - minArgs++ - maxArgs++ - } else { - return fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name()) - } - if p.IsRepeat() { - maxArgs = -1 - } - } +func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (err error) { + var info *funcInfo + if info, err = newFuncInfo(name, functor, returnType, params); err == nil { + ctx.funcStore[name] = info } - ctx.funcStore[name] = &funcInfo{ - name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params, - } - return nil + return } func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) { diff --git a/utils.go b/utils.go index 271cb50..93c9bd2 100644 --- a/utils.go +++ b/utils.go @@ -209,3 +209,11 @@ func toInt(value any, description string) (i int, err error) { } return } + +func ForAll[T, V any](ts []T, fn func(T) V) []V { + result := make([]V, len(ts)) + for i, t := range ts { + result[i] = fn(t) + } + return result +}