spitewaste 0.1.003 → 0.1.004
Sign up to get free protection for your applications and to get access to all the features.
- 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
|