-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
32 changed files
with
1,351 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Launch Package", | ||
"type": "go", | ||
"request": "launch", | ||
"mode": "auto", | ||
"program": "${fileDirname}", | ||
"console": "integratedTerminal" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
package interpreter | ||
|
||
import ( | ||
"bufio" | ||
"errors" | ||
"fmt" | ||
"go/build" | ||
"os" | ||
"os/exec" | ||
"reflect" | ||
"runtime" | ||
"strings" | ||
|
||
"github.com/traefik/yaegi/interp" | ||
|
||
"github.com/elgopher/pi/devtools/internal/interpreter/lib" | ||
"github.com/elgopher/pi/devtools/internal/snapshot" | ||
"github.com/elgopher/pi/devtools/internal/terminal" | ||
) | ||
|
||
func ExportFunc(name string, f any) { | ||
err := interpreter.Use(interp.Exports{ | ||
"main/main": map[string]reflect.Value{ | ||
name: reflect.ValueOf(f), | ||
}, | ||
}) | ||
if err != nil { | ||
panic(fmt.Sprintf("problem exporting function %s: %s", name, err)) | ||
} | ||
|
||
_, err = interpreter.Eval(`import . "main"`) // TODO Redeclaration? | ||
if err != nil { | ||
panic(fmt.Sprintf("problem exporting function %s: %s", name, err)) | ||
} | ||
} | ||
|
||
func ExportVar(name string, v any) { | ||
value := reflect.ValueOf(v) | ||
if value.Kind() != reflect.Ptr { | ||
panic(fmt.Sprintf("ExportVar: you must pass pointer to variable '%s', but you passed %T. Please add '&' in front.", name, v)) | ||
} | ||
err := interpreter.Use(interp.Exports{ | ||
"main/main": map[string]reflect.Value{ | ||
name: value.Elem(), | ||
}, | ||
}) | ||
if err != nil { | ||
panic(fmt.Sprintf("problem exporting variable %s: %s", name, err)) | ||
} | ||
|
||
_, err = interpreter.Eval(`import . "main"`) | ||
if err != nil { | ||
panic(fmt.Sprintf("problem exporting variable %s: %s", name, err)) | ||
} | ||
} | ||
|
||
// TODO Better read name from T | ||
func ExportType[T any](name string) { | ||
var nilValue *T | ||
err := interpreter.Use(interp.Exports{ | ||
"main/main": map[string]reflect.Value{ | ||
name: reflect.ValueOf(nilValue), | ||
}, | ||
}) | ||
if err != nil { | ||
panic(fmt.Sprintf("problem exporting type %s: %s", name, err)) | ||
} | ||
|
||
_, err = interpreter.Eval(`import . "main"`) | ||
if err != nil { | ||
panic(fmt.Sprintf("problem exporting type %s: %s", name, err)) | ||
} | ||
} | ||
|
||
var interpreter *interp.Interpreter | ||
|
||
func init() { | ||
interpreter = interp.New(interp.Options{ | ||
GoPath: gopath(), // if GoPath is set then Yaegi does not complain about setting GOPATH. | ||
}) | ||
err := interpreter.Use(lib.Symbols) | ||
if err != nil { | ||
_, _ = os.Stderr.WriteString(fmt.Sprintf("problem using Go interpreter symbols: %s", err)) | ||
} | ||
|
||
_, err = interpreter.Eval(` | ||
import ( | ||
"bytes" | ||
"fmt" | ||
"math" | ||
"math/rand" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
"github.com/elgopher/pi" | ||
"github.com/elgopher/pi/font" | ||
"github.com/elgopher/pi/image" | ||
"github.com/elgopher/pi/key" | ||
"github.com/elgopher/pi/snap" | ||
"github.com/elgopher/pi/state" | ||
) | ||
`) | ||
if err != nil { | ||
_, _ = os.Stderr.WriteString(fmt.Sprintf("importing interpreter packages failed: %s", err)) | ||
} | ||
ExportType[noResult]("__noResult") | ||
} | ||
|
||
func gopath() string { | ||
gopath := os.Getenv("GOPATH") | ||
if gopath == "" { | ||
gopath = build.Default.GOPATH | ||
} | ||
return gopath | ||
} | ||
|
||
func EvaluateNextCommand() { | ||
select { | ||
case cmd := <-terminal.Commands: | ||
defer func() { | ||
terminal.CommandExecuted <- struct{}{} | ||
}() | ||
defer func() { | ||
err := recover() | ||
if err != nil { | ||
fmt.Println("panic when running Yaegi", err) | ||
} | ||
}() | ||
if helpCmd := strings.Trim(cmd, " "); strings.HasPrefix(helpCmd, "help ") || helpCmd == "help" { | ||
printHelp(strings.Trim(strings.TrimLeft(helpCmd, "help"), " ")) | ||
} else { | ||
runGoCode(cmd) | ||
} | ||
default: | ||
return | ||
} | ||
} | ||
|
||
func printHelp(topic string) { | ||
switch topic { | ||
case "": | ||
println("This is interactive terminal. " + | ||
"You can write Go code here, which will run immediately in the paused game. " + | ||
"You can use all Pi packages: pi, font, image, key, snap, state and " + | ||
"selection of standard packages: bytes, fmt, math, rand, sort, strconv and strings. " + | ||
"\n\n" + | ||
"Type help topic for more information. For example: help pi or help pi.Spr") | ||
default: | ||
if goDoc(topic) { | ||
return | ||
} | ||
} | ||
} | ||
|
||
func goDoc(symbol string) bool { | ||
fmt.Println("###############################################################################") | ||
|
||
if strings.HasPrefix(symbol, "pi.") || symbol == "pi" { | ||
symbol = "github.com/elgopher/" + symbol | ||
} | ||
|
||
if strings.HasPrefix(symbol, "font.") || symbol == "font" || | ||
strings.HasPrefix(symbol, "image.") || symbol == "image" || | ||
strings.HasPrefix(symbol, "key.") || symbol == "key" || | ||
strings.HasPrefix(symbol, "snap.") || symbol == "snap" || | ||
strings.HasPrefix(symbol, "state.") || symbol == "state" { | ||
symbol = "github.com/elgopher/pi/" + symbol | ||
} | ||
|
||
var args []string | ||
args = append(args, "doc") | ||
args = append(args, symbol) | ||
command := exec.Command("go", args...) | ||
command.Stdout = bufio.NewWriter(os.Stdout) | ||
if err := command.Run(); err != nil { | ||
var exitErr *exec.ExitError | ||
if isExitErr := errors.As(err, &exitErr); isExitErr && exitErr.ExitCode() == 1 { | ||
fmt.Println("no help found") | ||
return true | ||
} | ||
fmt.Println("problem getting help:", err) | ||
} | ||
return false | ||
} | ||
|
||
func runGoCode(source string) { | ||
// We don't know if the source is a statement or an expression. Therefore, we override last result computed by the | ||
// interpreter to noResult{}. When source is a statement, then Yaegi returns last result computed by the interpreter, | ||
// which is noResult{}. When source is an expression, then the result of interpreted expression is returned. | ||
_, err := interpreter.Eval("__noResult{}") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
res, err := interpreter.Eval(source) | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
snapshot.Take() | ||
|
||
if res.IsValid() { | ||
if res.Type().Kind() != reflect.Func { | ||
kind := res.Type().Kind() | ||
if kind == reflect.Struct { | ||
if _, noResultFound := res.Interface().(noResult); !noResultFound { | ||
structName := res.Type().PkgPath() + "." + res.Type().Name() | ||
structName = structName[strings.LastIndex(structName, "/")+1:] | ||
fmt.Printf("%s: %+v\n", structName, res) | ||
} | ||
} else { | ||
fmt.Printf("%s: %+v\n", kind, res) | ||
} | ||
} else { | ||
name := runtime.FuncForPC(res.Pointer()).Name() | ||
if name != "" { | ||
printHelp(name) | ||
} | ||
} | ||
} | ||
} | ||
|
||
// noResult is a special struct which is used to check if code run by the interpreter returned something | ||
type noResult struct{} |
15 changes: 15 additions & 0 deletions
15
devtools/internal/interpreter/lib/github_com-elgopher-pi-font.go
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
19 changes: 19 additions & 0 deletions
19
devtools/internal/interpreter/lib/github_com-elgopher-pi-image.go
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.