bunch of changes
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -84,6 +84,7 @@ fabric.properties
|
||||
*.out
|
||||
*.swo
|
||||
*.swn
|
||||
*.pyc
|
||||
*.prof
|
||||
|
||||
**/.DS_Store
|
||||
@@ -91,3 +92,4 @@ fabric.properties
|
||||
/dist
|
||||
/.vscode
|
||||
/monkey
|
||||
/examples/fib
|
||||
@@ -12,27 +12,29 @@ func main() {
|
||||
compile bool
|
||||
version bool
|
||||
simple bool
|
||||
debug bool
|
||||
)
|
||||
|
||||
flag.BoolVar(&compile, "c", false, "Compile a monkey file into a '.monkeyc' bytecode file.")
|
||||
flag.BoolVar(&simple, "s", false, "Use simple REPL instead of opening a terminal.")
|
||||
flag.BoolVar(&version, "v", false, "Print Monkey version information.")
|
||||
flag.BoolVar(&compile, "c", false, "Compile a monkey file into a '.mc' bytecode file")
|
||||
flag.BoolVar(&simple, "s", false, "Use simple REPL instead of opening a terminal")
|
||||
flag.BoolVar(&version, "v", false, "Print Monkey version information")
|
||||
flag.BoolVar(&debug, "d", false, "Enable debug mode")
|
||||
flag.Parse()
|
||||
|
||||
switch {
|
||||
case compile:
|
||||
monkey.CompileFiles(flag.Args())
|
||||
monkey.CompileFiles(flag.Args(), debug)
|
||||
|
||||
case version:
|
||||
monkey.PrintVersionInfo(os.Stdout)
|
||||
|
||||
case flag.NArg() > 0:
|
||||
monkey.ExecFileVM(flag.Arg(0))
|
||||
monkey.ExecFileVM(flag.Arg(0), flag.Args()[1:], debug)
|
||||
|
||||
case simple:
|
||||
monkey.SimpleVmREPL()
|
||||
monkey.SimpleVmREPL(flag.Args(), debug)
|
||||
|
||||
default:
|
||||
monkey.VmREPL()
|
||||
monkey.VmREPL(flag.Args(), debug)
|
||||
}
|
||||
}
|
||||
|
||||
33
examples/fib.c
Normal file
33
examples/fib.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int n = 35;
|
||||
|
||||
int fib(int x) {
|
||||
if (x < 2) {
|
||||
return x;
|
||||
}
|
||||
return fib(x - 1) + fib(x - 2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc > 1) {
|
||||
char*p;
|
||||
|
||||
errno = 0;
|
||||
long conv = strtol(argv[1], &p, 10);
|
||||
|
||||
// Check for errors: e.g., the string does not represent an integer
|
||||
// or the integer is larger than int
|
||||
if (errno != 0 || *p != '\0' || conv > INT_MAX || conv < INT_MIN) {
|
||||
printf("invalid argument: %s\n", p);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
n = conv;
|
||||
}
|
||||
|
||||
printf("%d\n", fib(n));
|
||||
}
|
||||
25
examples/fib.go
Normal file
25
examples/fib.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func fib(x int) int {
|
||||
if x < 2 {
|
||||
return x
|
||||
}
|
||||
return fib(x-1) + fib(x-2)
|
||||
}
|
||||
|
||||
var n = 35
|
||||
|
||||
func main() {
|
||||
if len(os.Args) > 2 {
|
||||
n, _ = strconv.Atoi(os.Args[1])
|
||||
}
|
||||
fmt.Println(fib(n))
|
||||
}
|
||||
@@ -7,7 +7,7 @@ fib := fn(x) {
|
||||
|
||||
N := 35
|
||||
|
||||
if (len(args()) == 1) {
|
||||
if (len(args()) > 0) {
|
||||
N = int(args()[0])
|
||||
}
|
||||
|
||||
|
||||
22
examples/fib.py
Normal file
22
examples/fib.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
print(sys.argv)
|
||||
|
||||
n = 35
|
||||
|
||||
def fib(x):
|
||||
if (x < 2):
|
||||
return x
|
||||
return fib(x-1) + fib(x-2)
|
||||
|
||||
def main():
|
||||
global n
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
n = int(sys.argv[1])
|
||||
|
||||
print("{}\n".format(fib(n)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
10
examples/fib.tau
Normal file
10
examples/fib.tau
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env tau
|
||||
|
||||
fib = fn(n) {
|
||||
if n < 2 {
|
||||
return n
|
||||
}
|
||||
fib(n-1) + fib(n-2)
|
||||
}
|
||||
|
||||
println(fib(35))
|
||||
6
go.mod
6
go.mod
@@ -3,13 +3,15 @@ module monkey
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/pkg/profile v1.7.0
|
||||
golang.org/x/term v0.15.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tebeka/atexit v0.3.0
|
||||
golang.org/x/term v0.18.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
7
go.sum
7
go.sum
@@ -12,14 +12,21 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tebeka/atexit v0.3.0/go.mod h1:WJmSUSmMT7WoR7etUOaGBVXk+f5/ZJ+67qwuedq7Fbs=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -38,13 +38,13 @@ func (p *Program) TokenLiteral() string {
|
||||
}
|
||||
|
||||
func (p *Program) String() string {
|
||||
var out bytes.Buffer
|
||||
var s strings.Builder
|
||||
|
||||
for _, s := range p.Statements {
|
||||
out.WriteString(s.String())
|
||||
for _, stmt := range p.Statements {
|
||||
s.WriteString(fmt.Sprintf("%s\n", stmt.String()))
|
||||
}
|
||||
|
||||
return out.String()
|
||||
return s.String()
|
||||
}
|
||||
|
||||
type ReturnStatement struct {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
Args []string = os.Args
|
||||
Args []string = os.Args[1:] // Skip the monkey binary itself
|
||||
Stdin io.Reader = os.Stdin
|
||||
Stdout io.Writer = os.Stdout
|
||||
Stderr io.Writer = os.Stderr
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type cachedFrame struct {
|
||||
Key uint // pointer to key
|
||||
Value *Frame // pointer to value
|
||||
Frequency uint // frequency of access
|
||||
}
|
||||
|
||||
type frequencyNode struct {
|
||||
count uint // frequency count - never decreases
|
||||
valuesList *list.List // valuesList contains pointer to the head of values linked list
|
||||
inner *list.Element // actual content of the next element
|
||||
}
|
||||
|
||||
// creates a new frequency list node with the given count
|
||||
func newFrequencyNode(count uint) *frequencyNode {
|
||||
return &frequencyNode{
|
||||
count: count,
|
||||
valuesList: list.New(),
|
||||
inner: nil,
|
||||
}
|
||||
}
|
||||
|
||||
type keyRefNode struct {
|
||||
inner *list.Element // contains the actual value wrapped by a list element
|
||||
parentFreqNode *list.Element // contains reference to the frequency node element
|
||||
keyRef uint // contains pointer to the key
|
||||
valueRef *Frame // value
|
||||
}
|
||||
|
||||
// creates a new KeyRef node which is used to represent the value in linked list
|
||||
func newKeyRefNode(keyRef uint, valueRef *Frame, parent *list.Element) *keyRefNode {
|
||||
return &keyRefNode{
|
||||
inner: nil,
|
||||
parentFreqNode: parent,
|
||||
keyRef: keyRef,
|
||||
valueRef: valueRef,
|
||||
}
|
||||
}
|
||||
|
||||
// FrameCache implements all the methods and data-structures required for a LFU cache for caching frames.
|
||||
type FrameCache struct {
|
||||
rwLock sync.RWMutex // rwLock is a read-write mutex which provides concurrent reads but exclusive writes
|
||||
lookupTable map[uint]*keyRefNode // a hash table of <KeyType, *ValueType> for quick reference of values based on keys
|
||||
frequencies *list.List // internal linked list that contains frequency mapping
|
||||
maxSize uint // maxSize represents the maximum number of elements that can be in the cache before eviction
|
||||
}
|
||||
|
||||
// MaxSize returns the maximum size of the cache at that point in time
|
||||
func (lfu *FrameCache) MaxSize() uint {
|
||||
lfu.rwLock.RLock()
|
||||
defer lfu.rwLock.RUnlock()
|
||||
|
||||
return lfu.maxSize
|
||||
}
|
||||
|
||||
// CurrentSize returns the number of elements in that cache
|
||||
func (lfu *FrameCache) CurrentSize() uint {
|
||||
lfu.rwLock.RLock()
|
||||
defer lfu.rwLock.RUnlock()
|
||||
|
||||
return uint(len(lfu.lookupTable))
|
||||
}
|
||||
|
||||
// IsFull checks if the LFU cache is full
|
||||
func (lfu *FrameCache) IsFull() bool {
|
||||
lfu.rwLock.RLock()
|
||||
defer lfu.rwLock.RUnlock()
|
||||
|
||||
return uint(len(lfu.lookupTable)) == lfu.maxSize
|
||||
}
|
||||
|
||||
// SetMaxSize updates the max size of the LFU cache
|
||||
func (lfu *FrameCache) SetMaxSize(size uint) {
|
||||
lfu.rwLock.Lock()
|
||||
defer lfu.rwLock.Unlock()
|
||||
|
||||
lfu.maxSize = size
|
||||
}
|
||||
|
||||
// evict the least recently used element from the cache, this function is unsafe to be called externally
|
||||
// because it doesn't provide locking mechanism.
|
||||
func (lfu *FrameCache) unsafeEvict() error {
|
||||
// WARNING: This function assumes that a write lock has been held by the caller already
|
||||
|
||||
// get the head node of the list
|
||||
headFreq := lfu.frequencies.Front()
|
||||
if headFreq == nil {
|
||||
// list is empty, this is a very unusual condition
|
||||
return fmt.Errorf("internal error: failed to evict, empty frequency list")
|
||||
}
|
||||
|
||||
headFreqInner := (headFreq.Value).(*frequencyNode)
|
||||
|
||||
if headFreqInner.valuesList.Len() == 0 {
|
||||
// again this is a very unusual condition
|
||||
return fmt.Errorf("internal error: failed to evict, empty values list")
|
||||
}
|
||||
|
||||
headValuesList := headFreqInner.valuesList
|
||||
// pop the head of this this values list
|
||||
headValueNode := headValuesList.Front()
|
||||
removeResult := headValuesList.Remove(headValueNode).(*keyRefNode)
|
||||
|
||||
// update the values list
|
||||
headFreqInner.valuesList = headValuesList
|
||||
|
||||
if headFreqInner.valuesList.Len() == 0 && headFreqInner.count > 1 {
|
||||
// this node can be removed from the frequency list
|
||||
freqList := lfu.frequencies
|
||||
freqList.Remove(headFreq)
|
||||
lfu.frequencies = freqList
|
||||
}
|
||||
|
||||
// remove the key from lookup table
|
||||
key := removeResult.keyRef
|
||||
delete(lfu.lookupTable, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Put method inserts a `<KeyType, ValueType>` to the LFU cache and updates internal
|
||||
// data structures to keep track of access frequencies, if the cache is full, it evicts the
|
||||
// least frequently used value from the cache.
|
||||
func (lfu *FrameCache) Put(key uint, value *Frame) error {
|
||||
// get write lock
|
||||
lfu.rwLock.Lock()
|
||||
defer lfu.rwLock.Unlock()
|
||||
|
||||
if _, ok := lfu.lookupTable[key]; ok {
|
||||
// update the cache value
|
||||
lfu.lookupTable[key].valueRef = value
|
||||
return nil
|
||||
}
|
||||
|
||||
if lfu.maxSize == uint(len(lfu.lookupTable)) {
|
||||
lfu.unsafeEvict()
|
||||
}
|
||||
|
||||
valueNode := newKeyRefNode(key, value, nil)
|
||||
|
||||
head := lfu.frequencies.Front()
|
||||
if head == nil {
|
||||
// fresh linked list
|
||||
freqNode := newFrequencyNode(1)
|
||||
head = lfu.frequencies.PushFront(freqNode)
|
||||
freqNode.inner = head
|
||||
|
||||
} else {
|
||||
node := head.Value.(*frequencyNode)
|
||||
if node.count != 1 {
|
||||
freqNode := newFrequencyNode(1)
|
||||
head = lfu.frequencies.PushFront(freqNode)
|
||||
freqNode.inner = head
|
||||
}
|
||||
}
|
||||
|
||||
valueNode.parentFreqNode = head
|
||||
node := head.Value.(*frequencyNode)
|
||||
head = node.valuesList.PushBack(valueNode)
|
||||
valueNode.inner = head
|
||||
|
||||
lfu.lookupTable[key] = valueNode
|
||||
return nil
|
||||
}
|
||||
|
||||
// Evict can be called to manually perform eviction
|
||||
func (lfu *FrameCache) Evict() error {
|
||||
lfu.rwLock.Lock()
|
||||
defer lfu.rwLock.Unlock()
|
||||
|
||||
return lfu.unsafeEvict()
|
||||
}
|
||||
|
||||
func (lfu *FrameCache) unsafeUpdateFrequency(valueNode *keyRefNode) {
|
||||
parentFreqNode := valueNode.parentFreqNode
|
||||
currentNode := parentFreqNode.Value.(*frequencyNode)
|
||||
nextParentFreqNode := parentFreqNode.Next()
|
||||
|
||||
var newParent *list.Element = nil
|
||||
|
||||
if nextParentFreqNode == nil {
|
||||
// this is the last node
|
||||
// create a new node with frequency + 1
|
||||
newFreqNode := newFrequencyNode(currentNode.count + 1)
|
||||
lfu.frequencies.PushBack(newFreqNode)
|
||||
newParent = parentFreqNode.Next()
|
||||
|
||||
} else {
|
||||
nextNode := nextParentFreqNode.Value.(*frequencyNode)
|
||||
if nextNode.count == (currentNode.count + 1) {
|
||||
newParent = nextParentFreqNode
|
||||
} else {
|
||||
// insert a node in between
|
||||
newFreqNode := newFrequencyNode(currentNode.count + 1)
|
||||
|
||||
lfu.frequencies.InsertAfter(newFreqNode, parentFreqNode)
|
||||
newParent = parentFreqNode.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// remove from the existing list
|
||||
currentNode.valuesList.Remove(valueNode.inner)
|
||||
|
||||
newParentNode := newParent.Value.(*frequencyNode)
|
||||
valueNode.parentFreqNode = newParent
|
||||
newValueNode := newParentNode.valuesList.PushBack(valueNode)
|
||||
valueNode.inner = newValueNode
|
||||
|
||||
// check if the current node is empty
|
||||
if currentNode.valuesList.Len() == 0 {
|
||||
// remove the current node
|
||||
lfu.frequencies.Remove(parentFreqNode)
|
||||
}
|
||||
}
|
||||
|
||||
// Get can be called to obtain the value for given key
|
||||
func (lfu *FrameCache) Get(key uint) (*Frame, bool) {
|
||||
lfu.rwLock.Lock()
|
||||
defer lfu.rwLock.Unlock()
|
||||
|
||||
// check if data is in the map
|
||||
valueNode, found := lfu.lookupTable[key]
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
lfu.unsafeUpdateFrequency(valueNode)
|
||||
|
||||
return valueNode.valueRef, true
|
||||
}
|
||||
|
||||
// Delete removes the specified entry from LFU cache
|
||||
func (lfu *FrameCache) Delete(key uint) error {
|
||||
lfu.rwLock.Lock()
|
||||
defer lfu.rwLock.Unlock()
|
||||
|
||||
// check if the key is in the map
|
||||
valueNode, found := lfu.lookupTable[key]
|
||||
if !found {
|
||||
return fmt.Errorf("key %v not found", key)
|
||||
}
|
||||
|
||||
parentFreqNode := valueNode.parentFreqNode
|
||||
|
||||
currentNode := (parentFreqNode.Value).(*frequencyNode)
|
||||
currentNode.valuesList.Remove(valueNode.inner)
|
||||
|
||||
if currentNode.valuesList.Len() == 0 {
|
||||
lfu.frequencies.Remove(parentFreqNode)
|
||||
}
|
||||
|
||||
delete(lfu.lookupTable, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFrameCache ...
|
||||
func NewFrameCache(maxSize uint) *FrameCache {
|
||||
return &FrameCache{
|
||||
rwLock: sync.RWMutex{},
|
||||
lookupTable: make(map[uint]*keyRefNode),
|
||||
maxSize: maxSize,
|
||||
frequencies: list.New(),
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,8 @@ package vm
|
||||
import (
|
||||
"monkey/internal/code"
|
||||
"monkey/internal/object"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var cache = NewFrameCache(32)
|
||||
|
||||
type Frame struct {
|
||||
cl *object.Closure
|
||||
ip int
|
||||
@@ -15,21 +12,11 @@ type Frame struct {
|
||||
}
|
||||
|
||||
func NewFrame(cl *object.Closure, basePointer int) *Frame {
|
||||
key := uint(uintptr(unsafe.Pointer(cl))) + uint(basePointer)
|
||||
if frame, ok := cache.Get(key); ok {
|
||||
frame.Reset()
|
||||
return frame
|
||||
}
|
||||
|
||||
frame := &Frame{
|
||||
return &Frame{
|
||||
cl: cl,
|
||||
ip: -1,
|
||||
basePointer: basePointer,
|
||||
}
|
||||
|
||||
cache.Put(key, frame)
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
// NextOp ...
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@@ -201,22 +202,16 @@ func (vm *VM) LastPoppedStackElem() object.Object {
|
||||
}
|
||||
|
||||
func (vm *VM) Run() error {
|
||||
var n int
|
||||
var ip int
|
||||
var ins code.Instructions
|
||||
var op code.Opcode
|
||||
|
||||
if vm.Debug {
|
||||
log.Printf(
|
||||
"%-25s %-20s\n",
|
||||
fmt.Sprintf(
|
||||
"%04d %s", ip,
|
||||
strings.Split(ins[ip:].String(), "\n")[0][4:],
|
||||
),
|
||||
fmt.Sprintf(
|
||||
"[ip=%02d fp=%02d, sp=%02d]",
|
||||
ip, vm.fp-1, vm.sp,
|
||||
),
|
||||
)
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Printf("%d instructions executed in %s", n, time.Now().Sub(start))
|
||||
}()
|
||||
}
|
||||
|
||||
for vm.frame.ip < len(vm.frame.Instructions())-1 {
|
||||
@@ -487,6 +482,7 @@ func (vm *VM) Run() error {
|
||||
}
|
||||
|
||||
if vm.Debug {
|
||||
n++
|
||||
log.Printf(
|
||||
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
||||
"", ip, vm.fp-1, vm.sp,
|
||||
|
||||
27
monkey.go
27
monkey.go
@@ -4,6 +4,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"monkey/internal/object"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -42,7 +44,7 @@ func decode(path string) (*compiler.Bytecode, error) {
|
||||
return compiler.Decode(b), nil
|
||||
}
|
||||
|
||||
func compile(path string) (bc *compiler.Bytecode, err error) {
|
||||
func compile(path string, debug bool) (bc *compiler.Bytecode, err error) {
|
||||
input := string(mustReadFile(path))
|
||||
res, errs := parser.Parse(path, input)
|
||||
if len(errs) > 0 {
|
||||
@@ -55,7 +57,12 @@ func compile(path string) (bc *compiler.Bytecode, err error) {
|
||||
return nil, errors.New(buf.String())
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("AST:\n%s\n", res)
|
||||
}
|
||||
|
||||
c := compiler.New()
|
||||
c.Debug = debug
|
||||
c.SetFileInfo(path, input)
|
||||
if err = c.Compile(res); err != nil {
|
||||
return
|
||||
@@ -64,13 +71,15 @@ func compile(path string) (bc *compiler.Bytecode, err error) {
|
||||
return c.Bytecode(), nil
|
||||
}
|
||||
|
||||
func ExecFileVM(f string) (err error) {
|
||||
func ExecFileVM(f string, args []string, debug bool) (err error) {
|
||||
var bytecode *compiler.Bytecode
|
||||
|
||||
if filepath.Ext(f) == ".monkeyc" {
|
||||
object.Args = args
|
||||
|
||||
if filepath.Ext(f) == ".mc" {
|
||||
bytecode, err = decode(f)
|
||||
} else {
|
||||
bytecode, err = compile(f)
|
||||
bytecode, err = compile(f, debug)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -79,6 +88,7 @@ func ExecFileVM(f string) (err error) {
|
||||
}
|
||||
|
||||
mvm := vm.New(f, bytecode)
|
||||
mvm.Debug = debug
|
||||
if err = mvm.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
@@ -87,7 +97,7 @@ func ExecFileVM(f string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func CompileFiles(files []string) error {
|
||||
func CompileFiles(files []string, debug bool) error {
|
||||
for _, f := range files {
|
||||
b := mustReadFile(f)
|
||||
|
||||
@@ -99,7 +109,12 @@ func CompileFiles(files []string) error {
|
||||
return ErrParseError
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("AST:\n%s\n", res)
|
||||
}
|
||||
|
||||
c := compiler.New()
|
||||
c.Debug = debug
|
||||
if err := c.Compile(res); err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
@@ -112,7 +127,7 @@ func CompileFiles(files []string) error {
|
||||
}
|
||||
|
||||
ext := filepath.Ext(f)
|
||||
writeFile(f[:len(f)-len(ext)]+".monkeyc", cnt)
|
||||
writeFile(f[:len(f)-len(ext)]+".mc", cnt)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
29
repl.go
29
repl.go
@@ -3,7 +3,9 @@ package monkey
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/tebeka/atexit"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -18,19 +20,25 @@ import (
|
||||
// Package repl implements the Read-Eval-Print-Loop or interactive console
|
||||
// by lexing, parsing and evaluating the input in the interpreter
|
||||
|
||||
func VmREPL() error {
|
||||
func VmREPL(args []string, debug bool) error {
|
||||
var state = vm.NewVMState()
|
||||
|
||||
object.Args = args
|
||||
|
||||
initState, err := term.MakeRaw(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return fmt.Errorf("error opening terminal: %w", err)
|
||||
}
|
||||
defer term.Restore(0, initState)
|
||||
atexit.Register(func() {
|
||||
term.Restore(0, initState)
|
||||
})
|
||||
|
||||
t := term.NewTerminal(os.Stdin, ">>> ")
|
||||
t.AutoCompleteCallback = autoComplete
|
||||
object.Stdout = t
|
||||
object.ExitFunction = atexit.Exit
|
||||
|
||||
PrintVersionInfo(t)
|
||||
for {
|
||||
@@ -51,13 +59,20 @@ func VmREPL() error {
|
||||
continue
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("AST:\n%s\n", res)
|
||||
}
|
||||
|
||||
c := compiler.NewWithState(state.Symbols, &state.Constants)
|
||||
c.Debug = debug
|
||||
c.SetFileInfo("<stdin>", input)
|
||||
if err := c.Compile(res); err != nil {
|
||||
fmt.Fprintln(t, err)
|
||||
continue
|
||||
}
|
||||
|
||||
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
||||
mvm.Debug = debug
|
||||
if err := mvm.Run(); err != nil {
|
||||
fmt.Fprintf(t, "runtime error: %v\n", err)
|
||||
continue
|
||||
@@ -114,12 +129,14 @@ func acceptUntil(t *term.Terminal, start, end string) (string, error) {
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func SimpleVmREPL() {
|
||||
func SimpleVmREPL(args []string, debug bool) {
|
||||
var (
|
||||
state = vm.NewVMState()
|
||||
reader = bufio.NewReader(os.Stdin)
|
||||
)
|
||||
|
||||
object.Args = args
|
||||
|
||||
PrintVersionInfo(os.Stdout)
|
||||
for {
|
||||
fmt.Print(">>> ")
|
||||
@@ -140,14 +157,20 @@ func SimpleVmREPL() {
|
||||
continue
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("AST:\n%s\n", res)
|
||||
}
|
||||
|
||||
c := compiler.NewWithState(state.Symbols, &state.Constants)
|
||||
c.Debug = debug
|
||||
c.SetFileInfo("<stdin>", input)
|
||||
if err := c.Compile(res); err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
||||
|
||||
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
||||
mvm.Debug = debug
|
||||
if err := mvm.Run(); err != nil {
|
||||
fmt.Printf("runtime error: %v\n", err)
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user