/
gogo.go
156 lines (134 loc) · 3.73 KB
/
gogo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Example repl is a simple REPL (read-eval-print loop) for GO using
// http://github.com/0xfaded/eval to the heavy lifting to implement
// the eval() part.
//
// The intent here is to show how more to use the library, rather than
// be a full-featured REPL.
//
// A more complete REPL including command history, tab completion and
// readline editing is available as a separate package:
// http://github.com/rocky/go-fish
//
// (rocky) My intent here is also to have something that I can debug in
// the ssa-debugger tortoise/gub.sh. Right now that can't handle the
// unsafe package, pointers, and calls to C code. So that let's out
// go-gnureadline and lineedit.
package main
import (
"fmt"
"go/parser"
"os"
"reflect"
"github.com/raff/eval"
"github.com/gobs/cmd"
)
func intro_text() {
fmt.Printf(`=== A simple Go eval REPL ===
The environment is stored in global variable "env".
The last result is stored in a global variable "_".
Enter expressions to be evaluated at the "go>" prompt.
To quit, enter: "quit" or Ctrl-D (EOF).
`)
}
var (
commander = &cmd.Cmd{
Prompt: "go> ",
HistoryFile: ".replhistory",
PreLoop: intro_text,
Default: evalCmd,
EnableShell: true,
}
env = makeEnv()
)
func makeEnv() *eval.Env {
// A couple of things from the fmt package.
var fmt_funcs map[string]reflect.Value = make(map[string]reflect.Value)
fmt_funcs["Println"] = reflect.ValueOf(fmt.Println)
fmt_funcs["Printf"] = reflect.ValueOf(fmt.Printf)
// A stripped down package environment. See
// http://github.com/rocky/go-fish and repl_imports.go for a more
// complete environment.
pkgs := map[string]eval.Pkg{
"fmt": &eval.Env{
Name: "fmt",
Path: "fmt",
Vars: make(map[string]reflect.Value),
Consts: make(map[string]reflect.Value),
Funcs: fmt_funcs,
Types: make(map[string]reflect.Type),
Pkgs: make(map[string]eval.Pkg),
}, "os": &eval.Env{
Name: "os",
Path: "os",
Vars: map[string]reflect.Value{
"Stdout": reflect.ValueOf(&os.Stdout),
"Args": reflect.ValueOf(&os.Args)},
Consts: make(map[string]reflect.Value),
Funcs: make(map[string]reflect.Value),
Types: make(map[string]reflect.Type),
Pkgs: make(map[string]eval.Pkg),
},
}
mainEnv := eval.Env{
Name: ".",
Path: "",
Consts: make(map[string]reflect.Value),
Funcs: make(map[string]reflect.Value),
Types: make(map[string]reflect.Type),
Vars: make(map[string]reflect.Value),
Pkgs: pkgs,
}
return &mainEnv
}
func evalCmd(line string) {
ctx := &eval.Ctx{line}
if expr, err := parser.ParseExpr(line); err != nil {
//
// error parsing
//
if pair := eval.FormatErrorPos(line, err.Error()); len(pair) == 2 {
fmt.Println(pair[0])
fmt.Println(pair[1])
}
fmt.Printf("parse error: %s\n", err)
} else if cexpr, errs := eval.CheckExpr(ctx, expr, env); len(errs) != 0 {
//
// error ...
//
for _, cerr := range errs {
fmt.Printf("%v\n", cerr)
}
} else if vals, _, err := eval.EvalExpr(ctx, cexpr, env); err != nil {
//
// error evaluating expression
//
fmt.Printf("eval error: %s\n", err)
} else if vals == nil {
fmt.Println(vals)
} else {
for i, v := range *vals {
if i > 0 {
fmt.Printf(", ")
}
fmt.Printf("%s", eval.Inspect(v))
}
fmt.Printf("\n")
if len(*vals) == 1 {
env.Vars["_"] = (*vals)[0]
} else {
env.Vars["_"] = reflect.ValueOf(*vals)
}
}
}
func main() {
env.Vars["env"] = reflect.ValueOf(env)
env.Vars["_"] = reflect.ValueOf(nil)
commander.Init()
commander.Add(cmd.Command{
"quit",
`terminate application`,
func(string) (stop bool) {
return true
}})
commander.CmdLoop()
}