-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.go
302 lines (265 loc) · 6.46 KB
/
parser.go
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
package main
import (
"errors"
"fmt"
)
var NoMoreInstructionsError = errors.New("No more instructions to process")
var MachineHaltedError = errors.New("Machine halted")
var StackExhaustedError = errors.New("Stack exhausted!")
type MissingArgumentError struct {
opcode VMOpcode
ArgumentIndex MachineWord
}
func (m *MissingArgumentError) Error() string {
return fmt.Sprintf("Argument %d is missing from %v", m.ArgumentIndex, m.opcode)
}
type MachineWord uint16
type ReadOnlyMemory interface {
load(addr MachineWord) (MachineWord, error)
}
type Memory interface {
ReadOnlyMemory
store(addr MachineWord, value MachineWord) error
}
func ParseArgument(buffer MachineWord) (argument Argument, err error) {
argument.Value = buffer
if argument.Value >= 32768 && argument.Value <= 32775 {
argument.Type = RegisterArgumentType
argument.Value -= 32768
} else if argument.Value < 32768 {
argument.Type = LiteralArgumentType
} else {
err = fmt.Errorf("Invalid value %q", argument.Value)
}
return
}
func ParseArguments(memory ReadOnlyMemory, addr MachineWord, nargs MachineWord) (args []Argument, err error) {
args = make([]Argument, nargs)
for i := MachineWord(0); i < nargs; i += 1 {
value, readErr := memory.load(addr + i)
if readErr != nil {
err = readErr
break
}
if args[i], err = ParseArgument(value); err != nil {
args = args[:i]
break
}
}
return
}
func ParseCommand(memory ReadOnlyMemory, addr MachineWord) (cmd VMCommand, err error) {
rawOpcode, err := memory.load(addr)
if err != nil {
return cmd, nil
}
if rawOpcode > MachineWord(MaxOpcode) {
return cmd, fmt.Errorf("Invalid opcode \"%d\"", rawOpcode)
}
cmd.Opcode = VMOpcode(rawOpcode)
nargs := MachineWord(0)
switch cmd.Opcode {
case EqOp, GtOp, AddOp, MultOp, ModOp, AndOp, OrOp:
nargs = 3
case SetOp, JtOp, JfOp, NotOp, RMemOp, WMemOp:
nargs = 2
case PushOp, PopOp, JmpOp, CallOp, OutOp, InOp:
nargs = 1
case RetOp, NoopOp, HaltOp:
return
}
cmd.Arguments, err = ParseArguments(memory, addr+1, nargs)
return
}
func resolveArgument(arg Argument, registers []MachineWord) (MachineWord, error) {
// Either get value from register or return literal
switch arg.Type {
case LiteralArgumentType:
if arg.Value > 32767 {
return 0, fmt.Errorf("Invalid literal %q", arg.Value)
}
return arg.Value, nil
case RegisterArgumentType:
if arg.Value > 7 {
return 0, fmt.Errorf("Invalid register %q", arg.Value)
}
return MachineWord(registers[arg.Value]), nil
default:
return 0, fmt.Errorf("Invalid argument type %q", arg.Type)
}
}
func resolveArguments(args []Argument, registers []MachineWord) (values []MachineWord, err error) {
values = make([]MachineWord, len(args))
for i, arg := range args {
if values[i], err = resolveArgument(arg, registers); err != nil {
return
}
}
return
}
func push[T any](value T, stack *[]T) {
*stack = append(*stack, value)
}
func pop[T any](stack *[]T) (value T, err error) {
if len(*stack) == 0 {
err = StackExhaustedError
} else {
value = (*stack)[len(*stack)-1]
*stack = (*stack)[:len(*stack)-1]
}
return
}
func readChar() (char rune, err error) {
nchars, err := fmt.Scanf("%c", &char)
if nchars != 1 {
err = fmt.Errorf("Failed to read input from stdin")
}
return
}
type VM struct {
registers []MachineWord
stack []MachineWord
memory Memory
pc MachineWord
}
func NewVM(memory Memory) VM {
return VM{
registers: make([]MachineWord, 8),
stack: make([]MachineWord, 0),
memory: memory,
pc: MachineWord(0),
}
}
func (v *VM) resolve(args []Argument) ([]MachineWord, error) {
return resolveArguments(args, v.registers)
}
func (v *VM) fetch() (cmd VMCommand, err error) {
// Fetch instruction at pc
cmd, err = ParseCommand(v.memory, v.pc)
return
}
func (v *VM) Step() (err error) {
var cmd VMCommand
if cmd, err = v.fetch(); err != nil {
return err
}
if pc, err := v.executeOp(cmd); err != nil {
return err
} else {
v.pc = pc
}
return nil
}
func (v *VM) Run() error {
stepResult := v.Step()
for ; stepResult == nil; stepResult = v.Step() {
}
if !errors.Is(stepResult, NoMoreInstructionsError) {
return stepResult
}
return nil
}
func (v *VM) executeStorageOperation(cmd VMCommand) error {
dest := cmd.Arguments[0].Value
args, err := v.resolve(cmd.Arguments[1:])
if err != nil {
return err
}
switch cmd.Opcode {
case EqOp:
if args[0] == args[1] {
v.registers[dest] = 1
} else {
v.registers[dest] = 0
}
case GtOp:
if args[0] > args[1] {
v.registers[dest] = 1
} else {
v.registers[dest] = 0
}
case SetOp:
v.registers[dest] = args[0]
case PopOp:
v.registers[dest], err = pop(&v.stack)
if err != nil {
return err
}
case AddOp:
v.registers[dest] = (args[0] + args[1]) % 32768
case MultOp:
v.registers[dest] = (args[0] * args[1]) % 32768
case ModOp:
v.registers[dest] = args[0] % args[1]
case AndOp:
v.registers[dest] = args[0] & args[1]
case OrOp:
v.registers[dest] = args[0] | args[1]
case NotOp:
v.registers[dest] = (^args[0]) & 32767
case RMemOp:
v.registers[dest], err = v.memory.load(args[0])
if err != nil {
return err
}
case InOp:
char, err := readChar()
if err != nil {
return err
}
v.registers[dest] = MachineWord(char)
default:
return fmt.Errorf("Unknown storage command %q", cmd)
}
return nil
}
func (v *VM) executeControlFlow(cmd VMCommand) (pc MachineWord, err error) {
pc = v.pc + MachineWord(len(cmd.Arguments)) + 1
args, err := v.resolve(cmd.Arguments)
if err != nil {
return
}
switch cmd.Opcode {
case HaltOp:
err = MachineHaltedError
case RetOp:
pc, err = pop(&v.stack)
if errors.Is(err, StackExhaustedError) {
err = MachineHaltedError
}
case NoopOp:
case PushOp:
push(args[0], &v.stack)
case JmpOp:
pc = args[0]
case JtOp:
if args[0] != 0 {
pc = args[1]
}
case JfOp:
if args[0] == 0 {
pc = args[1]
}
case WMemOp:
err = v.memory.store(args[0], args[1])
case CallOp:
push(pc, &v.stack)
pc = args[0]
case OutOp:
fmt.Printf("%c", args[0])
default:
pc = 0
err = fmt.Errorf("Unknown control flow operation: %q", cmd)
}
return
}
func (v *VM) executeOp(cmd VMCommand) (MachineWord, error) {
switch cmd.Opcode {
case HaltOp, RetOp, NoopOp, PushOp, JmpOp, JtOp, JfOp, WMemOp, CallOp, OutOp:
return v.executeControlFlow(cmd)
case EqOp, GtOp, SetOp, PopOp, AddOp, MultOp, ModOp, AndOp, OrOp, NotOp, RMemOp, InOp:
return v.pc + MachineWord(len(cmd.Arguments)) + 1, v.executeStorageOperation(cmd)
default:
return 0, fmt.Errorf("Unknown operation: %q", cmd)
}
}