-
Notifications
You must be signed in to change notification settings - Fork 0
/
instrs.nim
179 lines (137 loc) · 5.04 KB
/
instrs.nim
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
import tables
import bitops
import sugar
import sequtils
import strutils
import math
import util
import parse
type Instr* = proc(stack: var seq[uint64], instr_ptr: var uint64): void
var instr_impls_by_code* = newTable[uint64, Instr]()
proc instr(name: string, impl: Instr): void =
let code = symbol_to_code(name);
instr_impls_by_code[code] = impl
# == Control Flow == #
# End the program (no implementation, handled specially)
instr "stop", nil
# Push the instruction pointer, nannified
instr "{", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add: instr_ptr.nannify
instr_ptr += 1
# Pop the top value, denannify it, and set the instruction pointer to it
instr "}", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
instr_ptr = stack.pop.denannify
# If the top value is unsigned, advance to the matching `fi`.
# Otherwise, do nothing.
instr "[", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
if stack.top.sign_bit == 1:
instr_ptr += 1
else:
var depth = 0
for idx in instr_ptr ..< stack.len.uint64:
let instr_code = stack[idx]
if instr_code == symbol_to_code("["):
depth += 1
elif instr_code == symbol_to_code("]"):
depth -= 1
if depth == 0:
instr_ptr = idx
return
abort "NpI"
# Noop
instr "]", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
instr_ptr += 1
# == Value Shuffling == #
# Move the next item onto the top of the stack
instr "push", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add stack[instr_ptr + 1]
instr_ptr += 2
# Duplicate the top item of the stack
instr "dup", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add: stack.top
instr_ptr += 1
# Swap the top two values
instr "swap", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add: [stack.pop, stack.pop] # An obfuscated but cool implementation!
instr_ptr += 1
# Pop the top item
instr "drop", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
discard stack.pop
instr_ptr += 1
# == Value Manipulation == #
# Increment the top item
instr "++", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add: stack.pop + 1
instr_ptr += 1
# Decrement the top item
instr "--", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add: stack.pop - 1
instr_ptr += 1
# Floating-point addition
instr "+", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add cast[uint64](cast[float64](stack.pop) + cast[float64](stack.pop))
instr_ptr += 1
# Floating-point subtraction
instr "-", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add cast[uint64](cast[float64](stack.pop) - cast[float64](stack.pop))
instr_ptr += 1
# Floating-point multiplication
instr "*", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add cast[uint64](cast[float64](stack.pop) * cast[float64](stack.pop))
instr_ptr += 1
# Floating-point division
instr "/", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add cast[uint64](cast[float64](stack.pop) / cast[float64](stack.pop))
instr_ptr += 1
# Floating-point exponentiation
instr "**", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stack.add cast[uint64](cast[float64](stack.pop).pow cast[float64](stack.pop))
instr_ptr += 1
# Flip the sign bit of the top item
instr "neg", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
let top = stack.pop
if top.sign_bit == 1:
stack.add: top.bitand(bitnot(1'u64 shl 63))
else:
stack.add: top.bitor(1'u64 shl 63)
instr_ptr += 1
# Rotate the top item one to the right, wrapping
instr "rotr", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
let top = stack.pop
stack.add: (top shl 63).bitor(top shr 1)
instr_ptr += 1
# Rotate the top item one to the left, wrapping
instr "rotl", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
let top = stack.pop
stack.add: (top shl 1).bitor(top shr 63)
instr_ptr += 1
# == IO == #
# Read a character from stdin
instr "get/char", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
var chr: char
try:
chr = stdin.read_char
except EOFError:
chr = '\0'
stack.add: cast[uint64](chr).nannify
instr_ptr += 1
# Print the top item
instr "put", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
echo cast[float64](stack.top).`$`
instr_ptr += 1
# Print the entire stack
# Useful for debugging ;)
instr "put/all", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
echo "[" & stack.map(val => cast[float64](val).`$`).join(", ") & "]"
instr_ptr += 1
# Print the top item as asn ascii character, according to the first 7 bits
instr "put/char", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
stdout.write stack.top.bitand(0b1111111).char
instr_ptr += 1
# Print the top item as a bitvector
instr "put/bits", proc(stack: var seq[uint64], instr_ptr: var uint64): void =
var chars = ""
for i in countdown(63, 0):
chars &= (stack.top shr i).bitand(1).`$`
echo chars
instr_ptr += 1