simon 3 роки тому
батько
коміт
b0674037af
2 змінених файлів з 37 додано та 2 видалено
  1. 22 2
      compiler/compiler.go
  2. 15 0
      compiler/compiler_test.go

+ 22 - 2
compiler/compiler.go

@@ -135,7 +135,7 @@ func (c *Compiler) Compile(node ast.Node) error {
 			return err
 		}
 
-		if c.lastInstructionIsPop() {
+		if c.lastInstructionIs(code.OpPop) {
 			c.removeLastPop()
 		}
 
@@ -152,7 +152,7 @@ func (c *Compiler) Compile(node ast.Node) error {
 			if err != nil {
 				return err
 			}
-			if c.lastInstructionIsPop() {
+			if c.lastInstructionIs(code.OpPop) {
 				c.removeLastPop()
 			}
 		}
@@ -248,6 +248,12 @@ func (c *Compiler) Compile(node ast.Node) error {
 			return err
 		}
 
+		if c.lastInstructionIs(code.OpPop) {
+			c.replaceLastPopWithReturn()
+		}
+		if !c.lastInstructionIs(code.OpReturnValue) {
+			c.emit(code.OpReturn)
+		}
 		instructions := c.leaveScope()
 
 		compiledFn := &object.CompileFunction{Instructions: instructions}
@@ -298,6 +304,13 @@ func (c *Compiler) lastInstructionIsPop() bool {
 	return c.scopes[c.scopeIndex].lastInstruction.Opcode == code.OpPop
 }
 
+func (c *Compiler) lastInstructionIs(op code.Opcode) bool {
+	if len(c.currentInstructions()) == 0 {
+		return false
+	}
+	return c.scopes[c.scopeIndex].lastInstruction.Opcode == op
+}
+
 func (c *Compiler) removeLastPop() {
 	last := c.scopes[c.scopeIndex].lastInstruction
 	previous := c.scopes[c.scopeIndex].previousInstruction
@@ -346,6 +359,13 @@ func (c *Compiler) leaveScope() code.Instructions {
 	return ins
 }
 
+func (c *Compiler) replaceLastPopWithReturn() {
+	lastPop := c.scopes[c.scopeIndex].lastInstruction.Position
+	c.replaceInstruction(lastPop, code.Make(code.OpReturnValue))
+
+	c.scopes[c.scopeIndex].lastInstruction.Opcode = code.OpReturnValue
+}
+
 func (c *Compiler) ByteCode() *ByteCode {
 	return &ByteCode{
 		Instructions: c.currentInstructions(),

+ 15 - 0
compiler/compiler_test.go

@@ -513,6 +513,21 @@ func TestCompilerScopes(t *testing.T) {
 	}
 }
 
+func TestFunctionWithoutReturnValue(t *testing.T) {
+	tests := []compilerTestCase{
+		{input: `fn() {}`,
+			expectedConstants: []interface{}{
+				[]code.Instructions{code.Make(code.OpReturn)},
+			},
+			expectedInstructions: []code.Instructions{
+				code.Make(code.OpConstant, 0),
+				code.Make(code.OpPop),
+			},
+		},
+	}
+	runCompilerTests(t, tests)
+}
+
 func runCompilerTests(t *testing.T, tests []compilerTestCase) {
 	t.Helper()