Skip to content

Commit

Permalink
enable relative imports (#285)
Browse files Browse the repository at this point in the history
* enable relative imports

* update per 1st review

* remove symlink stuffs

* fix cli run in make file

* make resolving import path explicit

* fix importDir
  • Loading branch information
Ozan Hacıbekiroğlu committed May 22, 2020
1 parent e059953 commit 4ed7576
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 20 deletions.
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -6,6 +6,7 @@ lint:

test: generate lint
go test -race -cover ./...
go run ./cmd/tengo -resolve ./testdata/cli/test.tengo

fmt:
go fmt ./...
29 changes: 21 additions & 8 deletions cmd/tengo/main.go
Expand Up @@ -25,13 +25,16 @@ var (
compileOutput string
showHelp bool
showVersion bool
resolvePath bool // TODO Remove this flag at version 3
version = "dev"
)

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()
}

Expand All @@ -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)
Expand All @@ -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())
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down
32 changes: 20 additions & 12 deletions compiler.go
Expand Up @@ -44,6 +44,7 @@ type Compiler struct {
file *parser.SourceFile
parent *Compiler
modulePath string
importDir string
constants []Object
symbolTable *SymbolTable
scopes []compilationScope
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}

Expand Down
13 changes: 13 additions & 0 deletions script.go
Expand Up @@ -3,6 +3,7 @@ package tengo
import (
"context"
"fmt"
"path/filepath"
"sync"

"github.com/d5/tengo/v2/parser"
Expand All @@ -16,6 +17,7 @@ type Script struct {
maxAllocs int64
maxConstObjects int
enableFileImport bool
importDir string
}

// NewScript creates a Script instance with an input script.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down
7 changes: 7 additions & 0 deletions testdata/cli/one.tengo
@@ -0,0 +1,7 @@

export {
fn: func(a) {
two := import("two/two")
return two.fn(a, "one")
}
}
15 changes: 15 additions & 0 deletions testdata/cli/test.tengo
@@ -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 changes: 6 additions & 0 deletions testdata/cli/three.tengo
@@ -0,0 +1,6 @@
export {
fn: func(a, b, c) {
four := import("./two/four/four.tengo")
return four.fn(a, b, c, "three")
}
}
7 changes: 7 additions & 0 deletions testdata/cli/two/five/five.tengo
@@ -0,0 +1,7 @@
export {
fn: func(...args) {
text := import("text")
args = append(args, "five")
return text.join(args, " ")
}
}
6 changes: 6 additions & 0 deletions testdata/cli/two/four/four.tengo
@@ -0,0 +1,6 @@
export {
fn: func(a, b, c, d) {
five := import("../five/five")
return five.fn(a, b, c, d, "four")
}
}
6 changes: 6 additions & 0 deletions testdata/cli/two/two.tengo
@@ -0,0 +1,6 @@
export {
fn: func(a, b) {
three := import("../three")
return three.fn(a, b, "two")
}
}

0 comments on commit 4ed7576

Please sign in to comment.