spitewaste 0.1.004 → 0.1.009

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,18 +4,44 @@ import stack ; to_a
4
4
  import string ; charat, strcat, strindex, strlen
5
5
 
6
6
  ; inserts between the top two stack values the intervening consecutive elements
7
- ; ! TODO: Support step, and negative as well.
8
- ; [I J] => [I I+1 ... J]
7
+ ; supports counting up or down
8
+ ; [I J] => [I I±1 ... J]
9
9
  ;
10
10
  ; [2 5] => [2 3 4 5]
11
+ ; [5 2] => [5 4 3 2]
11
12
  ; [0 4] => [0 1 2 3 4]
13
+ ; [4 0] => [4 3 2 1 0]
12
14
  ; [-2 0] => [-2 -1 0]
15
+ ; [0 -2] => [0 -1 -2]
13
16
  ; [-3 3] => [-3 -2 -1 0 1 2 3]
17
+ ; [3 -3] => [3 2 1 0 -1 -2 -3]
14
18
  ; [4 4] => [4 5] TODO: bug
15
- range:
19
+ range: dup copy 2 sub jn _range_down
16
20
  copy 1 push 1 add swap
17
- copy 1 copy 1 sub jn range
18
- pop ret
21
+ copy 1 copy 1 sub jn range pop ret
22
+ _range_down:
23
+ copy 1 push 1 sub swap
24
+ dup copy 2 sub jn _range_down pop ret
25
+
26
+ $range_loop(fn, cmp) {
27
+ `fn`:
28
+ copy 1 copy 1 add swap dup
29
+ copy 2 add push -1 load `cmp` jz `fn` pop ret
30
+ }
31
+
32
+ ; inserts between the top two stack values the intervening consecutive elements,
33
+ ; counting by `step` up/down to (but never beyond) J
34
+ ; [I J S] => [I I±S ... ~J]
35
+ ;
36
+ ; [1 4 1] => [1 2 3 4]
37
+ ; [4 1 -1] => [4 3 2 1]
38
+ ; [2 10 3] => [2 5 8]
39
+ ; [10 2 -3] => [10 7 4]
40
+ ; [-5 25 10] => [-5 5 15 25]
41
+ ; [25 -5 -10] => [25 15 5 -5]
42
+ steprange: swap push -1 swap store dup copy 2 sub jn _steprange_down_loop
43
+ $range_loop(_steprange_loop, :gt)
44
+ $range_loop(_steprange_down_loop, :lt)
19
45
 
20
46
  ; prints the string at the top of the stack and halts execution
21
47
  die!: :println exit
@@ -34,7 +60,12 @@ alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
34
60
  ; ["123____" 10] => [123000] ; TODO: bug
35
61
  ; ["dead" 16] => [57005]
36
62
  ; ["gabe" 17] => [81699]
37
- stoi: swap push 0 ; accumulator
63
+ ; ["0" 10] => [0] ["-0" 10] => [0]
64
+ ; ["-10001" 2] => [-17]
65
+ ; ["-123" 10] => [-123]
66
+ ; ["-ff" 16] => [-255]
67
+ stoi: swap dup :_stoi_sign swap copy 1 :eq
68
+ push 2 mul $-- push -2 swap store push 0
38
69
  _stoi_loop: ; [b s a]
39
70
  swap dup jz _stoi_done
40
71
  swap copy 2 copy 2
@@ -44,10 +75,11 @@ _stoi_loop: ; [b s a]
44
75
  dup jn _stoi_invalid ; found something non-alphanumeric
45
76
  mul add swap push 128 div swap
46
77
  jump _stoi_loop
47
- _stoi_invalid: pop pop slide 1 swap div ret ; return what we were able to parse
48
- _stoi_done: swap slide 2 ret
78
+ _stoi_sign: dup push 0 :charat push '-' :eq copy 1 :strlen :strslice ret
79
+ _stoi_invalid: pop pop slide 1 swap div push -2 load mul ret
80
+ _stoi_done: swap slide 2 push -2 load mul ret
49
81
 
50
- ;;; creature comforts
82
+ ; creature comforts
51
83
  bin: push 2 :stoi ret
52
84
  oct: push 8 :stoi ret
53
85
  to_i: push 10 :stoi ret
@@ -71,11 +103,14 @@ _itos_loop:
71
103
  swap jump _itos_loop
72
104
  _itos_done: swap slide 2 ret
73
105
 
74
- ; creature comfort
75
- to_s: push 10 :itos ret
106
+ ; creature comforts
107
+ to_bin: push 2 :itos ret
108
+ to_oct: push 8 :itos ret
109
+ to_s: push 10 :itos ret
110
+ to_hex: push 16 :itos ret
76
111
 
77
112
  ; puts the digits of N in base B on the stack as a pseudo-array
78
- ; [N B]
113
+ ; [N B] => [Dn ...D0 n]
79
114
  ;
80
115
  ; [42 2] => [1 0 1 0 1 0 6]
81
116
  ; [12345 10] => [1 2 3 4 5 5]
@@ -125,22 +160,33 @@ _neq_no: push 0 ret
125
160
  ; pops A and B and pushes 1 if A is greater than B, 0 otherwise
126
161
  ; [A B] => [A > B]
127
162
  ;
128
- ; [4 3] => [1]
129
- ; [3 4] => [0]
130
- ; [2 2] => [0]
131
- ; [2 1] => [1]
163
+ ; [4 3] => [1] [3 4] => [0]
164
+ ; [2 2] => [0] [2 1] => [1]
132
165
  gt: swap ; intentionally flow into lt
133
166
 
134
167
  ; pops A and B and pushes 1 if A is less than than B, 0 otherwise
135
168
  ; [A B] => [A < B]
136
169
  ;
137
- ; [3 4] => [1]
138
- ; [4 3] => [0]
139
- ; [2 2] => [0]
140
- ; [1 2] => [1]
170
+ ; [3 4] => [1] [4 3] => [0]
171
+ ; [2 2] => [0] [1 2] => [1]
141
172
  lt: sub jn _lt_yes push 0 ret
142
173
  _lt_yes: push 1 ret
143
174
 
175
+ ; pops A and B and pushes 1 if A is less than or equal to B, 0 otherwise
176
+ ; [A B] => [A > B]
177
+ ;
178
+ ; [2 2] => [1] [2 1] => [0]
179
+ ; [4 3] => [0] [3 4] => [1]
180
+ lte: swap ; intentionally flow into gte
181
+
182
+ ; pops A and B and pushes 1 if A is greater than or equal to B, 0 otherwise
183
+ ; [A B] => [A > B]
184
+ ;
185
+ ; [2 2] => [1] [1 2] => [0]
186
+ ; [3 4] => [0] [4 3] => [1]
187
+ gte: sub jn _gte_no push 1 ret
188
+ _gte_no: push 0 ret
189
+
144
190
  ; Though extremely rare, it's possible that we know a particular value is
145
191
  ; stored in the heap at some key, just not which one. This subroutine takes
146
192
  ; a value V to search for and a starting index I, and either returns the first
@@ -11,7 +11,7 @@ module Spitewaste
11
11
  INSTRUCTIONS = /(\S+):|(\b(#{OPERATORS_M2T.keys * ?|})\s+(-?\d\S*)?)/
12
12
  SPECIAL_INSN = /(call|jump|jz|jn)\s+(\S+)/
13
13
 
14
- attr_reader :src, :instructions, :error
14
+ attr_reader :src, :instructions, :error, :symbol_table
15
15
 
16
16
  def initialize program, **options
17
17
  @src = program.dup
@@ -127,8 +127,22 @@ module Spitewaste
127
127
  def propagate_macros
128
128
  # Macros are write-once, allowing user code to customize the special
129
129
  # values that get used to drive certain behavior in the standard library.
130
+
131
+ parse = -> s { s.split(?,).map &:strip }
132
+
133
+ # Macro "functions" get handled first.
134
+ @src.gsub!(/(\$\S+?)\(([^)]+)\)\s*{(.+?)}/m) {
135
+ @macros[$1] ||= [$2, $3]; ''
136
+ }
137
+ @src.gsub!(/(\$\S+?)\(([^)]+)\)/) {
138
+ params, body = @macros[$1]
139
+ raise "no macro function '#$1'" unless body
140
+ map = parse[params].zip(parse[$2]).to_h
141
+ body.gsub(/`(.+?)`/) { map[$1] }
142
+ }
143
+
130
144
  @src.gsub!(/(\$\S+)\s*=\s*(.+)/) { @macros[$1] ||= $2; '' }
131
- @src.gsub!(/(\$\S+)/) { @macros[$1] || raise("no macro '#{$1}'") }
145
+ @src.gsub!(/(\$\S+)/) { @macros[$1] || raise("no macro '#$1'") }
132
146
  end
133
147
 
134
148
  def eliminate_dead_code!
@@ -146,7 +160,7 @@ module Spitewaste
146
160
  subroutines = {}
147
161
  while label = tokens.shift
148
162
  sublen = tokens.index { |t| t[/:$/] } || tokens.size
149
- subroutines[label.chop] = tokens.shift sublen
163
+ subroutines[label.chop] ||= tokens.shift sublen
150
164
  end
151
165
 
152
166
  # A subroutine may indirectly depend on the one immediately after by
@@ -1,3 +1,3 @@
1
1
  module Spitewaste
2
- VERSION = '0.1.004'
2
+ VERSION = '0.1.009'
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.004
4
+ version: 0.1.009
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-14 00:00:00.000000000 Z
11
+ date: 2020-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -86,6 +86,7 @@ files:
86
86
  - lib/spitewaste/cli/asm.rb
87
87
  - lib/spitewaste/cli/compile.rb
88
88
  - lib/spitewaste/cli/convert.rb
89
+ - lib/spitewaste/cli/docs.rb
89
90
  - lib/spitewaste/cli/exec.rb
90
91
  - lib/spitewaste/cli/image.rb
91
92
  - lib/spitewaste/emitter.rb
@@ -99,6 +100,7 @@ files:
99
100
  - lib/spitewaste/libspw/array.spw
100
101
  - lib/spitewaste/libspw/bits.spw
101
102
  - lib/spitewaste/libspw/case.spw
103
+ - lib/spitewaste/libspw/docs.json
102
104
  - lib/spitewaste/libspw/fun.spw
103
105
  - lib/spitewaste/libspw/io.spw
104
106
  - lib/spitewaste/libspw/math.spw