voodoo 0.7.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/voodooc +71 -16
- data/lib/voodoo/compiler.rb +41 -13
- data/lib/voodoo/config.rb +5 -0
- data/lib/voodoo/generators/amd64_nasm_generator.rb +4 -0
- data/lib/voodoo/generators/arm_elf_generator.rb +33 -0
- data/lib/voodoo/generators/arm_gas_generator.rb +927 -0
- data/lib/voodoo/generators/common_code_generator.rb +19 -6
- data/lib/voodoo/generators/gas_elf_generator.rb +2 -2
- data/lib/voodoo/generators/i386_nasm_generator.rb +6 -1
- data/lib/voodoo/generators/mips_gas_generator.rb +14 -4
- data/lib/voodoo/generators/nasm_elf_generator.rb +1 -1
- data/lib/voodoo/generators/nasm_generator.rb +6 -10
- data/lib/voodoo/parser.rb +307 -128
- data/lib/voodoo/validator.rb +391 -0
- metadata +13 -11
- data/lib/voodoo/generators/generator_api1.rb +0 -95
@@ -0,0 +1,391 @@
|
|
1
|
+
module Voodoo
|
2
|
+
# Functionality for validating Voodoo code.
|
3
|
+
module Validator
|
4
|
+
|
5
|
+
# Expressions that take two arguments
|
6
|
+
BINOPS = [:add, :and, :asr, :bsr, :div, :'get-byte', :'get-word',
|
7
|
+
:mod, :mul, :or, :rol, :ror, :shl, :shr, :sub, :xor]
|
8
|
+
# Expressions that take zero or more parameters
|
9
|
+
VARARG_EXPRS = [:call, :'tail-call']
|
10
|
+
# Symbols that may occur as the first word of an expression
|
11
|
+
EXPRS = BINOPS + VARARG_EXPRS + [:not]
|
12
|
+
|
13
|
+
# Symbols that are a valid start of a statement
|
14
|
+
STATEMENTS = [:block, :call, :goto,
|
15
|
+
:ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne,
|
16
|
+
:label, :let, :return, :set,
|
17
|
+
:'set-byte', :'set-word', :'tail-call']
|
18
|
+
|
19
|
+
# Symbols that are valid at top-level
|
20
|
+
TOP_LEVELS = [:align, :byte, :export, :function, :import,
|
21
|
+
:section, :string, :word] + STATEMENTS
|
22
|
+
|
23
|
+
NTH = ['First', 'Second', 'Third']
|
24
|
+
|
25
|
+
module_function
|
26
|
+
|
27
|
+
# Validates an expression.
|
28
|
+
# Returns true if the expression is valid.
|
29
|
+
# Raises ValidationError if the expression is not valid.
|
30
|
+
def validate_expression code
|
31
|
+
if int_or_symbol_or_at? code
|
32
|
+
true
|
33
|
+
elsif code.respond_to? :[]
|
34
|
+
if BINOPS.member? code[0]
|
35
|
+
# binop should have 2 parameters, both of them atomic values
|
36
|
+
assert_n_params code, 2
|
37
|
+
assert_params_are_values code
|
38
|
+
else
|
39
|
+
# code[0] is not a binop
|
40
|
+
case code[0]
|
41
|
+
when :call, :'tail-call'
|
42
|
+
# call should have at least 1 parameter
|
43
|
+
# and all parameters should be atomic values
|
44
|
+
assert_at_least_n_params code, 1
|
45
|
+
assert_params_are_values code
|
46
|
+
when :'get-byte', :'get-word'
|
47
|
+
# Should have exactly 2 parameters, both of which should be values.
|
48
|
+
assert_n_params code, 2
|
49
|
+
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
|
+
else
|
55
|
+
raise ValidationError.new("#{code[0].inspect}" +
|
56
|
+
" cannot start an expression",
|
57
|
+
code)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
else
|
61
|
+
# code is not an atomic value and does not respond to :[]
|
62
|
+
raise ValidationError.new("#{code.inspect} is not a valid expression",
|
63
|
+
code)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Validates a statement.
|
68
|
+
# Returns true if the statement is valid.
|
69
|
+
# Raises ValidationError if the statement is not valid.
|
70
|
+
def validate_statement code
|
71
|
+
begin
|
72
|
+
case code[0]
|
73
|
+
when :block
|
74
|
+
code[1..-1].each {|stmt| validate_statement stmt}
|
75
|
+
true
|
76
|
+
|
77
|
+
when :call, :'tail-call'
|
78
|
+
validate_expression code
|
79
|
+
|
80
|
+
when :goto
|
81
|
+
# should have exactly 1 parameter, which should be atomic
|
82
|
+
assert_n_params code, 1
|
83
|
+
assert_params_are_values code
|
84
|
+
|
85
|
+
when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
|
86
|
+
# Should have 2 or 3 parameters.
|
87
|
+
# First parameter should be an array (or similar) containing
|
88
|
+
# two elements, both atomic.
|
89
|
+
# Second parameter should consist of one or more statements.
|
90
|
+
# Third parameter, if present, should consist of zero or more
|
91
|
+
# statements.
|
92
|
+
# let is not allowed as a statement in either parameter, though
|
93
|
+
# it can be embedded in a block in either.
|
94
|
+
if code.length < 3 || code.length > 4
|
95
|
+
raise ValidationError.new("#{code[0]} takes 2 or 3 parameters",
|
96
|
+
code)
|
97
|
+
elsif code[1].length != 2
|
98
|
+
raise ValidationError.new("#{code[0]} requires two values to" +
|
99
|
+
" compare in its first parameter",
|
100
|
+
code)
|
101
|
+
elsif !code[1].all? {|x| int_or_symbol_or_at? x}
|
102
|
+
raise ValidationError.new("Values to compare should be values" +
|
103
|
+
" (symbols, integers, or at-exrpssions)",
|
104
|
+
code)
|
105
|
+
else
|
106
|
+
code[2].each do |stmt|
|
107
|
+
validate_statement stmt
|
108
|
+
if stmt[0] == :let
|
109
|
+
raise ValidationError.new("let is not allowed inside " +
|
110
|
+
code[0].to_s, code)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if code.length > 3
|
115
|
+
code[3].each do |stmt|
|
116
|
+
validate_statement stmt
|
117
|
+
if stmt[0] == :let
|
118
|
+
raise ValidationError.new("let is not allowed inside " +
|
119
|
+
code[0].to_s, code)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
when :label
|
128
|
+
# should have 1 parameter which should be a symbol
|
129
|
+
if code.length != 2 || !code[1].kind_of?(::Symbol)
|
130
|
+
raise ValidationError.new("label requires a single symbol" +
|
131
|
+
"as its parameter", code)
|
132
|
+
else
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
136
|
+
when :let, :set
|
137
|
+
# should have at least 2 parameters
|
138
|
+
if code.length < 3
|
139
|
+
raise ValidationError.new("#{code[0]} requires a symbol" +
|
140
|
+
" and an expression", code)
|
141
|
+
elsif code[1].kind_of? ::Symbol
|
142
|
+
# After the symbol, there should be an expression
|
143
|
+
expr = code[2..-1]
|
144
|
+
if expr.length == 1
|
145
|
+
validate_expression expr[0]
|
146
|
+
else
|
147
|
+
validate_expression expr
|
148
|
+
end
|
149
|
+
else
|
150
|
+
raise ValidationError.new("First parameter to #{code[0]} should be" +
|
151
|
+
" a symbol", code)
|
152
|
+
end
|
153
|
+
|
154
|
+
when :return
|
155
|
+
# Should either have no parameters, or a single expression as
|
156
|
+
# a parameter.
|
157
|
+
case code.length
|
158
|
+
when 1
|
159
|
+
true
|
160
|
+
when 2
|
161
|
+
validate_expression code[1]
|
162
|
+
else
|
163
|
+
validate_expression code[1..-1]
|
164
|
+
end
|
165
|
+
|
166
|
+
when :'set-byte', :'set-word'
|
167
|
+
# Should have exactly 3 parameters, all of which should be
|
168
|
+
# atomic values.
|
169
|
+
assert_n_params code, 3
|
170
|
+
assert_params_are_values code
|
171
|
+
|
172
|
+
else
|
173
|
+
raise ValidationError.new("Not a valid statement: #{code.inspect}",
|
174
|
+
code)
|
175
|
+
end
|
176
|
+
|
177
|
+
rescue ValidationError
|
178
|
+
# Pass it on
|
179
|
+
raise
|
180
|
+
|
181
|
+
rescue Exception => e
|
182
|
+
if code.respond_to? :[]
|
183
|
+
# Pass on the exception
|
184
|
+
raise
|
185
|
+
else
|
186
|
+
raise ValidationError.new("#{code.inspect} does not respond to" +
|
187
|
+
":[]", code)
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Validates a top-level directive.
|
194
|
+
# Returns true if the directive is valid.
|
195
|
+
# Raises ValidationError if the directive is not valid.
|
196
|
+
def validate_top_level code
|
197
|
+
begin
|
198
|
+
case code[0]
|
199
|
+
when :align
|
200
|
+
# Should either have no parameter or a single integer parameter
|
201
|
+
if code.length == 1 || (code.length == 2 &&
|
202
|
+
code[1].kind_of?(::Integer))
|
203
|
+
true
|
204
|
+
else
|
205
|
+
raise ValidationError.new("align requires either a single" +
|
206
|
+
" integer parameter, or no parameters",
|
207
|
+
code)
|
208
|
+
end
|
209
|
+
|
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
|
+
when :export, :import
|
221
|
+
# Should have at least 1 parameter, and all parameters should
|
222
|
+
# be symbols.
|
223
|
+
if code.length < 2
|
224
|
+
raise ValidationError.new("#{code[0]} requires at least " +
|
225
|
+
" one parameter", code)
|
226
|
+
elsif code[1..-1].all? {|x| x.kind_of? ::Symbol}
|
227
|
+
true
|
228
|
+
else
|
229
|
+
raise ValidationError.new("All parameters to #{code[0]}" +
|
230
|
+
" should be symbols", code)
|
231
|
+
end
|
232
|
+
|
233
|
+
when :function
|
234
|
+
|
235
|
+
# Check that formal parameters have been specified
|
236
|
+
if code.length < 2
|
237
|
+
raise ValidationError.new("Formal parameters should be" +
|
238
|
+
" specified for function",
|
239
|
+
code)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Check that all formal parameters are symbols
|
243
|
+
code[1].each do |formal|
|
244
|
+
unless formal.kind_of? ::Symbol
|
245
|
+
raise ValidationError.new("Formal parameter #{formal.inspect}" +
|
246
|
+
" should be a symbol",
|
247
|
+
formal)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Verify statements
|
252
|
+
code[2..-1].each do |stmt|
|
253
|
+
if stmt.respond_to?(:[]) && stmt[0] == :function
|
254
|
+
raise ValidationError.new("Function definitions are only " +
|
255
|
+
"valid at top-level", stmt)
|
256
|
+
else
|
257
|
+
validate_statement stmt
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
when :section
|
262
|
+
|
263
|
+
# Check that we have a string or a symbol
|
264
|
+
case code.length
|
265
|
+
when 1
|
266
|
+
raise ValidationError.new("Section name should be specified",
|
267
|
+
code)
|
268
|
+
when 2
|
269
|
+
unless code[1].kind_of?(::String) || code[1].kind_of?(::Symbol)
|
270
|
+
raise ValidationError.new("Section name should be a string" +
|
271
|
+
" or a symbol",
|
272
|
+
code)
|
273
|
+
end
|
274
|
+
|
275
|
+
else
|
276
|
+
raise ValidationError.new("section directive should have only" +
|
277
|
+
" a single parameter",
|
278
|
+
code);
|
279
|
+
end
|
280
|
+
|
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
|
+
else
|
291
|
+
if STATEMENTS.member? code[0]
|
292
|
+
validate_statement code
|
293
|
+
else
|
294
|
+
raise ValidationError.new("Directive #{code[0]}" +
|
295
|
+
" not valid at top-level",
|
296
|
+
code)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
rescue ValidationError
|
301
|
+
# Pass it on
|
302
|
+
raise
|
303
|
+
|
304
|
+
rescue Exception => e
|
305
|
+
if code.respond_to? :[]
|
306
|
+
# Pass on the exception
|
307
|
+
raise
|
308
|
+
else
|
309
|
+
raise ValidationError.new("#{code.inspect} does not respond to" +
|
310
|
+
":[]", code)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# If we got here, all is well
|
315
|
+
true
|
316
|
+
end
|
317
|
+
|
318
|
+
# Base class for errors signaled by the Validator.
|
319
|
+
class ValidationError < StandardError
|
320
|
+
def initialize message, code = nil
|
321
|
+
super message
|
322
|
+
@code = code
|
323
|
+
end
|
324
|
+
attr_reader :code
|
325
|
+
end
|
326
|
+
|
327
|
+
# Tests that an expression has at least n parameters. Raises a
|
328
|
+
# ValidationError if this is not the case.
|
329
|
+
def assert_at_least_n_params expr, n
|
330
|
+
if expr.length <= n
|
331
|
+
if n == 1
|
332
|
+
raise ValidationError.new \
|
333
|
+
"#{expr[0]} should have at least one parameter"
|
334
|
+
else
|
335
|
+
raise ValidationError.new \
|
336
|
+
"#{expr[0]} should have at least #{n} parameters"
|
337
|
+
end
|
338
|
+
end
|
339
|
+
true
|
340
|
+
end
|
341
|
+
|
342
|
+
# Tests that an expression has exactly n parameters. Raises a
|
343
|
+
# ValidationError if this is not the case.
|
344
|
+
def assert_n_params expr, n
|
345
|
+
if expr.length != n + 1
|
346
|
+
if n == 1
|
347
|
+
raise ValidationError.new \
|
348
|
+
"#{expr[0]} should have exactly one parameter"
|
349
|
+
else
|
350
|
+
raise ValidationError.new \
|
351
|
+
"#{expr[0]} should have exactly #{n} parameters"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
true
|
355
|
+
end
|
356
|
+
|
357
|
+
# Tests that parameters to an expression are
|
358
|
+
# values (integers, symbols, or at-expressions),
|
359
|
+
# and raises ValidationError if this is not the
|
360
|
+
# case.
|
361
|
+
# If ns is nil (default) all parameters should me
|
362
|
+
# values.
|
363
|
+
# Alternatively, ns may be a range or array containing
|
364
|
+
# the indices of the parameters that should be
|
365
|
+
# values.
|
366
|
+
def assert_params_are_values expr, ns = nil
|
367
|
+
if ns == nil
|
368
|
+
ns = (1...expr.length)
|
369
|
+
end
|
370
|
+
ns.each do |i|
|
371
|
+
unless int_or_symbol_or_at?(expr[i])
|
372
|
+
raise ValidationError.new \
|
373
|
+
"#{NTH[i - 1]} parameter to #{expr[0]}" +
|
374
|
+
" should be a value (symbol, integer, or at-expression)"
|
375
|
+
end
|
376
|
+
end
|
377
|
+
true
|
378
|
+
end
|
379
|
+
|
380
|
+
def int_or_symbol? x
|
381
|
+
x.kind_of?(::Symbol) || x.kind_of?(::Integer)
|
382
|
+
end
|
383
|
+
|
384
|
+
def int_or_symbol_or_at? x
|
385
|
+
int_or_symbol?(x) ||
|
386
|
+
(x.respond_to?(:length) && x.length == 2 && x[0] == :'@' &&
|
387
|
+
int_or_symbol?(x[1]))
|
388
|
+
end
|
389
|
+
|
390
|
+
end
|
391
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: voodoo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
- 7
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 1.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Robbert Haarman
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-14 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -41,18 +41,20 @@ files:
|
|
41
41
|
- lib/voodoo/code_generator.rb
|
42
42
|
- lib/voodoo/compiler.rb
|
43
43
|
- lib/voodoo/config.rb
|
44
|
-
- lib/voodoo/
|
44
|
+
- lib/voodoo/validator.rb
|
45
45
|
- lib/voodoo/generators/common_code_generator.rb
|
46
|
-
- lib/voodoo/generators/
|
46
|
+
- lib/voodoo/generators/mips_gas_generator.rb
|
47
|
+
- lib/voodoo/generators/i386_elf_generator.rb
|
48
|
+
- lib/voodoo/generators/command_postprocessor.rb
|
47
49
|
- lib/voodoo/generators/mips_elf_generator.rb
|
48
50
|
- lib/voodoo/generators/gas_elf_generator.rb
|
51
|
+
- lib/voodoo/generators/arm_elf_generator.rb
|
52
|
+
- lib/voodoo/generators/arm_gas_generator.rb
|
49
53
|
- lib/voodoo/generators/amd64_nasm_generator.rb
|
50
|
-
- lib/voodoo/generators/mips_gas_generator.rb
|
51
|
-
- lib/voodoo/generators/generator_api1.rb
|
52
|
-
- lib/voodoo/generators/i386_nasm_generator.rb
|
53
|
-
- lib/voodoo/generators/command_postprocessor.rb
|
54
54
|
- lib/voodoo/generators/nasm_elf_generator.rb
|
55
|
-
- lib/voodoo/generators/
|
55
|
+
- lib/voodoo/generators/amd64_elf_generator.rb
|
56
|
+
- lib/voodoo/generators/nasm_generator.rb
|
57
|
+
- lib/voodoo/generators/i386_nasm_generator.rb
|
56
58
|
has_rdoc: true
|
57
59
|
homepage: http://inglorion.net/software/voodoo/
|
58
60
|
licenses: []
|
@@ -1,95 +0,0 @@
|
|
1
|
-
module Voodoo
|
2
|
-
# This module implements the old code generation API, consisting of
|
3
|
-
# the following methods:
|
4
|
-
#
|
5
|
-
# #add_code
|
6
|
-
# #add_code_label
|
7
|
-
# #add_data
|
8
|
-
# #add_data_label
|
9
|
-
# #add_function
|
10
|
-
# #add_function_label
|
11
|
-
# #align_code
|
12
|
-
# #align_data
|
13
|
-
# #align_function
|
14
|
-
#
|
15
|
-
module GeneratorApi1
|
16
|
-
# Add code.
|
17
|
-
#
|
18
|
-
# Parameters:
|
19
|
-
# [actions] an Array of actions to be added
|
20
|
-
#
|
21
|
-
# Example:
|
22
|
-
# add_code [:call, :exit, 0]
|
23
|
-
def add_code *actions
|
24
|
-
add :code, *actions
|
25
|
-
end
|
26
|
-
|
27
|
-
# Add a label at the current address in the code section.
|
28
|
-
#
|
29
|
-
# Example:
|
30
|
-
# add_code_label :mylabel
|
31
|
-
def add_code_label name
|
32
|
-
in_section(:code) { label name }
|
33
|
-
end
|
34
|
-
|
35
|
-
# Add data.
|
36
|
-
#
|
37
|
-
# Parameters:
|
38
|
-
# [defs] an Array of data definitions
|
39
|
-
#
|
40
|
-
# Example:
|
41
|
-
# add_data [:word, 42]
|
42
|
-
def add_data *defs
|
43
|
-
add :data, *defs
|
44
|
-
end
|
45
|
-
|
46
|
-
# Add a label at the current address in the data section.
|
47
|
-
#
|
48
|
-
# Example:
|
49
|
-
# add_data_label :msg
|
50
|
-
def add_data_label name
|
51
|
-
add :data, [:label, name]
|
52
|
-
end
|
53
|
-
|
54
|
-
# Add a label at the address where the next function will be generated.
|
55
|
-
#
|
56
|
-
# Example:
|
57
|
-
# add_function_label :add_one
|
58
|
-
def add_function_label name
|
59
|
-
add :functions, [:label, name]
|
60
|
-
end
|
61
|
-
|
62
|
-
# Align code on the next _alignment_-byte boundary.
|
63
|
-
# If _alignment_ is not specified, the default code alignment
|
64
|
-
# is used.
|
65
|
-
def align_code alignment = nil
|
66
|
-
if alignment
|
67
|
-
add :code, [:align, alignment]
|
68
|
-
else
|
69
|
-
add :code, [:align]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Align data on the next _alignment_-byte boundary.
|
74
|
-
# If _alignment_ is not specified, the default data alignment
|
75
|
-
# is used.
|
76
|
-
def align_data alignment = nil
|
77
|
-
if alignment
|
78
|
-
add :data, [:align, alignment]
|
79
|
-
else
|
80
|
-
add :data, [:align]
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# Align function on the next _alignment_-byte boundary.
|
85
|
-
# If _alignment_ is not specified, the default function alignment
|
86
|
-
# is used.
|
87
|
-
def align_function alignment = nil
|
88
|
-
if alignment
|
89
|
-
add :functions, [:align, alignment]
|
90
|
-
else
|
91
|
-
add :functions, [:align]
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|