Dynamically generated code for import
bfreis opened this issue · 0 comments
Hi!
While using tengo
embedded into some code, I wanted to make it possible to dynamically provide source code for imports, without having to import files from disk. Specifically, the import names I want to use do not map to files on disk, and are unknown before compiling a script (so I couldn't even somehow pre-generate them anywhere).
I couldn't find any easy way to do it, just a very ugly hack, based on trying to compile on a loop, handling any CompilerError
on ImportExpr
by generating the module I need, adding it to a ModuleMap
, and trying again. Something like this:
moduleMap := tengo.NewModuleMap()
script := tengo.NewScript(src)
script.SetImports(moduleMap)
var c *tengo.Compiled
var err error
done := false
for !done {
done = true
c, err = script.Compile()
var cerr *tengo.CompilerError
if errors.As(err, &cerr) {
if node, ok := cerr.Node.(*parser.ImportExpr); ok {
mSrc := generateModuleSource(node.ModuleName)
moduleMap.AddSourceModule(node.ModuleName, mSrc)
err = nil
done = false
}
}
}
if err != nil { ... }
// use c here
But this is beyond ugly.
There's a rather simple alternative: if we extract a trivial interface from *tengo.ModuleMap
with just its Get
method, and change a few places that declare parameters or fields with the new interface, I can pass an interface instead, and do my dynamic generation there.
E.g.:
// (some changes omitted for brevity)
// tengo: modules.go:
type ModuleGetter interface {
Get(name string) Importable
}
// tengo: script.go:
func (s *Script) SetImports(modules ModuleGetter) {
s.modules = modules
}
// my_code.go:
type DynamicModuleGetter { ... }
func (d *DynamicModuleGetter) Get(name string) tengo.Importable {
src := ...
return &tengo.SourceModule{Src: src}
}
// ...
m := &DynamicModuleGetter{...}
script := tengo.NewScript(src)
script.SetImports(m)
c, err := script.Compile()
if err != nil { ... }
// use c here
This has the added benefit of getting the code closer to Go's good practice of taking interfaces instead of concrete types.
Will send a PR soon.
Thoughts?