Kaynağa Gözat

Evaluating Index Operator Expressions

simon 3 yıl önce
ebeveyn
işleme
7f561b71d5
4 değiştirilmiş dosya ile 136 ekleme ve 1 silme
  1. 38 0
      evaluator/evaluator.go
  2. 76 0
      evaluator/evaluator_test.go
  3. 21 0
      object/object.go
  4. 1 1
      parser/parser_test.go

+ 38 - 0
evaluator/evaluator.go

@@ -78,6 +78,24 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
 		return applyFunction(function, args)
 	case *ast.StringLiteral:
 		return &object.String{Value: node.Value}
+	case *ast.ArrayLiteral:
+		elements := evalExpressions(node.Element, env)
+
+		if len(elements) == 1 && isError(elements[0]) {
+			// return  object.Error
+			return elements[0]
+		}
+		return &object.Array{Elements: elements}
+	case *ast.IndexExpression:
+		left := Eval(node.Left, env)
+		if isError(left) {
+			return left
+		}
+		index := Eval(node.Index, env)
+		if isError(index) {
+			return index
+		}
+		return evalIndexExpression(left, index)
 	}
 
 	return nil
@@ -274,6 +292,26 @@ func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Ob
 	return result
 }
 
+func evalIndexExpression(left, index object.Object) object.Object {
+	switch {
+	case left.Type() == object.ArrayObj && index.Type() == object.IntegerObj:
+		return evalArrayIndexExpression(left, index)
+	default:
+		return newError("index operator not supported: %s", left.Type())
+	}
+}
+
+func evalArrayIndexExpression(array, index object.Object) object.Object {
+	arrayObj := array.(*object.Array)
+	idx := index.(*object.Integer).Value
+	max := int64(len(arrayObj.Elements) - 1)
+
+	if idx < 0 || idx > max {
+		return NULL
+	}
+	return arrayObj.Elements[idx]
+}
+
 func isTruthy(obj object.Object) bool {
 	switch obj {
 	case NULL:

+ 76 - 0
evaluator/evaluator_test.go

@@ -4,6 +4,7 @@ import (
 	"github/runnignwater/monkey/lexer"
 	"github/runnignwater/monkey/object"
 	"github/runnignwater/monkey/parser"
+	"log"
 	"testing"
 )
 
@@ -354,3 +355,78 @@ func TestBuiltinFunctions(t *testing.T) {
 		}
 	}
 }
+
+func TestArrayLiterals(t *testing.T) {
+	input := "[1, 2 * 2, 3 + 5]"
+
+	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) != 3 {
+		log.Fatalf("array has wrong num of elements. got=%d",
+			len(result.Elements))
+	}
+
+	testIntegerObject(t, result.Elements[0], 1)
+	testIntegerObject(t, result.Elements[1], 4)
+	testIntegerObject(t, result.Elements[2], 8)
+}
+func TestArrayIndexExpression(t *testing.T) {
+	tests := []struct {
+		input    string
+		expected interface{}
+	}{
+		{
+			"[1, 2, 3][0]",
+			1,
+		},
+		{
+			"[1, 2, 3][1]",
+			2,
+		},
+		{
+			"[1, 2, 3][2]",
+			3,
+		},
+		{
+			"let i = 0; [1][i];",
+			1,
+		},
+		{
+			"[1, 2, 3][1 + 1]",
+			3,
+		},
+		{
+			"let myArray = [1, 2, 3];myArray[2]",
+			3,
+		},
+		{
+			"let myArray = [1, 2, 3];myArray[0]+myArray[1]+myArray[2]",
+			6,
+		},
+		{
+			"let myArray = [1, 2, 3]; let i = myArray[0];myArray[i];",
+			2,
+		},
+		{
+			"[1, 2, 3][3]",
+			nil,
+		},
+		{
+			"[1, 2, 3][-1]",
+			nil,
+		},
+	}
+
+	for _, tt := range tests {
+		evaluated := testEval(tt.input)
+		integer, ok := tt.expected.(int)
+		if ok {
+			testIntegerObject(t, evaluated, int64(integer))
+		} else {
+			testNullObject(t, evaluated)
+		}
+	}
+}

+ 21 - 0
object/object.go

@@ -19,6 +19,7 @@ const (
 	FunctionObj    = "FUNCTION"
 	StringObj      = "STRING"
 	BuiltinObj     = "BUILTIN"
+	ArrayObj       = "ARRAY"
 )
 
 // Object source code as an Object
@@ -95,6 +96,26 @@ type String struct {
 func (s *String) Type() ObjType   { return StringObj }
 func (s *String) Inspect() string { return s.Value }
 
+type Array struct {
+	Elements []Object
+}
+
+func (ao *Array) Type() ObjType { return ArrayObj }
+func (ao *Array) Inspect() string {
+	var out bytes.Buffer
+
+	elements := []string{}
+	for _, e := range ao.Elements {
+		elements = append(elements, e.Inspect())
+	}
+
+	out.WriteString("[")
+	out.WriteString(strings.Join(elements, ", "))
+	out.WriteString("]")
+
+	return out.String()
+}
+
 type Builtin struct {
 	Fn BuiltinFunction
 }

+ 1 - 1
parser/parser_test.go

@@ -753,7 +753,7 @@ func TestIndexExpressionParsing(t *testing.T) {
 	if !ok {
 		t.Fatalf("exp not *ast.IndexExpression. got=%T", stmt.Expression)
 	}
-	if !testIdentifier(t, indexExp, "myArray") {
+	if !testIdentifier(t, indexExp.Left, "myArray") {
 		return
 	}
 	if !testInfixExpression(t, indexExp.Index, 1, "+", 1) {