spitewaste 0.1.001
Sign up to get free protection for your applications and to get access to all the features.
- 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
|