Null my old friend

This commit is contained in:
Chuck Smith
2024-02-07 16:02:06 -05:00
parent 77401260a2
commit 6ba2d3abe4
5 changed files with 42 additions and 16 deletions

View File

@@ -26,6 +26,7 @@ const (
OpBang
OpJumpNotTruthy
OpJump
OpNull
)
type Definition struct {
@@ -49,6 +50,7 @@ var definitions = map[Opcode]*Definition{
OpBang: {"OpBang", []int{}},
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
OpJump: {"OpJump", []int{2}},
OpNull: {"OpNull", []int{}},
}
func Lookup(op byte) (*Definition, error) {

View File

@@ -134,16 +134,15 @@ func (c *Compiler) Compile(node ast.Node) error {
c.removeLastPop()
}
// Emit an `OnJump` with a bogus value
jumpPos := c.emit(code.OpJump, 9999)
afterConsequencePos := len(c.instructions)
c.changeOperand(jumpNotTruthyPos, afterConsequencePos)
if node.Alternative == nil {
afterConsequencePos := len(c.instructions)
c.changeOperand(jumpNotTruthyPos, afterConsequencePos)
c.emit(code.OpNull)
} else {
// Emit an `OnJump` with a bogus value
jumpPos := c.emit(code.OpJump, 9999)
afterConsequencePos := len(c.instructions)
c.changeOperand(jumpNotTruthyPos, afterConsequencePos)
err = c.Compile(node.Alternative)
if err != nil {
return err
@@ -152,12 +151,11 @@ func (c *Compiler) Compile(node ast.Node) error {
if c.lastInstructionIsPop() {
c.removeLastPop()
}
afterAlternativePos := len(c.instructions)
c.changeOperand(jumpPos, afterAlternativePos)
}
afterAlternativePos := len(c.instructions)
c.changeOperand(jumpPos, afterAlternativePos)
case *ast.BlockStatement:
for _, s := range node.Statements {
err := c.Compile(s)

View File

@@ -182,17 +182,22 @@ func TestConditionals(t *testing.T) {
`,
expectedConstants: []interface{}{10, 3333},
expectedInstructions: []code.Instructions{
// 0000
code.Make(code.OpTrue),
// 0001
code.Make(code.OpJumpNotTruthy, 7),
code.Make(code.OpJumpNotTruthy, 10),
// 0004
code.Make(code.OpConstant, 0),
// 0007
code.Make(code.OpPop),
// 0008
code.Make(code.OpConstant, 1),
code.Make(code.OpJump, 11),
// 0010
code.Make(code.OpNull),
// 0011
code.Make(code.OpPop),
// 0012
code.Make(code.OpConstant, 1),
// 0015
code.Make(code.OpPop),
},
}, {
input: `

View File

@@ -9,6 +9,7 @@ import (
const StackSize = 2048
var Null = &object.Null{}
var True = &object.Boolean{Value: true}
var False = &object.Boolean{Value: false}
@@ -99,6 +100,12 @@ func (vm *VM) Run() error {
if !isTruthy(condition) {
ip = pos - 1
}
case code.OpNull:
err := vm.push(Null)
if err != nil {
return err
}
}
}
@@ -111,6 +118,9 @@ func isTruthy(obj object.Object) bool {
case *object.Boolean:
return obj.Value
case *object.Null:
return false
default:
return true
}
@@ -211,6 +221,8 @@ func (vm *VM) executeBangOperator() error {
return vm.push(False)
case False:
return vm.push(True)
case Null:
return vm.push(True)
default:
return vm.push(False)
}

View File

@@ -54,6 +54,11 @@ func testExpectedObject(t *testing.T, expected interface{}, actual object.Object
if err != nil {
t.Errorf("testBooleanObject failed: %s", err)
}
case *object.Null:
if actual != Null {
t.Errorf("object is not Null: %T (%+v)", actual, actual)
}
}
}
@@ -138,6 +143,7 @@ func TestBooleanExpressions(t *testing.T) {
{"!!true", true},
{"!!false", false},
{"!!5", true},
{"!(if (false) { 5; })", true},
}
runVmTests(t, tests)
@@ -152,6 +158,9 @@ func TestConditionals(t *testing.T) {
{"if (1 < 2) { 10 }", 10},
{"if (1 < 2) { 10 } else { 20 }", 10},
{"if (1 > 2) { 10 } else { 20 }", 20},
{"if (1 > 2) { 10 }", Null},
{"if (false) { 10 }", Null},
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
}
runVmTests(t, tests)