further improvements
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"monkey/internal/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
@@ -20,10 +21,6 @@ const StackSize = 2048
|
||||
const GlobalsSize = 65536
|
||||
const MaxFrames = 1024
|
||||
|
||||
var Null = object.Null{}
|
||||
var True = object.Boolean{Value: true}
|
||||
var False = object.Boolean{Value: false}
|
||||
|
||||
func isTruthy(obj object.Object) bool {
|
||||
switch obj := obj.(type) {
|
||||
|
||||
@@ -114,6 +111,7 @@ func (s *VMState) ExportedHash() *object.Hash {
|
||||
|
||||
type VM struct {
|
||||
Debug bool
|
||||
Trace bool
|
||||
|
||||
state *VMState
|
||||
|
||||
@@ -143,9 +141,9 @@ func (vm *VM) popFrame() Frame {
|
||||
|
||||
// New constructs a new monkey-lang bytecode virtual machine
|
||||
func New(fn string, bytecode *compiler.Bytecode) *VM {
|
||||
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
|
||||
mainClosure := &object.Closure{Fn: mainFn}
|
||||
mainFrame := NewFrame(mainClosure, 0)
|
||||
mainFn := object.CompiledFunction{Instructions: bytecode.Instructions}
|
||||
mainClosure := object.Closure{Fn: &mainFn}
|
||||
mainFrame := NewFrame(&mainClosure, 0)
|
||||
|
||||
frames := make([]Frame, MaxFrames)
|
||||
frames[0] = mainFrame
|
||||
@@ -169,9 +167,9 @@ func New(fn string, bytecode *compiler.Bytecode) *VM {
|
||||
}
|
||||
|
||||
func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM {
|
||||
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
|
||||
mainClosure := &object.Closure{Fn: mainFn}
|
||||
mainFrame := NewFrame(mainClosure, 0)
|
||||
mainFn := object.CompiledFunction{Instructions: bytecode.Instructions}
|
||||
mainClosure := object.Closure{Fn: &mainFn}
|
||||
mainFrame := NewFrame(&mainClosure, 0)
|
||||
|
||||
frames := make([]Frame, MaxFrames)
|
||||
frames[0] = mainFrame
|
||||
@@ -222,13 +220,13 @@ func (vm *VM) executeConstant() error {
|
||||
func (vm *VM) executeAssignGlobal() error {
|
||||
globalIndex := vm.currentFrame().ReadUint16()
|
||||
vm.state.Globals[globalIndex] = vm.pop()
|
||||
return vm.push(Null)
|
||||
return vm.push(object.NULL)
|
||||
}
|
||||
|
||||
func (vm *VM) executeAssignLocal() error {
|
||||
localIndex := vm.currentFrame().ReadUint8()
|
||||
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = vm.pop()
|
||||
return vm.push(Null)
|
||||
return vm.push(object.NULL)
|
||||
}
|
||||
|
||||
func (vm *VM) executeSetGlobal() error {
|
||||
@@ -241,7 +239,7 @@ func (vm *VM) executeSetGlobal() error {
|
||||
vm.state.Globals[globalIndex] = ref
|
||||
}
|
||||
|
||||
return vm.push(Null)
|
||||
return vm.push(object.NULL)
|
||||
}
|
||||
|
||||
func (vm *VM) executeGetGlobal() error {
|
||||
@@ -259,7 +257,7 @@ func (vm *VM) executeSetLocal() error {
|
||||
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = ref
|
||||
}
|
||||
|
||||
return vm.push(Null)
|
||||
return vm.push(object.NULL)
|
||||
}
|
||||
|
||||
func (vm *VM) executeGetLocal() error {
|
||||
@@ -283,15 +281,15 @@ func (vm *VM) executeCurrentClosure() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeTrue() error {
|
||||
return vm.push(True)
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeFalse() error {
|
||||
return vm.push(False)
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeNull() error {
|
||||
return vm.push(Null)
|
||||
return vm.push(object.NULL)
|
||||
}
|
||||
|
||||
func (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
|
||||
@@ -547,9 +545,9 @@ func (vm *VM) executeEqual() error {
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val == 0 {
|
||||
return vm.push(True)
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(False)
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, "==")
|
||||
@@ -562,9 +560,9 @@ func (vm *VM) executeNotEqual() error {
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val != 0 {
|
||||
return vm.push(True)
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(False)
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, "!=")
|
||||
@@ -577,9 +575,9 @@ func (vm *VM) executeGreaterThan() error {
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val == 1 {
|
||||
return vm.push(True)
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(False)
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, ">")
|
||||
@@ -592,9 +590,9 @@ func (vm *VM) executeGreaterThanOrEqual() error {
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val >= 0 {
|
||||
return vm.push(True)
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(False)
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, ">")
|
||||
@@ -630,7 +628,7 @@ func (vm *VM) executeSetItem() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return vm.push(Null)
|
||||
return vm.push(object.NULL)
|
||||
}
|
||||
|
||||
return fmt.Errorf(
|
||||
@@ -664,7 +662,7 @@ func (vm *VM) executeCall() error {
|
||||
switch callee := callee.(type) {
|
||||
case *object.Closure:
|
||||
return vm.callClosure(callee, args)
|
||||
case *object.Builtin:
|
||||
case object.Builtin:
|
||||
return vm.callBuiltin(callee, args)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
@@ -727,7 +725,7 @@ func (vm *VM) callClosure(cl *object.Closure, numArgs int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
|
||||
func (vm *VM) callBuiltin(builtin object.Builtin, numArgs int) error {
|
||||
args := vm.stack[vm.sp-numArgs : vm.sp]
|
||||
|
||||
result := builtin.Fn(args...)
|
||||
@@ -739,7 +737,7 @@ func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := vm.push(Null)
|
||||
err := vm.push(object.NULL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -761,8 +759,8 @@ func (vm *VM) pushClosure(constIndex, numFree int) error {
|
||||
}
|
||||
vm.sp = vm.sp - numFree
|
||||
|
||||
closure := &object.Closure{Fn: function, Free: free}
|
||||
return vm.push(closure)
|
||||
closure := object.Closure{Fn: function, Free: free}
|
||||
return vm.push(&closure)
|
||||
}
|
||||
|
||||
func (vm *VM) loadModule(name object.Object) error {
|
||||
@@ -788,20 +786,38 @@ func (vm *VM) LastPoppedStackElem() object.Object {
|
||||
}
|
||||
|
||||
func (vm *VM) Run() (err error) {
|
||||
var n int
|
||||
var (
|
||||
op code.Opcode
|
||||
opcodeFreqs = make(map[code.Opcode]int)
|
||||
)
|
||||
|
||||
if vm.Debug {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Printf("%d instructions executeuted in %s", n, time.Now().Sub(start))
|
||||
total := 0
|
||||
opcodes := make([]code.Opcode, 0, len(opcodeFreqs))
|
||||
|
||||
for opcode := range opcodeFreqs {
|
||||
opcodes = append(opcodes, opcode)
|
||||
}
|
||||
|
||||
sort.SliceStable(opcodes, func(i, j int) bool {
|
||||
return opcodeFreqs[opcodes[i]] > opcodeFreqs[opcodes[j]]
|
||||
})
|
||||
|
||||
log.Printf("%d instructions executed in %s", total, time.Now().Sub(start))
|
||||
log.Print("Top 10 instructions:")
|
||||
for _, opcode := range opcodes[:10] {
|
||||
log.Printf("%10d %s", opcodeFreqs[opcode], opcode)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for err == nil {
|
||||
op := vm.currentFrame().ReadNextOp()
|
||||
op = vm.currentFrame().ReadNextOp()
|
||||
|
||||
if vm.Debug {
|
||||
n++
|
||||
opcodeFreqs[op]++
|
||||
log.Printf(
|
||||
"%-25s %-20s\n",
|
||||
fmt.Sprintf("%04d %s", vm.currentFrame().ip, op),
|
||||
@@ -949,7 +965,7 @@ func (vm *VM) Run() (err error) {
|
||||
err = fmt.Errorf("unhandled opcode: %s", op)
|
||||
}
|
||||
|
||||
if vm.Debug {
|
||||
if vm.Trace {
|
||||
log.Printf(
|
||||
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
||||
"", vm.currentFrame().ip, vm.fp-1, vm.sp,
|
||||
|
||||
@@ -130,7 +130,7 @@ func testExpectedObject(t *testing.T, expected interface{}, actual object.Object
|
||||
}
|
||||
|
||||
case object.Null:
|
||||
if actual != Null {
|
||||
if actual != object.NULL {
|
||||
t.Errorf("object is not Null: %T (%+v)", actual, actual)
|
||||
}
|
||||
|
||||
@@ -302,14 +302,14 @@ 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 (1 > 2) { 10 }", object.NULL},
|
||||
{"if (false) { 10 }", object.NULL},
|
||||
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
|
||||
{"if (true) { a := 5; }", Null},
|
||||
{"if (true) { 10; a := 5; }", Null},
|
||||
{"if (false) { 10 } else { b := 5; }", Null},
|
||||
{"if (false) { 10 } else { 10; b := 5; }", Null},
|
||||
{"if (true) { a := 5; } else { 10 }", Null},
|
||||
{"if (true) { a := 5; }", object.NULL},
|
||||
{"if (true) { 10; a := 5; }", object.NULL},
|
||||
{"if (false) { 10 } else { b := 5; }", object.NULL},
|
||||
{"if (false) { 10 } else { 10; b := 5; }", object.NULL},
|
||||
{"if (true) { a := 5; } else { 10 }", object.NULL},
|
||||
{"x := 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
||||
{"if (1 < 2) { 10 } else if (1 == 2) { 20 }", 10},
|
||||
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
||||
@@ -430,13 +430,13 @@ func TestIndexExpressions(t *testing.T) {
|
||||
{"[1, 2, 3][1]", 2},
|
||||
{"[1, 2, 3][0 + 2]", 3},
|
||||
{"[[1, 1, 1]][0][0]", 1},
|
||||
{"[][0]", Null},
|
||||
{"[1, 2, 3][99]", Null},
|
||||
{"[1][-1]", Null},
|
||||
{"[][0]", object.NULL},
|
||||
{"[1, 2, 3][99]", object.NULL},
|
||||
{"[1][-1]", object.NULL},
|
||||
{"{1: 1, 2: 2}[1]", 1},
|
||||
{"{1: 1, 2: 2}[2]", 2},
|
||||
{"{1: 1}[0]", Null},
|
||||
{"{}[0]", Null},
|
||||
{"{1: 1}[0]", object.NULL},
|
||||
{"{}[0]", object.NULL},
|
||||
{`"abc"[0]`, "a"},
|
||||
{`"abc"[1]`, "b"},
|
||||
{`"abc"[2]`, "c"},
|
||||
@@ -506,7 +506,7 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
|
||||
noReturn := fn() { };
|
||||
noReturn();
|
||||
`,
|
||||
expected: Null,
|
||||
expected: object.NULL,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
@@ -515,7 +515,7 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
|
||||
noReturn();
|
||||
noReturnTwo();
|
||||
`,
|
||||
expected: Null,
|
||||
expected: object.NULL,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -724,16 +724,16 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||
},
|
||||
{`len([1, 2, 3])`, 3},
|
||||
{`len([])`, 0},
|
||||
{`print("hello", "world!")`, Null},
|
||||
{`print("hello", "world!")`, object.NULL},
|
||||
{`first([1, 2, 3])`, 1},
|
||||
{`first([])`, Null},
|
||||
{`first([])`, object.NULL},
|
||||
{`first(1)`,
|
||||
&object.Error{
|
||||
Message: "TypeError: first() expected argument #1 to be `array` got `int`",
|
||||
},
|
||||
},
|
||||
{`last([1, 2, 3])`, 3},
|
||||
{`last([])`, Null},
|
||||
{`last([])`, object.NULL},
|
||||
{`last(1)`,
|
||||
&object.Error{
|
||||
Message: "TypeError: last() expected argument #1 to be `array` got `int`",
|
||||
|
||||
Reference in New Issue
Block a user