spitewaste 0.1.010 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ ;;; Miscellaneous terminal-based functionality
2
+
3
+ import util ; hex2rgb
4
+
5
+ $_setg(code) {
6
+ push 91,27 ochr ochr
7
+ push `code` onum push 50,59 ochr ochr
8
+ :hex2rgb each (push 59 ochr onum)
9
+ push 109 ochr ret
10
+ }
11
+
12
+ setfg: $_setg(38)
13
+ setbg: $_setg(48)
14
+ reset: push 109,91,27 ochr ochr ochr 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!
@@ -15,22 +15,24 @@ import string ; charat, strcat, strindex, strlen
15
15
  ; [0 -2] => [0 -1 -2]
16
16
  ; [-3 3] => [-3 -2 -1 0 1 2 3]
17
17
  ; [3 -3] => [3 2 1 0 -1 -2 -3]
18
- ; [4 4] => [4 5] TODO: bug
19
- range: dup copy 2 sub jn _range_down
18
+ ; [4 4] => [4]
19
+ range: dup copy 2 sub jz _range_one dup copy 2 sub jn _range_down
20
20
  copy 1 push 1 add swap
21
21
  copy 1 copy 1 sub jn range pop ret
22
+ _range_one: pop ret
22
23
  _range_down:
23
24
  copy 1 push 1 sub swap
24
25
  dup copy 2 sub jn _range_down pop ret
25
26
 
26
27
  $range_loop(fn, cmp) {
27
28
  `fn`:
28
- copy 1 copy 1 add swap dup
29
- copy 2 add push -1 load `cmp` jz `fn` pop ret
29
+ dup copy 2 add @-1 `cmp` jz _`fn`_done
30
+ copy 1 copy 1 add swap jump `fn`
31
+ _`fn`_done: pop ret
30
32
  }
31
33
 
32
- ; inserts between the top two stack values the intervening consecutive elements,
33
- ; counting by `step` up/down to (but never beyond) J
34
+ ; inserts between I and J the intervening consecutive elements, counting by
35
+ ; step S up/down to (but never beyond) J
34
36
  ; [I J S] => [I I±S ... ~J]
35
37
  ;
36
38
  ; [1 4 1] => [1 2 3 4]
@@ -41,13 +43,17 @@ $range_loop(fn, cmp) {
41
43
  ; [25 -5 -10] => [25 15 5 -5]
42
44
  ; [4 20 3] => [4 7 10 13 16 19]
43
45
  ; [20 4 -3] => [20 17 14 11 8 5]
46
+ ; [3 9 7] => [3]
47
+ ; [9 3 -7] => [9]
44
48
  steprange: swap push -1 copy 1 store copy 2 sub
45
49
  jn _steprange_down_loop jump _steprange_loop ; prevent DCE
46
- $range_loop(_steprange_loop, :gt)
47
- $range_loop(_steprange_down_loop, :lt)
50
+ $range_loop(_steprange_loop, :lte)
51
+ $range_loop(_steprange_down_loop, :gte)
48
52
 
49
- ; prints the string at the top of the stack and halts execution
50
- 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
51
57
 
52
58
  ; for stoi and itos
53
59
  alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
@@ -68,7 +74,7 @@ alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
68
74
  ; ["-123" 10] => [-123]
69
75
  ; ["-ff" 16] => [-255]
70
76
  stoi: swap dup :_stoi_sign swap copy 1 :eq
71
- push 2 mul $-- push -2 swap store push 0
77
+ push 2 mul $-- ^-2 push 0
72
78
  _stoi_loop: ; [b s a]
73
79
  swap dup jz _stoi_done
74
80
  swap copy 2 copy 2
@@ -79,10 +85,11 @@ _stoi_loop: ; [b s a]
79
85
  mul add swap push 128 div swap
80
86
  jump _stoi_loop
81
87
  _stoi_sign: dup push 0 :charat push '-' :eq copy 1 :strlen :strslice ret
82
- _stoi_invalid: pop pop slide 1 swap div push -2 load mul ret
83
- _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
84
90
 
85
91
  ; creature comforts
92
+
86
93
  bin: push 2 :stoi ret
87
94
  oct: push 8 :stoi ret
88
95
  to_i: push 10 :stoi ret
@@ -92,11 +99,13 @@ hex: push 16 :stoi ret
92
99
  ; [N B]
93
100
  ;
94
101
  ; [42 2] => ["101010"]
102
+ ; [-42 2] => ["-101010"]
95
103
  ; [511 8] => ["777"]
96
104
  ; [12345 10] => ["12345"]
105
+ ; [-54321 10] => ["-54321"]
97
106
  ; [57005 16] => ["dead"]
98
107
  ; [81699 17] => ["gabe"]
99
- itos: swap push 0 ; accumulator
108
+ itos: swap push -2 copy 1 :neg? store :abs push 0
100
109
  _itos_loop:
101
110
  swap dup jz _itos_done
102
111
  swap copy 1 copy 3 mod
@@ -104,9 +113,10 @@ _itos_loop:
104
113
  swap :strcat
105
114
  swap copy 2 div
106
115
  swap jump _itos_loop
107
- _itos_done: swap slide 2 ret
116
+ _itos_done: swap slide 2 push 45 @-2 mul swap :strcat ret
108
117
 
109
118
  ; creature comforts
119
+
110
120
  to_bin: push 2 :itos ret
111
121
  to_oct: push 8 :itos ret
112
122
  to_s: push 10 :itos ret
@@ -121,12 +131,10 @@ to_hex: push 16 :itos ret
121
131
  ; [256 16] => [1 0 0 3]
122
132
  digits:
123
133
  copy 1 jz _digits_zero ; special case
124
- push -1 swap store
125
- push -1 swap ; sentinel value
134
+ ^-1 push -1 swap ; sentinel value
126
135
  _digits_loop:
127
136
  dup jz _digits_done
128
- push -1 load :divmod
129
- swap jump _digits_loop
137
+ @-1 :divmod swap jump _digits_loop
130
138
  _digits_zero: dup div ret
131
139
  _digits_done: push 1 sub :to_a ret
132
140
 
@@ -190,11 +198,79 @@ lte: swap ; intentionally flow into gte
190
198
  gte: sub jn _gte_no push 1 ret
191
199
  _gte_no: push 0 ret
192
200
 
201
+ ; returns 1 if the number N is between A and B (inclusive), 0 otherwise
202
+ ; ! A must be <= B for sensible results TODO: bug?
203
+ ; [N A B]
204
+ ;
205
+ ; [5 0 10] => [1]
206
+ ; [11 0 10] => [0]
207
+ ; [4 0 4] => [1]
208
+ ; [-1 0 4] => [0]
209
+ ; [-5 -10 0] => [1]
210
+ ; [3 4 2] => [0]
211
+ between?: copy 2 :gte swap copy 2 :lte mul slide 1 ret
212
+
193
213
  ; Though extremely rare, it's possible that we know a particular value is
194
214
  ; stored in the heap at some key, just not which one. This subroutine takes
195
215
  ; a value V to search for and a starting index I, and either returns the first
196
216
  ; key associated with that value or loops forever. Probably don't touch.
197
217
  ; [V I]
198
- heap_seeking_missile:
199
- $++ dup load copy 2 :eq jz heap_search
218
+ heap-seeking_missile:
219
+ $++ dup load copy 2 :eq jz heap-seeking_missile
200
220
  slide 1 ret
221
+
222
+ ; converts the #RRGGBB (leading '#' optional) color string S to
223
+ ; its individual RGB components as integers in the range 0-255
224
+ ; [S] => [R G B]
225
+ ;
226
+ ; ["#000000"] => [0 0 0]
227
+ ; ["ffffff"] => [255 255 255]
228
+ ; ["#102030"] => [16 32 48]
229
+ ; ["c0ffee"] => [192 255 238]
230
+ hex2rgb:
231
+ dup push 0 :charat push '#' :eq push 127 mul $++ div
232
+ push 128,2 :pow :divmod :hex swap
233
+ push 128,2 :pow :divmod :hex swap :hex ret
234
+
235
+ ; converts R, G, and B components to length-6 hexadecimal string S
236
+ ; ! dies if any of the values to convert aren't between 0 and 255
237
+ ; [R G B] => [S]
238
+ ;
239
+ ; [0 0 0] => ["000000"]
240
+ ; [255 255 255] => ["ffffff"]
241
+ ; [16 32 48] => ["102030"]
242
+ ; [192 255 238] => ["c0ffee"]
243
+ rgb2hex:
244
+ push 3 :arydup all (push 0,255 :between?) jz _rgb2hex_invalid
245
+ pop copy 2 push 256,2 :pow mul
246
+ copy 2 push 256 mul add add
247
+ slide 2 :to_hex push 6,48 :rjustc ret
248
+ _rgb2hex_invalid: push "(rgb2hex) invalid RGB" :die!
249
+
250
+ ; stashes the array A in negative heap space starting at index I
251
+ ; [A I] => []
252
+ aryheap:
253
+ $++ dup copy 2 store
254
+ swap times ($-- swap copy 1 swap store) pop ret
255
+
256
+ ; restores the heaped array starting at index I
257
+ ; [I] => [A]
258
+ heapary:
259
+ $++ dup load swap copy 1 sub swap
260
+ times (dup load swap $++) load ret
261
+
262
+ ; swaps the elements in the heap at indices I and J
263
+ ; ! TODO: make heap effects doctest-able
264
+ ; [I J] => []
265
+ heapswap: dup load swap copy 2 load store store ret
266
+
267
+ ; returns the number of nanoseconds N since the Unix epoch
268
+ ; [] => [N]
269
+ time: push "date +%s%N" shell :to_i ret
270
+
271
+ $bench(insns) {
272
+ #insns :println
273
+ :time ^0 `insns` :time @0 sub
274
+ push 10,9 :pow :divmod swap onum
275
+ push '.' ochr :to_s push 9,48 :rjustc :println
276
+ }
@@ -64,9 +64,13 @@ maxby_lesser_%1$s: swap jump maxby_resume_%1$s
64
64
  maxby_done_%1$s:
65
65
  SPW
66
66
 
67
- 'minby' => 'maxby (%2$s push -1 mul)',
68
- 'each' => 'dup times (dup call roll %2$s push 1 sub) pop',
69
- 'count' => 'select (%2$s) dup call nslide',
67
+ 'minby' => 'maxby (%2$s push -1 mul)',
68
+ 'each' => 'dup times (dup call roll %2$s push 1 sub) pop',
69
+ 'all' => 'map (%2$s) reduce (add) push -11 load call eq',
70
+ # TODO: optimize any to stop early if possible
71
+ 'any' => 'map (%2$s) reduce (add) push 0 call gt',
72
+ 'none' => 'map (%2$s) reduce (add) push 0 call eq',
73
+ 'count' => 'select (%2$s) dup call nslide',
70
74
  'select' => generate_filter_spw('select', 0, 1),
71
75
  'reject' => generate_filter_spw('reject', 1, 0),
72
76
  }
@@ -57,14 +57,14 @@ module Spitewaste
57
57
  private
58
58
 
59
59
  def preprocess!
60
- @src.prepend "import syntax\n"
60
+ @src << "\nimport syntax"
61
61
  resolve_imports
62
62
  seed_prng if @seen.include? 'random'
63
63
  resolve_strings
64
- remove_comments
65
64
  add_sugar
66
- fucktionalize
65
+ remove_comments
67
66
  propagate_macros
67
+ fucktionalize
68
68
  end
69
69
 
70
70
  def resolve_imports
@@ -78,7 +78,13 @@ module Spitewaste
78
78
  while @src['import']
79
79
  imports = []
80
80
  @src.gsub!(/import\s+(\S+).*/) {
81
- imports << resolve($1) if @seen.add? $1
81
+ if $1 == ?*
82
+ imports = Dir[LIBSPW + '/*.spw'].map {
83
+ File.read(_1) if @seen.add? File.basename(_1, '.spw')
84
+ }
85
+ else
86
+ imports << resolve($1) if @seen.add? $1
87
+ end
82
88
  '' # remove import statement
83
89
  }
84
90
  @src << imports.join(?\n)
@@ -91,7 +97,7 @@ module Spitewaste
91
97
  end
92
98
 
93
99
  def seed_prng
94
- @src.prepend "push $seed,#{rand 2**31} store $seed = -9001"
100
+ @src.prepend "push $seed,#{rand 2**31} store $seed = -9001\n"
95
101
  end
96
102
 
97
103
  def resolve_strings
@@ -111,6 +117,10 @@ module Spitewaste
111
117
  @src.gsub!(/'(.)'/) { $1.ord }
112
118
  # quick push (`push 1,2,3` desugars to individual pushes)
113
119
  @src.gsub!(/push \S+/) { |m| m.split(?,) * ' push ' }
120
+ # quick store (`^2` = `push 2 swap store`)
121
+ @src.gsub!(/\^(\S+)/, 'push \1 swap store')
122
+ # quick load (`@2` = `push 2 load`)
123
+ @src.gsub!(/@(\S+)/, 'push \1 load')
114
124
  end
115
125
 
116
126
  def gensym
@@ -131,18 +141,20 @@ module Spitewaste
131
141
  parse = -> s { s.split(?,).map &:strip }
132
142
 
133
143
  # Macro "functions" get handled first.
134
- @src.gsub!(/(\$\S+?)\(([^)]+)\)\s*{(.+?)}/m) {
144
+ @src.gsub!(/(\$\S+?)\(([^)]*)\)\s*{(.+?)}/m) {
135
145
  @macros[$1] ||= [$2, $3]; ''
136
146
  }
137
- @src.gsub!(/(\$\S+?)\(([^)]+)\)/) {
147
+ @src.gsub!(/(\$\S+?)\(([^)]*)\)/) {
138
148
  params, body = @macros[$1]
139
149
  raise "no macro function '#$1'" unless body
140
150
  map = parse[params].zip(parse[$2]).to_h
141
- body.gsub(/`(.+?)`/) { map[$1] }
151
+ body
152
+ .gsub(/`(.+?)`/) { map[$1] }
153
+ .gsub(/#(\S+)/) { "push #{Spitewaste.strpack map[$1]}" }
142
154
  }
143
155
 
144
156
  @src.gsub!(/(\$\S+)\s*=\s*(.+)/) { @macros[$1] ||= $2; '' }
145
- @src.gsub!(/(\$\S+)/) { @macros[$1] || raise("no macro '#$1'") }
157
+ @src.gsub!(/(\$[^)\s]+)/) { @macros[$1] || raise("no macro '#$1'") }
146
158
  end
147
159
 
148
160
  def eliminate_dead_code!
@@ -1,3 +1,3 @@
1
1
  module Spitewaste
2
- VERSION = '0.1.010'
2
+ VERSION = '0.2.2'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spitewaste
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.010
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Collided Scope (collidedscope)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-18 00:00:00.000000000 Z
11
+ date: 2021-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -110,6 +110,7 @@ files:
110
110
  - lib/spitewaste/libspw/stack.spw
111
111
  - lib/spitewaste/libspw/string.spw
112
112
  - lib/spitewaste/libspw/syntax.spw
113
+ - lib/spitewaste/libspw/terminal.spw
113
114
  - lib/spitewaste/libspw/test.spw
114
115
  - lib/spitewaste/libspw/util.spw
115
116
  - lib/spitewaste/parsers/assembly.rb