commit 4da9d4fc0e5ca1e1e2420536976b9644d19cc346
parent 073b20492e0f7bb8c9b8e93b013a8ac68e66cd6d
Author: bsandro <email@bsandro.tech>
Date: Sun, 26 Jun 2022 00:25:51 +0300
object.Context
Diffstat:
4 files changed, 85 insertions(+), 31 deletions(-)
diff --git a/eval/eval.go b/eval/eval.go
@@ -31,50 +31,58 @@ func isError(obj object.Object) bool {
return false
}
-func Eval(node ast.Node) object.Object {
+func Eval(node ast.Node, ctx *object.Context) object.Object {
switch node := node.(type) {
case *ast.Program:
- return evalProgram(node)
+ return evalProgram(node, ctx)
case *ast.ExpressionStatement:
- return Eval(node.Expression)
+ return Eval(node.Expression, ctx)
case *ast.IntegerLiteral:
return &object.Integer{Value: node.Value}
case *ast.Boolean:
return boolToObject(node.Value)
case *ast.PrefixExpression:
- right := Eval(node.Right)
+ right := Eval(node.Right, ctx)
if isError(right) {
return right
}
- return evalPrefixExpression(node.Operator, right)
+ return evalPrefixExpression(node.Operator, right, ctx)
case *ast.InfixExpression:
- left := Eval(node.Left)
+ left := Eval(node.Left, ctx)
if isError(left) {
return left
}
- right := Eval(node.Right)
+ right := Eval(node.Right, ctx)
if isError(right) {
return right
}
- return evalInfixExpression(node.Operator, left, right)
+ return evalInfixExpression(node.Operator, left, right, ctx)
case *ast.BlockStatement:
- return evalBlockStatement(node)
+ return evalBlockStatement(node, ctx)
case *ast.IfExpression:
- return evalIfExpression(node)
+ return evalIfExpression(node, ctx)
case *ast.ReturnStatement:
- value := Eval(node.ReturnValue)
+ value := Eval(node.ReturnValue, ctx)
if isError(value) {
return value
}
return &object.ReturnValue{Value: value}
+ case *ast.LetStatement:
+ value := Eval(node.Value, ctx)
+ if isError(value) {
+ return value
+ }
+ ctx.Set(node.Name.Value, value)
+ case *ast.Identifier:
+ return evalIdentifier(node, ctx)
}
return nil
}
-func evalStatements(statements []ast.Statement) object.Object {
+func evalStatements(statements []ast.Statement, ctx *object.Context) object.Object {
var result object.Object
for _, statement := range statements {
- result = Eval(statement)
+ result = Eval(statement, ctx)
if returnValue, ok := result.(*object.ReturnValue); ok {
return returnValue.Value
}
@@ -82,18 +90,18 @@ func evalStatements(statements []ast.Statement) object.Object {
return result
}
-func evalPrefixExpression(operator string, right object.Object) object.Object {
+func evalPrefixExpression(operator string, right object.Object, ctx *object.Context) object.Object {
switch operator {
case "!":
- return evalShriekOperatorExpression(right)
+ return evalShriekOperatorExpression(right, ctx)
case "-":
- return evalMinusPrefixOperatorExpression(right)
+ return evalMinusPrefixOperatorExpression(right, ctx)
default:
return newError("unknown operator: %s%s", operator, right.Type())
}
}
-func evalShriekOperatorExpression(right object.Object) object.Object {
+func evalShriekOperatorExpression(right object.Object, ctx *object.Context) object.Object {
switch right {
case TRUE:
return FALSE
@@ -106,7 +114,7 @@ func evalShriekOperatorExpression(right object.Object) object.Object {
}
}
-func evalMinusPrefixOperatorExpression(right object.Object) object.Object {
+func evalMinusPrefixOperatorExpression(right object.Object, ctx *object.Context) object.Object {
if right.Type() != object.INTEGER_OBJ {
return newError("unknown operator: -%s", right.Type())
}
@@ -114,10 +122,10 @@ func evalMinusPrefixOperatorExpression(right object.Object) object.Object {
return &object.Integer{Value: -value}
}
-func evalInfixExpression(operator string, left, right object.Object) object.Object {
+func evalInfixExpression(operator string, left, right object.Object, ctx *object.Context) object.Object {
switch {
case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
- return evalIntegerInfixExpression(operator, left, right)
+ return evalIntegerInfixExpression(operator, left, right, ctx)
case operator == "==":
return boolToObject(left == right)
case operator == "!=":
@@ -129,7 +137,7 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
}
}
-func evalIntegerInfixExpression(operator string, left, right object.Object) object.Object {
+func evalIntegerInfixExpression(operator string, left, right object.Object, ctx *object.Context) object.Object {
leftValue := left.(*object.Integer).Value
rightValue := right.(*object.Integer).Value
switch operator {
@@ -154,14 +162,14 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje
}
}
-func evalIfExpression(ie *ast.IfExpression) object.Object {
- condition := Eval(ie.Condition)
+func evalIfExpression(ie *ast.IfExpression, ctx *object.Context) object.Object {
+ condition := Eval(ie.Condition, ctx)
if isError(condition) {
return condition
} else if isTruthy(condition) {
- return Eval(ie.Consequence)
+ return Eval(ie.Consequence, ctx)
} else if ie.Alternative != nil {
- return Eval(ie.Alternative)
+ return Eval(ie.Alternative, ctx)
} else {
return NULL
}
@@ -180,10 +188,10 @@ func isTruthy(obj object.Object) bool {
}
}
-func evalProgram(program *ast.Program) object.Object {
+func evalProgram(program *ast.Program, ctx *object.Context) object.Object {
var result object.Object
for _, statement := range program.Statements {
- result = Eval(statement)
+ result = Eval(statement, ctx)
switch result := result.(type) {
case *object.ReturnValue:
return result.Value
@@ -194,10 +202,10 @@ func evalProgram(program *ast.Program) object.Object {
return result
}
-func evalBlockStatement(block *ast.BlockStatement) object.Object {
+func evalBlockStatement(block *ast.BlockStatement, ctx *object.Context) object.Object {
var result object.Object
for _, statement := range block.Statements {
- result = Eval(statement)
+ result = Eval(statement, ctx)
if result != nil {
rt := result.Type()
if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ {
@@ -207,3 +215,11 @@ func evalBlockStatement(block *ast.BlockStatement) object.Object {
}
return result
}
+
+func evalIdentifier(node *ast.Identifier, ctx *object.Context) object.Object {
+ value, ok := ctx.Get(node.Value)
+ if !ok {
+ return newError("identifier not found: " + node.Value)
+ }
+ return value
+}
diff --git a/eval/eval_test.go b/eval/eval_test.go
@@ -31,7 +31,8 @@ func testEval(input string) object.Object {
l := lexer.New(input)
p := parser.New(l)
prg := p.ParseProgram()
- return Eval(prg)
+ ctx := object.NewContext()
+ return Eval(prg, ctx)
}
func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
@@ -162,6 +163,7 @@ func TestErrorHandling(t *testing.T) {
{"-true", "unknown operator: -BOOLEAN"},
{"true+false;", "unknown operator: BOOLEAN + BOOLEAN"},
{"if (10>1) { true + false; }", "unknown operator: BOOLEAN + BOOLEAN"},
+ {"foo", "identifier not found: foo"},
}
for _, tt := range tests {
evaluated := testEval(tt.input)
@@ -175,3 +177,18 @@ func TestErrorHandling(t *testing.T) {
}
}
}
+
+func TestLetStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {"let a = 5; a;", 5},
+ {"let a = 8*8; a;", 64},
+ {"let a = 8; let b=a; b;", 8},
+ {"let a = 5; let b=a; let c = a+b+5; c", 15},
+ }
+ for _, tt := range tests {
+ testIntegerObject(t, testEval(tt.input), tt.expected)
+ }
+}
diff --git a/object/context.go b/object/context.go
@@ -0,0 +1,20 @@
+package object
+
+type Context struct {
+ store map[string]Object
+}
+
+func NewContext() *Context {
+ s := make(map[string]Object)
+ return &Context{store: s}
+}
+
+func (ctx *Context) Get(name string) (Object, bool) {
+ obj, ok := ctx.store[name]
+ return obj, ok
+}
+
+func (ctx *Context) Set(name string, obj Object) Object {
+ ctx.store[name] = obj
+ return obj
+}
diff --git a/repl/repl.go b/repl/repl.go
@@ -13,6 +13,7 @@ const PROMPT = "$> "
func Start(in io.Reader, out io.Writer) {
scanner := bufio.NewScanner(in)
+ ctx := object.NewContext()
for {
fmt.Fprintf(out, PROMPT)
@@ -27,7 +28,7 @@ func Start(in io.Reader, out io.Writer) {
if len(p.Errors()) != 0 {
printParserErrors(out, p.Errors())
}
- evaluated := eval.Eval(prg)
+ evaluated := eval.Eval(prg, ctx)
if evaluated != nil {
io.WriteString(out, evaluated.Inspect())
io.WriteString(out, "\n")