Skip to content

Commit dcf30c3

Browse files
committed
added print (stdout) and log (logger/stderr) and new -shared-state option for multi file mode, updated readma
1 parent 470673d commit dcf30c3

File tree

8 files changed

+74
-8
lines changed

8 files changed

+74
-8
lines changed

README.md

+20-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ go install golang.org/x/tools/cmd/stringer@latest
3131
make # for stripped down executable including build tags etc to make it minimal
3232
```
3333

34-
Status: done up to and including 4.5: ie functional int, string and boolean expressions, functions, lambdas, arrays, maps, etc
34+
Status: All done: ie functional int, string and boolean expressions, functions, lambdas, arrays, maps,
35+
print, log, and more
36+
3537

3638
### Reading notes
3739

@@ -94,3 +96,20 @@ Status: done up to and including 4.5: ie functional int, string and boolean expr
9496
- [ ] assignment to maps and arrays
9597

9698
- [ ] for loop
99+
100+
### Usage
101+
102+
```
103+
gorepl 0.15.0 usage:
104+
gorepl [flags] *.gr files to interpret or no arg for stdin repl...
105+
or 1 of the special arguments
106+
gorepl {help|envhelp|version|buildinfo}
107+
flags:
108+
-eval
109+
show eval results (default true)
110+
-parse
111+
show parse tree
112+
-shared-state
113+
All files share same interpreter state (default is new state for each)
114+
```
115+
(excluding logger control, see `gorepl help` for all the flags, of note `-logger-no-color` will turn off colors for gorepl too)

apply2.gr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Another example (see sample.gr)
22
// Variant with accumulation of result down the stack
33

4-
a = [ 1, 3, 5, 7]
4+
a = [ 1, 3, 5, 7] // or run `gorepl -shared-state *.gr` and you can comment this out (a defined in apply.gr)
55

66
apply = func(f, a) {
77
// helper function

eval/eval.go

+33-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package eval
22

33
import (
44
"fmt"
5+
"os"
6+
"strings"
57

68
"fortio.org/log"
79
"github.com/ldemailly/gorepl/ast"
@@ -43,7 +45,13 @@ func (s *State) evalAssignment(right object.Object, node *ast.InfixExpression) o
4345
return right // maybe only if it's a literal?
4446
}
4547

46-
func ArgCheck[T any](msg string, n int, args []T) *object.Error {
48+
func ArgCheck[T any](msg string, n int, vararg bool, args []T) *object.Error {
49+
if vararg {
50+
if len(args) < n {
51+
return &object.Error{Value: fmt.Sprintf("%s: wrong number of arguments. got=%d, want at least %d", msg, len(args), n)}
52+
}
53+
return nil
54+
}
4755
if len(args) != n {
4856
return &object.Error{Value: fmt.Sprintf("%s: wrong number of arguments. got=%d, want=%d", msg, len(args), n)}
4957
}
@@ -164,8 +172,9 @@ func (s *State) evalMapLiteral(node *ast.MapLiteral) object.Object {
164172
}
165173

166174
func (s *State) evalBuiltin(node *ast.Builtin) object.Object {
167-
// so far all 3 have exactly 1 argument.
168-
if oerr := ArgCheck(node.Literal, 1, node.Parameters); oerr != nil {
175+
// all take 1 arg exactly except print and log which take 1+.
176+
varArg := node.Type == token.PRINT || node.Type == token.LOG
177+
if oerr := ArgCheck(node.Literal, 1, varArg, node.Parameters); oerr != nil {
169178
return *oerr
170179
}
171180
val := s.evalInternal(node.Parameters[0])
@@ -175,6 +184,27 @@ func (s *State) evalBuiltin(node *ast.Builtin) object.Object {
175184
}
176185
arr, _ := val.(object.Array)
177186
switch node.Type { //nolint:exhaustive // we have default, only 2 cases.
187+
case token.PRINT:
188+
fallthrough
189+
case token.LOG:
190+
buf := strings.Builder{}
191+
for i, v := range node.Parameters {
192+
if i > 0 {
193+
buf.WriteString(" ")
194+
}
195+
r := s.evalInternal(v)
196+
if isString := r.Type() == object.STRING; isString {
197+
buf.WriteString(r.(object.String).Value)
198+
} else {
199+
buf.WriteString(r.Inspect())
200+
}
201+
}
202+
if node.Type == token.PRINT {
203+
os.Stdout.WriteString(buf.String())
204+
} else {
205+
log.Printf(buf.String())
206+
}
207+
return object.NULL
178208
case token.FIRST:
179209
if rt != object.ARRAY {
180210
break

main.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
func main() {
1414
showParse := flag.Bool("parse", false, "show parse tree")
1515
showEval := flag.Bool("eval", true, "show eval results")
16+
sharedState := flag.Bool("shared-state", false, "All files share same interpreter state (default is new state for each)")
1617
cli.ArgsHelp = "*.gr files to interpret or no arg for stdin repl..."
1718
cli.MaxArgs = -1
1819
cli.Main()
@@ -26,13 +27,18 @@ func main() {
2627
return
2728
}
2829
options.All = true
30+
s := eval.NewState()
2931
for _, file := range flag.Args() {
30-
s := eval.NewState()
3132
f, err := os.Open(file)
3233
if err != nil {
3334
log.Fatalf("%v", err)
3435
}
36+
log.Infof("Running %s", file)
3537
repl.EvalAll(s, f, os.Stdout, options)
3638
f.Close()
39+
if !*sharedState {
40+
s = eval.NewState()
41+
}
3742
}
43+
log.Infof("All done")
3844
}

parser/parser.go

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ func New(l *lexer.Lexer) *Parser {
7676
p.registerPrefix(token.LBRACKET, p.parseArrayLiteral)
7777
p.registerPrefix(token.LBRACE, p.parseMapLiteral)
7878
p.registerPrefix(token.LINECOMMENT, p.parseComment)
79+
p.registerPrefix(token.PRINT, p.parseBuiltin)
80+
p.registerPrefix(token.LOG, p.parseBuiltin)
7981

8082
p.infixParseFns = make(map[token.Type]infixParseFn)
8183
p.registerInfix(token.PLUS, p.parseInfixExpression)

sample.gr

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See also the other *.gr files
44

55
fact=func(n) { // function
6+
log("called fact ", n) // log (timestamped stderr output)
67
if (n<=1) {
78
return 1
89
}
@@ -13,6 +14,8 @@ a=[fact(5), "abc", 76-3] // array can contain different types
1314

1415
m={"key": a, 73: 29} // so do maps
1516

17+
print("m is:", m, "\n") // stdout print
18+
1619
first(m["key"]) // get the value from key from map, which is an array, and the first element of the array is our factorial 5
1720
// could also have been m["key"][0]
1821

token/token.go

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ const (
6464
LEN
6565
FIRST
6666
REST
67+
PRINT
68+
LOG
6769
)
6870

6971
//go:generate stringer -type=Type
@@ -81,6 +83,8 @@ var keywords = map[string]Type{
8183
"len": LEN,
8284
"first": FIRST,
8385
"rest": REST,
86+
"print": PRINT,
87+
"log": LOG,
8488
}
8589

8690
func LookupIdent(ident string) Type {

token/type_string.go

+4-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)