From 85f608e8f0a0eb10c049bb52a165e546c066dd4f Mon Sep 17 00:00:00 2001 From: Jacek Olszak Date: Sat, 5 Aug 2023 12:27:13 +0200 Subject: [PATCH] Add scripting --- .github/workflows/build.yml | 2 +- README.md | 2 +- devtools/control.go | 6 + devtools/internal/inspector/measure.go | 2 +- devtools/internal/interpreter/interpreter.go | 186 ++++++++++++++++++ .../lib/github_com-elgopher-pi-font.go | 15 ++ .../lib/github_com-elgopher-pi-image.go | 19 ++ .../lib/github_com-elgopher-pi-key.go | 92 +++++++++ .../lib/github_com-elgopher-pi-snap.go | 15 ++ .../lib/github_com-elgopher-pi-state.go | 23 +++ .../interpreter/lib/github_com-elgopher-pi.go | 116 +++++++++++ .../internal/interpreter/lib/go1_20_bytes.go | 76 +++++++ .../internal/interpreter/lib/go1_20_fmt.go | 148 ++++++++++++++ .../internal/interpreter/lib/go1_20_math.go | 113 +++++++++++ .../interpreter/lib/go1_20_math_rand.go | 75 +++++++ .../internal/interpreter/lib/go1_20_sort.go | 59 ++++++ .../interpreter/lib/go1_20_strconv.go | 56 ++++++ .../interpreter/lib/go1_20_strings.go | 70 +++++++ devtools/internal/interpreter/lib/lib.go | 5 + devtools/internal/terminal/terminal.go | 40 ++++ devtools/scripting.go | 25 +++ devtools/update.go | 2 + docs/ROADMAP.md | 1 + examples/hello/main.go | 22 +++ examples/shapes/main.go | 1 - examples/yaegi/yaegi.go | 108 ++++++++++ go.mod | 5 +- go.sum | 8 + pixmap.go | 6 + 29 files changed, 1293 insertions(+), 5 deletions(-) create mode 100644 devtools/internal/interpreter/interpreter.go create mode 100644 devtools/internal/interpreter/lib/github_com-elgopher-pi-font.go create mode 100644 devtools/internal/interpreter/lib/github_com-elgopher-pi-image.go create mode 100644 devtools/internal/interpreter/lib/github_com-elgopher-pi-key.go create mode 100644 devtools/internal/interpreter/lib/github_com-elgopher-pi-snap.go create mode 100644 devtools/internal/interpreter/lib/github_com-elgopher-pi-state.go create mode 100644 devtools/internal/interpreter/lib/github_com-elgopher-pi.go create mode 100644 devtools/internal/interpreter/lib/go1_20_bytes.go create mode 100644 devtools/internal/interpreter/lib/go1_20_fmt.go create mode 100644 devtools/internal/interpreter/lib/go1_20_math.go create mode 100644 devtools/internal/interpreter/lib/go1_20_math_rand.go create mode 100644 devtools/internal/interpreter/lib/go1_20_sort.go create mode 100644 devtools/internal/interpreter/lib/go1_20_strconv.go create mode 100644 devtools/internal/interpreter/lib/go1_20_strings.go create mode 100644 devtools/internal/interpreter/lib/lib.go create mode 100644 devtools/internal/terminal/terminal.go create mode 100644 devtools/scripting.go create mode 100644 examples/yaegi/yaegi.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca019ff..d9aaa9d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - go: [ 1.18 ] + go: [ 1.20 ] env: DISPLAY: ':99.0' steps: diff --git a/README.md b/README.md index 87d1643..ce6761e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Pi is under development. Only limited functionality is provided. API is not stab ## How to get started? 1. Install dependencies - * [Go 1.18+](https://go.dev/dl/) + * [Go 1.20+](https://go.dev/dl/) * If not on Windows, please install additional dependencies for [Linux](docs/install-linux.md) or [macOS](docs/install-macos.md). 2. Try examples from [examples](examples) directory. 3. Create a new game using provided [Github template](https://github.com/elgopher/pi-template). diff --git a/devtools/control.go b/devtools/control.go index d14f48a..b7342f6 100644 --- a/devtools/control.go +++ b/devtools/control.go @@ -4,8 +4,11 @@ package devtools import ( + "fmt" + "github.com/elgopher/pi" "github.com/elgopher/pi/devtools/internal/snapshot" + "github.com/elgopher/pi/devtools/internal/terminal" ) var ( @@ -17,10 +20,13 @@ func pauseGame() { gamePaused = true timeWhenPaused = pi.TimeSeconds snapshot.Take() + terminal.StartReadingCommands() } func resumeGame() { gamePaused = false pi.TimeSeconds = timeWhenPaused snapshot.Draw() + fmt.Println("Game resumed") + terminal.StopReadingCommandsFromStdin() } diff --git a/devtools/internal/inspector/measure.go b/devtools/internal/inspector/measure.go index 7820464..05db5ea 100644 --- a/devtools/internal/inspector/measure.go +++ b/devtools/internal/inspector/measure.go @@ -94,7 +94,7 @@ func (m *Measure) Update() { case pi.MouseBtnp(pi.MouseLeft) && !distance.measuring: distance.measuring = true distance.startX, distance.startY = x, y - fmt.Printf("Measuring started at (%d, %d)\n", x, y) + fmt.Printf("\nMeasuring started at (%d, %d)\n", x, y) case !pi.MouseBtn(pi.MouseLeft) && distance.measuring: distance.measuring = false dist, width, height := calcDistance() diff --git a/devtools/internal/interpreter/interpreter.go b/devtools/internal/interpreter/interpreter.go new file mode 100644 index 0000000..633abd0 --- /dev/null +++ b/devtools/internal/interpreter/interpreter.go @@ -0,0 +1,186 @@ +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)) + } +} + +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("Here you can write Go code. It will be run immediately in the running game.") + default: + if goDoc(topic) { + return + } + } +} + +func goDoc(symbol string) bool { + command := exec.Command("go", "doc", "-all", symbol) + 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) { + 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 { + 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() + printHelp(name) + } + } +} diff --git a/devtools/internal/interpreter/lib/github_com-elgopher-pi-font.go b/devtools/internal/interpreter/lib/github_com-elgopher-pi-font.go new file mode 100644 index 0000000..ecc4826 --- /dev/null +++ b/devtools/internal/interpreter/lib/github_com-elgopher-pi-font.go @@ -0,0 +1,15 @@ +// Code generated by 'yaegi extract github.com/elgopher/pi/font'. DO NOT EDIT. + +package lib + +import ( + "github.com/elgopher/pi/font" + "reflect" +) + +func init() { + Symbols["github.com/elgopher/pi/font/font"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Load": reflect.ValueOf(font.Load), + } +} diff --git a/devtools/internal/interpreter/lib/github_com-elgopher-pi-image.go b/devtools/internal/interpreter/lib/github_com-elgopher-pi-image.go new file mode 100644 index 0000000..2a734ea --- /dev/null +++ b/devtools/internal/interpreter/lib/github_com-elgopher-pi-image.go @@ -0,0 +1,19 @@ +// Code generated by 'yaegi extract github.com/elgopher/pi/image'. DO NOT EDIT. + +package lib + +import ( + "github.com/elgopher/pi/image" + "reflect" +) + +func init() { + Symbols["github.com/elgopher/pi/image/image"] = map[string]reflect.Value{ + // function, constant and variable definitions + "DecodePNG": reflect.ValueOf(image.DecodePNG), + + // type definitions + "Image": reflect.ValueOf((*image.Image)(nil)), + "RGB": reflect.ValueOf((*image.RGB)(nil)), + } +} diff --git a/devtools/internal/interpreter/lib/github_com-elgopher-pi-key.go b/devtools/internal/interpreter/lib/github_com-elgopher-pi-key.go new file mode 100644 index 0000000..b2bbe21 --- /dev/null +++ b/devtools/internal/interpreter/lib/github_com-elgopher-pi-key.go @@ -0,0 +1,92 @@ +// Code generated by 'yaegi extract github.com/elgopher/pi/key'. DO NOT EDIT. + +package lib + +import ( + "github.com/elgopher/pi/key" + "reflect" +) + +func init() { + Symbols["github.com/elgopher/pi/key/key"] = map[string]reflect.Value{ + // function, constant and variable definitions + "A": reflect.ValueOf(key.A), + "Alt": reflect.ValueOf(key.Alt), + "Apostrophe": reflect.ValueOf(key.Apostrophe), + "B": reflect.ValueOf(key.B), + "Back": reflect.ValueOf(key.Back), + "Backquote": reflect.ValueOf(key.Backquote), + "Backslash": reflect.ValueOf(key.Backslash), + "BracketLeft": reflect.ValueOf(key.BracketLeft), + "BracketRight": reflect.ValueOf(key.BracketRight), + "Btn": reflect.ValueOf(key.Btn), + "Btnp": reflect.ValueOf(key.Btnp), + "C": reflect.ValueOf(key.C), + "Cap": reflect.ValueOf(key.Cap), + "Comma": reflect.ValueOf(key.Comma), + "Ctrl": reflect.ValueOf(key.Ctrl), + "D": reflect.ValueOf(key.D), + "Digit0": reflect.ValueOf(key.Digit0), + "Digit1": reflect.ValueOf(key.Digit1), + "Digit2": reflect.ValueOf(key.Digit2), + "Digit3": reflect.ValueOf(key.Digit3), + "Digit4": reflect.ValueOf(key.Digit4), + "Digit5": reflect.ValueOf(key.Digit5), + "Digit6": reflect.ValueOf(key.Digit6), + "Digit7": reflect.ValueOf(key.Digit7), + "Digit8": reflect.ValueOf(key.Digit8), + "Digit9": reflect.ValueOf(key.Digit9), + "Down": reflect.ValueOf(key.Down), + "Duration": reflect.ValueOf(&key.Duration).Elem(), + "E": reflect.ValueOf(key.E), + "Enter": reflect.ValueOf(key.Enter), + "Equal": reflect.ValueOf(key.Equal), + "Esc": reflect.ValueOf(key.Esc), + "F": reflect.ValueOf(key.F), + "F1": reflect.ValueOf(key.F1), + "F10": reflect.ValueOf(key.F10), + "F11": reflect.ValueOf(key.F11), + "F12": reflect.ValueOf(key.F12), + "F2": reflect.ValueOf(key.F2), + "F3": reflect.ValueOf(key.F3), + "F4": reflect.ValueOf(key.F4), + "F5": reflect.ValueOf(key.F5), + "F6": reflect.ValueOf(key.F6), + "F7": reflect.ValueOf(key.F7), + "F8": reflect.ValueOf(key.F8), + "F9": reflect.ValueOf(key.F9), + "G": reflect.ValueOf(key.G), + "H": reflect.ValueOf(key.H), + "I": reflect.ValueOf(key.I), + "J": reflect.ValueOf(key.J), + "K": reflect.ValueOf(key.K), + "L": reflect.ValueOf(key.L), + "Left": reflect.ValueOf(key.Left), + "M": reflect.ValueOf(key.M), + "Minus": reflect.ValueOf(key.Minus), + "N": reflect.ValueOf(key.N), + "O": reflect.ValueOf(key.O), + "P": reflect.ValueOf(key.P), + "Period": reflect.ValueOf(key.Period), + "Q": reflect.ValueOf(key.Q), + "R": reflect.ValueOf(key.R), + "Right": reflect.ValueOf(key.Right), + "S": reflect.ValueOf(key.S), + "Semicolon": reflect.ValueOf(key.Semicolon), + "Shift": reflect.ValueOf(key.Shift), + "Slash": reflect.ValueOf(key.Slash), + "Space": reflect.ValueOf(key.Space), + "T": reflect.ValueOf(key.T), + "Tab": reflect.ValueOf(key.Tab), + "U": reflect.ValueOf(key.U), + "Up": reflect.ValueOf(key.Up), + "V": reflect.ValueOf(key.V), + "W": reflect.ValueOf(key.W), + "X": reflect.ValueOf(key.X), + "Y": reflect.ValueOf(key.Y), + "Z": reflect.ValueOf(key.Z), + + // type definitions + "Button": reflect.ValueOf((*key.Button)(nil)), + } +} diff --git a/devtools/internal/interpreter/lib/github_com-elgopher-pi-snap.go b/devtools/internal/interpreter/lib/github_com-elgopher-pi-snap.go new file mode 100644 index 0000000..d19152b --- /dev/null +++ b/devtools/internal/interpreter/lib/github_com-elgopher-pi-snap.go @@ -0,0 +1,15 @@ +// Code generated by 'yaegi extract github.com/elgopher/pi/snap'. DO NOT EDIT. + +package lib + +import ( + "github.com/elgopher/pi/snap" + "reflect" +) + +func init() { + Symbols["github.com/elgopher/pi/snap/snap"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Take": reflect.ValueOf(snap.Take), + } +} diff --git a/devtools/internal/interpreter/lib/github_com-elgopher-pi-state.go b/devtools/internal/interpreter/lib/github_com-elgopher-pi-state.go new file mode 100644 index 0000000..2f49218 --- /dev/null +++ b/devtools/internal/interpreter/lib/github_com-elgopher-pi-state.go @@ -0,0 +1,23 @@ +// Code generated by 'yaegi extract github.com/elgopher/pi/state'. DO NOT EDIT. + +package lib + +import ( + "github.com/elgopher/pi/state" + "reflect" +) + +func init() { + Symbols["github.com/elgopher/pi/state/state"] = map[string]reflect.Value{ + // function, constant and variable definitions + "All": reflect.ValueOf(state.All), + "Delete": reflect.ValueOf(state.Delete), + "ErrInvalidStateName": reflect.ValueOf(&state.ErrInvalidStateName).Elem(), + "ErrNilStateOutput": reflect.ValueOf(&state.ErrNilStateOutput).Elem(), + "ErrNotFound": reflect.ValueOf(&state.ErrNotFound).Elem(), + "ErrStateMarshalFailed": reflect.ValueOf(&state.ErrStateMarshalFailed).Elem(), + "ErrStateUnmarshalFailed": reflect.ValueOf(&state.ErrStateUnmarshalFailed).Elem(), + //"Load": reflect.ValueOf(state.Load), // TODO Replace generic parameter with any and uncomment this line + "Save": reflect.ValueOf(state.Save), + } +} diff --git a/devtools/internal/interpreter/lib/github_com-elgopher-pi.go b/devtools/internal/interpreter/lib/github_com-elgopher-pi.go new file mode 100644 index 0000000..507fad4 --- /dev/null +++ b/devtools/internal/interpreter/lib/github_com-elgopher-pi.go @@ -0,0 +1,116 @@ +// Code generated by 'yaegi extract github.com/elgopher/pi'. DO NOT EDIT. + +package lib + +import ( + "go/constant" + "go/token" + "reflect" + + "github.com/elgopher/pi" +) + +func init() { + Symbols["github.com/elgopher/pi/pi"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Atan2": reflect.ValueOf(pi.Atan2), + "Audio": reflect.ValueOf(pi.Audio), + "Btn": reflect.ValueOf(pi.Btn), + "BtnBits": reflect.ValueOf(pi.BtnBits), + "BtnPlayer": reflect.ValueOf(pi.BtnPlayer), + "Btnp": reflect.ValueOf(pi.Btnp), + "BtnpBits": reflect.ValueOf(pi.BtnpBits), + "BtnpPlayer": reflect.ValueOf(pi.BtnpPlayer), + "Camera": reflect.ValueOf(pi.Camera), + "CameraReset": reflect.ValueOf(pi.CameraReset), + "Circ": reflect.ValueOf(pi.Circ), + "CircFill": reflect.ValueOf(pi.CircFill), + "Clip": reflect.ValueOf(pi.Clip), + "ClipReset": reflect.ValueOf(pi.ClipReset), + "Cls": reflect.ValueOf(pi.Cls), + "ClsCol": reflect.ValueOf(pi.ClsCol), + "ColorTransparency": reflect.ValueOf(&pi.ColorTransparency).Elem(), + "Controllers": reflect.ValueOf(&pi.Controllers).Elem(), + "Cos": reflect.ValueOf(pi.Cos), + "CustomFont": reflect.ValueOf(pi.CustomFont), + "DisplayPalette": reflect.ValueOf(&pi.DisplayPalette).Elem(), + "Down": reflect.ValueOf(pi.Down), + "Draw": reflect.ValueOf(&pi.Draw).Elem(), + "DrawPalette": reflect.ValueOf(&pi.DrawPalette).Elem(), + "GameLoopStopped": reflect.ValueOf(&pi.GameLoopStopped).Elem(), + "Left": reflect.ValueOf(pi.Left), + "Line": reflect.ValueOf(pi.Line), + "Load": reflect.ValueOf(pi.Load), + "MaxInt": reflect.ValueOf(pi.MaxInt[int]), // TODO Generic functions not supported by Yaegi yet + "Mid": reflect.ValueOf(pi.Mid), + "MidInt": reflect.ValueOf(pi.MidInt[int]), // TODO Generic functions not supported by Yaegi yet + "MinInt": reflect.ValueOf(pi.MinInt[int]), // TODO Generic functions not supported by Yaegi yet + "MouseBtn": reflect.ValueOf(pi.MouseBtn), + "MouseBtnDuration": reflect.ValueOf(&pi.MouseBtnDuration).Elem(), + "MouseBtnp": reflect.ValueOf(pi.MouseBtnp), + "MouseLeft": reflect.ValueOf(pi.MouseLeft), + "MouseMiddle": reflect.ValueOf(pi.MouseMiddle), + "MousePos": reflect.ValueOf(pi.MousePos), + "MousePosition": reflect.ValueOf(&pi.MousePosition).Elem(), + "MouseRight": reflect.ValueOf(pi.MouseRight), + "NewPixMap": reflect.ValueOf(pi.NewPixMap), + "NewPixMapWithPixels": reflect.ValueOf(pi.NewPixMapWithPixels), + "O": reflect.ValueOf(pi.O), + "Pal": reflect.ValueOf(pi.Pal), + "PalDisplay": reflect.ValueOf(pi.PalDisplay), + "PalReset": reflect.ValueOf(pi.PalReset), + "Palette": reflect.ValueOf(&pi.Palette).Elem(), + "Palt": reflect.ValueOf(pi.Palt), + "PaltReset": reflect.ValueOf(pi.PaltReset), + "Pget": reflect.ValueOf(pi.Pget), + "Print": reflect.ValueOf(pi.Print), + "Pset": reflect.ValueOf(pi.Pset), + "Rect": reflect.ValueOf(pi.Rect), + "RectFill": reflect.ValueOf(pi.RectFill), + "Reset": reflect.ValueOf(pi.Reset), + "Right": reflect.ValueOf(pi.Right), + "Scr": reflect.ValueOf(pi.Scr), + "ScreenCamera": reflect.ValueOf(&pi.ScreenCamera).Elem(), + "SetCustomFontHeight": reflect.ValueOf(pi.SetCustomFontHeight), + "SetCustomFontSpecialWidth": reflect.ValueOf(pi.SetCustomFontSpecialWidth), + "SetCustomFontWidth": reflect.ValueOf(pi.SetCustomFontWidth), + "SetScreenSize": reflect.ValueOf(pi.SetScreenSize), + "Sget": reflect.ValueOf(pi.Sget), + "Sin": reflect.ValueOf(pi.Sin), + "Spr": reflect.ValueOf(pi.Spr), + "SprSheet": reflect.ValueOf(pi.SprSheet), + "SprSize": reflect.ValueOf(pi.SprSize), + "SprSizeFlip": reflect.ValueOf(pi.SprSizeFlip), + "SpriteHeight": reflect.ValueOf(constant.MakeFromLiteral("8", token.INT, 0)), + "SpriteWidth": reflect.ValueOf(constant.MakeFromLiteral("8", token.INT, 0)), + "Sset": reflect.ValueOf(pi.Sset), + "Stop": reflect.ValueOf(pi.Stop), + "SystemFont": reflect.ValueOf(pi.SystemFont), + "Time": reflect.ValueOf(pi.Time), + "TimeSeconds": reflect.ValueOf(&pi.TimeSeconds).Elem(), + "Up": reflect.ValueOf(pi.Up), + "Update": reflect.ValueOf(&pi.Update).Elem(), + "UseEmptySpriteSheet": reflect.ValueOf(pi.UseEmptySpriteSheet), + "X": reflect.ValueOf(pi.X), + + // type definitions + "AudioSystem": reflect.ValueOf((*pi.AudioSystem)(nil)), + "Button": reflect.ValueOf((*pi.Button)(nil)), + "Controller": reflect.ValueOf((*pi.Controller)(nil)), + "Font": reflect.ValueOf((*pi.Font)(nil)), + //"Int": reflect.ValueOf((*pi.Int)(nil)), // TODO Generic constraints not supported by Yaegi yet + "MouseButton": reflect.ValueOf((*pi.MouseButton)(nil)), + "PixMap": reflect.ValueOf((*pi.PixMap)(nil)), + "Pointer": reflect.ValueOf((*pi.Pointer)(nil)), + "Position": reflect.ValueOf((*pi.Position)(nil)), + "Region": reflect.ValueOf((*pi.Region)(nil)), + + // interface wrapper definitions + "_Int": reflect.ValueOf((*_github_com_elgopher_pi_Int)(nil)), + } +} + +// _github_com_elgopher_pi_Int is an interface wrapper for Int type +type _github_com_elgopher_pi_Int struct { + IValue interface{} +} diff --git a/devtools/internal/interpreter/lib/go1_20_bytes.go b/devtools/internal/interpreter/lib/go1_20_bytes.go new file mode 100644 index 0000000..ef50481 --- /dev/null +++ b/devtools/internal/interpreter/lib/go1_20_bytes.go @@ -0,0 +1,76 @@ +// Code generated by 'yaegi extract bytes'. DO NOT EDIT. + +package lib + +import ( + "bytes" + "go/constant" + "go/token" + "reflect" +) + +func init() { + Symbols["bytes/bytes"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Clone": reflect.ValueOf(bytes.Clone), + "Compare": reflect.ValueOf(bytes.Compare), + "Contains": reflect.ValueOf(bytes.Contains), + "ContainsAny": reflect.ValueOf(bytes.ContainsAny), + "ContainsRune": reflect.ValueOf(bytes.ContainsRune), + "Count": reflect.ValueOf(bytes.Count), + "Cut": reflect.ValueOf(bytes.Cut), + "CutPrefix": reflect.ValueOf(bytes.CutPrefix), + "CutSuffix": reflect.ValueOf(bytes.CutSuffix), + "Equal": reflect.ValueOf(bytes.Equal), + "EqualFold": reflect.ValueOf(bytes.EqualFold), + "ErrTooLarge": reflect.ValueOf(&bytes.ErrTooLarge).Elem(), + "Fields": reflect.ValueOf(bytes.Fields), + "FieldsFunc": reflect.ValueOf(bytes.FieldsFunc), + "HasPrefix": reflect.ValueOf(bytes.HasPrefix), + "HasSuffix": reflect.ValueOf(bytes.HasSuffix), + "Index": reflect.ValueOf(bytes.Index), + "IndexAny": reflect.ValueOf(bytes.IndexAny), + "IndexByte": reflect.ValueOf(bytes.IndexByte), + "IndexFunc": reflect.ValueOf(bytes.IndexFunc), + "IndexRune": reflect.ValueOf(bytes.IndexRune), + "Join": reflect.ValueOf(bytes.Join), + "LastIndex": reflect.ValueOf(bytes.LastIndex), + "LastIndexAny": reflect.ValueOf(bytes.LastIndexAny), + "LastIndexByte": reflect.ValueOf(bytes.LastIndexByte), + "LastIndexFunc": reflect.ValueOf(bytes.LastIndexFunc), + "Map": reflect.ValueOf(bytes.Map), + "MinRead": reflect.ValueOf(constant.MakeFromLiteral("512", token.INT, 0)), + "NewBuffer": reflect.ValueOf(bytes.NewBuffer), + "NewBufferString": reflect.ValueOf(bytes.NewBufferString), + "NewReader": reflect.ValueOf(bytes.NewReader), + "Repeat": reflect.ValueOf(bytes.Repeat), + "Replace": reflect.ValueOf(bytes.Replace), + "ReplaceAll": reflect.ValueOf(bytes.ReplaceAll), + "Runes": reflect.ValueOf(bytes.Runes), + "Split": reflect.ValueOf(bytes.Split), + "SplitAfter": reflect.ValueOf(bytes.SplitAfter), + "SplitAfterN": reflect.ValueOf(bytes.SplitAfterN), + "SplitN": reflect.ValueOf(bytes.SplitN), + "Title": reflect.ValueOf(bytes.Title), + "ToLower": reflect.ValueOf(bytes.ToLower), + "ToLowerSpecial": reflect.ValueOf(bytes.ToLowerSpecial), + "ToTitle": reflect.ValueOf(bytes.ToTitle), + "ToTitleSpecial": reflect.ValueOf(bytes.ToTitleSpecial), + "ToUpper": reflect.ValueOf(bytes.ToUpper), + "ToUpperSpecial": reflect.ValueOf(bytes.ToUpperSpecial), + "ToValidUTF8": reflect.ValueOf(bytes.ToValidUTF8), + "Trim": reflect.ValueOf(bytes.Trim), + "TrimFunc": reflect.ValueOf(bytes.TrimFunc), + "TrimLeft": reflect.ValueOf(bytes.TrimLeft), + "TrimLeftFunc": reflect.ValueOf(bytes.TrimLeftFunc), + "TrimPrefix": reflect.ValueOf(bytes.TrimPrefix), + "TrimRight": reflect.ValueOf(bytes.TrimRight), + "TrimRightFunc": reflect.ValueOf(bytes.TrimRightFunc), + "TrimSpace": reflect.ValueOf(bytes.TrimSpace), + "TrimSuffix": reflect.ValueOf(bytes.TrimSuffix), + + // type definitions + "Buffer": reflect.ValueOf((*bytes.Buffer)(nil)), + "Reader": reflect.ValueOf((*bytes.Reader)(nil)), + } +} diff --git a/devtools/internal/interpreter/lib/go1_20_fmt.go b/devtools/internal/interpreter/lib/go1_20_fmt.go new file mode 100644 index 0000000..323a308 --- /dev/null +++ b/devtools/internal/interpreter/lib/go1_20_fmt.go @@ -0,0 +1,148 @@ +// Code generated by 'yaegi extract fmt'. DO NOT EDIT. + +package lib + +import ( + "fmt" + "reflect" +) + +func init() { + Symbols["fmt/fmt"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Append": reflect.ValueOf(fmt.Append), + "Appendf": reflect.ValueOf(fmt.Appendf), + "Appendln": reflect.ValueOf(fmt.Appendln), + "Errorf": reflect.ValueOf(fmt.Errorf), + "FormatString": reflect.ValueOf(fmt.FormatString), + "Fprint": reflect.ValueOf(fmt.Fprint), + "Fprintf": reflect.ValueOf(fmt.Fprintf), + "Fprintln": reflect.ValueOf(fmt.Fprintln), + "Fscan": reflect.ValueOf(fmt.Fscan), + "Fscanf": reflect.ValueOf(fmt.Fscanf), + "Fscanln": reflect.ValueOf(fmt.Fscanln), + "Print": reflect.ValueOf(fmt.Print), + "Printf": reflect.ValueOf(fmt.Printf), + "Println": reflect.ValueOf(fmt.Println), + "Scan": reflect.ValueOf(fmt.Scan), + "Scanf": reflect.ValueOf(fmt.Scanf), + "Scanln": reflect.ValueOf(fmt.Scanln), + "Sprint": reflect.ValueOf(fmt.Sprint), + "Sprintf": reflect.ValueOf(fmt.Sprintf), + "Sprintln": reflect.ValueOf(fmt.Sprintln), + "Sscan": reflect.ValueOf(fmt.Sscan), + "Sscanf": reflect.ValueOf(fmt.Sscanf), + "Sscanln": reflect.ValueOf(fmt.Sscanln), + + // type definitions + "Formatter": reflect.ValueOf((*fmt.Formatter)(nil)), + "GoStringer": reflect.ValueOf((*fmt.GoStringer)(nil)), + "ScanState": reflect.ValueOf((*fmt.ScanState)(nil)), + "Scanner": reflect.ValueOf((*fmt.Scanner)(nil)), + "State": reflect.ValueOf((*fmt.State)(nil)), + "Stringer": reflect.ValueOf((*fmt.Stringer)(nil)), + + // interface wrapper definitions + "_Formatter": reflect.ValueOf((*_fmt_Formatter)(nil)), + "_GoStringer": reflect.ValueOf((*_fmt_GoStringer)(nil)), + "_ScanState": reflect.ValueOf((*_fmt_ScanState)(nil)), + "_Scanner": reflect.ValueOf((*_fmt_Scanner)(nil)), + "_State": reflect.ValueOf((*_fmt_State)(nil)), + "_Stringer": reflect.ValueOf((*_fmt_Stringer)(nil)), + } +} + +// _fmt_Formatter is an interface wrapper for Formatter type +type _fmt_Formatter struct { + IValue interface{} + WFormat func(f fmt.State, verb rune) +} + +func (W _fmt_Formatter) Format(f fmt.State, verb rune) { + W.WFormat(f, verb) +} + +// _fmt_GoStringer is an interface wrapper for GoStringer type +type _fmt_GoStringer struct { + IValue interface{} + WGoString func() string +} + +func (W _fmt_GoStringer) GoString() string { + return W.WGoString() +} + +// _fmt_ScanState is an interface wrapper for ScanState type +type _fmt_ScanState struct { + IValue interface{} + WRead func(buf []byte) (n int, err error) + WReadRune func() (r rune, size int, err error) + WSkipSpace func() + WToken func(skipSpace bool, f func(rune) bool) (token []byte, err error) + WUnreadRune func() error + WWidth func() (wid int, ok bool) +} + +func (W _fmt_ScanState) Read(buf []byte) (n int, err error) { + return W.WRead(buf) +} +func (W _fmt_ScanState) ReadRune() (r rune, size int, err error) { + return W.WReadRune() +} +func (W _fmt_ScanState) SkipSpace() { + W.WSkipSpace() +} +func (W _fmt_ScanState) Token(skipSpace bool, f func(rune) bool) (token []byte, err error) { + return W.WToken(skipSpace, f) +} +func (W _fmt_ScanState) UnreadRune() error { + return W.WUnreadRune() +} +func (W _fmt_ScanState) Width() (wid int, ok bool) { + return W.WWidth() +} + +// _fmt_Scanner is an interface wrapper for Scanner type +type _fmt_Scanner struct { + IValue interface{} + WScan func(state fmt.ScanState, verb rune) error +} + +func (W _fmt_Scanner) Scan(state fmt.ScanState, verb rune) error { + return W.WScan(state, verb) +} + +// _fmt_State is an interface wrapper for State type +type _fmt_State struct { + IValue interface{} + WFlag func(c int) bool + WPrecision func() (prec int, ok bool) + WWidth func() (wid int, ok bool) + WWrite func(b []byte) (n int, err error) +} + +func (W _fmt_State) Flag(c int) bool { + return W.WFlag(c) +} +func (W _fmt_State) Precision() (prec int, ok bool) { + return W.WPrecision() +} +func (W _fmt_State) Width() (wid int, ok bool) { + return W.WWidth() +} +func (W _fmt_State) Write(b []byte) (n int, err error) { + return W.WWrite(b) +} + +// _fmt_Stringer is an interface wrapper for Stringer type +type _fmt_Stringer struct { + IValue interface{} + WString func() string +} + +func (W _fmt_Stringer) String() string { + if W.WString == nil { + return "" + } + return W.WString() +} diff --git a/devtools/internal/interpreter/lib/go1_20_math.go b/devtools/internal/interpreter/lib/go1_20_math.go new file mode 100644 index 0000000..5e55e6d --- /dev/null +++ b/devtools/internal/interpreter/lib/go1_20_math.go @@ -0,0 +1,113 @@ +// Code generated by 'yaegi extract math'. DO NOT EDIT. + +package lib + +import ( + "go/constant" + "go/token" + "math" + "reflect" +) + +func init() { + Symbols["math/math"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Abs": reflect.ValueOf(math.Abs), + "Acos": reflect.ValueOf(math.Acos), + "Acosh": reflect.ValueOf(math.Acosh), + "Asin": reflect.ValueOf(math.Asin), + "Asinh": reflect.ValueOf(math.Asinh), + "Atan": reflect.ValueOf(math.Atan), + "Atan2": reflect.ValueOf(math.Atan2), + "Atanh": reflect.ValueOf(math.Atanh), + "Cbrt": reflect.ValueOf(math.Cbrt), + "Ceil": reflect.ValueOf(math.Ceil), + "Copysign": reflect.ValueOf(math.Copysign), + "Cos": reflect.ValueOf(math.Cos), + "Cosh": reflect.ValueOf(math.Cosh), + "Dim": reflect.ValueOf(math.Dim), + "E": reflect.ValueOf(constant.MakeFromLiteral("2.71828182845904523536028747135266249775724709369995957496696762566337824315673231520670375558666729784504486779277967997696994772644702281675346915668215131895555530285035761295375777990557253360748291015625", token.FLOAT, 0)), + "Erf": reflect.ValueOf(math.Erf), + "Erfc": reflect.ValueOf(math.Erfc), + "Erfcinv": reflect.ValueOf(math.Erfcinv), + "Erfinv": reflect.ValueOf(math.Erfinv), + "Exp": reflect.ValueOf(math.Exp), + "Exp2": reflect.ValueOf(math.Exp2), + "Expm1": reflect.ValueOf(math.Expm1), + "FMA": reflect.ValueOf(math.FMA), + "Float32bits": reflect.ValueOf(math.Float32bits), + "Float32frombits": reflect.ValueOf(math.Float32frombits), + "Float64bits": reflect.ValueOf(math.Float64bits), + "Float64frombits": reflect.ValueOf(math.Float64frombits), + "Floor": reflect.ValueOf(math.Floor), + "Frexp": reflect.ValueOf(math.Frexp), + "Gamma": reflect.ValueOf(math.Gamma), + "Hypot": reflect.ValueOf(math.Hypot), + "Ilogb": reflect.ValueOf(math.Ilogb), + "Inf": reflect.ValueOf(math.Inf), + "IsInf": reflect.ValueOf(math.IsInf), + "IsNaN": reflect.ValueOf(math.IsNaN), + "J0": reflect.ValueOf(math.J0), + "J1": reflect.ValueOf(math.J1), + "Jn": reflect.ValueOf(math.Jn), + "Ldexp": reflect.ValueOf(math.Ldexp), + "Lgamma": reflect.ValueOf(math.Lgamma), + "Ln10": reflect.ValueOf(constant.MakeFromLiteral("2.30258509299404568401799145468436420760110148862877297603332784146804725494827975466552490443295866962642372461496758838959542646932914211937012833592062802600362869664962772731087170541286468505859375", token.FLOAT, 0)), + "Ln2": reflect.ValueOf(constant.MakeFromLiteral("0.6931471805599453094172321214581765680755001343602552541206800092715999496201383079363438206637927920954189307729314303884387720696314608777673678644642390655170150035209453154294578780536539852619171142578125", token.FLOAT, 0)), + "Log": reflect.ValueOf(math.Log), + "Log10": reflect.ValueOf(math.Log10), + "Log10E": reflect.ValueOf(constant.MakeFromLiteral("0.43429448190325182765112891891660508229439700580366656611445378416636798190620320263064286300825210972160277489744884502676719847561509639618196799746596688688378591625127711495224502868950366973876953125", token.FLOAT, 0)), + "Log1p": reflect.ValueOf(math.Log1p), + "Log2": reflect.ValueOf(math.Log2), + "Log2E": reflect.ValueOf(constant.MakeFromLiteral("1.44269504088896340735992468100189213742664595415298593413544940772066427768997545329060870636212628972710992130324953463427359402479619301286929040235571747101382214539290471666532766903401352465152740478515625", token.FLOAT, 0)), + "Logb": reflect.ValueOf(math.Logb), + "Max": reflect.ValueOf(math.Max), + "MaxFloat32": reflect.ValueOf(constant.MakeFromLiteral("340282346638528859811704183484516925440", token.FLOAT, 0)), + "MaxFloat64": reflect.ValueOf(constant.MakeFromLiteral("179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368", token.FLOAT, 0)), + "MaxInt": reflect.ValueOf(constant.MakeFromLiteral("9223372036854775807", token.INT, 0)), + "MaxInt16": reflect.ValueOf(constant.MakeFromLiteral("32767", token.INT, 0)), + "MaxInt32": reflect.ValueOf(constant.MakeFromLiteral("2147483647", token.INT, 0)), + "MaxInt64": reflect.ValueOf(constant.MakeFromLiteral("9223372036854775807", token.INT, 0)), + "MaxInt8": reflect.ValueOf(constant.MakeFromLiteral("127", token.INT, 0)), + "MaxUint": reflect.ValueOf(constant.MakeFromLiteral("18446744073709551615", token.INT, 0)), + "MaxUint16": reflect.ValueOf(constant.MakeFromLiteral("65535", token.INT, 0)), + "MaxUint32": reflect.ValueOf(constant.MakeFromLiteral("4294967295", token.INT, 0)), + "MaxUint64": reflect.ValueOf(constant.MakeFromLiteral("18446744073709551615", token.INT, 0)), + "MaxUint8": reflect.ValueOf(constant.MakeFromLiteral("255", token.INT, 0)), + "Min": reflect.ValueOf(math.Min), + "MinInt": reflect.ValueOf(constant.MakeFromLiteral("-9223372036854775808", token.INT, 0)), + "MinInt16": reflect.ValueOf(constant.MakeFromLiteral("-32768", token.INT, 0)), + "MinInt32": reflect.ValueOf(constant.MakeFromLiteral("-2147483648", token.INT, 0)), + "MinInt64": reflect.ValueOf(constant.MakeFromLiteral("-9223372036854775808", token.INT, 0)), + "MinInt8": reflect.ValueOf(constant.MakeFromLiteral("-128", token.INT, 0)), + "Mod": reflect.ValueOf(math.Mod), + "Modf": reflect.ValueOf(math.Modf), + "NaN": reflect.ValueOf(math.NaN), + "Nextafter": reflect.ValueOf(math.Nextafter), + "Nextafter32": reflect.ValueOf(math.Nextafter32), + "Phi": reflect.ValueOf(constant.MakeFromLiteral("1.6180339887498948482045868343656381177203091798057628621354486119746080982153796619881086049305501566952211682590824739205931370737029882996587050475921915678674035433959321750307935872115194797515869140625", token.FLOAT, 0)), + "Pi": reflect.ValueOf(constant.MakeFromLiteral("3.141592653589793238462643383279502884197169399375105820974944594789982923695635954704435713335896673485663389728754819466702315787113662862838515639906529162340867271374644786874341662041842937469482421875", token.FLOAT, 0)), + "Pow": reflect.ValueOf(math.Pow), + "Pow10": reflect.ValueOf(math.Pow10), + "Remainder": reflect.ValueOf(math.Remainder), + "Round": reflect.ValueOf(math.Round), + "RoundToEven": reflect.ValueOf(math.RoundToEven), + "Signbit": reflect.ValueOf(math.Signbit), + "Sin": reflect.ValueOf(math.Sin), + "Sincos": reflect.ValueOf(math.Sincos), + "Sinh": reflect.ValueOf(math.Sinh), + "SmallestNonzeroFloat32": reflect.ValueOf(constant.MakeFromLiteral("1.40129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125e-45", token.FLOAT, 0)), + "SmallestNonzeroFloat64": reflect.ValueOf(constant.MakeFromLiteral("4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625e-324", token.FLOAT, 0)), + "Sqrt": reflect.ValueOf(math.Sqrt), + "Sqrt2": reflect.ValueOf(constant.MakeFromLiteral("1.414213562373095048801688724209698078569671875376948073176679739576083351575381440094441524123797447886801949755143139115339040409162552642832693297721230919563348109313505318596071447245776653289794921875", token.FLOAT, 0)), + "SqrtE": reflect.ValueOf(constant.MakeFromLiteral("1.64872127070012814684865078781416357165377610071014801157507931167328763229187870850146925823776361770041160388013884200789716007979526823569827080974091691342077871211546646890155898290686309337615966796875", token.FLOAT, 0)), + "SqrtPhi": reflect.ValueOf(constant.MakeFromLiteral("1.2720196495140689642524224617374914917156080418400962486166403754616080542166459302584536396369727769747312116100875915825863540562126478288118732191412003988041797518382391984914647764526307582855224609375", token.FLOAT, 0)), + "SqrtPi": reflect.ValueOf(constant.MakeFromLiteral("1.772453850905516027298167483341145182797549456122387128213807789740599698370237052541269446184448945647349951047154197675245574635259260134350885938555625028620527962319730619356050738133490085601806640625", token.FLOAT, 0)), + "Tan": reflect.ValueOf(math.Tan), + "Tanh": reflect.ValueOf(math.Tanh), + "Trunc": reflect.ValueOf(math.Trunc), + "Y0": reflect.ValueOf(math.Y0), + "Y1": reflect.ValueOf(math.Y1), + "Yn": reflect.ValueOf(math.Yn), + } +} diff --git a/devtools/internal/interpreter/lib/go1_20_math_rand.go b/devtools/internal/interpreter/lib/go1_20_math_rand.go new file mode 100644 index 0000000..9113d41 --- /dev/null +++ b/devtools/internal/interpreter/lib/go1_20_math_rand.go @@ -0,0 +1,75 @@ +// Code generated by 'yaegi extract math/rand'. DO NOT EDIT. + +package lib + +import ( + "math/rand" + "reflect" +) + +func init() { + Symbols["math/rand/rand"] = map[string]reflect.Value{ + // function, constant and variable definitions + "ExpFloat64": reflect.ValueOf(rand.ExpFloat64), + "Float32": reflect.ValueOf(rand.Float32), + "Float64": reflect.ValueOf(rand.Float64), + "Int": reflect.ValueOf(rand.Int), + "Int31": reflect.ValueOf(rand.Int31), + "Int31n": reflect.ValueOf(rand.Int31n), + "Int63": reflect.ValueOf(rand.Int63), + "Int63n": reflect.ValueOf(rand.Int63n), + "Intn": reflect.ValueOf(rand.Intn), + "New": reflect.ValueOf(rand.New), + "NewSource": reflect.ValueOf(rand.NewSource), + "NewZipf": reflect.ValueOf(rand.NewZipf), + "NormFloat64": reflect.ValueOf(rand.NormFloat64), + "Perm": reflect.ValueOf(rand.Perm), + "Read": reflect.ValueOf(rand.Read), + "Seed": reflect.ValueOf(rand.Seed), + "Shuffle": reflect.ValueOf(rand.Shuffle), + "Uint32": reflect.ValueOf(rand.Uint32), + "Uint64": reflect.ValueOf(rand.Uint64), + + // type definitions + "Rand": reflect.ValueOf((*rand.Rand)(nil)), + "Source": reflect.ValueOf((*rand.Source)(nil)), + "Source64": reflect.ValueOf((*rand.Source64)(nil)), + "Zipf": reflect.ValueOf((*rand.Zipf)(nil)), + + // interface wrapper definitions + "_Source": reflect.ValueOf((*_math_rand_Source)(nil)), + "_Source64": reflect.ValueOf((*_math_rand_Source64)(nil)), + } +} + +// _math_rand_Source is an interface wrapper for Source type +type _math_rand_Source struct { + IValue interface{} + WInt63 func() int64 + WSeed func(seed int64) +} + +func (W _math_rand_Source) Int63() int64 { + return W.WInt63() +} +func (W _math_rand_Source) Seed(seed int64) { + W.WSeed(seed) +} + +// _math_rand_Source64 is an interface wrapper for Source64 type +type _math_rand_Source64 struct { + IValue interface{} + WInt63 func() int64 + WSeed func(seed int64) + WUint64 func() uint64 +} + +func (W _math_rand_Source64) Int63() int64 { + return W.WInt63() +} +func (W _math_rand_Source64) Seed(seed int64) { + W.WSeed(seed) +} +func (W _math_rand_Source64) Uint64() uint64 { + return W.WUint64() +} diff --git a/devtools/internal/interpreter/lib/go1_20_sort.go b/devtools/internal/interpreter/lib/go1_20_sort.go new file mode 100644 index 0000000..8d17ce0 --- /dev/null +++ b/devtools/internal/interpreter/lib/go1_20_sort.go @@ -0,0 +1,59 @@ +// Code generated by 'yaegi extract sort'. DO NOT EDIT. + +package lib + +import ( + "reflect" + "sort" +) + +func init() { + Symbols["sort/sort"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Find": reflect.ValueOf(sort.Find), + "Float64s": reflect.ValueOf(sort.Float64s), + "Float64sAreSorted": reflect.ValueOf(sort.Float64sAreSorted), + "Ints": reflect.ValueOf(sort.Ints), + "IntsAreSorted": reflect.ValueOf(sort.IntsAreSorted), + "IsSorted": reflect.ValueOf(sort.IsSorted), + "Reverse": reflect.ValueOf(sort.Reverse), + "Search": reflect.ValueOf(sort.Search), + "SearchFloat64s": reflect.ValueOf(sort.SearchFloat64s), + "SearchInts": reflect.ValueOf(sort.SearchInts), + "SearchStrings": reflect.ValueOf(sort.SearchStrings), + "Slice": reflect.ValueOf(sort.Slice), + "SliceIsSorted": reflect.ValueOf(sort.SliceIsSorted), + "SliceStable": reflect.ValueOf(sort.SliceStable), + "Sort": reflect.ValueOf(sort.Sort), + "Stable": reflect.ValueOf(sort.Stable), + "Strings": reflect.ValueOf(sort.Strings), + "StringsAreSorted": reflect.ValueOf(sort.StringsAreSorted), + + // type definitions + "Float64Slice": reflect.ValueOf((*sort.Float64Slice)(nil)), + "IntSlice": reflect.ValueOf((*sort.IntSlice)(nil)), + "Interface": reflect.ValueOf((*sort.Interface)(nil)), + "StringSlice": reflect.ValueOf((*sort.StringSlice)(nil)), + + // interface wrapper definitions + "_Interface": reflect.ValueOf((*_sort_Interface)(nil)), + } +} + +// _sort_Interface is an interface wrapper for Interface type +type _sort_Interface struct { + IValue interface{} + WLen func() int + WLess func(i int, j int) bool + WSwap func(i int, j int) +} + +func (W _sort_Interface) Len() int { + return W.WLen() +} +func (W _sort_Interface) Less(i int, j int) bool { + return W.WLess(i, j) +} +func (W _sort_Interface) Swap(i int, j int) { + W.WSwap(i, j) +} diff --git a/devtools/internal/interpreter/lib/go1_20_strconv.go b/devtools/internal/interpreter/lib/go1_20_strconv.go new file mode 100644 index 0000000..d8ab53a --- /dev/null +++ b/devtools/internal/interpreter/lib/go1_20_strconv.go @@ -0,0 +1,56 @@ +// Code generated by 'yaegi extract strconv'. DO NOT EDIT. + +package lib + +import ( + "go/constant" + "go/token" + "reflect" + "strconv" +) + +func init() { + Symbols["strconv/strconv"] = map[string]reflect.Value{ + // function, constant and variable definitions + "AppendBool": reflect.ValueOf(strconv.AppendBool), + "AppendFloat": reflect.ValueOf(strconv.AppendFloat), + "AppendInt": reflect.ValueOf(strconv.AppendInt), + "AppendQuote": reflect.ValueOf(strconv.AppendQuote), + "AppendQuoteRune": reflect.ValueOf(strconv.AppendQuoteRune), + "AppendQuoteRuneToASCII": reflect.ValueOf(strconv.AppendQuoteRuneToASCII), + "AppendQuoteRuneToGraphic": reflect.ValueOf(strconv.AppendQuoteRuneToGraphic), + "AppendQuoteToASCII": reflect.ValueOf(strconv.AppendQuoteToASCII), + "AppendQuoteToGraphic": reflect.ValueOf(strconv.AppendQuoteToGraphic), + "AppendUint": reflect.ValueOf(strconv.AppendUint), + "Atoi": reflect.ValueOf(strconv.Atoi), + "CanBackquote": reflect.ValueOf(strconv.CanBackquote), + "ErrRange": reflect.ValueOf(&strconv.ErrRange).Elem(), + "ErrSyntax": reflect.ValueOf(&strconv.ErrSyntax).Elem(), + "FormatBool": reflect.ValueOf(strconv.FormatBool), + "FormatComplex": reflect.ValueOf(strconv.FormatComplex), + "FormatFloat": reflect.ValueOf(strconv.FormatFloat), + "FormatInt": reflect.ValueOf(strconv.FormatInt), + "FormatUint": reflect.ValueOf(strconv.FormatUint), + "IntSize": reflect.ValueOf(constant.MakeFromLiteral("64", token.INT, 0)), + "IsGraphic": reflect.ValueOf(strconv.IsGraphic), + "IsPrint": reflect.ValueOf(strconv.IsPrint), + "Itoa": reflect.ValueOf(strconv.Itoa), + "ParseBool": reflect.ValueOf(strconv.ParseBool), + "ParseComplex": reflect.ValueOf(strconv.ParseComplex), + "ParseFloat": reflect.ValueOf(strconv.ParseFloat), + "ParseInt": reflect.ValueOf(strconv.ParseInt), + "ParseUint": reflect.ValueOf(strconv.ParseUint), + "Quote": reflect.ValueOf(strconv.Quote), + "QuoteRune": reflect.ValueOf(strconv.QuoteRune), + "QuoteRuneToASCII": reflect.ValueOf(strconv.QuoteRuneToASCII), + "QuoteRuneToGraphic": reflect.ValueOf(strconv.QuoteRuneToGraphic), + "QuoteToASCII": reflect.ValueOf(strconv.QuoteToASCII), + "QuoteToGraphic": reflect.ValueOf(strconv.QuoteToGraphic), + "QuotedPrefix": reflect.ValueOf(strconv.QuotedPrefix), + "Unquote": reflect.ValueOf(strconv.Unquote), + "UnquoteChar": reflect.ValueOf(strconv.UnquoteChar), + + // type definitions + "NumError": reflect.ValueOf((*strconv.NumError)(nil)), + } +} diff --git a/devtools/internal/interpreter/lib/go1_20_strings.go b/devtools/internal/interpreter/lib/go1_20_strings.go new file mode 100644 index 0000000..d6dc91a --- /dev/null +++ b/devtools/internal/interpreter/lib/go1_20_strings.go @@ -0,0 +1,70 @@ +// Code generated by 'yaegi extract strings'. DO NOT EDIT. + +package lib + +import ( + "reflect" + "strings" +) + +func init() { + Symbols["strings/strings"] = map[string]reflect.Value{ + // function, constant and variable definitions + "Clone": reflect.ValueOf(strings.Clone), + "Compare": reflect.ValueOf(strings.Compare), + "Contains": reflect.ValueOf(strings.Contains), + "ContainsAny": reflect.ValueOf(strings.ContainsAny), + "ContainsRune": reflect.ValueOf(strings.ContainsRune), + "Count": reflect.ValueOf(strings.Count), + "Cut": reflect.ValueOf(strings.Cut), + "CutPrefix": reflect.ValueOf(strings.CutPrefix), + "CutSuffix": reflect.ValueOf(strings.CutSuffix), + "EqualFold": reflect.ValueOf(strings.EqualFold), + "Fields": reflect.ValueOf(strings.Fields), + "FieldsFunc": reflect.ValueOf(strings.FieldsFunc), + "HasPrefix": reflect.ValueOf(strings.HasPrefix), + "HasSuffix": reflect.ValueOf(strings.HasSuffix), + "Index": reflect.ValueOf(strings.Index), + "IndexAny": reflect.ValueOf(strings.IndexAny), + "IndexByte": reflect.ValueOf(strings.IndexByte), + "IndexFunc": reflect.ValueOf(strings.IndexFunc), + "IndexRune": reflect.ValueOf(strings.IndexRune), + "Join": reflect.ValueOf(strings.Join), + "LastIndex": reflect.ValueOf(strings.LastIndex), + "LastIndexAny": reflect.ValueOf(strings.LastIndexAny), + "LastIndexByte": reflect.ValueOf(strings.LastIndexByte), + "LastIndexFunc": reflect.ValueOf(strings.LastIndexFunc), + "Map": reflect.ValueOf(strings.Map), + "NewReader": reflect.ValueOf(strings.NewReader), + "NewReplacer": reflect.ValueOf(strings.NewReplacer), + "Repeat": reflect.ValueOf(strings.Repeat), + "Replace": reflect.ValueOf(strings.Replace), + "ReplaceAll": reflect.ValueOf(strings.ReplaceAll), + "Split": reflect.ValueOf(strings.Split), + "SplitAfter": reflect.ValueOf(strings.SplitAfter), + "SplitAfterN": reflect.ValueOf(strings.SplitAfterN), + "SplitN": reflect.ValueOf(strings.SplitN), + "Title": reflect.ValueOf(strings.Title), + "ToLower": reflect.ValueOf(strings.ToLower), + "ToLowerSpecial": reflect.ValueOf(strings.ToLowerSpecial), + "ToTitle": reflect.ValueOf(strings.ToTitle), + "ToTitleSpecial": reflect.ValueOf(strings.ToTitleSpecial), + "ToUpper": reflect.ValueOf(strings.ToUpper), + "ToUpperSpecial": reflect.ValueOf(strings.ToUpperSpecial), + "ToValidUTF8": reflect.ValueOf(strings.ToValidUTF8), + "Trim": reflect.ValueOf(strings.Trim), + "TrimFunc": reflect.ValueOf(strings.TrimFunc), + "TrimLeft": reflect.ValueOf(strings.TrimLeft), + "TrimLeftFunc": reflect.ValueOf(strings.TrimLeftFunc), + "TrimPrefix": reflect.ValueOf(strings.TrimPrefix), + "TrimRight": reflect.ValueOf(strings.TrimRight), + "TrimRightFunc": reflect.ValueOf(strings.TrimRightFunc), + "TrimSpace": reflect.ValueOf(strings.TrimSpace), + "TrimSuffix": reflect.ValueOf(strings.TrimSuffix), + + // type definitions + "Builder": reflect.ValueOf((*strings.Builder)(nil)), + "Reader": reflect.ValueOf((*strings.Reader)(nil)), + "Replacer": reflect.ValueOf((*strings.Replacer)(nil)), + } +} diff --git a/devtools/internal/interpreter/lib/lib.go b/devtools/internal/interpreter/lib/lib.go new file mode 100644 index 0000000..3c3f7a2 --- /dev/null +++ b/devtools/internal/interpreter/lib/lib.go @@ -0,0 +1,5 @@ +package lib + +import "reflect" + +var Symbols = map[string]map[string]reflect.Value{} diff --git a/devtools/internal/terminal/terminal.go b/devtools/internal/terminal/terminal.go new file mode 100644 index 0000000..9050cde --- /dev/null +++ b/devtools/internal/terminal/terminal.go @@ -0,0 +1,40 @@ +package terminal + +import ( + "errors" + "io" + "os" + + "github.com/peterh/liner" +) + +var linerState *liner.State + +var Commands = make(chan string, 1) +var CommandExecuted = make(chan struct{}, 1) + +func StartReadingCommands() { + linerState = liner.NewLiner() + linerState.SetCtrlCAborts(true) + + go func() { + for { + cmd, err := linerState.Prompt("> ") + if err != nil { + if err == io.EOF || errors.Is(err, liner.ErrPromptAborted) { + linerState.Close() + os.Exit(0) + } + linerState.Close() + panic(err) + } + Commands <- cmd + <-CommandExecuted + linerState.AppendHistory(cmd) + } + }() +} + +func StopReadingCommandsFromStdin() { + linerState.Close() +} diff --git a/devtools/scripting.go b/devtools/scripting.go new file mode 100644 index 0000000..c4982ab --- /dev/null +++ b/devtools/scripting.go @@ -0,0 +1,25 @@ +package devtools + +import "github.com/elgopher/pi/devtools/internal/interpreter" + +// ExportFunc exports your own function which then can be used +// in terminal. +// +// f must be function. +func ExportFunc(name string, f any) { + interpreter.ExportFunc(name, f) +} + +// ExportVar exports your own variable which then can be used +// in terminal. +// +// name is a variable name. +// +// v must be a pointer to variable. +func ExportVar(name string, v any) { + interpreter.ExportVar(name, v) +} + +func ExportType[T any](name string) { + interpreter.ExportType[T](name) +} diff --git a/devtools/update.go b/devtools/update.go index 311651f..6ea228e 100644 --- a/devtools/update.go +++ b/devtools/update.go @@ -5,6 +5,7 @@ package devtools import ( "github.com/elgopher/pi/devtools/internal/inspector" + "github.com/elgopher/pi/devtools/internal/interpreter" "github.com/elgopher/pi/key" ) @@ -18,6 +19,7 @@ func updateDevTools() { } if gamePaused { + interpreter.EvaluateNextCommand() inspector.Update() } } diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index c86f71d..9afcc9e 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -42,6 +42,7 @@ * [ ] drawing on the screen using Pi functions * [x] Pset, shapes * [ ] Spr, Print + * [x] scripting - writing Go code live when game is running * [ ] palette inspector * [ ] display, draw palette * [ ] sprite-sheet editor diff --git a/examples/hello/main.go b/examples/hello/main.go index cb66157..52fd2fc 100644 --- a/examples/hello/main.go +++ b/examples/hello/main.go @@ -15,6 +15,10 @@ import ( var resources embed.FS func main() { + devtools.ExportFunc("circ", circ) // TODO Move to new example + devtools.ExportVar("state", &state) // TODO Move to new example + devtools.ExportType[Struct]("Struct") // TODO Move to new example + devtools.ExportFunc("funcAcceptingStruct", funcAcceptingStruct) // TODO Move to new example // Tell Pi to load resources embedded inside binary: pi.Load(resources) // Pi runs the game in a loop. 30 times per second, Pi @@ -37,3 +41,21 @@ func main() { // Run game with devtools (Hit F12 to show screen inspector) devtools.MustRun(ebitengine.Run) } + +// TODO Move to new example +var state = make([]uint8, 256) + +// TODO Move to new example +func circ(x, y int) { + pi.Circ(x, y, 3, state[0]) +} + +// TODO MOVE to new example +type Struct struct { + Field string +} + +// TODO MOVE to new example +func funcAcceptingStruct(p Struct) { + +} diff --git a/examples/shapes/main.go b/examples/shapes/main.go index bde93cc..420b221 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -61,7 +61,6 @@ func main() { pi.Load(resources) pi.Draw = func() { pi.Cls() - // change the shape if right mouse button was just pressed if pi.MouseBtnp(pi.MouseRight) { current++ diff --git a/examples/yaegi/yaegi.go b/examples/yaegi/yaegi.go new file mode 100644 index 0000000..760aaf7 --- /dev/null +++ b/examples/yaegi/yaegi.go @@ -0,0 +1,108 @@ +package main + +import ( + "fmt" + "reflect" + + "github.com/traefik/yaegi/interp" + "github.com/traefik/yaegi/stdlib" + + "github.com/elgopher/pi" + "github.com/elgopher/pi/image" +) + +func main() { + of := reflect.TypeOf(pi.Spr) + fmt.Println(of.PkgPath()) + + i := interp.New(interp.Options{ + Unrestricted: false, + }) + err := i.Use(map[string]map[string]reflect.Value{ + "github.com/elgopher/pi/pi": { + "Spr": reflect.ValueOf(pi.Spr), + "Cls": reflect.ValueOf(pi.Cls), + "Pset": reflect.ValueOf(pi.Pset), + "DrawPalette": reflect.ValueOf(&pi.DrawPalette).Elem(), + "Palette": reflect.ValueOf(&pi.Palette).Elem(), + "Draw": reflect.ValueOf(&pi.Draw).Elem(), + }, + "image/image": { + "RGB": reflect.ValueOf((*image.RGB)(nil)), + }, + }) + fmt.Println(err) + i.Use(stdlib.Symbols) + + res, err := i.Eval(`import "image"`) + fmt.Println(err) + + res, err = i.Eval(`import "fmt"`) + fmt.Println(err) + + res, err = i.Eval(`import "github.com/elgopher/pi"`) + fmt.Println(err) + + res, err = i.Eval("pi.Palette[0]=image.RGB{R:1,G:2,B:3}") + fmt.Println(res, err) + + res, err = i.Eval("pi.Palette") + fmt.Println(res, err) + + res, err = i.Eval(`func placek() { fmt.Println("ok") }`) + fmt.Println(res, err) + + res, err = i.Eval("placek()") + fmt.Println(err) + + //res, err = i.Eval("pi.Draw = func() { pi.Cls(); pi.Pset(0,1,2); } ") + //switch res.Kind() { + //case reflect.Func: + // fmt.Println(err) + //} + + exportFunc(i, "RectFill", pi.RectFill) + exportFunc(i, "Rect", pi.Rect) + exportVar(i, "DisplayPalette", &pi.DisplayPalette) + + _, err = i.Eval(`import . "main"`) + fmt.Println(err) + + res, err = i.Eval("RectFill(0,0,10,10,1)") + fmt.Println(err) + // + //res, err = i.Eval("white := byte(7); Rect(0,0,10,10,1)") + //fmt.Println(err) + + pi.DisplayPalette[0] = 1 + + res, err = i.Eval("DisplayPalette[0]=2") + fmt.Println(res, err) + + fmt.Println(pi.DisplayPalette[0]) + + //ebitengine.MustRun() + +} + +func exportFunc(i *interp.Interpreter, name string, f any) { + err := i.Use(interp.Exports{ + "main/main": map[string]reflect.Value{ + name: reflect.ValueOf(f), + }, + }) + if err != nil { + panic(err) + } +} + +func exportVar[T any](i *interp.Interpreter, name string, f *T) { + err := i.Use(interp.Exports{ + "main/main": map[string]reflect.Value{ + name: reflect.ValueOf(f).Elem(), + }, + }) + if err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index 0ccccf5..af00a05 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/elgopher/pi -go 1.18 +go 1.20 require ( github.com/hajimehoshi/ebiten/v2 v2.5.6 + github.com/peterh/liner v1.2.2 github.com/stretchr/testify v1.8.4 + github.com/traefik/yaegi v0.15.1 ) require ( @@ -13,6 +15,7 @@ require ( github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect github.com/hajimehoshi/oto/v2 v2.4.1 // indirect github.com/jezek/xgb v1.1.0 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect golang.org/x/image v0.6.0 // indirect diff --git a/go.sum b/go.sum index b07958c..be80a1b 100644 --- a/go.sum +++ b/go.sum @@ -11,10 +11,17 @@ github.com/hajimehoshi/oto/v2 v2.4.1 h1:iTfZSulqdmQ5Hh4tVyVzNnK3aA4SgjbDapSM0YH3 github.com/hajimehoshi/oto/v2 v2.4.1/go.mod h1:guyF8uIgSrchrKewS1E6Xyx7joUbKOi4g9W7vpcYBSc= github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk= github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= +github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/traefik/yaegi v0.15.1 h1:YA5SbaL6HZA0Exh9T/oArRHqGN2HQ+zgmCY7dkoTXu4= +github.com/traefik/yaegi v0.15.1/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -44,6 +51,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pixmap.go b/pixmap.go index 661da32..f536d42 100644 --- a/pixmap.go +++ b/pixmap.go @@ -3,6 +3,8 @@ package pi +import "fmt" + // PixMap is a generic data structure for manipulating any kind of pixel data - screen, sprite-sheet etc. // PixMap uses a single byte (8 bits) for storing single color/pixel. This means that max 256 colors // can be used. PixMap can also be used for maps which not necessary contain pixel colors, such as world map @@ -21,6 +23,10 @@ type PixMap struct { wholeLinePix []byte } +func (p PixMap) String() string { + return fmt.Sprintf("{pix:(%d)[%d %d ...], width:%d, height: %d,clip:%+v}", len(p.pix), p.pix[0], p.pix[1], p.width, p.height, p.clip) +} + // NewPixMap creates new instance of PixMap with specified size. // Width and height cannot be negative. func NewPixMap(width, height int) PixMap {