-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstackasm01.py
More file actions
321 lines (250 loc) · 6.27 KB
/
stackasm01.py
File metadata and controls
321 lines (250 loc) · 6.27 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
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# stackasm v0.1
import inspect
import readline
import atexit
import gzip
def main():
repl()
def repl():
load_history()
print("stackasm v0.1")
while True:
try:
eval_str(input("> "))
except EvalError as e:
print(f"eval failed: {e}")
except EOFError:
print("bye")
break
def load_history():
history_file = ".stackasm_history"
try:
readline.read_history_file(history_file)
except FileNotFoundError:
try:
with open(history_file, "wb"):
pass
except IOError as e:
print(f"failed to create history file {history_file}")
sys.exit(1)
atexit.register(readline.write_history_file, history_file)
def eval_str(source: str):
for word in source.split():
evaluate(word)
# evaluator state
stack = []
dictionary = {}
class EvalError(Exception):
"""Evaluation failed"""
current_quote = []
quote_depth = 0
def evaluate(word: str):
global current_quote, quote_depth
if word == "]":
quote_depth -= 1
if quote_depth == 0:
stack.append(current_quote)
else:
current_quote.append(word)
return
if word == "[":
if quote_depth == 0:
current_quote = []
else:
current_quote.append(word)
quote_depth += 1
return
if quote_depth:
current_quote.append(word)
return
if isinstance(word, str) and word.startswith("'"):
stack.append(word[1:])
return
if isinstance(word, list):
stack.append(word)
return
try:
stack.append(int(word))
return
except ValueError:
pass
try:
fn = dictionary[word]
except KeyError:
raise EvalError(f"unknown word: {word}") from None
fn()
# builtins
def builtin(name_or_fn):
if isinstance(name_or_fn, str):
def inner(fn):
if not callable(fn):
raise ValueError("expected a function")
dictionary[name_or_fn] = lambda: stack_call(fn)
return fn
return inner
if callable(name_or_fn):
dictionary[name_or_fn.__name__] = lambda: stack_call(name_or_fn)
return name_or_fn
else:
raise ValueError("expected a function or a name")
def stack_call(fn):
global stack
if not callable(fn):
raise ValueError("expected a callable")
arity = len(inspect.signature(fn).parameters)
if arity > len(stack):
raise EvalError("stack underflow")
if arity > 0:
args = stack[-arity:]
stack = stack[:-arity]
else:
args = []
result = fn(*args)
if result is None:
return
if isinstance(result, tuple):
stack.extend(result)
else:
stack.append(result)
def stack_pretty(l):
def pretty_parts(l):
for item in l:
if isinstance(item, list):
yield "["
yield from pretty_parts(item)
yield "]"
else:
yield str(item)
return " ".join(pretty_parts(l))
@builtin
def apply(quotation):
for word in quotation:
evaluate(word)
@builtin("def")
def define(name, value):
if isinstance(value, list):
dictionary[name] = lambda: apply(value)
else:
dictionary[name] = lambda: stack.append(value)
@builtin("+")
def add(a, b):
return a + b
@builtin
def negate(x):
return -x
@builtin
def drop(x):
return
@builtin
def swap(x, y):
return y, x
@builtin
def dup(x):
return x, x
@builtin
def rot(a, b, c):
return b, c, a
@builtin("shift-left")
def shift_left(x, amount):
return x << amount
@builtin("shift-right")
def shift_right(x, amount):
return x >> amount
@builtin("bitwise-or")
def bitwise_or(x, y):
return x | y
@builtin("import")
def import_file(name):
with open(name, "r") as f:
source = f.read()
eval_str(source)
@builtin("bin")
def make_bin(x):
# hax
return int(str(x), 2)
@builtin(".")
def print_top_of_stack(x):
print(x)
@builtin(".s")
def print_stack():
print(f"stack: {stack_pretty(stack)}")
buffer_stack = []
current_buffer = []
temp_buffer = []
@builtin("to-temp-buffer")
def to_temp_buffer():
global temp_buffer
temp_buffer = current_buffer
@builtin("emit-temp-buffer")
def emit_temp_buffer():
current_buffer.extend(temp_buffer)
@builtin("push-buffer")
def push_buffer():
global current_buffer
buffer_stack.append(current_buffer)
current_buffer = []
@builtin("consume-buffer")
def consume_buffer(transform):
global current_buffer
to_consume = current_buffer
current_buffer = buffer_stack.pop()
for x in to_consume:
stack.append(x)
apply(transform)
@builtin("swap-buffer")
def swap_buffer():
global current_buffer
second = buffer_stack.pop()
buffer_stack.append(current_buffer)
current_buffer = second
@builtin
def emit(x):
current_buffer.append(x & 0xFF)
@builtin("buffer-nth")
def buffer_nth(n):
return current_buffer[n]
@builtin("print-buffer")
def print_buffer():
print(current_buffer)
@builtin("bin-buffer")
def bin_buffer():
for item in current_buffer:
print(bin(item)[2:].rjust(8, "0"))
@builtin("buffer-pos")
def buffer_pos():
return len(current_buffer)
@builtin("symbol-length")
def symbol_length(s):
return len(s)
@builtin("emit-symbol")
def emit_symbol(s):
current_buffer.extend((ord(c) for c in s))
@builtin("emit-obj")
def emit_obj(x):
current_buffer.append(x)
@builtin("clear-buffer")
def clear_buffer():
current_buffer.clear()
@builtin("write-gzip-buffer")
def write_gzip_buffer(filename):
with gzip.open(filename, "wb") as f:
f.write(bytearray(current_buffer))
@builtin("asm-to-blocks-compcpu")
def asm_to_blocks_compcpu():
global current_buffer
from itertools import product
def index_to_xz(i):
x = 2 if i < 32 else 0
z = 63 - (i % 32) * 2
return x, z
map = {
(x, z, b * 2 + 1): ((byte >> b) & 1) + 1
for i, byte in enumerate(current_buffer)
for b in range(8)
for x, z in (index_to_xz(i),)
}
current_buffer = [
map.get((x, z, y), 0) for y, z, x in product(range(16), range(64), range(3))
]
if __name__ == "__main__":
main()