Skip to content

Commit

Permalink
Adding println, suppress all log() output when -quiet is passed t…
Browse files Browse the repository at this point in the history
…o the interpreter (log level >= error) (#90)

* Adding println

* handle println() correctly

* suppress all log() in -quiet mode
  • Loading branch information
ldemailly authored Aug 2, 2024
1 parent 9cfa42a commit bedeb61
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 68 deletions.
98 changes: 58 additions & 40 deletions eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type State struct {
env *object.Environment
Out io.Writer
LogOut io.Writer
NoLog bool // turn log() into print() (for EvalString)
NoLog bool // turn log() into println() (for EvalString)
cache Cache
}

Expand Down Expand Up @@ -213,61 +213,79 @@ func (s *State) evalMapLiteral(node *ast.MapLiteral) object.Object {
return result
}

func (s *State) evalPrintLogError(node *ast.Builtin) object.Object {
doLog := (node.Type() == token.LOG)
if doLog && (log.GetLogLevel() >= log.Error) {
return object.NULL
}
buf := strings.Builder{}
for i, v := range node.Parameters {
if i > 0 {
buf.WriteString(" ")
}
r := s.evalInternal(v)
if isString := r.Type() == object.STRING; isString {
buf.WriteString(r.(object.String).Value)
} else {
buf.WriteString(r.Inspect())
}
}
if node.Type() == token.ERROR {
return object.Error{Value: buf.String()}
}
if (s.NoLog && doLog) || node.Type() == token.PRINTLN {
buf.WriteRune('\n') // log() has a implicit newline when using log.Xxx, print() doesn't, println() does.
}
if doLog && !s.NoLog {
// Consider passing the arguments to log instead of making a string concatenation.
log.Printf("%s", buf.String())
} else {
where := s.Out
if doLog {
where = s.LogOut
}
_, err := where.Write([]byte(buf.String()))
if err != nil {
log.Warnf("print: %v", err)
}
}
return object.NULL
}

func (s *State) evalBuiltin(node *ast.Builtin) object.Object {
// all take 1 arg exactly except print and log which take 1+.
t := node.Type()
min := 1
varArg := (t == token.PRINT || t == token.LOG || t == token.ERROR)
if oerr := ArgCheck(node.Literal(), 1, varArg, node.Parameters); oerr != nil {
if t == token.PRINTLN {
min = 0
varArg = true
}
if oerr := ArgCheck(node.Literal(), min, varArg, node.Parameters); oerr != nil {
return *oerr
}
if t == token.QUOTE {
return s.quote(node.Parameters[0])
}
val := s.evalInternal(node.Parameters[0])
rt := val.Type()
if rt == object.ERROR {
return val
var val object.Object
var rt object.Type
if min > 0 {
val = s.evalInternal(node.Parameters[0])
rt = val.Type()
if rt == object.ERROR {
return val
}
}
arr, _ := val.(object.Array)
switch t { //nolint:exhaustive // we have default, only 2 cases.
switch t { //nolint:exhaustive // we have defaults and covering all the builtins.
case token.ERROR:
fallthrough
case token.PRINT:
fallthrough
case token.PRINTLN:
fallthrough
case token.LOG:
buf := strings.Builder{}
for i, v := range node.Parameters {
if i > 0 {
buf.WriteString(" ")
}
r := s.evalInternal(v)
if isString := r.Type() == object.STRING; isString {
buf.WriteString(r.(object.String).Value)
} else {
buf.WriteString(r.Inspect())
}
}
if node.Type() == token.ERROR {
return object.Error{Value: buf.String()}
}
doLog := node.Type() != token.PRINT
if s.NoLog && doLog {
buf.WriteRune('\n') // log() has a implicit newline when using log.Xxx, print() doesn't.
}
if doLog && !s.NoLog {
// Consider passing the arguments to log instead of making a string concatenation.
log.Printf("%s", buf.String())
} else {
where := s.Out
if doLog {
where = s.LogOut
}
_, err := where.Write([]byte(buf.String()))
if err != nil {
log.Warnf("print: %v", err)
}
}
return object.NULL
return s.evalPrintLogError(node)
case token.FIRST:
if rt != object.ARRAY {
break
Expand Down
4 changes: 2 additions & 2 deletions examples/sample.gr
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ a=[fact(5), "abc", 76-3] // array can contain different types

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

print("m is:", m, "\n") // stdout print
print("Outputting a smiley: 😀\n")
println("m is:", m) // stdout print
println("Outputting a smiley: 😀")

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
// could also have been m["key"][0]
Expand Down
10 changes: 5 additions & 5 deletions main_test.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ stdout '^1\n3$'
!grol -c 'm=macro(){};m()'
stdout '^<err: macro should return Quote. got=object.Null \({}\)>\n$'

# quiet mode and fast fibbonaci
grol -quiet -c 'fib=func(x){if x<=1 {x} else {fib(x-1)+fib(x-2)}}; fib(92)'
# quiet mode and fast fibbonaci (log won't show)
grol -quiet -c 'fib=func(x){log("fib",x);if x<=1 {x} else {fib(x-1)+fib(x-2)}}; fib(92)'
stdout '^7540113804746346429\n$'
!stdout '\n\n'
!stderr .
Expand Down Expand Up @@ -91,8 +91,8 @@ a=[fact(5), "abc", 76-3] // array can contain different types

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

print("m is:", m, ".\n") // stdout print
print("Outputting a smiley: 😀\n")
println("m is:", m) // stdout print
println("Outputting a smiley: 😀")

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
// could also have been m["key"][0]
Expand All @@ -112,7 +112,7 @@ fib = func(x) {
fib(50)
-- sample_test_stdout --
macro test: greater
m is: {73:29,"key":[120,"abc",73]} .
m is: {73:29,"key":[120,"abc",73]}
Outputting a smiley: 😀
120
-- fib50_stdout --
Expand Down
1 change: 1 addition & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func New(l *lexer.Lexer) *Parser {
p.registerPrefix(token.LINECOMMENT, p.parseComment)
p.registerPrefix(token.BLOCKCOMMENT, p.parseComment)
p.registerPrefix(token.PRINT, p.parseBuiltin)
p.registerPrefix(token.PRINTLN, p.parseBuiltin)
p.registerPrefix(token.LOG, p.parseBuiltin)
p.registerPrefix(token.MACRO, p.parseMacroLiteral)
p.registerPrefix(token.ERROR, p.parseBuiltin)
Expand Down
29 changes: 15 additions & 14 deletions repl/repl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ fact=func(n) { // function
n*fact(n-1)
}
result = fact(5)
print("Factorial of 5 is", result, "\n") // print to stdout
println("Factorial of 5 is", result) // print to stdout
result`
expected := `called fact 5
called fact 4
called fact 3
called fact 2
called fact 1
Factorial of 5 is 120` + " \n120\n" // there is an extra space before \n that vscode wants to remove
Factorial of 5 is 120` + "\n120\n" // there is an extra space before \n that vscode wants to remove
if got, errs, _ := repl.EvalString(s); got != expected || len(errs) > 0 {
t.Errorf("EvalString() got %v\n---\n%s\n---want---\n%s\n---", errs, got, expected)
}
Expand All @@ -33,32 +33,33 @@ func TestEvalMemoPrint(t *testing.T) {
s := `
fact=func(n) {
log("logger fact", n) // should be actual executions of the function only
print("print fact", n, ".\n") // should get recorded
println("print fact", n) // should get recorded
if (n<=1) {
return 1
}
n*self(n-1)
}
fact(3)
print("---\n")
print("---")
println()
result = fact(5)
print("Factorial of 5 is", result, ".\n") // print to stdout
println("Factorial of 5 is", result) // print to stdout
result`
expected := `logger fact 3
logger fact 2
logger fact 1
print fact 3 .
print fact 2 .
print fact 1 .
print fact 3
print fact 2
print fact 1
---
logger fact 5
logger fact 4
print fact 5 .
print fact 4 .
print fact 3 .
print fact 2 .
print fact 1 .
Factorial of 5 is 120 .
print fact 5
print fact 4
print fact 3
print fact 2
print fact 1
Factorial of 5 is 120
120
`
if got, errs, _ := repl.EvalString(s); got != expected || len(errs) > 0 {
Expand Down
1 change: 1 addition & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const (
FIRST
REST
PRINT
PRINTLN
LOG
ERROR

Expand Down
13 changes: 7 additions & 6 deletions token/type_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion wasm/grol_wasm.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
<div>
<label for="input">Edit the sample/Enter your GROL code here:</label>
<textarea id="input" rows="12" cols="80">
print("Outputting a smiley: 😀\n")
println("Outputting a smiley: 😀")
fact=func(n) { // function example
log("called fact ", n) // log output
// parenthesis are optional:
Expand Down

0 comments on commit bedeb61

Please sign in to comment.