Change assignment into expressions
Some checks failed
Test / build (push) Waiting to run
Build / build (push) Has been cancelled

This commit is contained in:
Chuck Smith
2024-03-19 20:30:30 -04:00
parent aa0582ed72
commit be81b9a6d6
12 changed files with 478 additions and 153 deletions

View File

@@ -2,6 +2,7 @@ package compiler
import (
"fmt"
"github.com/stretchr/testify/assert"
"monkey/ast"
"monkey/code"
"monkey/lexer"
@@ -16,6 +17,12 @@ type compilerTestCase struct {
expectedInstructions []code.Instructions
}
type compilerTestCase2 struct {
input string
constants []interface{}
instructions string
}
func TestIntegerArithmetic(t *testing.T) {
tests := []compilerTestCase{
{
@@ -264,34 +271,30 @@ func TestConditionals(t *testing.T) {
// 0006
code.Make(code.OpTrue),
// 0007
code.Make(code.OpJumpNotTruthy, 20),
code.Make(code.OpJumpNotTruthy, 19),
// 0010
code.Make(code.OpConstant, 1),
// 0013
code.Make(code.OpAssignGlobal, 0),
// 0016
// 0018
code.Make(code.OpJump, 20),
// 0019
code.Make(code.OpNull),
// 0017
code.Make(code.OpJump, 21),
// 0020
code.Make(code.OpNull),
// 0021
code.Make(code.OpPop),
// 0022
// 0021
code.Make(code.OpFalse),
// 0023
code.Make(code.OpJumpNotTruthy, 36),
// 0025
// 0022
code.Make(code.OpJumpNotTruthy, 34),
// 0024
code.Make(code.OpConstant, 2),
// 0029
// 0028
code.Make(code.OpAssignGlobal, 0),
// 0032
code.Make(code.OpJump, 35),
// 0035
code.Make(code.OpNull),
// 0033
code.Make(code.OpJump, 37),
// 0036
code.Make(code.OpNull),
// 0037
code.Make(code.OpPop),
},
},
@@ -475,7 +478,7 @@ func TestIndexExpressions(t *testing.T) {
code.Make(code.OpConstant, 3),
code.Make(code.OpConstant, 4),
code.Make(code.OpAdd),
code.Make(code.OpIndex),
code.Make(code.OpGetItem),
code.Make(code.OpPop),
},
},
@@ -489,7 +492,7 @@ func TestIndexExpressions(t *testing.T) {
code.Make(code.OpConstant, 2),
code.Make(code.OpConstant, 3),
code.Make(code.OpSub),
code.Make(code.OpIndex),
code.Make(code.OpGetItem),
code.Make(code.OpPop),
},
},
@@ -823,6 +826,21 @@ func TestFunctionCalls(t *testing.T) {
runCompilerTests(t, tests)
}
func TestAssignmentExpressions(t *testing.T) {
tests := []compilerTestCase2{
{
input: `
let x = 1
x = 2
`,
constants: []interface{}{1, 2},
instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpConstant 1\n0009 OpAssignGlobal 0\n0012 OpPop\n",
},
}
runCompilerTests2(t, tests)
}
func TestAssignmentStatementScopes(t *testing.T) {
tests := []compilerTestCase{
{
@@ -1085,14 +1103,16 @@ func TestIteration(t *testing.T) {
// 0000
code.Make(code.OpTrue),
// 0001
code.Make(code.OpJumpNotTruthy, 10),
code.Make(code.OpJumpNotTruthy, 11),
// 0004
code.Make(code.OpConstant, 0),
// 0007
code.Make(code.OpPop),
// 0008
code.Make(code.OpJump, 0),
// 0010
code.Make(code.OpNoop),
// 0011
code.Make(code.OpNull),
// 0012
code.Make(code.OpPop),
},
},
@@ -1127,6 +1147,25 @@ func runCompilerTests(t *testing.T, tests []compilerTestCase) {
}
}
func runCompilerTests2(t *testing.T, tests []compilerTestCase2) {
t.Helper()
assert := assert.New(t)
for _, tt := range tests {
program := parse(tt.input)
compiler := New()
err := compiler.Compile(program)
assert.NoError(err)
bytecode := compiler.Bytecode()
assert.Equal(tt.instructions, bytecode.Instructions.String())
testConstants2(t, tt.constants, bytecode.Constants)
}
}
func parse(input string) *ast.Program {
l := lexer.New(input)
p := parser.New(l)
@@ -1193,6 +1232,28 @@ func testConstants(t *testing.T, expected []interface{}, actual []object.Object)
return nil
}
func testConstants2(t *testing.T, expected []interface{}, actual []object.Object) {
assert := assert.New(t)
assert.Equal(len(expected), len(actual))
for i, constant := range expected {
switch constant := constant.(type) {
case []code.Instructions:
fn, ok := actual[i].(*object.CompiledFunction)
assert.True(ok)
assert.Equal(constant, fn.Instructions.String())
case string:
assert.Equal(constant, actual[i].(*object.String).Value)
case int:
assert.Equal(int64(constant), actual[i].(*object.Integer).Value)
}
}
}
func testIntegerObject(expected int64, actual object.Object) interface{} {
result, ok := actual.(*object.Integer)
if !ok {