Add the rest of the evaluator tests.
Signed-off-by: jmug <u.g.a.mariano@gmail.com>
This commit is contained in:
parent
b68bbf01d2
commit
4eede91cfd
1 changed files with 120 additions and 32 deletions
|
|
@ -51,6 +51,15 @@ func TestEvalBooleanExpression(t *testing.T) {
|
|||
{"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 {
|
||||
|
|
@ -70,15 +79,6 @@ func TestBangOperator(t *testing.T) {
|
|||
{"!!true", true},
|
||||
{"!!false", false},
|
||||
{"!!5", 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 {
|
||||
|
|
@ -121,6 +121,7 @@ func TestReturnStatements(t *testing.T) {
|
|||
{"return 10; 9;", 10},
|
||||
{"return 2 * 5; 9;", 10},
|
||||
{"9; return 2 * 5; 9;", 10},
|
||||
{"if (10 > 1) { return 10; }", 10},
|
||||
{
|
||||
`
|
||||
if (10 > 1) {
|
||||
|
|
@ -133,6 +134,25 @@ if (10 > 1) {
|
|||
`,
|
||||
10,
|
||||
},
|
||||
{
|
||||
`
|
||||
let f = fn(x) {
|
||||
return x;
|
||||
x + 10;
|
||||
};
|
||||
f(10);`,
|
||||
10,
|
||||
},
|
||||
{
|
||||
`
|
||||
let f = fn(x) {
|
||||
let result = x + 10;
|
||||
return result;
|
||||
return 10;
|
||||
};
|
||||
f(10);`,
|
||||
20,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -162,10 +182,18 @@ func TestErrorHandling(t *testing.T) {
|
|||
"true + false;",
|
||||
"unknown operator: BOOLEAN + BOOLEAN",
|
||||
},
|
||||
{
|
||||
"true + false + true + false;",
|
||||
"unknown operator: BOOLEAN + BOOLEAN",
|
||||
},
|
||||
{
|
||||
"5; true + false; 5",
|
||||
"unknown operator: BOOLEAN + BOOLEAN",
|
||||
},
|
||||
{
|
||||
`"Hello" - "World"`,
|
||||
"unknown operator: STRING - STRING",
|
||||
},
|
||||
{
|
||||
"if (10 > 1) { true + false; }",
|
||||
"unknown operator: BOOLEAN + BOOLEAN",
|
||||
|
|
@ -186,14 +214,14 @@ if (10 > 1) {
|
|||
"foobar",
|
||||
"identifier not found: foobar",
|
||||
},
|
||||
{
|
||||
`"Hello" - "World"`,
|
||||
"unknown operator: STRING - STRING",
|
||||
},
|
||||
{
|
||||
`{"name": "Monkey"}[fn(x) { x }];`,
|
||||
"unusable as hash key: FUNCTION",
|
||||
},
|
||||
{
|
||||
`999[1]`,
|
||||
"index operator not supported: INTEGER",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -272,6 +300,35 @@ func TestFunctionApplication(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEnclosingEnvironments(t *testing.T) {
|
||||
input := `
|
||||
let first = 10;
|
||||
let second = 10;
|
||||
let third = 10;
|
||||
|
||||
let ourFunction = fn(first) {
|
||||
let second = 20;
|
||||
|
||||
first + second + third;
|
||||
};
|
||||
|
||||
ourFunction(20) + first + second;`
|
||||
|
||||
testIntegerObject(t, testEval(input), 70)
|
||||
}
|
||||
|
||||
func TestClosures(t *testing.T) {
|
||||
input := `
|
||||
let newAdder = fn(x) {
|
||||
fn(y) { x + y };
|
||||
};
|
||||
|
||||
let addTwo = newAdder(2);
|
||||
addTwo(2);`
|
||||
|
||||
testIntegerObject(t, testEval(input), 4)
|
||||
}
|
||||
|
||||
func TestStringLiteral(t *testing.T) {
|
||||
input := `"Hello World!"`
|
||||
|
||||
|
|
@ -303,13 +360,26 @@ func TestStringConcatenation(t *testing.T) {
|
|||
func TestBuiltinFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected any
|
||||
expected interface{}
|
||||
}{
|
||||
{`len("")`, 0},
|
||||
{`len("four")`, 4},
|
||||
{`len("hello world")`, 11},
|
||||
{`len(1)`, "argument to `len` not supported, got INTEGER"},
|
||||
{`len("one", "two")`, "wrong number of arguments. got=2, want=1"},
|
||||
{`len([1, 2, 3])`, 3},
|
||||
{`len([])`, 0},
|
||||
{`puts("hello", "world!")`, nil},
|
||||
{`first([1, 2, 3])`, 1},
|
||||
{`first([])`, nil},
|
||||
{`first(1)`, "argument to `first` must be ARRAY, got INTEGER"},
|
||||
{`last([1, 2, 3])`, 3},
|
||||
{`last([])`, nil},
|
||||
{`last(1)`, "argument to `last` must be ARRAY, got INTEGER"},
|
||||
{`rest([1, 2, 3])`, []int{2, 3}},
|
||||
{`rest([])`, nil},
|
||||
{`push([], 1)`, []int{1}},
|
||||
{`push(1, 1)`, "argument to `push` must be ARRAY, got INTEGER"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -318,6 +388,8 @@ func TestBuiltinFunctions(t *testing.T) {
|
|||
switch expected := tt.expected.(type) {
|
||||
case int:
|
||||
testIntegerObject(t, evaluated, int64(expected))
|
||||
case nil:
|
||||
testNullObject(t, evaluated)
|
||||
case string:
|
||||
errObj, ok := evaluated.(*object.Error)
|
||||
if !ok {
|
||||
|
|
@ -329,6 +401,22 @@ func TestBuiltinFunctions(t *testing.T) {
|
|||
t.Errorf("wrong error message. expected=%q, got=%q",
|
||||
expected, errObj.Message)
|
||||
}
|
||||
case []int:
|
||||
array, ok := evaluated.(*object.Array)
|
||||
if !ok {
|
||||
t.Errorf("obj not Array. got=%T (%+v)", evaluated, evaluated)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(array.Elements) != len(expected) {
|
||||
t.Errorf("wrong num of elements. want=%d, got=%d",
|
||||
len(expected), len(array.Elements))
|
||||
continue
|
||||
}
|
||||
|
||||
for i, expectedElem := range expected {
|
||||
testIntegerObject(t, array.Elements[i], int64(expectedElem))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -412,14 +500,14 @@ func TestArrayIndexExpressions(t *testing.T) {
|
|||
|
||||
func TestHashLiterals(t *testing.T) {
|
||||
input := `let two = "two";
|
||||
{
|
||||
"one": 10 - 9,
|
||||
two: 1 + 1,
|
||||
"thr" + "ee": 6 / 2,
|
||||
4: 4,
|
||||
true: 5,
|
||||
false: 6
|
||||
}`
|
||||
{
|
||||
"one": 10 - 9,
|
||||
two: 1 + 1,
|
||||
"thr" + "ee": 6 / 2,
|
||||
4: 4,
|
||||
true: 5,
|
||||
false: 6
|
||||
}`
|
||||
|
||||
evaluated := testEval(input)
|
||||
result, ok := evaluated.(*object.Hash)
|
||||
|
|
@ -495,21 +583,13 @@ func TestHashIndexExpressions(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 testEval(input string) object.Object {
|
||||
l := lexer.New(input)
|
||||
p := parser.New(l)
|
||||
program := p.ParseProgram()
|
||||
env := object.NewEnvironment()
|
||||
|
||||
return Eval(program, object.NewEnvironment())
|
||||
return Eval(program, env)
|
||||
}
|
||||
|
||||
func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
|
||||
|
|
@ -540,3 +620,11 @@ func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue