optimizations
This commit is contained in:
@@ -36,7 +36,7 @@ func Find(ctx context.Context, args ...object.Object) object.Object {
|
||||
|
||||
// find in array
|
||||
if haystack, ok := args[0].(*object.Array); ok {
|
||||
needle := args[1].(object.Comparable)
|
||||
needle := args[1]
|
||||
i := sort.Search(len(haystack.Elements), func(i int) bool {
|
||||
return needle.Compare(haystack.Elements[i]) == 0
|
||||
})
|
||||
|
||||
@@ -26,37 +26,37 @@ func isError(obj object.Object) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func Eval(node ast.Node, ctx context.Context, env *object.Environment) object.Object {
|
||||
func Eval(ctx context.Context, node ast.Node, env *object.Environment) object.Object {
|
||||
switch node := node.(type) {
|
||||
|
||||
// Statements
|
||||
case *ast.Program:
|
||||
return evalProgram(node, ctx, env)
|
||||
return evalProgram(ctx, node, env)
|
||||
|
||||
case *ast.ExpressionStatement:
|
||||
return Eval(node.Expression, ctx, env)
|
||||
return Eval(ctx, node.Expression, env)
|
||||
|
||||
case *ast.BlockStatement:
|
||||
return evalBlockStatements(node, ctx, env)
|
||||
return evalBlockStatements(ctx, node, env)
|
||||
|
||||
case *ast.IfExpression:
|
||||
return evalIfExpression(node, ctx, env)
|
||||
return evalIfExpression(ctx, node, env)
|
||||
|
||||
case *ast.WhileExpression:
|
||||
return evalWhileExpression(node, ctx, env)
|
||||
return evalWhileExpression(ctx, node, env)
|
||||
|
||||
case *ast.ImportExpression:
|
||||
return evalImportExpression(node, ctx, env)
|
||||
return evalImportExpression(ctx, node, env)
|
||||
|
||||
case *ast.ReturnStatement:
|
||||
val := Eval(node.ReturnValue, ctx, env)
|
||||
val := Eval(ctx, node.ReturnValue, env)
|
||||
if isError(val) {
|
||||
return val
|
||||
}
|
||||
return object.ReturnValue{Value: val}
|
||||
|
||||
case *ast.BindExpression:
|
||||
value := Eval(node.Value, ctx, env)
|
||||
value := Eval(ctx, node.Value, env)
|
||||
if isError(value) {
|
||||
return value
|
||||
}
|
||||
@@ -73,12 +73,12 @@ func Eval(node ast.Node, ctx context.Context, env *object.Environment) object.Ob
|
||||
return newError("expected identifier on left got=%T", node.Left)
|
||||
|
||||
case *ast.AssignmentExpression:
|
||||
left := Eval(node.Left, ctx, env)
|
||||
left := Eval(ctx, node.Left, env)
|
||||
if isError(left) {
|
||||
return left
|
||||
}
|
||||
|
||||
value := Eval(node.Value, ctx, env)
|
||||
value := Eval(ctx, node.Value, env)
|
||||
if isError(value) {
|
||||
return value
|
||||
}
|
||||
@@ -86,13 +86,13 @@ func Eval(node ast.Node, ctx context.Context, env *object.Environment) object.Ob
|
||||
if ident, ok := node.Left.(*ast.Identifier); ok {
|
||||
env.Set(ident.Value, value)
|
||||
} else if ie, ok := node.Left.(*ast.IndexExpression); ok {
|
||||
obj := Eval(ie.Left, ctx, env)
|
||||
obj := Eval(ctx, ie.Left, env)
|
||||
if isError(obj) {
|
||||
return obj
|
||||
}
|
||||
|
||||
if array, ok := obj.(*object.Array); ok {
|
||||
index := Eval(ie.Index, ctx, env)
|
||||
index := Eval(ctx, ie.Index, env)
|
||||
if isError(index) {
|
||||
return index
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func Eval(node ast.Node, ctx context.Context, env *object.Environment) object.Ob
|
||||
return newError("cannot index array with %#v", index)
|
||||
}
|
||||
} else if hash, ok := obj.(*object.Hash); ok {
|
||||
key := Eval(ie.Index, ctx, env)
|
||||
key := Eval(ctx, ie.Index, env)
|
||||
if isError(key) {
|
||||
return key
|
||||
}
|
||||
@@ -140,29 +140,29 @@ func Eval(node ast.Node, ctx context.Context, env *object.Environment) object.Ob
|
||||
return NULL
|
||||
|
||||
case *ast.PrefixExpression:
|
||||
right := Eval(node.Right, ctx, env)
|
||||
right := Eval(ctx, node.Right, env)
|
||||
if isError(right) {
|
||||
return right
|
||||
}
|
||||
return evalPrefixExpression(node.Operator, right)
|
||||
|
||||
case *ast.InfixExpression:
|
||||
left := Eval(node.Left, ctx, env)
|
||||
left := Eval(ctx, node.Left, env)
|
||||
if isError(left) {
|
||||
return left
|
||||
}
|
||||
right := Eval(node.Right, ctx, env)
|
||||
right := Eval(ctx, node.Right, env)
|
||||
if isError(right) {
|
||||
return right
|
||||
}
|
||||
return evalInfixExpression(node.Operator, left, right)
|
||||
|
||||
case *ast.CallExpression:
|
||||
function := Eval(node.Function, ctx, env)
|
||||
function := Eval(ctx, node.Function, env)
|
||||
if isError(function) {
|
||||
return function
|
||||
}
|
||||
args := evalExpressions(node.Arguments, ctx, env)
|
||||
args := evalExpressions(ctx, node.Arguments, env)
|
||||
if len(args) == 1 && isError(args[0]) {
|
||||
return args[0]
|
||||
}
|
||||
@@ -173,39 +173,39 @@ func Eval(node ast.Node, ctx context.Context, env *object.Environment) object.Ob
|
||||
return object.String{Value: node.Value}
|
||||
|
||||
case *ast.ArrayLiteral:
|
||||
elements := evalExpressions(node.Elements, ctx, env)
|
||||
elements := evalExpressions(ctx, node.Elements, env)
|
||||
if len(elements) == 1 && isError(elements[0]) {
|
||||
return elements[0]
|
||||
}
|
||||
return &object.Array{Elements: elements}
|
||||
|
||||
case *ast.IndexExpression:
|
||||
left := Eval(node.Left, ctx, env)
|
||||
left := Eval(ctx, node.Left, env)
|
||||
if isError(left) {
|
||||
return left
|
||||
}
|
||||
index := Eval(node.Index, ctx, env)
|
||||
index := Eval(ctx, node.Index, env)
|
||||
if isError(index) {
|
||||
return index
|
||||
}
|
||||
return evalIndexExpression(left, index)
|
||||
|
||||
case *ast.HashLiteral:
|
||||
return evalHashLiteral(node, ctx, env)
|
||||
return evalHashLiteral(ctx, node, env)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func evalImportExpression(ie *ast.ImportExpression, ctx context.Context, env *object.Environment) object.Object {
|
||||
name := Eval(ie.Name, ctx, env)
|
||||
func evalImportExpression(ctx context.Context, ie *ast.ImportExpression, env *object.Environment) object.Object {
|
||||
name := Eval(ctx, ie.Name, env)
|
||||
if isError(name) {
|
||||
return name
|
||||
}
|
||||
|
||||
if s, ok := name.(object.String); ok {
|
||||
attrs := EvalModule(ctx, s.Value)
|
||||
attrs := Module(ctx, s.Value)
|
||||
if isError(attrs) {
|
||||
return attrs
|
||||
}
|
||||
@@ -214,17 +214,17 @@ func evalImportExpression(ie *ast.ImportExpression, ctx context.Context, env *ob
|
||||
return newError("ImportError: invalid import path '%s'", name)
|
||||
}
|
||||
|
||||
func evalWhileExpression(we *ast.WhileExpression, ctx context.Context, env *object.Environment) object.Object {
|
||||
func evalWhileExpression(ctx context.Context, we *ast.WhileExpression, env *object.Environment) object.Object {
|
||||
var result object.Object
|
||||
|
||||
for {
|
||||
condition := Eval(we.Condition, ctx, env)
|
||||
condition := Eval(ctx, we.Condition, env)
|
||||
if isError(condition) {
|
||||
return condition
|
||||
}
|
||||
|
||||
if isTruthy(condition) {
|
||||
result = Eval(we.Consequence, ctx, env)
|
||||
result = Eval(ctx, we.Consequence, env)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
@@ -237,11 +237,11 @@ func evalWhileExpression(we *ast.WhileExpression, ctx context.Context, env *obje
|
||||
return NULL
|
||||
}
|
||||
|
||||
func evalProgram(program *ast.Program, ctx context.Context, env *object.Environment) object.Object {
|
||||
func evalProgram(ctx context.Context, program *ast.Program, env *object.Environment) object.Object {
|
||||
var result object.Object
|
||||
|
||||
for _, statement := range program.Statements {
|
||||
result = Eval(statement, ctx, env)
|
||||
result = Eval(ctx, statement, env)
|
||||
|
||||
switch result := result.(type) {
|
||||
case object.ReturnValue:
|
||||
@@ -255,11 +255,11 @@ func evalProgram(program *ast.Program, ctx context.Context, env *object.Environm
|
||||
return result
|
||||
}
|
||||
|
||||
func evalBlockStatements(block *ast.BlockStatement, ctx context.Context, env *object.Environment) object.Object {
|
||||
func evalBlockStatements(ctx context.Context, block *ast.BlockStatement, env *object.Environment) object.Object {
|
||||
var result object.Object
|
||||
|
||||
for _, statement := range block.Statements {
|
||||
result = Eval(statement, ctx, env)
|
||||
result = Eval(ctx, statement, env)
|
||||
|
||||
if result != nil {
|
||||
rt := result.Type()
|
||||
@@ -385,17 +385,17 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
|
||||
return object.String{Value: strings.Repeat(rightVal, int(leftVal))}
|
||||
|
||||
case operator == "==":
|
||||
return nativeBoolToBooleanObject(left.(object.Comparable).Compare(right) == 0)
|
||||
return nativeBoolToBooleanObject(left.Compare(right) == 0)
|
||||
case operator == "!=":
|
||||
return nativeBoolToBooleanObject(left.(object.Comparable).Compare(right) != 0)
|
||||
return nativeBoolToBooleanObject(left.Compare(right) != 0)
|
||||
case operator == "<=":
|
||||
return nativeBoolToBooleanObject(left.(object.Comparable).Compare(right) < 1)
|
||||
return nativeBoolToBooleanObject(left.Compare(right) < 1)
|
||||
case operator == ">=":
|
||||
return nativeBoolToBooleanObject(left.(object.Comparable).Compare(right) > -1)
|
||||
return nativeBoolToBooleanObject(left.Compare(right) > -1)
|
||||
case operator == "<":
|
||||
return nativeBoolToBooleanObject(left.(object.Comparable).Compare(right) == -1)
|
||||
return nativeBoolToBooleanObject(left.Compare(right) == -1)
|
||||
case operator == ">":
|
||||
return nativeBoolToBooleanObject(left.(object.Comparable).Compare(right) == 1)
|
||||
return nativeBoolToBooleanObject(left.Compare(right) == 1)
|
||||
|
||||
case left.Type() == object.BooleanType && right.Type() == object.BooleanType:
|
||||
return evalBooleanInfixExpression(operator, left, right)
|
||||
@@ -477,16 +477,16 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje
|
||||
}
|
||||
}
|
||||
|
||||
func evalIfExpression(ie *ast.IfExpression, ctx context.Context, env *object.Environment) object.Object {
|
||||
condition := Eval(ie.Condition, ctx, env)
|
||||
func evalIfExpression(ctx context.Context, ie *ast.IfExpression, env *object.Environment) object.Object {
|
||||
condition := Eval(ctx, ie.Condition, env)
|
||||
if isError(condition) {
|
||||
return condition
|
||||
}
|
||||
|
||||
if isTruthy(condition) {
|
||||
return Eval(ie.Consequence, ctx, env)
|
||||
return Eval(ctx, ie.Consequence, env)
|
||||
} else if ie.Alternative != nil {
|
||||
return Eval(ie.Alternative, ctx, env)
|
||||
return Eval(ctx, ie.Alternative, env)
|
||||
} else {
|
||||
return NULL
|
||||
}
|
||||
@@ -509,8 +509,8 @@ func newError(format string, a ...interface{}) object.Error {
|
||||
return object.Error{Message: fmt.Sprintf(format, a...)}
|
||||
}
|
||||
|
||||
// EvalModule evaluates the named module and returns a object.Module objec
|
||||
func EvalModule(ctx context.Context, name string) object.Object {
|
||||
// Module evaluates the named module and returns a object.Module object
|
||||
func Module(ctx context.Context, name string) object.Object {
|
||||
filename := utils.FindModule(name)
|
||||
|
||||
b, err := os.ReadFile(filename)
|
||||
@@ -527,7 +527,7 @@ func EvalModule(ctx context.Context, name string) object.Object {
|
||||
}
|
||||
|
||||
env := object.NewEnvironment()
|
||||
Eval(module, ctx, env)
|
||||
Eval(ctx, module, env)
|
||||
|
||||
return env.ExportedHash()
|
||||
}
|
||||
@@ -544,11 +544,11 @@ func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object
|
||||
return newError("identifier not found: " + node.Value)
|
||||
}
|
||||
|
||||
func evalExpressions(exps []ast.Expression, ctx context.Context, env *object.Environment) []object.Object {
|
||||
func evalExpressions(ctx context.Context, exps []ast.Expression, env *object.Environment) []object.Object {
|
||||
var result []object.Object
|
||||
|
||||
for _, e := range exps {
|
||||
evaluated := Eval(e, ctx, env)
|
||||
evaluated := Eval(ctx, e, env)
|
||||
if isError(evaluated) {
|
||||
return []object.Object{evaluated}
|
||||
}
|
||||
@@ -563,7 +563,7 @@ func applyFunction(ctx context.Context, fn object.Object, args []object.Object)
|
||||
|
||||
case object.Function:
|
||||
extendedEnv := extendFunctionEnv(fn, args)
|
||||
evaluated := Eval(fn.Body, ctx, extendedEnv)
|
||||
evaluated := Eval(ctx, fn.Body, extendedEnv)
|
||||
return unwrapReturnValue(evaluated)
|
||||
|
||||
case object.Builtin:
|
||||
@@ -656,11 +656,11 @@ func evalArrayIndexExpression(array, index object.Object) object.Object {
|
||||
return arrayObject.Elements[idx]
|
||||
}
|
||||
|
||||
func evalHashLiteral(node *ast.HashLiteral, ctx context.Context, env *object.Environment) object.Object {
|
||||
func evalHashLiteral(ctx context.Context, node *ast.HashLiteral, env *object.Environment) object.Object {
|
||||
pairs := make(map[object.HashKey]object.HashPair)
|
||||
|
||||
for keyNode, valueNode := range node.Pairs {
|
||||
key := Eval(keyNode, ctx, env)
|
||||
key := Eval(ctx, keyNode, env)
|
||||
if isError(key) {
|
||||
return key
|
||||
}
|
||||
@@ -670,7 +670,7 @@ func evalHashLiteral(node *ast.HashLiteral, ctx context.Context, env *object.Env
|
||||
return newError("unusable as hash key: %s", key.Type())
|
||||
}
|
||||
|
||||
value := Eval(valueNode, ctx, env)
|
||||
value := Eval(ctx, valueNode, env)
|
||||
if isError(value) {
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -800,7 +800,7 @@ func testEval(input string) object.Object {
|
||||
program := p.ParseProgram()
|
||||
env := object.NewEnvironment()
|
||||
|
||||
return Eval(program, context.New(), env)
|
||||
return Eval(context.New(), program, env)
|
||||
}
|
||||
|
||||
func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
|
||||
|
||||
@@ -63,11 +63,8 @@ func (ao *Array) String() string {
|
||||
return ao.Inspect()
|
||||
}
|
||||
|
||||
func (ao *Array) Less(i, j int) bool {
|
||||
if cmp, ok := ao.Elements[i].(Comparable); ok {
|
||||
return cmp.Compare(ao.Elements[j]) == -1
|
||||
}
|
||||
return false
|
||||
func (a *Array) Less(i, j int) bool {
|
||||
return a.Elements[i].Compare(a.Elements[j]) == -1
|
||||
}
|
||||
|
||||
func (a *Array) Add(other Object) (Object, error) {
|
||||
@@ -129,18 +126,15 @@ func (ao *Array) Set(index, other Object) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ao *Array) Compare(other Object) int {
|
||||
func (a *Array) Compare(other Object) int {
|
||||
if obj, ok := other.(*Array); ok {
|
||||
if len(ao.Elements) != len(obj.Elements) {
|
||||
if len(a.Elements) != len(obj.Elements) {
|
||||
return -1
|
||||
}
|
||||
for i, el := range ao.Elements {
|
||||
cmp, ok := el.(Comparable)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
if cmp.Compare(obj.Elements[i]) != 0 {
|
||||
return cmp.Compare(obj.Elements[i])
|
||||
for i, el := range a.Elements {
|
||||
val := el.Compare(obj.Elements[i])
|
||||
if val != 0 {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,19 @@ func (b Builtin) Inspect() string {
|
||||
return fmt.Sprintf("<built-in function %s>", b.Name)
|
||||
}
|
||||
|
||||
func (b Builtin) Compare(other Object) int {
|
||||
if obj, ok := other.(Builtin); ok {
|
||||
if b.Name > obj.Name {
|
||||
return 1
|
||||
}
|
||||
if b.Name == obj.Name {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (b Builtin) String() string {
|
||||
return b.Inspect()
|
||||
}
|
||||
|
||||
@@ -23,11 +23,16 @@ func (cf *CompiledFunction) Inspect() string {
|
||||
return fmt.Sprintf("CompiledFunction[%p]", cf)
|
||||
}
|
||||
|
||||
func (cf CompiledFunction) Compare(other Object) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (cf *CompiledFunction) String() string {
|
||||
return cf.Inspect()
|
||||
}
|
||||
|
||||
type Closure struct {
|
||||
BaseObject
|
||||
Fn *CompiledFunction
|
||||
Free []Object
|
||||
}
|
||||
|
||||
@@ -23,6 +23,23 @@ func (e Error) Copy() Object {
|
||||
return Error{Message: e.Message}
|
||||
}
|
||||
|
||||
func (e Error) Compare(other Object) int {
|
||||
if obj, ok := other.(Error); ok {
|
||||
if e.Message > obj.Message {
|
||||
return 1
|
||||
}
|
||||
if e.Message == obj.Message {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e Error) String() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
)
|
||||
|
||||
type Function struct {
|
||||
BaseObject
|
||||
|
||||
Parameters []*ast.Identifier
|
||||
Body *ast.BlockStatement
|
||||
Env *Environment
|
||||
@@ -43,6 +45,7 @@ func (f Function) String() string {
|
||||
}
|
||||
|
||||
type ReturnValue struct {
|
||||
BaseObject
|
||||
Value Object
|
||||
}
|
||||
|
||||
|
||||
@@ -97,18 +97,14 @@ func (h *Hash) Compare(other Object) int {
|
||||
return -1
|
||||
}
|
||||
for _, pair := range h.Pairs {
|
||||
left := pair.Value
|
||||
hashed := left.(Hasher)
|
||||
hashed := pair.Value.(Hasher)
|
||||
right, ok := obj.Pairs[hashed.Hash()]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
cmp, ok := left.(Comparable)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
if cmp.Compare(right.Value) != 0 {
|
||||
return cmp.Compare(right.Value)
|
||||
val := pair.Value.Compare(right.Value)
|
||||
if val != 0 {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,11 +57,6 @@ func (t Type) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// Comparable is the interface for objects to implement suitable comparisons
|
||||
type Comparable interface {
|
||||
Compare(other Object) int
|
||||
}
|
||||
|
||||
// Copyable is the interface for creating copies of objects
|
||||
type Copyable interface {
|
||||
Copy() Object
|
||||
@@ -73,9 +68,14 @@ type Object interface {
|
||||
fmt.Stringer
|
||||
Type() Type
|
||||
Bool() bool
|
||||
Compare(Object) int
|
||||
Inspect() string
|
||||
}
|
||||
|
||||
type BaseObject struct{}
|
||||
|
||||
func (o BaseObject) Compare(other Object) int { return -1 }
|
||||
|
||||
// Hasher is the interface for objects to provide suitable hash keys
|
||||
type Hasher interface {
|
||||
Hash() HashKey
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"monkey"
|
||||
"net/http"
|
||||
|
||||
@@ -12,6 +14,9 @@ func (s *Server) runHandler() router.Handle {
|
||||
opts := &monkey.Options{
|
||||
Stdout: w,
|
||||
Stderr: w,
|
||||
Exit: func(status int) {
|
||||
io.WriteString(w, fmt.Sprintf("<p><i>Program exited with %d.</i></p>", status))
|
||||
},
|
||||
}
|
||||
|
||||
err := monkey.ExecString(r.FormValue("code"), opts)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<body>
|
||||
<div class="layout">
|
||||
<div class="top">
|
||||
<b><a href="https://git.unflavoredmeson.com/unflavoredmeson/monkey-lango"
|
||||
<b><a href="https://gitea.unflavoredmeson.com/unflavoredmeson/monkey-lango"
|
||||
style="color: #FFF; text-decoration: none;">Monkey</a></b>
|
||||
<span>⌘/Ctrl + ENTER to Run. ⌘/Ctrl + SPACE to Format.</span>
|
||||
</div>
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
)
|
||||
|
||||
type frame struct {
|
||||
cl *object.Closure
|
||||
ip int
|
||||
ip uint16
|
||||
basePointer int
|
||||
cl *object.Closure
|
||||
}
|
||||
|
||||
func newFrame(cl *object.Closure, basePointer int) frame {
|
||||
@@ -30,7 +30,7 @@ func (f *frame) SetFree(idx uint8, obj object.Object) {
|
||||
f.cl.Free[idx] = obj
|
||||
}
|
||||
|
||||
func (f *frame) SetIP(ip int) {
|
||||
func (f *frame) SetIP(ip uint16) {
|
||||
f.ip = ip
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"monkey/internal/builtins"
|
||||
"monkey/internal/code"
|
||||
"monkey/internal/compiler"
|
||||
@@ -13,8 +12,6 @@ import (
|
||||
"monkey/internal/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@@ -151,20 +148,25 @@ func (vm *VM) popFrame() frame {
|
||||
return vm.frames[vm.fp]
|
||||
}
|
||||
|
||||
// Option defines a function option for the virtual machine.
|
||||
type Option func(*VM)
|
||||
|
||||
// WithContext defines an option to set the context for the virtual machine.
|
||||
func WithContext(ctx context.Context) Option {
|
||||
return func(vm *VM) { vm.ctx = ctx }
|
||||
}
|
||||
|
||||
// WithDebug enables debug mode in the VM.
|
||||
func WithDebug(debug bool) Option {
|
||||
return func(vm *VM) { vm.debug = debug }
|
||||
}
|
||||
|
||||
// WithState sets the state of the VM.
|
||||
func WithState(state *State) Option {
|
||||
return func(vm *VM) { vm.state = state }
|
||||
}
|
||||
|
||||
// WithTrace sets the trace flag for the VM.
|
||||
func WithTrace(trace bool) Option {
|
||||
return func(vm *VM) { vm.trace = trace }
|
||||
}
|
||||
@@ -224,6 +226,29 @@ func (vm *VM) pop() object.Object {
|
||||
return o
|
||||
}
|
||||
|
||||
func (vm *VM) pop2() (object.Object, object.Object) {
|
||||
if vm.sp == 1 {
|
||||
panic("stack underflow")
|
||||
}
|
||||
|
||||
o1 := vm.stack[vm.sp-1]
|
||||
o2 := vm.stack[vm.sp-2]
|
||||
vm.sp -= 2
|
||||
return o1, o2
|
||||
}
|
||||
|
||||
func (vm *VM) pop3() (object.Object, object.Object, object.Object) {
|
||||
if vm.sp == 2 {
|
||||
panic("stack underflow")
|
||||
}
|
||||
|
||||
o1 := vm.stack[vm.sp-1]
|
||||
o2 := vm.stack[vm.sp-2]
|
||||
o3 := vm.stack[vm.sp-3]
|
||||
vm.sp -= 3
|
||||
return o1, o2, o3
|
||||
}
|
||||
|
||||
func (vm *VM) executeSetGlobal() error {
|
||||
globalIndex := vm.currentFrame().ReadUint16()
|
||||
|
||||
@@ -301,8 +326,7 @@ func (vm *VM) executeMakeArray() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeAdd() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -335,8 +359,7 @@ func (vm *VM) executeAdd() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeSub() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -351,8 +374,7 @@ func (vm *VM) executeSub() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeMul() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case *object.Array:
|
||||
@@ -379,8 +401,7 @@ func (vm *VM) executeMul() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeDiv() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -396,8 +417,7 @@ func (vm *VM) executeDiv() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeMod() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -412,8 +432,7 @@ func (vm *VM) executeMod() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeOr() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Boolean:
|
||||
@@ -428,8 +447,7 @@ func (vm *VM) executeOr() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeAnd() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Boolean:
|
||||
@@ -444,8 +462,7 @@ func (vm *VM) executeAnd() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeBitwiseOr() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -460,8 +477,7 @@ func (vm *VM) executeBitwiseOr() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeBitwiseXor() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -476,8 +492,7 @@ func (vm *VM) executeBitwiseXor() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeBitwiseAnd() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -503,8 +518,7 @@ func (vm *VM) executeBitwiseNot() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeLeftShift() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -519,8 +533,7 @@ func (vm *VM) executeLeftShift() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeRightShift() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -535,63 +548,39 @@ func (vm *VM) executeRightShift() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeEqual() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val == 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) == 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, "==")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeNotEqual() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val != 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) != 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, "!=")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeGreaterThan() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val == 1 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) == 1 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, ">")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeGreaterThanOrEqual() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val >= 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) == 1 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, ">")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeNot() error {
|
||||
@@ -621,9 +610,7 @@ func (vm *VM) executeMinus() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeSetItem() error {
|
||||
right := vm.pop()
|
||||
index := vm.pop()
|
||||
left := vm.pop()
|
||||
right, index, left := vm.pop3()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case *object.Array:
|
||||
@@ -647,8 +634,7 @@ func (vm *VM) executeSetItem() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeGetItem() error {
|
||||
index := vm.pop()
|
||||
left := vm.pop()
|
||||
index, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.String:
|
||||
@@ -749,7 +735,7 @@ func (vm *VM) pushClosure(constIndex, numFree int) error {
|
||||
for i := 0; i < numFree; i++ {
|
||||
free[i] = vm.stack[vm.sp-numFree+i]
|
||||
}
|
||||
vm.sp = vm.sp - numFree
|
||||
vm.sp -= numFree
|
||||
|
||||
closure := object.Closure{Fn: function, Free: free}
|
||||
return vm.push(&closure)
|
||||
@@ -778,49 +764,9 @@ func (vm *VM) LastPoppedStackElem() object.Object {
|
||||
}
|
||||
|
||||
func (vm *VM) Run() (err error) {
|
||||
var (
|
||||
op code.Opcode
|
||||
opcodeFreqs = make(map[code.Opcode]int)
|
||||
)
|
||||
|
||||
if vm.debug {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
end := time.Now().Sub(start)
|
||||
total := 0
|
||||
opcodes := make([]code.Opcode, 0, len(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 %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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for err == nil {
|
||||
op = vm.currentFrame().ReadNextOp()
|
||||
|
||||
if vm.debug {
|
||||
opcodeFreqs[op]++
|
||||
log.Printf(
|
||||
"%-25s %-20s\n",
|
||||
fmt.Sprintf("%04d %s", vm.currentFrame().ip, op),
|
||||
fmt.Sprintf(
|
||||
"[ip=%02d fp=%02d, sp=%02d]",
|
||||
vm.currentFrame().ip, vm.fp-1, vm.sp,
|
||||
),
|
||||
)
|
||||
}
|
||||
op := vm.currentFrame().ReadNextOp()
|
||||
|
||||
switch op {
|
||||
case code.OpConstant:
|
||||
@@ -837,11 +783,11 @@ func (vm *VM) Run() (err error) {
|
||||
err = vm.push(object.FALSE)
|
||||
|
||||
case code.OpJump:
|
||||
pos := int(vm.currentFrame().ReadUint16())
|
||||
pos := vm.currentFrame().ReadUint16()
|
||||
vm.currentFrame().SetIP(pos)
|
||||
|
||||
case code.OpJumpNotTruthy:
|
||||
pos := int(vm.currentFrame().ReadUint16())
|
||||
pos := vm.currentFrame().ReadUint16()
|
||||
if !isTruthy(vm.pop()) {
|
||||
vm.currentFrame().SetIP(pos)
|
||||
}
|
||||
@@ -979,14 +925,7 @@ func (vm *VM) Run() (err error) {
|
||||
default:
|
||||
err = fmt.Errorf("unhandled opcode: %s", op)
|
||||
}
|
||||
|
||||
if vm.trace {
|
||||
log.Printf(
|
||||
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
||||
"", vm.currentFrame().ip, vm.fp-1, vm.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
@@ -713,12 +713,10 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||
{`len("hello world")`, 11},
|
||||
{
|
||||
`len(1)`,
|
||||
&object.Error{
|
||||
Message: "TypeError: object of type 'int' has no len()",
|
||||
},
|
||||
fmt.Errorf("TypeError: object of type 'int' has no len()"),
|
||||
},
|
||||
{`len("one", "two")`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: len() takes exactly 1 argument (2 given)",
|
||||
},
|
||||
},
|
||||
@@ -728,14 +726,14 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||
{`first([1, 2, 3])`, 1},
|
||||
{`first([])`, object.NULL},
|
||||
{`first(1)`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: first() expected argument #1 to be `array` got `int`",
|
||||
},
|
||||
},
|
||||
{`last([1, 2, 3])`, 3},
|
||||
{`last([])`, object.NULL},
|
||||
{`last(1)`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: last() expected argument #1 to be `array` got `int`",
|
||||
},
|
||||
},
|
||||
@@ -743,12 +741,12 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||
{`rest([])`, []int{}},
|
||||
{`push([], 1)`, []int{1}},
|
||||
{`push(1, 1)`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: push() expected argument #1 to be `array` got `int`",
|
||||
},
|
||||
},
|
||||
{`input()`, ""},
|
||||
{`pop([])`, &object.Error{
|
||||
{`pop([])`, object.Error{
|
||||
Message: "IndexError: pop from an empty array",
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user