bunch of changes
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -84,6 +84,7 @@ fabric.properties
|
|||||||
*.out
|
*.out
|
||||||
*.swo
|
*.swo
|
||||||
*.swn
|
*.swn
|
||||||
|
*.pyc
|
||||||
*.prof
|
*.prof
|
||||||
|
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
@@ -91,3 +92,4 @@ fabric.properties
|
|||||||
/dist
|
/dist
|
||||||
/.vscode
|
/.vscode
|
||||||
/monkey
|
/monkey
|
||||||
|
/examples/fib
|
||||||
@@ -12,27 +12,29 @@ func main() {
|
|||||||
compile bool
|
compile bool
|
||||||
version bool
|
version bool
|
||||||
simple bool
|
simple bool
|
||||||
|
debug bool
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.BoolVar(&compile, "c", false, "Compile a monkey file into a '.monkeyc' bytecode file.")
|
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(&simple, "s", false, "Use simple REPL instead of opening a terminal")
|
||||||
flag.BoolVar(&version, "v", false, "Print Monkey version information.")
|
flag.BoolVar(&version, "v", false, "Print Monkey version information")
|
||||||
|
flag.BoolVar(&debug, "d", false, "Enable debug mode")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case compile:
|
case compile:
|
||||||
monkey.CompileFiles(flag.Args())
|
monkey.CompileFiles(flag.Args(), debug)
|
||||||
|
|
||||||
case version:
|
case version:
|
||||||
monkey.PrintVersionInfo(os.Stdout)
|
monkey.PrintVersionInfo(os.Stdout)
|
||||||
|
|
||||||
case flag.NArg() > 0:
|
case flag.NArg() > 0:
|
||||||
monkey.ExecFileVM(flag.Arg(0))
|
monkey.ExecFileVM(flag.Arg(0), flag.Args()[1:], debug)
|
||||||
|
|
||||||
case simple:
|
case simple:
|
||||||
monkey.SimpleVmREPL()
|
monkey.SimpleVmREPL(flag.Args(), debug)
|
||||||
|
|
||||||
default:
|
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
|
N := 35
|
||||||
|
|
||||||
if (len(args()) == 1) {
|
if (len(args()) > 0) {
|
||||||
N = int(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
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/stretchr/testify v1.8.4
|
|
||||||
github.com/pkg/profile v1.7.0
|
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 (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // 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
|
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/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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
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.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.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 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
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.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 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
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/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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 {
|
func (p *Program) String() string {
|
||||||
var out bytes.Buffer
|
var s strings.Builder
|
||||||
|
|
||||||
for _, s := range p.Statements {
|
for _, stmt := range p.Statements {
|
||||||
out.WriteString(s.String())
|
s.WriteString(fmt.Sprintf("%s\n", stmt.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.String()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReturnStatement struct {
|
type ReturnStatement struct {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Args []string = os.Args
|
Args []string = os.Args[1:] // Skip the monkey binary itself
|
||||||
Stdin io.Reader = os.Stdin
|
Stdin io.Reader = os.Stdin
|
||||||
Stdout io.Writer = os.Stdout
|
Stdout io.Writer = os.Stdout
|
||||||
Stderr io.Writer = os.Stderr
|
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 (
|
import (
|
||||||
"monkey/internal/code"
|
"monkey/internal/code"
|
||||||
"monkey/internal/object"
|
"monkey/internal/object"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var cache = NewFrameCache(32)
|
|
||||||
|
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
cl *object.Closure
|
cl *object.Closure
|
||||||
ip int
|
ip int
|
||||||
@@ -15,21 +12,11 @@ type Frame struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewFrame(cl *object.Closure, basePointer int) *Frame {
|
func NewFrame(cl *object.Closure, basePointer int) *Frame {
|
||||||
key := uint(uintptr(unsafe.Pointer(cl))) + uint(basePointer)
|
return &Frame{
|
||||||
if frame, ok := cache.Get(key); ok {
|
|
||||||
frame.Reset()
|
|
||||||
return frame
|
|
||||||
}
|
|
||||||
|
|
||||||
frame := &Frame{
|
|
||||||
cl: cl,
|
cl: cl,
|
||||||
ip: -1,
|
ip: -1,
|
||||||
basePointer: basePointer,
|
basePointer: basePointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.Put(key, frame)
|
|
||||||
|
|
||||||
return frame
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextOp ...
|
// NextOp ...
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -201,22 +202,16 @@ func (vm *VM) LastPoppedStackElem() object.Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) Run() error {
|
func (vm *VM) Run() error {
|
||||||
|
var n int
|
||||||
var ip int
|
var ip int
|
||||||
var ins code.Instructions
|
var ins code.Instructions
|
||||||
var op code.Opcode
|
var op code.Opcode
|
||||||
|
|
||||||
if vm.Debug {
|
if vm.Debug {
|
||||||
log.Printf(
|
start := time.Now()
|
||||||
"%-25s %-20s\n",
|
defer func() {
|
||||||
fmt.Sprintf(
|
log.Printf("%d instructions executed in %s", n, time.Now().Sub(start))
|
||||||
"%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,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for vm.frame.ip < len(vm.frame.Instructions())-1 {
|
for vm.frame.ip < len(vm.frame.Instructions())-1 {
|
||||||
@@ -487,6 +482,7 @@ func (vm *VM) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if vm.Debug {
|
if vm.Debug {
|
||||||
|
n++
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
||||||
"", ip, vm.fp-1, vm.sp,
|
"", ip, vm.fp-1, vm.sp,
|
||||||
|
|||||||
27
monkey.go
27
monkey.go
@@ -4,6 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
"monkey/internal/object"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -42,7 +44,7 @@ func decode(path string) (*compiler.Bytecode, error) {
|
|||||||
return compiler.Decode(b), nil
|
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))
|
input := string(mustReadFile(path))
|
||||||
res, errs := parser.Parse(path, input)
|
res, errs := parser.Parse(path, input)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
@@ -55,7 +57,12 @@ func compile(path string) (bc *compiler.Bytecode, err error) {
|
|||||||
return nil, errors.New(buf.String())
|
return nil, errors.New(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
log.Printf("AST:\n%s\n", res)
|
||||||
|
}
|
||||||
|
|
||||||
c := compiler.New()
|
c := compiler.New()
|
||||||
|
c.Debug = debug
|
||||||
c.SetFileInfo(path, input)
|
c.SetFileInfo(path, input)
|
||||||
if err = c.Compile(res); err != nil {
|
if err = c.Compile(res); err != nil {
|
||||||
return
|
return
|
||||||
@@ -64,13 +71,15 @@ func compile(path string) (bc *compiler.Bytecode, err error) {
|
|||||||
return c.Bytecode(), nil
|
return c.Bytecode(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecFileVM(f string) (err error) {
|
func ExecFileVM(f string, args []string, debug bool) (err error) {
|
||||||
var bytecode *compiler.Bytecode
|
var bytecode *compiler.Bytecode
|
||||||
|
|
||||||
if filepath.Ext(f) == ".monkeyc" {
|
object.Args = args
|
||||||
|
|
||||||
|
if filepath.Ext(f) == ".mc" {
|
||||||
bytecode, err = decode(f)
|
bytecode, err = decode(f)
|
||||||
} else {
|
} else {
|
||||||
bytecode, err = compile(f)
|
bytecode, err = compile(f, debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -79,6 +88,7 @@ func ExecFileVM(f string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mvm := vm.New(f, bytecode)
|
mvm := vm.New(f, bytecode)
|
||||||
|
mvm.Debug = debug
|
||||||
if err = mvm.Run(); err != nil {
|
if err = mvm.Run(); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
@@ -87,7 +97,7 @@ func ExecFileVM(f string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func CompileFiles(files []string) error {
|
func CompileFiles(files []string, debug bool) error {
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
b := mustReadFile(f)
|
b := mustReadFile(f)
|
||||||
|
|
||||||
@@ -99,7 +109,12 @@ func CompileFiles(files []string) error {
|
|||||||
return ErrParseError
|
return ErrParseError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
log.Printf("AST:\n%s\n", res)
|
||||||
|
}
|
||||||
|
|
||||||
c := compiler.New()
|
c := compiler.New()
|
||||||
|
c.Debug = debug
|
||||||
if err := c.Compile(res); err != nil {
|
if err := c.Compile(res); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
continue
|
continue
|
||||||
@@ -112,7 +127,7 @@ func CompileFiles(files []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext := filepath.Ext(f)
|
ext := filepath.Ext(f)
|
||||||
writeFile(f[:len(f)-len(ext)]+".monkeyc", cnt)
|
writeFile(f[:len(f)-len(ext)]+".mc", cnt)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
29
repl.go
29
repl.go
@@ -3,7 +3,9 @@ package monkey
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tebeka/atexit"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -18,19 +20,25 @@ import (
|
|||||||
// Package repl implements the Read-Eval-Print-Loop or interactive console
|
// Package repl implements the Read-Eval-Print-Loop or interactive console
|
||||||
// by lexing, parsing and evaluating the input in the interpreter
|
// by lexing, parsing and evaluating the input in the interpreter
|
||||||
|
|
||||||
func VmREPL() error {
|
func VmREPL(args []string, debug bool) error {
|
||||||
var state = vm.NewVMState()
|
var state = vm.NewVMState()
|
||||||
|
|
||||||
|
object.Args = args
|
||||||
|
|
||||||
initState, err := term.MakeRaw(0)
|
initState, err := term.MakeRaw(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return fmt.Errorf("error opening terminal: %w", err)
|
return fmt.Errorf("error opening terminal: %w", err)
|
||||||
}
|
}
|
||||||
defer term.Restore(0, initState)
|
defer term.Restore(0, initState)
|
||||||
|
atexit.Register(func() {
|
||||||
|
term.Restore(0, initState)
|
||||||
|
})
|
||||||
|
|
||||||
t := term.NewTerminal(os.Stdin, ">>> ")
|
t := term.NewTerminal(os.Stdin, ">>> ")
|
||||||
t.AutoCompleteCallback = autoComplete
|
t.AutoCompleteCallback = autoComplete
|
||||||
object.Stdout = t
|
object.Stdout = t
|
||||||
|
object.ExitFunction = atexit.Exit
|
||||||
|
|
||||||
PrintVersionInfo(t)
|
PrintVersionInfo(t)
|
||||||
for {
|
for {
|
||||||
@@ -51,13 +59,20 @@ func VmREPL() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
log.Printf("AST:\n%s\n", res)
|
||||||
|
}
|
||||||
|
|
||||||
c := compiler.NewWithState(state.Symbols, &state.Constants)
|
c := compiler.NewWithState(state.Symbols, &state.Constants)
|
||||||
|
c.Debug = debug
|
||||||
|
c.SetFileInfo("<stdin>", input)
|
||||||
if err := c.Compile(res); err != nil {
|
if err := c.Compile(res); err != nil {
|
||||||
fmt.Fprintln(t, err)
|
fmt.Fprintln(t, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
||||||
|
mvm.Debug = debug
|
||||||
if err := mvm.Run(); err != nil {
|
if err := mvm.Run(); err != nil {
|
||||||
fmt.Fprintf(t, "runtime error: %v\n", err)
|
fmt.Fprintf(t, "runtime error: %v\n", err)
|
||||||
continue
|
continue
|
||||||
@@ -114,12 +129,14 @@ func acceptUntil(t *term.Terminal, start, end string) (string, error) {
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SimpleVmREPL() {
|
func SimpleVmREPL(args []string, debug bool) {
|
||||||
var (
|
var (
|
||||||
state = vm.NewVMState()
|
state = vm.NewVMState()
|
||||||
reader = bufio.NewReader(os.Stdin)
|
reader = bufio.NewReader(os.Stdin)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object.Args = args
|
||||||
|
|
||||||
PrintVersionInfo(os.Stdout)
|
PrintVersionInfo(os.Stdout)
|
||||||
for {
|
for {
|
||||||
fmt.Print(">>> ")
|
fmt.Print(">>> ")
|
||||||
@@ -140,14 +157,20 @@ func SimpleVmREPL() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
log.Printf("AST:\n%s\n", res)
|
||||||
|
}
|
||||||
|
|
||||||
c := compiler.NewWithState(state.Symbols, &state.Constants)
|
c := compiler.NewWithState(state.Symbols, &state.Constants)
|
||||||
|
c.Debug = debug
|
||||||
c.SetFileInfo("<stdin>", input)
|
c.SetFileInfo("<stdin>", input)
|
||||||
if err := c.Compile(res); err != nil {
|
if err := c.Compile(res); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
|
||||||
|
|
||||||
|
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
||||||
|
mvm.Debug = debug
|
||||||
if err := mvm.Run(); err != nil {
|
if err := mvm.Run(); err != nil {
|
||||||
fmt.Printf("runtime error: %v\n", err)
|
fmt.Printf("runtime error: %v\n", err)
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user