Compare commits

...

13 Commits

Author SHA1 Message Date
Ludovic Fernandez
373e3204b4 chore: revert stdlib improvements 2019-07-26 22:36:03 -07:00
Marc Vertes
5f41fcb8bc doc: add a Limitations section in README 2019-07-26 20:56:03 -07:00
Ludovic Fernandez
43b8594c82 feat: improve stdlib generation. 2019-07-26 17:10:04 -07:00
Marc Vertes
4c877cc348 fix: correct assign to binary interface value (#291) 2019-07-26 10:59:16 -07:00
Marc Vertes
f199520048 doc: first attempt to document goexports command 2019-07-25 23:02:03 -07:00
Taufiq Rahman
4df03252f6 doc: Fixes the title and other formattings 2019-07-25 12:06:03 -07:00
Ludovic Fernandez
2dfede3b90 doc: add playground examples. 2019-07-25 09:08:03 -07:00
Fernandez Ludovic
6191f5d38a chore: add godoc badge. 2019-07-24 22:01:51 -07:00
Ludovic Fernandez
24db786bb6 fix: exclude syslog for windows, nacl, and plan9. 2019-07-24 18:58:04 -07:00
Ludovic Fernandez
aa98e2c2a9 feat: support yaegi in go playground 2019-07-24 17:52:03 -07:00
Ludovic Fernandez
8ace2eb6e6 doc: improve documentation. 2019-07-24 11:44:04 -07:00
Cuong Manh Le
f2034819c2 fix: blank identifier cause panic (#255) 2019-07-24 11:20:04 -07:00
Brendan Le Glaunec
172d4a4657 Fix installation instructions (#250) 2019-07-24 09:11:07 -07:00
11 changed files with 125 additions and 63 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -4,6 +4,7 @@
[![release](https://img.shields.io/github/tag-date/containous/yaegi.svg?label=alpha)](https://github.com/containous/yaegi/releases) [![release](https://img.shields.io/github/tag-date/containous/yaegi.svg?label=alpha)](https://github.com/containous/yaegi/releases)
[![Build Status](https://travis-ci.com/containous/yaegi.svg?branch=master)](https://travis-ci.com/containous/yaegi) [![Build Status](https://travis-ci.com/containous/yaegi.svg?branch=master)](https://travis-ci.com/containous/yaegi)
[![GoDoc](https://godoc.org/github.com/containous/yaegi?status.svg)](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
View File

@@ -0,0 +1,9 @@
package main
func main() {
_ = 1
println(1)
}
// Output:
// 1

18
_test/interface9.go Normal file
View 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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -1,4 +1,4 @@
// +build go1.11,!go1.12 // +build go1.11,!go1.12,!windows,!nacl,!plan9
package stdlib package stdlib

View File

@@ -1,4 +1,4 @@
// +build go1.12,!go1.13 // +build go1.12,!go1.13,!windows,!nacl,!plan9
package stdlib package stdlib