Add tons of builtin helpers and array operations.
Some checks failed
Test / build (push) Waiting to run
Build / build (push) Has been cancelled

This commit is contained in:
Chuck Smith
2024-03-24 12:11:46 -04:00
parent c9d96236dd
commit 99cea83f57
23 changed files with 413 additions and 49 deletions

View File

@@ -306,8 +306,29 @@ func evalIntegerPrefixOperatorExpression(operator string, right object.Object) o
func evalInfixExpression(operator string, left, right object.Object) object.Object {
switch {
// {"a": 1} + {"b": 2}
case operator == "+" && left.Type() == object.HASH_OBJ && right.Type() == object.HASH_OBJ:
leftVal := left.(*object.Hash).Pairs
rightVal := right.(*object.Hash).Pairs
pairs := make(map[object.HashKey]object.HashPair)
for k, v := range leftVal {
pairs[k] = v
}
for k, v := range rightVal {
pairs[k] = v
}
return &object.Hash{Pairs: pairs}
// [1] + [2]
case operator == "+" && left.Type() == object.ARRAY_OBJ && right.Type() == object.ARRAY_OBJ:
leftVal := left.(*object.Array).Elements
rightVal := right.(*object.Array).Elements
elements := make([]object.Object, len(leftVal)+len(rightVal))
elements = append(leftVal, rightVal...)
return &object.Array{Elements: elements}
// [1] * 3
case left.Type() == object.ARRAY_OBJ && right.Type() == object.INTEGER_OBJ:
case operator == "*" && left.Type() == object.ARRAY_OBJ && right.Type() == object.INTEGER_OBJ:
leftVal := left.(*object.Array).Elements
rightVal := int(right.(*object.Integer).Value)
elements := leftVal
@@ -316,7 +337,7 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
}
return &object.Array{Elements: elements}
// 3 * [1]
case left.Type() == object.INTEGER_OBJ && right.Type() == object.ARRAY_OBJ:
case operator == "*" && left.Type() == object.INTEGER_OBJ && right.Type() == object.ARRAY_OBJ:
leftVal := int(left.(*object.Integer).Value)
rightVal := right.(*object.Array).Elements
elements := rightVal
@@ -326,12 +347,12 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
return &object.Array{Elements: elements}
// " " * 4
case left.Type() == object.STRING_OBJ && right.Type() == object.INTEGER_OBJ:
case operator == "*" && left.Type() == object.STRING_OBJ && right.Type() == object.INTEGER_OBJ:
leftVal := left.(*object.String).Value
rightVal := right.(*object.Integer).Value
return &object.String{Value: strings.Repeat(leftVal, int(rightVal))}
// 4 * " "
case left.Type() == object.INTEGER_OBJ && right.Type() == object.STRING_OBJ:
case operator == "*" && left.Type() == object.INTEGER_OBJ && right.Type() == object.STRING_OBJ:
leftVal := left.(*object.Integer).Value
rightVal := right.(*object.String).Value
return &object.String{Value: strings.Repeat(rightVal, int(leftVal))}

View File

@@ -182,27 +182,27 @@ func TestErrorHandling(t *testing.T) {
}{
{
"5 + true;",
"type mismatch: INTEGER + BOOLEAN",
"type mismatch: int + bool",
},
{
"5 + true; 5;",
"type mismatch: INTEGER + BOOLEAN",
"type mismatch: int + bool",
},
{
"-true",
"unknown operator: -BOOLEAN",
"unknown operator: -bool",
},
{
"true + false;",
"unknown operator: BOOLEAN + BOOLEAN",
"unknown operator: bool + bool",
},
{
"5; true + false; 5",
"unknown operator: BOOLEAN + BOOLEAN",
"unknown operator: bool + bool",
},
{
"if (10 > 1) { true + false; }",
"unknown operator: BOOLEAN + BOOLEAN",
"unknown operator: bool + bool",
},
{
`
@@ -214,7 +214,7 @@ func TestErrorHandling(t *testing.T) {
return 1;
}
`,
"unknown operator: BOOLEAN + BOOLEAN",
"unknown operator: bool + bool",
},
{
"foobar",
@@ -222,11 +222,11 @@ func TestErrorHandling(t *testing.T) {
},
{
`"Hello" - "World"`,
"unknown operator: STRING - STRING",
"unknown operator: str - str",
},
{
`{"name": "Monkey"}[fn(x) { x }];`,
"unusable as hash key: FUNCTION",
"unusable as hash key: fn",
},
}
@@ -395,21 +395,21 @@ func TestBuiltinFunctions(t *testing.T) {
{`len("")`, 0},
{`len("four")`, 4},
{`len("hello world")`, 11},
{`len(1)`, errors.New("argument to `len` not supported, got INTEGER")},
{`len(1)`, errors.New("argument to `len` not supported, got int")},
{`len("one", "two")`, errors.New("wrong number of arguments. got=2, want=1")},
{`len("∑")`, 1},
{`len([1, 2, 3])`, 3},
{`len([])`, 0},
{`first([1, 2, 3])`, 1},
{`first([])`, nil},
{`first(1)`, errors.New("argument to `first` must be ARRAY, got INTEGER")},
{`first(1)`, errors.New("argument to `first` must be array, got int")},
{`last([1, 2, 3])`, 3},
{`last([])`, nil},
{`last(1)`, errors.New("argument to `last` must be ARRAY, got INTEGER")},
{`last(1)`, errors.New("argument to `last` must be array, got int")},
{`rest([1, 2, 3])`, []int{2, 3}},
{`rest([])`, nil},
{`push([], 1)`, []int{1}},
{`push(1, 1)`, errors.New("argument to `push` must be ARRAY, got INTEGER")},
{`push(1, 1)`, errors.New("argument to `push` must be array, got int")},
{`print("Hello World")`, nil},
{`input()`, ""},
{`pop([])`, errors.New("cannot pop from an empty array")},
@@ -500,6 +500,24 @@ func TestArrayDuplication(t *testing.T) {
testIntegerObject(t, result.Elements[2], 1)
}
func TestArrayMerging(t *testing.T) {
input := "[1] + [2]"
evaluated := testEval(input)
result, ok := evaluated.(*object.Array)
if !ok {
t.Fatalf("object is not Array. got=%T (%+v)", evaluated, evaluated)
}
if len(result.Elements) != 2 {
t.Fatalf("array has wrong num of elements. got=%d",
len(result.Elements))
}
testIntegerObject(t, result.Elements[0], 1)
testIntegerObject(t, result.Elements[1], 2)
}
func TestArrayIndexExpressions(t *testing.T) {
tests := []struct {
input string
@@ -599,6 +617,33 @@ func TestHashLiterals(t *testing.T) {
}
}
func TestHashMerging(t *testing.T) {
input := `{"a": 1} + {"b": 2}`
evaluated := testEval(input)
result, ok := evaluated.(*object.Hash)
if !ok {
t.Fatalf("Eval didn't return Hash. got=%T (%+v)", evaluated, evaluated)
}
expected := map[object.HashKey]int64{
(&object.String{Value: "a"}).HashKey(): 1,
(&object.String{Value: "b"}).HashKey(): 2,
}
if len(result.Pairs) != len(expected) {
t.Fatalf("Hash has wrong num of pairs. got=%d", len(result.Pairs))
}
for expectedKey, expectedValue := range expected {
pair, ok := result.Pairs[expectedKey]
if !ok {
t.Errorf("no pair for given key in Pairs")
}
testIntegerObject(t, pair.Value, expectedValue)
}
}
func TestHashSelectorExpressions(t *testing.T) {
tests := []struct {
input string