From 153fe0de740ea8639fae374f6ed296bbe66a511b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 13 Oct 2021 06:06:18 +0800 Subject: [PATCH] igop yaegi --- all.bash | 2 +- cmd/igop/internal/lib/default_yaegi.go | 28 +++++++ cmd/igop/internal/run/run.go | 71 +++++++++++++++++ cmd/igop/internal/run/run_gomacro.go | 48 +----------- cmd/igop/internal/run/run_yaegi.go | 43 +++++++++++ cmd/igop/main.go | 2 - go.mod | 5 ++ go.sum | 2 + igop_yaegi.go | 101 +++++++++++++++++++++++++ igop_yaegi_ast.go | 80 ++++++++++++++++++++ 10 files changed, 335 insertions(+), 47 deletions(-) create mode 100644 cmd/igop/internal/lib/default_yaegi.go create mode 100644 cmd/igop/internal/run/run.go create mode 100644 cmd/igop/internal/run/run_yaegi.go create mode 100644 igop_yaegi.go create mode 100644 igop_yaegi_ast.go diff --git a/all.bash b/all.bash index 7f7d920..544ee6b 100755 --- a/all.bash +++ b/all.bash @@ -1,3 +1,3 @@ set -e -go install -tags gomacro -v ./... +go install -tags yaegi -v ./... diff --git a/cmd/igop/internal/lib/default_yaegi.go b/cmd/igop/internal/lib/default_yaegi.go new file mode 100644 index 0000000..d5dcabc --- /dev/null +++ b/cmd/igop/internal/lib/default_yaegi.go @@ -0,0 +1,28 @@ +//go:build yaegi +// +build yaegi + +/* + Copyright 2021 The GoPlus Authors (goplus.org) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package lib + +import ( + "reflect" +) + +var ( + Symbols = map[string]map[string]reflect.Value{} +) diff --git a/cmd/igop/internal/run/run.go b/cmd/igop/internal/run/run.go new file mode 100644 index 0000000..811cf8f --- /dev/null +++ b/cmd/igop/internal/run/run.go @@ -0,0 +1,71 @@ +/* + Copyright 2021 The GoPlus Authors (goplus.org) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package run implements the ``igop run'' command. +package run + +import ( + "fmt" + "os" + "runtime" + + "github.com/goplus/gop" + "github.com/goplus/igop/cmd/igop/internal/base" + "github.com/qiniu/x/log" +) + +// ----------------------------------------------------------------------------- + +// Cmd - igop run +var Cmd = &base.Command{ + UsageLine: "igop run [-version -quiet -debug] ", + Short: "Run a Go+ program", +} + +var ( + flag = &Cmd.Flag + flagAsm = flag.Bool("asm", false, "generates `asm` code of Go bytecode backend") + flagQuiet = flag.Bool("quiet", false, "don't generate any compiling stage log") + flagDebug = flag.Bool("debug", false, "print debug information") + flagVersion = flag.Bool("version", false, "print gop version") +) + +func init() { + Cmd.Run = runCmd +} + +func runCmd(cmd *base.Command, args []string) { + flag.Parse(args) + if *flagVersion { + fmt.Println("gop", gop.Version(), runtime.GOOS+"/"+runtime.GOARCH) + return + } + if flag.NArg() < 1 { + cmd.Usage(os.Stderr) + } + + log.SetFlags(log.Ldefault &^ log.LstdFlags) + if *flagQuiet { + log.SetOutputLevel(0x7000) + } else if *flagDebug { + log.SetOutputLevel(log.Ldebug) + } + + srcDir := flag.Arg(0) + runDir(srcDir, *flagAsm) +} + +// ----------------------------------------------------------------------------- diff --git a/cmd/igop/internal/run/run_gomacro.go b/cmd/igop/internal/run/run_gomacro.go index 12a4b6d..2f3c5e5 100644 --- a/cmd/igop/internal/run/run_gomacro.go +++ b/cmd/igop/internal/run/run_gomacro.go @@ -17,65 +17,25 @@ limitations under the License. */ -// Package run implements the ``igop run'' command. package run import ( - "fmt" - "os" - "runtime" - "github.com/cosmos72/gomacro/fast" - "github.com/goplus/gop" "github.com/goplus/igop" - "github.com/goplus/igop/cmd/igop/internal/base" "github.com/qiniu/x/log" -) - -// ----------------------------------------------------------------------------- -// Cmd - igop run -var Cmd = &base.Command{ - UsageLine: "igop run [-version -quiet -debug] ", - Short: "Run a program powered by Go+ spx engine", -} - -var ( - flag = &Cmd.Flag - flagAsm = flag.Bool("asm", false, "generates `asm` code of Go bytecode backend") - flagQuiet = flag.Bool("quiet", false, "don't generate any compiling stage log") - flagDebug = flag.Bool("debug", false, "print debug information") - flagVersion = flag.Bool("version", false, "print gop version") + _ "github.com/goplus/igop/cmd/igop/internal/lib" ) -func init() { - Cmd.Run = runCmd -} - -func runCmd(cmd *base.Command, args []string) { - flag.Parse(args) - if *flagVersion { - fmt.Println("gop", gop.Version(), runtime.GOOS+"/"+runtime.GOARCH) - return - } - if flag.NArg() < 1 { - cmd.Usage(os.Stderr) - } - - log.SetFlags(log.Ldefault &^ log.LstdFlags) - if *flagQuiet { - log.SetOutputLevel(0x7000) - } else if *flagDebug { - log.SetOutputLevel(log.Ldebug) - } +// ----------------------------------------------------------------------------- - srcDir := flag.Arg(0) +func runDir(srcDir string, asm bool) { it := fast.New() app, err := igop.CompileDir(it, srcDir) if err != nil { log.Fatalln(err) } - if *flagAsm { + if asm { panic("not impl") } it.RunExpr(app) diff --git a/cmd/igop/internal/run/run_yaegi.go b/cmd/igop/internal/run/run_yaegi.go new file mode 100644 index 0000000..1f76447 --- /dev/null +++ b/cmd/igop/internal/run/run_yaegi.go @@ -0,0 +1,43 @@ +//go:build yaegi +// +build yaegi + +/* + Copyright 2020 The GoPlus Authors (goplus.org) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package run + +import ( + "github.com/goplus/igop" + "github.com/goplus/igop/cmd/igop/internal/lib" + "github.com/qiniu/x/log" + "github.com/traefik/yaegi/interp" + "github.com/traefik/yaegi/stdlib" +) + +// ----------------------------------------------------------------------------- + +func runDir(srcDir string, asm bool) { + it := interp.New(interp.Options{}) + it.Use(stdlib.Symbols) + it.Use(lib.Symbols) + app, err := igop.CompileDir(it, srcDir) + if err != nil { + log.Fatalln(err) + } + if asm { + panic("not impl") + } + it.Execute(app) +} + +// ----------------------------------------------------------------------------- diff --git a/cmd/igop/main.go b/cmd/igop/main.go index 4ae7c31..82fdf68 100644 --- a/cmd/igop/main.go +++ b/cmd/igop/main.go @@ -21,8 +21,6 @@ import ( "github.com/goplus/igop/cmd/igop/internal/base" "github.com/goplus/igop/cmd/igop/internal/run" - - _ "github.com/goplus/igop/cmd/igop/internal/lib" ) func main() { diff --git a/go.mod b/go.mod index f7b5b42..45a9dd5 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,9 @@ require ( github.com/goplus/gop v1.0.14 github.com/goplus/gox v1.7.10 github.com/qiniu/x v1.11.5 + github.com/traefik/yaegi v0.10.0 ) + +replace ( + github.com/traefik/yaegi => /Users/xushiwei/work/yaegi +) \ No newline at end of file diff --git a/go.sum b/go.sum index e663631..3534089 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/qiniu/x v1.11.5 h1:TYr5cl4g2yoHAZeDK4MTjKF6CMoG+IHlCDvvM5qym6U= github.com/qiniu/x v1.11.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/traefik/yaegi v0.10.0 h1:c/0rhUcj5+KJhJX++eCrPeKXnJaOZ17X8gYCznU9Xxc= +github.com/traefik/yaegi v0.10.0/go.mod h1:RuCwD8/wsX7b6KoQHOaIFUfuH3gQIK4KWnFFmJMw5VA= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/igop_yaegi.go b/igop_yaegi.go new file mode 100644 index 0000000..4a8d465 --- /dev/null +++ b/igop_yaegi.go @@ -0,0 +1,101 @@ +//go:build yaegi +// +build yaegi + +/* + Copyright 2021 The GoPlus Authors (goplus.org) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package igop + +import ( + "bytes" + "errors" + "go/token" + "log" + "os" + "path/filepath" + + "github.com/goplus/gop/cl" + "github.com/goplus/gop/parser" + "github.com/goplus/gox" + "github.com/traefik/yaegi/interp" +) + +// ----------------------------------------------------------------------------- + +func CompileDir(it *interp.Interpreter, srcDir string) (app *interp.Program, err error) { + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, srcDir, nil, 0) + if err != nil { + return + } + mainPkg, ok := pkgs["main"] + if !ok { + return nil, errors.New("not a main package") + } + + modDir, noCacheFile := findGoModDir(srcDir) + conf := &cl.Config{ + Dir: modDir, TargetDir: srcDir, Fset: fset, CacheLoadPkgs: true, PersistLoadPkgs: !noCacheFile} + out, err := cl.NewPackage("", mainPkg, conf) + if err != nil { + return + } + conf.PkgsLoader.Save() + var b bytes.Buffer + err = gox.WriteTo(&b, out, false) + if err != nil { + return + } + return it.Compile(b.String()) +} + +/* +// astFileToPkg translate ast.File to ast.Package +func astFileToPkg(file *ast.File, fileName string) (pkg *ast.Package) { + pkg = &ast.Package{ + Name: file.Name.Name, + Files: make(map[string]*ast.File), + } + pkg.Files[fileName] = file + return +} +*/ + +func findGoModFile(dir string) (modfile string, noCacheFile bool, err error) { + modfile, err = cl.FindGoModFile(dir) + if err != nil { + home := os.Getenv("HOME") + modfile = home + "/gop/go.mod" + if fi, e := os.Lstat(modfile); e == nil && !fi.IsDir() { + return modfile, true, nil + } + modfile = home + "/goplus/go.mod" + if fi, e := os.Lstat(modfile); e == nil && !fi.IsDir() { + return modfile, true, nil + } + } + return +} + +func findGoModDir(dir string) (string, bool) { + modfile, nocachefile, err := findGoModFile(dir) + if err != nil { + log.Fatalln("findGoModFile:", err) + } + return filepath.Dir(modfile), nocachefile +} + +// ----------------------------------------------------------------------------- diff --git a/igop_yaegi_ast.go b/igop_yaegi_ast.go new file mode 100644 index 0000000..1bd0753 --- /dev/null +++ b/igop_yaegi_ast.go @@ -0,0 +1,80 @@ +//go:build yaegiast +// +build yaegiast + +package igop + +import ( + "errors" + "go/token" + "log" + "os" + "path/filepath" + + "github.com/goplus/gop/cl" + "github.com/goplus/gop/parser" + "github.com/goplus/gox" + "github.com/traefik/yaegi/interp" +) + +// ----------------------------------------------------------------------------- + +func CompileDir(it *interp.Interpreter, srcDir string) (app *interp.Program, err error) { + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, srcDir, nil, 0) + if err != nil { + return + } + mainPkg, ok := pkgs["main"] + if !ok { + return nil, errors.New("not a main package") + } + + modDir, noCacheFile := findGoModDir(srcDir) + conf := &cl.Config{ + Dir: modDir, TargetDir: srcDir, Fset: fset, CacheLoadPkgs: true, PersistLoadPkgs: !noCacheFile} + out, err := cl.NewPackage("", mainPkg, conf) + if err != nil { + return + } + conf.PkgsLoader.Save() + file := gox.ASTFile(out, false) + return it.CompileAST(file) +} + +/* +// astFileToPkg translate ast.File to ast.Package +func astFileToPkg(file *ast.File, fileName string) (pkg *ast.Package) { + pkg = &ast.Package{ + Name: file.Name.Name, + Files: make(map[string]*ast.File), + } + pkg.Files[fileName] = file + return +} +*/ + +func findGoModFile(dir string) (modfile string, noCacheFile bool, err error) { + modfile, err = cl.FindGoModFile(dir) + if err != nil { + home := os.Getenv("HOME") + modfile = home + "/gop/go.mod" + if fi, e := os.Lstat(modfile); e == nil && !fi.IsDir() { + return modfile, true, nil + } + modfile = home + "/goplus/go.mod" + if fi, e := os.Lstat(modfile); e == nil && !fi.IsDir() { + return modfile, true, nil + } + } + return +} + +func findGoModDir(dir string) (string, bool) { + modfile, nocachefile, err := findGoModFile(dir) + if err != nil { + log.Fatalln("findGoModFile:", err) + } + return filepath.Dir(modfile), nocachefile +} + +// -----------------------------------------------------------------------------