voodoo 0.7.0 → 1.0.0
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 +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
|