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.

This commit is contained in:
Celestino Amoroso 2024-05-11 10:45:38 +02:00
parent 0a9543543d
commit 50e7168214
6 changed files with 88 additions and 31 deletions

View File

@ -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)
}

54
global-context.go Normal file
View File

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

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)

View File

@ -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)