Null my old friend
This commit is contained in:
@@ -26,6 +26,7 @@ const (
|
|||||||
OpBang
|
OpBang
|
||||||
OpJumpNotTruthy
|
OpJumpNotTruthy
|
||||||
OpJump
|
OpJump
|
||||||
|
OpNull
|
||||||
)
|
)
|
||||||
|
|
||||||
type Definition struct {
|
type Definition struct {
|
||||||
@@ -49,6 +50,7 @@ var definitions = map[Opcode]*Definition{
|
|||||||
OpBang: {"OpBang", []int{}},
|
OpBang: {"OpBang", []int{}},
|
||||||
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
|
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
|
||||||
OpJump: {"OpJump", []int{2}},
|
OpJump: {"OpJump", []int{2}},
|
||||||
|
OpNull: {"OpNull", []int{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Lookup(op byte) (*Definition, error) {
|
func Lookup(op byte) (*Definition, error) {
|
||||||
|
|||||||
@@ -134,16 +134,15 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
c.removeLastPop()
|
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 {
|
if node.Alternative == nil {
|
||||||
afterConsequencePos := len(c.instructions)
|
c.emit(code.OpNull)
|
||||||
c.changeOperand(jumpNotTruthyPos, afterConsequencePos)
|
|
||||||
} else {
|
} 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)
|
err = c.Compile(node.Alternative)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -152,12 +151,11 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
if c.lastInstructionIsPop() {
|
if c.lastInstructionIsPop() {
|
||||||
c.removeLastPop()
|
c.removeLastPop()
|
||||||
}
|
}
|
||||||
|
|
||||||
afterAlternativePos := len(c.instructions)
|
|
||||||
c.changeOperand(jumpPos, afterAlternativePos)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
afterAlternativePos := len(c.instructions)
|
||||||
|
c.changeOperand(jumpPos, afterAlternativePos)
|
||||||
|
|
||||||
case *ast.BlockStatement:
|
case *ast.BlockStatement:
|
||||||
for _, s := range node.Statements {
|
for _, s := range node.Statements {
|
||||||
err := c.Compile(s)
|
err := c.Compile(s)
|
||||||
|
|||||||
@@ -182,17 +182,22 @@ func TestConditionals(t *testing.T) {
|
|||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{10, 3333},
|
expectedConstants: []interface{}{10, 3333},
|
||||||
expectedInstructions: []code.Instructions{
|
expectedInstructions: []code.Instructions{
|
||||||
|
// 0000
|
||||||
code.Make(code.OpTrue),
|
code.Make(code.OpTrue),
|
||||||
// 0001
|
// 0001
|
||||||
code.Make(code.OpJumpNotTruthy, 7),
|
code.Make(code.OpJumpNotTruthy, 10),
|
||||||
// 0004
|
// 0004
|
||||||
code.Make(code.OpConstant, 0),
|
code.Make(code.OpConstant, 0),
|
||||||
// 0007
|
// 0007
|
||||||
code.Make(code.OpPop),
|
code.Make(code.OpJump, 11),
|
||||||
// 0008
|
// 0010
|
||||||
code.Make(code.OpConstant, 1),
|
code.Make(code.OpNull),
|
||||||
// 0011
|
// 0011
|
||||||
code.Make(code.OpPop),
|
code.Make(code.OpPop),
|
||||||
|
// 0012
|
||||||
|
code.Make(code.OpConstant, 1),
|
||||||
|
// 0015
|
||||||
|
code.Make(code.OpPop),
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
input: `
|
input: `
|
||||||
|
|||||||
12
vm/vm.go
12
vm/vm.go
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
const StackSize = 2048
|
const StackSize = 2048
|
||||||
|
|
||||||
|
var Null = &object.Null{}
|
||||||
var True = &object.Boolean{Value: true}
|
var True = &object.Boolean{Value: true}
|
||||||
var False = &object.Boolean{Value: false}
|
var False = &object.Boolean{Value: false}
|
||||||
|
|
||||||
@@ -99,6 +100,12 @@ func (vm *VM) Run() error {
|
|||||||
if !isTruthy(condition) {
|
if !isTruthy(condition) {
|
||||||
ip = pos - 1
|
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:
|
case *object.Boolean:
|
||||||
return obj.Value
|
return obj.Value
|
||||||
|
|
||||||
|
case *object.Null:
|
||||||
|
return false
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -211,6 +221,8 @@ func (vm *VM) executeBangOperator() error {
|
|||||||
return vm.push(False)
|
return vm.push(False)
|
||||||
case False:
|
case False:
|
||||||
return vm.push(True)
|
return vm.push(True)
|
||||||
|
case Null:
|
||||||
|
return vm.push(True)
|
||||||
default:
|
default:
|
||||||
return vm.push(False)
|
return vm.push(False)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ func testExpectedObject(t *testing.T, expected interface{}, actual object.Object
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("testBooleanObject failed: %s", err)
|
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},
|
{"!!true", true},
|
||||||
{"!!false", false},
|
{"!!false", false},
|
||||||
{"!!5", true},
|
{"!!5", true},
|
||||||
|
{"!(if (false) { 5; })", true},
|
||||||
}
|
}
|
||||||
|
|
||||||
runVmTests(t, tests)
|
runVmTests(t, tests)
|
||||||
@@ -152,6 +158,9 @@ func TestConditionals(t *testing.T) {
|
|||||||
{"if (1 < 2) { 10 }", 10},
|
{"if (1 < 2) { 10 }", 10},
|
||||||
{"if (1 < 2) { 10 } else { 20 }", 10},
|
{"if (1 < 2) { 10 } else { 20 }", 10},
|
||||||
{"if (1 > 2) { 10 } else { 20 }", 20},
|
{"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)
|
runVmTests(t, tests)
|
||||||
|
|||||||
Reference in New Issue
Block a user