spitewaste 0.1.003 → 0.1.008

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,6 @@
1
+ import io ; print, println
2
+ import string ; strcat
3
+
1
4
  assert_eq:
2
5
  dup copy 2 sub jz _assert_eq_yes
3
6
  push "expected " :print onum
@@ -1,24 +1,46 @@
1
- import syntax
2
-
3
- ; Calling range() will insert between the top two stack values all of the
4
- ; intervening consecutive elements: [9 10 3 7] -> [9 10 3 4 5 6 7]
1
+ import io ; println
2
+ import math ; divmod, pow
3
+ import stack ; to_a
4
+ import string ; charat, strcat, strindex, strlen
5
+
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]
9
+ ;
10
+ ; [2 5] => [2 3 4 5]
11
+ ; [0 4] => [0 1 2 3 4]
12
+ ; [-2 0] => [-2 -1 0]
13
+ ; [-3 3] => [-3 -2 -1 0 1 2 3]
14
+ ; [4 4] => [4 5] TODO: bug
5
15
  range:
6
16
  copy 1 push 1 add swap
7
17
  copy 1 copy 1 sub jn range
8
18
  pop ret
9
19
 
10
- ;;;
11
-
12
- die: :println exit
13
- ;;;
20
+ ; prints the string at the top of the stack and halts execution
21
+ die!: :println exit
14
22
 
23
+ ; for stoi and itos
15
24
  alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
16
25
 
17
- ; string to integer in base b
18
- stoi: ; [s b]
19
- swap push 0 ; tally
20
-
21
- _stoi_loop: ; [b s t]
26
+ ; converts the string S to an integer in base B (2-36)
27
+ ; ! TODO: support prefixes?
28
+ ; [S B]
29
+ ;
30
+ ; ["101010" 2] => [42]
31
+ ; ["0777" 8] => [511]
32
+ ; ["12345" 10] => [12345]
33
+ ; ["123_" 10] => [123]
34
+ ; ["123____" 10] => [123000] ; TODO: bug
35
+ ; ["dead" 16] => [57005]
36
+ ; ["gabe" 17] => [81699]
37
+ ; ["0" 10] => [0] ["-0" 10] => [0]
38
+ ; ["-10001" 2] => [-17]
39
+ ; ["-123" 10] => [-123]
40
+ ; ["-ff" 16] => [-255]
41
+ stoi: swap dup :_stoi_sign swap copy 1 :eq
42
+ push 2 mul $-- push -2 swap store push 0
43
+ _stoi_loop: ; [b s a]
22
44
  swap dup jz _stoi_done
23
45
  swap copy 2 copy 2
24
46
  :strlen push 1 sub :pow
@@ -27,24 +49,25 @@ _stoi_loop: ; [b s t]
27
49
  dup jn _stoi_invalid ; found something non-alphanumeric
28
50
  mul add swap push 128 div swap
29
51
  jump _stoi_loop
52
+ _stoi_sign: dup push 0 :charat push '-' :eq copy 1 :strlen :strslice ret
53
+ _stoi_invalid: pop pop slide 1 swap div push -2 load mul ret
54
+ _stoi_done: swap slide 2 push -2 load mul ret
30
55
 
31
- _stoi_invalid: pop pop slide 1 swap div ret
32
-
33
- _stoi_done: swap slide 2 ret
34
-
35
- ;;;
36
-
56
+ ; creature comforts
37
57
  bin: push 2 :stoi ret
38
58
  oct: push 8 :stoi ret
39
59
  to_i: push 10 :stoi ret
40
60
  hex: push 16 :stoi ret
41
61
 
42
- ;;;
43
-
44
- ; integer to string in base b
45
- itos: ; [i b]
46
- swap push 0 ; empty string
47
-
62
+ ; converts the integer N to a string in base B
63
+ ; [N B]
64
+ ;
65
+ ; [42 2] => ["101010"]
66
+ ; [511 8] => ["777"]
67
+ ; [12345 10] => ["12345"]
68
+ ; [57005 16] => ["dead"]
69
+ ; [81699 17] => ["gabe"]
70
+ itos: swap push 0 ; accumulator
48
71
  _itos_loop:
49
72
  swap dup jz _itos_done
50
73
  swap copy 1 copy 3 mod
@@ -52,47 +75,86 @@ _itos_loop:
52
75
  swap :strcat
53
76
  swap copy 2 div
54
77
  swap jump _itos_loop
55
-
56
78
  _itos_done: swap slide 2 ret
57
79
 
58
- to_s: push 10 :itos ret
59
-
60
- ;;;
61
-
62
- digits: ; [ n d ]
80
+ ; creature comforts
81
+ to_bin: push 2 :itos ret
82
+ to_oct: push 8 :itos ret
83
+ to_s: push 10 :itos ret
84
+ to_hex: push 16 :itos ret
85
+
86
+ ; puts the digits of N in base B on the stack as a pseudo-array
87
+ ; [N B] => [Dn ...D0 n]
88
+ ;
89
+ ; [42 2] => [1 0 1 0 1 0 6]
90
+ ; [12345 10] => [1 2 3 4 5 5]
91
+ ; [255 16] => [15 15 2]
92
+ ; [256 16] => [1 0 0 3]
93
+ digits:
63
94
  copy 1 jz _digits_zero ; special case
64
95
  push -1 swap store
65
96
  push -1 swap ; sentinel value
66
-
67
97
  _digits_loop:
68
98
  dup jz _digits_done
69
99
  push -1 load :divmod
70
100
  swap jump _digits_loop
71
-
72
101
  _digits_zero: dup div ret
73
-
74
102
  _digits_done: push 1 sub :to_a ret
75
103
 
76
- ; increment value at heap address n
104
+ ; increments the value at heap address N TODO: make heap effects test-able ?
105
+ ; [N]
77
106
  inc: dup load $++ store ret
78
107
 
79
- ; decrement value at heap address n
108
+ ; decrements the value at heap address N
109
+ ; [N]
80
110
  dec: dup load $-- store ret
81
111
 
82
- ;;;
83
-
112
+ ; pops A and B and pushes 1 if they're equal, 0 otherwise
113
+ ; [A B] => [A == B]
114
+ ;
115
+ ; [1 2] => [0]
116
+ ; [3 3] => [1]
117
+ ; [-4 4] => [0]
118
+ ; ['A' 65] => [1]
119
+ ; ['B' 65] => [0]
84
120
  eq: sub jz _eq_yes push 0 ret
85
121
  _eq_yes: push 1 ret
86
122
 
123
+ ; pops A and B and pushes 1 if they're not equal, 0 otherwise
124
+ ; [A B] => [A != B]
125
+ ;
126
+ ; [1 2] => [1]
127
+ ; [3 3] => [0]
128
+ ; [-4 4] => [1]
129
+ ; ['A' 65] => [0]
130
+ ; ['B' 65] => [1]
87
131
  neq: sub jz _neq_no push 1 ret
88
132
  _neq_no: push 0 ret
89
133
 
90
- gt: swap
134
+ ; pops A and B and pushes 1 if A is greater than B, 0 otherwise
135
+ ; [A B] => [A > B]
136
+ ;
137
+ ; [4 3] => [1]
138
+ ; [3 4] => [0]
139
+ ; [2 2] => [0]
140
+ ; [2 1] => [1]
141
+ gt: swap ; intentionally flow into lt
142
+
143
+ ; pops A and B and pushes 1 if A is less than than B, 0 otherwise
144
+ ; [A B] => [A < B]
145
+ ;
146
+ ; [3 4] => [1]
147
+ ; [4 3] => [0]
148
+ ; [2 2] => [0]
149
+ ; [1 2] => [1]
91
150
  lt: sub jn _lt_yes push 0 ret
92
151
  _lt_yes: push 1 ret
93
152
 
94
- ;;;
95
-
96
- heap_search:
153
+ ; Though extremely rare, it's possible that we know a particular value is
154
+ ; stored in the heap at some key, just not which one. This subroutine takes
155
+ ; a value V to search for and a starting index I, and either returns the first
156
+ ; key associated with that value or loops forever. Probably don't touch.
157
+ ; [V I]
158
+ heap_seeking_missile:
97
159
  $++ dup load copy 2 :eq jz heap_search
98
160
  slide 1 ret
@@ -57,6 +57,7 @@ module Spitewaste
57
57
  private
58
58
 
59
59
  def preprocess!
60
+ @src.prepend "import syntax\n"
60
61
  resolve_imports
61
62
  seed_prng if @seen.include? 'random'
62
63
  resolve_strings
@@ -94,7 +95,7 @@ module Spitewaste
94
95
  end
95
96
 
96
97
  def resolve_strings
97
- @src.gsub!(/"([^"]+)"/m) {
98
+ @src.gsub!(/"([^"]*)"/m) {
98
99
  [0, *$1.reverse.bytes] * ' push ' + ' :strpack'
99
100
  }
100
101
  end
@@ -118,31 +119,36 @@ module Spitewaste
118
119
 
119
120
  def fucktionalize
120
121
  # Iteratively remove pseudo-fp calls until we can't to allow nesting.
121
- 1 while @src.gsub!(/(#{FUCKTIONAL.keys * ?|})\s*\((.+)\)/) do
122
+ 1 while @src.gsub!(/(#{FUCKTIONAL.keys * ?|})\s*\((.+?)\)/m) do
122
123
  FUCKTIONAL[$1] % [gensym, $2]
123
124
  end
124
125
  end
125
126
 
126
127
  def propagate_macros
127
- @src.gsub!(/(\$\S+)\s*=\s*(.+)/) { @macros[$1] = $2; '' }
128
- @src.gsub!(/(\$\S+)/) { @macros[$1] || raise("no macro '#{$1}'") }
129
-
130
- # @src.gsub!(/(\$[^(]+)\s*\((.+)\)\s*{(.+)}/m) { @macros[$1] = $2, $3; '' }
131
- # @src.gsub!(/(\$[^(]+)\((.+?)\)/m) {
132
- # unless (params, body = @macros[$1])
133
- # raise "no macro function '#{$1}'"
134
- # end
135
-
136
- # params = params.split(?,).map &:strip
137
- # args = $2.split ?,
138
- # body.gsub /\b(#{params * ?|})\b/, params.zip(args).to_h
139
- # }
128
+ # Macros are write-once, allowing user code to customize the special
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
+
144
+ @src.gsub!(/(\$\S+)\s*=\s*(.+)/) { @macros[$1] ||= $2; '' }
145
+ @src.gsub!(/(\$\S+)/) { @macros[$1] || raise("no macro '#$1'") }
140
146
  end
141
147
 
142
148
  def eliminate_dead_code!
143
149
  tokens = @src.split
144
150
 
145
- # We need an entry point from which to begin determining which routines
151
+ # We need an entry point whence to begin determining which routines
146
152
  # are never invoked, but Whitespace programs aren't required to start
147
153
  # with a label. Here, we add an implcit "main" to the beginning of the
148
154
  # source unless it already contains an explicit entry point. TODO: better?
@@ -1,3 +1,3 @@
1
1
  module Spitewaste
2
- VERSION = '0.1.003'
2
+ VERSION = '0.1.008'
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.003
4
+ version: 0.1.008
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-13 00:00:00.000000000 Z
11
+ date: 2020-12-16 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,11 +100,13 @@ 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
105
107
  - lib/spitewaste/libspw/prime.spw
106
108
  - lib/spitewaste/libspw/random.spw
109
+ - lib/spitewaste/libspw/rational.spw
107
110
  - lib/spitewaste/libspw/stack.spw
108
111
  - lib/spitewaste/libspw/string.spw
109
112
  - lib/spitewaste/libspw/syntax.spw