spitewaste 0.1.003 → 0.1.004
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 +27 -10
- data/lib/spitewaste.rb +1 -1
- data/lib/spitewaste/cli/exec.rb +10 -13
- data/lib/spitewaste/libspw/array.spw +5 -1
- data/lib/spitewaste/libspw/bits.spw +4 -2
- data/lib/spitewaste/libspw/case.spw +3 -1
- data/lib/spitewaste/libspw/fun.spw +5 -1
- data/lib/spitewaste/libspw/io.spw +2 -0
- data/lib/spitewaste/libspw/math.spw +83 -45
- data/lib/spitewaste/libspw/prime.spw +3 -2
- data/lib/spitewaste/libspw/random.spw +2 -0
- data/lib/spitewaste/libspw/rational.spw +153 -0
- data/lib/spitewaste/libspw/stack.spw +54 -30
- data/lib/spitewaste/libspw/string.spw +228 -92
- data/lib/spitewaste/libspw/test.spw +3 -0
- data/lib/spitewaste/libspw/util.spw +92 -39
- data/lib/spitewaste/parsers/spitewaste.rb +7 -15
- data/lib/spitewaste/version.rb +1 -1
- metadata +3 -2
@@ -1,77 +1,101 @@
|
|
1
|
+
;;; Heavy-handed stack manipulation
|
2
|
+
|
3
|
+
; These subrtouines do some pretty intricate stack-based operations, relying
|
4
|
+
; heavily on clobbering the heap in order to maintain their "bookkeeping".
|
5
|
+
; Many of them use heap addresses -10 and lower, unbounded, so they're only
|
6
|
+
; meant to be used in a pinch or when there just isn't much of an alternative.
|
7
|
+
|
8
|
+
import util ; dec
|
9
|
+
|
10
|
+
; rolls the Ith element (counting from 0) to the top of the stack,
|
11
|
+
; shifting the elements above it down by 1.
|
12
|
+
; [I]
|
13
|
+
;
|
14
|
+
; [1 2 3 2] => [2 3 1]
|
15
|
+
; [1 2 3 4 1] => [1 2 4 3]
|
16
|
+
; [1 2 3 4 5 3] => [1 3 4 5 2]
|
1
17
|
roll:
|
2
18
|
push -10 dup store ; current heap index kept at -10
|
3
|
-
|
4
19
|
_roll_keep: ; [n]
|
5
20
|
dup jz _roll_remove
|
6
21
|
push -10 :dec
|
7
22
|
swap push -10 load swap store
|
8
23
|
push 1 sub jump _roll_keep
|
9
|
-
|
10
24
|
_roll_remove:
|
11
25
|
push 10 sub load
|
12
26
|
swap push -10 swap store
|
13
|
-
|
14
27
|
_roll_restore: ; i
|
15
28
|
dup load swap push 1 add
|
16
29
|
dup push 10 add jz _roll_done
|
17
30
|
jump _roll_restore
|
18
|
-
|
19
31
|
_roll_done: load ret
|
20
32
|
|
21
|
-
|
22
|
-
|
33
|
+
; "buries" X in the stack at index I, counting from 0
|
34
|
+
; [X I]
|
35
|
+
;
|
36
|
+
; [1 2 3 4 2] => [1 4 2 3] ; 2nd element of stack now 4
|
37
|
+
; [1 2 3 4 5 8 5] => [8 1 2 3 4 5]
|
23
38
|
bury:
|
24
39
|
push -10 dup store ; current heap index kept at -10
|
25
40
|
swap push -9 swap store ; preserve element to bury
|
26
|
-
|
27
41
|
_bury_keep: ; [n]
|
28
42
|
dup jz _bury_restore
|
29
43
|
push -10 :dec
|
30
44
|
swap push -10 load swap store
|
31
45
|
push 1 sub jump _bury_keep
|
32
|
-
|
33
46
|
_bury_restore:
|
34
47
|
push 9 sub load
|
35
48
|
push -10 load :_roll_restore pop ret
|
36
49
|
|
37
|
-
;;;
|
38
50
|
|
51
|
+
; "digs" out the Ith element of the stack and discards it
|
52
|
+
; [I]
|
53
|
+
;
|
54
|
+
; [1 2 3 4 5 2] => [1 2 4 5]
|
55
|
+
; [1 2 3 4 5 4] => [2 3 4 5]
|
39
56
|
dig: :roll pop ret
|
40
57
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
58
|
+
; pops elements off the stack until it hits the specified sentinel value S,
|
59
|
+
; pushing them to the resulting pseudo-array. It's often more convenient to
|
60
|
+
; build up a collection in reverse order, and we often don't know in advance
|
61
|
+
; how many elements we'll meet, but we do know to stop at the sentinel.
|
62
|
+
; [S En ... E1 S] => [E1 ... En n]
|
63
|
+
;
|
64
|
+
; [-1 9 8 7 -1] => [7 8 9 3]
|
65
|
+
; [0 'c' 'b' 'a' 0] => ['a' 'b' 'c' 3]
|
66
|
+
to_a: push -1 swap store push -10 dup store
|
47
67
|
_to_a_loop:
|
48
68
|
dup push -1 load sub jz _to_a_sentinel
|
49
69
|
push -10 dup :dec load
|
50
70
|
swap store jump _to_a_loop
|
51
|
-
|
52
71
|
_to_a_sentinel: pop push -10
|
53
|
-
|
54
72
|
_to_a_restore:
|
55
73
|
dup push -10 load sub jz _to_a_done
|
56
74
|
push 1 sub dup load swap
|
57
75
|
jump _to_a_restore
|
58
|
-
|
59
76
|
_to_a_done: push -10 swap sub ret
|
60
77
|
|
61
|
-
|
62
|
-
|
63
|
-
;
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
78
|
+
; pops N elements off the top of the stack
|
79
|
+
; [N]
|
80
|
+
;
|
81
|
+
; [1 2 3 4 5 3] => [1 2]
|
82
|
+
; [1 2 3 4 0] => [1 2 3 4]
|
83
|
+
npop: dup jz _npop_done swap pop push 1 sub jump npop
|
68
84
|
_npop_done: pop ret
|
69
85
|
|
70
|
-
;
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
86
|
+
; slides N elements off the stack, as if the `slide` operator took an argument
|
87
|
+
; [N]
|
88
|
+
;
|
89
|
+
; [1 2 3 4 5 2] => [1 2 5]
|
90
|
+
; [1 2 3 4 1] => [1 2 4]
|
91
|
+
nslide: swap push -1 swap store :npop push -1 load ret
|
92
|
+
|
93
|
+
; copies the Nth element to the top of the stack; this does exactly what
|
94
|
+
; a `copy N` instruction would do, but we don't always know N in advance
|
95
|
+
; [N]
|
96
|
+
;
|
97
|
+
; [1 2 3 4 0] => [1 2 3 4 4] ; `copy 0` is just dup
|
98
|
+
; [1 2 3 4 3] => [1 2 3 4 1]
|
75
99
|
ncopy: push -10 dup store
|
76
100
|
_ncopy_loop:
|
77
101
|
dup jz _ncopy_save swap
|
@@ -1,104 +1,134 @@
|
|
1
|
-
import math
|
2
1
|
import case
|
2
|
+
import math ; divmod, ilog, max, pow
|
3
|
+
import stack ; to_a
|
4
|
+
import util ; gt, lt, neq, range
|
3
5
|
|
4
6
|
;;; String packing and unpacking
|
5
7
|
|
6
8
|
; convert a 0-terminated string on the stack to a single base-128 integer
|
7
|
-
|
9
|
+
; [0 ... c b a] => ["abc..."]
|
10
|
+
;
|
11
|
+
; [0 99 98 97] => ["abc"]
|
12
|
+
; [0 99 98 97] => [1634657]
|
13
|
+
; [0] => [0]
|
14
|
+
strpack: push 0 ; accumulator
|
8
15
|
_strpack_loop:
|
9
16
|
swap dup jz _strpack_done
|
10
|
-
copy 1 push 128 mul add
|
11
|
-
slide 1
|
17
|
+
copy 1 push 128 mul add slide 1
|
12
18
|
jump _strpack_loop
|
13
|
-
|
14
19
|
_strpack_done: pop :strrev ret
|
15
20
|
|
16
21
|
; convert a single base-128 integer to a 0-terminated string on the stack
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
; ["abc..."] => [0 ... c b a]
|
23
|
+
;
|
24
|
+
; ["abc"] => [0 99 98 97]
|
25
|
+
; [1634657] => [0 99 98 97]
|
26
|
+
; [0] => [0]
|
27
|
+
strunpack: :strrev push 0 swap ; terminator
|
20
28
|
_strunpack_loop:
|
21
29
|
dup jz _strunpack_done
|
22
30
|
dup push 128 mod swap push 128 div
|
23
31
|
jump _strunpack_loop
|
24
|
-
|
25
32
|
_strunpack_done: pop ret
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
;
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
; returns the length of a packed string, which is just the
|
35
|
+
; value itself log-128, +1 if the integer logarithm isn't exact.
|
36
|
+
; [S] => [len(S)]
|
37
|
+
;
|
38
|
+
; [""] => [0]
|
39
|
+
; ["abc"] => [3]
|
40
|
+
; ["foobar"] => [6]
|
41
|
+
strlen: dup push 128 :ilog swap push 128 mod push 0 :neq add ret
|
33
42
|
|
34
43
|
; takes two packed strings and returns their concatenation (as a packed string)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
44
|
+
; [S T] => [S+T]
|
45
|
+
;
|
46
|
+
; ["foo" ""] => ["foo"]
|
47
|
+
; ["" "foo"] => ["foo"]
|
48
|
+
; ["foo" "bar"] => ["foobar"]
|
49
|
+
strcat: push 128 copy 2 :strlen :pow mul add ret
|
50
|
+
|
51
|
+
; reverses a packed string "in-place"
|
52
|
+
; [S] => [reverse(S)]
|
53
|
+
;
|
54
|
+
; ["foo"] => ["oof"]
|
55
|
+
; ["bark"] => ["krab"]
|
56
|
+
; ["ab"] => ["ba"] ['a'] => ['a'] [""] => [""]
|
57
|
+
strrev: push 0 swap
|
58
|
+
_strrev_loop:
|
45
59
|
dup jz _strrev_done
|
46
|
-
swap push 128 mul
|
60
|
+
swap push 128 mul
|
47
61
|
copy 1 push 128 mod add
|
48
62
|
swap push 128 div
|
49
63
|
jump _strrev_loop
|
50
|
-
|
51
64
|
_strrev_done: pop ret
|
52
65
|
|
53
|
-
|
54
|
-
|
55
|
-
; takes a packed string, a start index, and a length and returns the
|
66
|
+
; takes a packed string S, a start index I, and a length L and returns the
|
56
67
|
; corresponding substring (simply by doing division with powers of 128; neat)
|
68
|
+
; [S I L] => [substring]
|
69
|
+
;
|
70
|
+
; ["foobar" 0 6] => ["foobar"]
|
71
|
+
; ["foobar" 1 4] => ["ooba"]
|
72
|
+
; ["foobar" 1 10] => ["oobar"]
|
73
|
+
; ["foobar" 5 1] => ['r']
|
74
|
+
; ["foobar" 6 0] => [""]
|
57
75
|
strslice:
|
58
76
|
swap push 128 swap :pow
|
59
77
|
copy 2 swap div
|
60
78
|
swap push 128 swap :pow
|
61
79
|
mod slide 1 ret
|
62
80
|
|
63
|
-
|
64
|
-
|
65
|
-
;
|
81
|
+
; returns the index of substring T in string S (or -1 if not found)
|
82
|
+
; [S T] => [index]
|
83
|
+
;
|
84
|
+
; ["foobar" 'o'] => [1]
|
85
|
+
; ["foobar" "ob"] => [2]
|
86
|
+
; ["foobar" ""] => [0]
|
87
|
+
; ["foobar" "bar"] => [3]
|
88
|
+
; ["foobar" "bark"] => [-1]
|
66
89
|
strindex: swap push 0
|
67
|
-
|
68
90
|
_strindex_loop: ; [t s i]
|
69
91
|
copy 1 copy 3 :strlen push 0 swap :strslice
|
70
92
|
copy 3 sub jz _strindex_found
|
71
|
-
push 1 add
|
72
|
-
swap push 128 div dup jz _strindex_no
|
93
|
+
push 1 add swap push 128 div dup jz _strindex_no
|
73
94
|
swap jump _strindex_loop
|
74
|
-
|
75
95
|
_strindex_no: push -1 slide 3 ret
|
76
|
-
|
77
96
|
_strindex_found: slide 2 ret
|
78
97
|
|
79
|
-
|
80
|
-
|
98
|
+
; returns the character at index I in string S
|
99
|
+
; [S I] => [char]
|
100
|
+
;
|
101
|
+
; ["foobar" 1] => ['o']
|
102
|
+
; ["foobar" 3] => ['b']
|
103
|
+
; ["foobar" 5] => ['r']
|
104
|
+
; ["foobar" 6] => [""]
|
81
105
|
charat: push 1 :strslice ret
|
82
106
|
|
83
|
-
|
84
|
-
|
85
|
-
;
|
107
|
+
; returns 1 if the character at the top of the stack is
|
108
|
+
; alphabetical (ASCII 65-90 or 97-122), 0 otherwise
|
109
|
+
; [C] => [0/1]
|
110
|
+
;
|
111
|
+
; ['@'] => [0] ['a'] => [1]
|
112
|
+
; ['z'] => [1] ['['] => [0]
|
113
|
+
; ['`'] => [0] ['A'] => [1]
|
114
|
+
; ['Z'] => [1] ['{'] => [0]
|
86
115
|
isalpha:
|
87
116
|
dup push 123 :lt jz _isalpha_no
|
88
117
|
dup push 64 :gt jz _isalpha_no
|
89
118
|
push 32 mod $-- push 32 mod push 26 :lt ret
|
90
119
|
_isalpha_no: dup sub ret
|
91
120
|
|
92
|
-
|
93
|
-
|
94
|
-
;
|
121
|
+
; returns string S replicated N times
|
122
|
+
; [S N] => [S+S+...]
|
123
|
+
;
|
124
|
+
; ["abc" 1] => ["abc"]
|
125
|
+
; ["abc" 2] => ["abcabc"]
|
126
|
+
; ["abc" 0] => [""]
|
95
127
|
strrep: push 0 swap
|
96
|
-
_strrep_loop:
|
128
|
+
_strrep_loop:
|
97
129
|
dup jz _strrep_done
|
98
130
|
swap copy 2 :strcat
|
99
|
-
swap push 1 sub
|
100
|
-
jump _strrep_loop
|
101
|
-
|
131
|
+
swap push 1 sub jump _strrep_loop
|
102
132
|
_strrep_done: swap slide 2 ret
|
103
133
|
|
104
134
|
;;; String alignment
|
@@ -107,19 +137,48 @@ _strrep_done: swap slide 2 ret
|
|
107
137
|
; we swap before calling strcat.
|
108
138
|
_justc: swap copy 2 :strlen sub push 0 :max :strrep ret
|
109
139
|
|
110
|
-
;
|
140
|
+
; left-justifies string S to width W with character C
|
141
|
+
; [S W C] => [S, left-justified]
|
142
|
+
;
|
143
|
+
; ["foo" 5 'x'] => ["fooxx"]
|
144
|
+
; ["foobar" 4 'x'] => ["foobar"]
|
145
|
+
; ["" 3 'x'] => ["xxx"]
|
111
146
|
ljustc: :_justc :strcat ret
|
112
147
|
|
113
|
-
;
|
148
|
+
; left-justifies string S to width W with spaces
|
149
|
+
; [S W] => [S, left-justified]
|
150
|
+
;
|
151
|
+
; ["foo" 5] => ["foo "]
|
152
|
+
; ["foobar" 4] => ["foobar"]
|
153
|
+
; ["" 3] => [528416]
|
114
154
|
ljust: push ' ' :ljustc ret
|
115
155
|
|
116
|
-
;
|
156
|
+
; right-justifies string S to width W with character C
|
157
|
+
; [S W C] => [S, right-justified]
|
158
|
+
;
|
159
|
+
; ["foo" 5 'x'] => ["xxfoo"]
|
160
|
+
; ["foobar" 4 'x'] => ["foobar"]
|
161
|
+
; ["" 3 'x'] => ["xxx"]
|
117
162
|
rjustc: :_justc swap :strcat ret
|
118
163
|
|
119
|
-
;
|
164
|
+
; right-justifies string S to width W with spaces
|
165
|
+
; [S W C] => [S, right-justified]
|
166
|
+
;
|
167
|
+
; ["foo" 5] => [" foo"]
|
168
|
+
; ["foobar" 4] => ["foobar"]
|
169
|
+
; ["" 3] => [528416]
|
120
170
|
rjust: push ' ' :rjustc ret
|
121
171
|
|
122
|
-
;
|
172
|
+
; centers string S to width W with character C, favoring left alignment when
|
173
|
+
; there's a parity mismatch (even-length string to odd width or vice versa)
|
174
|
+
; ! TODO: This seems unnecessarily intricate, but perhaps just its nature.
|
175
|
+
; [S W C] => [S, centered]
|
176
|
+
;
|
177
|
+
; ["abc" 7 'x'] => ["xxabcxx"]
|
178
|
+
; ["abc" 6 'x'] => ["xabcxx"]
|
179
|
+
; ["abcd" 6 'o'] => ["oabcdo"]
|
180
|
+
; ["abcd" 7 'o'] => ["oabcdoo"]
|
181
|
+
; ["abcd" 3 '!'] => ["abcd"]
|
123
182
|
centerc:
|
124
183
|
swap dup copy 3 :strlen sub
|
125
184
|
push 0 :max push 2 div
|
@@ -130,22 +189,42 @@ centerc:
|
|
130
189
|
copy 2 swap :strrep :strcat
|
131
190
|
slide 2 ret
|
132
191
|
|
133
|
-
;
|
192
|
+
; centers string S to width W with spaces
|
193
|
+
; [S W] => [S, centered]
|
194
|
+
;
|
195
|
+
; ["abc" 7] => [" abc "]
|
196
|
+
; ["abc" 6] => [" abc "]
|
197
|
+
; ["abcd" 6] => [" abcd "]
|
198
|
+
; ["abcd" 7] => [" abcd "]
|
199
|
+
; ["abcd" 3] => ["abcd"]
|
134
200
|
center: push ' ' :centerc ret
|
135
201
|
|
136
202
|
;;;
|
137
203
|
|
138
|
-
;
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
;
|
144
|
-
; !
|
145
|
-
|
204
|
+
; removes the last character of a string
|
205
|
+
; [S] => [S.chop]
|
206
|
+
;
|
207
|
+
; ["foobar"] => ["fooba"]
|
208
|
+
; ["abc"] => ["ab"]
|
209
|
+
; ["a"] => [""]
|
210
|
+
; ! [""] => ERROR TODO: Should just be a no-op.
|
211
|
+
strchop: dup :strlen push 1 sub push 0 swap :strslice ret
|
212
|
+
|
213
|
+
; splits string S on delimiting character C, leaving the resultant substrings
|
214
|
+
; on the stack as a pseudo-array (length at top of stack)
|
215
|
+
; ! TODO: permit string delimiter
|
216
|
+
; ! clobbers heap addresses -1 (strlen), -2, and -3
|
217
|
+
; [S C] => [A]
|
218
|
+
;
|
219
|
+
; ["fooxbar" 'x'] => ["foo" "bar" 2]
|
220
|
+
; ["foobar" 'x'] => ["foobar" 1]
|
221
|
+
; ["foo|bar|baz" '|'] => ["foo" "bar" "baz" 3]
|
222
|
+
; ["foo,,bar" ','] => ["foo" "" "bar" 3]
|
223
|
+
; ["/foo/bar/" '/'] => ["" "foo" "bar" "" 4]
|
224
|
+
strsplit:
|
146
225
|
push -3 push 1 store ; number of found substrings
|
147
226
|
push -2 swap store ; stash delimiter to allow some stack juggling
|
148
|
-
_strsplit_loop:
|
227
|
+
_strsplit_loop:
|
149
228
|
dup dup push -2 load
|
150
229
|
:strindex dup jn _strsplit_done ; done when index of delimiter is -1
|
151
230
|
push 0 swap :strslice
|
@@ -154,80 +233,137 @@ _strsplit_loop: ; [s]
|
|
154
233
|
swap push -3 swap push 1 add store ; update number of found
|
155
234
|
push 1 add push 128 swap :pow div ; shrink haystack
|
156
235
|
jump _strsplit_loop
|
157
|
-
|
158
236
|
_strsplit_done: push 2 sub slide 1 load ret
|
159
237
|
|
160
|
-
|
238
|
+
; splits the string S on newlines
|
239
|
+
lines: push 10 :strsplit ret
|
161
240
|
|
162
|
-
;
|
163
|
-
; ! clobbers heap address -2 (strlen uses -1)
|
241
|
+
; joins the pseudo-array of strings A into string S with delimiter string D
|
242
|
+
; ! clobbers heap address -2 (and strlen uses -1)
|
243
|
+
; [A D] => [S]
|
244
|
+
;
|
245
|
+
; ["foo" "bar" 2 'x'] => ["fooxbar"]
|
246
|
+
; ["foo" "bar" "baz" 3 '--'] => ["foo--bar--baz"]
|
247
|
+
; ["foo" 1 "?!"] => ["foo"]
|
164
248
|
strjoinc:
|
165
249
|
dup :strlen pop ; get delimiter length into -1
|
166
250
|
push -2 swap store
|
167
251
|
map (push -2 load :strcat) ; add delimiter to all elements
|
168
252
|
swap push 128 copy 1 :strlen
|
169
253
|
push -2 load :strlen
|
170
|
-
sub :pow mod swap ; remove delimiter from last and flow
|
254
|
+
sub :pow mod swap ; remove delimiter from last and flow into strjoin
|
255
|
+
|
256
|
+
; concatenates the pseudo-array of strings A into string S
|
257
|
+
; [A] => [S]
|
258
|
+
;
|
259
|
+
; ["foo" 1] => ["foo"]
|
260
|
+
; ["foo" "bar" 2] => ["foobar"]
|
261
|
+
; ["foo" 'x' "bar" 'x' "baz" 5] => ["fooxbarxbaz"]
|
171
262
|
strjoin: reduce (:strcat) ret
|
172
263
|
|
173
|
-
|
174
|
-
|
175
|
-
;
|
264
|
+
; returns the number of ocurrences of character C in string S
|
265
|
+
; [S C] => [N]
|
266
|
+
;
|
267
|
+
; ["foobar" 'a'] => [1]
|
268
|
+
; ["foobar" 'o'] => [2]
|
269
|
+
; ["foobar" 'c'] => [0]
|
176
270
|
strcountc: swap push 0 swap
|
177
|
-
|
178
|
-
_strcountc_loop: ; c t s
|
271
|
+
_strcountc_loop:
|
179
272
|
dup jz _strcountc_done
|
180
273
|
dup push 128 mod copy 3 sub jz _strcountc_yes
|
181
274
|
push 128 div jump _strcountc_loop
|
182
|
-
|
183
275
|
_strcountc_yes:
|
184
276
|
swap push 1 add swap push 128 div
|
185
277
|
jump _strcountc_loop
|
186
|
-
|
187
278
|
_strcountc_done: swap slide 2 ret
|
188
279
|
|
189
|
-
; number of ocurrences of all characters in string
|
280
|
+
; returns the total number of ocurrences of all characters in string T in string S
|
281
|
+
; [S T] => [N]
|
190
282
|
; ! clobbers heap address -2
|
283
|
+
;
|
284
|
+
; ["foobar" 'o'] => [2]
|
285
|
+
; ["foobar" "ob"] => [3]
|
286
|
+
; ["foxboar" "box"] => [4]
|
287
|
+
; ["eunoia" "aeiou"] => [5]
|
288
|
+
; ["why" "aeiou"] => [0]
|
191
289
|
strcount:
|
192
290
|
swap push -2 swap store
|
193
291
|
:strunpack push 0 :to_a
|
194
292
|
map (push -2 load swap :strcountc)
|
195
293
|
reduce (add) ret
|
196
294
|
|
197
|
-
|
198
|
-
|
199
|
-
;
|
295
|
+
; translates all characters in A to the corresponding characters in B
|
296
|
+
; in stirng S, ; similar to the `tr` utility in Unix. A and B must be
|
297
|
+
; of the same length. TODO: make this smarter (ranges, length mismatch)
|
200
298
|
; ! clobbers heap addresses -1, -2, and -3
|
201
|
-
|
299
|
+
; [S A B] => [S]
|
300
|
+
;
|
301
|
+
; ["abcd" "abc" "xyz"] => ["xyzd"]
|
302
|
+
; ["foobar" "oba" "ele"] => ["feeler"]
|
303
|
+
; ["abcdcba" "abcd" "xyz|"] => ["xyz|zyx"]
|
304
|
+
strtrans:
|
202
305
|
push -3 swap store
|
203
306
|
push -2 swap store
|
204
307
|
dup :strlen push -1 swap store
|
205
308
|
:strunpack push -1 load
|
206
|
-
map (:_strtrans) pop
|
207
|
-
:strpack ret
|
208
|
-
|
309
|
+
map (:_strtrans) pop :strpack ret
|
209
310
|
_strtrans:
|
210
311
|
dup push -2 load swap :strindex
|
211
312
|
dup jn _strtrans_no
|
212
313
|
push -3 load swap :charat
|
213
314
|
slide 1 ret
|
214
|
-
|
215
315
|
_strtrans_no: pop ret
|
216
316
|
|
317
|
+
; expands the length-2 string S to contain the intervening ASCII characters
|
318
|
+
; ! TODO: make this smarter; multiple ranges in one string
|
319
|
+
; [S] => [S, expanded]
|
320
|
+
;
|
321
|
+
; ["CJ"] => ["CDEFGHIJ"]
|
322
|
+
; ["DA"] => ["DE"] TODO: bug
|
323
|
+
; ["af"] => ["abcdef"]
|
324
|
+
; ["09"] => ["0123456789"]
|
325
|
+
; ["(1"] => ["()*+,-./01"]
|
217
326
|
strexpand:
|
218
327
|
push 0 swap push 128 :divmod
|
219
328
|
swap :range :strpack :strrev ret
|
220
329
|
|
221
|
-
|
330
|
+
; "squeezes" runs of the same character in string S to just one occurrence
|
331
|
+
; [S] => [S, squeezed]
|
332
|
+
;
|
333
|
+
; ["abc"] => ["abc"]
|
334
|
+
; ["foobar"] => ["fobar"]
|
335
|
+
; ["bookkeeper"] => ["bokeper"]
|
336
|
+
; ["xxxxxxx"] => ["x"]
|
337
|
+
strsqueeze: push 0 swap
|
222
338
|
_strsqueeze_loop: ; [s]
|
223
339
|
dup jz _strsqueeze_done
|
224
340
|
push 128 :divmod dup copy 3 sub jz _strsqueeze_skip
|
225
341
|
swap jump _strsqueeze_loop
|
226
|
-
|
227
342
|
_strsqueeze_skip: pop jump _strsqueeze_loop
|
228
|
-
|
229
343
|
_strsqueeze_done: pop :strpack :strrev ret
|
230
344
|
|
231
|
-
|
232
|
-
|
233
|
-
|
345
|
+
; returns the sum of the ordinal values of the characters in string S
|
346
|
+
; [S] => [N]
|
347
|
+
;
|
348
|
+
; ["ABC"] => [198]
|
349
|
+
; ["012"] => [147]
|
350
|
+
; ["a"] => [97]
|
351
|
+
; [""] => [] TODO: bug, should be 0
|
352
|
+
strsum: :strunpack push 0 :to_a reduce (add) ret
|
353
|
+
|
354
|
+
; rotates the string S to the left N times, wrapping
|
355
|
+
; [S N]
|
356
|
+
;
|
357
|
+
; ["abc" 0] => ["abc"]
|
358
|
+
; ["abcd" 1] => ["bcda"]
|
359
|
+
; ["abcd" 5] => ["bcda"]
|
360
|
+
; ["foodbar" 4] => ["barfood"]
|
361
|
+
strrotl: push 128 swap copy 2 :strlen mod :pow :divmod :strcat ret
|
362
|
+
|
363
|
+
; rotates the string S to the right N times, wrapping
|
364
|
+
; [S N]
|
365
|
+
;
|
366
|
+
; ["abcd" 1] => ["dabc"]
|
367
|
+
; ["abcd" 5] => ["dabc"]
|
368
|
+
; ["foodbar" 3] => ["barfood"]
|
369
|
+
strrotr: push 0 swap sub :strrotl ret
|