enable relative imports (#285)
* enable relative imports * update per 1st review * remove symlink stuffs * fix cli run in make file * make resolving import path explicit * fix importDir
This commit is contained in:
parent
e059953c35
commit
4ed75764ce
10 changed files with 102 additions and 20 deletions
1
Makefile
1
Makefile
|
@ -6,6 +6,7 @@ lint:
|
||||||
|
|
||||||
test: generate lint
|
test: generate lint
|
||||||
go test -race -cover ./...
|
go test -race -cover ./...
|
||||||
|
go run ./cmd/tengo -resolve ./testdata/cli/test.tengo
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
|
|
|
@ -25,6 +25,7 @@ var (
|
||||||
compileOutput string
|
compileOutput string
|
||||||
showHelp bool
|
showHelp bool
|
||||||
showVersion bool
|
showVersion bool
|
||||||
|
resolvePath bool // TODO Remove this flag at version 3
|
||||||
version = "dev"
|
version = "dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,6 +33,8 @@ func init() {
|
||||||
flag.BoolVar(&showHelp, "help", false, "Show help")
|
flag.BoolVar(&showHelp, "help", false, "Show help")
|
||||||
flag.StringVar(&compileOutput, "o", "", "Compile output file")
|
flag.StringVar(&compileOutput, "o", "", "Compile output file")
|
||||||
flag.BoolVar(&showVersion, "version", false, "Show version")
|
flag.BoolVar(&showVersion, "version", false, "Show version")
|
||||||
|
flag.BoolVar(&resolvePath, "resolve", false,
|
||||||
|
"Resolve relative import paths")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,10 +58,20 @@ func main() {
|
||||||
inputData, err := ioutil.ReadFile(inputFile)
|
inputData, err := ioutil.ReadFile(inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr,
|
_, _ = fmt.Fprintf(os.Stderr,
|
||||||
"Error reading input file: %s", err.Error())
|
"Error reading input file: %s\n", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputFile, err = filepath.Abs(inputFile)
|
||||||
|
if err != nil {
|
||||||
|
_, _ = fmt.Fprintf(os.Stderr, "Error file path: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(inputData) > 1 && string(inputData[:2]) == "#!" {
|
||||||
|
copy(inputData, "//")
|
||||||
|
}
|
||||||
|
|
||||||
if compileOutput != "" {
|
if compileOutput != "" {
|
||||||
err := CompileOnly(modules, inputData, inputFile,
|
err := CompileOnly(modules, inputData, inputFile,
|
||||||
compileOutput)
|
compileOutput)
|
||||||
|
@ -67,9 +80,6 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
} else if filepath.Ext(inputFile) == sourceFileExt {
|
} else if filepath.Ext(inputFile) == sourceFileExt {
|
||||||
if len(inputData) > 1 && string(inputData[:2]) == "#!" {
|
|
||||||
copy(inputData, "//")
|
|
||||||
}
|
|
||||||
err := CompileAndRun(modules, inputData, inputFile)
|
err := CompileAndRun(modules, inputData, inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintln(os.Stderr, err.Error())
|
_, _ = fmt.Fprintln(os.Stderr, err.Error())
|
||||||
|
@ -90,7 +100,7 @@ func CompileOnly(
|
||||||
data []byte,
|
data []byte,
|
||||||
inputFile, outputFile string,
|
inputFile, outputFile string,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
bytecode, err := compileSrc(modules, data, filepath.Base(inputFile))
|
bytecode, err := compileSrc(modules, data, inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -125,7 +135,7 @@ func CompileAndRun(
|
||||||
data []byte,
|
data []byte,
|
||||||
inputFile string,
|
inputFile string,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
bytecode, err := compileSrc(modules, data, filepath.Base(inputFile))
|
bytecode, err := compileSrc(modules, data, inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -215,10 +225,10 @@ func RunREPL(modules *tengo.ModuleMap, in io.Reader, out io.Writer) {
|
||||||
func compileSrc(
|
func compileSrc(
|
||||||
modules *tengo.ModuleMap,
|
modules *tengo.ModuleMap,
|
||||||
src []byte,
|
src []byte,
|
||||||
filename string,
|
inputFile string,
|
||||||
) (*tengo.Bytecode, error) {
|
) (*tengo.Bytecode, error) {
|
||||||
fileSet := parser.NewFileSet()
|
fileSet := parser.NewFileSet()
|
||||||
srcFile := fileSet.AddFile(filename, -1, len(src))
|
srcFile := fileSet.AddFile(filepath.Base(inputFile), -1, len(src))
|
||||||
|
|
||||||
p := parser.NewParser(srcFile, src, nil)
|
p := parser.NewParser(srcFile, src, nil)
|
||||||
file, err := p.ParseFile()
|
file, err := p.ParseFile()
|
||||||
|
@ -228,6 +238,9 @@ func compileSrc(
|
||||||
|
|
||||||
c := tengo.NewCompiler(srcFile, nil, nil, modules, nil)
|
c := tengo.NewCompiler(srcFile, nil, nil, modules, nil)
|
||||||
c.EnableFileImport(true)
|
c.EnableFileImport(true)
|
||||||
|
if resolvePath {
|
||||||
|
c.SetImportDir(filepath.Dir(inputFile))
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.Compile(file); err != nil {
|
if err := c.Compile(file); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
32
compiler.go
32
compiler.go
|
@ -44,6 +44,7 @@ type Compiler struct {
|
||||||
file *parser.SourceFile
|
file *parser.SourceFile
|
||||||
parent *Compiler
|
parent *Compiler
|
||||||
modulePath string
|
modulePath string
|
||||||
|
importDir string
|
||||||
constants []Object
|
constants []Object
|
||||||
symbolTable *SymbolTable
|
symbolTable *SymbolTable
|
||||||
scopes []compilationScope
|
scopes []compilationScope
|
||||||
|
@ -520,7 +521,7 @@ func (c *Compiler) Compile(node parser.Node) error {
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
case []byte: // module written in Tengo
|
case []byte: // module written in Tengo
|
||||||
compiled, err := c.compileModule(node,
|
compiled, err := c.compileModule(node,
|
||||||
node.ModuleName, node.ModuleName, v)
|
node.ModuleName, v, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -537,24 +538,20 @@ func (c *Compiler) Compile(node parser.Node) error {
|
||||||
moduleName += ".tengo"
|
moduleName += ".tengo"
|
||||||
}
|
}
|
||||||
|
|
||||||
modulePath, err := filepath.Abs(moduleName)
|
modulePath, err := filepath.Abs(
|
||||||
|
filepath.Join(c.importDir, moduleName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.errorf(node, "module file path error: %s",
|
return c.errorf(node, "module file path error: %s",
|
||||||
err.Error())
|
err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.checkCyclicImports(node, modulePath); err != nil {
|
moduleSrc, err := ioutil.ReadFile(modulePath)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
moduleSrc, err := ioutil.ReadFile(moduleName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.errorf(node, "module file read error: %s",
|
return c.errorf(node, "module file read error: %s",
|
||||||
err.Error())
|
err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
compiled, err := c.compileModule(node,
|
compiled, err := c.compileModule(node, modulePath, moduleSrc, true)
|
||||||
moduleName, modulePath, moduleSrc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -634,6 +631,11 @@ func (c *Compiler) EnableFileImport(enable bool) {
|
||||||
c.allowFileImport = enable
|
c.allowFileImport = enable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetImportDir sets the initial import directory path for file imports.
|
||||||
|
func (c *Compiler) SetImportDir(dir string) {
|
||||||
|
c.importDir = dir
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) compileAssign(
|
func (c *Compiler) compileAssign(
|
||||||
node parser.Node,
|
node parser.Node,
|
||||||
lhs, rhs []parser.Expr,
|
lhs, rhs []parser.Expr,
|
||||||
|
@ -957,8 +959,9 @@ func (c *Compiler) checkCyclicImports(
|
||||||
|
|
||||||
func (c *Compiler) compileModule(
|
func (c *Compiler) compileModule(
|
||||||
node parser.Node,
|
node parser.Node,
|
||||||
moduleName, modulePath string,
|
modulePath string,
|
||||||
src []byte,
|
src []byte,
|
||||||
|
isFile bool,
|
||||||
) (*CompiledFunction, error) {
|
) (*CompiledFunction, error) {
|
||||||
if err := c.checkCyclicImports(node, modulePath); err != nil {
|
if err := c.checkCyclicImports(node, modulePath); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -969,7 +972,7 @@ func (c *Compiler) compileModule(
|
||||||
return compiledModule, nil
|
return compiledModule, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
modFile := c.file.Set().AddFile(moduleName, -1, len(src))
|
modFile := c.file.Set().AddFile(modulePath, -1, len(src))
|
||||||
p := parser.NewParser(modFile, src, nil)
|
p := parser.NewParser(modFile, src, nil)
|
||||||
file, err := p.ParseFile()
|
file, err := p.ParseFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -986,7 +989,7 @@ func (c *Compiler) compileModule(
|
||||||
symbolTable = symbolTable.Fork(false)
|
symbolTable = symbolTable.Fork(false)
|
||||||
|
|
||||||
// compile module
|
// compile module
|
||||||
moduleCompiler := c.fork(modFile, modulePath, symbolTable)
|
moduleCompiler := c.fork(modFile, modulePath, symbolTable, isFile)
|
||||||
if err := moduleCompiler.Compile(file); err != nil {
|
if err := moduleCompiler.Compile(file); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1084,11 +1087,16 @@ func (c *Compiler) fork(
|
||||||
file *parser.SourceFile,
|
file *parser.SourceFile,
|
||||||
modulePath string,
|
modulePath string,
|
||||||
symbolTable *SymbolTable,
|
symbolTable *SymbolTable,
|
||||||
|
isFile bool,
|
||||||
) *Compiler {
|
) *Compiler {
|
||||||
child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
|
child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
|
||||||
child.modulePath = modulePath // module file path
|
child.modulePath = modulePath // module file path
|
||||||
child.parent = c // parent to set to current compiler
|
child.parent = c // parent to set to current compiler
|
||||||
child.allowFileImport = c.allowFileImport
|
child.allowFileImport = c.allowFileImport
|
||||||
|
child.importDir = c.importDir
|
||||||
|
if isFile && c.importDir != "" {
|
||||||
|
child.importDir = filepath.Dir(modulePath)
|
||||||
|
}
|
||||||
return child
|
return child
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
script.go
13
script.go
|
@ -3,6 +3,7 @@ package tengo
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/d5/tengo/v2/parser"
|
"github.com/d5/tengo/v2/parser"
|
||||||
|
@ -16,6 +17,7 @@ type Script struct {
|
||||||
maxAllocs int64
|
maxAllocs int64
|
||||||
maxConstObjects int
|
maxConstObjects int
|
||||||
enableFileImport bool
|
enableFileImport bool
|
||||||
|
importDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewScript creates a Script instance with an input script.
|
// NewScript creates a Script instance with an input script.
|
||||||
|
@ -56,6 +58,16 @@ func (s *Script) SetImports(modules *ModuleMap) {
|
||||||
s.modules = modules
|
s.modules = modules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetImportDir sets the initial import directory for script files.
|
||||||
|
func (s *Script) SetImportDir(dir string) error {
|
||||||
|
dir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.importDir = dir
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetMaxAllocs sets the maximum number of objects allocations during the run
|
// SetMaxAllocs sets the maximum number of objects allocations during the run
|
||||||
// time. Compiled script will return ErrObjectAllocLimit error if it
|
// time. Compiled script will return ErrObjectAllocLimit error if it
|
||||||
// exceeds this limit.
|
// exceeds this limit.
|
||||||
|
@ -93,6 +105,7 @@ func (s *Script) Compile() (*Compiled, error) {
|
||||||
|
|
||||||
c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil)
|
c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil)
|
||||||
c.EnableFileImport(s.enableFileImport)
|
c.EnableFileImport(s.enableFileImport)
|
||||||
|
c.SetImportDir(s.importDir)
|
||||||
if err := c.Compile(file); err != nil {
|
if err := c.Compile(file); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
7
testdata/cli/one.tengo
vendored
Normal file
7
testdata/cli/one.tengo
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
export {
|
||||||
|
fn: func(a) {
|
||||||
|
two := import("two/two")
|
||||||
|
return two.fn(a, "one")
|
||||||
|
}
|
||||||
|
}
|
15
testdata/cli/test.tengo
vendored
Executable file
15
testdata/cli/test.tengo
vendored
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env tengo
|
||||||
|
|
||||||
|
os := import("os")
|
||||||
|
one := import("one")
|
||||||
|
fmt := import("fmt")
|
||||||
|
text := import("text")
|
||||||
|
expected := ["test", "one", "two", "three", "four", "five"]
|
||||||
|
expected = text.join(expected, " ")
|
||||||
|
if v := one.fn("test"); v != expected {
|
||||||
|
fmt.printf("relative import test error:\n\texpected: %v\n\tgot : %v\n",
|
||||||
|
expected, v)
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
args := text.join(os.args(), " ")
|
||||||
|
fmt.println("ok\t", args)
|
6
testdata/cli/three.tengo
vendored
Normal file
6
testdata/cli/three.tengo
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export {
|
||||||
|
fn: func(a, b, c) {
|
||||||
|
four := import("./two/four/four.tengo")
|
||||||
|
return four.fn(a, b, c, "three")
|
||||||
|
}
|
||||||
|
}
|
7
testdata/cli/two/five/five.tengo
vendored
Normal file
7
testdata/cli/two/five/five.tengo
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export {
|
||||||
|
fn: func(...args) {
|
||||||
|
text := import("text")
|
||||||
|
args = append(args, "five")
|
||||||
|
return text.join(args, " ")
|
||||||
|
}
|
||||||
|
}
|
6
testdata/cli/two/four/four.tengo
vendored
Normal file
6
testdata/cli/two/four/four.tengo
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export {
|
||||||
|
fn: func(a, b, c, d) {
|
||||||
|
five := import("../five/five")
|
||||||
|
return five.fn(a, b, c, d, "four")
|
||||||
|
}
|
||||||
|
}
|
6
testdata/cli/two/two.tengo
vendored
Normal file
6
testdata/cli/two/two.tengo
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export {
|
||||||
|
fn: func(a, b) {
|
||||||
|
three := import("../three")
|
||||||
|
return three.fn(a, b, "two")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue