function definition and usage rationalized

This commit is contained in:
Celestino Amoroso 2024-05-24 06:28:48 +02:00
parent d545a35acf
commit e5f63c3d9d
16 changed files with 342 additions and 251 deletions

View File

@ -7,7 +7,7 @@ package expr
const ( const (
typeAny = "any" typeAny = "any"
typeBoolean = "boolean" typeBoolean = "boolean"
typeFloat = "decimal" typeFloat = "float"
typeFraction = "fraction" typeFraction = "fraction"
typeHandle = "handle" typeHandle = "handle"
typeInt = "integer" typeInt = "integer"

View File

@ -24,7 +24,7 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
} }
// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs()) // ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
// ctx.RegisterFuncInfo(name, info) // 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) { func exportObjects(destCtx, sourceCtx ExprContext) {

View File

@ -4,28 +4,11 @@
// context.go // context.go
package expr package expr
// ---- Function template
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
// ---- Functor interface // ---- Functor interface
type Functor interface { type Functor interface {
Invoke(ctx ExprContext, name string, args []any) (result any, err error) Invoke(ctx ExprContext, name string, args []any) (result any, err error)
} SetFunc(info ExprFunc)
GetFunc() ExprFunc
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() {<body>}"
} }
// ---- Function Param Info // ---- Function Param Info
@ -39,6 +22,7 @@ type ExprFuncParam interface {
// ---- Function Info // ---- Function Info
type ExprFunc interface { type ExprFunc interface {
Formatter
Name() string Name() string
MinArgs() int MinArgs() int
MaxArgs() int MaxArgs() int
@ -59,5 +43,5 @@ type ExprContext interface {
Call(name string, args []any) (result any, err error) Call(name string, args []any) (result any, err error)
// RegisterFunc(name string, f Functor, minArgs, maxArgs int) // RegisterFunc(name string, f Functor, minArgs, maxArgs int)
RegisterFuncInfo(info ExprFunc) RegisterFuncInfo(info ExprFunc)
RegisterFunc2(name string, f Functor, returnType string, param []ExprFuncParam) error RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
} }

View File

@ -54,6 +54,24 @@ func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err
return 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) { func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) { switch v := args[0].(type) {
case int64: case int64:
@ -144,34 +162,20 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
newFuncParam(paramValue), newFuncParam(paramValue),
} }
// ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, 1) ctx.RegisterFunc("isNil", newGolangFunctor(isNilFunc), typeBoolean, anyParams)
ctx.RegisterFunc2("isNil", newSimpleFunctor(isNilFunc), typeBoolean, anyParams) ctx.RegisterFunc("isInt", newGolangFunctor(isIntFunc), typeBoolean, anyParams)
// ctx.RegisterFunc("isInt", &simpleFunctor{f: isIntFunc}, 1, 1) ctx.RegisterFunc("isFloat", newGolangFunctor(isFloatFunc), typeBoolean, anyParams)
ctx.RegisterFunc2("isInt", newSimpleFunctor(isIntFunc), typeBoolean, anyParams) ctx.RegisterFunc("isBool", newGolangFunctor(isBoolFunc), typeBoolean, anyParams)
// ctx.RegisterFunc("isFloat", &simpleFunctor{f: isFloatFunc}, 1, 1) ctx.RegisterFunc("isString", newGolangFunctor(isStringFunc), typeBoolean, anyParams)
ctx.RegisterFunc2("isFloat", newSimpleFunctor(isFloatFunc), typeBoolean, anyParams) ctx.RegisterFunc("isFract", newGolangFunctor(isFractionFunc), typeBoolean, anyParams)
// ctx.RegisterFunc("isBool", &simpleFunctor{f: isBoolFunc}, 1, 1) ctx.RegisterFunc("isRational", newGolangFunctor(isRationalFunc), typeBoolean, anyParams)
ctx.RegisterFunc2("isBool", newSimpleFunctor(isBoolFunc), typeBoolean, anyParams) ctx.RegisterFunc("isList", newGolangFunctor(isListFunc), typeBoolean, anyParams)
// ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1) ctx.RegisterFunc("isDict", newGolangFunctor(isDictionaryFunc), typeBoolean, anyParams)
ctx.RegisterFunc2("isString", newSimpleFunctor(isStringFunc), typeBoolean, anyParams)
// ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1) ctx.RegisterFunc("bool", newGolangFunctor(boolFunc), typeBoolean, anyParams)
//ctx.RegisterFunc2("isFraction", &simpleFunctor{f: isFractionFunc}, typeBoolean, anyParams) ctx.RegisterFunc("int", newGolangFunctor(intFunc), typeInt, anyParams)
// ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1) ctx.RegisterFunc("dec", newGolangFunctor(decFunc), typeFloat, anyParams)
ctx.RegisterFunc2("isFract", newSimpleFunctor(isFractionFunc), typeBoolean, anyParams) ctx.RegisterFunc("fract", newGolangFunctor(fractFunc), typeFraction, []ExprFuncParam{
// 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{
newFuncParam(paramValue), newFuncParam(paramValue),
newFuncParamFlagDef("denominator", pfOptional, 1), newFuncParamFlagDef("denominator", pfOptional, 1),
}) })

View File

@ -137,12 +137,10 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
} }
func ImportImportFuncs(ctx ExprContext) { func ImportImportFuncs(ctx ExprContext) {
// ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1) ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{
ctx.RegisterFunc2("import", newSimpleFunctor(importFunc), typeAny, []ExprFuncParam{
newFuncParamFlag(typeFilepath, pfRepeat), newFuncParamFlag(typeFilepath, pfRepeat),
}) })
//ctx.RegisterFunc("importAll", &simpleFunctor{f: importAllFunc}, 1, -1) ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{
ctx.RegisterFunc2("importAll", newSimpleFunctor(importAllFunc), typeAny, []ExprFuncParam{
newFuncParamFlag(typeFilepath, pfRepeat), newFuncParamFlag(typeFilepath, pfRepeat),
}) })
} }

View File

@ -167,13 +167,11 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
} }
func ImportMathFuncs(ctx ExprContext) { func ImportMathFuncs(ctx ExprContext) {
//ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1) ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{
ctx.RegisterFunc2("add", &simpleFunctor{f: addFunc}, typeNumber, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0), newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
}) })
// ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1) ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{
ctx.RegisterFunc2("mul", &simpleFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 1), newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 1),
}) })
} }

View File

@ -158,30 +158,24 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
} }
func ImportOsFuncs(ctx ExprContext) { func ImportOsFuncs(ctx ExprContext) {
// ctx.RegisterFunc("openFile", &simpleFunctor{f: openFileFunc}, 1, 1) ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{
ctx.RegisterFunc2("openFile", newSimpleFunctor(openFileFunc), typeHandle, []ExprFuncParam{
newFuncParam(typeFilepath), newFuncParam(typeFilepath),
}) })
// ctx.RegisterFunc("appendFile", &simpleFunctor{f: appendFileFunc}, 1, 1) ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{
ctx.RegisterFunc2("appendFile", newSimpleFunctor(appendFileFunc), typeHandle, []ExprFuncParam{
newFuncParam(typeFilepath), newFuncParam(typeFilepath),
}) })
// ctx.RegisterFunc("createFile", &simpleFunctor{f: createFileFunc}, 1, 1) ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{
ctx.RegisterFunc2("createFile", newSimpleFunctor(createFileFunc), typeHandle, []ExprFuncParam{
newFuncParam(typeFilepath), newFuncParam(typeFilepath),
}) })
// ctx.RegisterFunc("writeFile", &simpleFunctor{f: writeFileFunc}, 1, -1) ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{
ctx.RegisterFunc2("writeFile", newSimpleFunctor(writeFileFunc), typeInt, []ExprFuncParam{
newFuncParam(typeHandle), newFuncParam(typeHandle),
newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""), newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""),
}) })
// ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2) ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{
ctx.RegisterFunc2("readFile", newSimpleFunctor(readFileFunc), typeString, []ExprFuncParam{
newFuncParam(typeHandle), newFuncParam(typeHandle),
newFuncParamFlagDef("limitCh", pfOptional, "\\n"), newFuncParamFlagDef("limitCh", pfOptional, "\\n"),
}) })
//ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1) ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{
ctx.RegisterFunc2("closeFile", newSimpleFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{
newFuncParam(typeHandle), newFuncParam(typeHandle),
}) })
} }

View File

@ -183,33 +183,33 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
// Import above functions in the context // Import above functions in the context
func ImportStringFuncs(ctx ExprContext) { func ImportStringFuncs(ctx ExprContext) {
ctx.RegisterFunc2("joinStr", newSimpleFunctor(joinStrFunc), typeString, []ExprFuncParam{ ctx.RegisterFunc("joinStr", newGolangFunctor(joinStrFunc), typeString, []ExprFuncParam{
newFuncParam(paramSeparator), newFuncParam(paramSeparator),
newFuncParamFlagDef(paramItem, pfOptional|pfRepeat, ""), newFuncParamFlagDef(paramItem, pfOptional|pfRepeat, ""),
}) })
ctx.RegisterFunc2("subStr", newSimpleFunctor(subStrFunc), typeString, []ExprFuncParam{ ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{
newFuncParam(paramSource), newFuncParam(paramSource),
newFuncParamFlagDef(paramStart, pfOptional, 0), newFuncParamFlagDef(paramStart, pfOptional, 0),
newFuncParamFlagDef(paramCount, pfOptional, -1), newFuncParamFlagDef(paramCount, pfOptional, -1),
}) })
ctx.RegisterFunc2("splitStr", newSimpleFunctor(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, -1),
}) })
ctx.RegisterFunc2("trimStr", newSimpleFunctor(trimStrFunc), typeString, []ExprFuncParam{ ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{
newFuncParam(paramSource), newFuncParam(paramSource),
}) })
ctx.RegisterFunc2("startsWithStr", newSimpleFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{ ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{
newFuncParam(paramSource), newFuncParam(paramSource),
newFuncParamFlag(paramPrefix, pfRepeat), newFuncParamFlag(paramPrefix, pfRepeat),
}) })
ctx.RegisterFunc2("endsWithStr", newSimpleFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{ ctx.RegisterFunc("endsWithStr", newGolangFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{
newFuncParam(paramSource), newFuncParam(paramSource),
newFuncParamFlag(paramSuffix, pfRepeat), newFuncParamFlag(paramSuffix, pfRepeat),
}) })

232
function.go Normal file
View File

@ -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() {<body>}"
}
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(" {<body>}")
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
}

View File

@ -38,9 +38,9 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
for _, arg := range args { for _, arg := range args {
if isFunc(arg.Value) { if isFunc(arg.Value) {
if f, ok := arg.Value.(FuncTemplate); ok { if f, ok := arg.Value.(FuncTemplate); ok {
functor := newSimpleFunctor(f) functor := newGolangFunctor(f)
// ctx.RegisterFunc(arg.Name, functor, 0, -1) // 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), newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
}) })
} else { } else {

View File

@ -76,30 +76,30 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
// -------- eval func def // -------- eval func def
// TODO // TODO
type funcDefFunctor struct { // type funcDefFunctor struct {
params []string // params []string
expr Expr // expr Expr
} // }
func (funcDef *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { // func (funcDef *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
for i, p := range funcDef.params { // for i, p := range funcDef.params {
if i < len(args) { // if i < len(args) {
arg := args[i] // arg := args[i]
if functor, ok := arg.(Functor); ok { // if functor, ok := arg.(Functor); ok {
// ctx.RegisterFunc(p, functor, 0, -1) // // ctx.RegisterFunc(p, functor, 0, -1)
ctx.RegisterFunc2(p, functor, typeAny, []ExprFuncParam{ // ctx.RegisterFunc2(p, functor, typeAny, []ExprFuncParam{
newFuncParam(paramValue), // newFuncParam(paramValue),
}) // })
} else { // } else {
ctx.UnsafeSetVar(p, arg) // ctx.UnsafeSetVar(p, arg)
} // }
} else { // } else {
ctx.UnsafeSetVar(p, nil) // ctx.UnsafeSetVar(p, nil)
} // }
} // }
result, err = funcDef.expr.eval(ctx, false) // result, err = funcDef.expr.eval(ctx, false)
return // 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()
@ -108,10 +108,11 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
for _, param := range self.children { for _, param := range self.children {
paramList = append(paramList, param.source()) paramList = append(paramList, param.source())
} }
v = &funcDefFunctor{ v = newExprFunctor(expr, paramList)
params: paramList, // v = &funcDefFunctor{
expr: expr, // params: paramList,
} // expr: expr,
// }
} else { } else {
err = errors.New("invalid function definition: the body specification must be an expression") err = errors.New("invalid function definition: the body specification must be an expression")
} }

View File

@ -74,7 +74,8 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
ds = make(map[string]Functor) ds = make(map[string]Functor)
for keyAny, item := range *dictAny { for keyAny, item := range *dictAny {
if key, ok := keyAny.(string); ok { 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 ds[key] = functor
if index := slices.Index(requiredFields, key); index >= 0 { if index := slices.Index(requiredFields, key); index >= 0 {
foundFields |= 1 << index foundFields |= 1 << index

View File

@ -34,14 +34,15 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
funcName := rightChild.source() funcName := rightChild.source()
if info, exists, _ := GetFuncInfo(ctx, funcName); exists { if info, exists, _ := GetFuncInfo(ctx, funcName); exists {
// ctx.RegisterFuncInfo(info) // ctx.RegisterFuncInfo(info)
ctx.RegisterFunc2(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params()) ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
} else if funcDef, ok := functor.(*funcDefFunctor); ok { } else if funcDef, ok := functor.(*exprFunctor); ok {
paramCount := len(funcDef.params) paramSpecs := ForAll(funcDef.params, newFuncParam)
paramSpecs := make([]ExprFuncParam, paramCount) // paramCount := len(funcDef.params)
for i := range paramSpecs { // paramSpecs := make([]ExprFuncParam, paramCount)
paramSpecs[i] = newFuncParam(funcDef.params[i]) // for i := range paramSpecs {
} // paramSpecs[i] = newFuncParam(funcDef.params[i])
ctx.RegisterFunc2(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)
} }

View File

@ -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 { } else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok { if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) //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), newFuncParamFlag(paramValue, pfOptional|pfRepeat),
}) })
} else { } else {

View File

@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// simple-func-store.go // simple-store.go
package expr package expr
import ( import (
@ -15,117 +15,6 @@ type SimpleStore struct {
funcStore map[string]*funcInfo 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 { func NewSimpleStore() *SimpleStore {
ctx := &SimpleStore{ ctx := &SimpleStore{
varStore: make(map[string]any), varStore: make(map[string]any),
@ -251,31 +140,12 @@ func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) {
ctx.funcStore[info.Name()], _ = info.(*funcInfo) ctx.funcStore[info.Name()], _ = info.(*funcInfo)
} }
func (ctx *SimpleStore) RegisterFunc2(name string, functor Functor, returnType string, params []ExprFuncParam) error { func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (err error) {
var minArgs = 0 var info *funcInfo
var maxArgs = 0 if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
if params != nil { ctx.funcStore[name] = info
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
}
}
} }
ctx.funcStore[name] = &funcInfo{ return
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params,
}
return nil
} }
func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) { func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {

View File

@ -209,3 +209,11 @@ func toInt(value any, description string) (i int, err error) {
} }
return 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
}