restructure project
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -81,11 +81,13 @@ fabric.properties
|
||||
*-e
|
||||
*.so
|
||||
*.bak
|
||||
*.txt
|
||||
*.out
|
||||
*.swo
|
||||
*.swn
|
||||
.DS_Store
|
||||
*.prof
|
||||
|
||||
**/.DS_Store
|
||||
|
||||
/dist
|
||||
/.vscode
|
||||
/monkey-lang
|
||||
/monkey
|
||||
@@ -3,18 +3,21 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/pkg/profile"
|
||||
"io"
|
||||
"log"
|
||||
"monkey/compiler"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/repl"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/profile"
|
||||
|
||||
"monkey/internal"
|
||||
"monkey/internal/compiler"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/parser"
|
||||
"monkey/internal/repl"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -66,7 +69,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if version {
|
||||
fmt.Printf("%s %s", path.Base(os.Args[0]), FullVersion())
|
||||
fmt.Printf("%s %s", path.Base(os.Args[0]), internal.FullVersion())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -5,4 +5,10 @@ fib := fn(x) {
|
||||
return fib(x-1) + fib(x-2)
|
||||
}
|
||||
|
||||
print(fib(35))
|
||||
N := 35
|
||||
|
||||
if (len(args()) == 1) {
|
||||
N = int(args()[0])
|
||||
}
|
||||
|
||||
print(fib(N))
|
||||
@@ -3,7 +3,7 @@ package ast
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"monkey/token"
|
||||
"monkey/internal/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ package ast
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"monkey/token"
|
||||
"monkey/internal/token"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Abs ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Args ...
|
||||
@@ -1,12 +1,9 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
)
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Bool ...
|
||||
@@ -2,7 +2,7 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/object"
|
||||
"monkey/internal/object"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Chr ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Divmod ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Exit ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
import (
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// First ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// HashOf ...
|
||||
@@ -2,8 +2,8 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// IdOf ...
|
||||
@@ -1,14 +1,11 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
)
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Int ...
|
||||
func Int(args ...object.Object) object.Object {
|
||||
if err := typing.Check(
|
||||
@@ -1,11 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
)
|
||||
|
||||
import (
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Last ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Len ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strings"
|
||||
)
|
||||
|
||||
import "strings"
|
||||
|
||||
// Lower ...
|
||||
func Lower(args ...object.Object) object.Object {
|
||||
if err := typing.Check(
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package builtins
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Ord ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Pop ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
func pow(x, y int64) int64 {
|
||||
@@ -1,12 +1,11 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"fmt"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Print ...
|
||||
func Print(args ...object.Object) object.Object {
|
||||
if err := typing.Check(
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Push ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package builtins
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// ReadFile ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Rest ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Reversed ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
)
|
||||
|
||||
import (
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// Str ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// TypeOf ...
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package builtins
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"monkey/object"
|
||||
"monkey/typing"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/typing"
|
||||
)
|
||||
|
||||
// WriteFile ...
|
||||
@@ -3,10 +3,10 @@ package compiler
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"monkey/ast"
|
||||
"monkey/builtins"
|
||||
"monkey/code"
|
||||
"monkey/object"
|
||||
"monkey/internal/ast"
|
||||
"monkey/internal/builtins"
|
||||
"monkey/internal/code"
|
||||
"monkey/internal/object"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
@@ -3,11 +3,11 @@ package compiler
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"monkey/ast"
|
||||
"monkey/code"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/internal/ast"
|
||||
"monkey/internal/code"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/parser"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -2,12 +2,12 @@ package evaluator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/ast"
|
||||
"monkey/builtins"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/utils"
|
||||
"monkey/internal/ast"
|
||||
"monkey/internal/builtins"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/parser"
|
||||
"monkey/internal/utils"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -3,10 +3,10 @@ package evaluator
|
||||
import (
|
||||
"errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/utils"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/parser"
|
||||
"monkey/internal/utils"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -862,9 +862,9 @@ func TestImportExpressions(t *testing.T) {
|
||||
input string
|
||||
expected interface{}
|
||||
}{
|
||||
{`mod := import("../testdata/mod"); mod.A`, 5},
|
||||
{`mod := import("../testdata/mod"); mod.Sum(2, 3)`, 5},
|
||||
{`mod := import("../testdata/mod"); mod.a`, nil},
|
||||
{`mod := import("../../testdata/mod"); mod.A`, 5},
|
||||
{`mod := import("../../testdata/mod"); mod.Sum(2, 3)`, 5},
|
||||
{`mod := import("../../testdata/mod"); mod.a`, nil},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -890,7 +890,7 @@ func TestImportSearchPaths(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExamples(t *testing.T) {
|
||||
matches, err := filepath.Glob("./examples/*.monkey")
|
||||
matches, err := filepath.Glob("../../examples/*.monkey")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package lexer
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"monkey/token"
|
||||
"monkey/internal/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"monkey/token"
|
||||
"monkey/internal/token"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/code"
|
||||
"monkey/internal/code"
|
||||
)
|
||||
|
||||
type CompiledFunction struct {
|
||||
@@ -2,7 +2,7 @@ package object
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"monkey/ast"
|
||||
"monkey/internal/ast"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -2,9 +2,9 @@ package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/ast"
|
||||
"monkey/lexer"
|
||||
"monkey/token"
|
||||
"monkey/internal/ast"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/token"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ package parser
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"monkey/ast"
|
||||
"monkey/lexer"
|
||||
"monkey/internal/ast"
|
||||
"monkey/internal/lexer"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package main
|
||||
package internal
|
||||
|
||||
import "monkey/object"
|
||||
import "monkey/internal/object"
|
||||
|
||||
// Hello ...
|
||||
func Hello(args ...object.Object) object.Object {
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"monkey/compiler"
|
||||
"monkey/evaluator"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/vm"
|
||||
"monkey/internal/compiler"
|
||||
"monkey/internal/evaluator"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/parser"
|
||||
"monkey/internal/vm"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ package typing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/object"
|
||||
"monkey/internal/object"
|
||||
)
|
||||
|
||||
type CheckFunc func(name string, args []object.Object) error
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
270
internal/vm/cache.go
Normal file
270
internal/vm/cache.go
Normal file
@@ -0,0 +1,270 @@
|
||||
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(),
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"monkey/code"
|
||||
"monkey/object"
|
||||
"monkey/internal/code"
|
||||
"monkey/internal/object"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var cache = NewFrameCache(32)
|
||||
|
||||
type Frame struct {
|
||||
cl *object.Closure
|
||||
ip int
|
||||
@@ -12,16 +15,32 @@ type Frame struct {
|
||||
}
|
||||
|
||||
func NewFrame(cl *object.Closure, basePointer int) *Frame {
|
||||
return &Frame{
|
||||
key := uint(uintptr(unsafe.Pointer(cl))) + uint(basePointer)
|
||||
if frame, ok := cache.Get(key); ok {
|
||||
frame.Reset()
|
||||
return frame
|
||||
}
|
||||
|
||||
frame := &Frame{
|
||||
cl: cl,
|
||||
ip: -1,
|
||||
basePointer: basePointer}
|
||||
basePointer: basePointer,
|
||||
}
|
||||
|
||||
cache.Put(key, frame)
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
// NextOp ...
|
||||
func (f *Frame) NextOp() code.Opcode {
|
||||
return code.Opcode(f.Instructions()[f.ip+1])
|
||||
}
|
||||
|
||||
func (f *Frame) Reset() {
|
||||
f.ip = -1
|
||||
}
|
||||
|
||||
func (f *Frame) Instructions() code.Instructions {
|
||||
return f.cl.Fn.Instructions
|
||||
}
|
||||
|
||||
func (f *Frame) NextOp() code.Opcode {
|
||||
return code.Opcode(f.Instructions()[f.ip+1])
|
||||
}
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"monkey/builtins"
|
||||
"monkey/code"
|
||||
"monkey/compiler"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/utils"
|
||||
"monkey/internal/builtins"
|
||||
"monkey/internal/code"
|
||||
"monkey/internal/compiler"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/parser"
|
||||
"monkey/internal/utils"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
@@ -2,12 +2,12 @@ package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"monkey/ast"
|
||||
"monkey/compiler"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/utils"
|
||||
"monkey/internal/ast"
|
||||
"monkey/internal/compiler"
|
||||
"monkey/internal/lexer"
|
||||
"monkey/internal/object"
|
||||
"monkey/internal/parser"
|
||||
"monkey/internal/utils"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -1185,15 +1185,15 @@ func BenchmarkFibonacci(b *testing.B) {
|
||||
func TestImportExpressions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `mod := import("../testdata/mod"); mod.A`,
|
||||
input: `mod := import("../../testdata/mod"); mod.A`,
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
input: `mod := import("../testdata/mod"); mod.Sum(2, 3)`,
|
||||
input: `mod := import("../../testdata/mod"); mod.Sum(2, 3)`,
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
input: `mod := import("../testdata/mod"); mod.a`,
|
||||
input: `mod := import("../../testdata/mod"); mod.a`,
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
@@ -1202,11 +1202,11 @@ func TestImportExpressions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestImportSearchPaths(t *testing.T) {
|
||||
utils.AddPath("../testdata")
|
||||
utils.AddPath("../../testdata")
|
||||
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `mod := import("../testdata/mod"); mod.A`,
|
||||
input: `mod := import("../../testdata/mod"); mod.A`,
|
||||
expected: 5,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user