summaryrefslogtreecommitdiff
path: root/llgo
diff options
context:
space:
mode:
authorAndrew Wilkins <axwalk@gmail.com>2016-04-25 01:18:20 +0000
committerAndrew Wilkins <axwalk@gmail.com>2016-04-25 01:18:20 +0000
commit48a68f87174deac522a0039305a315b7e17aedf0 (patch)
tree3c513da7ab9cbe0f8ec780cb242cddd24667dca2 /llgo
parentda3b1e0100dd2777b96ce1339ab2cc29d5f65991 (diff)
[llgo] llgoi: separate evaluation from printing
Summary: Separate the evaluation of expressions from printing of results. This is in preparation for splitting the core of the interpreter out for use in alternative interpreter frontends. At the same time, the output is made less noisy in response to comments on the golang-nuts announcement. We would ideally print out values using Go syntax, but this is impractical until we have libgo based on Go 1.5. When that happens, fmt's %#v will handle reflect.Value better, and so we can fix/filter type names to remove automatically generated package names. Reviewers: pcc Subscribers: llvm-commits, axw Differential Revision: http://reviews.llvm.org/D13761
Diffstat (limited to 'llgo')
-rw-r--r--llgo/cmd/llgoi/llgoi.go147
-rw-r--r--llgo/test/llgoi/arith.test2
-rw-r--r--llgo/test/llgoi/import-source.test6
-rw-r--r--llgo/test/llgoi/import-source2.test6
-rw-r--r--llgo/test/llgoi/interfaces.test19
-rw-r--r--llgo/test/llgoi/maps.test23
-rw-r--r--llgo/test/llgoi/vars.test15
7 files changed, 124 insertions, 94 deletions
diff --git a/llgo/cmd/llgoi/llgoi.go b/llgo/cmd/llgoi/llgoi.go
index 401e8f34df2..4aa774d6363 100644
--- a/llgo/cmd/llgoi/llgoi.go
+++ b/llgo/cmd/llgoi/llgoi.go
@@ -83,8 +83,9 @@ type interp struct {
imports []*types.Package
scope map[string]types.Object
- pkgmap map[string]*types.Package
- pkgnum int
+ modules map[string]llvm.Module
+ pkgmap map[string]*types.Package
+ pkgnum int
}
func (in *interp) makeCompilerOptions() error {
@@ -119,6 +120,7 @@ func (in *interp) init() error {
in.liner = liner.NewLiner()
in.scope = make(map[string]types.Object)
in.pkgmap = make(map[string]*types.Package)
+ in.modules = make(map[string]llvm.Module)
err := in.makeCompilerOptions()
if err != nil {
@@ -139,23 +141,21 @@ func (in *interp) loadSourcePackageFromCode(pkgcode, pkgpath string, copts irgen
if err != nil {
return nil, err
}
-
files := []*ast.File{file}
-
return in.loadSourcePackage(fset, files, pkgpath, copts)
}
-func (in *interp) loadSourcePackage(fset *token.FileSet, files []*ast.File, pkgpath string, copts irgen.CompilerOptions) (pkg *types.Package, err error) {
+func (in *interp) loadSourcePackage(fset *token.FileSet, files []*ast.File, pkgpath string, copts irgen.CompilerOptions) (_ *types.Package, resultErr error) {
compiler, err := irgen.NewCompiler(copts)
if err != nil {
- return
+ return nil, err
}
module, err := compiler.Compile(fset, files, pkgpath)
if err != nil {
- return
+ return nil, err
}
- pkg = module.Package
+ in.modules[pkgpath] = module.Module
if in.engine.C != nil {
in.engine.AddModule(module.Module)
@@ -163,25 +163,33 @@ func (in *interp) loadSourcePackage(fset *token.FileSet, files []*ast.File, pkgp
options := llvm.NewMCJITCompilerOptions()
in.engine, err = llvm.NewMCJITCompiler(module.Module, options)
if err != nil {
- return
+ return nil, err
}
}
- importname := irgen.ManglePackagePath(pkgpath) + "..import$descriptor"
- importglobal := module.Module.NamedGlobal(importname)
-
- var importfunc func()
- *(*unsafe.Pointer)(unsafe.Pointer(&importfunc)) = in.engine.PointerToGlobal(importglobal)
+ var importFunc func()
+ importAddress := in.getPackageSymbol(pkgpath, ".import$descriptor")
+ *(*unsafe.Pointer)(unsafe.Pointer(&importFunc)) = importAddress
defer func() {
p := recover()
if p != nil {
- err = fmt.Errorf("panic: %v\n%v", p, string(debug.Stack()))
+ resultErr = fmt.Errorf("panic: %v\n%v", p, string(debug.Stack()))
}
}()
- importfunc()
- in.pkgmap[pkgpath] = pkg
- return
+ importFunc()
+ in.pkgmap[pkgpath] = module.Package
+
+ return module.Package, nil
+}
+
+func (in *interp) getPackageSymbol(pkgpath, name string) unsafe.Pointer {
+ symbolName := irgen.ManglePackagePath(pkgpath) + "." + name
+ global := in.modules[pkgpath].NamedGlobal(symbolName)
+ if global.IsNil() {
+ return nil
+ }
+ return in.engine.PointerToGlobal(global)
}
func (in *interp) augmentPackageScope(pkg *types.Package) {
@@ -246,19 +254,17 @@ func (l *line) ready() bool {
return l.parens <= 0 && l.bracks <= 0 && l.braces <= 0
}
-func (in *interp) readExprLine(str string, assigns []string) error {
+func (in *interp) readExprLine(str string, assigns []string) ([]interface{}, error) {
in.pendingLine.append(str, assigns)
-
- if in.pendingLine.ready() {
- err := in.interpretLine(in.pendingLine)
- in.pendingLine = line{}
- return err
- } else {
- return nil
+ if !in.pendingLine.ready() {
+ return nil, nil
}
+ results, err := in.interpretLine(in.pendingLine)
+ in.pendingLine = line{}
+ return results, err
}
-func (in *interp) interpretLine(l line) error {
+func (in *interp) interpretLine(l line) ([]interface{}, error) {
pkgname := fmt.Sprintf("input%05d", in.pkgnum)
in.pkgnum++
@@ -277,14 +283,12 @@ func (in *interp) interpretLine(l line) error {
var err error
tv, err = types.Eval(l.line, pkg, scope)
if err != nil {
- return err
+ return nil, err
}
}
var code bytes.Buffer
- fmt.Fprintf(&code, "package %s", pkgname)
- code.WriteString("\n\nimport __fmt__ \"fmt\"\n")
- code.WriteString("import __os__ \"os\"\n")
+ fmt.Fprintf(&code, "package %s\n", pkgname)
for _, pkg := range in.imports {
fmt.Fprintf(&code, "import %q\n", pkg.Path())
@@ -306,7 +310,7 @@ func (in *interp) interpretLine(l line) error {
typs = append(typs, types.Typ[types.Bool])
}
if len(l.assigns) != 0 && len(l.assigns) != len(typs) {
- return errors.New("return value mismatch")
+ return nil, errors.New("return value mismatch")
}
code.WriteString("var ")
@@ -324,31 +328,34 @@ func (in *interp) interpretLine(l line) error {
fmt.Fprintf(&code, "__llgoiV%d", i)
}
}
- fmt.Fprintf(&code, " = %s\n\n", l.line)
+ fmt.Fprintf(&code, " = %s\n", l.line)
- code.WriteString("func init() {\n\t")
- for i, t := range typs {
- var varname, prefix string
+ code.WriteString("func init() {\n")
+ varnames := make([]string, len(typs))
+ for i := range typs {
+ var varname string
if len(l.assigns) != 0 && l.assigns[i] != "" {
if _, ok := in.scope[l.assigns[i]]; ok {
fmt.Fprintf(&code, "\t%s = __llgoiV%d\n", l.assigns[i], i)
}
varname = l.assigns[i]
- prefix = l.assigns[i]
} else {
varname = fmt.Sprintf("__llgoiV%d", i)
- prefix = fmt.Sprintf("#%d", i)
- }
- if _, ok := t.Underlying().(*types.Interface); ok {
- fmt.Fprintf(&code, "\t__fmt__.Printf(\"%s %s (%%T) = %%+v\\n\", %s, %s)\n", prefix, t.String(), varname, varname)
- } else {
- fmt.Fprintf(&code, "\t__fmt__.Printf(\"%s %s = %%+v\\n\", %s)\n", prefix, t.String(), varname)
}
+ varnames[i] = varname
+ }
+ code.WriteString("}\n\n")
+
+ code.WriteString("func __llgoiResults() []interface{} {\n")
+ code.WriteString("\treturn []interface{}{\n")
+ for _, varname := range varnames {
+ fmt.Fprintf(&code, "\t\t%s,\n", varname)
}
- code.WriteString("}")
+ code.WriteString("\t}\n")
+ code.WriteString("}\n")
} else {
if len(l.assigns) != 0 {
- return errors.New("return value mismatch")
+ return nil, errors.New("return value mismatch")
}
fmt.Fprintf(&code, "func init() {\n\t%s}", l.line)
@@ -359,11 +366,18 @@ func (in *interp) interpretLine(l line) error {
copts.DisableUnusedImportCheck = true
pkg, err := in.loadSourcePackageFromCode(code.String(), pkgname, copts)
if err != nil {
- return err
+ return nil, err
}
-
in.imports = append(in.imports, pkg)
+ var results []interface{}
+ llgoiResultsAddress := in.getPackageSymbol(pkgname, "__llgoiResults$descriptor")
+ if llgoiResultsAddress != nil {
+ var resultsFunc func() []interface{}
+ *(*unsafe.Pointer)(unsafe.Pointer(&resultsFunc)) = llgoiResultsAddress
+ results = resultsFunc()
+ }
+
for _, assign := range l.assigns {
if assign != "" {
if _, ok := in.scope[assign]; !ok {
@@ -376,7 +390,7 @@ func (in *interp) interpretLine(l line) error {
in.scope[l.declName] = pkg.Scope().Lookup(l.declName)
}
- return nil
+ return results, nil
}
func (in *interp) maybeReadAssignment(line string, s *scanner.Scanner, initial string, base int) (bool, error) {
@@ -404,7 +418,9 @@ func (in *interp) maybeReadAssignment(line string, s *scanner.Scanner, initial s
return false, nil
}
- return true, in.readExprLine(line[int(pos)-base+2:], assigns)
+ // It's an assignment statement, there are no results.
+ _, err := in.readExprLine(line[int(pos)-base+2:], assigns)
+ return true, err
}
func (in *interp) loadPackage(pkgpath string) (*types.Package, error) {
@@ -428,8 +444,6 @@ func (in *interp) loadPackage(pkgpath string) (*types.Package, error) {
}
}
- fmt.Printf("# %s\n", pkgpath)
-
inputs := make([]string, len(buildpkg.GoFiles))
for i, file := range buildpkg.GoFiles {
inputs[i] = filepath.Join(buildpkg.Dir, file)
@@ -446,7 +460,7 @@ func (in *interp) loadPackage(pkgpath string) (*types.Package, error) {
// readLine accumulates lines of input, including trailing newlines,
// executing statements as they are completed.
-func (in *interp) readLine(line string) error {
+func (in *interp) readLine(line string) ([]interface{}, error) {
if !in.pendingLine.ready() {
return in.readExprLine(line, nil)
}
@@ -459,33 +473,32 @@ func (in *interp) readLine(line string) error {
_, tok, lit := s.Scan()
switch tok {
case token.EOF:
- return nil
+ return nil, nil
case token.IMPORT:
_, tok, lit = s.Scan()
if tok != token.STRING {
- return errors.New("expected string literal")
+ return nil, errors.New("expected string literal")
}
pkgpath, err := strconv.Unquote(lit)
if err != nil {
- return err
+ return nil, err
}
pkg, err := in.loadPackage(pkgpath)
if err != nil {
- return err
+ return nil, err
}
in.imports = append(in.imports, pkg)
- return nil
+ return nil, nil
case token.IDENT:
ok, err := in.maybeReadAssignment(line, &s, lit, file.Base())
if err != nil {
- return err
+ return nil, err
}
if ok {
- return nil
+ return nil, nil
}
-
fallthrough
default:
@@ -493,6 +506,14 @@ func (in *interp) readLine(line string) error {
}
}
+// printResult prints a value that was the result of an expression evaluated
+// by the interpreter.
+func printResult(w io.Writer, v interface{}) {
+ // TODO the result should be formatted in Go syntax, without
+ // package qualifiers for types defined within the interpreter.
+ fmt.Fprintf(w, "%+v", v)
+}
+
// formatHistory reformats the provided Go source by collapsing all lines
// and adding semicolons where required, suitable for adding to line history.
func formatHistory(input []byte) string {
@@ -560,10 +581,14 @@ func main() {
continue
}
buf.WriteString(line + "\n")
- err = in.readLine(line + "\n")
+ results, err := in.readLine(line + "\n")
if err != nil {
fmt.Println(err)
}
+ for _, result := range results {
+ printResult(os.Stdout, result)
+ fmt.Println()
+ }
}
if liner.TerminalSupported() {
diff --git a/llgo/test/llgoi/arith.test b/llgo/test/llgoi/arith.test
index 83b30d9d561..b7869a9e1e3 100644
--- a/llgo/test/llgoi/arith.test
+++ b/llgo/test/llgoi/arith.test
@@ -1,4 +1,4 @@
// RUN: llgoi < %s | FileCheck %s
1 + 1
-// CHECK: #0 untyped int = 2
+// CHECK: 2
diff --git a/llgo/test/llgoi/import-source.test b/llgo/test/llgoi/import-source.test
index b545b52697e..3619fb4ba9a 100644
--- a/llgo/test/llgoi/import-source.test
+++ b/llgo/test/llgoi/import-source.test
@@ -4,17 +4,15 @@
Answer := 1
import "foo"
-// CHECK: # bar
-// CHECK: # foo
// Test that importing binary after source works.
import "strconv"
foo.Answer()
-// CHECK: #0 int = 42
+// CHECK: 42
strconv.FormatBool(true)
-// CHECK: #0 string = true
+// CHECK: true
var v1 strconv.NumError
var v2 strconv.NumError
diff --git a/llgo/test/llgoi/import-source2.test b/llgo/test/llgoi/import-source2.test
index fcf3b475bd6..0be45ee685c 100644
--- a/llgo/test/llgoi/import-source2.test
+++ b/llgo/test/llgoi/import-source2.test
@@ -4,11 +4,9 @@
import "strconv"
import "foo"
-// CHECK: # bar
-// CHECK: # foo
foo.Answer()
-// CHECK: #0 int = 42
+// CHECK: 42
strconv.FormatBool(true)
-// CHECK: #0 string = true
+// CHECK: true
diff --git a/llgo/test/llgoi/interfaces.test b/llgo/test/llgoi/interfaces.test
index bc12b09ab66..125e0157916 100644
--- a/llgo/test/llgoi/interfaces.test
+++ b/llgo/test/llgoi/interfaces.test
@@ -2,18 +2,21 @@
import "errors"
err := errors.New("foo")
-// CHECK: err error {{.*}} = foo
+err
+// CHECK: foo
err.(interface{Foo()})
// CHECK: panic: interface conversion
-_, _ := err.(interface{Foo()})
-// CHECK: #0 interface{Foo()} (<nil>) = <nil>
-// CHECK: #1 bool = false
+_, ok := err.(interface{Foo()})
+ok
+// CHECK: false
err.(interface{Error() string})
-// CHECK: #0 interface{Error() string} {{.*}} = foo
+// CHECK: foo
-_, _ := err.(interface{Error() string})
-// CHECK: #0 interface{Error() string} {{.*}} = foo
-// CHECK: #1 bool = true
+iface, ok := err.(interface{Error() string})
+iface
+// CHECK: foo
+ok
+// CHECK: true
diff --git a/llgo/test/llgoi/maps.test b/llgo/test/llgoi/maps.test
index 8678104d3e8..ab1ba7eaa11 100644
--- a/llgo/test/llgoi/maps.test
+++ b/llgo/test/llgoi/maps.test
@@ -1,22 +1,27 @@
// RUN: llgoi < %s | FileCheck %s
m := make(map[int]int)
-// CHECK: m map[int]int = map[]
+m
+// CHECK: map[]
m[0]
-// CHECK: #0 int = 0
+// CHECK: 0
-_, _ := m[0]
-// CHECK: #0 int = 0
-// CHECK: #1 bool = false
+m0, ok := m[0]
+m0
+// CHECK: 0
+ok
+// CHECK: false
func() {
m[0] = 1
}()
m[0]
-// CHECK: #0 int = 1
+// CHECK: 1
-_, _ := m[0]
-// CHECK: #0 int = 1
-// CHECK: #1 bool = true
+m0, ok = m[0]
+m0
+// CHECK: 1
+ok
+// CHECK: true
diff --git a/llgo/test/llgoi/vars.test b/llgo/test/llgoi/vars.test
index 1e1f0635636..c28198cf0f1 100644
--- a/llgo/test/llgoi/vars.test
+++ b/llgo/test/llgoi/vars.test
@@ -1,17 +1,18 @@
// RUN: llgoi < %s 2>&1 | FileCheck %s
x := 3
-// CHECK: x untyped int = 3
+x
+// CHECK: 3
x + x
-// CHECK: #0 int = 6
+// CHECK: 6
x * x
-// CHECK: #0 int = 9
+// CHECK: 9
x = 4
x + x
-// CHECK: #0 int = 8
+// CHECK: 8
x := true
// CHECK: cannot assign {{.*}} to x (variable of type int)
@@ -19,11 +20,11 @@ x := true
x, y := func() (int, int) {
return 1, 2
}()
-// CHECK: x int = 1
-// CHECK: y int = 2
+// CHECK: 1
+// CHECK: 2
x, _ = func() (int, int) {
return 3, 4
}()
x
-// CHECK: #0 int = 3
+// CHECK: 3