spitewaste 0.2.0 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +2 -3
- data/bin/spw +1 -0
- data/lib/spitewaste.rb +4 -0
- data/lib/spitewaste/assembler.rb +1 -1
- data/lib/spitewaste/cli/version.rb +9 -0
- data/lib/spitewaste/emitters/image.rb +3 -2
- data/lib/spitewaste/emitters/schemes.yaml +127 -0
- data/lib/spitewaste/libspw/array.spw +54 -17
- data/lib/spitewaste/libspw/bits.spw +4 -4
- data/lib/spitewaste/libspw/docs.json +551 -16
- data/lib/spitewaste/libspw/fun.spw +36 -5
- data/lib/spitewaste/libspw/io.spw +13 -11
- data/lib/spitewaste/libspw/math.spw +6 -9
- data/lib/spitewaste/libspw/random.spw +32 -9
- data/lib/spitewaste/libspw/rational.spw +2 -2
- data/lib/spitewaste/libspw/stack.spw +23 -14
- data/lib/spitewaste/libspw/string.spw +45 -24
- data/lib/spitewaste/libspw/test.spw +3 -0
- data/lib/spitewaste/libspw/util.spw +41 -13
- data/lib/spitewaste/parsers/spitewaste.rb +8 -6
- data/lib/spitewaste/version.rb +1 -1
- metadata +4 -3
@@ -36,6 +36,12 @@ _isop_resume: sub add swap push 128 div jump _isop_loop
|
|
36
36
|
_isop_no: dup jump _isop_resume
|
37
37
|
_isop_done: pop ret
|
38
38
|
|
39
|
+
$_do_collatz() {
|
40
|
+
dup push 2 mod
|
41
|
+
swap copy 1 push 2 mul $++ mul
|
42
|
+
push 2 copy 2 sub div add
|
43
|
+
}
|
44
|
+
|
39
45
|
; returns the elements of the Collatz sequence for integer N as a pseudo-array
|
40
46
|
; ! may run forever on some as-yet-unknown input
|
41
47
|
; [N] => [A]
|
@@ -48,18 +54,29 @@ _isop_done: pop ret
|
|
48
54
|
collatz: push 1 ; sequence length
|
49
55
|
_collatz_loop:
|
50
56
|
copy 1 dup push 1 sub jz _collatz_done
|
51
|
-
|
52
|
-
swap copy 1 push 2 mul $++ mul
|
53
|
-
push 2 copy 2 sub div add
|
54
|
-
swap $++ jump _collatz_loop
|
57
|
+
$_do_collatz() swap $++ jump _collatz_loop
|
55
58
|
_collatz_done: pop ret
|
56
59
|
|
60
|
+
; returns the length L of the Collatz sequence for integer N
|
61
|
+
; ! may run forever on some as-yet-unknown input
|
62
|
+
; [N] => [L]
|
63
|
+
;
|
64
|
+
; [1] => [1]
|
65
|
+
; [4] => [3]
|
66
|
+
; [7] => [17]
|
67
|
+
; [189] => [107]
|
68
|
+
collatz_len: push 1 swap
|
69
|
+
_collatz_len_loop:
|
70
|
+
dup $-- jz _collatz_done
|
71
|
+
$_do_collatz() swap $++ swap jump _collatz_len_loop
|
72
|
+
copy 1 dup push 1 sub jz _collatz_done
|
73
|
+
|
57
74
|
; ruby:
|
58
75
|
; push "'" :strcat push "ruby -e 'p " swap :strcat shell ret
|
59
76
|
|
60
77
|
$_to_roman(r, v) {
|
61
78
|
push `v` :divmod swap push `r` swap :strrep
|
62
|
-
|
79
|
+
@-2 swap :strcat ^-2
|
63
80
|
}
|
64
81
|
|
65
82
|
; converts the number N to a string of roman numerals R
|
@@ -77,3 +94,17 @@ to_roman: push -2,0 store
|
|
77
94
|
$_to_roman("X", 10) $_to_roman("IX", 9)
|
78
95
|
$_to_roman("V", 5) $_to_roman("IV", 4)
|
79
96
|
$_to_roman("I", 1) push 2 sub load ret
|
97
|
+
|
98
|
+
; applies the ROT13 "cipher" to the string S
|
99
|
+
; [S] => [S']
|
100
|
+
;
|
101
|
+
; ["gnat"] => ["tang"]
|
102
|
+
; ["purely"] => ["cheryl"]
|
103
|
+
; ["cat.PNG"] => ["png.CAT"]
|
104
|
+
; ["Hello, world."] => ["Uryyb, jbeyq."]
|
105
|
+
; ["a123z"] => ["n123m"]
|
106
|
+
ROT13:
|
107
|
+
push "AZ" :strexpand push "az" :strexpand :strcat
|
108
|
+
push "NZ" :strexpand push "AM" :strexpand
|
109
|
+
push "nz" :strexpand push "am" :strexpand
|
110
|
+
:strcat :strcat :strcat :strtrans ret
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import string ; strcat, strpack, strrev, strunpack
|
2
2
|
|
3
3
|
; prints the character at the top of the stack until terminating zero
|
4
|
+
; [0 ... C] => []
|
4
5
|
print: :strunpack
|
5
6
|
_print_loop:
|
6
7
|
dup jz _print_done
|
@@ -8,34 +9,35 @@ _print_loop:
|
|
8
9
|
_print_done: pop ret
|
9
10
|
|
10
11
|
; print with newline
|
12
|
+
; [0 ... C] => []
|
11
13
|
println: :print push 10 ochr ret
|
12
14
|
|
13
|
-
;;;
|
14
|
-
|
15
15
|
; reads a line of input onto the top of the stack as a packed string
|
16
16
|
; ! clobbers heap address -1
|
17
|
+
; [] => [L]
|
17
18
|
getline: push 0 ; terminator for strpack
|
18
|
-
|
19
|
-
; read characters onto the stack until newline (10) or EOF (-1)
|
20
19
|
_getline_loop:
|
21
20
|
push -1 dup ichr load
|
22
21
|
dup jn _getline_eof
|
23
22
|
dup push 10 sub jz _getline_done
|
24
23
|
jump _getline_loop
|
25
|
-
|
26
24
|
_getline_eof: pop
|
27
25
|
_getline_done: :strpack :strrev ret
|
28
26
|
|
29
|
-
|
30
|
-
|
27
|
+
; displays the string S then reads a line of input
|
28
|
+
; [S] => [L]
|
31
29
|
prompt: :print :getline ret
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
; consume stdin until EOF
|
31
|
+
; consume stdin until EOF into the string S
|
32
|
+
; [] => [S]
|
36
33
|
readall: push 0 ; accumulated string
|
37
34
|
_readall_loop:
|
38
35
|
:getline dup jz _readall_done
|
39
36
|
:strcat jump _readall_loop
|
40
|
-
|
41
37
|
_readall_done: pop ret
|
38
|
+
|
39
|
+
; returns the contents C of the file at path P (a string)
|
40
|
+
; NON-STANDARD! This function makes use of the `shell` instruction, which is
|
41
|
+
; only(?) available in the Spiceweight Whitespace interpreter.
|
42
|
+
; [P] => [C]
|
43
|
+
readfile: push "cat " swap :strcat shell ret
|
@@ -51,7 +51,7 @@ ilog: push -1,0 store ; accumulator at -1
|
|
51
51
|
_ilog_loop: ; [n b]
|
52
52
|
swap copy 1 div dup jz _ilog_done
|
53
53
|
push -1 :inc swap jump _ilog_loop
|
54
|
-
_ilog_done:
|
54
|
+
_ilog_done: @-1 slide 2 ret
|
55
55
|
|
56
56
|
; returns the greatest common divisor of A and B
|
57
57
|
; [A B] => [gcd(A, B)]
|
@@ -111,10 +111,7 @@ abs: dup :sign mul ret
|
|
111
111
|
; [42 6] => [7 0]
|
112
112
|
; [ 1 5] => [0 1]
|
113
113
|
; ! [9 0] => [!!] TODO: find a way to expect exceptions
|
114
|
-
divmod:
|
115
|
-
push -1 swap store
|
116
|
-
dup push -1 load div
|
117
|
-
swap push -1 load mod ret
|
114
|
+
divmod: ^-1 dup @-1 div swap @-1 mod ret
|
118
115
|
|
119
116
|
; returns whether N is greater than 0
|
120
117
|
; [N] => [N > 0]
|
@@ -138,11 +135,11 @@ neg?: :sign push -1 :eq ret
|
|
138
135
|
; [25] => [1 5 25 3] ; no duplicate for perfect squares
|
139
136
|
; [60] => [1 2 3 4 5 6 60 30 20 15 12 10 12]
|
140
137
|
divisors: ; [n]
|
141
|
-
dup
|
138
|
+
dup ^-1 ; preserve N because array operations
|
142
139
|
:isqrt push 1 swap :range dup ; 1..isqrt(N)
|
143
|
-
reject (
|
144
|
-
map (
|
145
|
-
|
140
|
+
reject (@-1 swap mod) :arydup ; get first half of divisors
|
141
|
+
map (@-1 swap div) :arycat ; map first half to second half
|
142
|
+
@-1 copy 2 dup mul sub jz _divisors_square ret
|
146
143
|
_divisors_square: slide 1 $-- ret ; de-duplicate when N is a perfect square
|
147
144
|
|
148
145
|
; returns the number of ways to choose K elements from a set of N
|
@@ -1,16 +1,39 @@
|
|
1
|
-
import
|
1
|
+
import array ; sorted?
|
2
|
+
import math ; pow
|
3
|
+
import string ; strtoa
|
2
4
|
|
3
|
-
|
5
|
+
; seeds the random number generator with integer S
|
6
|
+
; [S] => []
|
7
|
+
srand: ^$seed ret
|
4
8
|
|
9
|
+
; returns the next number N in the linear congruential generator (better MINSTD)
|
10
|
+
; [] => [N]
|
5
11
|
rand:
|
6
12
|
push $seed dup dup load
|
7
|
-
push
|
8
|
-
push 2,
|
13
|
+
push 48271 mul
|
14
|
+
push 2,31 :pow $-- mod
|
9
15
|
store load ret
|
10
16
|
|
11
|
-
|
12
|
-
|
17
|
+
; returns a random integer I between A and B (inclusive)
|
18
|
+
; [A B] => [I]
|
19
|
+
rand_range:
|
20
|
+
$++ copy 1 sub :rand swap mod add ret
|
13
21
|
|
14
|
-
|
15
|
-
|
16
|
-
|
22
|
+
; returns an array A of N random integers between 1 and D (inclusive)
|
23
|
+
; [N D] => [A]
|
24
|
+
dice: ^-2 dup ^-1 times (push 1 @-2 :rand_range) @-1 ret
|
25
|
+
|
26
|
+
; shuffles the array A in-place using the modern Fisher-Yates algorithm
|
27
|
+
; [A] => [A']
|
28
|
+
shuffle: dup $-- ^-3
|
29
|
+
_shuffle_loop:
|
30
|
+
push 0 @-3 :rand_range @-3 :aryswap
|
31
|
+
push -3 :dec @-3 push -1 mul jn _shuffle_loop ret
|
32
|
+
|
33
|
+
; shuffles the characters of the string S, producing a random anagram
|
34
|
+
; [S] => [S']
|
35
|
+
strfry: :strtoa :shuffle pop :strpack ret
|
36
|
+
|
37
|
+
; sorts the array A if you're lucky
|
38
|
+
; [A] => [A']
|
39
|
+
bogosort: :shuffle :arydup :sorted? jz bogosort ret
|
@@ -187,9 +187,9 @@ _ratpow_neg: push -1 mul swap :ratinv swap :ratpow ret
|
|
187
187
|
; [R(355,113) 6] => [3 141592]
|
188
188
|
; [R(8675,309) 10] => [28 744336569] TODO: leading 0 is lost (bug)
|
189
189
|
; [R(2,4) 3] => [0 500]
|
190
|
-
to_f:
|
190
|
+
to_f: ^-2 :from_r :divmod push 0 swap
|
191
191
|
_to_f_loop:
|
192
|
-
|
192
|
+
@-1 :divmod swap
|
193
193
|
copy 2 push 10 mul add
|
194
194
|
swap push 10 mul
|
195
195
|
push 2 :dig
|
@@ -1,6 +1,6 @@
|
|
1
1
|
;;; Heavy-handed stack manipulation
|
2
2
|
|
3
|
-
; These
|
3
|
+
; These subroutines do some pretty intricate stack-based operations, relying
|
4
4
|
; heavily on clobbering the heap in order to maintain their "bookkeeping".
|
5
5
|
; Many of them use heap addresses -10 and lower, unbounded, so they're only
|
6
6
|
; meant to be used in a pinch or when there just isn't much of an alternative.
|
@@ -16,15 +16,13 @@ import util ; dec
|
|
16
16
|
; [1 2 3 4 5 3] => [1 3 4 5 2]
|
17
17
|
roll:
|
18
18
|
push -10 dup store ; current heap index kept at -10
|
19
|
-
_roll_keep:
|
19
|
+
_roll_keep:
|
20
20
|
dup jz _roll_remove
|
21
21
|
push -10 :dec
|
22
|
-
swap
|
22
|
+
swap @-10 swap store
|
23
23
|
push 1 sub jump _roll_keep
|
24
|
-
_roll_remove:
|
25
|
-
|
26
|
-
swap push -10 swap store
|
27
|
-
_roll_restore: ; i
|
24
|
+
_roll_remove: push 10 sub load swap ^-10
|
25
|
+
_roll_restore:
|
28
26
|
dup load swap push 1 add
|
29
27
|
dup push 10 add jz _roll_done
|
30
28
|
jump _roll_restore
|
@@ -37,15 +35,15 @@ _roll_done: load ret
|
|
37
35
|
; [1 2 3 4 5 8 5] => [8 1 2 3 4 5]
|
38
36
|
bury:
|
39
37
|
push -10 dup store ; current heap index kept at -10
|
40
|
-
swap
|
38
|
+
swap ^-9 ; preserve element to bury
|
41
39
|
_bury_keep: ; [n]
|
42
40
|
dup jz _bury_restore
|
43
41
|
push -10 :dec
|
44
|
-
swap
|
42
|
+
swap @-10 swap store
|
45
43
|
push 1 sub jump _bury_keep
|
46
44
|
_bury_restore:
|
47
45
|
push 9 sub load
|
48
|
-
|
46
|
+
@-10 :_roll_restore pop ret
|
49
47
|
|
50
48
|
|
51
49
|
; "digs" out the Ith element of the stack and discards it
|
@@ -63,14 +61,14 @@ dig: :roll pop ret
|
|
63
61
|
;
|
64
62
|
; [-1 9 8 7 -1] => [7 8 9 3]
|
65
63
|
; [0 'c' 'b' 'a' 0] => ['a' 'b' 'c' 3]
|
66
|
-
to_a:
|
64
|
+
to_a: ^-1 push -10 dup store
|
67
65
|
_to_a_loop:
|
68
|
-
dup
|
66
|
+
dup @-1 sub jz _to_a_sentinel
|
69
67
|
push -10 dup :dec load
|
70
68
|
swap store jump _to_a_loop
|
71
69
|
_to_a_sentinel: pop push -10
|
72
70
|
_to_a_restore:
|
73
|
-
dup
|
71
|
+
dup @-10 sub jz _to_a_done
|
74
72
|
push 1 sub dup load swap
|
75
73
|
jump _to_a_restore
|
76
74
|
_to_a_done: push -10 swap sub ret
|
@@ -88,7 +86,7 @@ _npop_done: pop ret
|
|
88
86
|
;
|
89
87
|
; [1 2 3 4 5 2] => [1 2 5]
|
90
88
|
; [1 2 3 4 1] => [1 2 4]
|
91
|
-
nslide: swap
|
89
|
+
nslide: swap ^-1 :npop @-1 ret
|
92
90
|
|
93
91
|
; copies the Nth element to the top of the stack; this does exactly what
|
94
92
|
; a `copy N` instruction would do, but we don't always know N in advance
|
@@ -106,3 +104,14 @@ _ncopy_restore:
|
|
106
104
|
dup push 9 add jz _ncopy_done
|
107
105
|
dup load swap push 1 add jump _ncopy_restore
|
108
106
|
_ncopy_done: pop ret
|
107
|
+
|
108
|
+
; swaps the two arrays at the top of the stack
|
109
|
+
; [A B] => [B A]
|
110
|
+
;
|
111
|
+
; [1 2 3 3 3 2 1 3] => [3 2 1 3 1 2 3 3]
|
112
|
+
; [5 6 2 9 7 5 3 1 5] => [9 7 5 3 1 5 5 6 2]
|
113
|
+
; [0 0 2 4 2 0 3] => [4 2 0 3 0 0 2]
|
114
|
+
; [1 1 2 1] => [2 1 1 1]
|
115
|
+
swapary:
|
116
|
+
push -10 :aryheap push -11 @-9 sub :aryheap
|
117
|
+
push -10 :heapary push -11 @-9 sub :heapary ret
|
@@ -224,13 +224,13 @@ _strchop_empty: ret
|
|
224
224
|
; ["foo,,bar" ','] => ["foo" "" "bar" 3]
|
225
225
|
; ["/foo/bar/" '/'] => ["" "foo" "bar" "" 4]
|
226
226
|
strsplit:
|
227
|
-
push -3
|
228
|
-
|
227
|
+
push -3,1 store ; number of found substrings
|
228
|
+
^-2 ; stash delimiter to allow some stack juggling
|
229
229
|
_strsplit_loop:
|
230
|
-
dup dup
|
230
|
+
dup dup @-2
|
231
231
|
:strindex dup jn _strsplit_done ; done when index of delimiter is -1
|
232
232
|
push 0 swap :strslice
|
233
|
-
swap copy 1
|
233
|
+
swap copy 1 @-3
|
234
234
|
swap :strlen
|
235
235
|
swap push -3 swap push 1 add store ; update number of found
|
236
236
|
push 1 add push 128 swap :pow div ; shrink haystack
|
@@ -248,11 +248,10 @@ lines: push 10 :strsplit ret
|
|
248
248
|
; ["foo" "bar" "baz" 3 '--'] => ["foo--bar--baz"]
|
249
249
|
; ["foo" 1 "?!"] => ["foo"]
|
250
250
|
strjoinc:
|
251
|
-
dup :strlen pop ; get delimiter length into -1
|
252
|
-
|
253
|
-
map (push -2 load :strcat) ; add delimiter to all elements
|
251
|
+
dup :strlen pop ^-2 ; get delimiter length into -1
|
252
|
+
map (@-2 :strcat) ; add delimiter to all elements
|
254
253
|
swap push 128 copy 1 :strlen
|
255
|
-
|
254
|
+
@-2 :strlen
|
256
255
|
sub :pow mod swap ; remove delimiter from last and flow into strjoin
|
257
256
|
|
258
257
|
; concatenates the pseudo-array of strings A into string S
|
@@ -289,9 +288,8 @@ _strcountc_done: swap slide 2 ret
|
|
289
288
|
; ["eunoia" "aeiou"] => [5]
|
290
289
|
; ["why" "aeiou"] => [0]
|
291
290
|
strcount:
|
292
|
-
swap push
|
293
|
-
|
294
|
-
map (push -2 load swap :strcountc)
|
291
|
+
swap ^-2 :strunpack push 0 :to_a
|
292
|
+
map (@-2 swap :strcountc)
|
295
293
|
reduce (add) ret
|
296
294
|
|
297
295
|
; translates all characters in A to the corresponding characters in B
|
@@ -303,16 +301,13 @@ strcount:
|
|
303
301
|
; ["abcd" "abc" "xyz"] => ["xyzd"]
|
304
302
|
; ["foobar" "oba" "ele"] => ["feeler"]
|
305
303
|
; ["abcdcba" "abcd" "xyz|"] => ["xyz|zyx"]
|
306
|
-
strtrans:
|
307
|
-
|
308
|
-
push -2 swap store
|
309
|
-
dup :strlen push -1 swap store
|
310
|
-
:strunpack push -1 load
|
304
|
+
strtrans: ^-3 ^-2
|
305
|
+
dup :strlen ^-1 :strunpack @-1
|
311
306
|
map (:_strtrans) pop :strpack ret
|
312
307
|
_strtrans:
|
313
|
-
dup
|
308
|
+
dup @-2 swap :strindex
|
314
309
|
dup jn _strtrans_no
|
315
|
-
|
310
|
+
@-3 swap :charat
|
316
311
|
slide 1 ret
|
317
312
|
_strtrans_no: pop ret
|
318
313
|
|
@@ -346,8 +341,8 @@ _strsqueeze_skip: pop jump _strsqueeze_loop
|
|
346
341
|
_strsqueeze_done: pop :strpack :strrev ret
|
347
342
|
|
348
343
|
$_strdel(cmp) {
|
349
|
-
:strunpack
|
350
|
-
select (
|
344
|
+
:strunpack @-1 :strlen
|
345
|
+
select (@-2 swap :strindex `cmp`)
|
351
346
|
pop :strpack ret
|
352
347
|
}
|
353
348
|
|
@@ -390,13 +385,39 @@ strrotl: push 128 swap copy 2 :strlen mod :pow :divmod :strcat ret
|
|
390
385
|
; ["abcd" 1] => ["dabc"]
|
391
386
|
; ["abcd" 5] => ["dabc"]
|
392
387
|
; ["foodbar" 3] => ["barfood"]
|
393
|
-
strrotr: push
|
388
|
+
strrotr: push -1 mul :strrotl ret
|
389
|
+
|
390
|
+
; gets the characters of the string S onto the stack as a pseudo-array, but
|
391
|
+
; with a leading 0 on the assumption that it'll eventually be repacked
|
392
|
+
;
|
393
|
+
; ["abc"] => [0 99 98 97 3]
|
394
|
+
strtoa: dup :strlen pop :strunpack @-1 $++ ret
|
394
395
|
|
395
396
|
; frobnicates the string S by XORing all its bytes with 42
|
396
397
|
; [S] => [S']
|
397
398
|
;
|
398
399
|
; ["foobar"] => ["LEEHKX"]
|
399
400
|
; ["LEEHKX"] => ["foobar"]
|
400
|
-
memfrob:
|
401
|
-
|
402
|
-
|
401
|
+
memfrob: :strtoa map (push 42 :bxor) pop :strpack ret
|
402
|
+
|
403
|
+
; returns 1 if the string S begins with substring T, 0 otherwise
|
404
|
+
; [S T] => [0 | 1]
|
405
|
+
;
|
406
|
+
; ["foobar" "foo"] => [1]
|
407
|
+
; ["foobar" "boo"] => [0]
|
408
|
+
; ["abc123" "123"] => [0]
|
409
|
+
; [" foo" " "] = [1]
|
410
|
+
strbegins?:
|
411
|
+
dup :strlen copy 2 swap push 0 swap
|
412
|
+
:strslice :eq slide 1 ret
|
413
|
+
|
414
|
+
; returns 1 if the string S ends with substring T, 0 otherwise
|
415
|
+
; [S T] => [0 | 1]
|
416
|
+
;
|
417
|
+
; ["foobar" "bar"] => [1]
|
418
|
+
; ["foobar" "foo"] => [0]
|
419
|
+
; ["abc123" "abc"] => [0]
|
420
|
+
; ["foo " " "] = [1]
|
421
|
+
strends?:
|
422
|
+
:strrev dup :strlen copy 2 :strrev swap push 0 swap
|
423
|
+
:strslice :eq slide 1 ret
|
@@ -26,7 +26,7 @@ _range_down:
|
|
26
26
|
|
27
27
|
$range_loop(fn, cmp) {
|
28
28
|
`fn`:
|
29
|
-
dup copy 2 add
|
29
|
+
dup copy 2 add @-1 `cmp` jz _`fn`_done
|
30
30
|
copy 1 copy 1 add swap jump `fn`
|
31
31
|
_`fn`_done: pop ret
|
32
32
|
}
|
@@ -50,8 +50,10 @@ steprange: swap push -1 copy 1 store copy 2 sub
|
|
50
50
|
$range_loop(_steprange_loop, :lte)
|
51
51
|
$range_loop(_steprange_down_loop, :gte)
|
52
52
|
|
53
|
-
; prints the string at the top of the stack and halts execution
|
54
|
-
|
53
|
+
; prints the string at the top of the stack and halts execution after pushing
|
54
|
+
; something onto the stack to signal abnormal termination/unclean exit
|
55
|
+
; [...] => [... 1]
|
56
|
+
die!: :println push 1 exit
|
55
57
|
|
56
58
|
; for stoi and itos
|
57
59
|
alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
|
@@ -72,7 +74,7 @@ alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
|
|
72
74
|
; ["-123" 10] => [-123]
|
73
75
|
; ["-ff" 16] => [-255]
|
74
76
|
stoi: swap dup :_stoi_sign swap copy 1 :eq
|
75
|
-
push 2 mul $--
|
77
|
+
push 2 mul $-- ^-2 push 0
|
76
78
|
_stoi_loop: ; [b s a]
|
77
79
|
swap dup jz _stoi_done
|
78
80
|
swap copy 2 copy 2
|
@@ -83,8 +85,8 @@ _stoi_loop: ; [b s a]
|
|
83
85
|
mul add swap push 128 div swap
|
84
86
|
jump _stoi_loop
|
85
87
|
_stoi_sign: dup push 0 :charat push '-' :eq copy 1 :strlen :strslice ret
|
86
|
-
_stoi_invalid: pop pop slide 1 swap div
|
87
|
-
_stoi_done: swap slide 2
|
88
|
+
_stoi_invalid: pop pop slide 1 swap div @-2 mul ret
|
89
|
+
_stoi_done: swap slide 2 @-2 mul ret
|
88
90
|
|
89
91
|
; creature comforts
|
90
92
|
|
@@ -111,7 +113,7 @@ _itos_loop:
|
|
111
113
|
swap :strcat
|
112
114
|
swap copy 2 div
|
113
115
|
swap jump _itos_loop
|
114
|
-
_itos_done: swap slide 2 push 45
|
116
|
+
_itos_done: swap slide 2 push 45 @-2 mul swap :strcat ret
|
115
117
|
|
116
118
|
; creature comforts
|
117
119
|
|
@@ -129,12 +131,10 @@ to_hex: push 16 :itos ret
|
|
129
131
|
; [256 16] => [1 0 0 3]
|
130
132
|
digits:
|
131
133
|
copy 1 jz _digits_zero ; special case
|
132
|
-
push -1 swap
|
133
|
-
push -1 swap ; sentinel value
|
134
|
+
^-1 push -1 swap ; sentinel value
|
134
135
|
_digits_loop:
|
135
136
|
dup jz _digits_done
|
136
|
-
|
137
|
-
swap jump _digits_loop
|
137
|
+
@-1 :divmod swap jump _digits_loop
|
138
138
|
_digits_zero: dup div ret
|
139
139
|
_digits_done: push 1 sub :to_a ret
|
140
140
|
|
@@ -215,8 +215,8 @@ between?: copy 2 :gte swap copy 2 :lte mul slide 1 ret
|
|
215
215
|
; a value V to search for and a starting index I, and either returns the first
|
216
216
|
; key associated with that value or loops forever. Probably don't touch.
|
217
217
|
; [V I]
|
218
|
-
|
219
|
-
$++ dup load copy 2 :eq jz
|
218
|
+
heap-seeking_missile:
|
219
|
+
$++ dup load copy 2 :eq jz heap-seeking_missile
|
220
220
|
slide 1 ret
|
221
221
|
|
222
222
|
; converts the #RRGGBB (leading '#' optional) color string S to
|
@@ -246,3 +246,31 @@ rgb2hex:
|
|
246
246
|
copy 2 push 256 mul add add
|
247
247
|
slide 2 :to_hex push 6,48 :rjustc ret
|
248
248
|
_rgb2hex_invalid: push "(rgb2hex) invalid RGB" :die!
|
249
|
+
|
250
|
+
; stashes the array A in negative heap space starting at index I
|
251
|
+
; [A I] => []
|
252
|
+
aryheap:
|
253
|
+
$++ dup copy 2 store
|
254
|
+
swap times ($-- swap copy 1 swap store) pop ret
|
255
|
+
|
256
|
+
; restores the heaped array starting at index I
|
257
|
+
; [I] => [A]
|
258
|
+
heapary:
|
259
|
+
$++ dup load swap copy 1 sub swap
|
260
|
+
times (dup load swap $++) load ret
|
261
|
+
|
262
|
+
; swaps the elements in the heap at indices I and J
|
263
|
+
; ! TODO: make heap effects doctest-able
|
264
|
+
; [I J] => []
|
265
|
+
heapswap: dup load swap copy 2 load store store ret
|
266
|
+
|
267
|
+
; returns the number of nanoseconds N since the Unix epoch
|
268
|
+
; [] => [N]
|
269
|
+
time: push "date +%s%N" shell :to_i ret
|
270
|
+
|
271
|
+
$bench(insns) {
|
272
|
+
#insns :println
|
273
|
+
:time ^0 `insns` :time @0 sub
|
274
|
+
push 10,9 :pow :divmod swap onum
|
275
|
+
push '.' ochr :to_s push 9,48 :rjustc :println
|
276
|
+
}
|