we in era3 boys; overhauled expression system, and variables are now in maps
This commit is contained in:
parent
bf29e6c3dd
commit
43e450c207
14 changed files with 945 additions and 452 deletions
195
core/vm.go
195
core/vm.go
|
|
@ -104,6 +104,8 @@ const (
|
|||
|
||||
// InstructionSwap swap the two top items on the stack (1, 2 -> 2, 1)
|
||||
InstructionSwap
|
||||
// InstructionDuplicate push a copy of the item on top of the stack (1 -> 1, 1)
|
||||
InstructionDuplicate
|
||||
|
||||
// InstructionAnd pop two booleans and push true if both are true
|
||||
InstructionAnd
|
||||
|
|
@ -234,6 +236,8 @@ func (b Bytecode) String() string {
|
|||
return "ACCESS_PROPERTY"
|
||||
case InstructionConcatLists:
|
||||
return "CONCAT_LISTS"
|
||||
case InstructionDuplicate:
|
||||
return "DUPLICATE"
|
||||
}
|
||||
return "UNDEFINED"
|
||||
}
|
||||
|
|
@ -267,6 +271,30 @@ func (c Chunk) String() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func (c *Chunk) Equals(other *Chunk) bool {
|
||||
if len(c.Bytecode) != len(other.Bytecode) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, bc := range c.Bytecode {
|
||||
if other.Bytecode[i] != bc {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.Constants) != len(other.Constants) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(c.Constants); i++ {
|
||||
if other.Constants[i] != c.Constants[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
|
||||
return &Chunk{bytecode, constants}
|
||||
}
|
||||
|
|
@ -327,23 +355,26 @@ type VM struct {
|
|||
chunk *Chunk
|
||||
|
||||
// instruction pointer
|
||||
ip Pos
|
||||
scope Pos
|
||||
ip Pos
|
||||
|
||||
// global variable storage
|
||||
globals map[string]Value
|
||||
variableEnd Pos
|
||||
globals map[string]Value
|
||||
// local variable storage
|
||||
scope *Scope
|
||||
|
||||
stack *Stack[Value]
|
||||
call *Stack[Call]
|
||||
}
|
||||
|
||||
type Scope struct {
|
||||
current map[string]Value
|
||||
parent *Scope
|
||||
}
|
||||
|
||||
type Call struct {
|
||||
chunk *Chunk
|
||||
ip Pos
|
||||
stackEnd Pos
|
||||
variableEnd Pos
|
||||
scope Pos
|
||||
chunk *Chunk
|
||||
ip Pos
|
||||
scope *Scope
|
||||
}
|
||||
|
||||
var DefaultGlobals = map[string]Value{
|
||||
|
|
@ -355,7 +386,7 @@ var DefaultGlobals = map[string]Value{
|
|||
},
|
||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||
println(v[0].String())
|
||||
return nil, nil
|
||||
return &NilValue{}, nil
|
||||
},
|
||||
nil,
|
||||
false,
|
||||
|
|
@ -368,7 +399,7 @@ var DefaultGlobals = map[string]Value{
|
|||
},
|
||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||
print(v[0].String())
|
||||
return nil, nil
|
||||
return &NilValue{}, nil
|
||||
},
|
||||
nil,
|
||||
false,
|
||||
|
|
@ -630,6 +661,9 @@ func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
|
|||
call: NewStack[Call](callstackSize),
|
||||
|
||||
globals: DefaultGlobals,
|
||||
scope: &Scope{
|
||||
current: map[string]Value{},
|
||||
},
|
||||
}
|
||||
|
||||
return vm
|
||||
|
|
@ -646,24 +680,20 @@ func (vm *VM) Next() bool {
|
|||
case InstructionReturn:
|
||||
if vm.call.Current == 0 {
|
||||
return false
|
||||
} else {
|
||||
v := vm.stack.Pop()
|
||||
c := vm.call.Pop()
|
||||
|
||||
// reset stack current and variable end and scope
|
||||
vm.variableEnd = c.variableEnd
|
||||
vm.stack.Current = c.stackEnd
|
||||
vm.scope = c.scope
|
||||
|
||||
// reset to calling position
|
||||
vm.ip = c.ip
|
||||
vm.chunk = c.chunk
|
||||
|
||||
vm.purgeVars()
|
||||
|
||||
vm.stack.Push(v)
|
||||
}
|
||||
|
||||
v := vm.stack.Pop()
|
||||
c := vm.call.Pop()
|
||||
|
||||
// reset stack current and variable end and scope
|
||||
vm.scope = c.scope
|
||||
|
||||
// reset to calling position
|
||||
vm.ip = c.ip
|
||||
vm.chunk = c.chunk
|
||||
|
||||
vm.stack.Push(v)
|
||||
|
||||
case InstructionPop:
|
||||
vm.stack.Pop()
|
||||
|
||||
|
|
@ -805,28 +835,21 @@ func (vm *VM) Next() bool {
|
|||
switch f := v.(type) {
|
||||
case *FunctionValue:
|
||||
vm.call.Push(Call{
|
||||
chunk: vm.chunk,
|
||||
ip: vm.ip,
|
||||
stackEnd: vm.stack.Current - Pos(len(f.Params)),
|
||||
variableEnd: vm.variableEnd,
|
||||
scope: vm.scope,
|
||||
chunk: vm.chunk,
|
||||
ip: vm.ip,
|
||||
scope: vm.scope,
|
||||
})
|
||||
|
||||
vm.descend()
|
||||
|
||||
for i := len(f.Params) - 1; i >= 0; i-- {
|
||||
p := vm.stack.Current - Pos(len(f.Params)) + Pos(i)
|
||||
vm.stack.items[p] = &VariableValue{
|
||||
f.Params[i].Name,
|
||||
vm.stack.items[p],
|
||||
vm.scope,
|
||||
}
|
||||
vm.addVar(f.Params[i].Name, vm.stack.Pop())
|
||||
}
|
||||
|
||||
if f.Parent != nil {
|
||||
vm.addVar("this", f.Parent)
|
||||
}
|
||||
|
||||
vm.variableEnd = vm.stack.Current
|
||||
|
||||
vm.chunk = f.Chunk
|
||||
vm.ip = 0
|
||||
case *BuiltinFunctionValue:
|
||||
|
|
@ -868,24 +891,18 @@ func (vm *VM) Next() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
vm.stack.Push(v.value)
|
||||
vm.stack.Push(v)
|
||||
|
||||
case InstructionSetLocal:
|
||||
value := vm.stack.Pop().(Value)
|
||||
value := vm.stack.Peek().Clone()
|
||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
||||
|
||||
v := vm.getVar(name)
|
||||
|
||||
if v == nil {
|
||||
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
|
||||
}
|
||||
|
||||
v.value = value.Clone()
|
||||
vm.setVar(name, value)
|
||||
|
||||
case InstructionDeclareLocal:
|
||||
vm.addVar(
|
||||
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
|
||||
vm.stack.Pop().Clone(),
|
||||
vm.stack.Peek().Clone(),
|
||||
)
|
||||
|
||||
case InstructionGetGlobal:
|
||||
|
|
@ -954,6 +971,9 @@ func (vm *VM) Next() bool {
|
|||
|
||||
vm.stack.Push(r, l)
|
||||
|
||||
case InstructionDuplicate:
|
||||
vm.stack.Push(vm.stack.Peek().Clone())
|
||||
|
||||
case InstructionAccessProperty:
|
||||
source := vm.stack.Pop()
|
||||
property := vm.ReadConstant()
|
||||
|
|
@ -973,6 +993,7 @@ func (vm *VM) Next() bool {
|
|||
vm.stack.Push(member)
|
||||
|
||||
case InstructionBreakpoint:
|
||||
vm.stack.Push(&NilValue{})
|
||||
|
||||
default:
|
||||
panic("invalid byte code")
|
||||
|
|
@ -985,11 +1006,9 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
|||
switch f := v.(type) {
|
||||
case *FunctionValue:
|
||||
vm.call.Push(Call{
|
||||
chunk: vm.chunk,
|
||||
ip: vm.ip,
|
||||
stackEnd: vm.stack.Current,
|
||||
variableEnd: vm.variableEnd,
|
||||
scope: vm.scope,
|
||||
chunk: vm.chunk,
|
||||
ip: vm.ip,
|
||||
scope: vm.scope,
|
||||
})
|
||||
|
||||
for i := 0; i < len(f.Params); i++ {
|
||||
|
|
@ -1000,8 +1019,6 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
|||
vm.addVar("this", f.Parent)
|
||||
}
|
||||
|
||||
vm.variableEnd = vm.stack.Current
|
||||
|
||||
vm.chunk = f.Chunk
|
||||
vm.ip = 0
|
||||
|
||||
|
|
@ -1028,6 +1045,14 @@ func (vm *VM) TryNextByte() (Bytecode, error) {
|
|||
return 0, errors.New("there are no more instructions")
|
||||
}
|
||||
|
||||
for int(vm.ip) >= len(vm.chunk.Bytecode) && vm.call.Current > 0 {
|
||||
c := vm.call.Pop()
|
||||
|
||||
vm.ip = c.ip
|
||||
vm.chunk = c.chunk
|
||||
vm.scope = c.scope
|
||||
}
|
||||
|
||||
v := vm.chunk.Bytecode[vm.ip]
|
||||
vm.ip++
|
||||
|
||||
|
|
@ -1045,53 +1070,55 @@ func (vm *VM) NextByte() Bytecode {
|
|||
}
|
||||
|
||||
func (vm *VM) ascend() {
|
||||
vm.scope--
|
||||
|
||||
if vm.scope < 0 {
|
||||
if vm.scope.parent == nil {
|
||||
panic("invalid scope")
|
||||
}
|
||||
|
||||
vm.purgeVars()
|
||||
}
|
||||
|
||||
// purgeVars remove all variables not within scope
|
||||
func (vm *VM) purgeVars() {
|
||||
for ; vm.variableEnd > 0 && vm.stack.items[vm.variableEnd-1].(*VariableValue).scope > vm.scope; vm.variableEnd-- {
|
||||
vm.stack.Pop()
|
||||
}
|
||||
vm.scope = vm.scope.parent
|
||||
}
|
||||
|
||||
func (vm *VM) descend() {
|
||||
vm.scope++
|
||||
old := vm.scope
|
||||
|
||||
vm.scope = &Scope{
|
||||
map[string]Value{},
|
||||
old,
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) addVar(name string, value Value) {
|
||||
vm.variableEnd++
|
||||
vm.stack.Push(&VariableValue{
|
||||
name,
|
||||
value,
|
||||
vm.scope,
|
||||
})
|
||||
vm.scope.current[name] = value
|
||||
}
|
||||
|
||||
func (vm *VM) getVar(name string) *VariableValue {
|
||||
for i := vm.variableEnd - 1; i >= 0; i-- {
|
||||
v, ok := vm.stack.items[i].(*VariableValue)
|
||||
func (vm *VM) getVar(name string) Value {
|
||||
s := vm.scope
|
||||
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if v.name == name {
|
||||
for s != nil {
|
||||
if v, ok := s.current[name]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
s = s.parent
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *VM) setVar(name string, v Value) {
|
||||
s := vm.scope
|
||||
|
||||
for s != nil {
|
||||
if _, ok := s.current[name]; ok {
|
||||
s.current[name] = v
|
||||
break
|
||||
}
|
||||
|
||||
s = s.parent
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) HasNext() bool {
|
||||
return vm.ip < Pos(len(vm.chunk.Bytecode))
|
||||
return vm.ip < Pos(len(vm.chunk.Bytecode)) || vm.call.Current > 0
|
||||
}
|
||||
|
||||
func (vm *VM) GetConstant(id Bytecode) Value {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue