// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // operator-plugin.go package expr import ( "fmt" "io" "plugin" ) //-------- plugin term func newPluginTerm(tk *Token) (inst *term) { return &term{ tk: *tk, children: make([]*term, 0, 1), position: posPrefix, priority: priSign, evalFunc: evalPlugin, } } func importPlugin(ctx ExprContext, dirList []string, name string) (err error) { var filePath string var p *plugin.Plugin var sym plugin.Symbol var moduleName string var importFunc func(ctx ExprContext) var ok bool decoratedName := fmt.Sprintf("expr-%s-plugin.so", name) if filePath, err = makeFilepath(decoratedName, dirList); err != nil { return } if p, err = plugin.Open(filePath); err != nil { return } if sym, err = p.Lookup("MODULE_NAME"); err != nil { return } if moduleName = *sym.(*string); moduleName == "" { err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName) return } if sym, err = p.Lookup("ImportFuncs"); err != nil { return } if importFunc, ok = sym.(func(ExprContext)); !ok { err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName) return } registerPlugin(moduleName, p) importFunc(globalCtx) return } func evalPlugin(ctx ExprContext, self *term) (v any, err error) { var childValue any var moduleSpec any if childValue, err = self.evalPrefix(ctx); err != nil { return } dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH) count := 0 it := NewAnyIterator(childValue) for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { if module, ok := moduleSpec.(string); ok { if err = importPlugin(ctx, dirList, module); err != nil { break } count++ } else { err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, typeName(moduleSpec)) break } } if err == io.EOF { err = nil } if err == nil { v = int64(count) } return } // init func init() { registerTermConstructor(SymKwPlugin, newPluginTerm) }