Skip to content

Commit a320c37

Browse files
committed
Start of chapter 3, modified, eval literals and repl
1 parent 75e5312 commit a320c37

File tree

6 files changed

+168
-4
lines changed

6 files changed

+168
-4
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ gorpl
2828

2929
.DS_Store
3030
__*
31+
*_string.go

.vscode/launch.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Launch Package",
6+
"type": "go",
7+
"request": "launch",
8+
"mode": "auto",
9+
"console": "integratedTerminal", // so we can type input
10+
"program": "${fileDirname}"
11+
}
12+
]
13+
}

eval/eval.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package eval
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/ldemailly/gorpl/ast"
7+
"github.com/ldemailly/gorpl/object"
8+
)
9+
10+
func Eval(node any) object.Object {
11+
switch node := node.(type) {
12+
// Statements
13+
case *ast.Program:
14+
return evalStatements(node.Statements)
15+
16+
case *ast.ExpressionStatement:
17+
return Eval(node.Val)
18+
19+
// Expressions
20+
case *ast.IntegerLiteral:
21+
return &object.Integer{Value: node.Val}
22+
}
23+
24+
return &object.Error{Value: fmt.Sprintf("unknown node type: %T", node)}
25+
}
26+
27+
func evalStatements(stmts []ast.Node) object.Object {
28+
var result object.Object
29+
result = &object.Null{} // no crash when empty program.
30+
31+
for _, statement := range stmts {
32+
result = Eval(statement)
33+
}
34+
35+
return result
36+
}

eval/eval_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package eval_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ldemailly/gorpl/eval"
7+
"github.com/ldemailly/gorpl/lexer"
8+
"github.com/ldemailly/gorpl/object"
9+
"github.com/ldemailly/gorpl/parser"
10+
)
11+
12+
func TestEvalIntegerExpression(t *testing.T) {
13+
tests := []struct {
14+
input string
15+
expected int64
16+
}{
17+
{"5", 5},
18+
{"10", 10},
19+
}
20+
21+
for _, tt := range tests {
22+
evaluated := testEval(tt.input)
23+
testIntegerObject(t, evaluated, tt.expected)
24+
}
25+
}
26+
27+
func testEval(input string) object.Object {
28+
l := lexer.New(input)
29+
p := parser.New(l)
30+
program := p.ParseProgram()
31+
32+
return eval.Eval(program)
33+
}
34+
35+
func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
36+
result, ok := obj.(*object.Integer)
37+
if !ok {
38+
t.Errorf("object is not Integer. got=%T (%+v)", obj, obj)
39+
return false
40+
}
41+
if result.Value != expected {
42+
t.Errorf("object has wrong value. got=%d, want=%d",
43+
result.Value, expected)
44+
return false
45+
}
46+
47+
return true
48+
}

object/object.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package object
2+
3+
import (
4+
"strconv"
5+
)
6+
7+
type Type uint8
8+
9+
type Object interface {
10+
Type() Type
11+
Inspect() string
12+
}
13+
14+
const (
15+
UNKNOWN Type = iota
16+
INTEGER
17+
BOOLEAN
18+
NULL
19+
ERROR
20+
LAST
21+
)
22+
23+
//go:generate stringer -type=Type
24+
var _ = LAST.String() // force compile error if go generate is missing.
25+
26+
type Integer struct {
27+
Value int64
28+
}
29+
30+
func (i *Integer) Inspect() string {
31+
return strconv.FormatInt(i.Value, 10)
32+
}
33+
34+
func (i *Integer) Type() Type {
35+
return INTEGER
36+
}
37+
38+
type Boolean struct {
39+
Value bool
40+
}
41+
42+
func (b *Boolean) Type() Type {
43+
return BOOLEAN
44+
}
45+
46+
func (b *Boolean) Inspect() string {
47+
return strconv.FormatBool(b.Value)
48+
}
49+
50+
type Null struct{}
51+
52+
func (n *Null) Type() Type { return NULL }
53+
func (n *Null) Inspect() string { return "<null>" }
54+
55+
type Error struct {
56+
Value string // message
57+
}
58+
59+
func (e *Error) Type() Type { return ERROR }
60+
func (e *Error) Inspect() string { return "<err: " + e.Value + ">" }

repl/repl.go

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@ import (
55
"fmt"
66
"io"
77

8+
"fortio.org/log"
9+
"github.com/ldemailly/gorpl/eval"
810
"github.com/ldemailly/gorpl/lexer"
911
"github.com/ldemailly/gorpl/parser"
1012
)
1113

1214
const PROMPT = "$ "
1315

14-
func printParserErrors(out io.Writer, p *parser.Parser) bool {
16+
func logParserErrors(p *parser.Parser) bool {
1517
errors := p.Errors()
1618
if len(errors) == 0 {
1719
return false
1820
}
1921

20-
fmt.Fprintf(out, "parser has %d error(s)\n", len(errors))
22+
log.Critf("parser has %d error(s)", len(errors))
2123
for _, msg := range errors {
22-
fmt.Fprintf(out, "parser error: %s\n", msg)
24+
log.Errf("parser error: %s", msg)
2325
}
2426
return true
2527
}
@@ -39,9 +41,13 @@ func Start(in io.Reader, out io.Writer) {
3941

4042
p := parser.New(l)
4143
program := p.ParseProgram()
42-
if printParserErrors(out, p) {
44+
if logParserErrors(p) {
4345
continue
4446
}
47+
fmt.Print("== Parse ==> ")
4548
fmt.Println(program.String())
49+
fmt.Print("== Eval ==> ")
50+
obj := eval.Eval(program)
51+
fmt.Println(obj.Inspect())
4652
}
4753
}

0 commit comments

Comments
 (0)