Improved closure context persistence. Now it is possibile to define counters like this func(base){func(){@base=base+1}}

This commit is contained in:
Celestino Amoroso 2024-06-24 07:20:17 +02:00
parent 3b641ac793
commit e41ddc664c
5 changed files with 82 additions and 63 deletions

View File

@ -17,19 +17,31 @@ type exprFunctor struct {
// } // }
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor { func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
return &exprFunctor{expr: e, params: params, defCtx: ctx} var defCtx ExprContext
if ctx != nil {
// if ctx.GetParent() != nil {
// defCtx = ctx.Clone()
// defCtx.SetParent(ctx)
// } else {
defCtx = ctx
// }
}
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
}
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
return functor.defCtx
} }
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
if functor.defCtx != nil { // if functor.defCtx != nil {
ctx.Merge(functor.defCtx) // ctx.Merge(functor.defCtx)
} // }
for i, p := range functor.params { for i, p := range functor.params {
if i < len(args) { if i < len(args) {
arg := args[i] arg := args[i]
if funcArg, ok := arg.(Functor); ok { if funcArg, ok := arg.(Functor); ok {
// ctx.RegisterFunc(p, functor, 0, -1)
paramSpecs := funcArg.GetParams() paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs) ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
} else { } else {
@ -42,17 +54,3 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
result, err = functor.expr.Eval(ctx) result, err = functor.expr.Eval(ctx)
return return
} }
// 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, &params); err == nil {
// if v, err = ctx.Call(funcName, params); err == nil {
// exportObjects(parentCtx, ctx)
// }
// }
// }
// return
// }

View File

@ -10,6 +10,7 @@ type Functor interface {
SetFunc(info ExprFunc) SetFunc(info ExprFunc)
GetFunc() ExprFunc GetFunc() ExprFunc
GetParams() []ExprFuncParam GetParams() []ExprFuncParam
GetDefinitionContext() ExprContext
} }
// ---- Function Param Info // ---- Function Param Info
@ -31,6 +32,8 @@ type ExprFunc interface {
Functor() Functor Functor() Functor
Params() []ExprFuncParam Params() []ExprFuncParam
ReturnType() string ReturnType() string
PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext)
} }
// ----Expression Context // ----Expression Context

View File

@ -21,7 +21,7 @@ func (functor *baseFunctor) ToString(opt FmtOpt) (s string) {
if functor.info != nil { if functor.info != nil {
s = functor.info.ToString(opt) s = functor.info.ToString(opt)
} else { } else {
s = "func() {}" s = "func(){}"
} }
return s return s
} }
@ -42,6 +42,10 @@ func (functor *baseFunctor) GetFunc() ExprFunc {
return functor.info return functor.info
} }
func (functor *baseFunctor) GetDefinitionContext() ExprContext {
return nil
}
// ---- Function Parameters // ---- Function Parameters
type paramFlags uint16 type paramFlags uint16
@ -94,13 +98,15 @@ func (param *funcParamInfo) DefaultValue() any {
} }
// --- Functions // --- Functions
// funcInfo implements ExprFunc
type funcInfo struct { type funcInfo struct {
name string name string
minArgs int minArgs int
maxArgs int maxArgs int
functor Functor functor Functor
params []ExprFuncParam formalParams []ExprFuncParam
returnType string returnType string
} }
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) { func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
@ -126,7 +132,7 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
} }
} }
info = &funcInfo{ info = &funcInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params, name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
} }
functor.SetFunc(info) functor.SetFunc(info)
return info, nil return info, nil
@ -137,7 +143,7 @@ func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncPar
} }
func (info *funcInfo) Params() []ExprFuncParam { func (info *funcInfo) Params() []ExprFuncParam {
return info.params return info.formalParams
} }
func (info *funcInfo) ReturnType() string { func (info *funcInfo) ReturnType() string {
@ -152,8 +158,8 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
sb.WriteString(info.Name()) sb.WriteString(info.Name())
} }
sb.WriteByte('(') sb.WriteByte('(')
if info.params != nil { if info.formalParams != nil {
for i, p := range info.params { for i, p := range info.formalParams {
if i > 0 { if i > 0 {
sb.WriteString(", ") sb.WriteString(", ")
} }
@ -180,7 +186,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
} else { } else {
sb.WriteString(TypeAny) sb.WriteString(TypeAny)
} }
sb.WriteString(" {}") sb.WriteString("{}")
return sb.String() return sb.String()
} }
@ -200,41 +206,52 @@ func (info *funcInfo) Functor() Functor {
return info.functor return info.functor
} }
func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
ctx = defCtx.Clone()
ctx.SetParent(defCtx)
} else {
ctx = parentCtx.Clone()
ctx.SetParent(parentCtx)
}
return
}
func (info *funcInfo) PrepareCall(parentCtx ExprContext, name string, varActualParams *[]any) (ctx ExprContext, err error) {
passedCount := len(*varActualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
}
for i := passedCount; i < len(info.formalParams); i++ {
p := info.formalParams[i]
if !p.IsDefault() {
break
}
*varActualParams = append(*varActualParams, p.DefaultValue())
}
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varActualParams) {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varActualParams))
}
if err == nil {
ctx = info.AllocContext(parentCtx)
}
return
}
// ----- Call a function --- // ----- Call a function ---
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) { func CallFunction(parentCtx ExprContext, name string, actualParams []any) (result any, err error) {
if info, exists, owner := GetFuncInfo(ctx, name); exists { if info, exists, _ := GetFuncInfo(parentCtx, name); exists {
passedCount := len(*varParams) var ctx ExprContext
if info.MinArgs() > passedCount { if ctx, err = info.PrepareCall(parentCtx, name, &actualParams); err == nil {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount) functor := info.Functor()
} result, err = functor.Invoke(ctx, name, actualParams)
for i, p := range info.Params() { exportObjectsToParent(ctx)
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 { } else {
err = fmt.Errorf("unknown function %s()", name) err = fmt.Errorf("unknown function %s()", name)
} }
return return
} }
func CallFunction(parentCtx ExprContext, name string, params []any) (result any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
if err = checkFunctionCall(ctx, name, &params); err == nil {
result, err = ctx.Call(name, params)
exportObjectsToParent(ctx)
}
return
}

View File

@ -36,10 +36,11 @@ func (ctx *SimpleStore) GetParent() ExprContext {
} }
func (ctx *SimpleStore) Clone() ExprContext { func (ctx *SimpleStore) Clone() ExprContext {
return &SimpleStore{ clone := &SimpleStore{
varStore: CloneFilteredMap(ctx.varStore, filterRefName), varStore: CloneFilteredMap(ctx.varStore, filterRefName),
funcStore: CloneFilteredMap(ctx.funcStore, filterRefName), funcStore: CloneFilteredMap(ctx.funcStore, filterRefName),
} }
return clone
} }
func (ctx *SimpleStore) Merge(src ExprContext) { func (ctx *SimpleStore) Merge(src ExprContext) {

View File

@ -44,7 +44,7 @@ func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
func TestFunctionToStringSimple(t *testing.T) { func TestFunctionToStringSimple(t *testing.T) {
source := NewGolangFunctor(dummy) source := NewGolangFunctor(dummy)
want := "func() {}" want := "func(){}"
got := source.ToString(0) got := source.ToString(0)
if got != want { if got != want {
t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want) t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)