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:
parent
ec01fd1a31
commit
5456f60038
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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() }
|
||||||
|
Loading…
Reference in New Issue
Block a user