3f55a6b5b0
Implement module imports
116 lines
2.5 KiB
Go
116 lines
2.5 KiB
Go
package compiler
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"strings"
|
|
|
|
"github.com/d5/tengo/compiler/parser"
|
|
"github.com/d5/tengo/compiler/source"
|
|
"github.com/d5/tengo/objects"
|
|
)
|
|
|
|
var (
|
|
fileSet = source.NewFileSet()
|
|
)
|
|
|
|
func (c *Compiler) compileModule(moduleName string) (*objects.CompiledModule, error) {
|
|
compiledModule, exists := c.loadCompiledModule(moduleName)
|
|
if exists {
|
|
return compiledModule, nil
|
|
}
|
|
|
|
// read module source from loader
|
|
var moduleSrc []byte
|
|
if c.moduleLoader == nil {
|
|
// default loader: read from local file
|
|
if !strings.HasSuffix(moduleName, ".tengo") {
|
|
moduleName += ".tengo"
|
|
}
|
|
|
|
if err := c.checkCyclicImports(moduleName); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var err error
|
|
moduleSrc, err = ioutil.ReadFile(moduleName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if err := c.checkCyclicImports(moduleName); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var err error
|
|
moduleSrc, err = c.moduleLoader(moduleName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
compiledModule, err := c.doCompileModule(moduleName, moduleSrc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c.storeCompiledModule(moduleName, compiledModule)
|
|
|
|
return compiledModule, nil
|
|
}
|
|
|
|
func (c *Compiler) checkCyclicImports(moduleName string) error {
|
|
if c.moduleName == moduleName {
|
|
return fmt.Errorf("cyclic module import: %s", moduleName)
|
|
} else if c.parent != nil {
|
|
return c.parent.checkCyclicImports(moduleName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.CompiledModule, error) {
|
|
p := parser.NewParser(fileSet.AddFile(moduleName, -1, len(src)), src, nil)
|
|
file, err := p.ParseFile()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
symbolTable := NewSymbolTable()
|
|
globals := make(map[string]int)
|
|
|
|
moduleCompiler := c.fork(moduleName, symbolTable)
|
|
if err := moduleCompiler.Compile(file); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, name := range symbolTable.Names() {
|
|
symbol, _, _ := symbolTable.Resolve(name)
|
|
if symbol.Scope == ScopeGlobal {
|
|
globals[name] = symbol.Index
|
|
}
|
|
}
|
|
|
|
return &objects.CompiledModule{
|
|
Instructions: moduleCompiler.Bytecode().Instructions,
|
|
Globals: globals,
|
|
}, nil
|
|
}
|
|
|
|
func (c *Compiler) loadCompiledModule(moduleName string) (mod *objects.CompiledModule, ok bool) {
|
|
if c.parent != nil {
|
|
return c.parent.loadCompiledModule(moduleName)
|
|
}
|
|
|
|
mod, ok = c.compiledModules[moduleName]
|
|
|
|
return
|
|
}
|
|
|
|
func (c *Compiler) storeCompiledModule(moduleName string, module *objects.CompiledModule) {
|
|
if c.parent != nil {
|
|
c.parent.storeCompiledModule(moduleName, module)
|
|
}
|
|
|
|
c.compiledModules[moduleName] = module
|
|
}
|