spitewaste 0.1.001 → 0.1.006
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -6
- data/LICENSE +13 -0
- data/README.md +2 -5
- data/Rakefile +75 -2
- data/TUTORIAL.md +1 -1
- data/bin/spw +1 -0
- data/lib/spitewaste.rb +1 -1
- data/lib/spitewaste/assembler.rb +4 -4
- data/lib/spitewaste/cli.rb +21 -19
- data/lib/spitewaste/cli/asm.rb +2 -0
- data/lib/spitewaste/cli/compile.rb +2 -0
- data/lib/spitewaste/cli/convert.rb +2 -0
- data/lib/spitewaste/cli/docs.rb +51 -0
- data/lib/spitewaste/cli/exec.rb +12 -13
- data/lib/spitewaste/cli/image.rb +2 -0
- data/lib/spitewaste/emitters/assembly.rb +1 -1
- data/lib/spitewaste/libspw/array.spw +75 -20
- data/lib/spitewaste/libspw/bits.spw +70 -68
- data/lib/spitewaste/libspw/case.spw +34 -26
- data/lib/spitewaste/libspw/docs.json +1 -0
- data/lib/spitewaste/libspw/fun.spw +40 -24
- data/lib/spitewaste/libspw/io.spw +2 -0
- data/lib/spitewaste/libspw/math.spw +120 -79
- data/lib/spitewaste/libspw/prime.spw +3 -2
- data/lib/spitewaste/libspw/random.spw +2 -0
- data/lib/spitewaste/libspw/rational.spw +153 -0
- data/lib/spitewaste/libspw/stack.spw +54 -30
- data/lib/spitewaste/libspw/string.spw +228 -92
- data/lib/spitewaste/libspw/test.spw +3 -0
- data/lib/spitewaste/libspw/util.spw +97 -41
- data/lib/spitewaste/parsers/fucktional.rb +1 -0
- data/lib/spitewaste/parsers/spitewaste.rb +25 -19
- data/lib/spitewaste/version.rb +1 -1
- data/spitewaste.gemspec +17 -12
- metadata +64 -8
- data/demo/factorial-nicespace.png +0 -0
- data/demo/factorial.asm +0 -47
- data/demo/factorial.png +0 -0
- data/demo/factorial.wsa +0 -5
@@ -1,24 +1,41 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
;
|
45
|
-
|
46
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
;
|
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
|
-
;
|
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
|
-
|
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
|
-
|
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
|
@@ -66,6 +66,7 @@ SPW
|
|
66
66
|
|
67
67
|
'minby' => 'maxby (%2$s push -1 mul)',
|
68
68
|
'each' => 'dup times (dup call roll %2$s push 1 sub) pop',
|
69
|
+
'count' => 'select (%2$s) dup call nslide',
|
69
70
|
'select' => generate_filter_spw('select', 0, 1),
|
70
71
|
'reject' => generate_filter_spw('reject', 1, 0),
|
71
72
|
}
|
@@ -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,13 +95,13 @@ module Spitewaste
|
|
94
95
|
end
|
95
96
|
|
96
97
|
def resolve_strings
|
97
|
-
@src.gsub!(/"([^"]
|
98
|
+
@src.gsub!(/"([^"]*)"/m) {
|
98
99
|
[0, *$1.reverse.bytes] * ' push ' + ' :strpack'
|
99
100
|
}
|
100
101
|
end
|
101
102
|
|
102
103
|
def remove_comments
|
103
|
-
@src.gsub!(
|
104
|
+
@src.gsub!(/;.*/, '')
|
104
105
|
end
|
105
106
|
|
106
107
|
def add_sugar
|
@@ -109,7 +110,7 @@ module Spitewaste
|
|
109
110
|
# character literals
|
110
111
|
@src.gsub!(/'(.)'/) { $1.ord }
|
111
112
|
# quick push (`push 1,2,3` desugars to individual pushes)
|
112
|
-
@src.gsub!(/push \S+/) {
|
113
|
+
@src.gsub!(/push \S+/) { |m| m.split(?,) * ' push ' }
|
113
114
|
end
|
114
115
|
|
115
116
|
def gensym
|
@@ -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*\((
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
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?
|
data/lib/spitewaste/version.rb
CHANGED
data/spitewaste.gemspec
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
require_relative 'lib/spitewaste/version'
|
2
2
|
|
3
|
-
Gem::Specification.new do |
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'spitewaste'
|
5
|
+
s.version = Spitewaste::VERSION
|
6
|
+
s.author = 'Collided Scope (collidedscope)'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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.
|
4
|
+
version: 0.1.006
|
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-
|
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
|
@@ -19,20 +75,18 @@ extensions: []
|
|
19
75
|
extra_rdoc_files: []
|
20
76
|
files:
|
21
77
|
- Gemfile
|
78
|
+
- LICENSE
|
22
79
|
- README.md
|
23
80
|
- Rakefile
|
24
81
|
- TUTORIAL.md
|
25
82
|
- bin/spw
|
26
|
-
- demo/factorial-nicespace.png
|
27
|
-
- demo/factorial.asm
|
28
|
-
- demo/factorial.png
|
29
|
-
- demo/factorial.wsa
|
30
83
|
- lib/spitewaste.rb
|
31
84
|
- lib/spitewaste/assembler.rb
|
32
85
|
- lib/spitewaste/cli.rb
|
33
86
|
- lib/spitewaste/cli/asm.rb
|
34
87
|
- lib/spitewaste/cli/compile.rb
|
35
88
|
- lib/spitewaste/cli/convert.rb
|
89
|
+
- lib/spitewaste/cli/docs.rb
|
36
90
|
- lib/spitewaste/cli/exec.rb
|
37
91
|
- lib/spitewaste/cli/image.rb
|
38
92
|
- lib/spitewaste/emitter.rb
|
@@ -46,11 +100,13 @@ files:
|
|
46
100
|
- lib/spitewaste/libspw/array.spw
|
47
101
|
- lib/spitewaste/libspw/bits.spw
|
48
102
|
- lib/spitewaste/libspw/case.spw
|
103
|
+
- lib/spitewaste/libspw/docs.json
|
49
104
|
- lib/spitewaste/libspw/fun.spw
|
50
105
|
- lib/spitewaste/libspw/io.spw
|
51
106
|
- lib/spitewaste/libspw/math.spw
|
52
107
|
- lib/spitewaste/libspw/prime.spw
|
53
108
|
- lib/spitewaste/libspw/random.spw
|
109
|
+
- lib/spitewaste/libspw/rational.spw
|
54
110
|
- lib/spitewaste/libspw/stack.spw
|
55
111
|
- lib/spitewaste/libspw/string.spw
|
56
112
|
- lib/spitewaste/libspw/syntax.spw
|