Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
373e3204b4 | ||
|
|
5f41fcb8bc | ||
|
|
43b8594c82 | ||
|
|
4c877cc348 | ||
|
|
f199520048 | ||
|
|
4df03252f6 | ||
|
|
2dfede3b90 | ||
|
|
6191f5d38a | ||
|
|
24db786bb6 | ||
|
|
aa98e2c2a9 | ||
|
|
8ace2eb6e6 | ||
|
|
f2034819c2 | ||
|
|
172d4a4657 |
@@ -36,7 +36,7 @@ before_script:
|
|||||||
script:
|
script:
|
||||||
- make check
|
- make check
|
||||||
- go build -v ./...
|
- go build -v ./...
|
||||||
- GO111MODULE=off go test -v ./...
|
- make tests
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
- provider: script
|
- provider: script
|
||||||
|
|||||||
3
Makefile
3
Makefile
@@ -16,4 +16,7 @@ cmd/goexports/goexports: cmd/goexports/goexports.go
|
|||||||
generate: gen_all_syscall
|
generate: gen_all_syscall
|
||||||
go generate
|
go generate
|
||||||
|
|
||||||
|
tests:
|
||||||
|
GO111MODULE=off go test -v ./...
|
||||||
|
|
||||||
.PHONY: check gen_all_syscall gen_tests
|
.PHONY: check gen_all_syscall gen_tests
|
||||||
|
|||||||
76
README.md
76
README.md
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
[](https://github.com/containous/yaegi/releases)
|
[](https://github.com/containous/yaegi/releases)
|
||||||
[](https://travis-ci.com/containous/yaegi)
|
[](https://travis-ci.com/containous/yaegi)
|
||||||
|
[](https://godoc.org/github.com/containous/yaegi)
|
||||||
|
|
||||||
Yaegi is Another Elegant Go Interpreter.
|
Yaegi is Another Elegant Go Interpreter.
|
||||||
It powers executable Go scripts and plugins, in embedded interpreters or interactive shells, on top of the Go runtime.
|
It powers executable Go scripts and plugins, in embedded interpreters or interactive shells, on top of the Go runtime.
|
||||||
@@ -11,49 +12,32 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Complete support of [Go specification][specs]
|
* Complete support of [Go specification][specs]
|
||||||
* In pure Go, using only standard library
|
* Written in pure Go, using only the standard library
|
||||||
* Simple interpreter API: `New()`, `Eval()`, `Use()`
|
* Simple interpreter API: `New()`, `Eval()`, `Use()`
|
||||||
* works everywhere Go works
|
* Works everywhere Go works
|
||||||
* All Go & runtime resources accessible from script (with control)
|
* All Go & runtime resources accessible from script (with control)
|
||||||
* Security: `unsafe` and `syscall` packages not used or exported by default
|
* Security: `unsafe` and `syscall` packages neither used nor exported by default
|
||||||
|
* Support Go 1.11 and Go 1.12 (the latest 2 major releases)
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
### As library
|
### Go package
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/containous/yaegi/interp"
|
import "github.com/containous/yaegi/interp"
|
||||||
```
|
```
|
||||||
|
|
||||||
### REPL
|
### Command-line executable
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go get -u github.com/containous/yaegi/cmd
|
go get -u github.com/containous/yaegi/cmd/yaegi
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that you can use [rlwrap](https://github.com/hanslub42/rlwrap) (install with your favorite package manager),
|
||||||
|
and alias the `yaegi` command in `alias yaegi='rlwrap yaegi'` in your `~/.bashrc`, to have history and command line edition.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### As a command line interpreter
|
|
||||||
|
|
||||||
The Yaegi command can run an interactive Read-Eval-Print-Loop:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ yaegi
|
|
||||||
> 1 + 2
|
|
||||||
3
|
|
||||||
> import "fmt"
|
|
||||||
> fmt.Println("Hello World")
|
|
||||||
Hello World
|
|
||||||
>
|
|
||||||
```
|
|
||||||
|
|
||||||
Or interpret Go files:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ yaegi cmd/yaegi/yaegi.go
|
|
||||||
>
|
|
||||||
```
|
|
||||||
|
|
||||||
### As an embedded interpreter
|
### As an embedded interpreter
|
||||||
|
|
||||||
Create an interpreter with `New()`, run Go code with `Eval()`:
|
Create an interpreter with `New()`, run Go code with `Eval()`:
|
||||||
@@ -76,13 +60,15 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = i.Eval(`fmt.Println("hello")`)
|
_, err = i.Eval(`fmt.Println("Hello Yaegi")`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[Go Playground](https://play.golang.org/p/zzvw4VlerLP)
|
||||||
|
|
||||||
### As a dynamic extension framework
|
### As a dynamic extension framework
|
||||||
|
|
||||||
The following program is compiled ahead of time, except `bar()` which is interpreted, with the following steps:
|
The following program is compiled ahead of time, except `bar()` which is interpreted, with the following steps:
|
||||||
@@ -119,10 +105,43 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[Go Playground](https://play.golang.org/p/6SEAoaO7n0U)
|
||||||
|
|
||||||
|
### As a command-line interpreter
|
||||||
|
|
||||||
|
The Yaegi command can run an interactive Read-Eval-Print-Loop:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ yaegi
|
||||||
|
> 1 + 2
|
||||||
|
3
|
||||||
|
> import "fmt"
|
||||||
|
> fmt.Println("Hello World")
|
||||||
|
Hello World
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or interpret Go files:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ yaegi cmd/yaegi/yaegi.go
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs].
|
Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs].
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
Beside the known [bugs] which are supposed to be fixed in the short term, there are some limitations not planned to be addressed soon:
|
||||||
|
|
||||||
|
- assembly files (`.s`) are not supported
|
||||||
|
- calling C code is not supported (no virtual "C" package)
|
||||||
|
- interfaces to be used from the pre-compiled code can not be added dynamically, as it is required to pre-compile interface wrappers
|
||||||
|
- representation of types by `reflect` and printing values using %T may give different results between compiled mode and interpreted mode
|
||||||
|
- interpreting computation intensive code is likely to remain significantly slower than in compiled mode
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Yaegi is an open source project, and your feedback and contributions are needed and always welcome.
|
Yaegi is an open source project, and your feedback and contributions are needed and always welcome.
|
||||||
@@ -139,3 +158,4 @@ Yaegi is an open source project, and your feedback and contributions are needed
|
|||||||
[github]: https://github.com/containous/yaegi
|
[github]: https://github.com/containous/yaegi
|
||||||
[Issues]: https://github.com/containous/yaegi/issues
|
[Issues]: https://github.com/containous/yaegi/issues
|
||||||
[pull requests]: https://github.com/containous/yaegi/issues
|
[pull requests]: https://github.com/containous/yaegi/issues
|
||||||
|
[bugs]: https://github.com/containous/yaegi/issues?q=is%3Aissue+is%3Aopen+label%3Abug
|
||||||
|
|||||||
9
_test/assign8.go
Normal file
9
_test/assign8.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
_ = 1
|
||||||
|
println(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
18
_test/interface9.go
Normal file
18
_test/interface9.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Int int
|
||||||
|
|
||||||
|
func (I Int) String() string {
|
||||||
|
return "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var i Int
|
||||||
|
var st fmt.Stringer = i
|
||||||
|
fmt.Println(st.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// foo
|
||||||
@@ -1,8 +1,21 @@
|
|||||||
//go:generate go build
|
//go:generate go build
|
||||||
|
|
||||||
// This program generates code to register binary program symbols to the interpreter.
|
/*
|
||||||
// See stdlib.go for usage
|
Goexports generates wrappers of package exported symbols
|
||||||
|
|
||||||
|
Output files are written in the current directory, and prefixed with the go version.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
goexports package...
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
goexports github.com/containous/yaegi/interp
|
||||||
|
|
||||||
|
The same goexport program is used for all target operating systems and architectures.
|
||||||
|
The GOOS and GOARCH environment variables set the desired target.
|
||||||
|
*/
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -22,8 +35,8 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
const model = `// +build {{.CurrentGoVersion}},!{{.NextGoVersion}}
|
const model = `// +build {{.BuildTags}}
|
||||||
|
|
||||||
package {{.Dest}}
|
package {{.Dest}}
|
||||||
|
|
||||||
// Code generated by 'goexports {{.PkgName}}'. DO NOT EDIT.
|
// Code generated by 'goexports {{.PkgName}}'. DO NOT EDIT.
|
||||||
@@ -120,6 +133,7 @@ func genContent(dest, pkgName string) ([]byte, error) {
|
|||||||
if !o.Exported() {
|
if !o.Exported() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pname := path.Base(pkgName) + "." + name
|
pname := path.Base(pkgName) + "." + name
|
||||||
switch o := o.(type) {
|
switch o := o.(type) {
|
||||||
case *types.Const:
|
case *types.Const:
|
||||||
@@ -137,11 +151,12 @@ func genContent(dest, pkgName string) ([]byte, error) {
|
|||||||
if !f.Exported() {
|
if !f.Exported() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sig := f.Type().(*types.Signature)
|
|
||||||
args := make([]string, sig.Params().Len())
|
sign := f.Type().(*types.Signature)
|
||||||
|
args := make([]string, sign.Params().Len())
|
||||||
params := make([]string, len(args))
|
params := make([]string, len(args))
|
||||||
for j := range args {
|
for j := range args {
|
||||||
v := sig.Params().At(j)
|
v := sign.Params().At(j)
|
||||||
if args[j] = v.Name(); args[j] == "" {
|
if args[j] = v.Name(); args[j] == "" {
|
||||||
args[j] = fmt.Sprintf("a%d", j)
|
args[j] = fmt.Sprintf("a%d", j)
|
||||||
}
|
}
|
||||||
@@ -149,16 +164,19 @@ func genContent(dest, pkgName string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
arg := "(" + strings.Join(args, ", ") + ")"
|
arg := "(" + strings.Join(args, ", ") + ")"
|
||||||
param := "(" + strings.Join(params, ", ") + ")"
|
param := "(" + strings.Join(params, ", ") + ")"
|
||||||
results := make([]string, sig.Results().Len())
|
|
||||||
|
results := make([]string, sign.Results().Len())
|
||||||
for j := range results {
|
for j := range results {
|
||||||
v := sig.Results().At(j)
|
v := sign.Results().At(j)
|
||||||
results[j] = v.Name() + " " + types.TypeString(v.Type(), qualify)
|
results[j] = v.Name() + " " + types.TypeString(v.Type(), qualify)
|
||||||
}
|
}
|
||||||
result := "(" + strings.Join(results, ", ") + ")"
|
result := "(" + strings.Join(results, ", ") + ")"
|
||||||
|
|
||||||
ret := ""
|
ret := ""
|
||||||
if sig.Results().Len() > 0 {
|
if sign.Results().Len() > 0 {
|
||||||
ret = "return"
|
ret = "return"
|
||||||
}
|
}
|
||||||
|
|
||||||
methods = append(methods, Method{f.Name(), param, result, arg, ret})
|
methods = append(methods, Method{f.Name(), param, result, arg, ret})
|
||||||
}
|
}
|
||||||
wrap[name] = Wrap{prefix + name, methods}
|
wrap[name] = Wrap{prefix + name, methods}
|
||||||
@@ -181,16 +199,20 @@ func genContent(dest, pkgName string) ([]byte, error) {
|
|||||||
return nil, fmt.Errorf("template parsing error: %v", err)
|
return nil, fmt.Errorf("template parsing error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildTags := currentGoVersion + ",!" + nextGoVersion
|
||||||
|
if pkgName == "log/syslog" {
|
||||||
|
buildTags += ",!windows,!nacl,!plan9"
|
||||||
|
}
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Dest": dest,
|
"Dest": dest,
|
||||||
"Imports": imports,
|
"Imports": imports,
|
||||||
"PkgName": pkgName,
|
"PkgName": pkgName,
|
||||||
"Val": val,
|
"Val": val,
|
||||||
"Typ": typ,
|
"Typ": typ,
|
||||||
"Wrap": wrap,
|
"Wrap": wrap,
|
||||||
"CurrentGoVersion": currentGoVersion,
|
"BuildTags": buildTags,
|
||||||
"NextGoVersion": nextGoVersion,
|
|
||||||
}
|
}
|
||||||
err = parse.Execute(b, data)
|
err = parse.Execute(b, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
|
|||||||
dest, src := n.child[i], n.child[sbase+i]
|
dest, src := n.child[i], n.child[sbase+i]
|
||||||
var sym *symbol
|
var sym *symbol
|
||||||
var level int
|
var level int
|
||||||
if n.kind == defineStmt {
|
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
|
||||||
if src.typ != nil && src.typ.cat == nilT {
|
if src.typ != nil && src.typ.cat == nilT {
|
||||||
err = src.cfgErrorf("use of untyped nil")
|
err = src.cfgErrorf("use of untyped nil")
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ type Options struct {
|
|||||||
// New returns a new interpreter
|
// New returns a new interpreter
|
||||||
func New(options Options) *Interpreter {
|
func New(options Options) *Interpreter {
|
||||||
i := Interpreter{
|
i := Interpreter{
|
||||||
opt: opt{goPath: getGoPath(options)},
|
opt: opt{goPath: options.GoPath},
|
||||||
fset: token.NewFileSet(),
|
fset: token.NewFileSet(),
|
||||||
universe: initUniverse(),
|
universe: initUniverse(),
|
||||||
scopes: map[string]*scope{},
|
scopes: map[string]*scope{},
|
||||||
@@ -143,18 +143,6 @@ func New(options Options) *Interpreter {
|
|||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGoPath(options Options) string {
|
|
||||||
if options.GoPath != "" {
|
|
||||||
return options.GoPath
|
|
||||||
}
|
|
||||||
|
|
||||||
goPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return goPath
|
|
||||||
}
|
|
||||||
|
|
||||||
func initUniverse() *scope {
|
func initUniverse() *scope {
|
||||||
sc := &scope{global: true, sym: map[string]*symbol{
|
sc := &scope{global: true, sym: map[string]*symbol{
|
||||||
// predefined Go types
|
// predefined Go types
|
||||||
|
|||||||
@@ -223,6 +223,8 @@ func assign(n *node) {
|
|||||||
switch {
|
switch {
|
||||||
case dest.typ.cat == interfaceT:
|
case dest.typ.cat == interfaceT:
|
||||||
svalue[i] = genValueInterface(src)
|
svalue[i] = genValueInterface(src)
|
||||||
|
case dest.typ.cat == valueT && dest.typ.rtype.Kind() == reflect.Interface:
|
||||||
|
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
|
||||||
case dest.typ.cat == valueT && src.typ.cat == funcT:
|
case dest.typ.cat == valueT && src.typ.cat == funcT:
|
||||||
svalue[i] = genFunctionWrapper(src)
|
svalue[i] = genFunctionWrapper(src)
|
||||||
case src.kind == basicLit && src.val == nil:
|
case src.kind == basicLit && src.val == nil:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build go1.11,!go1.12
|
// +build go1.11,!go1.12,!windows,!nacl,!plan9
|
||||||
|
|
||||||
package stdlib
|
package stdlib
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build go1.12,!go1.13
|
// +build go1.12,!go1.13,!windows,!nacl,!plan9
|
||||||
|
|
||||||
package stdlib
|
package stdlib
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user