spitewaste 0.1.008 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +5 -3
- data/lib/spitewaste.rb +2 -0
- data/lib/spitewaste/assembler.rb +48 -3
- data/lib/spitewaste/cli/docs.rb +2 -2
- data/lib/spitewaste/libspw/array.spw +64 -2
- data/lib/spitewaste/libspw/bits.spw +5 -1
- data/lib/spitewaste/libspw/docs.json +5744 -1
- data/lib/spitewaste/libspw/fun.spw +21 -0
- data/lib/spitewaste/libspw/math.spw +5 -4
- data/lib/spitewaste/libspw/random.spw +4 -0
- data/lib/spitewaste/libspw/rational.spw +30 -1
- data/lib/spitewaste/libspw/string.spw +41 -8
- data/lib/spitewaste/libspw/terminal.spw +14 -0
- data/lib/spitewaste/libspw/test.spw +2 -3
- data/lib/spitewaste/libspw/util.spw +104 -16
- data/lib/spitewaste/parsers/fucktional.rb +7 -3
- data/lib/spitewaste/parsers/spitewaste.rb +17 -7
- data/lib/spitewaste/version.rb +1 -1
- metadata +3 -2
@@ -56,3 +56,24 @@ _collatz_done: pop ret
|
|
56
56
|
|
57
57
|
; ruby:
|
58
58
|
; push "'" :strcat push "ruby -e 'p " swap :strcat shell ret
|
59
|
+
|
60
|
+
$_to_roman(r, v) {
|
61
|
+
push `v` :divmod swap push `r` swap :strrep
|
62
|
+
push -2 load swap :strcat push -2 swap store
|
63
|
+
}
|
64
|
+
|
65
|
+
; converts the number N to a string of roman numerals R
|
66
|
+
; [N] => [R]
|
67
|
+
;
|
68
|
+
; [2020] => ["MMXX"]
|
69
|
+
; [1666] => ["MDCLXVI"]
|
70
|
+
; [1337] => ["MCCCXXXVII"]
|
71
|
+
; [94] => ["XCIV"]
|
72
|
+
to_roman: push -2,0 store
|
73
|
+
$_to_roman("M", 1000) $_to_roman("CM", 900)
|
74
|
+
$_to_roman("D", 500) $_to_roman("CD", 400)
|
75
|
+
$_to_roman("C", 100) $_to_roman("XC", 90)
|
76
|
+
$_to_roman("L", 50) $_to_roman("XL", 40)
|
77
|
+
$_to_roman("X", 10) $_to_roman("IX", 9)
|
78
|
+
$_to_roman("V", 5) $_to_roman("IV", 4)
|
79
|
+
$_to_roman("I", 1) push 2 sub load ret
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import array ; arycat, arydup
|
2
2
|
import util ; eq, inc, range
|
3
3
|
|
4
|
-
; returns B raised to the power E
|
5
|
-
; ! TODO: support negative exponents (return Rational)
|
4
|
+
; returns B raised to the power E; if E must be negative, use ratpow instead
|
6
5
|
; [B E] => [B**E]
|
7
6
|
;
|
8
7
|
; [0 0] => [1], [0 9] => [0], [9 0] => [1]
|
@@ -20,10 +19,10 @@ _pow_done: swap slide 2 ret
|
|
20
19
|
;
|
21
20
|
; [0] => [1], [1] => [1], [2] => [2]
|
22
21
|
; [3] => [6], [5] => [120], [10] => [3628800]
|
23
|
-
factorial: push 0 swap :range
|
22
|
+
factorial: dup jz _fac_zero push 0 swap :range
|
24
23
|
_fac_loop: swap dup jz _fac_done mul jump _fac_loop
|
25
|
-
|
26
24
|
_fac_done: pop ret
|
25
|
+
_fac_zero: $++ ret
|
27
26
|
|
28
27
|
; returns the integer square root of N
|
29
28
|
; [N] => [floor(sqrt(N))]
|
@@ -155,6 +154,8 @@ _divisors_square: slide 1 $-- ret ; de-duplicate when N is a perfect square
|
|
155
154
|
; [16 4] => [1820]
|
156
155
|
; [50 3] => [19600]
|
157
156
|
nCk:
|
157
|
+
copy 1 copy 1 sub jn _nCk_empty
|
158
158
|
copy 1 copy 1 sub :factorial
|
159
159
|
swap :factorial mul
|
160
160
|
swap :factorial swap div ret
|
161
|
+
_nCk_empty: push 0 slide 2 ret
|
@@ -169,8 +169,37 @@ ratinv:
|
|
169
169
|
; [R(4,2) 5] => [R(32,1)]
|
170
170
|
; [R(-3,7) 0] => [R(1,1)]
|
171
171
|
; [R(-3,14) 2] => [R(9,196)]
|
172
|
-
|
172
|
+
; [R(5,1) -1] => [R(1,5)]
|
173
|
+
; [R(2,5) -2] => [R(25,4)]
|
174
|
+
; [R(-3,14) -3] => [R(-2744,27)]
|
175
|
+
ratpow: dup jn _ratpow_neg
|
173
176
|
swap push 2 :divmod push 2 mul $--
|
174
177
|
swap $RAT :divmod copy 2 mul
|
175
178
|
copy 3 :pow swap copy 3 :pow
|
176
179
|
swap :to_r :ratsimp slide 2 ret
|
180
|
+
_ratpow_neg: push -1 mul swap :ratinv swap :ratpow ret
|
181
|
+
|
182
|
+
; converts the rational number R to a "pseudo-float" with a whole (W) part
|
183
|
+
; and a fractional (F) part composed of P digits after the decimal point
|
184
|
+
; [R P] => [W F]
|
185
|
+
;
|
186
|
+
; [R(22,7) 2] => [3 14]
|
187
|
+
; [R(355,113) 6] => [3 141592]
|
188
|
+
; [R(8675,309) 10] => [28 744336569] TODO: leading 0 is lost (bug)
|
189
|
+
; [R(2,4) 3] => [0 500]
|
190
|
+
to_f: push -2 swap store :from_r :divmod push 0 swap
|
191
|
+
_to_f_loop:
|
192
|
+
push -1 load :divmod swap
|
193
|
+
copy 2 push 10 mul add
|
194
|
+
swap push 10 mul
|
195
|
+
push 2 :dig
|
196
|
+
push -2 dup :dec load :neg? jz _to_f_loop pop ret
|
197
|
+
|
198
|
+
; converts the rational number R to a string S of the form
|
199
|
+
; "<simplified numerator>/<simplified denominator>"
|
200
|
+
; [R] => [S]
|
201
|
+
;
|
202
|
+
; [R(22,7)] => ["22/7"]
|
203
|
+
; [R(2,4)] => ["1/2"]
|
204
|
+
; [R(99,9)] => ["11/1"]
|
205
|
+
ratstr: :ratsimp :from_r push 2 map (:to_s) push '/' :strjoinc ret
|
@@ -207,8 +207,10 @@ center: push ' ' :centerc ret
|
|
207
207
|
; ["foobar"] => ["fooba"]
|
208
208
|
; ["abc"] => ["ab"]
|
209
209
|
; ["a"] => [""]
|
210
|
-
;
|
211
|
-
strchop: dup
|
210
|
+
; [""] => [""]
|
211
|
+
strchop: dup jz _strchop_empty
|
212
|
+
dup :strlen push 1 sub push 0 swap :strslice ret
|
213
|
+
_strchop_empty: ret
|
212
214
|
|
213
215
|
; splits string S on delimiting character C, leaving the resultant substrings
|
214
216
|
; on the stack as a pseudo-array (length at top of stack)
|
@@ -293,7 +295,7 @@ strcount:
|
|
293
295
|
reduce (add) ret
|
294
296
|
|
295
297
|
; translates all characters in A to the corresponding characters in B
|
296
|
-
; in
|
298
|
+
; in string S, ; similar to the `tr` utility in Unix. A and B must be
|
297
299
|
; of the same length. TODO: make this smarter (ranges, length mismatch)
|
298
300
|
; ! clobbers heap addresses -1, -2, and -3
|
299
301
|
; [S A B] => [S']
|
@@ -319,9 +321,10 @@ _strtrans_no: pop ret
|
|
319
321
|
; [S] => [S']
|
320
322
|
;
|
321
323
|
; ["CJ"] => ["CDEFGHIJ"]
|
322
|
-
; ["DA"] => ["
|
324
|
+
; ["DA"] => ["DCBA"]
|
323
325
|
; ["af"] => ["abcdef"]
|
324
326
|
; ["09"] => ["0123456789"]
|
327
|
+
; ["90"] => ["9876543210"]
|
325
328
|
; ["(1"] => ["()*+,-./01"]
|
326
329
|
strexpand:
|
327
330
|
push 0 swap push 128 :divmod
|
@@ -342,17 +345,38 @@ _strsqueeze_loop: ; [s]
|
|
342
345
|
_strsqueeze_skip: pop jump _strsqueeze_loop
|
343
346
|
_strsqueeze_done: pop :strpack :strrev ret
|
344
347
|
|
348
|
+
$_strdel(cmp) {
|
349
|
+
:strunpack push -1 load :strlen
|
350
|
+
select (push -2 load swap :strindex `cmp`)
|
351
|
+
pop :strpack ret
|
352
|
+
}
|
353
|
+
|
354
|
+
; returns the string S with all characters in string T removed, like `tr -d`.
|
355
|
+
; If the first character of T is '^', instead only those characters are kept.
|
356
|
+
; [S T] => [S']
|
357
|
+
;
|
358
|
+
; ["abc123" "abc"] => ["123"]
|
359
|
+
; ["abc123" "123"] => ["abc"]
|
360
|
+
; ["abcba12321" "abc"] => ["12321"]
|
361
|
+
; ["abc12321cba" "^2ac"] => ["ac22ca"]
|
362
|
+
; ["facetious" "^aeiou"] => ["aeiou"]
|
363
|
+
strdel: push -1 copy 2 store push -2 copy 1 store
|
364
|
+
push 0 :charat push '^' :neq jz _strdel_comp $_strdel(:neg?)
|
365
|
+
_strdel_comp: $_strdel(:pos?)
|
366
|
+
|
345
367
|
; returns the sum of the ordinal values of the characters in string S
|
346
368
|
; [S] => [N]
|
347
369
|
;
|
348
370
|
; ["ABC"] => [198]
|
349
371
|
; ["012"] => [147]
|
350
372
|
; ["a"] => [97]
|
351
|
-
; [""] => []
|
352
|
-
strsum:
|
373
|
+
; [""] => [0]
|
374
|
+
strsum: dup jz _strsum_empty
|
375
|
+
:strunpack push 0 :to_a reduce (add) ret
|
376
|
+
_strsum_empty: ret
|
353
377
|
|
354
378
|
; rotates the string S to the left N times, wrapping
|
355
|
-
; [S N]
|
379
|
+
; [S N] => [S']
|
356
380
|
;
|
357
381
|
; ["abc" 0] => ["abc"]
|
358
382
|
; ["abcd" 1] => ["bcda"]
|
@@ -361,9 +385,18 @@ strsum: :strunpack push 0 :to_a reduce (add) ret
|
|
361
385
|
strrotl: push 128 swap copy 2 :strlen mod :pow :divmod :strcat ret
|
362
386
|
|
363
387
|
; rotates the string S to the right N times, wrapping
|
364
|
-
; [S N]
|
388
|
+
; [S N] => [S']
|
365
389
|
;
|
366
390
|
; ["abcd" 1] => ["dabc"]
|
367
391
|
; ["abcd" 5] => ["dabc"]
|
368
392
|
; ["foodbar" 3] => ["barfood"]
|
369
393
|
strrotr: push 0 swap sub :strrotl ret
|
394
|
+
|
395
|
+
; frobnicates the string S by XORing all its bytes with 42
|
396
|
+
; [S] => [S']
|
397
|
+
;
|
398
|
+
; ["foobar"] => ["LEEHKX"]
|
399
|
+
; ["LEEHKX"] => ["foobar"]
|
400
|
+
memfrob:
|
401
|
+
dup :strlen pop :strunpack push -1 load $++
|
402
|
+
map (push 42 :bxor) pop :strpack ret
|
@@ -0,0 +1,14 @@
|
|
1
|
+
;;; Miscellaneous terminal-based functionality
|
2
|
+
|
3
|
+
import util ; hex2rgb
|
4
|
+
|
5
|
+
$_setg(code) {
|
6
|
+
push 91,27 ochr ochr
|
7
|
+
push `code` onum push 50,59 ochr ochr
|
8
|
+
:hex2rgb each (push 59 ochr onum)
|
9
|
+
push 109 ochr ret
|
10
|
+
}
|
11
|
+
|
12
|
+
setfg: $_setg(38)
|
13
|
+
setbg: $_setg(48)
|
14
|
+
reset: push 109,91,27 ochr ochr ochr ret
|
@@ -1,11 +1,10 @@
|
|
1
1
|
import io ; print, println
|
2
2
|
import string ; strcat
|
3
|
+
import util ; die!
|
3
4
|
|
4
5
|
assert_eq:
|
5
6
|
dup copy 2 sub jz _assert_eq_yes
|
6
7
|
push "expected " :print onum
|
7
8
|
push ", got " :print onum
|
8
|
-
push " for test " :strcat :
|
9
|
-
exit
|
10
|
-
|
9
|
+
push " for test " swap :strcat :die!
|
11
10
|
_assert_eq_yes: pop pop pop ret
|
@@ -4,18 +4,51 @@ import stack ; to_a
|
|
4
4
|
import string ; charat, strcat, strindex, strlen
|
5
5
|
|
6
6
|
; inserts between the top two stack values the intervening consecutive elements
|
7
|
-
;
|
8
|
-
; [I J] => [I I
|
7
|
+
; supports counting up or down
|
8
|
+
; [I J] => [I I±1 ... J]
|
9
9
|
;
|
10
10
|
; [2 5] => [2 3 4 5]
|
11
|
+
; [5 2] => [5 4 3 2]
|
11
12
|
; [0 4] => [0 1 2 3 4]
|
13
|
+
; [4 0] => [4 3 2 1 0]
|
12
14
|
; [-2 0] => [-2 -1 0]
|
15
|
+
; [0 -2] => [0 -1 -2]
|
13
16
|
; [-3 3] => [-3 -2 -1 0 1 2 3]
|
14
|
-
; [
|
15
|
-
|
17
|
+
; [3 -3] => [3 2 1 0 -1 -2 -3]
|
18
|
+
; [4 4] => [4]
|
19
|
+
range: dup copy 2 sub jz _range_one dup copy 2 sub jn _range_down
|
16
20
|
copy 1 push 1 add swap
|
17
|
-
copy 1 copy 1 sub jn range
|
18
|
-
|
21
|
+
copy 1 copy 1 sub jn range pop ret
|
22
|
+
_range_one: pop ret
|
23
|
+
_range_down:
|
24
|
+
copy 1 push 1 sub swap
|
25
|
+
dup copy 2 sub jn _range_down pop ret
|
26
|
+
|
27
|
+
$range_loop(fn, cmp) {
|
28
|
+
`fn`:
|
29
|
+
dup copy 2 add push -1 load `cmp` jz _`fn`_done
|
30
|
+
copy 1 copy 1 add swap jump `fn`
|
31
|
+
_`fn`_done: pop ret
|
32
|
+
}
|
33
|
+
|
34
|
+
; inserts between I and J the intervening consecutive elements, counting by
|
35
|
+
; step S up/down to (but never beyond) J
|
36
|
+
; [I J S] => [I I±S ... ~J]
|
37
|
+
;
|
38
|
+
; [1 4 1] => [1 2 3 4]
|
39
|
+
; [4 1 -1] => [4 3 2 1]
|
40
|
+
; [2 10 3] => [2 5 8]
|
41
|
+
; [10 2 -3] => [10 7 4]
|
42
|
+
; [-5 25 10] => [-5 5 15 25]
|
43
|
+
; [25 -5 -10] => [25 15 5 -5]
|
44
|
+
; [4 20 3] => [4 7 10 13 16 19]
|
45
|
+
; [20 4 -3] => [20 17 14 11 8 5]
|
46
|
+
; [3 9 7] => [3]
|
47
|
+
; [9 3 -7] => [9]
|
48
|
+
steprange: swap push -1 copy 1 store copy 2 sub
|
49
|
+
jn _steprange_down_loop jump _steprange_loop ; prevent DCE
|
50
|
+
$range_loop(_steprange_loop, :lte)
|
51
|
+
$range_loop(_steprange_down_loop, :gte)
|
19
52
|
|
20
53
|
; prints the string at the top of the stack and halts execution
|
21
54
|
die!: :println exit
|
@@ -54,6 +87,7 @@ _stoi_invalid: pop pop slide 1 swap div push -2 load mul ret
|
|
54
87
|
_stoi_done: swap slide 2 push -2 load mul ret
|
55
88
|
|
56
89
|
; creature comforts
|
90
|
+
|
57
91
|
bin: push 2 :stoi ret
|
58
92
|
oct: push 8 :stoi ret
|
59
93
|
to_i: push 10 :stoi ret
|
@@ -63,11 +97,13 @@ hex: push 16 :stoi ret
|
|
63
97
|
; [N B]
|
64
98
|
;
|
65
99
|
; [42 2] => ["101010"]
|
100
|
+
; [-42 2] => ["-101010"]
|
66
101
|
; [511 8] => ["777"]
|
67
102
|
; [12345 10] => ["12345"]
|
103
|
+
; [-54321 10] => ["-54321"]
|
68
104
|
; [57005 16] => ["dead"]
|
69
105
|
; [81699 17] => ["gabe"]
|
70
|
-
itos: swap push
|
106
|
+
itos: swap push -2 copy 1 :neg? store :abs push 0
|
71
107
|
_itos_loop:
|
72
108
|
swap dup jz _itos_done
|
73
109
|
swap copy 1 copy 3 mod
|
@@ -75,9 +111,10 @@ _itos_loop:
|
|
75
111
|
swap :strcat
|
76
112
|
swap copy 2 div
|
77
113
|
swap jump _itos_loop
|
78
|
-
_itos_done: swap slide 2 ret
|
114
|
+
_itos_done: swap slide 2 push 45,-2 load mul swap :strcat ret
|
79
115
|
|
80
116
|
; creature comforts
|
117
|
+
|
81
118
|
to_bin: push 2 :itos ret
|
82
119
|
to_oct: push 8 :itos ret
|
83
120
|
to_s: push 10 :itos ret
|
@@ -134,22 +171,45 @@ _neq_no: push 0 ret
|
|
134
171
|
; pops A and B and pushes 1 if A is greater than B, 0 otherwise
|
135
172
|
; [A B] => [A > B]
|
136
173
|
;
|
137
|
-
; [4 3] => [1]
|
138
|
-
; [
|
139
|
-
; [2 2] => [0]
|
140
|
-
; [2 1] => [1]
|
174
|
+
; [4 3] => [1] [3 4] => [0]
|
175
|
+
; [2 2] => [0] [2 1] => [1]
|
141
176
|
gt: swap ; intentionally flow into lt
|
142
177
|
|
143
178
|
; pops A and B and pushes 1 if A is less than than B, 0 otherwise
|
144
179
|
; [A B] => [A < B]
|
145
180
|
;
|
146
|
-
; [3 4] => [1]
|
147
|
-
; [
|
148
|
-
; [2 2] => [0]
|
149
|
-
; [1 2] => [1]
|
181
|
+
; [3 4] => [1] [4 3] => [0]
|
182
|
+
; [2 2] => [0] [1 2] => [1]
|
150
183
|
lt: sub jn _lt_yes push 0 ret
|
151
184
|
_lt_yes: push 1 ret
|
152
185
|
|
186
|
+
; pops A and B and pushes 1 if A is less than or equal to B, 0 otherwise
|
187
|
+
; [A B] => [A > B]
|
188
|
+
;
|
189
|
+
; [2 2] => [1] [2 1] => [0]
|
190
|
+
; [4 3] => [0] [3 4] => [1]
|
191
|
+
lte: swap ; intentionally flow into gte
|
192
|
+
|
193
|
+
; pops A and B and pushes 1 if A is greater than or equal to B, 0 otherwise
|
194
|
+
; [A B] => [A > B]
|
195
|
+
;
|
196
|
+
; [2 2] => [1] [1 2] => [0]
|
197
|
+
; [3 4] => [0] [4 3] => [1]
|
198
|
+
gte: sub jn _gte_no push 1 ret
|
199
|
+
_gte_no: push 0 ret
|
200
|
+
|
201
|
+
; returns 1 if the number N is between A and B (inclusive), 0 otherwise
|
202
|
+
; ! A must be <= B for sensible results TODO: bug?
|
203
|
+
; [N A B]
|
204
|
+
;
|
205
|
+
; [5 0 10] => [1]
|
206
|
+
; [11 0 10] => [0]
|
207
|
+
; [4 0 4] => [1]
|
208
|
+
; [-1 0 4] => [0]
|
209
|
+
; [-5 -10 0] => [1]
|
210
|
+
; [3 4 2] => [0]
|
211
|
+
between?: copy 2 :gte swap copy 2 :lte mul slide 1 ret
|
212
|
+
|
153
213
|
; Though extremely rare, it's possible that we know a particular value is
|
154
214
|
; stored in the heap at some key, just not which one. This subroutine takes
|
155
215
|
; a value V to search for and a starting index I, and either returns the first
|
@@ -158,3 +218,31 @@ _lt_yes: push 1 ret
|
|
158
218
|
heap_seeking_missile:
|
159
219
|
$++ dup load copy 2 :eq jz heap_search
|
160
220
|
slide 1 ret
|
221
|
+
|
222
|
+
; converts the #RRGGBB (leading '#' optional) color string S to
|
223
|
+
; its individual RGB components as integers in the range 0-255
|
224
|
+
; [S] => [R G B]
|
225
|
+
;
|
226
|
+
; ["#000000"] => [0 0 0]
|
227
|
+
; ["ffffff"] => [255 255 255]
|
228
|
+
; ["#102030"] => [16 32 48]
|
229
|
+
; ["c0ffee"] => [192 255 238]
|
230
|
+
hex2rgb:
|
231
|
+
dup push 0 :charat push '#' :eq push 127 mul $++ div
|
232
|
+
push 128,2 :pow :divmod :hex swap
|
233
|
+
push 128,2 :pow :divmod :hex swap :hex ret
|
234
|
+
|
235
|
+
; converts R, G, and B components to length-6 hexadecimal string S
|
236
|
+
; ! dies if any of the values to convert aren't between 0 and 255
|
237
|
+
; [R G B] => [S]
|
238
|
+
;
|
239
|
+
; [0 0 0] => ["000000"]
|
240
|
+
; [255 255 255] => ["ffffff"]
|
241
|
+
; [16 32 48] => ["102030"]
|
242
|
+
; [192 255 238] => ["c0ffee"]
|
243
|
+
rgb2hex:
|
244
|
+
push 3 :arydup all (push 0,255 :between?) jz _rgb2hex_invalid
|
245
|
+
pop copy 2 push 256,2 :pow mul
|
246
|
+
copy 2 push 256 mul add add
|
247
|
+
slide 2 :to_hex push 6,48 :rjustc ret
|
248
|
+
_rgb2hex_invalid: push "(rgb2hex) invalid RGB" :die!
|
@@ -64,9 +64,13 @@ maxby_lesser_%1$s: swap jump maxby_resume_%1$s
|
|
64
64
|
maxby_done_%1$s:
|
65
65
|
SPW
|
66
66
|
|
67
|
-
'minby'
|
68
|
-
'each'
|
69
|
-
'
|
67
|
+
'minby' => 'maxby (%2$s push -1 mul)',
|
68
|
+
'each' => 'dup times (dup call roll %2$s push 1 sub) pop',
|
69
|
+
'all' => 'map (%2$s) reduce (add) push -11 load call eq',
|
70
|
+
# TODO: optimize any to stop early if possible
|
71
|
+
'any' => 'map (%2$s) reduce (add) push 0 call gt',
|
72
|
+
'none' => 'map (%2$s) reduce (add) push 0 call eq',
|
73
|
+
'count' => 'select (%2$s) dup call nslide',
|
70
74
|
'select' => generate_filter_spw('select', 0, 1),
|
71
75
|
'reject' => generate_filter_spw('reject', 1, 0),
|
72
76
|
}
|
@@ -11,7 +11,7 @@ module Spitewaste
|
|
11
11
|
INSTRUCTIONS = /(\S+):|(\b(#{OPERATORS_M2T.keys * ?|})\s+(-?\d\S*)?)/
|
12
12
|
SPECIAL_INSN = /(call|jump|jz|jn)\s+(\S+)/
|
13
13
|
|
14
|
-
attr_reader :src, :instructions, :error
|
14
|
+
attr_reader :src, :instructions, :error, :symbol_table
|
15
15
|
|
16
16
|
def initialize program, **options
|
17
17
|
@src = program.dup
|
@@ -57,14 +57,14 @@ module Spitewaste
|
|
57
57
|
private
|
58
58
|
|
59
59
|
def preprocess!
|
60
|
-
@src
|
60
|
+
@src << "\nimport syntax"
|
61
61
|
resolve_imports
|
62
62
|
seed_prng if @seen.include? 'random'
|
63
63
|
resolve_strings
|
64
|
-
remove_comments
|
65
64
|
add_sugar
|
66
|
-
|
65
|
+
remove_comments
|
67
66
|
propagate_macros
|
67
|
+
fucktionalize
|
68
68
|
end
|
69
69
|
|
70
70
|
def resolve_imports
|
@@ -78,7 +78,13 @@ module Spitewaste
|
|
78
78
|
while @src['import']
|
79
79
|
imports = []
|
80
80
|
@src.gsub!(/import\s+(\S+).*/) {
|
81
|
-
|
81
|
+
if $1 == ?*
|
82
|
+
imports = Dir[LIBSPW + '/*.spw'].map {
|
83
|
+
File.read(_1) if @seen.add? File.basename(_1, '.spw')
|
84
|
+
}
|
85
|
+
else
|
86
|
+
imports << resolve($1) if @seen.add? $1
|
87
|
+
end
|
82
88
|
'' # remove import statement
|
83
89
|
}
|
84
90
|
@src << imports.join(?\n)
|
@@ -91,7 +97,7 @@ module Spitewaste
|
|
91
97
|
end
|
92
98
|
|
93
99
|
def seed_prng
|
94
|
-
@src.prepend "push $seed,#{rand 2**31} store $seed = -9001"
|
100
|
+
@src.prepend "push $seed,#{rand 2**31} store $seed = -9001\n"
|
95
101
|
end
|
96
102
|
|
97
103
|
def resolve_strings
|
@@ -111,6 +117,10 @@ module Spitewaste
|
|
111
117
|
@src.gsub!(/'(.)'/) { $1.ord }
|
112
118
|
# quick push (`push 1,2,3` desugars to individual pushes)
|
113
119
|
@src.gsub!(/push \S+/) { |m| m.split(?,) * ' push ' }
|
120
|
+
# quick store (`^2` = `push 2 swap store`)
|
121
|
+
@src.gsub!(/\^(-?\d+)/, 'push \1 swap store')
|
122
|
+
# quick load (`@2` = `push 2 load`)
|
123
|
+
@src.gsub!(/@(-?\d+)/, 'push \1 load')
|
114
124
|
end
|
115
125
|
|
116
126
|
def gensym
|
@@ -160,7 +170,7 @@ module Spitewaste
|
|
160
170
|
subroutines = {}
|
161
171
|
while label = tokens.shift
|
162
172
|
sublen = tokens.index { |t| t[/:$/] } || tokens.size
|
163
|
-
subroutines[label.chop]
|
173
|
+
subroutines[label.chop] ||= tokens.shift sublen
|
164
174
|
end
|
165
175
|
|
166
176
|
# A subroutine may indirectly depend on the one immediately after by
|