spitewaste 0.1.001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/README.md +55 -0
- data/Rakefile +10 -0
- data/TUTORIAL.md +125 -0
- data/bin/spw +10 -0
- data/demo/factorial-nicespace.png +0 -0
- data/demo/factorial.asm +47 -0
- data/demo/factorial.png +0 -0
- data/demo/factorial.wsa +5 -0
- data/lib/spitewaste.rb +35 -0
- data/lib/spitewaste/assembler.rb +56 -0
- data/lib/spitewaste/cli.rb +51 -0
- data/lib/spitewaste/cli/asm.rb +10 -0
- data/lib/spitewaste/cli/compile.rb +60 -0
- data/lib/spitewaste/cli/convert.rb +53 -0
- data/lib/spitewaste/cli/exec.rb +43 -0
- data/lib/spitewaste/cli/image.rb +53 -0
- data/lib/spitewaste/emitter.rb +10 -0
- data/lib/spitewaste/emitters/assembly.rb +7 -0
- data/lib/spitewaste/emitters/codegen.rb +72 -0
- data/lib/spitewaste/emitters/image.rb +135 -0
- data/lib/spitewaste/emitters/linefeed.png +0 -0
- data/lib/spitewaste/emitters/schemes.yaml +1143 -0
- data/lib/spitewaste/emitters/whitespace.rb +14 -0
- data/lib/spitewaste/emitters/wsassembly.rb +7 -0
- data/lib/spitewaste/libspw/array.spw +82 -0
- data/lib/spitewaste/libspw/bits.spw +72 -0
- data/lib/spitewaste/libspw/case.spw +32 -0
- data/lib/spitewaste/libspw/fun.spw +42 -0
- data/lib/spitewaste/libspw/io.spw +39 -0
- data/lib/spitewaste/libspw/math.spw +117 -0
- data/lib/spitewaste/libspw/prime.spw +46 -0
- data/lib/spitewaste/libspw/random.spw +10 -0
- data/lib/spitewaste/libspw/stack.spw +84 -0
- data/lib/spitewaste/libspw/string.spw +233 -0
- data/lib/spitewaste/libspw/syntax.spw +2 -0
- data/lib/spitewaste/libspw/test.spw +8 -0
- data/lib/spitewaste/libspw/util.spw +98 -0
- data/lib/spitewaste/parsers/assembly.rb +35 -0
- data/lib/spitewaste/parsers/fucktional.rb +72 -0
- data/lib/spitewaste/parsers/spitewaste.rb +192 -0
- data/lib/spitewaste/parsers/whitespace.rb +60 -0
- data/lib/spitewaste/version.rb +3 -0
- data/spitewaste.gemspec +17 -0
- metadata +88 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
roll:
|
2
|
+
push -10 dup store ; current heap index kept at -10
|
3
|
+
|
4
|
+
_roll_keep: ; [n]
|
5
|
+
dup jz _roll_remove
|
6
|
+
push -10 :dec
|
7
|
+
swap push -10 load swap store
|
8
|
+
push 1 sub jump _roll_keep
|
9
|
+
|
10
|
+
_roll_remove:
|
11
|
+
push 10 sub load
|
12
|
+
swap push -10 swap store
|
13
|
+
|
14
|
+
_roll_restore: ; i
|
15
|
+
dup load swap push 1 add
|
16
|
+
dup push 10 add jz _roll_done
|
17
|
+
jump _roll_restore
|
18
|
+
|
19
|
+
_roll_done: load ret
|
20
|
+
|
21
|
+
;;;
|
22
|
+
|
23
|
+
bury:
|
24
|
+
push -10 dup store ; current heap index kept at -10
|
25
|
+
swap push -9 swap store ; preserve element to bury
|
26
|
+
|
27
|
+
_bury_keep: ; [n]
|
28
|
+
dup jz _bury_restore
|
29
|
+
push -10 :dec
|
30
|
+
swap push -10 load swap store
|
31
|
+
push 1 sub jump _bury_keep
|
32
|
+
|
33
|
+
_bury_restore:
|
34
|
+
push 9 sub load
|
35
|
+
push -10 load :_roll_restore pop ret
|
36
|
+
|
37
|
+
;;;
|
38
|
+
|
39
|
+
dig: :roll pop ret
|
40
|
+
|
41
|
+
;;;
|
42
|
+
|
43
|
+
to_a:
|
44
|
+
push -1 swap store
|
45
|
+
push -10 dup store
|
46
|
+
|
47
|
+
_to_a_loop:
|
48
|
+
dup push -1 load sub jz _to_a_sentinel
|
49
|
+
push -10 dup :dec load
|
50
|
+
swap store jump _to_a_loop
|
51
|
+
|
52
|
+
_to_a_sentinel: pop push -10
|
53
|
+
|
54
|
+
_to_a_restore:
|
55
|
+
dup push -10 load sub jz _to_a_done
|
56
|
+
push 1 sub dup load swap
|
57
|
+
jump _to_a_restore
|
58
|
+
|
59
|
+
_to_a_done: push -10 swap sub ret
|
60
|
+
|
61
|
+
;;;
|
62
|
+
|
63
|
+
; dynamic pop ; [n]
|
64
|
+
npop:
|
65
|
+
dup jz _npop_done
|
66
|
+
swap pop push 1 sub jump npop
|
67
|
+
|
68
|
+
_npop_done: pop ret
|
69
|
+
|
70
|
+
; dynamic slide ; [n]
|
71
|
+
nslide:
|
72
|
+
swap push -1 swap store
|
73
|
+
:npop push -1 load ret
|
74
|
+
|
75
|
+
ncopy: push -10 dup store
|
76
|
+
_ncopy_loop:
|
77
|
+
dup jz _ncopy_save swap
|
78
|
+
push -10 dup :dec load swap store
|
79
|
+
push 1 sub jump _ncopy_loop
|
80
|
+
_ncopy_save: push 10 sub dup load swap copy 2 store
|
81
|
+
_ncopy_restore:
|
82
|
+
dup push 9 add jz _ncopy_done
|
83
|
+
dup load swap push 1 add jump _ncopy_restore
|
84
|
+
_ncopy_done: pop ret
|
@@ -0,0 +1,233 @@
|
|
1
|
+
import math
|
2
|
+
import case
|
3
|
+
|
4
|
+
;;; String packing and unpacking
|
5
|
+
|
6
|
+
; convert a 0-terminated string on the stack to a single base-128 integer
|
7
|
+
strpack: push 0 ; tally
|
8
|
+
_strpack_loop:
|
9
|
+
swap dup jz _strpack_done
|
10
|
+
copy 1 push 128 mul add
|
11
|
+
slide 1
|
12
|
+
jump _strpack_loop
|
13
|
+
|
14
|
+
_strpack_done: pop :strrev ret
|
15
|
+
|
16
|
+
; convert a single base-128 integer to a 0-terminated string on the stack
|
17
|
+
strunpack:
|
18
|
+
:strrev push 0 swap ; terminator
|
19
|
+
|
20
|
+
_strunpack_loop:
|
21
|
+
dup jz _strunpack_done
|
22
|
+
dup push 128 mod swap push 128 div
|
23
|
+
jump _strunpack_loop
|
24
|
+
|
25
|
+
_strunpack_done: pop ret
|
26
|
+
|
27
|
+
;;;
|
28
|
+
|
29
|
+
; takes a packed string and just returns it log128 to give the length
|
30
|
+
strlen: push 128 :ilog ret
|
31
|
+
|
32
|
+
;;;
|
33
|
+
|
34
|
+
; takes two packed strings and returns their concatenation (as a packed string)
|
35
|
+
strcat:
|
36
|
+
push 128 copy 2 :strlen :pow
|
37
|
+
mul add ret
|
38
|
+
|
39
|
+
;;;
|
40
|
+
|
41
|
+
; reverses a packed string in-place (heapless) [s] => [s.reverse]
|
42
|
+
strrev: push 0 swap ; [tally string]
|
43
|
+
|
44
|
+
_strrev_loop: ; [t s]
|
45
|
+
dup jz _strrev_done
|
46
|
+
swap push 128 mul ; [s t]
|
47
|
+
copy 1 push 128 mod add
|
48
|
+
swap push 128 div
|
49
|
+
jump _strrev_loop
|
50
|
+
|
51
|
+
_strrev_done: pop ret
|
52
|
+
|
53
|
+
;;;
|
54
|
+
|
55
|
+
; takes a packed string, a start index, and a length and returns the
|
56
|
+
; corresponding substring (simply by doing division with powers of 128; neat)
|
57
|
+
strslice:
|
58
|
+
swap push 128 swap :pow
|
59
|
+
copy 2 swap div
|
60
|
+
swap push 128 swap :pow
|
61
|
+
mod slide 1 ret
|
62
|
+
|
63
|
+
;;;
|
64
|
+
|
65
|
+
; index of substring t in string s ; [s t]
|
66
|
+
strindex: swap push 0
|
67
|
+
|
68
|
+
_strindex_loop: ; [t s i]
|
69
|
+
copy 1 copy 3 :strlen push 0 swap :strslice
|
70
|
+
copy 3 sub jz _strindex_found
|
71
|
+
push 1 add ; increment index
|
72
|
+
swap push 128 div dup jz _strindex_no
|
73
|
+
swap jump _strindex_loop
|
74
|
+
|
75
|
+
_strindex_no: push -1 slide 3 ret
|
76
|
+
|
77
|
+
_strindex_found: slide 2 ret
|
78
|
+
|
79
|
+
;;;
|
80
|
+
|
81
|
+
charat: push 1 :strslice ret
|
82
|
+
|
83
|
+
;;;
|
84
|
+
|
85
|
+
; 1 if the character at the top of the stack is alphabetical, 0 otherwise
|
86
|
+
isalpha:
|
87
|
+
dup push 123 :lt jz _isalpha_no
|
88
|
+
dup push 64 :gt jz _isalpha_no
|
89
|
+
push 32 mod $-- push 32 mod push 26 :lt ret
|
90
|
+
_isalpha_no: dup sub ret
|
91
|
+
|
92
|
+
;;;
|
93
|
+
|
94
|
+
; repeat a string s n times [s n]
|
95
|
+
strrep: push 0 swap
|
96
|
+
_strrep_loop: ; [s t n]
|
97
|
+
dup jz _strrep_done
|
98
|
+
swap copy 2 :strcat
|
99
|
+
swap push 1 sub
|
100
|
+
jump _strrep_loop
|
101
|
+
|
102
|
+
_strrep_done: swap slide 2 ret
|
103
|
+
|
104
|
+
;;; String alignment
|
105
|
+
|
106
|
+
; helper function for ljustc and rjustc, since the only difference is whether
|
107
|
+
; we swap before calling strcat.
|
108
|
+
_justc: swap copy 2 :strlen sub push 0 :max :strrep ret
|
109
|
+
|
110
|
+
; Left-justify string s to width w with character c. [s w c]
|
111
|
+
ljustc: :_justc :strcat ret
|
112
|
+
|
113
|
+
; Left-justify string s to width w with spaces. [s w]
|
114
|
+
ljust: push ' ' :ljustc ret
|
115
|
+
|
116
|
+
; Right-justify string s to width w with character c. [s w c]
|
117
|
+
rjustc: :_justc swap :strcat ret
|
118
|
+
|
119
|
+
; Right-justify string s to width w with spaces. [s w]
|
120
|
+
rjust: push ' ' :rjustc ret
|
121
|
+
|
122
|
+
; Center string s to width w with character c. [s w c]
|
123
|
+
centerc:
|
124
|
+
swap dup copy 3 :strlen sub
|
125
|
+
push 0 :max push 2 div
|
126
|
+
copy 2 swap :strrep
|
127
|
+
copy 3 :strcat
|
128
|
+
swap copy 1 :strlen sub
|
129
|
+
push 0 :max
|
130
|
+
copy 2 swap :strrep :strcat
|
131
|
+
slide 2 ret
|
132
|
+
|
133
|
+
; Center string s to width w with spaces. [s w]
|
134
|
+
center: push ' ' :centerc ret
|
135
|
+
|
136
|
+
;;;
|
137
|
+
|
138
|
+
; remove the last character of a string
|
139
|
+
strchop:
|
140
|
+
dup :strlen push 1 sub
|
141
|
+
push 0 swap :strslice ret
|
142
|
+
|
143
|
+
; Split string s on delimiting character d.
|
144
|
+
; ! clobbers heap addresses -1 and -2
|
145
|
+
strsplit: ; [s d]
|
146
|
+
push -3 push 1 store ; number of found substrings
|
147
|
+
push -2 swap store ; stash delimiter to allow some stack juggling
|
148
|
+
_strsplit_loop: ; [s]
|
149
|
+
dup dup push -2 load
|
150
|
+
:strindex dup jn _strsplit_done ; done when index of delimiter is -1
|
151
|
+
push 0 swap :strslice
|
152
|
+
swap copy 1 push -3 load
|
153
|
+
swap :strlen
|
154
|
+
swap push -3 swap push 1 add store ; update number of found
|
155
|
+
push 1 add push 128 swap :pow div ; shrink haystack
|
156
|
+
jump _strsplit_loop
|
157
|
+
|
158
|
+
_strsplit_done: push 2 sub slide 1 load ret
|
159
|
+
|
160
|
+
;;;
|
161
|
+
|
162
|
+
; [...strs n delim]
|
163
|
+
; ! clobbers heap address -2 (strlen uses -1)
|
164
|
+
strjoinc:
|
165
|
+
dup :strlen pop ; get delimiter length into -1
|
166
|
+
push -2 swap store
|
167
|
+
map (push -2 load :strcat) ; add delimiter to all elements
|
168
|
+
swap push 128 copy 1 :strlen
|
169
|
+
push -2 load :strlen
|
170
|
+
sub :pow mod swap ; remove delimiter from last and flow
|
171
|
+
strjoin: reduce (:strcat) ret
|
172
|
+
|
173
|
+
;;;
|
174
|
+
|
175
|
+
; return the number of ocurrences of character c in string s [s c]
|
176
|
+
strcountc: swap push 0 swap
|
177
|
+
|
178
|
+
_strcountc_loop: ; c t s
|
179
|
+
dup jz _strcountc_done
|
180
|
+
dup push 128 mod copy 3 sub jz _strcountc_yes
|
181
|
+
push 128 div jump _strcountc_loop
|
182
|
+
|
183
|
+
_strcountc_yes:
|
184
|
+
swap push 1 add swap push 128 div
|
185
|
+
jump _strcountc_loop
|
186
|
+
|
187
|
+
_strcountc_done: swap slide 2 ret
|
188
|
+
|
189
|
+
; number of ocurrences of all characters in string c [s c]
|
190
|
+
; ! clobbers heap address -2
|
191
|
+
strcount:
|
192
|
+
swap push -2 swap store
|
193
|
+
:strunpack push 0 :to_a
|
194
|
+
map (push -2 load swap :strcountc)
|
195
|
+
reduce (add) ret
|
196
|
+
|
197
|
+
;;;
|
198
|
+
|
199
|
+
; Convert all characters in src to the corresponding characters in dest.
|
200
|
+
; ! clobbers heap addresses -1, -2, and -3
|
201
|
+
strtrans: ; [string src dest]
|
202
|
+
push -3 swap store
|
203
|
+
push -2 swap store
|
204
|
+
dup :strlen push -1 swap store
|
205
|
+
:strunpack push -1 load
|
206
|
+
map (:_strtrans) pop
|
207
|
+
:strpack ret
|
208
|
+
|
209
|
+
_strtrans:
|
210
|
+
dup push -2 load swap :strindex
|
211
|
+
dup jn _strtrans_no
|
212
|
+
push -3 load swap :charat
|
213
|
+
slide 1 ret
|
214
|
+
|
215
|
+
_strtrans_no: pop ret
|
216
|
+
|
217
|
+
strexpand:
|
218
|
+
push 0 swap push 128 :divmod
|
219
|
+
swap :range :strpack :strrev ret
|
220
|
+
|
221
|
+
strsqueeze: push 0 swap ; [sentinel s]
|
222
|
+
_strsqueeze_loop: ; [s]
|
223
|
+
dup jz _strsqueeze_done
|
224
|
+
push 128 :divmod dup copy 3 sub jz _strsqueeze_skip
|
225
|
+
swap jump _strsqueeze_loop
|
226
|
+
|
227
|
+
_strsqueeze_skip: pop jump _strsqueeze_loop
|
228
|
+
|
229
|
+
_strsqueeze_done: pop :strpack :strrev ret
|
230
|
+
|
231
|
+
strsum: :strunpack push 0 :to_a :arysum ret
|
232
|
+
|
233
|
+
lines: push 10 :strsplit ret
|
@@ -0,0 +1,98 @@
|
|
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]
|
5
|
+
range:
|
6
|
+
copy 1 push 1 add swap
|
7
|
+
copy 1 copy 1 sub jn range
|
8
|
+
pop ret
|
9
|
+
|
10
|
+
;;;
|
11
|
+
|
12
|
+
die: :println exit
|
13
|
+
;;;
|
14
|
+
|
15
|
+
alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
|
16
|
+
|
17
|
+
; string to integer in base b
|
18
|
+
stoi: ; [s b]
|
19
|
+
swap push 0 ; tally
|
20
|
+
|
21
|
+
_stoi_loop: ; [b s t]
|
22
|
+
swap dup jz _stoi_done
|
23
|
+
swap copy 2 copy 2
|
24
|
+
:strlen push 1 sub :pow
|
25
|
+
copy 2 push 128 mod
|
26
|
+
:alpha swap :strindex
|
27
|
+
dup jn _stoi_invalid ; found something non-alphanumeric
|
28
|
+
mul add swap push 128 div swap
|
29
|
+
jump _stoi_loop
|
30
|
+
|
31
|
+
_stoi_invalid: pop pop slide 1 swap div ret
|
32
|
+
|
33
|
+
_stoi_done: swap slide 2 ret
|
34
|
+
|
35
|
+
;;;
|
36
|
+
|
37
|
+
bin: push 2 :stoi ret
|
38
|
+
oct: push 8 :stoi ret
|
39
|
+
to_i: push 10 :stoi ret
|
40
|
+
hex: push 16 :stoi ret
|
41
|
+
|
42
|
+
;;;
|
43
|
+
|
44
|
+
; integer to string in base b
|
45
|
+
itos: ; [i b]
|
46
|
+
swap push 0 ; empty string
|
47
|
+
|
48
|
+
_itos_loop:
|
49
|
+
swap dup jz _itos_done
|
50
|
+
swap copy 1 copy 3 mod
|
51
|
+
:alpha swap :charat
|
52
|
+
swap :strcat
|
53
|
+
swap copy 2 div
|
54
|
+
swap jump _itos_loop
|
55
|
+
|
56
|
+
_itos_done: swap slide 2 ret
|
57
|
+
|
58
|
+
to_s: push 10 :itos ret
|
59
|
+
|
60
|
+
;;;
|
61
|
+
|
62
|
+
digits: ; [ n d ]
|
63
|
+
copy 1 jz _digits_zero ; special case
|
64
|
+
push -1 swap store
|
65
|
+
push -1 swap ; sentinel value
|
66
|
+
|
67
|
+
_digits_loop:
|
68
|
+
dup jz _digits_done
|
69
|
+
push -1 load :divmod
|
70
|
+
swap jump _digits_loop
|
71
|
+
|
72
|
+
_digits_zero: dup div ret
|
73
|
+
|
74
|
+
_digits_done: push 1 sub :to_a ret
|
75
|
+
|
76
|
+
; increment value at heap address n
|
77
|
+
inc: dup load $++ store ret
|
78
|
+
|
79
|
+
; decrement value at heap address n
|
80
|
+
dec: dup load $-- store ret
|
81
|
+
|
82
|
+
;;;
|
83
|
+
|
84
|
+
eq: sub jz _eq_yes push 0 ret
|
85
|
+
_eq_yes: push 1 ret
|
86
|
+
|
87
|
+
neq: sub jz _neq_no push 1 ret
|
88
|
+
_neq_no: push 0 ret
|
89
|
+
|
90
|
+
gt: swap
|
91
|
+
lt: sub jn _lt_yes push 0 ret
|
92
|
+
_lt_yes: push 1 ret
|
93
|
+
|
94
|
+
;;;
|
95
|
+
|
96
|
+
heap_search:
|
97
|
+
$++ dup load copy 2 :eq jz heap_search
|
98
|
+
slide 1 ret
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Spitewaste
|
2
|
+
class AssemblyParser
|
3
|
+
OperatorError = Class.new Exception
|
4
|
+
SyntaxError = Class.new Exception
|
5
|
+
|
6
|
+
attr_reader :instructions, :error
|
7
|
+
|
8
|
+
def initialize program, **options
|
9
|
+
@program = program
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
mnemonics = OPERATORS_M2T.keys
|
14
|
+
@instructions = @program.lines.map.with_index(1) { |insn, no|
|
15
|
+
op, arg = insn.split
|
16
|
+
|
17
|
+
unless i = mnemonics.index(op = op.to_sym)
|
18
|
+
@error = [:unknown, op, [i, 1]]
|
19
|
+
raise OperatorError, "unknown operator '#{op}' (line #{no})", []
|
20
|
+
end
|
21
|
+
|
22
|
+
bad_arg = -> kind {
|
23
|
+
@error = [kind, op, [i, 1]]
|
24
|
+
raise SyntaxError, "#{kind} argument for #{op} operator (line #{no})", []
|
25
|
+
}
|
26
|
+
|
27
|
+
bad_arg[:missing] if i < 8 && !arg
|
28
|
+
bad_arg[:invalid] if i < 8 && !arg[/^-?\d+$/]
|
29
|
+
bad_arg[:useless] if i > 7 && arg
|
30
|
+
|
31
|
+
[op, arg && arg.to_i]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|