Simplify return operation
Some checks failed
Build / build (push) Failing after 1m11s
Test / build (push) Failing after 11m42s

This commit is contained in:
Chuck Smith
2024-03-18 16:33:39 -04:00
parent b47a39e1b2
commit 98c8582fdb
7 changed files with 87 additions and 51 deletions

View File

@@ -36,7 +36,6 @@ const (
OpHash OpHash
OpIndex OpIndex
OpCall OpCall
OpReturnValue
OpReturn OpReturn
OpGetLocal OpGetLocal
OpSetLocal OpSetLocal
@@ -78,7 +77,6 @@ var definitions = map[Opcode]*Definition{
OpHash: {"OpHash", []int{2}}, OpHash: {"OpHash", []int{2}},
OpIndex: {"OpIndex", []int{}}, OpIndex: {"OpIndex", []int{}},
OpCall: {"OpCall", []int{1}}, OpCall: {"OpCall", []int{1}},
OpReturnValue: {"OpReturnValue", []int{}},
OpReturn: {"OpReturn", []int{}}, OpReturn: {"OpReturn", []int{}},
OpGetLocal: {"OpGetLocal", []int{1}}, OpGetLocal: {"OpGetLocal", []int{1}},
OpSetLocal: {"OpSetLocal", []int{1}}, OpSetLocal: {"OpSetLocal", []int{1}},

View File

@@ -157,6 +157,9 @@ func (c *Compiler) Compile(node ast.Node) error {
} }
case *ast.IfExpression: case *ast.IfExpression:
if c.lastInstructionIs(code.OpPop) {
c.removeLastPop()
}
err := c.Compile(node.Condition) err := c.Compile(node.Condition)
if err != nil { if err != nil {
return err return err
@@ -314,7 +317,8 @@ func (c *Compiler) Compile(node ast.Node) error {
if c.lastInstructionIs(code.OpPop) { if c.lastInstructionIs(code.OpPop) {
c.replaceLastPopWithReturn() c.replaceLastPopWithReturn()
} }
if !c.lastInstructionIs(code.OpReturnValue) { if !c.lastInstructionIs(code.OpReturn) {
c.emit(code.OpNull)
c.emit(code.OpReturn) c.emit(code.OpReturn)
} }
@@ -341,7 +345,7 @@ func (c *Compiler) Compile(node ast.Node) error {
return err return err
} }
c.emit(code.OpReturnValue) c.emit(code.OpReturn)
case *ast.CallExpression: case *ast.CallExpression:
err := c.Compile(node.Function) err := c.Compile(node.Function)
@@ -481,9 +485,9 @@ func (c *Compiler) leaveScope() code.Instructions {
func (c *Compiler) replaceLastPopWithReturn() { func (c *Compiler) replaceLastPopWithReturn() {
lastPos := c.scopes[c.scopeIndex].lastInstruction.Position lastPos := c.scopes[c.scopeIndex].lastInstruction.Position
c.replaceInstruction(lastPos, code.Make(code.OpReturnValue)) c.replaceInstruction(lastPos, code.Make(code.OpReturn))
c.scopes[c.scopeIndex].lastInstruction.Opcode = code.OpReturnValue c.scopes[c.scopeIndex].lastInstruction.Opcode = code.OpReturn
} }
type Bytecode struct { type Bytecode struct {

View File

@@ -243,6 +243,44 @@ func TestConditionals(t *testing.T) {
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
{
input: `
let x = 0; if (true) { x = 1; }; if (false) { x = 2; }
`,
expectedConstants: []interface{}{0, 1, 2},
expectedInstructions: []code.Instructions{
// 0000
code.Make(code.OpConstant, 0),
// 0003
code.Make(code.OpSetGlobal, 0),
// 0006
code.Make(code.OpTrue),
// 0007
code.Make(code.OpJumpNotTruthy, 19),
// 0010
code.Make(code.OpConstant, 1),
// 0013
code.Make(code.OpAssignGlobal, 0),
// 0016
code.Make(code.OpJump, 20),
// 0019
code.Make(code.OpNull),
// 0020
code.Make(code.OpFalse),
// 0021
code.Make(code.OpJumpNotTruthy, 33),
// 0024
code.Make(code.OpConstant, 2),
// 0027
code.Make(code.OpAssignGlobal, 0),
// 0030
code.Make(code.OpJump, 34),
// 0033
code.Make(code.OpNull),
// 0034
code.Make(code.OpPop),
},
},
} }
runCompilerTests(t, tests) runCompilerTests(t, tests)
@@ -457,7 +495,7 @@ func TestFunctions(t *testing.T) {
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -474,7 +512,7 @@ func TestFunctions(t *testing.T) {
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -491,7 +529,7 @@ func TestFunctions(t *testing.T) {
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -504,12 +542,13 @@ func TestFunctions(t *testing.T) {
runCompilerTests(t, tests) runCompilerTests(t, tests)
} }
func TestFunctionsWithoutReturnValue(t *testing.T) { func TestFunctionsWithoutReturn(t *testing.T) {
tests := []compilerTestCase{ tests := []compilerTestCase{
{ {
input: `fn() { }`, input: `fn() { }`,
expectedConstants: []interface{}{ expectedConstants: []interface{}{
[]code.Instructions{ []code.Instructions{
code.Make(code.OpNull),
code.Make(code.OpReturn), code.Make(code.OpReturn),
}, },
}, },
@@ -537,12 +576,12 @@ func TestClosures(t *testing.T) {
code.Make(code.OpGetFree, 0), code.Make(code.OpGetFree, 0),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
[]code.Instructions{ []code.Instructions{
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 0, 1), code.Make(code.OpClosure, 0, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -567,18 +606,18 @@ func TestClosures(t *testing.T) {
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
[]code.Instructions{ []code.Instructions{
code.Make(code.OpGetFree, 0), code.Make(code.OpGetFree, 0),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 0, 2), code.Make(code.OpClosure, 0, 2),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
[]code.Instructions{ []code.Instructions{
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 1, 1), code.Make(code.OpClosure, 1, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -619,7 +658,7 @@ func TestClosures(t *testing.T) {
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 2), code.Make(code.OpConstant, 2),
@@ -627,14 +666,14 @@ func TestClosures(t *testing.T) {
code.Make(code.OpGetFree, 0), code.Make(code.OpGetFree, 0),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 4, 2), code.Make(code.OpClosure, 4, 2),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpSetLocal, 0), code.Make(code.OpSetLocal, 0),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 5, 1), code.Make(code.OpClosure, 5, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -703,7 +742,7 @@ func TestFunctionCalls(t *testing.T) {
24, 24,
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 0), // The literal "24" code.Make(code.OpConstant, 0), // The literal "24"
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -721,7 +760,7 @@ func TestFunctionCalls(t *testing.T) {
24, 24,
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 0), // The literal "24" code.Make(code.OpConstant, 0), // The literal "24"
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -740,7 +779,7 @@ func TestFunctionCalls(t *testing.T) {
expectedConstants: []interface{}{ expectedConstants: []interface{}{
[]code.Instructions{ []code.Instructions{
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
24, 24,
}, },
@@ -765,7 +804,7 @@ func TestFunctionCalls(t *testing.T) {
code.Make(code.OpGetLocal, 1), code.Make(code.OpGetLocal, 1),
code.Make(code.OpPop), code.Make(code.OpPop),
code.Make(code.OpGetLocal, 2), code.Make(code.OpGetLocal, 2),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
24, 24,
25, 25,
@@ -800,6 +839,7 @@ func TestAssignmentStatementScopes(t *testing.T) {
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpAssignGlobal, 0), code.Make(code.OpAssignGlobal, 0),
code.Make(code.OpNull),
code.Make(code.OpReturn), code.Make(code.OpReturn),
}, },
}, },
@@ -822,6 +862,7 @@ func TestAssignmentStatementScopes(t *testing.T) {
code.Make(code.OpSetLocal, 0), code.Make(code.OpSetLocal, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpAssignLocal, 0), code.Make(code.OpAssignLocal, 0),
code.Make(code.OpNull),
code.Make(code.OpReturn), code.Make(code.OpReturn),
}, },
}, },
@@ -846,7 +887,7 @@ func TestLetStatementScopes(t *testing.T) {
55, 55,
[]code.Instructions{ []code.Instructions{
code.Make(code.OpGetGlobal, 0), code.Make(code.OpGetGlobal, 0),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -869,7 +910,7 @@ func TestLetStatementScopes(t *testing.T) {
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetLocal, 0), code.Make(code.OpSetLocal, 0),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -896,7 +937,7 @@ func TestLetStatementScopes(t *testing.T) {
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpGetLocal, 1), code.Make(code.OpGetLocal, 1),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -954,7 +995,7 @@ func TestBuiltins(t *testing.T) {
code.Make(code.OpGetBuiltin, 0), code.Make(code.OpGetBuiltin, 0),
code.Make(code.OpArray, 0), code.Make(code.OpArray, 0),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
@@ -982,7 +1023,7 @@ func TestRecursiveFunctions(t *testing.T) {
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSub), code.Make(code.OpSub),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
1, 1,
}, },
@@ -1011,7 +1052,7 @@ func TestRecursiveFunctions(t *testing.T) {
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSub), code.Make(code.OpSub),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
1, 1,
[]code.Instructions{ []code.Instructions{
@@ -1020,7 +1061,7 @@ func TestRecursiveFunctions(t *testing.T) {
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpConstant, 2), code.Make(code.OpConstant, 2),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
code.Make(code.OpReturnValue), code.Make(code.OpReturn),
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{

View File

@@ -14,7 +14,7 @@ var Builtins = []struct {
}{ }{
{ {
"len", "len",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "len", Fn: func(args ...Object) Object {
if len(args) != 1 { if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", return newError("wrong number of arguments. got=%d, want=1",
len(args)) len(args))
@@ -34,7 +34,7 @@ var Builtins = []struct {
}, },
{ {
"input", "input",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "input", Fn: func(args ...Object) Object {
if len(args) > 0 { if len(args) > 0 {
obj, ok := args[0].(*String) obj, ok := args[0].(*String)
if !ok { if !ok {
@@ -57,7 +57,7 @@ var Builtins = []struct {
}, },
{ {
"print", "print",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "print", Fn: func(args ...Object) Object {
for _, arg := range args { for _, arg := range args {
fmt.Println(arg.Inspect()) fmt.Println(arg.Inspect())
} }
@@ -68,7 +68,7 @@ var Builtins = []struct {
}, },
{ {
"first", "first",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "first", Fn: func(args ...Object) Object {
if len(args) != 1 { if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args)) return newError("wrong number of arguments. got=%d, want=1", len(args))
} }
@@ -87,7 +87,7 @@ var Builtins = []struct {
}, },
{ {
"last", "last",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "last", Fn: func(args ...Object) Object {
if len(args) != 1 { if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args)) return newError("wrong number of arguments. got=%d, want=1", len(args))
} }
@@ -107,7 +107,7 @@ var Builtins = []struct {
}, },
{ {
"rest", "rest",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "rest", Fn: func(args ...Object) Object {
if len(args) != 1 { if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", return newError("wrong number of arguments. got=%d, want=1",
len(args)) len(args))
@@ -131,7 +131,7 @@ var Builtins = []struct {
}, },
{ {
"push", "push",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "push", Fn: func(args ...Object) Object {
if len(args) != 2 { if len(args) != 2 {
return newError("wrong number of arguments. got=%d, want=2", return newError("wrong number of arguments. got=%d, want=2",
len(args)) len(args))
@@ -158,7 +158,7 @@ var Builtins = []struct {
}, },
{ {
"pop", "pop",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "pop", Fn: func(args ...Object) Object {
if len(args) != 1 { if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", return newError("wrong number of arguments. got=%d, want=1",
len(args)) len(args))
@@ -184,7 +184,7 @@ var Builtins = []struct {
}, },
{ {
"exit", "exit",
&Builtin{Fn: func(args ...Object) Object { &Builtin{Name: "exit", Fn: func(args ...Object) Object {
if len(args) == 1 { if len(args) == 1 {
if args[0].Type() != INTEGER_OBJ { if args[0].Type() != INTEGER_OBJ {
return newError("argument to `exit` must be INTEGER, got %s", return newError("argument to `exit` must be INTEGER, got %s",

View File

@@ -151,6 +151,7 @@ func (s *String) Clone() Object {
type BuiltinFunction func(args ...Object) Object type BuiltinFunction func(args ...Object) Object
type Builtin struct { type Builtin struct {
Name string
Fn BuiltinFunction Fn BuiltinFunction
} }
@@ -158,7 +159,7 @@ func (b Builtin) Type() ObjectType {
return BUILTIN_OBJ return BUILTIN_OBJ
} }
func (b Builtin) Inspect() string { func (b Builtin) Inspect() string {
return "builtin function" return fmt.Sprintf("<built-in function %s>", b.Name)
} }
type Array struct { type Array struct {

View File

@@ -247,7 +247,7 @@ func (vm *VM) Run() error {
return err return err
} }
case code.OpReturnValue: case code.OpReturn:
returnValue := vm.pop() returnValue := vm.pop()
frame := vm.popFrame() frame := vm.popFrame()
@@ -258,15 +258,6 @@ func (vm *VM) Run() error {
return err return err
} }
case code.OpReturn:
frame := vm.popFrame()
vm.sp = frame.basePointer - 1
err := vm.push(Null)
if err != nil {
return err
}
case code.OpSetLocal: case code.OpSetLocal:
localIndex := code.ReadUint8(ins[ip+1:]) localIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1 vm.currentFrame().ip += 1

View File

@@ -252,7 +252,8 @@ func TestConditionals(t *testing.T) {
{"if (1 > 2) { 10 } else { 20 }", 20}, {"if (1 > 2) { 10 } else { 20 }", 20},
{"if (1 > 2) { 10 }", Null}, {"if (1 > 2) { 10 }", Null},
{"if (false) { 10 }", Null}, {"if (false) { 10 }", Null},
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20}, //{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
{"let x = 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
} }
runVmTests(t, tests) runVmTests(t, tests)