update + repl fixed + bugfixes

This commit is contained in:
Neemek 2026-07-10 21:15:28 +02:00
parent d315a53fbd
commit 828ebb23c0
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
10 changed files with 138 additions and 124 deletions

View file

@ -120,7 +120,7 @@ func TestAll(t *testing.T) {
t.Log("Comparing stacks")
CompareStacks(t, tc.expectedStack, vm.stack)
CompareStacks(t, tc.expectedStack, vm.Stack)
// expected scope == nil => we don't care
if tc.expectedScope != nil {

View file

@ -966,6 +966,8 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
}
return c.deduceSignature(n.statements[len(n.statements)-1])
case AssignNodeType:
return c.deduceSignature(tree.(*AssignNode).value)
default:
return nil, c.error(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()), tree)
}

View file

@ -642,7 +642,7 @@ func TestCompile(t *testing.T) {
}
t.Log("Executed bytecode")
CompareStacks(t, testCase.expectedStack, vm.stack)
CompareStacks(t, testCase.expectedStack, vm.Stack)
CompareScope(t, testCase.expectedScope, vm.scope)
})
}
@ -693,8 +693,8 @@ func TestCompiler_CleanStack(t *testing.T) {
}
// make sure stack has only assigned values
for i := 1; i < int(vm.stack.Current); i++ {
v := vm.stack.items[i]
for i := 1; i < int(vm.Stack.Current); i++ {
v := vm.Stack.items[i]
if v == nil || v.Type() != VariableValueType {
t.Errorf("Unclean stack! value %v at %d on the stack is intermediary", v.String(), i)

View file

@ -343,7 +343,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
yield,
logic,
start,
start + p.prev.End,
p.prev.End,
}
if name != nil {
@ -352,7 +352,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
fn,
true,
start,
start + p.prev.End,
p.prev.End,
}, nil
}

View file

@ -362,7 +362,7 @@ type VM struct {
// local variable storage
scope *Scope
stack *Stack[Value]
Stack *Stack[Value]
call *Stack[Call]
}
@ -657,7 +657,7 @@ var DefaultGlobals = map[string]Value{
func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
vm := &VM{
chunk: chunk,
stack: NewStack[Value](stackSize),
Stack: NewStack[Value](stackSize),
call: NewStack[Call](callstackSize),
globals: DefaultGlobals,
@ -682,7 +682,7 @@ func (vm *VM) Next() bool {
return false
}
v := vm.stack.Pop()
v := vm.Stack.Pop()
c := vm.call.Pop()
// reset stack current and variable end and scope
@ -692,10 +692,10 @@ func (vm *VM) Next() bool {
vm.ip = c.ip
vm.chunk = c.chunk
vm.stack.Push(v)
vm.Stack.Push(v)
case InstructionPop:
vm.stack.Pop()
vm.Stack.Pop()
case InstructionConstant:
c := vm.ReadConstant()
@ -704,140 +704,140 @@ func (vm *VM) Next() bool {
c.Scope = vm.scope
}
vm.stack.Push(c)
vm.Stack.Push(c)
case InstructionAddFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&FloatValue{l + r})
vm.Stack.Push(&FloatValue{l + r})
case InstructionSubFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&FloatValue{l - r})
vm.Stack.Push(&FloatValue{l - r})
case InstructionMulFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&FloatValue{l * r})
vm.Stack.Push(&FloatValue{l * r})
case InstructionDivFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&FloatValue{l / r})
vm.Stack.Push(&FloatValue{l / r})
case InstructionNegateFloat:
v := vm.stack.Pop().(*FloatValue).Number
v := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&FloatValue{-v})
vm.Stack.Push(&FloatValue{-v})
case InstructionAddInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Add(l, r)})
vm.Stack.Push(&IntegerValue{new(big.Int).Add(l, r)})
case InstructionSubInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Sub(l, r)})
vm.Stack.Push(&IntegerValue{new(big.Int).Sub(l, r)})
case InstructionMulInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Mul(l, r)})
vm.Stack.Push(&IntegerValue{new(big.Int).Mul(l, r)})
case InstructionDivInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Div(l, r)})
vm.Stack.Push(&IntegerValue{new(big.Int).Div(l, r)})
case InstructionNegateInt:
v := vm.stack.Pop().(*IntegerValue).Number
v := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Neg(v)})
vm.Stack.Push(&IntegerValue{new(big.Int).Neg(v)})
case InstructionEquals:
vm.stack.Push(
&BoolValue{vm.stack.Pop().Equals(vm.stack.Pop())},
vm.Stack.Push(
&BoolValue{vm.Stack.Pop().Equals(vm.Stack.Pop())},
)
case InstructionNotEqual:
vm.stack.Push(
&BoolValue{!vm.stack.Pop().Equals(vm.stack.Pop())},
vm.Stack.Push(
&BoolValue{!vm.Stack.Pop().Equals(vm.Stack.Pop())},
)
case InstructionNot:
b := vm.stack.Pop().(*BoolValue).Boolean
vm.stack.Push(&BoolValue{!b})
b := vm.Stack.Pop().(*BoolValue).Boolean
vm.Stack.Push(&BoolValue{!b})
case InstructionAnd:
r := vm.stack.Pop().(*BoolValue).Boolean
l := vm.stack.Pop().(*BoolValue).Boolean
vm.stack.Push(&BoolValue{l && r})
r := vm.Stack.Pop().(*BoolValue).Boolean
l := vm.Stack.Pop().(*BoolValue).Boolean
vm.Stack.Push(&BoolValue{l && r})
case InstructionOr:
r := vm.stack.Pop().(*BoolValue).Boolean
l := vm.stack.Pop().(*BoolValue).Boolean
vm.stack.Push(&BoolValue{l || r})
r := vm.Stack.Pop().(*BoolValue).Boolean
l := vm.Stack.Pop().(*BoolValue).Boolean
vm.Stack.Push(&BoolValue{l || r})
case InstructionLessFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l < r})
vm.Stack.Push(&BoolValue{l < r})
case InstructionLessOrEqualFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l <= r})
vm.Stack.Push(&BoolValue{l <= r})
case InstructionGreaterFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l > r})
vm.Stack.Push(&BoolValue{l > r})
case InstructionGreaterOrEqualFloat:
r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*FloatValue).Number
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l >= r})
vm.Stack.Push(&BoolValue{l >= r})
case InstructionLessInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) == -1})
vm.Stack.Push(&BoolValue{l.Cmp(r) == -1})
case InstructionLessOrEqualInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) != 1})
vm.Stack.Push(&BoolValue{l.Cmp(r) != 1})
case InstructionGreaterInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) == 1})
vm.Stack.Push(&BoolValue{l.Cmp(r) == 1})
case InstructionGreaterOrEqualInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
r := vm.Stack.Pop().(*IntegerValue).Number
l := vm.Stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) != -1})
vm.Stack.Push(&BoolValue{l.Cmp(r) != -1})
case InstructionCall:
v := vm.stack.Pop()
v := vm.Stack.Pop()
switch f := v.(type) {
case *FunctionValue:
vm.call.Push(Call{
@ -850,7 +850,7 @@ func (vm *VM) Next() bool {
vm.descend()
for i := len(f.Params) - 1; i >= 0; i-- {
vm.addVar(f.Params[i].Name, vm.stack.Pop())
vm.addVar(f.Params[i].Name, vm.Stack.Pop())
}
if f.Parent != nil {
@ -863,7 +863,7 @@ func (vm *VM) Next() bool {
args := make([]Value, len(f.Signature.In))
for i := len(f.Signature.In) - 1; i >= 0; i-- {
args[i] = vm.stack.Pop()
args[i] = vm.Stack.Pop()
}
v, err := f.F(vm, f.Parent, args)
@ -871,9 +871,13 @@ func (vm *VM) Next() bool {
vm.error(err.Error())
}
vm.stack.Push(v)
if v == nil {
v = &NilValue{}
}
vm.Stack.Push(v)
default:
vm.error(fmt.Sprintf("value called is not a function (%s, type %T)", v.DebugString(), v))
vm.error(fmt.Sprintf("%s (%s) is not callable ", v.DebugString(), v.Type()))
return false
}
@ -885,7 +889,7 @@ func (vm *VM) Next() bool {
case InstructionJumpFalse:
n := vm.NextU16()
if !vm.stack.Pop().(*BoolValue).Boolean {
if !vm.Stack.Pop().(*BoolValue).Boolean {
vm.ip += Pos(n)
}
@ -898,10 +902,10 @@ func (vm *VM) Next() bool {
return false
}
vm.stack.Push(v)
vm.Stack.Push(v)
case InstructionSetLocal:
value := vm.stack.Peek().Clone()
value := vm.Stack.Peek().Clone()
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
vm.setVar(name, value)
@ -909,50 +913,50 @@ func (vm *VM) Next() bool {
case InstructionDeclareLocal:
vm.addVar(
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
vm.stack.Peek().Clone(),
vm.Stack.Peek().Clone(),
)
case InstructionGetGlobal:
vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text])
vm.Stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text])
case InstructionSetGlobal:
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text] = vm.stack.Pop()
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text] = vm.Stack.Pop()
case InstructionTrue:
vm.stack.Push(&BoolValue{true})
vm.Stack.Push(&BoolValue{true})
case InstructionFalse:
vm.stack.Push(&BoolValue{false})
vm.Stack.Push(&BoolValue{false})
case InstructionNil:
vm.stack.Push(&NilValue{})
vm.Stack.Push(&NilValue{})
case InstructionFormList:
n := int(vm.NextU16())
items := make([]Value, n)
for i := n - 1; i >= 0; i-- {
items[i] = vm.stack.Pop()
items[i] = vm.Stack.Pop()
}
vm.stack.Push(&ListValue{
vm.Stack.Push(&ListValue{
items,
})
case InstructionNewList:
vm.stack.Push(&ListValue{[]Value{}})
vm.Stack.Push(&ListValue{[]Value{}})
case InstructionAppend:
value := vm.stack.Pop()
list := vm.stack.Pop().(*ListValue)
value := vm.Stack.Pop()
list := vm.Stack.Pop().(*ListValue)
list.Items = append(list.Items, value)
vm.stack.Push(list)
vm.Stack.Push(list)
case InstructionConcatLists:
r := vm.stack.Pop().(*ListValue)
l := vm.stack.Pop().(*ListValue)
r := vm.Stack.Pop().(*ListValue)
l := vm.Stack.Pop().(*ListValue)
vm.stack.Push(&ListValue{
vm.Stack.Push(&ListValue{
append(l.Items, r.Items...),
})
@ -963,26 +967,26 @@ func (vm *VM) Next() bool {
vm.ascend()
case InstructionStringConversion:
v := vm.stack.Pop()
vm.stack.Push(&StringValue{v.String()})
v := vm.Stack.Pop()
vm.Stack.Push(&StringValue{v.String()})
case InstructionStringConcatenation:
r := vm.stack.Pop().(*StringValue).Text
l := vm.stack.Pop().(*StringValue).Text
r := vm.Stack.Pop().(*StringValue).Text
l := vm.Stack.Pop().(*StringValue).Text
vm.stack.Push(&StringValue{l + r})
vm.Stack.Push(&StringValue{l + r})
case InstructionSwap:
r := vm.stack.Pop()
l := vm.stack.Pop()
r := vm.Stack.Pop()
l := vm.Stack.Pop()
vm.stack.Push(r, l)
vm.Stack.Push(r, l)
case InstructionDuplicate:
vm.stack.Push(vm.stack.Peek().Clone())
vm.Stack.Push(vm.Stack.Peek().Clone())
case InstructionAccessProperty:
source := vm.stack.Pop()
source := vm.Stack.Pop()
property := vm.ReadConstant()
member, err := source.Get(property.(*StringValue).String())
@ -997,7 +1001,7 @@ func (vm *VM) Next() bool {
member.(*BuiltinFunctionValue).Parent = source
}
vm.stack.Push(member)
vm.Stack.Push(member)
case InstructionBreakpoint:
/*
@ -1009,7 +1013,7 @@ func (vm *VM) Next() bool {
s = s.parent
}
*/
vm.stack.Push(&NilValue{})
vm.Stack.Push(&NilValue{})
default:
panic("invalid byte code")
@ -1027,6 +1031,9 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
scope: vm.scope,
})
vm.scope = f.Scope
vm.descend()
for i := 0; i < len(f.Params); i++ {
vm.addVar(f.Params[i].Name, args[i])
}
@ -1043,7 +1050,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
vm.Next()
return vm.stack.Pop(), nil
return vm.Stack.Pop(), nil
case *BuiltinFunctionValue:
return f.F(vm, f.Parent, args)
@ -1069,6 +1076,10 @@ func (vm *VM) TryNextByte() (Bytecode, error) {
vm.scope = c.scope
}
if int(vm.ip) >= len(vm.chunk.Bytecode) {
return 0, errors.New("there are no more instructions")
}
v := vm.chunk.Bytecode[vm.ip]
vm.ip++

View file

@ -76,8 +76,8 @@ func TestNewVM(t *testing.T) {
}
// should have given stack size
if vm.stack.Capacity != stackSize {
t.Errorf("vm.stack.Capacity = %d, want %d", vm.stack.Capacity, stackSize)
if vm.Stack.Capacity != stackSize {
t.Errorf("vm.stack.Capacity = %d, want %d", vm.Stack.Capacity, stackSize)
}
// should have given call stack size
@ -628,7 +628,7 @@ func TestVM_Execution(t *testing.T) {
for vm.Next() {
}
CompareStacks(t, test.resultingStack, vm.stack)
CompareStacks(t, test.resultingStack, vm.Stack)
})
}
}