-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathrepl.go
205 lines (181 loc) · 5.72 KB
/
repl.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright 2013-2015 Rocky Bernstein. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package go-fish 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.
//
// Inside this package we provide two front-ends, one which uses GNU
// Readline (http://code.google.com/p/go-gnureadline) and one which doesn't.
// Feel free to add patches to support other kinds of readline support.
//
package repl
// We separate this from the main package so that the main package
// can provide its own readline function. This could be, for example,
// GNU Readline, lineedit or something else.
import (
"bufio"
"flag"
"fmt"
"go/ast"
"io"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"github.com/rocky/eval"
)
var Highlight = flag.Bool("highlight", true, `use syntax highlighting in output`)
// Maxwidth is the size of the line. We will try to wrap text that is
// longer than this. It like the COLUMNS environment variable
var Maxwidth int
// ReadLineFnType is function signature for a common read line
// interface that we support.
type ReadLineFnType func(prompt string, add_history ... bool) (string, error)
var readLineFn ReadLineFnType
type InspectFnType func(a ...interface{}) string
var initial_cwd string
// GOFISH_RESTART_CMD is a string that was used to invoke gofish.
//If we want to restart gofish, this is what we'll use.
var GOFISH_RESTART_CMD string
// HistoryFile returns a string file name to use for saving command
// history entries.
func HistoryFile(history_basename string) string {
home_dir := os.Getenv("HOME")
if home_dir == "" {
// FIXME: also try ~ ?
fmt.Println("ignoring history file; environment variable HOME not set")
return ""
}
history_file := filepath.Join(home_dir, history_basename)
if fi, err := os.Stat(history_file); err != nil {
fmt.Println("No history file found to read in: ", err.Error())
} else {
if fi.IsDir() {
Errmsg("Ignoring history file %s; is a directory, should be a file",
history_file)
return ""
}
}
return history_file
}
// Input is a workaround for the fact that ReadLineFnType doesn't have
// an input parameter, but SimpleReadLine below needs a
// *bufioReader. So set this global variable beforehand if you are using
// SimpleReadLine.
var Input *bufio.Reader
// SimpleReadLine is simple replacement for GNU readline.
// prompt is the command prompt to print before reading input.
// add_history is ignored, but provided as a parameter to match
// those readline interfaces that do support saving command history.
func SimpleReadLine(prompt string, add_history ... bool) (string, error) {
fmt.Printf(prompt)
line, err := Input.ReadString('\n')
if err == nil {
line = strings.TrimRight(line, "\r\n")
}
return line, err
}
func SimpleInspect(a ...interface{}) string {
value := a[0].(reflect.Value)
return eval.Inspect(value)
}
func init() {
widthstr := os.Getenv("COLUMNS")
initial_cwd, _ = os.Getwd()
GOFISH_RESTART_CMD = os.Getenv("GOFISH_RESTART_CMD")
if len(widthstr) == 0 {
Maxwidth = 80
} else if i, err := strconv.Atoi(widthstr); err == nil {
Maxwidth = i
}
}
// MakeEvalEnv creates an environment to use in evaluation. The
// environment is exactly that environment needed by eval
// automatically extracted from the package eval
// (http://github.com/0xfaded/eval).
func MakeEvalEnv() *eval.SimpleEnv {
return EvalEnvironment()
}
// LeaveREPL is set when we want to quit.
var LeaveREPL bool = false
// ExitCode is the exit code this program will set on exit.
var ExitCode int = 0
// Env is the evaluation environment we are working with.
var Env *eval.SimpleEnv
// REPL is the read, eval, and print loop.
func REPL(env *eval.SimpleEnv, readLineFn ReadLineFnType, inspectFn InspectFnType) {
var err error
// A place to store result values of expressions entered
// interactively
results := make([]interface{}, 0, 10)
env.Vars["results"] = reflect.ValueOf(&results)
Env = env
exprs := 0
line, err := readLineFn("gofish> ", true)
for true {
if err != nil {
if err == io.EOF { break }
panic(err)
}
if wasProcessed(line) {
if LeaveREPL {break}
line, err = readLineFn("gofish> ", true)
continue
}
if stmt, err := eval.ParseStmt(line); err != nil {
if pair := eval.FormatErrorPos(line, err.Error()); len(pair) == 2 {
Msg(pair[0])
Msg(pair[1])
}
Errmsg("parse error: %s", err)
} else if expr, ok := stmt.(*ast.ExprStmt); ok {
if cexpr, errs := eval.CheckExpr(expr.X, env); len(errs) != 0 {
for _, cerr := range errs {
Errmsg("%v", cerr)
}
} else if vals, err := eval.EvalExpr(cexpr, env); err != nil {
Errmsg("panic: %s", err)
} else if len(vals) == 0 {
fmt.Printf("Kind=Slice\nvoid\n")
} else if len(vals) == 1 {
value := (vals)[0]
if value.IsValid() {
kind := value.Kind().String()
typ := value.Type().String()
if typ != kind {
Msg("Kind = %v", kind)
Msg("Type = %v", typ)
} else {
Msg("Kind = Type = %v", kind)
}
Msg("results[%d] = %s", exprs, inspectFn(value))
exprs += 1
results = append(results, (vals)[0].Interface())
} else {
Msg("%s", value)
}
} else {
Msg("Kind = Multi-Value")
size := len(vals)
for i, v := range vals {
fmt.Printf("%s", inspectFn(v))
if i < size-1 { fmt.Printf(", ") }
}
Msg("")
exprs += 1
results = append(results, vals)
}
} else {
if cstmt, errs := eval.CheckStmt(stmt, env); len(errs) != 0 {
for _, cerr := range errs {
Errmsg("%v", cerr)
}
} else if _, err := eval.InterpStmt(cstmt, env); err != nil {
Errmsg("panic: %s", err)
}
}
line, err = readLineFn("gofish> ", true)
}
}