From fe33fda0abb63d11447de94101c661998720412b Mon Sep 17 00:00:00 2001 From: csmith Date: Mon, 1 Apr 2024 17:44:15 -0400 Subject: [PATCH] misc fixes --- bench.sh | 9 +++++++++ examples/fibi.monkey | 8 +++++++- examples/fibt.monkey | 8 +++++++- internal/object/errors.go | 14 ++++++++++++++ internal/object/int.go | 3 +++ internal/vm/vm.go | 22 ++++++++++++++++++---- monkey.go | 5 ++++- repl.go | 32 +++++++++++++++++++++++++++++--- 8 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 bench.sh diff --git a/bench.sh b/bench.sh new file mode 100644 index 0000000..10acd90 --- /dev/null +++ b/bench.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +currentBranch="$(git branch --show-current)" + +hyperfine \ + -w 5 \ + -p "make build" \ + -n "$currentBranch" \ + './monkey examples/fib.monkey' \ No newline at end of file diff --git a/examples/fibi.monkey b/examples/fibi.monkey index bb31a98..f5af75b 100644 --- a/examples/fibi.monkey +++ b/examples/fibi.monkey @@ -15,4 +15,10 @@ fib := fn(n) { return a } -print(fib(35)) \ No newline at end of file +N := 35 + +if (len(args()) > 0) { + N = int(args()[0]) +} + +print(fib(N)) \ No newline at end of file diff --git a/examples/fibt.monkey b/examples/fibt.monkey index c7f6cfb..6ffa629 100644 --- a/examples/fibt.monkey +++ b/examples/fibt.monkey @@ -8,4 +8,10 @@ fib := fn(n, a, b) { return fib(n - 1, b, a + b) } -print(fib(35, 0, 1)) \ No newline at end of file +N := 35 + +if (len(args()) > 0) { + N = int(args()[0]) +} + +print(fib(N, 0, 1)) \ No newline at end of file diff --git a/internal/object/errors.go b/internal/object/errors.go index 4e2978a..a7295d0 100644 --- a/internal/object/errors.go +++ b/internal/object/errors.go @@ -18,3 +18,17 @@ func (e BinaryOpError) Error() string { func NewBinaryOpError(left, right Object, op string) BinaryOpError { return BinaryOpError{left, right, op} } + +// DivisionByZeroError is an error returned when dividing by zero +type DivisionByZeroError struct { + left Object +} + +func (e DivisionByZeroError) Error() string { + return fmt.Sprintf("cannot divide %s by zero", e.left) +} + +// NewDivisionByZeroError returns a new DivisionByZeroError +func NewDivisionByZeroError(left Object) DivisionByZeroError { + return DivisionByZeroError{left} +} diff --git a/internal/object/int.go b/internal/object/int.go index 3ffcd7c..7c6be32 100644 --- a/internal/object/int.go +++ b/internal/object/int.go @@ -65,6 +65,9 @@ func (i Integer) Mul(other Object) (Object, error) { func (i Integer) Div(other Object) (Object, error) { switch obj := other.(type) { case Integer: + if obj.Value == 0 { + return nil, NewDivisionByZeroError(i) + } return Integer{i.Value / obj.Value}, nil default: return nil, NewBinaryOpError(i, other, "/") diff --git a/internal/vm/vm.go b/internal/vm/vm.go index b8f8fb5..4096d7d 100644 --- a/internal/vm/vm.go +++ b/internal/vm/vm.go @@ -130,11 +130,19 @@ func (vm *VM) currentFrame() *Frame { } func (vm *VM) pushFrame(f Frame) { + if vm.fp >= MaxFrames { + panic("frame overflow") + } + vm.frames[vm.fp] = f vm.fp++ } func (vm *VM) popFrame() Frame { + if vm.fp == 0 { + panic("fame underflow") + } + vm.fp-- return vm.frames[vm.fp] } @@ -201,6 +209,10 @@ func (vm *VM) push(o object.Object) error { } func (vm *VM) pop() object.Object { + if vm.sp == 0 { + panic("stack underflow") + } + o := vm.stack[vm.sp-1] vm.sp-- return o @@ -768,20 +780,22 @@ func (vm *VM) Run() (err error) { if vm.Debug { start := time.Now() defer func() { + end := time.Now().Sub(start) total := 0 opcodes := make([]code.Opcode, 0, len(opcodeFreqs)) - for opcode := range opcodeFreqs { + for opcode, count := range opcodeFreqs { opcodes = append(opcodes, opcode) + total += count } 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("%d instructions executed in %s %d/µs", total, end, total/int(end.Microseconds())) + log.Print("Top Instructions:") + for _, opcode := range opcodes[:min(len(opcodes), 10)] { log.Printf("%10d %s", opcodeFreqs[opcode], opcode) } }() diff --git a/monkey.go b/monkey.go index 3c87f82..f73e708 100644 --- a/monkey.go +++ b/monkey.go @@ -81,12 +81,15 @@ func ExecFileVM(f string, args []string, debug, trace bool) (err error) { } else { bytecode, err = compile(f, debug) } - if err != nil { fmt.Println(err) return } + if debug { + log.Printf("Bytecode:\n%s\n", bytecode) + } + mvm := vm.New(f, bytecode) mvm.Debug = debug mvm.Trace = trace diff --git a/repl.go b/repl.go index 390753c..6263b19 100644 --- a/repl.go +++ b/repl.go @@ -30,13 +30,20 @@ func VmREPL(args []string, debug, trace bool) error { fmt.Println(err) return fmt.Errorf("error opening terminal: %w", err) } - defer term.Restore(0, initState) + defer func() { + if err := recover(); err != nil { + log.Printf("recovered from panic: %s", err) + } + term.Restore(0, initState) + }() atexit.Register(func() { term.Restore(0, initState) }) t := term.NewTerminal(os.Stdin, ">>> ") t.AutoCompleteCallback = autoComplete + + object.Args = args object.Stdout = t object.ExitFunction = atexit.Exit @@ -71,6 +78,10 @@ func VmREPL(args []string, debug, trace bool) error { continue } + if debug { + log.Printf("Bytecode:\n%s\n", c.Bytecode()) + } + mvm := vm.NewWithState("", c.Bytecode(), state) mvm.Debug = debug mvm.Trace = trace @@ -80,7 +91,7 @@ func VmREPL(args []string, debug, trace bool) error { continue } - if val := mvm.LastPoppedStackElem(); val.Type() != object.NullType { + if val := mvm.LastPoppedStackElem(); val != nil && val.Type() != object.NullType { fmt.Fprintln(t, val.Inspect()) } } @@ -137,7 +148,18 @@ func SimpleVmREPL(args []string, debug, trace bool) { reader = bufio.NewReader(os.Stdin) ) + defer func() { + if err := recover(); err != nil { + log.Printf("recovered from panic: %s", err) + } + }() + + t := term.NewTerminal(os.Stdin, ">>> ") + t.AutoCompleteCallback = autoComplete + object.Args = args + object.Stdout = t + object.ExitFunction = atexit.Exit PrintVersionInfo(os.Stdout) for { @@ -171,6 +193,10 @@ func SimpleVmREPL(args []string, debug, trace bool) { continue } + if debug { + log.Printf("Bytecode:\n%s\n", c.Bytecode()) + } + mvm := vm.NewWithState("", c.Bytecode(), state) mvm.Debug = debug mvm.Trace = trace @@ -180,7 +206,7 @@ func SimpleVmREPL(args []string, debug, trace bool) { continue } - if val := mvm.LastPoppedStackElem(); val.Type() != object.NullType { + if val := mvm.LastPoppedStackElem(); val != nil && val.Type() != object.NullType { fmt.Println(val.Inspect()) } }