spitewaste 0.2.0 → 0.2.01

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cd3201a4c8647de4fb29c8529c45fb379c2633cd660a4155e28720931202f94
4
- data.tar.gz: 3ba703110341a406d476aa8afa8d89c8a57b9d6e136f319956f28f7d311b16e7
3
+ metadata.gz: 9ce4494399350d9afb01a0c84aef1ce504da4a24349b33a45de83d6b6f4fa10b
4
+ data.tar.gz: 330b14a9eb241ff0ac8986956c8f790dd88ce87db4823e08ff19edfc7db0aee1
5
5
  SHA512:
6
- metadata.gz: d103c80be7025cca6d784127444702460394e4d0f137f124581212b6d92523b80c7e95e15642f9c0036b26545e821dcee206686818d0728dbe57c7b57a388ef0
7
- data.tar.gz: 39f473d86a4df1a125a6edae3ff1e29c5a069da2aa72862834097b7151bd11c4fdfe93f8119b60c7009468e4f5c2a05ef60a92d99a9b9198485f79c42411f33d
6
+ metadata.gz: 04d8acb9a1ed8e1617447407ee511b38b35d2f15a0ef2cc836690138f67f1e036712eb912d3c1dc7331e4d4d2d8694b4fad67be4a0fdefec55515144c9b4a9ce
7
+ data.tar.gz: 3161a39f4f7b364a5204d4174bbe7f1aa3ac43c530c86d6273b3fe61a04a93eb450688c882d9709f06a105bed35e37479310183cdb39d1509cf90049f2f0fcf7
data/Rakefile CHANGED
@@ -15,9 +15,8 @@ task :docs do |t|
15
15
 
16
16
  Dir.chdir('lib/spitewaste/libspw') do |d|
17
17
  Dir['*.spw'].sort.each do |path|
18
- next if path['random']
19
- lib = File.basename path, '.spw'
20
- docs[lib] = extract_docs path
18
+ mod = File.basename path, '.spw'
19
+ docs[mod] = extract_docs path
21
20
  end
22
21
 
23
22
  File.open('docs.json', ?w) { |f|
@@ -10,6 +10,12 @@ import util ; dec, die!, eq
10
10
 
11
11
  $amax = 1000
12
12
 
13
+ ; prints the array A to stdout in the form [e1,e2,...]
14
+ ; [A] => []
15
+ aryprint:
16
+ map (:to_s) push ',' :strjoinc
17
+ push '[' ochr :print push ']' ochr ret
18
+
13
19
  ; places an array of N elements E at the top of the stack
14
20
  ; [N E] => [A]
15
21
  ;
@@ -36,7 +42,23 @@ arysum: reduce (add) ret
36
42
  ; [7 50 10 2] => [7 50 10 2 50 10 2]
37
43
  arydup:
38
44
  dup $++ push -2 copy 1 store
39
- times (push -2 load $-- :ncopy) ret
45
+ times (@-2 $-- :ncopy) ret
46
+
47
+ ; returns 1 if the arrays A1 and A2 have the same length and
48
+ ; contain the same elements in the same order, 0 otherwise
49
+ ; [A1 A2] => [0 | 1]
50
+ ;
51
+ ; [1 2 3 3 1 2 3 3] => [1]
52
+ ; [1 2 3 3 3 2 1 3] => [0]
53
+ ; [1 2 2 1 2 3 3] => [0]
54
+ ; [1 2 3 3 1 2 2] => [0]
55
+ ; [7 10 2 7 10 2] => [1]
56
+ ; [6 1 9 1] => [0]
57
+ aryeq: dup $++ :roll copy 1 copy 1 :eq jz _aryeq_difflen ; length mismatch
58
+ ^-1 times (@-1 :ncopy :eq jz _aryeq_no)
59
+ push 1 @-1 :nslide ret
60
+ _aryeq_no: push 0 @-1 @-5 sub $-- :nslide ret ; ! -5 is magic from times()
61
+ _aryeq_difflen: add push 0 swap :nslide ret
40
62
 
41
63
  ; places the element E at the end of the array A, increasing its length by 1
42
64
  ; [A E] => [A']
@@ -99,15 +121,15 @@ arycat: dup $++ :roll add ret
99
121
  ;;;
100
122
 
101
123
  arypack:
102
- :arydup reduce (:max) $++ push -1 swap store
103
- $-- times (push -1 load mul add)
104
- push $amax mul push -1 load add
105
- push $amax mul push -10 load add ret
124
+ :arydup reduce (:max) $++ ^-1
125
+ $-- times (@-1 mul add)
126
+ push $amax mul @-1 add
127
+ push $amax mul @-10 add ret
106
128
 
107
129
  aryunpack:
108
- push $amax :divmod push -2 swap store
109
- push $amax :divmod push -1 swap store
110
- push -2 load times (push -1 load :divmod swap)
130
+ push $amax :divmod ^-2
131
+ push $amax :divmod ^-1
132
+ @-2 times (@-1 :divmod swap)
111
133
  push 2 sub load ret
112
134
 
113
135
  arylen: push $amax mod ret
@@ -161,8 +183,8 @@ _aryswap_noop: pop pop ret
161
183
  ; [6 8 -3 4 0 5] => [-3 8]
162
184
  ; [7 1] => [7 7]
163
185
  minmax:
164
- :arydup reduce (:max) push -1 swap store
165
- reduce (:min) push -1 load ret
186
+ :arydup reduce (:max) ^-1
187
+ reduce (:min) @-1 ret
166
188
 
167
189
  ;;; TODO: make sort not atrociously inefficient
168
190
 
@@ -170,11 +192,27 @@ sort: push -3 copy 1 store ; preserve length
170
192
  _sort_loop:
171
193
  :arydup reduce (:min)
172
194
  push -1 copy 1 store copy 1 $++ :bury ; stash minimum element
173
- :arydup push -1 load :aryindex
195
+ :arydup @-1 :aryindex
174
196
  copy 1 swap sub :dig $-- ; remove minimum element from array
175
197
  push 0 copy 1 sub jn _sort_loop
176
198
  push 3 sub load ret
177
199
 
200
+ ; returns 0 if any of the elements in array A is
201
+ ; strictly less than the one before it, 1 otherwise
202
+ ; [A] => [0 | 1]
203
+ ;
204
+ ; [1 2 3 3] => [1]
205
+ ; [3 2 1 3] => [0]
206
+ ; [1 1 2 2 4] => [1]
207
+ ; [1 2 3 4 3 5] => [0]
208
+ sorted?: $--
209
+ _sorted_loop:
210
+ copy 1 copy 3 sub jn _sorted_no
211
+ $-- dup jz _sorted_yes
212
+ slide 1 jump _sorted_loop
213
+ _sorted_no: $++ push 0 swap :nslide ret
214
+ _sorted_yes: push 1 slide 3 ret
215
+
178
216
  ; reverses the array A
179
217
  ; [A] => [A']
180
218
  ;
@@ -196,4 +234,4 @@ aryrep: push -1 swap push -3 copy 1 store store
196
234
  _aryrep_loop:
197
235
  push -1 dup :dec load jz _aryrep_done
198
236
  :arydup jump _aryrep_loop
199
- _aryrep_done: push -3 load $-- times (:arycat) ret
237
+ _aryrep_done: @-3 $-- times (:arycat) ret
@@ -12,8 +12,8 @@ $bitfun(op, bit, done) {
12
12
  push -2,1,-1,0 store store
13
13
  _`op`_loop: push -1
14
14
  copy 2 push 2 mod copy 2 push 2 mod `bit`
15
- push -1 load swap push -2 load dup push 2 mul
16
- push -2 swap store mul add store
15
+ @-1 swap @-2 dup push 2 mul
16
+ ^-2 mul add store
17
17
  push 2 div swap push 2 div
18
18
  dup copy 2 mul jz _`op`_done swap jump _`op`_loop
19
19
  _`op`_done: `done` ret
@@ -35,7 +35,7 @@ band: $bitfun(band, mul, mul $-- load)
35
35
  ; [61 77] => [125] [93 65] => [93]
36
36
  ; [14 87] => [95] [71 37] => [103]
37
37
  ; [7 19] => [23] [38 92] => [126]
38
- bor: $bitfun(bor, add $++ push 2 div, add push -2 load mul push -1 load add)
38
+ bor: $bitfun(bor, add $++ push 2 div, add @-2 mul @-1 add)
39
39
 
40
40
  ; returns the bitwise XOR of A and B
41
41
  ; [A B] => [A ^ B]
@@ -44,7 +44,7 @@ bor: $bitfun(bor, add $++ push 2 div, add push -2 load mul push -1 load add)
44
44
  ; [51 5] => [54] [97 77] => [44]
45
45
  ; [39 65] => [102] [12 26] => [22]
46
46
  ; [44 36] => [8] [6 21] => [19]
47
- bxor: $bitfun(bxor, :neq, add push -2 load mul push -1 load add)
47
+ bxor: $bitfun(bxor, :neq, add @-2 mul @-1 add)
48
48
 
49
49
  ; returns the infinite-precision bitwise NOT of N by inverting all of its bits
50
50
  ; [N] => [~N]
@@ -135,6 +135,95 @@
135
135
  ]
136
136
  ]
137
137
  },
138
+ "aryeq": {
139
+ "full": "returns 1 if the arrays A1 and A2 have the same length and\ncontain the same elements in the same order, 0 otherwise\n[A1 A2] => [0 | 1]\n\n[1 2 3 3 1 2 3 3] => [1]\n[1 2 3 3 3 2 1 3] => [0]\n[1 2 2 1 2 3 3] => [0]\n[1 2 3 3 1 2 2] => [0]\n[7 10 2 7 10 2] => [1]\n[6 1 9 1] => [0]\n",
140
+ "desc": "returns 1 if the arrays A1 and A2 have the same length and\ncontain the same elements in the same order, 0 otherwise",
141
+ "effect": "[A1 A2] => [0 | 1]",
142
+ "cases": [
143
+ [
144
+ [
145
+ 1,
146
+ 2,
147
+ 3,
148
+ 3,
149
+ 1,
150
+ 2,
151
+ 3,
152
+ 3
153
+ ],
154
+ [
155
+ 1
156
+ ]
157
+ ],
158
+ [
159
+ [
160
+ 1,
161
+ 2,
162
+ 3,
163
+ 3,
164
+ 3,
165
+ 2,
166
+ 1,
167
+ 3
168
+ ],
169
+ [
170
+ 0
171
+ ]
172
+ ],
173
+ [
174
+ [
175
+ 1,
176
+ 2,
177
+ 2,
178
+ 1,
179
+ 2,
180
+ 3,
181
+ 3
182
+ ],
183
+ [
184
+ 0
185
+ ]
186
+ ],
187
+ [
188
+ [
189
+ 1,
190
+ 2,
191
+ 3,
192
+ 3,
193
+ 1,
194
+ 2,
195
+ 2
196
+ ],
197
+ [
198
+ 0
199
+ ]
200
+ ],
201
+ [
202
+ [
203
+ 7,
204
+ 10,
205
+ 2,
206
+ 7,
207
+ 10,
208
+ 2
209
+ ],
210
+ [
211
+ 1
212
+ ]
213
+ ],
214
+ [
215
+ [
216
+ 6,
217
+ 1,
218
+ 9,
219
+ 1
220
+ ],
221
+ [
222
+ 0
223
+ ]
224
+ ]
225
+ ]
226
+ },
138
227
  "aryfill": {
139
228
  "full": "places an array of N elements E at the top of the stack\n[N E] => [A]\n\n[3 0] => [0 0 0 3]\n[5 1] => [1 1 1 1 1 5]\n[4 -2] => [-2 -2 -2 -2 4]\n",
140
229
  "desc": "places an array of N elements E at the top of the stack",
@@ -338,6 +427,14 @@
338
427
  ]
339
428
  ]
340
429
  },
430
+ "aryprint": {
431
+ "full": "prints the array A to stdout in the form [e1,e2,...]\n[A] => []\n",
432
+ "desc": "prints the array A to stdout in the form [e1,e2,...]",
433
+ "effect": "[A] => []",
434
+ "cases": [
435
+
436
+ ]
437
+ },
341
438
  "arypush": {
342
439
  "full": "places the element E at the end of the array A, increasing its length by 1\n[A E] => [A']\n\n[3 2 1 3 7] => [3 2 1 7 4]\n[4 2 2 0] => [4 2 0 3]\n[6 1 9] => [6 9 2]\n[7 5 8 2 4] => [7 5 8 4 3]\n",
343
440
  "desc": "places the element E at the end of the array A, increasing its length by 1",
@@ -794,6 +891,60 @@
794
891
  ]
795
892
  ]
796
893
  ]
894
+ },
895
+ "sorted?": {
896
+ "full": "returns 0 if any of the elements in array A is\nstrictly less than the one before it, 1 otherwise\n[A] => [0 | 1]\n\n[1 2 3 3] => [1]\n[3 2 1 3] => [0]\n[1 1 2 2 4] => [1]\n[1 2 3 4 3 5] => [0]\n",
897
+ "desc": "returns 0 if any of the elements in array A is\nstrictly less than the one before it, 1 otherwise",
898
+ "effect": "[A] => [0 | 1]",
899
+ "cases": [
900
+ [
901
+ [
902
+ 1,
903
+ 2,
904
+ 3,
905
+ 3
906
+ ],
907
+ [
908
+ 1
909
+ ]
910
+ ],
911
+ [
912
+ [
913
+ 3,
914
+ 2,
915
+ 1,
916
+ 3
917
+ ],
918
+ [
919
+ 0
920
+ ]
921
+ ],
922
+ [
923
+ [
924
+ 1,
925
+ 1,
926
+ 2,
927
+ 2,
928
+ 4
929
+ ],
930
+ [
931
+ 1
932
+ ]
933
+ ],
934
+ [
935
+ [
936
+ 1,
937
+ 2,
938
+ 3,
939
+ 4,
940
+ 3,
941
+ 5
942
+ ],
943
+ [
944
+ 0
945
+ ]
946
+ ]
947
+ ]
797
948
  }
798
949
  },
799
950
  "bits": {
@@ -1405,6 +1556,45 @@
1405
1556
  ]
1406
1557
  ]
1407
1558
  },
1559
+ "collatz_len": {
1560
+ "full": "returns the length L of the Collatz sequence for integer N\n[N] => [L]\n\n[1] => [1]\n[4] => [3]\n[7] => [17]\n[189] => [107]\n",
1561
+ "desc": "returns the length L of the Collatz sequence for integer N",
1562
+ "effect": "[N] => [L]",
1563
+ "cases": [
1564
+ [
1565
+ [
1566
+ 1
1567
+ ],
1568
+ [
1569
+ 1
1570
+ ]
1571
+ ],
1572
+ [
1573
+ [
1574
+ 4
1575
+ ],
1576
+ [
1577
+ 3
1578
+ ]
1579
+ ],
1580
+ [
1581
+ [
1582
+ 7
1583
+ ],
1584
+ [
1585
+ 17
1586
+ ]
1587
+ ],
1588
+ [
1589
+ [
1590
+ 189
1591
+ ],
1592
+ [
1593
+ 107
1594
+ ]
1595
+ ]
1596
+ ]
1597
+ },
1408
1598
  "isop": {
1409
1599
  "full": "returns the sum of the alphabetical characters in string S where A=1, B=2...\n[S] => [sum]\n\n[\"Math\"] => [42]\n[\"wizards\"] => [100]\n[\"AbCd\"] => [10]\n",
1410
1600
  "desc": "returns the sum of the alphabetical characters in string S where A=1, B=2...",
@@ -2373,6 +2563,56 @@
2373
2563
  ]
2374
2564
  }
2375
2565
  },
2566
+ "random": {
2567
+ "dice": {
2568
+ "full": "returns an array A of N random integers between 1 and D (inclusive)\n[N D] => [A]\n",
2569
+ "desc": "returns an array A of N random integers between 1 and D (inclusive)",
2570
+ "effect": "[N D] => [A]",
2571
+ "cases": [
2572
+
2573
+ ]
2574
+ },
2575
+ "rand": {
2576
+ "full": "returns the next number N in the linear congruential generator (better MINSTD)\n[] => [N]\n",
2577
+ "desc": "returns the next number N in the linear congruential generator (better MINSTD)",
2578
+ "effect": "[] => [N]",
2579
+ "cases": [
2580
+
2581
+ ]
2582
+ },
2583
+ "rand_range": {
2584
+ "full": "returns a random integer I between A and B (inclusive)\n[A B] => [I]\n",
2585
+ "desc": "returns a random integer I between A and B (inclusive)",
2586
+ "effect": "[A B] => [I]",
2587
+ "cases": [
2588
+
2589
+ ]
2590
+ },
2591
+ "shuffle": {
2592
+ "full": "shuffles the array A in-place using the modern Fisher-Yates algorithm\n[A] => [A']\n",
2593
+ "desc": "shuffles the array A in-place using the modern Fisher-Yates algorithm",
2594
+ "effect": "[A] => [A']",
2595
+ "cases": [
2596
+
2597
+ ]
2598
+ },
2599
+ "srand": {
2600
+ "full": "seeds the random number generator with integer S\n[S] => []\n",
2601
+ "desc": "seeds the random number generator with integer S",
2602
+ "effect": "[S] => []",
2603
+ "cases": [
2604
+
2605
+ ]
2606
+ },
2607
+ "strfry": {
2608
+ "full": "shuffles the characters of the string S, producing a random anagram\n[S] => [S']\n",
2609
+ "desc": "shuffles the characters of the string S, producing a random anagram",
2610
+ "effect": "[S] => [S']",
2611
+ "cases": [
2612
+
2613
+ ]
2614
+ }
2615
+ },
2376
2616
  "rational": {
2377
2617
  "from_r": {
2378
2618
  "full": "decomposes the rational number R into its numerator N and denominator D\n[R] => [N D]\n\n[R(22,7)] => [22 7]\n[R(-3,4)] => [-3 4]\n[R(3,-4)] => [-3 4]\n[R(4,8)] => [4 8] ; no implicit simplification\n",
@@ -4686,6 +4926,25 @@
4686
4926
  ]
4687
4927
  ]
4688
4928
  },
4929
+ "strtoa": {
4930
+ "full": "gets the characters of the string S onto the stack as a pseudo-array, but\nwith a leading 0 on the assumption that it'll eventually be repacked\n\n[\"abc\"] => [0 99 98 97 3]\n",
4931
+ "desc": "gets the characters of the string S onto the stack as a pseudo-array, but",
4932
+ "effect": "with a leading 0 on the assumption that it'll eventually be repacked",
4933
+ "cases": [
4934
+ [
4935
+ [
4936
+ 1634657
4937
+ ],
4938
+ [
4939
+ 0,
4940
+ 99,
4941
+ 98,
4942
+ 97,
4943
+ 3
4944
+ ]
4945
+ ]
4946
+ ]
4947
+ },
4689
4948
  "strtrans": {
4690
4949
  "full": "translates all characters in A to the corresponding characters in B\nin string S, ; similar to the `tr` utility in Unix. A and B must be\nof the same length. TODO: make this smarter (ranges, length mismatch)\n[S A B] => [S']\n\n[\"abcd\" \"abc\" \"xyz\"] => [\"xyzd\"]\n[\"foobar\" \"oba\" \"ele\"] => [\"feeler\"]\n[\"abcdcba\" \"abcd\" \"xyz|\"] => [\"xyz|zyx\"]\n",
4691
4950
  "desc": "translates all characters in A to the corresponding characters in B\nin string S, ; similar to the `tr` utility in Unix. A and B must be\nof the same length. TODO: make this smarter (ranges, length mismatch)",
@@ -4852,9 +5111,9 @@
4852
5111
  ]
4853
5112
  },
4854
5113
  "die!": {
4855
- "full": "prints the string at the top of the stack and halts execution\n",
4856
- "desc": "",
4857
- "effect": "prints the string at the top of the stack and halts execution",
5114
+ "full": "prints the string at the top of the stack and halts execution after pushing\nsomething onto the stack to signal abnormal termination/unclean exit\n[...] => [... 1]\n",
5115
+ "desc": "prints the string at the top of the stack and halts execution after pushing\nsomething onto the stack to signal abnormal termination/unclean exit",
5116
+ "effect": "[...] => [... 1]",
4858
5117
  "cases": [
4859
5118
 
4860
5119
  ]
@@ -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
- dup push 2 mod
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
- push -2 load swap :strcat push -2 swap store
79
+ @-2 swap :strcat ^-2
63
80
  }
64
81
 
65
82
  ; converts the number N to a string of roman numerals R
@@ -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: push -1 load slide 2 ret
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 push -1 swap store ; preserve N because array operations
138
+ dup ^-1 ; preserve N because array operations
142
139
  :isqrt push 1 swap :range dup ; 1..isqrt(N)
143
- reject (push -1 load swap mod) :arydup ; get first half of divisors
144
- map (push -1 load swap div) :arycat ; map first half to second half
145
- push -1 load copy 2 dup mul sub jz _divisors_square ret
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,34 @@
1
- import math ; pow
1
+ import math ; pow
2
+ import string ; strtoa
2
3
 
3
- srand: push $seed swap store ret
4
+ ; seeds the random number generator with integer S
5
+ ; [S] => []
6
+ srand: ^$seed ret
4
7
 
8
+ ; returns the next number N in the linear congruential generator (better MINSTD)
9
+ ; [] => [N]
5
10
  rand:
6
11
  push $seed dup dup load
7
- push 3,13,10244807 mul mul mul
8
- push 2,32 :pow mod
12
+ push 48271 mul
13
+ push 2,31 :pow $-- mod
9
14
  store load ret
10
15
 
11
- rand_range: ; [a b]
12
- copy 1 sub :rand swap mod add ret
16
+ ; returns a random integer I between A and B (inclusive)
17
+ ; [A B] => [I]
18
+ rand_range:
19
+ $++ copy 1 sub :rand swap mod add ret
13
20
 
14
- dice:
15
- push -2 swap store push 1 :aryfill
16
- map (push -2 load :rand_range) ret
21
+ ; returns an array A of N random integers between 1 and D (inclusive)
22
+ ; [N D] => [A]
23
+ dice: ^-2 dup ^-1 times (push 1 @-2 :rand_range) @-1 ret
24
+
25
+ ; shuffles the array A in-place using the modern Fisher-Yates algorithm
26
+ ; [A] => [A']
27
+ shuffle: dup $-- ^-3
28
+ _shuffle_loop:
29
+ push 0 @-3 :rand_range @-3 :aryswap
30
+ push -3 :dec @-3 push -1 mul jn _shuffle_loop ret
31
+
32
+ ; shuffles the characters of the string S, producing a random anagram
33
+ ; [S] => [S']
34
+ strfry: :strtoa :shuffle pop :strpack 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: push -2 swap store :from_r :divmod push 0 swap
190
+ to_f: ^-2 :from_r :divmod push 0 swap
191
191
  _to_f_loop:
192
- push -1 load :divmod swap
192
+ @-1 :divmod swap
193
193
  copy 2 push 10 mul add
194
194
  swap push 10 mul
195
195
  push 2 :dig
@@ -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: ; [n]
19
+ _roll_keep:
20
20
  dup jz _roll_remove
21
21
  push -10 :dec
22
- swap push -10 load swap store
22
+ swap @-10 swap store
23
23
  push 1 sub jump _roll_keep
24
- _roll_remove:
25
- push 10 sub load
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 push -9 swap store ; preserve element to bury
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 push -10 load swap store
42
+ swap @-10 swap store
45
43
  push 1 sub jump _bury_keep
46
44
  _bury_restore:
47
45
  push 9 sub load
48
- push -10 load :_roll_restore pop ret
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: push -1 swap store push -10 dup store
64
+ to_a: ^-1 push -10 dup store
67
65
  _to_a_loop:
68
- dup push -1 load sub jz _to_a_sentinel
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 push -10 load sub jz _to_a_done
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 push -1 swap store :npop push -1 load ret
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
@@ -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 push 1 store ; number of found substrings
228
- push -2 swap store ; stash delimiter to allow some stack juggling
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 push -2 load
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 push -3 load
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
- push -2 swap store
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
- push -2 load :strlen
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 -2 swap store
293
- :strunpack push 0 :to_a
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
- push -3 swap store
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 push -2 load swap :strindex
308
+ dup @-2 swap :strindex
314
309
  dup jn _strtrans_no
315
- push -3 load swap :charat
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 push -1 load :strlen
350
- select (push -2 load swap :strindex `cmp`)
344
+ :strunpack @-1 :strlen
345
+ select (@-2 swap :strindex `cmp`)
351
346
  pop :strpack ret
352
347
  }
353
348
 
@@ -390,13 +385,17 @@ 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 0 swap sub :strrotl ret
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
- dup :strlen pop :strunpack push -1 load $++
402
- map (push 42 :bxor) pop :strpack ret
401
+ memfrob: :strtoa map (push 42 :bxor) pop :strpack ret
@@ -8,3 +8,6 @@ assert_eq:
8
8
  push ", got " :print onum
9
9
  push " for test " swap :strcat :die!
10
10
  _assert_eq_yes: pop pop pop ret
11
+
12
+ assert_nz: jz _assert_nz_no pop ret
13
+ _assert_nz_no: push "expected nonzero for test " swap :strcat :die!
@@ -26,7 +26,7 @@ _range_down:
26
26
 
27
27
  $range_loop(fn, cmp) {
28
28
  `fn`:
29
- dup copy 2 add push -1 load `cmp` jz _`fn`_done
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
- die!: :println exit
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 $-- push -2 swap store push 0
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 push -2 load mul ret
87
- _stoi_done: swap slide 2 push -2 load mul ret
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,-2 load mul swap :strcat ret
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 store
133
- push -1 swap ; sentinel value
134
+ ^-1 push -1 swap ; sentinel value
134
135
  _digits_loop:
135
136
  dup jz _digits_done
136
- push -1 load :divmod
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
 
@@ -118,9 +118,9 @@ module Spitewaste
118
118
  # quick push (`push 1,2,3` desugars to individual pushes)
119
119
  @src.gsub!(/push \S+/) { |m| m.split(?,) * ' push ' }
120
120
  # quick store (`^2` = `push 2 swap store`)
121
- @src.gsub!(/\^(-?\d+)/, 'push \1 swap store')
121
+ @src.gsub!(/\^(\S+)/, 'push \1 swap store')
122
122
  # quick load (`@2` = `push 2 load`)
123
- @src.gsub!(/@(-?\d+)/, 'push \1 load')
123
+ @src.gsub!(/@(\S+)/, 'push \1 load')
124
124
  end
125
125
 
126
126
  def gensym
@@ -141,10 +141,10 @@ module Spitewaste
141
141
  parse = -> s { s.split(?,).map &:strip }
142
142
 
143
143
  # Macro "functions" get handled first.
144
- @src.gsub!(/(\$\S+?)\(([^)]+)\)\s*{(.+?)}/m) {
144
+ @src.gsub!(/(\$\S+?)\(([^)]*)\)\s*{(.+?)}/m) {
145
145
  @macros[$1] ||= [$2, $3]; ''
146
146
  }
147
- @src.gsub!(/(\$\S+?)\(([^)]+)\)/) {
147
+ @src.gsub!(/(\$\S+?)\(([^)]*)\)/) {
148
148
  params, body = @macros[$1]
149
149
  raise "no macro function '#$1'" unless body
150
150
  map = parse[params].zip(parse[$2]).to_h
@@ -1,3 +1,3 @@
1
1
  module Spitewaste
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.01'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spitewaste
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.01
5
5
  platform: ruby
6
6
  authors:
7
7
  - Collided Scope (collidedscope)