1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
package vm
import (
"jinx/pkg/lang/vm/value"
)
type Stack struct {
data []value.Value
calls []callFrame
}
func NewStack() Stack {
data := make([]value.Value, 0, 64)
calls := make([]callFrame, 0, 8)
calls = append(calls, callFrame{
pc: 0,
returnPc: 0,
base: 0,
})
return Stack{
data: data,
calls: calls,
}
}
func (stack *Stack) Push(value value.Value) {
stack.data = append(stack.data, value)
}
func (stack *Stack) Pop() (value.Value, error) {
if stack.IsEmpty() || stack.ReachedBaseOfCall() {
return value.Value{}, ErrStackUnderflow
}
v, err := stack.Top()
if err != nil {
return value.Value{}, err
}
stack.data = stack.data[:stack.Len()-1]
return v, nil
}
func (stack *Stack) Local(offset int) (value.Value, error) {
if stack.ReachedBaseOfCall() {
return value.Value{}, ErrStackUnderflow
}
if offset < 0 || offset >= stack.Len() {
return value.Value{}, ErrLocalIndexOutOfBounds{Index: offset, Len: stack.Len()}
}
base := stack.TopCall().base
return stack.data[base+offset], nil
}
func (stack *Stack) Top() (value.Value, error) {
if stack.IsEmpty() || stack.ReachedBaseOfCall() {
return value.Value{}, ErrStackUnderflow
}
return stack.data[stack.Len()-1], nil
}
func (stack *Stack) Len() int {
return len(stack.data)
}
func (stack *Stack) IsEmpty() bool {
return len(stack.data) == 0
}
func (stack *Stack) PushCall(newPc, returnPc int) error {
if stack.CallDepth() == 1000 {
return ErrReachedMaxCallDepth
}
stack.calls = append(stack.calls, callFrame{
pc: newPc,
returnPc: returnPc,
base: stack.Len(),
})
return nil
}
func (stack *Stack) PopCall() (int, error) {
if stack.CallDepth() == 0 {
return 0, ErrReachedRootCallFrame
}
call := stack.TopCall()
stack.calls = stack.calls[:stack.CallDepth()-1]
return call.returnPc, nil
}
func (stack *Stack) ShiftTopCallBase(by int) error {
call := stack.TopCall()
newBase := call.base - by
if newBase < 0 {
return ErrCallBaseCantBeNegative
}
call.base = newBase
return nil
}
func (stack *Stack) TopCall() *callFrame {
return &stack.calls[len(stack.calls)-1]
}
func (stack *Stack) CallDepth() int {
return len(stack.calls)
}
func (stack *Stack) ReachedBaseOfCall() bool {
return stack.TopCall().base == stack.Len()
}
type callFrame struct {
pc int // Beginning of the called function.
returnPc int // Where to return to after the called function returns.
base int // Base of the local variables on the data stack.
}
|