spitewaste 0.1.001 → 0.1.006

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,6 @@
1
+ import math ; divmod
2
+ import util ; inc
3
+
1
4
  ; [n] => [0|1]
2
5
  prime?: ; [n]
3
6
  dup push 3 sub jn _prime_special ; special-case < 3
@@ -40,7 +43,5 @@ _factor_loop: ; [d n]
40
43
  _divisor_loop:
41
44
  dup copy 2 :divmod jz _divisor_keep
42
45
  pop swap :next_prime swap jump _factor_loop
43
-
44
46
  _divisor_keep: push -2 :inc slide 1 copy 1 swap jump _factor_loop
45
-
46
47
  _factor_done: slide 1 push 3 sub load ret
@@ -1,3 +1,5 @@
1
+ import math ; pow
2
+
1
3
  srand: push $seed swap store ret
2
4
 
3
5
  rand:
@@ -0,0 +1,153 @@
1
+ ;;; Rational numbers
2
+
3
+ import math ; abs, divmod, gcd, pow, sign
4
+ import util ; die!, neq
5
+
6
+ ; maximum value of either component of a rational number before behavior is
7
+ ; undefined; 2^31 by default to give interpreters without bignums a chance,
8
+ ; but customizable from userland.
9
+ $RAT = push 2,31 :pow
10
+
11
+ ; encodes a numerator N and a denominator D as a rational number R with the
12
+ ; following structure: ((N × $RAT + D) << 1) + sign (0 for negative, else 1).
13
+ ; This representation is nice because, with care, it makes it so that we never
14
+ ; have to involve the heap (besides divmod) just to do fairly basic arithmetic.
15
+ ; [N D] => [R]
16
+ ;
17
+ ; [22 7] => [R(22,7)]
18
+ ; [4 8] => [R(4,8)] ; no implicit simplification TODO: make it configurable?
19
+ ; [5 1] => [R(5,1)] ; no conversion to integer (duh)
20
+ ; [-3 4] => [R(-3,4)]
21
+ ; [3 -4] => [R(-3,4)] ; sign always held in the numerator
22
+ ; [-1 -3] => [R(1,3)]
23
+ to_r: dup jz _to_r_dbz
24
+ dup :sign copy 2 :sign mul push -1 :neq
25
+ copy 2 :abs copy 2 :abs
26
+ $RAT copy 2 mul add push 2 mul
27
+ copy 2 add slide 4 ret
28
+ _to_r_dbz: push "0 in denominator!" :die!
29
+
30
+ ; returns the numerator N of the rational number R, which may be negative
31
+ ; [R] => [N]
32
+ ;
33
+ ; [R(22,7)] => [22]
34
+ ; [R(-3,4)] => [-3]
35
+ ; [R(3,-4)] => [-3]
36
+ ratnum: push 2 :divmod push 2 mul push 1 sub swap $RAT div mul ret
37
+
38
+ ; returns the denominator D of the rational number R; always positive
39
+ ; [R] => [D]
40
+ ;
41
+ ; [R(22,7)] => [7]
42
+ ; [R(-3,4)] => [4]
43
+ ; [R(3,-4)] => [4]
44
+ ratden: push 2 div $RAT mod ret
45
+
46
+ ; decomposes the rational number R into its numerator N and denominator D
47
+ ; [R] => [N D]
48
+ ;
49
+ ; [R(22,7)] => [22 7]
50
+ ; [R(-3,4)] => [-3 4]
51
+ ; [R(3,-4)] => [-3 4]
52
+ ; [R(4,8)] => [4 8] ; no implicit simplification
53
+ from_r: dup :ratnum swap :ratden ret
54
+
55
+ ; fully simplifies the rational number R by dividing both of its components
56
+ ; by their greatest common divisor
57
+ ; [R] => [R, simplified]
58
+ ;
59
+ ; [R(4,8)] => [R(1,2)]
60
+ ; [R(-3,12)] => [R(-1,4)]
61
+ ; [R(17,-51)] => [R(-1,3)]
62
+ ratsimp:
63
+ push 2 :divmod swap $RAT :divmod dup copy 2 :gcd
64
+ swap copy 1 div copy 2 copy 2 div swap :to_r
65
+ slide 2 push 2 div push 2 mul add ret
66
+
67
+ ; helper prologue shared by all but ratmul
68
+ _rathead:
69
+ copy 1 :ratnum copy 1 :ratden mul
70
+ copy 2 :ratden copy 2 :ratnum mul ret
71
+
72
+ ; helper epilogue shared by all but ratdiv
73
+ _rattail:
74
+ copy 2 :ratden copy 2 :ratden mul
75
+ :to_r :ratsimp slide 2 ret
76
+
77
+ ; returns the simplified sum of the rational numbers Ra and Rb
78
+ ; [Ra Rb] => [Ra + Rb]
79
+ ;
80
+ ; [R(-9,-14) R(-3,19)] => [R(129,266)]
81
+ ; [R(17,30) R(18,10)] => [R(71,30)]
82
+ ; [R(-27,14) R(15,-23)] => [R(-831,322)]
83
+ ; [R(3,-9) R(8,3)] => [R(7,3)]
84
+ ; [R(-5,27) R(-2,-27)] => [R(-1,9)]
85
+ ; [R(-27,-8) R(-15,22)] => [R(237,88)]
86
+ ; [R(-9,-29) R(-27,3)] => [R(-252,29)]
87
+ ; [R(2,-21) R(4,6)] => [R(4,7)]
88
+ ratadd: :_rathead add :_rattail ret
89
+
90
+ ; returns the simplified difference of the rational numbers Ra and Rb
91
+ ; [Ra Rb] => [Ra - Rb]
92
+ ;
93
+ ; [R(21,25) R(-28,27)] => [R(1267,675)]
94
+ ; [R(14,7) R(13,6)] => [R(-1,6)]
95
+ ; [R(-24,-9) R(-5,-21)] => [R(17,7)]
96
+ ; [R(-27,-2) R(-2,26)] => [R(353,26)]
97
+ ; [R(-27,3) R(2,-22)] => [R(-98,11)]
98
+ ; [R(-4,23) R(-9,13)] => [R(155,299)]
99
+ ; [R(-14,19) R(-18,-11)] => [R(-496,209)]
100
+ ; [R(-29,21) R(-15,-16)] => [R(-779,336)]
101
+ ratsub: :_rathead sub :_rattail ret
102
+
103
+ ; returns the simplified product of the rational numbers Ra and Rb
104
+ ; [Ra Rb] => [Ra × Rb]
105
+ ;
106
+ ; [R(-24,26) R(-1,-30)] => [R(-2,65)]
107
+ ; [R(19,4) R(27,2)] => [R(513,8)]
108
+ ; [R(25,27) R(4,-11)] => [R(-100,297)]
109
+ ; [R(1,18) R(4,8)] => [R(1,36)]
110
+ ; [R(1,27) R(-8,29)] => [R(-8,783)]
111
+ ; [R(25,-13) R(-6,24)] => [R(25,52)]
112
+ ; [R(6,-13) R(9,23)] => [R(-54,299)]
113
+ ; [R(11,8) R(-19,-19)] => [R(11,8)]
114
+ ratmul: copy 1 :ratnum copy 1 :ratnum mul :_rattail ret
115
+
116
+ ; returns the simplified quotient of the rational numbers Ra and Rb
117
+ ; [Ra Rb] => [Ra / Rb]
118
+ ;
119
+ ; [R(-30,-22) R(15,12)] => [R(12,11)]
120
+ ; [R(13,28) R(-15,29)] => [R(-377,420)]
121
+ ; [R(7,-30) R(-22,-12)] => [R(-7,55)]
122
+ ; [R(15,4) R(8,-8)] => [R(-15,4)]
123
+ ; [R(-23,28) R(-16,-15)] => [R(-345,448)]
124
+ ; [R(-18,12) R(6,18)] => [R(-9,2)]
125
+ ; [R(29,-2) R(11,-21)] => [R(609,22)]
126
+ ; [R(-23,25) R(25,-3)] => [R(69,625)]
127
+ ratdiv: :_rathead :to_r :ratsimp slide 2 ret
128
+
129
+ ; returns the simplified modulus of the rational numbers Ra and Rb
130
+ ; [Ra Rb] => [Ra % Rb]
131
+ ;
132
+ ; [R(-15,-3) R(-16,-10)] => [R(1,5)]
133
+ ; [R(4,2) R(21,21)] => [R(0,1)]
134
+ ; [R(24,10) R(-18,-3)] => [R(12,5)]
135
+ ; [R(3,-7) R(-2,16)] => [R(-3,56)]
136
+ ; [R(4,28) R(-29,7)] => [R(-4,1)]
137
+ ; [R(7,-27) R(10,23)] => [R(109,621)]
138
+ ; [R(28,-3) R(30,-12)] => [R(-11,6)]
139
+ ; [R(-29,21) R(19,-23)] => [R(-268,483)]
140
+ ratmod: :_rathead mod :_rattail ret
141
+
142
+ ; returns the sign of the rational number R
143
+ ; [R] => [-1 | 0 | 1]
144
+ ;
145
+ ; [R(4,3)] => [1]
146
+ ; [R(4,-3)] => [-1]
147
+ ; [R(0,10)] => [0]
148
+ ; [R(-3,4)] => [-1]
149
+ ; [R(-3,-4)] => [1]
150
+ ratsign:
151
+ dup $RAT push 2 mul push 2 add sub jn _ratsign_zero
152
+ push 2 mod push 2 mul push 1 sub ret
153
+ _ratsign_zero: pop push 0 ret
@@ -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
- to_a:
44
- push -1 swap store
45
- push -10 dup store
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
- ; dynamic pop ; [n]
64
- npop:
65
- dup jz _npop_done
66
- swap pop push 1 sub jump npop
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
- ; dynamic slide ; [n]
71
- nslide:
72
- swap push -1 swap store
73
- :npop push -1 load ret
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
- strpack: push 0 ; tally
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
- strunpack:
18
- :strrev push 0 swap ; terminator
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
- ; takes a packed string and just returns it log128 to give the length
30
- strlen: push 128 :ilog ret
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
- strcat:
36
- push 128 copy 2 :strlen :pow
37
- mul add ret
38
-
39
- ;;;
40
-
41
- ; reverses a packed string in-place (heapless) [s] => [s.reverse]
42
- strrev: push 0 swap ; [tally string]
43
-
44
- _strrev_loop: ; [t s]
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] => [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 ; [s t]
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] => [S']
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
- ; index of substring t in string s ; [s t]
81
+ ; returns the index I of substring T in string S (or -1 if not found)
82
+ ; [S T] => [I]
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 ; increment index
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 C at index I in string S
99
+ ; [S I] => [C]
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
- ; 1 if the character at the top of the stack is alphabetical, 0 otherwise
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
- ; repeat a string s n times [s n]
121
+ ; returns string S replicated N times
122
+ ; [S N] => [S']
123
+ ;
124
+ ; ["abc" 1] => ["abc"]
125
+ ; ["abc" 2] => ["abcabc"]
126
+ ; ["abc" 0] => [""]
95
127
  strrep: push 0 swap
96
- _strrep_loop: ; [s t n]
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
- ; Left-justify string s to width w with character c. [s w c]
140
+ ; left-justifies string S to width W with character C
141
+ ; [S W C] => [S']
142
+ ;
143
+ ; ["foo" 5 'x'] => ["fooxx"]
144
+ ; ["foobar" 4 'x'] => ["foobar"]
145
+ ; ["" 3 'x'] => ["xxx"]
111
146
  ljustc: :_justc :strcat ret
112
147
 
113
- ; Left-justify string s to width w with spaces. [s w]
148
+ ; left-justifies string S to width W with spaces
149
+ ; [S W] => [S']
150
+ ;
151
+ ; ["foo" 5] => ["foo "]
152
+ ; ["foobar" 4] => ["foobar"]
153
+ ; ["" 3] => [528416]
114
154
  ljust: push ' ' :ljustc ret
115
155
 
116
- ; Right-justify string s to width w with character c. [s w c]
156
+ ; right-justifies string S to width W with character C
157
+ ; [S W C] => [S']
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
- ; Right-justify string s to width w with spaces. [s w]
164
+ ; right-justifies string S to width W with spaces
165
+ ; [S W C] => [S']
166
+ ;
167
+ ; ["foo" 5] => [" foo"]
168
+ ; ["foobar" 4] => ["foobar"]
169
+ ; ["" 3] => [528416]
120
170
  rjust: push ' ' :rjustc ret
121
171
 
122
- ; Center string s to width w with character c. [s w c]
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']
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
- ; Center string s to width w with spaces. [s w]
192
+ ; centers string S to width W with spaces
193
+ ; [S W] => [S']
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
- ; remove the last character of a string
139
- strchop:
140
- dup :strlen push 1 sub
141
- push 0 swap :strslice ret
142
-
143
- ; Split string s on delimiting character d.
144
- ; ! clobbers heap addresses -1 and -2
145
- strsplit: ; [s d]
204
+ ; removes the last character of a string
205
+ ; [S] => [S']
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: ; [s]
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
- ; [...strs n delim]
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
- ; return the number of ocurrences of character c in string s [s c]
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 c [s c]
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
- ; Convert all characters in src to the corresponding characters in dest.
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
- strtrans: ; [string src dest]
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']
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
- strsqueeze: push 0 swap ; [sentinel s]
330
+ ; "squeezes" runs of the same character in string S to just one occurrence
331
+ ; [S] => [S']
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
- strsum: :strunpack push 0 :to_a :arysum ret
232
-
233
- lines: push 10 :strsplit ret
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