-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbytecode.ts
More file actions
120 lines (102 loc) · 3.02 KB
/
Copy pathbytecode.ts
File metadata and controls
120 lines (102 loc) · 3.02 KB
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
export const Instruction = {
Add: { type: 'Add' },
Sub: { type: 'Sub' },
Mul: { type: 'Mul' },
Div: { type: 'Div' },
Rem: { type: 'Rem' },
Eq: { type: 'Eq' },
Lt: { type: 'Lt' },
Gt: { type: 'Gt' },
Le: { type: 'Le' },
Ge: { type: 'Ge' },
Push: (addr: number) => ({ type: 'Push', addr }) as const,
Load: (addr: number) => ({ type: 'Load', addr }) as const,
Save: (addr: number) => ({ type: 'Save', addr }) as const,
Jump: (offset: number) => ({ type: 'Jump', offset }) as const,
BEqZ: (offset: number) => ({ type: 'BEqZ', offset }) as const,
IsI64: { type: 'IsI64' },
Print: { type: 'Print' },
} as const
type GetValueOrReturnValue<T> = T extends (...args: any[]) => infer R ? R : T
type InstructionType = keyof typeof Instruction
export type Instruction = GetValueOrReturnValue<
(typeof Instruction)[InstructionType]
>
export class CodeBuffer {
static readonly maxInstructionSize = 3
static readonly maxGrowth = 1024 * 1024
#buf = new ArrayBuffer(1024, { maxByteLength: CodeBuffer.maxGrowth })
len = 0
get #view() {
return new DataView(this.#buf)
}
push(code: Instruction) {
if (this.len + CodeBuffer.maxInstructionSize > this.#buf.byteLength) {
const newLength = this.#buf.byteLength * 2
if (newLength <= CodeBuffer.maxGrowth) {
this.#buf.resize(newLength)
} else {
this.#buf = this.#buf.transferToFixedLength(newLength)
}
}
const start = this.len
this.#view.setUint8(this.len++, serializeCmd.get(code.type)!)
switch (code.type) {
case 'Push':
case 'Load':
case 'Save':
this.#view.setUint16(this.len, code.addr, true)
this.len += 2
break
case 'Jump':
case 'BEqZ':
this.#view.setInt16(this.len, code.offset, true)
this.len += 2
break
}
return new DataView(this.#buf, start, this.len - start)
}
get u8Array() {
return new Uint8Array(this.#buf, 0, this.len)
}
toString() {
const display = Array.of<string>()
let i = 0
while (i < this.len) {
const type = deserializeCmd.get(this.#view.getUint8(i))
let code = `${i}: ${type}`
switch (type) {
case 'Push':
case 'Load':
case 'Save':
code += `: addr=${this.#view.getUint16(i + 1, true)}`
i += 2
break
case 'Jump':
case 'BEqZ':
code += `: offset=${this.#view.getInt16(i + 1, true)}`
i += 2
break
}
display.push(code)
i++
}
return display.join('\n')
}
}
const serializeCmd = new Map(
(Object.keys(Instruction) as InstructionType[]).map((k, i) => [k, i]),
)
const deserializeCmd = new Map(Array.from(serializeCmd, ([k, v]) => [v, k]))
export type JumpableEntry = { from: number; fill: (offset: number) => void }
export class Label {
readonly #from = Array.of<JumpableEntry>()
jumpFrom(entry: JumpableEntry) {
this.#from.push(entry)
}
fillOffset(target: number) {
for (const { from, fill } of this.#from) {
fill(target - from)
}
}
}