simon 3 роки тому
батько
коміт
ba2e0d7843
4 змінених файлів з 90 додано та 3 видалено
  1. 3 0
      compiler/compiler.go
  2. 42 0
      compiler/compiler_test.go
  3. 19 3
      vm/vm.go
  4. 26 0
      vm/vm_test.go

+ 3 - 0
compiler/compiler.go

@@ -183,6 +183,9 @@ func (c *Compiler) Compile(node ast.Node) error {
 	case *ast.IntegerLiteral:
 		integer := &object.Integer{Value: node.Value}
 		c.emit(code.OpConstant, c.addConstant(integer))
+	case *ast.StringLiteral:
+		str := &object.String{Value: node.Value}
+		c.emit(code.OpConstant, c.addConstant(str))
 	}
 	return nil
 }

+ 42 - 0
compiler/compiler_test.go

@@ -264,6 +264,31 @@ func TestGlobalLetStatements(t *testing.T) {
 	runCompilerTests(t, tests)
 }
 
+func TestStringExpression(t *testing.T) {
+	tests := []compilerTestCase{
+		{
+			input:             `"monkey"`,
+			expectedConstants: []interface{}{"monkey"},
+			expectedInstructions: []code.Instructions{
+				code.Make(code.OpConstant, 0),
+				code.Make(code.OpPop),
+			},
+		},
+		{
+			input:             `"monkey" + "language"`,
+			expectedConstants: []interface{}{"monkey", "language"},
+			expectedInstructions: []code.Instructions{
+				code.Make(code.OpConstant, 0),
+				code.Make(code.OpConstant, 1),
+				code.Make(code.OpAdd),
+				code.Make(code.OpPop),
+			},
+		},
+	}
+
+	runCompilerTests(t, tests)
+}
+
 func runCompilerTests(t *testing.T, tests []compilerTestCase) {
 	t.Helper()
 
@@ -308,11 +333,28 @@ func testConstants(
 				return fmt.Errorf("constant %d -- testIntegerObject failed: %s",
 					i, err)
 			}
+		case string:
+			err := testStringObject(constant, actual[i])
+			if err != nil {
+				return fmt.Errorf("constant %d - testStringObject failed: %s", i, err)
+			}
 		}
 	}
 	return nil
 }
 
+func testStringObject(expected string, actual object.Object) error {
+	result, ok := actual.(*object.String)
+	if !ok {
+		return fmt.Errorf("object is not String. got=%T (%+v)", actual, actual)
+	}
+	if result.Value != expected {
+		return fmt.Errorf("object has wrong value. got=%s, want=%q", result.Value, expected)
+	}
+
+	return nil
+}
+
 func testIntegerObject(expected int64, actual object.Object) error {
 	result, ok := actual.(*object.Integer)
 	if !ok {

+ 19 - 3
vm/vm.go

@@ -168,11 +168,14 @@ func (vm *VM) executeBinaryOperation(op code.Opcode) error {
 
 	leftType := left.Type()
 	rightType := right.Type()
-	if leftType == object.IntegerObj && rightType == object.IntegerObj {
+	switch {
+	case leftType == object.IntegerObj && rightType == object.IntegerObj:
 		return vm.executeBinaryIntegerOperation(op, left, right)
+	case leftType == object.StringObj && rightType == object.StringObj:
+		return vm.executeBinaryStringOperation(op, left, right)
+	default:
+		return fmt.Errorf("unsupported types for binary operation: %s %s", leftType, rightType)
 	}
-
-	return fmt.Errorf("unsupported types of binary operation: %s %s", leftType, rightType)
 }
 
 func (vm *VM) executeComparison(op code.Opcode) error {
@@ -221,6 +224,19 @@ func (vm *VM) executeBinaryIntegerOperation(
 	return vm.push(&object.Integer{Value: result})
 }
 
+func (vm *VM) executeBinaryStringOperation(
+	op code.Opcode,
+	left, right object.Object,
+) error {
+	if op != code.OpAdd {
+		return fmt.Errorf("unknown string operator: %d", op)
+	}
+	leftValue := left.(*object.String).Value
+	rightValue := right.(*object.String).Value
+
+	return vm.push(&object.String{Value: leftValue + rightValue})
+}
+
 func (vm *VM) executeIntegerComparison(
 	op code.Opcode,
 	left, right object.Object,

+ 26 - 0
vm/vm_test.go

@@ -86,6 +86,16 @@ func TestGlobalLetStatement(t *testing.T) {
 	runVmTests(t, tests)
 }
 
+func TestStringExpression(t *testing.T) {
+	tests := []vmTestCase{
+		{`"monkey"`, "monkey"},
+		{`"mon" + "key"`, "monkey"},
+		{`"mon" + "key" + "banana"`, "monkeybanana"},
+	}
+
+	runVmTests(t, tests)
+}
+
 func runVmTests(t *testing.T, tests []vmTestCase) {
 	t.Helper()
 
@@ -134,9 +144,25 @@ func testExpectedObject(
 		if err != nil {
 			t.Errorf("testBooleanObject failed: %s", err)
 		}
+	case string:
+		err := testStringObject(expected, actual)
+		if err != nil {
+			t.Errorf("testStringObject failed: %s", err)
+		}
 	}
 }
 
+func testStringObject(expected string, actual object.Object) error {
+	result, ok := actual.(*object.String)
+	if !ok {
+		return fmt.Errorf("object is not String. got=%T (%+v)", actual, actual)
+	}
+	if result.Value != expected {
+		return fmt.Errorf("object has wrong value. got=%q, want=%q", result.Value, expected)
+	}
+	return nil
+}
+
 func testIntegerObject(expected int64, actual object.Object) error {
 	result, ok := actual.(*object.Integer)
 	if !ok {