| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- package evaluator
- import (
- "github/runnignwater/monkey/lexer"
- "github/runnignwater/monkey/object"
- "github/runnignwater/monkey/parser"
- "testing"
- )
- func TestEvalIntegerExpression(t *testing.T) {
- tests := []struct {
- input string
- expected int64
- }{
- {"5", 5},
- {"10", 10},
- {"-5", -5},
- {"-10", -10},
- {"5+5+5+5-10", 10},
- {"2*2*2*2*2", 32},
- {"-50+100+-50", 0},
- }
- for _, tt := range tests {
- evaluated := testEval(tt.input)
- testIntegerObject(t, evaluated, tt.expected)
- }
- }
- func TestEvalBooleanExpression(t *testing.T) {
- tests := []struct {
- input string
- expected bool
- }{
- {"true", true},
- {"false", false},
- {"1 < 2", true},
- {"1 > 2", false},
- {"1 == 1", true},
- {"1 != 1", false},
- {"1 == 2", false},
- {"1 != 2", true},
- {"true == true", true},
- {"false == false", true},
- {"(1 < 2) == true", true},
- }
- for _, tt := range tests {
- evaluated := testEval(tt.input)
- testBooleanObject(t, evaluated, tt.expected)
- }
- }
- func TestBangOperator(t *testing.T) {
- tests := []struct {
- input string
- expected bool
- }{
- {"!true", false},
- {"!false", true},
- {"!5", false},
- {"!!true", true},
- {"!!false", false},
- {"!!5", true},
- }
- for _, tt := range tests {
- evaluated := testEval(tt.input)
- testBooleanObject(t, evaluated, tt.expected)
- }
- }
- func TestIfElseExpression(t *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}", 10},
- {"if(1>2){10} else {20}", 20},
- }
- for _, tt := range tests {
- evaluated := testEval(tt.input)
- integer, ok := tt.expected.(int)
- if ok {
- testIntegerObject(t, evaluated, int64(integer))
- } else {
- testNullObject(t, evaluated)
- }
- }
- }
- func TestReturnStatements(t *testing.T) {
- tests := []struct {
- input string
- expected int64
- }{
- {"return 10;", 10},
- {"return 10;9", 10},
- {"return 2*5;9;", 10},
- {"9;return 3 *5; 9;", 15},
- {
- `if ( 10 > 1) {
- if( 10 > 1) {
- return 10;
- }
- return 1;
- }
- `,
- 10,
- },
- }
- for _, tt := range tests {
- evaluated := testEval(tt.input)
- testIntegerObject(t, evaluated, tt.expected)
- }
- }
- func TestErrorHandling(t *testing.T) {
- tests := []struct {
- input string
- expectedMsg string
- }{
- {
- "5 + true",
- "type mismatch: INTEGER + BOOLEAN",
- },
- {
- "5 + true; 5;",
- "type mismatch: INTEGER + BOOLEAN",
- },
- {
- "-true",
- "unknown operator: -BOOLEAN",
- },
- {
- "true + false",
- "unknown operator: BOOLEAN + BOOLEAN",
- },
- {
- "5;true + false;5",
- "unknown operator: BOOLEAN + BOOLEAN",
- },
- {
- "if ( 10 > 1) { true + false; }",
- "unknown operator: BOOLEAN + BOOLEAN",
- },
- {
- `
- if (10 > 1) {
- if (10 > 1) {
- return true + false;
- }
- return 1;
- }
- `,
- "unknown operator: BOOLEAN + BOOLEAN",
- },
- {
- "foobar",
- "identifier not found: foobar",
- },
- }
- for _, tt := range tests {
- evaluated := testEval(tt.input)
- errObj, ok := evaluated.(*object.Error)
- if !ok {
- t.Errorf("no error object returned. got=%T (%+v)", evaluated, evaluated)
- continue
- }
- if errObj.Msg != tt.expectedMsg {
- t.Errorf("wrong error message. expected=%q, got=%q", tt.expectedMsg, errObj.Msg)
- }
- }
- }
- func TestLetStatement(t *testing.T) {
- tests := []struct {
- input string
- expected int64
- }{
- {"let a = 5;a;", 5},
- {"let a = 5 * 5;a;", 25},
- {"let a = 5;let b = a;b;", 5},
- {"let a = 5; let b = a;let c = a + b + 5;c;", 15},
- }
- for _, tt := range tests {
- testIntegerObject(t, testEval(tt.input), tt.expected)
- }
- }
- func testNullObject(t *testing.T, obj object.Object) bool {
- if obj != NULL {
- t.Errorf("object is not NULL. got=%T (%+v)", obj, obj)
- return false
- }
- return true
- }
- func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
- result, ok := obj.(*object.Integer)
- if !ok {
- t.Errorf("object is not Integer. got=%T (%+v)", obj, obj)
- return false
- }
- if result.Value != expected {
- t.Errorf("object has wrong value. got=%d, want=%d", result.Value, expected)
- return false
- }
- return true
- }
- func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool {
- result, ok := obj.(*object.Boolean)
- if !ok {
- t.Errorf("object is not Boolean. got=%T (%+v)", obj, obj)
- return false
- }
- if result.Value != expected {
- t.Errorf("object has wrong value. got=%t, want=%t", result.Value, expected)
- return false
- }
- return true
- }
- func testEval(input string) object.Object {
- l := lexer.New(input)
- p := parser.New(l)
- program := p.ParseProgram()
- return Eval(program)
- }
|