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 != 1", false},
|
||||||
{"1 == 2", false},
|
{"1 == 2", false},
|
||||||
{"1 != 2", true},
|
{"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 {
|
||||||
|
|
@ -70,15 +79,6 @@ func TestBangOperator(t *testing.T) {
|
||||||
{"!!true", true},
|
{"!!true", true},
|
||||||
{"!!false", false},
|
{"!!false", false},
|
||||||
{"!!5", true},
|
{"!!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 {
|
for _, tt := range tests {
|
||||||
|
|
@ -121,6 +121,7 @@ func TestReturnStatements(t *testing.T) {
|
||||||
{"return 10; 9;", 10},
|
{"return 10; 9;", 10},
|
||||||
{"return 2 * 5; 9;", 10},
|
{"return 2 * 5; 9;", 10},
|
||||||
{"9; return 2 * 5; 9;", 10},
|
{"9; return 2 * 5; 9;", 10},
|
||||||
|
{"if (10 > 1) { return 10; }", 10},
|
||||||
{
|
{
|
||||||
`
|
`
|
||||||
if (10 > 1) {
|
if (10 > 1) {
|
||||||
|
|
@ -133,6 +134,25 @@ if (10 > 1) {
|
||||||
`,
|
`,
|
||||||
10,
|
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 {
|
for _, tt := range tests {
|
||||||
|
|
@ -162,10 +182,18 @@ func TestErrorHandling(t *testing.T) {
|
||||||
"true + false;",
|
"true + false;",
|
||||||
"unknown operator: BOOLEAN + BOOLEAN",
|
"unknown operator: BOOLEAN + BOOLEAN",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"true + false + true + false;",
|
||||||
|
"unknown operator: BOOLEAN + BOOLEAN",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"5; true + false; 5",
|
"5; true + false; 5",
|
||||||
"unknown operator: BOOLEAN + BOOLEAN",
|
"unknown operator: BOOLEAN + BOOLEAN",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`"Hello" - "World"`,
|
||||||
|
"unknown operator: STRING - STRING",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"if (10 > 1) { true + false; }",
|
"if (10 > 1) { true + false; }",
|
||||||
"unknown operator: BOOLEAN + BOOLEAN",
|
"unknown operator: BOOLEAN + BOOLEAN",
|
||||||
|
|
@ -186,14 +214,14 @@ if (10 > 1) {
|
||||||
"foobar",
|
"foobar",
|
||||||
"identifier not found: foobar",
|
"identifier not found: foobar",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
`"Hello" - "World"`,
|
|
||||||
"unknown operator: STRING - STRING",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
`{"name": "Monkey"}[fn(x) { x }];`,
|
`{"name": "Monkey"}[fn(x) { x }];`,
|
||||||
"unusable as hash key: FUNCTION",
|
"unusable as hash key: FUNCTION",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`999[1]`,
|
||||||
|
"index operator not supported: INTEGER",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
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) {
|
func TestStringLiteral(t *testing.T) {
|
||||||
input := `"Hello World!"`
|
input := `"Hello World!"`
|
||||||
|
|
||||||
|
|
@ -303,13 +360,26 @@ func TestStringConcatenation(t *testing.T) {
|
||||||
func TestBuiltinFunctions(t *testing.T) {
|
func TestBuiltinFunctions(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expected any
|
expected interface{}
|
||||||
}{
|
}{
|
||||||
{`len("")`, 0},
|
{`len("")`, 0},
|
||||||
{`len("four")`, 4},
|
{`len("four")`, 4},
|
||||||
{`len("hello world")`, 11},
|
{`len("hello world")`, 11},
|
||||||
{`len(1)`, "argument to `len` not supported, got INTEGER"},
|
{`len(1)`, "argument to `len` not supported, got INTEGER"},
|
||||||
{`len("one", "two")`, "wrong number of arguments. got=2, want=1"},
|
{`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 {
|
for _, tt := range tests {
|
||||||
|
|
@ -318,6 +388,8 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||||
switch expected := tt.expected.(type) {
|
switch expected := tt.expected.(type) {
|
||||||
case int:
|
case int:
|
||||||
testIntegerObject(t, evaluated, int64(expected))
|
testIntegerObject(t, evaluated, int64(expected))
|
||||||
|
case nil:
|
||||||
|
testNullObject(t, evaluated)
|
||||||
case string:
|
case string:
|
||||||
errObj, ok := evaluated.(*object.Error)
|
errObj, ok := evaluated.(*object.Error)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -329,6 +401,22 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||||
t.Errorf("wrong error message. expected=%q, got=%q",
|
t.Errorf("wrong error message. expected=%q, got=%q",
|
||||||
expected, errObj.Message)
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 {
|
func testEval(input string) object.Object {
|
||||||
l := lexer.New(input)
|
l := lexer.New(input)
|
||||||
p := parser.New(l)
|
p := parser.New(l)
|
||||||
program := p.ParseProgram()
|
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 {
|
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
|
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