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
|
||||
go test -race -cover ./...
|
||||
go run ./cmd/tengo -resolve ./testdata/cli/test.tengo
|
||||
|
||||
fmt:
|
||||
go fmt ./...
|
||||
|
|
|
@ -25,6 +25,7 @@ var (
|
|||
compileOutput string
|
||||
showHelp bool
|
||||
showVersion bool
|
||||
resolvePath bool // TODO Remove this flag at version 3
|
||||
version = "dev"
|
||||
)
|
||||
|
||||
|
@ -32,6 +33,8 @@ func init() {
|
|||
flag.BoolVar(&showHelp, "help", false, "Show help")
|
||||
flag.StringVar(&compileOutput, "o", "", "Compile output file")
|
||||
flag.BoolVar(&showVersion, "version", false, "Show version")
|
||||
flag.BoolVar(&resolvePath, "resolve", false,
|
||||
"Resolve relative import paths")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
|
@ -55,10 +58,20 @@ func main() {
|
|||
inputData, err := ioutil.ReadFile(inputFile)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr,
|
||||
"Error reading input file: %s", err.Error())
|
||||
"Error reading input file: %s\n", err.Error())
|
||||
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 != "" {
|
||||
err := CompileOnly(modules, inputData, inputFile,
|
||||
compileOutput)
|
||||
|
@ -67,9 +80,6 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
} else if filepath.Ext(inputFile) == sourceFileExt {
|
||||
if len(inputData) > 1 && string(inputData[:2]) == "#!" {
|
||||
copy(inputData, "//")
|
||||
}
|
||||
err := CompileAndRun(modules, inputData, inputFile)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintln(os.Stderr, err.Error())
|
||||
|
@ -90,7 +100,7 @@ func CompileOnly(
|
|||
data []byte,
|
||||
inputFile, outputFile string,
|
||||
) (err error) {
|
||||
bytecode, err := compileSrc(modules, data, filepath.Base(inputFile))
|
||||
bytecode, err := compileSrc(modules, data, inputFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -125,7 +135,7 @@ func CompileAndRun(
|
|||
data []byte,
|
||||
inputFile string,
|
||||
) (err error) {
|
||||
bytecode, err := compileSrc(modules, data, filepath.Base(inputFile))
|
||||
bytecode, err := compileSrc(modules, data, inputFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -215,10 +225,10 @@ func RunREPL(modules *tengo.ModuleMap, in io.Reader, out io.Writer) {
|
|||
func compileSrc(
|
||||
modules *tengo.ModuleMap,
|
||||
src []byte,
|
||||
filename string,
|
||||
inputFile string,
|
||||
) (*tengo.Bytecode, error) {
|
||||
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)
|
||||
file, err := p.ParseFile()
|
||||
|
@ -228,6 +238,9 @@ func compileSrc(
|
|||
|
||||
c := tengo.NewCompiler(srcFile, nil, nil, modules, nil)
|
||||
c.EnableFileImport(true)
|
||||
if resolvePath {
|
||||
c.SetImportDir(filepath.Dir(inputFile))
|
||||
}
|
||||
|
||||
if err := c.Compile(file); err != nil {
|
||||
return nil, err
|
||||
|
|
32
compiler.go
32
compiler.go
|
@ -44,6 +44,7 @@ type Compiler struct {
|
|||
file *parser.SourceFile
|
||||
parent *Compiler
|
||||
modulePath string
|
||||
importDir string
|
||||
constants []Object
|
||||
symbolTable *SymbolTable
|
||||
scopes []compilationScope
|
||||
|
@ -520,7 +521,7 @@ func (c *Compiler) Compile(node parser.Node) error {
|
|||
switch v := v.(type) {
|
||||
case []byte: // module written in Tengo
|
||||
compiled, err := c.compileModule(node,
|
||||
node.ModuleName, node.ModuleName, v)
|
||||
node.ModuleName, v, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -537,24 +538,20 @@ func (c *Compiler) Compile(node parser.Node) error {
|
|||
moduleName += ".tengo"
|
||||
}
|
||||
|
||||
modulePath, err := filepath.Abs(moduleName)
|
||||
modulePath, err := filepath.Abs(
|
||||
filepath.Join(c.importDir, moduleName))
|
||||
if err != nil {
|
||||
return c.errorf(node, "module file path error: %s",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
if err := c.checkCyclicImports(node, modulePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
moduleSrc, err := ioutil.ReadFile(moduleName)
|
||||
moduleSrc, err := ioutil.ReadFile(modulePath)
|
||||
if err != nil {
|
||||
return c.errorf(node, "module file read error: %s",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
compiled, err := c.compileModule(node,
|
||||
moduleName, modulePath, moduleSrc)
|
||||
compiled, err := c.compileModule(node, modulePath, moduleSrc, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -634,6 +631,11 @@ func (c *Compiler) EnableFileImport(enable bool) {
|
|||
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(
|
||||
node parser.Node,
|
||||
lhs, rhs []parser.Expr,
|
||||
|
@ -957,8 +959,9 @@ func (c *Compiler) checkCyclicImports(
|
|||
|
||||
func (c *Compiler) compileModule(
|
||||
node parser.Node,
|
||||
moduleName, modulePath string,
|
||||
modulePath string,
|
||||
src []byte,
|
||||
isFile bool,
|
||||
) (*CompiledFunction, error) {
|
||||
if err := c.checkCyclicImports(node, modulePath); err != nil {
|
||||
return nil, err
|
||||
|
@ -969,7 +972,7 @@ func (c *Compiler) compileModule(
|
|||
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)
|
||||
file, err := p.ParseFile()
|
||||
if err != nil {
|
||||
|
@ -986,7 +989,7 @@ func (c *Compiler) compileModule(
|
|||
symbolTable = symbolTable.Fork(false)
|
||||
|
||||
// compile module
|
||||
moduleCompiler := c.fork(modFile, modulePath, symbolTable)
|
||||
moduleCompiler := c.fork(modFile, modulePath, symbolTable, isFile)
|
||||
if err := moduleCompiler.Compile(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1084,11 +1087,16 @@ func (c *Compiler) fork(
|
|||
file *parser.SourceFile,
|
||||
modulePath string,
|
||||
symbolTable *SymbolTable,
|
||||
isFile bool,
|
||||
) *Compiler {
|
||||
child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
|
||||
child.modulePath = modulePath // module file path
|
||||
child.parent = c // parent to set to current compiler
|
||||
child.allowFileImport = c.allowFileImport
|
||||
child.importDir = c.importDir
|
||||
if isFile && c.importDir != "" {
|
||||
child.importDir = filepath.Dir(modulePath)
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
|
|
13
script.go
13
script.go
|
@ -3,6 +3,7 @@ package tengo
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/d5/tengo/v2/parser"
|
||||
|
@ -16,6 +17,7 @@ type Script struct {
|
|||
maxAllocs int64
|
||||
maxConstObjects int
|
||||
enableFileImport bool
|
||||
importDir string
|
||||
}
|
||||
|
||||
// NewScript creates a Script instance with an input script.
|
||||
|
@ -56,6 +58,16 @@ func (s *Script) SetImports(modules *ModuleMap) {
|
|||
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
|
||||
// time. Compiled script will return ErrObjectAllocLimit error if it
|
||||
// exceeds this limit.
|
||||
|
@ -93,6 +105,7 @@ func (s *Script) Compile() (*Compiled, error) {
|
|||
|
||||
c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil)
|
||||
c.EnableFileImport(s.enableFileImport)
|
||||
c.SetImportDir(s.importDir)
|
||||
if err := c.Compile(file); err != nil {
|
||||
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