Infix Expression, Prefix Expression, Return Statement.

git-svn-id: https://svn.tlawal.org/svn/monkey@49 f6afcba9-9ef1-4bdd-9b72-7484f5705bac
This commit is contained in:
Tijani Lawal 2022-08-11 05:04:44 +00:00
parent ec01fd1a31
commit 5456f60038
3 changed files with 205 additions and 8 deletions

View File

@ -15,7 +15,7 @@ func Eval(node ast.Node) object.Object {
switch node := node.(type) { switch node := node.(type) {
// Statements // Statements
case *ast.Program: case *ast.Program:
return eval_statements(node.Statements) return eval_program(node)
case *ast.ExpressionStatement: case *ast.ExpressionStatement:
return Eval(node.Expression) return Eval(node.Expression)
@ -31,20 +31,52 @@ func Eval(node ast.Node) object.Object {
right := Eval(node.Right) right := Eval(node.Right)
return eval_prefix_expression(node.Operator, right) return eval_prefix_expression(node.Operator, right)
case *ast.InfixExpression:
left := Eval(node.Left)
right := Eval(node.Right)
return eval_infix_expression(node.Operator, left, right)
case *ast.BlockStatement:
return eval_block_statement(node)
case *ast.IfExpression:
return eval_if_expression(node)
case *ast.ReturnStatement:
val := Eval(node.ReturnValue)
return &object.ReturnValue{Value: val}
} }
return nil return nil
} }
func eval_statements(statements []ast.Statement) object.Object { func eval_program(program *ast.Program) object.Object {
var result object.Object var result object.Object
for _, statement := range statements { for _, statement := range program.Statements {
result = Eval(statement) result = Eval(statement)
if return_value, ok := result.(*object.ReturnValue); ok {
return return_value.Value
}
} }
return result return result
} }
func eval_block_statement(block *ast.BlockStatement) object.Object {
var result object.Object
for _, statement := range block.Statements {
result = Eval(statement)
if result != nil && result.Type() == object.RETURN_VALUE_OBJECT {
return result
}
}
return result
}
func native_bool_to_boolean_object(input bool) *object.Boolean { func native_bool_to_boolean_object(input bool) *object.Boolean {
if input { if input {
return TRUE return TRUE
@ -81,9 +113,86 @@ func eval_bang_operator_expression(right object.Object) object.Object {
} }
func eval_minus_prefix_operator_expression(right object.Object) object.Object { func eval_minus_prefix_operator_expression(right object.Object) object.Object {
if right.Type() != object.INTEGER_OBJECT{ if right.Type() != object.INTEGER_OBJECT {
return NULL return NULL
} }
value := right.(*object.Integer).Value value := right.(*object.Integer).Value
return &object.Integer{Value: -value} return &object.Integer{Value: -value}
} }
func eval_infix_expression(operator string, left object.Object, right object.Object) object.Object {
switch {
case left.Type() == object.INTEGER_OBJECT && right.Type() == object.INTEGER_OBJECT:
return eval_integer_infix_expression(operator, left, right)
case operator == "==":
return native_bool_to_boolean_object(left == right)
case operator == "!=":
return native_bool_to_boolean_object(left != right)
default:
return NULL
}
}
func eval_integer_infix_expression(operator string, left object.Object, right object.Object) object.Object {
left_value := left.(*object.Integer).Value
right_value := right.(*object.Integer).Value
switch operator {
case "+":
return &object.Integer{Value: left_value + right_value}
case "-":
return &object.Integer{Value: left_value - right_value}
case "*":
return &object.Integer{Value: left_value * right_value}
case "/":
return &object.Integer{Value: left_value / right_value}
case "<":
return native_bool_to_boolean_object(left_value < right_value)
case ">":
return native_bool_to_boolean_object(left_value > right_value)
case "==":
return native_bool_to_boolean_object(left_value == right_value)
case "!=":
return native_bool_to_boolean_object(left_value != right_value)
default:
return NULL
}
}
func eval_if_expression(ie *ast.IfExpression) object.Object {
condition := Eval(ie.Condition)
if is_truthy(condition) {
return Eval(ie.Consequence)
} else if ie.Alternative != nil {
return Eval(ie.Alternative)
} else {
return NULL
}
}
func is_truthy(object object.Object) bool {
switch object {
case NULL:
return false
case TRUE:
return true
case FALSE:
return false
default:
return true
}
}

View File

@ -16,7 +16,18 @@ func TestEvalIntegerExpression(l_test *testing.T) {
{"10", 10}, {"10", 10},
{"-10", -10}, {"-10", -10},
{"-5", -5}, {"-5", -5},
{"5 + 5 + 5 + 5 - 10", 10},
{"2 * 2 * 2 * 2 * 2", 32},
{"-50 + 100 + -50", 0},
{"5 * 2 + 10", 20},
{"5 + 2 * 10", 25},
{"20 + 2 * -10", 0},
{"50 / 2 * 2 + 10", 60},
{"2 * (5 + 10)", 30},
{"3 * 3 * 3 + 10", 37},
{"3 * (3 * 3) + 10", 37},
{"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50},
{"8 * (4 + 32 - (64 / 2) + 5) / (100 / 2 + (25 - 10 + 15) * 18)", 0},
} }
for _, tt := range tests { for _, tt := range tests {
@ -32,6 +43,23 @@ func TestEvalBooleanExpression(l_test *testing.T) {
}{ }{
{"true", true}, {"true", true},
{"false", false}, {"false", false},
{"1 < 2", true},
{"1 > 2", false},
{"1 < 1", false},
{"1 > 1", false},
{"1 == 1", true},
{"1 != 1", false},
{"1 == 2", false},
{"1 != 2", true},
{"true == true", true},
{"false == false", true},
{"true == false", false},
{"true != false", true},
{"false != true", true},
{"(1 < 2) == true", true},
{"(1 < 2) == false", false},
{"(1 > 2) == true", false},
{"(1 > 2) == false", true},
} }
for _, tt := range tests { for _, tt := range tests {
@ -60,6 +88,49 @@ func TestBangOperator(l_test *testing.T) {
} }
} }
func TestIfElseExpressions(l_test *testing.T) {
tests := []struct {
input string
expected interface{}
}{
{"if (true) { 10 }", 10},
{"if (false) { 10 }", nil},
{"if (1) { 10 }", 10},
{"if (1 < 2) { 10 }", 10},
{"if (1 > 2) { 10 }", nil},
{"if (1 > 2) { 10 } else {20}", 20},
{"if (1 < 2) { 10 } else {20}", 10},
{"if(10 > 1){if(10 > 1){ return 10;} return 1;}", 10},
}
for _, tt := range tests {
evaluated := test_eval(tt.input)
integer, ok := tt.expected.(int)
if ok {
test_integer_object(l_test, evaluated, int64(integer))
} else {
test_null_object(l_test, evaluated)
}
}
}
func TestReturnStatements(l_test *testing.T) {
tests := []struct {
input string
expected int64
}{
{"return 10;", 10},
{"return 10; 9;", 10},
{"return 2 * 5; 9;", 10},
{"9; return 2 * 5; 9;", 10},
}
for _, tt := range tests {
evaluated := test_eval(tt.input)
test_integer_object(l_test, evaluated, tt.expected)
}
}
// Helpers // Helpers
func test_eval(input string) object.Object { func test_eval(input string) object.Object {
l_lexer := lexer.New(input) l_lexer := lexer.New(input)
@ -95,3 +166,11 @@ func test_boolean_object(l_test *testing.T, l_object object.Object, expected boo
} }
return true return true
} }
func test_null_object(l_test *testing.T, object object.Object) bool {
if object != NULL {
l_test.Errorf("object is not NULL, got=%T (%+v)", object, object)
return false
}
return true
}

View File

@ -5,9 +5,10 @@ import "fmt"
type ObjectType string type ObjectType string
const ( const (
INTEGER_OBJECT = "INTEGER" INTEGER_OBJECT = "INTEGER"
BOOLEAN_OBJECT = "BOOLEAN" BOOLEAN_OBJECT = "BOOLEAN"
NULL_OBJECT = "NULL" NULL_OBJECT = "NULL"
RETURN_VALUE_OBJECT = "RETURN_VALUE"
) )
type Object interface { type Object interface {
@ -51,3 +52,11 @@ func (n *Null) Type() ObjectType {
func (n *Null) Inspect() string { func (n *Null) Inspect() string {
return "null" return "null"
} }
// Return
type ReturnValue struct {
Value Object
}
func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJECT }
func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() }