voodoo 1.0.2 → 1.1.1
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.
- data/bin/voodooc +85 -49
- data/lib/voodoo.rb +11 -6
- data/lib/voodoo/code_generator.rb +6 -6
- data/lib/voodoo/compiler.rb +2 -2
- data/lib/voodoo/config.rb +3 -3
- data/lib/voodoo/generators/amd64_nasm_generator.rb +196 -118
- data/lib/voodoo/generators/arm_gas_generator.rb +367 -334
- data/lib/voodoo/generators/common_code_generator.rb +467 -69
- data/lib/voodoo/generators/dummy_generator.rb +10 -0
- data/lib/voodoo/generators/i386_nasm_generator.rb +93 -60
- data/lib/voodoo/generators/mips_gas_generator.rb +275 -265
- data/lib/voodoo/generators/nasm_generator.rb +253 -251
- data/lib/voodoo/parser.rb +46 -22
- data/lib/voodoo/validator.rb +118 -60
- metadata +14 -14
- data/lib/voodoo/generators/arm_gas_generator.rb~ +0 -957
data/lib/voodoo/parser.rb
CHANGED
@@ -20,6 +20,10 @@ module Voodoo
|
|
20
20
|
# end
|
21
21
|
# end
|
22
22
|
class Parser
|
23
|
+
NUMBER_STARTER = /\d|-/
|
24
|
+
STRING_STARTER = '"'
|
25
|
+
SYMBOL_STARTER = /[[:alpha:]]|\\/
|
26
|
+
|
23
27
|
# Creates a parser using the specified object as input.
|
24
28
|
# The input object must support a method +getc+, which must
|
25
29
|
# return the next character of the input, or +nil+ to indicate
|
@@ -157,7 +161,9 @@ module Voodoo
|
|
157
161
|
end
|
158
162
|
end
|
159
163
|
|
160
|
-
# Parses
|
164
|
+
# Parses statements up to "end X". _kind_ should indicate the type
|
165
|
+
# of body being parsed: :block, :conditional, :function, or :group.
|
166
|
+
# Returns an array of statements.
|
161
167
|
def parse_body kind
|
162
168
|
wrap_exceptions do
|
163
169
|
body = []
|
@@ -168,6 +174,9 @@ module Voodoo
|
|
168
174
|
else
|
169
175
|
kind_text = kind.to_s
|
170
176
|
end
|
177
|
+
# Groups are allowed to contain top-level statements.
|
178
|
+
# All other kinds aren't.
|
179
|
+
top_level = kind == :group
|
171
180
|
done = false
|
172
181
|
until done
|
173
182
|
begin
|
@@ -186,14 +195,22 @@ module Voodoo
|
|
186
195
|
done = true
|
187
196
|
else
|
188
197
|
# Should be a normal statement. Validate it, then add it to body
|
189
|
-
if statement[0] == :function
|
190
|
-
parse_error "Function definitions are only allowed at top-level"
|
191
|
-
end
|
192
198
|
begin
|
193
|
-
|
199
|
+
if top_level
|
200
|
+
Validator.validate_top_level statement
|
201
|
+
else
|
202
|
+
Validator.validate_statement statement
|
203
|
+
end
|
194
204
|
body << statement
|
195
205
|
rescue Validator::ValidationError => e
|
196
|
-
|
206
|
+
magic_word = statement[0]
|
207
|
+
if !top_level &&
|
208
|
+
Validator::TOP_LEVELS.member?(magic_word) &&
|
209
|
+
!Validator::STATEMENTS.member?(magic_word)
|
210
|
+
parse_error "#{magic_word} is only allowed at top-level"
|
211
|
+
else
|
212
|
+
parse_error e.message
|
213
|
+
end
|
197
214
|
end
|
198
215
|
end
|
199
216
|
end
|
@@ -348,7 +365,7 @@ module Voodoo
|
|
348
365
|
symbol.to_s[-1] == ?:
|
349
366
|
end
|
350
367
|
|
351
|
-
# Tests if a symbol is a conditional starter
|
368
|
+
# Tests if a symbol is a conditional starter.
|
352
369
|
def is_conditional? symbol
|
353
370
|
[:ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne].member? symbol
|
354
371
|
end
|
@@ -368,7 +385,7 @@ module Voodoo
|
|
368
385
|
@lookahead
|
369
386
|
end
|
370
387
|
|
371
|
-
# Parses a conditional statement
|
388
|
+
# Parses a conditional statement.
|
372
389
|
def parse_conditional1 condition, operands
|
373
390
|
# Parse first clause and condition for next clause
|
374
391
|
consequent, next_condition = split_if_clause parse_body(:conditional)
|
@@ -386,7 +403,7 @@ module Voodoo
|
|
386
403
|
[condition, operands, consequent, alternative]
|
387
404
|
end
|
388
405
|
|
389
|
-
# Raises a ParseError at the current input position
|
406
|
+
# Raises a ParseError at the current input position.
|
390
407
|
def parse_error message, text = @text
|
391
408
|
# Create the error object
|
392
409
|
error = ParseError.new(message, @input_name, @start_line,
|
@@ -404,7 +421,7 @@ module Voodoo
|
|
404
421
|
raise error
|
405
422
|
end
|
406
423
|
|
407
|
-
# Parses a top-level
|
424
|
+
# Parses a top-level incantation without validating it.
|
408
425
|
def parse_top_level_nonvalidating
|
409
426
|
# Skip whitespace, comments, and empty lines
|
410
427
|
skip_to_next_top_level
|
@@ -458,16 +475,16 @@ module Voodoo
|
|
458
475
|
[:function, words[1..-1]] + body
|
459
476
|
elsif is_conditional?(words[0])
|
460
477
|
parse_conditional1 words[0], words[1..-1]
|
461
|
-
elsif words[0] == :block
|
462
|
-
body = parse_body
|
463
|
-
[
|
478
|
+
elsif words[0] == :block || words[0] == :group
|
479
|
+
body = parse_body words[0]
|
480
|
+
[words[0]] + body
|
464
481
|
else
|
465
482
|
# Statement or data declaration; simply return it
|
466
483
|
words
|
467
484
|
end
|
468
485
|
end
|
469
486
|
|
470
|
-
# Skips whitespace, newlines, and comments before a top-level
|
487
|
+
# Skips whitespace, newlines, and comments before a top-level incantation.
|
471
488
|
def skip_to_next_top_level
|
472
489
|
while true
|
473
490
|
case lookahead
|
@@ -527,14 +544,14 @@ module Voodoo
|
|
527
544
|
case lookahead
|
528
545
|
when :eof
|
529
546
|
nil
|
530
|
-
when
|
547
|
+
when NUMBER_STARTER
|
531
548
|
# Digit; parse number
|
532
549
|
parse_number
|
533
|
-
when
|
550
|
+
when SYMBOL_STARTER
|
534
551
|
# Letter, underscore, or backslash; parse symbol
|
535
552
|
# Note: \w matches digits, too, so keep this case after \d
|
536
553
|
parse_symbol
|
537
|
-
when
|
554
|
+
when STRING_STARTER
|
538
555
|
# Double quote; parse string
|
539
556
|
parse_string
|
540
557
|
when '@'
|
@@ -542,23 +559,30 @@ module Voodoo
|
|
542
559
|
# '@' must be followed by a number or symbol.
|
543
560
|
consume
|
544
561
|
case lookahead
|
545
|
-
when
|
562
|
+
when NUMBER_STARTER
|
546
563
|
expr = parse_number
|
547
|
-
when
|
564
|
+
when SYMBOL_STARTER
|
548
565
|
expr = parse_symbol
|
549
566
|
else
|
550
567
|
parse_error "Invalid character (#{lookahead}) " +
|
551
568
|
"in at-expression; expecting number or symbol"
|
552
569
|
end
|
553
570
|
[:'@', expr]
|
571
|
+
when '%'
|
572
|
+
consume
|
573
|
+
# Must be followed by a symbol.
|
574
|
+
if lookahead !~ SYMBOL_STARTER
|
575
|
+
parse_error "'%' must be followed by a symbol"
|
576
|
+
end
|
577
|
+
[:'%', parse_symbol]
|
554
578
|
else
|
555
579
|
# No valid starter for a token, return nil
|
556
580
|
nil
|
557
581
|
end
|
558
582
|
end
|
559
583
|
|
560
|
-
#
|
561
|
-
#
|
584
|
+
# Evaluates _block_ and checks that the result is a valid top-level
|
585
|
+
# incantation.
|
562
586
|
def validate_top_level &block
|
563
587
|
with_position do
|
564
588
|
result = yield
|
@@ -573,7 +597,7 @@ module Voodoo
|
|
573
597
|
end
|
574
598
|
end
|
575
599
|
|
576
|
-
#
|
600
|
+
# Evaluates block, keeping track of @start_line, @start_column
|
577
601
|
# at the beginning of the block, and @text during the evaluation
|
578
602
|
# of block.
|
579
603
|
def with_position &block
|
data/lib/voodoo/validator.rb
CHANGED
@@ -1,25 +1,31 @@
|
|
1
1
|
module Voodoo
|
2
2
|
# Functionality for validating Voodoo code.
|
3
|
+
#
|
4
|
+
# See validate_top_level, validate_statement, and validate_expression.
|
3
5
|
module Validator
|
4
6
|
|
5
|
-
# Expressions that take two
|
7
|
+
# Expressions that take two parameters.
|
6
8
|
BINOPS = [:add, :and, :asr, :bsr, :div, :'get-byte', :'get-word',
|
7
9
|
:mod, :mul, :or, :rol, :ror, :shl, :shr, :sub, :xor]
|
8
|
-
# Expressions that take
|
10
|
+
# Expressions that take a single parameter.
|
11
|
+
UNOPS = [:'auto-bytes', :'auto-words', :not]
|
12
|
+
# Expressions that take zero or more parameters.
|
9
13
|
VARARG_EXPRS = [:call, :'tail-call']
|
10
|
-
# Symbols that may occur as the first word of an expression
|
11
|
-
EXPRS = BINOPS +
|
14
|
+
# Symbols that may occur as the first word of an expression.
|
15
|
+
EXPRS = BINOPS + UNOPS + VARARG_EXPRS
|
12
16
|
|
13
|
-
# Symbols that are a valid start of a statement
|
14
|
-
STATEMENTS = [:block, :call, :goto,
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:'
|
17
|
+
# Symbols that are a valid start of a statement.
|
18
|
+
STATEMENTS = [:byte, :block, :call, :goto, :ifeq, :ifge,
|
19
|
+
:ifgt, :ifle, :iflt, :ifne, :label, :let, :return,
|
20
|
+
:'restore-frame', :'restore-locals', :'save-frame',
|
21
|
+
:'save-frame-and-locals', :'save-locals', :set,
|
22
|
+
:'set-byte', :'set-word', :string, :'tail-call', :word]
|
18
23
|
|
19
|
-
# Symbols that are valid at top-level
|
20
|
-
TOP_LEVELS = [:align, :
|
21
|
-
|
24
|
+
# Symbols that are valid at top-level.
|
25
|
+
TOP_LEVELS = [:align, :export, :function, :group, :import, :section] +
|
26
|
+
STATEMENTS
|
22
27
|
|
28
|
+
# Maps indices 0, 1, 2 to English words.
|
23
29
|
NTH = ['First', 'Second', 'Third']
|
24
30
|
|
25
31
|
module_function
|
@@ -31,13 +37,18 @@ module Voodoo
|
|
31
37
|
if int_or_symbol_or_at? code
|
32
38
|
true
|
33
39
|
elsif code.respond_to? :[]
|
34
|
-
|
40
|
+
op = code[0]
|
41
|
+
if BINOPS.member? op
|
35
42
|
# binop should have 2 parameters, both of them atomic values
|
36
43
|
assert_n_params code, 2
|
37
44
|
assert_params_are_values code
|
45
|
+
elsif UNOPS.member? op
|
46
|
+
# should have a single, atomic parameter
|
47
|
+
assert_n_params code, 1
|
48
|
+
assert_params_are_values code
|
38
49
|
else
|
39
|
-
#
|
40
|
-
case
|
50
|
+
# op is not a unary or binary operator
|
51
|
+
case op
|
41
52
|
when :call, :'tail-call'
|
42
53
|
# call should have at least 1 parameter
|
43
54
|
# and all parameters should be atomic values
|
@@ -47,10 +58,6 @@ module Voodoo
|
|
47
58
|
# Should have exactly 2 parameters, both of which should be values.
|
48
59
|
assert_n_params code, 2
|
49
60
|
assert_params_are_values code
|
50
|
-
when :not
|
51
|
-
# should have a single, atomic parameter
|
52
|
-
assert_n_params code, 1
|
53
|
-
assert_params_are_values code
|
54
61
|
else
|
55
62
|
raise ValidationError.new("#{code[0].inspect}" +
|
56
63
|
" cannot start an expression",
|
@@ -74,6 +81,16 @@ module Voodoo
|
|
74
81
|
code[1..-1].each {|stmt| validate_statement stmt}
|
75
82
|
true
|
76
83
|
|
84
|
+
when :byte, :word
|
85
|
+
# Should have a single integer or symbol parameter
|
86
|
+
if code.length != 2 || !int_or_symbol?(code[1])
|
87
|
+
raise ValidationError.new("#{code[0]} requires a single" +
|
88
|
+
" parameter that is either an " +
|
89
|
+
" integer or a symbol", code)
|
90
|
+
else
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
77
94
|
when :call, :'tail-call'
|
78
95
|
validate_expression code
|
79
96
|
|
@@ -133,12 +150,12 @@ module Voodoo
|
|
133
150
|
true
|
134
151
|
end
|
135
152
|
|
136
|
-
when :let
|
153
|
+
when :let
|
137
154
|
# should have at least 2 parameters
|
138
155
|
if code.length < 3
|
139
156
|
raise ValidationError.new("#{code[0]} requires a symbol" +
|
140
157
|
" and an expression", code)
|
141
|
-
elsif code[1]
|
158
|
+
elsif symbol? code[1]
|
142
159
|
# After the symbol, there should be an expression
|
143
160
|
expr = code[2..-1]
|
144
161
|
if expr.length == 1
|
@@ -151,6 +168,25 @@ module Voodoo
|
|
151
168
|
" a symbol", code)
|
152
169
|
end
|
153
170
|
|
171
|
+
when :set
|
172
|
+
# should have at least 2 parameters
|
173
|
+
if code.length < 3
|
174
|
+
raise ValidationError.new(
|
175
|
+
"#{code[0]} requires a symbol or at-expression" +
|
176
|
+
" followed by an expression", code)
|
177
|
+
elsif symbol_or_at? code[1]
|
178
|
+
# After the symbol/at-expr, there should be an expression
|
179
|
+
expr = code[2..-1]
|
180
|
+
if expr.length == 1
|
181
|
+
validate_expression expr[0]
|
182
|
+
else
|
183
|
+
validate_expression expr
|
184
|
+
end
|
185
|
+
else
|
186
|
+
raise ValidationError.new("First parameter to #{code[0]} should be" +
|
187
|
+
" a symbol or an at-expression", code)
|
188
|
+
end
|
189
|
+
|
154
190
|
when :return
|
155
191
|
# Should either have no parameters, or a single expression as
|
156
192
|
# a parameter.
|
@@ -169,9 +205,33 @@ module Voodoo
|
|
169
205
|
assert_n_params code, 3
|
170
206
|
assert_params_are_values code
|
171
207
|
|
208
|
+
when :'restore-frame', :'save-frame'
|
209
|
+
# Should have exactly 1 parameter.
|
210
|
+
assert_n_params code, 1
|
211
|
+
assert_params_are_values code
|
212
|
+
|
213
|
+
when :'restore-locals', :'save-frame-and-locals', :'save-locals'
|
214
|
+
# Should have 1 or more parameters.
|
215
|
+
assert_at_least_n_params code, 1
|
216
|
+
assert_params_are_values code
|
217
|
+
|
218
|
+
when :string
|
219
|
+
# Should have a single string parameter
|
220
|
+
if code.length != 2 || !code[1].kind_of?(::String)
|
221
|
+
raise ValidationError.new("string requires a single string" +
|
222
|
+
" as a parameter", code)
|
223
|
+
else
|
224
|
+
true
|
225
|
+
end
|
226
|
+
|
172
227
|
else
|
173
|
-
|
174
|
-
|
228
|
+
if TOP_LEVELS.member?(code[0]) && !STATEMENTS.member?(code[0])
|
229
|
+
raise ValidationError.new("#{code[0]} is only valid at top-level", code)
|
230
|
+
else
|
231
|
+
|
232
|
+
raise ValidationError.new("Not a valid statement: #{code.inspect}",
|
233
|
+
code)
|
234
|
+
end
|
175
235
|
end
|
176
236
|
|
177
237
|
rescue ValidationError
|
@@ -190,9 +250,9 @@ module Voodoo
|
|
190
250
|
end
|
191
251
|
end
|
192
252
|
|
193
|
-
# Validates a top-level
|
194
|
-
# Returns true if the
|
195
|
-
# Raises ValidationError if the
|
253
|
+
# Validates a top-level incantation.
|
254
|
+
# Returns true if the incantation is valid.
|
255
|
+
# Raises ValidationError if the incantation is not valid.
|
196
256
|
def validate_top_level code
|
197
257
|
begin
|
198
258
|
case code[0]
|
@@ -207,16 +267,6 @@ module Voodoo
|
|
207
267
|
code)
|
208
268
|
end
|
209
269
|
|
210
|
-
when :byte, :word
|
211
|
-
# Should have a single integer or symbol parameter
|
212
|
-
if code.length != 2 || !int_or_symbol?(code[1])
|
213
|
-
raise ValidationError.new("#{code[0]} requires a single" +
|
214
|
-
" parameter that is either an " +
|
215
|
-
" integer or a symbol", code)
|
216
|
-
else
|
217
|
-
true
|
218
|
-
end
|
219
|
-
|
220
270
|
when :export, :import
|
221
271
|
# Should have at least 1 parameter, and all parameters should
|
222
272
|
# be symbols.
|
@@ -248,15 +298,12 @@ module Voodoo
|
|
248
298
|
end
|
249
299
|
end
|
250
300
|
|
251
|
-
# Verify
|
252
|
-
code[2..-1].each
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
validate_statement stmt
|
258
|
-
end
|
259
|
-
end
|
301
|
+
# Verify body.
|
302
|
+
code[2..-1].each { |stmt| validate_statement stmt }
|
303
|
+
|
304
|
+
when :group
|
305
|
+
# Verify body.
|
306
|
+
code[1..-1].each { |stmt| validate_top_level stmt }
|
260
307
|
|
261
308
|
when :section
|
262
309
|
|
@@ -273,25 +320,16 @@ module Voodoo
|
|
273
320
|
end
|
274
321
|
|
275
322
|
else
|
276
|
-
raise ValidationError.new("section
|
323
|
+
raise ValidationError.new("section incantation should have only" +
|
277
324
|
" a single parameter",
|
278
325
|
code);
|
279
326
|
end
|
280
327
|
|
281
|
-
when :string
|
282
|
-
# Should have a single string parameter
|
283
|
-
if code.length != 2 || !code[1].kind_of?(::String)
|
284
|
-
raise ValidationError.new("string requires a single string" +
|
285
|
-
" as a parameter", code)
|
286
|
-
else
|
287
|
-
true
|
288
|
-
end
|
289
|
-
|
290
328
|
else
|
291
329
|
if STATEMENTS.member? code[0]
|
292
330
|
validate_statement code
|
293
331
|
else
|
294
|
-
raise ValidationError.new("
|
332
|
+
raise ValidationError.new("Incantation #{code[0]}" +
|
295
333
|
" not valid at top-level",
|
296
334
|
code)
|
297
335
|
end
|
@@ -377,14 +415,34 @@ module Voodoo
|
|
377
415
|
true
|
378
416
|
end
|
379
417
|
|
418
|
+
def at_expr? x
|
419
|
+
x.respond_to?(:length) && x.length == 2 && x[0] == :'@' &&
|
420
|
+
int_or_symbol?(x[1])
|
421
|
+
end
|
422
|
+
|
423
|
+
def int? x
|
424
|
+
x.kind_of?(::Integer) || substitution?(x)
|
425
|
+
end
|
426
|
+
|
380
427
|
def int_or_symbol? x
|
381
|
-
x.kind_of?(::Symbol) ||
|
428
|
+
x.kind_of?(::Symbol) || int?(x)
|
382
429
|
end
|
383
430
|
|
384
431
|
def int_or_symbol_or_at? x
|
385
|
-
int_or_symbol?(x) ||
|
386
|
-
|
387
|
-
|
432
|
+
int_or_symbol?(x) || at_expr?(x)
|
433
|
+
end
|
434
|
+
|
435
|
+
def substitution? x
|
436
|
+
x.respond_to?(:length) && x.length == 2 && x[0] == :'%' &&
|
437
|
+
symbol?(x[1])
|
438
|
+
end
|
439
|
+
|
440
|
+
def symbol? x
|
441
|
+
x.kind_of? ::Symbol
|
442
|
+
end
|
443
|
+
|
444
|
+
def symbol_or_at? x
|
445
|
+
symbol?(x) || at_expr?(x)
|
388
446
|
end
|
389
447
|
|
390
448
|
end
|