spitewaste 0.1.002 → 0.1.007

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,41 @@
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
+ stoi: swap push 0 ; accumulator
38
+ _stoi_loop: ; [b s a]
22
39
  swap dup jz _stoi_done
23
40
  swap copy 2 copy 2
24
41
  :strlen push 1 sub :pow
@@ -27,24 +44,24 @@ _stoi_loop: ; [b s t]
27
44
  dup jn _stoi_invalid ; found something non-alphanumeric
28
45
  mul add swap push 128 div swap
29
46
  jump _stoi_loop
30
-
31
- _stoi_invalid: pop pop slide 1 swap div ret
32
-
47
+ _stoi_invalid: pop pop slide 1 swap div ret ; return what we were able to parse
33
48
  _stoi_done: swap slide 2 ret
34
49
 
35
- ;;;
36
-
50
+ ; creature comforts
37
51
  bin: push 2 :stoi ret
38
52
  oct: push 8 :stoi ret
39
53
  to_i: push 10 :stoi ret
40
54
  hex: push 16 :stoi ret
41
55
 
42
- ;;;
43
-
44
- ; integer to string in base b
45
- itos: ; [i b]
46
- swap push 0 ; empty string
47
-
56
+ ; converts the integer N to a string in base B
57
+ ; [N B]
58
+ ;
59
+ ; [42 2] => ["101010"]
60
+ ; [511 8] => ["777"]
61
+ ; [12345 10] => ["12345"]
62
+ ; [57005 16] => ["dead"]
63
+ ; [81699 17] => ["gabe"]
64
+ itos: swap push 0 ; accumulator
48
65
  _itos_loop:
49
66
  swap dup jz _itos_done
50
67
  swap copy 1 copy 3 mod
@@ -52,47 +69,86 @@ _itos_loop:
52
69
  swap :strcat
53
70
  swap copy 2 div
54
71
  swap jump _itos_loop
55
-
56
72
  _itos_done: swap slide 2 ret
57
73
 
58
- to_s: push 10 :itos ret
59
-
60
- ;;;
61
-
62
- digits: ; [ n d ]
74
+ ; creature comforts
75
+ to_bin: push 2 :itos ret
76
+ to_oct: push 8 :itos ret
77
+ to_s: push 10 :itos ret
78
+ to_hex: push 16 :itos ret
79
+
80
+ ; puts the digits of N in base B on the stack as a pseudo-array
81
+ ; [N B] => [Dn ...D0 n]
82
+ ;
83
+ ; [42 2] => [1 0 1 0 1 0 6]
84
+ ; [12345 10] => [1 2 3 4 5 5]
85
+ ; [255 16] => [15 15 2]
86
+ ; [256 16] => [1 0 0 3]
87
+ digits:
63
88
  copy 1 jz _digits_zero ; special case
64
89
  push -1 swap store
65
90
  push -1 swap ; sentinel value
66
-
67
91
  _digits_loop:
68
92
  dup jz _digits_done
69
93
  push -1 load :divmod
70
94
  swap jump _digits_loop
71
-
72
95
  _digits_zero: dup div ret
73
-
74
96
  _digits_done: push 1 sub :to_a ret
75
97
 
76
- ; increment value at heap address n
98
+ ; increments the value at heap address N TODO: make heap effects test-able ?
99
+ ; [N]
77
100
  inc: dup load $++ store ret
78
101
 
79
- ; decrement value at heap address n
102
+ ; decrements the value at heap address N
103
+ ; [N]
80
104
  dec: dup load $-- store ret
81
105
 
82
- ;;;
83
-
106
+ ; pops A and B and pushes 1 if they're equal, 0 otherwise
107
+ ; [A B] => [A == B]
108
+ ;
109
+ ; [1 2] => [0]
110
+ ; [3 3] => [1]
111
+ ; [-4 4] => [0]
112
+ ; ['A' 65] => [1]
113
+ ; ['B' 65] => [0]
84
114
  eq: sub jz _eq_yes push 0 ret
85
115
  _eq_yes: push 1 ret
86
116
 
117
+ ; pops A and B and pushes 1 if they're not equal, 0 otherwise
118
+ ; [A B] => [A != B]
119
+ ;
120
+ ; [1 2] => [1]
121
+ ; [3 3] => [0]
122
+ ; [-4 4] => [1]
123
+ ; ['A' 65] => [0]
124
+ ; ['B' 65] => [1]
87
125
  neq: sub jz _neq_no push 1 ret
88
126
  _neq_no: push 0 ret
89
127
 
90
- gt: swap
128
+ ; pops A and B and pushes 1 if A is greater than B, 0 otherwise
129
+ ; [A B] => [A > B]
130
+ ;
131
+ ; [4 3] => [1]
132
+ ; [3 4] => [0]
133
+ ; [2 2] => [0]
134
+ ; [2 1] => [1]
135
+ gt: swap ; intentionally flow into lt
136
+
137
+ ; pops A and B and pushes 1 if A is less than than B, 0 otherwise
138
+ ; [A B] => [A < B]
139
+ ;
140
+ ; [3 4] => [1]
141
+ ; [4 3] => [0]
142
+ ; [2 2] => [0]
143
+ ; [1 2] => [1]
91
144
  lt: sub jn _lt_yes push 0 ret
92
145
  _lt_yes: push 1 ret
93
146
 
94
- ;;;
95
-
96
- heap_search:
147
+ ; Though extremely rare, it's possible that we know a particular value is
148
+ ; stored in the heap at some key, just not which one. This subroutine takes
149
+ ; a value V to search for and a starting index I, and either returns the first
150
+ ; key associated with that value or loops forever. Probably don't touch.
151
+ ; [V I]
152
+ heap_seeking_missile:
97
153
  $++ dup load copy 2 :eq jz heap_search
98
154
  slide 1 ret
@@ -19,7 +19,7 @@ module Spitewaste
19
19
  @symbol_file = options['symbol_file']
20
20
 
21
21
  preprocess!
22
- eliminate_dead_code!
22
+ eliminate_dead_code! unless options[:keep_dead_code]
23
23
  end
24
24
 
25
25
  def parse
@@ -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.002'
2
+ VERSION = '0.1.007'
3
3
  end
@@ -1,17 +1,22 @@
1
1
  require_relative 'lib/spitewaste/version'
2
2
 
3
- Gem::Specification.new do |spec|
4
- spec.name = 'spitewaste'
5
- spec.version = Spitewaste::VERSION
6
- spec.author = 'Collided Scope (collidedscope)'
3
+ Gem::Specification.new do |s|
4
+ s.name = 'spitewaste'
5
+ s.version = Spitewaste::VERSION
6
+ s.author = 'Collided Scope (collidedscope)'
7
7
 
8
- spec.summary = 'Make programming in Whitespace even better.'
9
- spec.description = 'Spitewaste is a collection of tools that makes it almost too easy to write Whitespace.'
10
- spec.homepage = 'https://github.com/collidedscope/spitewaste'
11
- spec.license = 'WTFPL'
8
+ s.summary = 'Make programming in Whitespace even better.'
9
+ s.description = 'Spitewaste is a collection of tools that makes it almost too easy to read and write Whitespace programs.'
10
+ s.homepage = 'https://github.com/collidedscope/spitewaste'
11
+ s.license = 'WTFPL'
12
12
 
13
- spec.files = `git ls-files`.split.reject { |f| f[/^test/] }
14
- spec.bindir = 'bin'
15
- spec.executables = ['spw']
16
- spec.require_paths = ['lib']
13
+ s.add_dependency 'rake', '~> 13.0.1'
14
+ s.add_dependency 'minitest', '~> 5.14.2'
15
+ s.add_dependency 'thor', '~> 1.0.1'
16
+ s.add_dependency 'oily_png', '~> 1.2.1'
17
+
18
+ s.files = `git ls-files`.split.reject { |f| f[/^(test|demo)/] }
19
+ s.bindir = 'bin'
20
+ s.executables = ['spw']
21
+ s.require_paths = ['lib']
17
22
  end
metadata CHANGED
@@ -1,17 +1,73 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spitewaste
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.002
4
+ version: 0.1.007
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
12
- dependencies: []
11
+ date: 2020-12-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 13.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 13.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 5.14.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 5.14.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: oily_png
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.2.1
13
69
  description: Spitewaste is a collection of tools that makes it almost too easy to
14
- write Whitespace.
70
+ read and write Whitespace programs.
15
71
  email:
16
72
  executables:
17
73
  - spw
@@ -24,16 +80,13 @@ files:
24
80
  - Rakefile
25
81
  - TUTORIAL.md
26
82
  - bin/spw
27
- - demo/factorial-nicespace.png
28
- - demo/factorial.asm
29
- - demo/factorial.png
30
- - demo/factorial.wsa
31
83
  - lib/spitewaste.rb
32
84
  - lib/spitewaste/assembler.rb
33
85
  - lib/spitewaste/cli.rb
34
86
  - lib/spitewaste/cli/asm.rb
35
87
  - lib/spitewaste/cli/compile.rb
36
88
  - lib/spitewaste/cli/convert.rb
89
+ - lib/spitewaste/cli/docs.rb
37
90
  - lib/spitewaste/cli/exec.rb
38
91
  - lib/spitewaste/cli/image.rb
39
92
  - lib/spitewaste/emitter.rb
@@ -47,11 +100,13 @@ files:
47
100
  - lib/spitewaste/libspw/array.spw
48
101
  - lib/spitewaste/libspw/bits.spw
49
102
  - lib/spitewaste/libspw/case.spw
103
+ - lib/spitewaste/libspw/docs.json
50
104
  - lib/spitewaste/libspw/fun.spw
51
105
  - lib/spitewaste/libspw/io.spw
52
106
  - lib/spitewaste/libspw/math.spw
53
107
  - lib/spitewaste/libspw/prime.spw
54
108
  - lib/spitewaste/libspw/random.spw
109
+ - lib/spitewaste/libspw/rational.spw
55
110
  - lib/spitewaste/libspw/stack.spw
56
111
  - lib/spitewaste/libspw/string.spw
57
112
  - lib/spitewaste/libspw/syntax.spw
Binary file
@@ -1,47 +0,0 @@
1
- label 0
2
- push 6
3
- call 1
4
- onum
5
- exit
6
- label 1
7
- push 1
8
- swap
9
- call 4
10
- dup
11
- dup
12
- dup
13
- push 2
14
- sub
15
- jn 3
16
- pop
17
- push -9
18
- swap
19
- push 1
20
- sub
21
- store
22
- label 2
23
- mul
24
- push -9
25
- dup
26
- load
27
- push 1
28
- sub
29
- dup
30
- jz 3
31
- store
32
- jump 2
33
- label 3
34
- pop
35
- pop
36
- ret
37
- label 4
38
- copy 1
39
- push 1
40
- add
41
- swap
42
- copy 1
43
- copy 1
44
- sub
45
- jn 4
46
- pop
47
- ret