further improvements
Some checks failed
Build / build (push) Successful in 10m40s
Publish Image / publish (push) Failing after 34s
Test / build (push) Has been cancelled

This commit is contained in:
2024-04-01 17:02:44 -04:00
parent aebbe43999
commit f9e6e164b0
26 changed files with 168 additions and 138 deletions

View File

@@ -50,5 +50,5 @@ func Bind(args ...object.Object) object.Object {
return newError("SocketError: %s", err)
}
return object.Null{}
return object.NULL
}

View File

@@ -7,7 +7,7 @@ import (
)
// Builtins ...
var Builtins = map[string]*object.Builtin{
var Builtins = map[string]object.Builtin{
"len": {Name: "len", Fn: Len},
"input": {Name: "input", Fn: Input},
"print": {Name: "print", Fn: Print},
@@ -58,7 +58,7 @@ var Builtins = map[string]*object.Builtin{
}
// BuiltinsIndex ...
var BuiltinsIndex []*object.Builtin
var BuiltinsIndex []object.Builtin
func init() {
var keys []string

View File

@@ -23,5 +23,5 @@ func Close(args ...object.Object) object.Object {
return newError("IOError: %s", err)
}
return object.Null{}
return object.NULL
}

View File

@@ -33,5 +33,5 @@ func FFI(args ...object.Object) object.Object {
return newError("error finding symbol: %s", err)
}
return &object.Builtin{Name: symbol, Fn: v.(object.BuiltinFunction)}
return object.Builtin{Name: symbol, Fn: v.(object.BuiltinFunction)}
}

View File

@@ -30,11 +30,11 @@ func IdOf(args ...object.Object) object.Object {
} else if h, ok := arg.(*object.Hash); ok {
return object.String{Value: fmt.Sprintf("%p", h)}
} else if f, ok := arg.(*object.Function); ok {
return object.String{Value: fmt.Sprintf("%p", f)}
return object.String{Value: fmt.Sprintf("%p", &f)}
} else if c, ok := arg.(*object.Closure); ok {
return object.String{Value: fmt.Sprintf("%p", c)}
} else if b, ok := arg.(*object.Builtin); ok {
return object.String{Value: fmt.Sprintf("%p", b)}
return object.String{Value: fmt.Sprintf("%p", &c)}
} else if b, ok := arg.(object.Builtin); ok {
return object.String{Value: fmt.Sprintf("%p", &b)}
}
return nil
}

View File

@@ -23,5 +23,5 @@ func Listen(args ...object.Object) object.Object {
return newError("SocketError: %s", err)
}
return object.Null{}
return object.NULL
}

View File

@@ -24,5 +24,5 @@ func WriteFile(args ...object.Object) object.Object {
return newError("IOError: error writing file %s: %s", filename, err)
}
return object.Null{}
return object.NULL
}

View File

@@ -126,7 +126,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
case *ast.FunctionLiteral:
params := node.Parameters
body := node.Body
return &object.Function{Parameters: params, Env: env, Body: body}
return object.Function{Parameters: params, Env: env, Body: body}
// Expressions
case *ast.IntegerLiteral:
@@ -560,12 +560,12 @@ func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Ob
func applyFunction(fn object.Object, args []object.Object) object.Object {
switch fn := fn.(type) {
case *object.Function:
case object.Function:
extendedEnv := extendFunctionEnv(fn, args)
evaluated := Eval(fn.Body, extendedEnv)
return unwrapReturnValue(evaluated)
case *object.Builtin:
case object.Builtin:
if result := fn.Fn(args...); result != nil {
return result
}
@@ -577,7 +577,7 @@ func applyFunction(fn object.Object, args []object.Object) object.Object {
}
}
func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
func extendFunctionEnv(fn object.Function, args []object.Object) *object.Environment {
env := object.NewEnclosedEnvironment(fn.Env)
for paramIdx, param := range fn.Parameters {

View File

@@ -354,7 +354,7 @@ func TestFunctionObject(t *testing.T) {
input := "fn(x) { x + 2; };"
evaluated := testEval(input)
fn, ok := evaluated.(*object.Function)
fn, ok := evaluated.(object.Function)
if !ok {
t.Fatalf("object is not Function. got=%T (%+v)", evaluated, evaluated)
}

View File

@@ -4,10 +4,22 @@ import (
"fmt"
)
var (
TRUE = Boolean{Value: true}
FALSE = Boolean{Value: false}
)
type Boolean struct {
Value bool
}
func NewBoolean(value bool) Boolean {
if value {
return TRUE
}
return FALSE
}
func (b Boolean) Bool() bool {
return b.Value
}

View File

@@ -7,18 +7,18 @@ type Builtin struct {
Fn BuiltinFunction
}
func (b *Builtin) Bool() bool {
func (b Builtin) Bool() bool {
return true
}
func (b *Builtin) Type() Type {
func (b Builtin) Type() Type {
return BuiltinType
}
func (b *Builtin) Inspect() string {
func (b Builtin) Inspect() string {
return fmt.Sprintf("<built-in function %s>", b.Name)
}
func (b *Builtin) String() string {
func (b Builtin) String() string {
return b.Inspect()
}

View File

@@ -27,17 +27,21 @@ func (i Integer) String() string {
}
func (i Integer) Add(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
switch obj := other.(type) {
case Integer:
return Integer{i.Value + obj.Value}, nil
default:
return nil, NewBinaryOpError(i, other, "+")
}
return Integer{i.Value + other.(Integer).Value}, nil
}
func (i Integer) Sub(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
switch obj := other.(type) {
case Integer:
return Integer{i.Value - obj.Value}, nil
default:
return nil, NewBinaryOpError(i, other, "-")
}
return Integer{i.Value - other.(Integer).Value}, nil
}
func (i Integer) Mul(other Object) (Object, error) {
@@ -115,15 +119,16 @@ func (i Integer) Negate() Object {
}
func (i Integer) Compare(other Object) int {
if obj, ok := other.(Integer); ok {
switch {
case i.Value < obj.Value:
switch obj := other.(type) {
case Integer:
if i.Value < obj.Value {
return -1
case i.Value > obj.Value:
return 1
default:
return 0
}
if i.Value > obj.Value {
return 1
}
return 0
default:
return -1
}
return -1
}

View File

@@ -1,5 +1,7 @@
package object
var NULL = Null{}
type Null struct{}
func (n Null) Bool() bool {

View File

@@ -12,6 +12,7 @@ import (
"monkey/internal/utils"
"os"
"path/filepath"
"sort"
"time"
"unicode"
)
@@ -20,10 +21,6 @@ const StackSize = 2048
const GlobalsSize = 65536
const MaxFrames = 1024
var Null = object.Null{}
var True = object.Boolean{Value: true}
var False = object.Boolean{Value: false}
func isTruthy(obj object.Object) bool {
switch obj := obj.(type) {
@@ -114,6 +111,7 @@ func (s *VMState) ExportedHash() *object.Hash {
type VM struct {
Debug bool
Trace bool
state *VMState
@@ -143,9 +141,9 @@ func (vm *VM) popFrame() Frame {
// New constructs a new monkey-lang bytecode virtual machine
func New(fn string, bytecode *compiler.Bytecode) *VM {
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
mainClosure := &object.Closure{Fn: mainFn}
mainFrame := NewFrame(mainClosure, 0)
mainFn := object.CompiledFunction{Instructions: bytecode.Instructions}
mainClosure := object.Closure{Fn: &mainFn}
mainFrame := NewFrame(&mainClosure, 0)
frames := make([]Frame, MaxFrames)
frames[0] = mainFrame
@@ -169,9 +167,9 @@ func New(fn string, bytecode *compiler.Bytecode) *VM {
}
func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM {
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
mainClosure := &object.Closure{Fn: mainFn}
mainFrame := NewFrame(mainClosure, 0)
mainFn := object.CompiledFunction{Instructions: bytecode.Instructions}
mainClosure := object.Closure{Fn: &mainFn}
mainFrame := NewFrame(&mainClosure, 0)
frames := make([]Frame, MaxFrames)
frames[0] = mainFrame
@@ -222,13 +220,13 @@ func (vm *VM) executeConstant() error {
func (vm *VM) executeAssignGlobal() error {
globalIndex := vm.currentFrame().ReadUint16()
vm.state.Globals[globalIndex] = vm.pop()
return vm.push(Null)
return vm.push(object.NULL)
}
func (vm *VM) executeAssignLocal() error {
localIndex := vm.currentFrame().ReadUint8()
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = vm.pop()
return vm.push(Null)
return vm.push(object.NULL)
}
func (vm *VM) executeSetGlobal() error {
@@ -241,7 +239,7 @@ func (vm *VM) executeSetGlobal() error {
vm.state.Globals[globalIndex] = ref
}
return vm.push(Null)
return vm.push(object.NULL)
}
func (vm *VM) executeGetGlobal() error {
@@ -259,7 +257,7 @@ func (vm *VM) executeSetLocal() error {
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = ref
}
return vm.push(Null)
return vm.push(object.NULL)
}
func (vm *VM) executeGetLocal() error {
@@ -283,15 +281,15 @@ func (vm *VM) executeCurrentClosure() error {
}
func (vm *VM) executeTrue() error {
return vm.push(True)
return vm.push(object.TRUE)
}
func (vm *VM) executeFalse() error {
return vm.push(False)
return vm.push(object.FALSE)
}
func (vm *VM) executeNull() error {
return vm.push(Null)
return vm.push(object.NULL)
}
func (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
@@ -547,9 +545,9 @@ func (vm *VM) executeEqual() error {
if obj, ok := left.(object.Comparable); ok {
val := obj.Compare(right)
if val == 0 {
return vm.push(True)
return vm.push(object.TRUE)
}
return vm.push(False)
return vm.push(object.FALSE)
}
return object.NewBinaryOpError(left, right, "==")
@@ -562,9 +560,9 @@ func (vm *VM) executeNotEqual() error {
if obj, ok := left.(object.Comparable); ok {
val := obj.Compare(right)
if val != 0 {
return vm.push(True)
return vm.push(object.TRUE)
}
return vm.push(False)
return vm.push(object.FALSE)
}
return object.NewBinaryOpError(left, right, "!=")
@@ -577,9 +575,9 @@ func (vm *VM) executeGreaterThan() error {
if obj, ok := left.(object.Comparable); ok {
val := obj.Compare(right)
if val == 1 {
return vm.push(True)
return vm.push(object.TRUE)
}
return vm.push(False)
return vm.push(object.FALSE)
}
return object.NewBinaryOpError(left, right, ">")
@@ -592,9 +590,9 @@ func (vm *VM) executeGreaterThanOrEqual() error {
if obj, ok := left.(object.Comparable); ok {
val := obj.Compare(right)
if val >= 0 {
return vm.push(True)
return vm.push(object.TRUE)
}
return vm.push(False)
return vm.push(object.FALSE)
}
return object.NewBinaryOpError(left, right, ">")
@@ -630,7 +628,7 @@ func (vm *VM) executeSetItem() error {
if err != nil {
return err
}
return vm.push(Null)
return vm.push(object.NULL)
}
return fmt.Errorf(
@@ -664,7 +662,7 @@ func (vm *VM) executeCall() error {
switch callee := callee.(type) {
case *object.Closure:
return vm.callClosure(callee, args)
case *object.Builtin:
case object.Builtin:
return vm.callBuiltin(callee, args)
default:
return fmt.Errorf(
@@ -727,7 +725,7 @@ func (vm *VM) callClosure(cl *object.Closure, numArgs int) error {
return nil
}
func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
func (vm *VM) callBuiltin(builtin object.Builtin, numArgs int) error {
args := vm.stack[vm.sp-numArgs : vm.sp]
result := builtin.Fn(args...)
@@ -739,7 +737,7 @@ func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
return err
}
} else {
err := vm.push(Null)
err := vm.push(object.NULL)
if err != nil {
return err
}
@@ -761,8 +759,8 @@ func (vm *VM) pushClosure(constIndex, numFree int) error {
}
vm.sp = vm.sp - numFree
closure := &object.Closure{Fn: function, Free: free}
return vm.push(closure)
closure := object.Closure{Fn: function, Free: free}
return vm.push(&closure)
}
func (vm *VM) loadModule(name object.Object) error {
@@ -788,20 +786,38 @@ func (vm *VM) LastPoppedStackElem() object.Object {
}
func (vm *VM) Run() (err error) {
var n int
var (
op code.Opcode
opcodeFreqs = make(map[code.Opcode]int)
)
if vm.Debug {
start := time.Now()
defer func() {
log.Printf("%d instructions executeuted in %s", n, time.Now().Sub(start))
total := 0
opcodes := make([]code.Opcode, 0, len(opcodeFreqs))
for opcode := range opcodeFreqs {
opcodes = append(opcodes, opcode)
}
sort.SliceStable(opcodes, func(i, j int) bool {
return opcodeFreqs[opcodes[i]] > opcodeFreqs[opcodes[j]]
})
log.Printf("%d instructions executed in %s", total, time.Now().Sub(start))
log.Print("Top 10 instructions:")
for _, opcode := range opcodes[:10] {
log.Printf("%10d %s", opcodeFreqs[opcode], opcode)
}
}()
}
for err == nil {
op := vm.currentFrame().ReadNextOp()
op = vm.currentFrame().ReadNextOp()
if vm.Debug {
n++
opcodeFreqs[op]++
log.Printf(
"%-25s %-20s\n",
fmt.Sprintf("%04d %s", vm.currentFrame().ip, op),
@@ -949,7 +965,7 @@ func (vm *VM) Run() (err error) {
err = fmt.Errorf("unhandled opcode: %s", op)
}
if vm.Debug {
if vm.Trace {
log.Printf(
"%-25s [ip=%02d fp=%02d, sp=%02d]",
"", vm.currentFrame().ip, vm.fp-1, vm.sp,

View File

@@ -130,7 +130,7 @@ func testExpectedObject(t *testing.T, expected interface{}, actual object.Object
}
case object.Null:
if actual != Null {
if actual != object.NULL {
t.Errorf("object is not Null: %T (%+v)", actual, actual)
}
@@ -302,14 +302,14 @@ func TestConditionals(t *testing.T) {
{"if (1 < 2) { 10 }", 10},
{"if (1 < 2) { 10 } else { 20 }", 10},
{"if (1 > 2) { 10 } else { 20 }", 20},
{"if (1 > 2) { 10 }", Null},
{"if (false) { 10 }", Null},
{"if (1 > 2) { 10 }", object.NULL},
{"if (false) { 10 }", object.NULL},
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
{"if (true) { a := 5; }", Null},
{"if (true) { 10; a := 5; }", Null},
{"if (false) { 10 } else { b := 5; }", Null},
{"if (false) { 10 } else { 10; b := 5; }", Null},
{"if (true) { a := 5; } else { 10 }", Null},
{"if (true) { a := 5; }", object.NULL},
{"if (true) { 10; a := 5; }", object.NULL},
{"if (false) { 10 } else { b := 5; }", object.NULL},
{"if (false) { 10 } else { 10; b := 5; }", object.NULL},
{"if (true) { a := 5; } else { 10 }", object.NULL},
{"x := 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
{"if (1 < 2) { 10 } else if (1 == 2) { 20 }", 10},
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
@@ -430,13 +430,13 @@ func TestIndexExpressions(t *testing.T) {
{"[1, 2, 3][1]", 2},
{"[1, 2, 3][0 + 2]", 3},
{"[[1, 1, 1]][0][0]", 1},
{"[][0]", Null},
{"[1, 2, 3][99]", Null},
{"[1][-1]", Null},
{"[][0]", object.NULL},
{"[1, 2, 3][99]", object.NULL},
{"[1][-1]", object.NULL},
{"{1: 1, 2: 2}[1]", 1},
{"{1: 1, 2: 2}[2]", 2},
{"{1: 1}[0]", Null},
{"{}[0]", Null},
{"{1: 1}[0]", object.NULL},
{"{}[0]", object.NULL},
{`"abc"[0]`, "a"},
{`"abc"[1]`, "b"},
{`"abc"[2]`, "c"},
@@ -506,7 +506,7 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
noReturn := fn() { };
noReturn();
`,
expected: Null,
expected: object.NULL,
},
{
input: `
@@ -515,7 +515,7 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
noReturn();
noReturnTwo();
`,
expected: Null,
expected: object.NULL,
},
}
@@ -724,16 +724,16 @@ func TestBuiltinFunctions(t *testing.T) {
},
{`len([1, 2, 3])`, 3},
{`len([])`, 0},
{`print("hello", "world!")`, Null},
{`print("hello", "world!")`, object.NULL},
{`first([1, 2, 3])`, 1},
{`first([])`, Null},
{`first([])`, object.NULL},
{`first(1)`,
&object.Error{
Message: "TypeError: first() expected argument #1 to be `array` got `int`",
},
},
{`last([1, 2, 3])`, 3},
{`last([])`, Null},
{`last([])`, object.NULL},
{`last(1)`,
&object.Error{
Message: "TypeError: last() expected argument #1 to be `array` got `int`",