spitewaste 0.1.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -0
  3. data/README.md +55 -0
  4. data/Rakefile +10 -0
  5. data/TUTORIAL.md +125 -0
  6. data/bin/spw +10 -0
  7. data/demo/factorial-nicespace.png +0 -0
  8. data/demo/factorial.asm +47 -0
  9. data/demo/factorial.png +0 -0
  10. data/demo/factorial.wsa +5 -0
  11. data/lib/spitewaste.rb +35 -0
  12. data/lib/spitewaste/assembler.rb +56 -0
  13. data/lib/spitewaste/cli.rb +51 -0
  14. data/lib/spitewaste/cli/asm.rb +10 -0
  15. data/lib/spitewaste/cli/compile.rb +60 -0
  16. data/lib/spitewaste/cli/convert.rb +53 -0
  17. data/lib/spitewaste/cli/exec.rb +43 -0
  18. data/lib/spitewaste/cli/image.rb +53 -0
  19. data/lib/spitewaste/emitter.rb +10 -0
  20. data/lib/spitewaste/emitters/assembly.rb +7 -0
  21. data/lib/spitewaste/emitters/codegen.rb +72 -0
  22. data/lib/spitewaste/emitters/image.rb +135 -0
  23. data/lib/spitewaste/emitters/linefeed.png +0 -0
  24. data/lib/spitewaste/emitters/schemes.yaml +1143 -0
  25. data/lib/spitewaste/emitters/whitespace.rb +14 -0
  26. data/lib/spitewaste/emitters/wsassembly.rb +7 -0
  27. data/lib/spitewaste/libspw/array.spw +82 -0
  28. data/lib/spitewaste/libspw/bits.spw +72 -0
  29. data/lib/spitewaste/libspw/case.spw +32 -0
  30. data/lib/spitewaste/libspw/fun.spw +42 -0
  31. data/lib/spitewaste/libspw/io.spw +39 -0
  32. data/lib/spitewaste/libspw/math.spw +117 -0
  33. data/lib/spitewaste/libspw/prime.spw +46 -0
  34. data/lib/spitewaste/libspw/random.spw +10 -0
  35. data/lib/spitewaste/libspw/stack.spw +84 -0
  36. data/lib/spitewaste/libspw/string.spw +233 -0
  37. data/lib/spitewaste/libspw/syntax.spw +2 -0
  38. data/lib/spitewaste/libspw/test.spw +8 -0
  39. data/lib/spitewaste/libspw/util.spw +98 -0
  40. data/lib/spitewaste/parsers/assembly.rb +35 -0
  41. data/lib/spitewaste/parsers/fucktional.rb +72 -0
  42. data/lib/spitewaste/parsers/spitewaste.rb +192 -0
  43. data/lib/spitewaste/parsers/whitespace.rb +60 -0
  44. data/lib/spitewaste/version.rb +3 -0
  45. data/spitewaste.gemspec +17 -0
  46. metadata +88 -0
@@ -0,0 +1,10 @@
1
+ srand: push $seed swap store ret
2
+
3
+ rand:
4
+ push $seed dup dup load
5
+ push 3,13,10244807 mul mul mul
6
+ push 2,32 :pow mod
7
+ store load ret
8
+
9
+ rand_range: ; [a b]
10
+ copy 1 sub :rand swap mod add ret
@@ -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,2 @@
1
+ $++ = push 1 add
2
+ $-- = push 1 sub
@@ -0,0 +1,8 @@
1
+ assert_eq:
2
+ dup copy 2 sub jz _assert_eq_yes
3
+ push "expected " :print onum
4
+ push ", got " :print onum
5
+ push " for test " :strcat :print :println
6
+ exit
7
+
8
+ _assert_eq_yes: pop pop pop 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