From 50e716821445ffa32b115945366deca086f1082b Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Sat, 11 May 2024 10:45:38 +0200 Subject: [PATCH] Since now builtin functions are registared in a new global context. This reduces the effort to copy the whole set of builtin functions in the context of a function call; only the called function will be copied, if it is global. --- funcs_test.go | 3 ++- global-context.go | 54 +++++++++++++++++++++++++++++++++++++++++++++ module-register.go | 47 +++++++++++++++++++-------------------- operand-func.go | 7 ++++-- operand-var.go | 4 ++-- operator-builtin.go | 4 ++-- 6 files changed, 88 insertions(+), 31 deletions(-) create mode 100644 global-context.go diff --git a/funcs_test.go b/funcs_test.go index 682d298..957f046 100644 --- a/funcs_test.go +++ b/funcs_test.go @@ -65,10 +65,11 @@ func TestFuncs(t *testing.T) { /* 52 */ {`isFract(1|3)`, true, nil}, /* 53 */ {`isFract(3|1)`, false, nil}, /* 54 */ {`isRational(3|1)`, true, nil}, + /* 55 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil}, } t.Setenv("EXPR_PATH", ".") - // parserTest(t, "Func", inputs[25:26]) + //parserTest(t, "Func", inputs[54:55]) parserTest(t, "Func", inputs) } diff --git a/global-context.go b/global-context.go new file mode 100644 index 0000000..6f525f7 --- /dev/null +++ b/global-context.go @@ -0,0 +1,54 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// global-context.go +package expr + +import "path/filepath" + +var globalCtx *SimpleFuncStore + +func ImportInContext(name string) (exists bool) { + var mod *module + if mod, exists = moduleRegister[name]; exists { + mod.importFunc(globalCtx) + mod.imported = true + } + return +} + +func ImportInContextByGlobPattern(pattern string) (count int, err error) { + var matched bool + for name, mod := range moduleRegister { + if matched, err = filepath.Match(pattern, name); err == nil { + if matched { + count++ + mod.importFunc(globalCtx) + mod.imported = true + } + } else { + break + } + } + return +} + +func GetVar(ctx ExprContext, name string) (value any, exists bool) { + if value, exists = ctx.GetVar(name); !exists { + value, exists = globalCtx.GetVar(name) + } + return +} + +func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) { + if item, exists = ctx.GetFuncInfo(name); exists { + ownerCtx = ctx + } else if item, exists = globalCtx.GetFuncInfo(name); exists { + ownerCtx = globalCtx + } + return +} + +func init() { + globalCtx = NewSimpleFuncStore() +} diff --git a/module-register.go b/module-register.go index ff5cbb4..1499cfe 100644 --- a/module-register.go +++ b/module-register.go @@ -6,7 +6,6 @@ package expr import ( "fmt" - "path/filepath" ) type module struct { @@ -31,30 +30,30 @@ func registerImport(name string, importFunc func(ExprContext), description strin moduleRegister[name] = newModule(importFunc, description) } -func ImportInContext(ctx ExprContext, name string) (exists bool) { - var mod *module - if mod, exists = moduleRegister[name]; exists { - mod.importFunc(ctx) - mod.imported = true - } - return -} +// func ImportInContext(ctx ExprContext, name string) (exists bool) { +// var mod *module +// if mod, exists = moduleRegister[name]; exists { +// mod.importFunc(ctx) +// mod.imported = true +// } +// return +// } -func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) { - var matched bool - for name, mod := range moduleRegister { - if matched, err = filepath.Match(pattern, name); err == nil { - if matched { - count++ - mod.importFunc(ctx) - mod.imported = true - } - } else { - break - } - } - return -} +// func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) { +// var matched bool +// for name, mod := range moduleRegister { +// if matched, err = filepath.Match(pattern, name); err == nil { +// if matched { +// count++ +// mod.importFunc(ctx) +// mod.imported = true +// } +// } else { +// break +// } +// } +// return +// } func IterateModules(op func(name, description string, imported bool) bool) { if op != nil { diff --git a/operand-func.go b/operand-func.go index 1512cd3..b4eaf7f 100644 --- a/operand-func.go +++ b/operand-func.go @@ -23,7 +23,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term { // -------- eval func call func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) { - if info, exists := ctx.GetFuncInfo(name); exists { + if info, exists, owner := GetFuncInfo(ctx, name); exists { if info.MinArgs() > len(params) { if info.MaxArgs() < 0 { err = fmt.Errorf("too few params -- expected %d or more, got %d", info.MinArgs(), len(params)) @@ -31,9 +31,12 @@ func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) { err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params)) } } - if info.MaxArgs() >= 0 && info.MaxArgs() < len(params) { + if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(params) { err = fmt.Errorf("too much params -- expected %d, got %d", info.MaxArgs(), len(params)) } + if err == nil && owner != ctx { + ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs()) + } } else { err = fmt.Errorf("unknown function %s()", name) } diff --git a/operand-var.go b/operand-var.go index 04fd4d2..ce4f1a4 100644 --- a/operand-var.go +++ b/operand-var.go @@ -24,8 +24,8 @@ func newVarTerm(tk *Token) *term { func evalVar(ctx ExprContext, self *term) (v any, err error) { var exists bool name := self.source() - if v, exists = ctx.GetVar(name); !exists { - if info, exists := ctx.GetFuncInfo(name); exists { + if v, exists = GetVar(ctx, name); !exists { + if info, exists, _ := GetFuncInfo(ctx, name); exists { v = info.Functor() } else { err = fmt.Errorf("undefined variable or function %q", name) diff --git a/operator-builtin.go b/operator-builtin.go index 187a9b2..e6b3e1e 100644 --- a/operator-builtin.go +++ b/operator-builtin.go @@ -28,13 +28,13 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) { count := 0 if IsString(childValue) { module, _ := childValue.(string) - count, err = ImportInContextByGlobPattern(ctx, module) + count, err = ImportInContextByGlobPattern(module) } else { var moduleSpec any it := NewAnyIterator(childValue) for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { if module, ok := moduleSpec.(string); ok { - if ImportInContext(ctx, module) { + if ImportInContext(module) { count++ } else { err = self.Errorf("unknown module %q", module)