voodoo 1.0.1 → 1.0.2
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/lib/voodoo/config.rb +3 -3
- data/lib/voodoo/generators/arm_gas_generator.rb~ +957 -0
- data/lib/voodoo/parser.rb +7 -3
- metadata +40 -55
data/lib/voodoo/config.rb
CHANGED
@@ -2,15 +2,15 @@ module Voodoo
|
|
2
2
|
# Methods to get and set configuration parameters
|
3
3
|
module Config
|
4
4
|
IMPLEMENTATION_NAME = 'Voodoo Compiler'
|
5
|
-
IMPLEMENTATION_VERSION = '1.0.
|
5
|
+
IMPLEMENTATION_VERSION = '1.0.2'
|
6
6
|
|
7
7
|
# Class that holds configuration parameters
|
8
8
|
class Configuration
|
9
9
|
def initialize
|
10
10
|
@default_architecture = :auto
|
11
11
|
@default_format = :elf
|
12
|
-
@gas_command = "
|
13
|
-
@nasm_command = "
|
12
|
+
@gas_command = "as"
|
13
|
+
@nasm_command = "nasm"
|
14
14
|
end
|
15
15
|
|
16
16
|
attr_accessor :default_format,
|
@@ -0,0 +1,957 @@
|
|
1
|
+
require 'voodoo/generators/common_code_generator'
|
2
|
+
|
3
|
+
module Voodoo
|
4
|
+
# = ARM GNU Assembler Code Generator
|
5
|
+
#
|
6
|
+
# The ARM code generator generates assembly code for use with
|
7
|
+
# the GNU assembler.
|
8
|
+
#
|
9
|
+
# == Calling Convention
|
10
|
+
#
|
11
|
+
# The first four arguments are passed in the registers r0 through r3.
|
12
|
+
# Any additional arguments are passed on the stack, starting at
|
13
|
+
# r13. r13 will always be a multiple of 8.
|
14
|
+
#
|
15
|
+
# The return address for the called function is passed in r14.
|
16
|
+
#
|
17
|
+
# The called function will store its return value in r0.
|
18
|
+
#
|
19
|
+
# The called function is required to preserve the values of registers
|
20
|
+
# r4 through r11 and register r13.
|
21
|
+
#
|
22
|
+
# This calling convention is compatible with the Procedure Call
|
23
|
+
# Standard for the ARM Architecture (AAPCS).
|
24
|
+
#
|
25
|
+
# == Call Frames
|
26
|
+
#
|
27
|
+
# Call frames have the following layout:
|
28
|
+
#
|
29
|
+
# When a function is called, it receives a stack frame that looks like
|
30
|
+
# the following:
|
31
|
+
#
|
32
|
+
# :
|
33
|
+
# old frame
|
34
|
+
# padding
|
35
|
+
# argn
|
36
|
+
# :
|
37
|
+
# arg4 <-- r13 points here
|
38
|
+
#
|
39
|
+
# The function prologue of functions generated by this code generator
|
40
|
+
# creates activation frames that look as follows:
|
41
|
+
#
|
42
|
+
# :
|
43
|
+
# old frame
|
44
|
+
# padding
|
45
|
+
# argn
|
46
|
+
# :
|
47
|
+
# arg4 <-- r11 points here
|
48
|
+
# saved r14
|
49
|
+
# saved r11
|
50
|
+
# :
|
51
|
+
# saved r4 <-- r13 points here
|
52
|
+
#
|
53
|
+
# == Register Usage
|
54
|
+
#
|
55
|
+
# Inside a function, registers r4..r8 and r10 are used for local variables
|
56
|
+
# and function arguments, whereas r11 is used as a frame pointer.
|
57
|
+
#
|
58
|
+
# r12 is used as a temporary, and r3 is used when another temporary
|
59
|
+
# is needed.
|
60
|
+
#
|
61
|
+
class ARMGasGenerator < CommonCodeGenerator
|
62
|
+
def initialize params
|
63
|
+
@WORDSIZE_BITS = 2
|
64
|
+
@WORDSIZE = 1 << @WORDSIZE_BITS
|
65
|
+
@CODE_ALIGNMENT = 4
|
66
|
+
@DATA_ALIGNMENT = @WORDSIZE
|
67
|
+
@FUNCTION_ALIGNMENT = @WORDSIZE
|
68
|
+
@STACK_ALIGNMENT_BITS = 3
|
69
|
+
@STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
|
70
|
+
|
71
|
+
@INITIAL_FRAME_SIZE = 2 * @WORDSIZE
|
72
|
+
@NREGISTER_ARGS = 4
|
73
|
+
@NREGISTER_LOCALS = 6
|
74
|
+
@FP = :r11
|
75
|
+
@RETURN = :r0
|
76
|
+
@TEMPORARY = :r12
|
77
|
+
@constants = []
|
78
|
+
@frame_offset = 0
|
79
|
+
@frame_size = 0
|
80
|
+
@function_end_label = nil
|
81
|
+
@imports = {}
|
82
|
+
@if_labels = []
|
83
|
+
@saved_registers = []
|
84
|
+
super params
|
85
|
+
@output_file_suffix = '.s'
|
86
|
+
@features.merge! \
|
87
|
+
:'bits-per-word' => '32',
|
88
|
+
:'byte-order' => 'little-endian',
|
89
|
+
:'bytes-per-word' => 4
|
90
|
+
end
|
91
|
+
|
92
|
+
# Create an entry in the constants table,
|
93
|
+
# returning the label that will refer to the constant.
|
94
|
+
# The value may be an integer or a label.
|
95
|
+
def add_constant value
|
96
|
+
label = gensym
|
97
|
+
@constants << [label, value]
|
98
|
+
label
|
99
|
+
end
|
100
|
+
|
101
|
+
def align alignment = nil
|
102
|
+
unless alignment
|
103
|
+
# Get default alignment
|
104
|
+
case @section
|
105
|
+
when :code
|
106
|
+
alignment = @CODE_ALIGNMENT
|
107
|
+
when :data
|
108
|
+
alignment = @DATA_ALIGNMENT
|
109
|
+
when :function
|
110
|
+
alignment = @FUNCTION_ALIGNMENT
|
111
|
+
else
|
112
|
+
# Use data alignment as default
|
113
|
+
alignment = @DATA_ALIGNMENT
|
114
|
+
end
|
115
|
+
end
|
116
|
+
emit ".align #{alignment}\n" unless alignment == 0
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns the fp-relative offset for the nth (0-based) argument.
|
120
|
+
def arg_offset n
|
121
|
+
(n - @NREGISTER_ARGS) * @WORDSIZE
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns an fp-relative reference for the nth (0-based) argument.
|
125
|
+
def arg_reference n
|
126
|
+
offset_reference arg_offset(n)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Return the register in which the nth (0-based) argument is stored, or
|
130
|
+
# nil if not stored in a register
|
131
|
+
def arg_register n
|
132
|
+
# The first @NREGISTER_ARGS arguments are in the v registers,
|
133
|
+
# the rest are on the stack.
|
134
|
+
if register_arg? n
|
135
|
+
"v#{n + 1}"
|
136
|
+
else
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Test if op is a binary operation
|
142
|
+
def assymetric_binop? op
|
143
|
+
[:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Test if a value is an at-expression
|
147
|
+
def at_expr? value
|
148
|
+
value.respond_to?(:[]) && value[0] == :'@'
|
149
|
+
end
|
150
|
+
|
151
|
+
def auto_bytes value, register
|
152
|
+
if value.kind_of? Integer
|
153
|
+
grow_frame value
|
154
|
+
else
|
155
|
+
temporary = register == :sp ? @TEMPORARY : register
|
156
|
+
load_value_into_register value, temporary
|
157
|
+
auto_bytes_register temporary
|
158
|
+
end
|
159
|
+
emit "cpy #{register}, sp\n" unless register == :sp
|
160
|
+
end
|
161
|
+
|
162
|
+
# auto-bytes where the value is supplied in a register and the return
|
163
|
+
# value will be in sp. register must not be sp.
|
164
|
+
def auto_bytes_register register
|
165
|
+
temporary = register == @TEMPORARY ? :r3 : @TEMPORARY
|
166
|
+
emit "add #{register}, #{register}, \##{@STACK_ALIGNMENT - 1}\n"
|
167
|
+
emit "mvn #{temporary}, \##{@STACK_ALIGNMENT - 1}\n"
|
168
|
+
emit "and #{register}, #{register}, #{temporary}\n"
|
169
|
+
emit "sub sp, #{temp1}\n"
|
170
|
+
end
|
171
|
+
|
172
|
+
def auto_words value, register
|
173
|
+
if value.kind_of? Integer
|
174
|
+
auto_bytes(value * @WORDSIZE, register)
|
175
|
+
else
|
176
|
+
temporary = register == :sp ? @TEMPORARY : register
|
177
|
+
load_value_into_register value, temporary
|
178
|
+
emit "lsl #{temporary}, #{temporary}, \##{@WORDSIZE_BITS}\n"
|
179
|
+
auto_bytes_register temporary
|
180
|
+
emit "cpy #{register}, sp\n" unless register == :sp
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Begins a new block.
|
185
|
+
def begin_block *code
|
186
|
+
emit "# begin block\n"
|
187
|
+
# If we are starting a block at top level, create a frame
|
188
|
+
if @environment == @top_level
|
189
|
+
nlocals = count_locals code
|
190
|
+
create_frame nlocals, false
|
191
|
+
end
|
192
|
+
@environment = Environment.new @environment
|
193
|
+
end
|
194
|
+
|
195
|
+
# Emit function prologue and declare _formals_ as function arguments
|
196
|
+
def begin_function formals, nlocals
|
197
|
+
if @environment != @top_level
|
198
|
+
raise "Can only begin a function at top level"
|
199
|
+
end
|
200
|
+
|
201
|
+
@function_end_label = gensym
|
202
|
+
emit "# function #{formals.join ' '}\n"
|
203
|
+
environment = Environment.new @environment
|
204
|
+
formals.each_with_index do |formal, i|
|
205
|
+
if i < @NREGISTER_ARGS
|
206
|
+
environment.add_arg formal, arg_register(i)
|
207
|
+
else
|
208
|
+
environment.add_arg formal, arg_offset(i)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
@environment = environment
|
212
|
+
emit_function_prologue formals, nlocals
|
213
|
+
end
|
214
|
+
|
215
|
+
# Test if op is a binary operation
|
216
|
+
def binop? op
|
217
|
+
assymetric_binop?(op) || symmetric_binop?(op)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Define a byte with the given value
|
221
|
+
def byte value
|
222
|
+
emit ".byte #{value}\n"
|
223
|
+
end
|
224
|
+
|
225
|
+
# Call a function.
|
226
|
+
def call func, *args
|
227
|
+
emit "# call #{func} #{args.join ' '}\n"
|
228
|
+
|
229
|
+
# Calculate how many arguments need to be pushed on
|
230
|
+
# the stack, and allocate space for them.
|
231
|
+
nstack_args = number_of_stack_arguments args.length
|
232
|
+
old_frame_offset = @frame_offset
|
233
|
+
old_frame_size = @frame_size
|
234
|
+
grow_frame nstack_args if nstack_args > 0
|
235
|
+
|
236
|
+
# Put stack arguments on the stack
|
237
|
+
(@NREGISTER_ARGS...args.length).each do |n|
|
238
|
+
load_value_into_register args[n], @TEMPORARY
|
239
|
+
emit "str #{@TEMPORARY}, " +
|
240
|
+
"[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
|
241
|
+
end
|
242
|
+
|
243
|
+
# Put register arguments in the right registers
|
244
|
+
nregister_args = number_of_register_arguments args.length
|
245
|
+
nregister_args.times do |n|
|
246
|
+
load_value_into_register args[n], :"a#{n + 1}"
|
247
|
+
end
|
248
|
+
|
249
|
+
# Call function
|
250
|
+
if global? func
|
251
|
+
emit "bl #{func}\n"
|
252
|
+
else
|
253
|
+
func_reg = load_value func
|
254
|
+
emit "blx #{func_reg}\n"
|
255
|
+
end
|
256
|
+
|
257
|
+
# Restore original stack frame
|
258
|
+
if old_frame_size != @frame_size
|
259
|
+
emit "add sp, sp, \##{@frame_size - old_frame_size}\n"
|
260
|
+
@frame_offset = old_frame_offset
|
261
|
+
@frame_size = old_frame_size
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Creates a stack frame for the given number of arguments
|
266
|
+
# and local variables.
|
267
|
+
def create_frame nvars, save_lr = true
|
268
|
+
# Calculate how many variables we will store in registers,
|
269
|
+
# and how many on the stack.
|
270
|
+
nregister_vars = [nvars, @NREGISTER_LOCALS].min
|
271
|
+
nstack_vars = nvars - nregister_vars
|
272
|
+
|
273
|
+
# Save the registers we will clobber to the stack.
|
274
|
+
clobbered = []
|
275
|
+
nregister_vars.times do |i|
|
276
|
+
clobbered << :"v#{i < 5 ? i + 1 : i + 2}"
|
277
|
+
end
|
278
|
+
clobbered << @FP
|
279
|
+
clobbered << :lr if save_lr
|
280
|
+
@saved_registers = clobbered
|
281
|
+
emit "stmfd sp!, {#{clobbered.join ', '}}\n"
|
282
|
+
emit "add #{@FP}, sp, \##{clobbered.length * @WORDSIZE}\n"
|
283
|
+
|
284
|
+
# Calculate frame size so that the stack pointer will
|
285
|
+
# be properly aligned at the end of emit_function_prologue.
|
286
|
+
@frame_size = stack_align((clobbered.length + nstack_vars) * @WORDSIZE)
|
287
|
+
extra_space = @frame_size - clobbered.length * @WORDSIZE
|
288
|
+
if extra_space > 0
|
289
|
+
emit "sub sp, sp, \##{extra_space}\n"
|
290
|
+
end
|
291
|
+
@frame_offset = 0
|
292
|
+
end
|
293
|
+
|
294
|
+
# Start a conditional using the specified branch instruction
|
295
|
+
# after the comparison.
|
296
|
+
def common_if comp, x, y = nil
|
297
|
+
emit "# #{comp} #{x} #{y}\n"
|
298
|
+
|
299
|
+
xreg = load_value x, @TEMPORARY
|
300
|
+
yreg = load_value y, :a4
|
301
|
+
|
302
|
+
falselabel = @environment.gensym
|
303
|
+
@if_labels.push falselabel
|
304
|
+
|
305
|
+
emit "cmp #{xreg}, #{yreg}\n"
|
306
|
+
|
307
|
+
lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
|
308
|
+
:ifle => "bgt", :iflt => "bge", :ifne => "beq" }
|
309
|
+
emit "#{lut[comp]} #{falselabel}\n"
|
310
|
+
end
|
311
|
+
|
312
|
+
# Counts the number of local variables created in
|
313
|
+
# a sequence of statements.
|
314
|
+
def count_locals statements
|
315
|
+
count = 0
|
316
|
+
each_statement(statements) do |statement|
|
317
|
+
if statement[0] == :let
|
318
|
+
# let introduces a single local
|
319
|
+
count = count + 1
|
320
|
+
end
|
321
|
+
end
|
322
|
+
count
|
323
|
+
end
|
324
|
+
|
325
|
+
# Destroys the current stack frame.
|
326
|
+
# If ret is true, loads the saved value of lr into pc.
|
327
|
+
def destroy_frame ret = false
|
328
|
+
# Set sp back to where saved registers were stored
|
329
|
+
saved = @saved_registers
|
330
|
+
emit "sub sp, #{@FP}, \##{saved.length * @WORDSIZE}\n"
|
331
|
+
|
332
|
+
if ret
|
333
|
+
index = saved.index :lr
|
334
|
+
if index
|
335
|
+
saved[index] = :pc
|
336
|
+
else
|
337
|
+
raise "Request to load saved lr into pc, but lr has not been saved"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
emit "ldmfd sp!, {#{saved.join ', '}}\n"
|
341
|
+
|
342
|
+
emit_constants if ret
|
343
|
+
end
|
344
|
+
|
345
|
+
# Writes any constants that need to be written to the instruction
|
346
|
+
# stream, and clears the list of constants that need to be written.
|
347
|
+
def emit_constants
|
348
|
+
@constants.each do |x|
|
349
|
+
label x[0]
|
350
|
+
word x[1]
|
351
|
+
end
|
352
|
+
@constants = []
|
353
|
+
end
|
354
|
+
|
355
|
+
# Emit function prologue.
|
356
|
+
def emit_function_prologue formals = [], nlocals = 0
|
357
|
+
# Calculate the number of arguments we were passed in
|
358
|
+
# registers, the total number of values we need to save
|
359
|
+
# on the stack, then create a stack frame and save
|
360
|
+
# the v registers we will be using.
|
361
|
+
nregister_args = [formals.length, @NREGISTER_ARGS].min
|
362
|
+
nvars = nregister_args + nlocals
|
363
|
+
create_frame nvars, true
|
364
|
+
|
365
|
+
# Move arguments that were passed in registers into
|
366
|
+
# callee-save registers.
|
367
|
+
nregister_args.times do |i|
|
368
|
+
emit "cpy v#{i + 1}, a#{i + 1}\n"
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Ends the current block.
|
373
|
+
def end_block
|
374
|
+
emit "# end block\n"
|
375
|
+
|
376
|
+
# If we are returning to top level, restore stack pointer
|
377
|
+
# and saved registers.
|
378
|
+
if @environment.parent == @top_level
|
379
|
+
offset = @frame_size - @saved_registers.length * @WORDSIZE
|
380
|
+
if offset > 0
|
381
|
+
emit "add sp, sp, \##{offset}\n"
|
382
|
+
end
|
383
|
+
emit "ldmfd sp!, {#{@saved_registers.join ', '}}\n"
|
384
|
+
@frame_size = 0
|
385
|
+
@frame_offset = 0
|
386
|
+
@saved_registers = []
|
387
|
+
|
388
|
+
# If we need to emit constants, do so now
|
389
|
+
unless @constants.empty?
|
390
|
+
lbl = gensym
|
391
|
+
goto lbl
|
392
|
+
label lbl
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# Restore old value of @environment
|
397
|
+
@environment = @environment.parent
|
398
|
+
end
|
399
|
+
|
400
|
+
# Ends a function body.
|
401
|
+
def end_function
|
402
|
+
if @environment == @top_level
|
403
|
+
raise "Cannot end function when not in a function"
|
404
|
+
end
|
405
|
+
|
406
|
+
emit "# function epilogue\n"
|
407
|
+
label @function_end_label
|
408
|
+
|
409
|
+
destroy_frame true
|
410
|
+
@frame_size = 0
|
411
|
+
@frame_offset = 0
|
412
|
+
@saved_registers = []
|
413
|
+
emit "# end function\n\n"
|
414
|
+
@environment = @top_level
|
415
|
+
end
|
416
|
+
|
417
|
+
# Ends a conditional.
|
418
|
+
def end_if
|
419
|
+
label @if_labels.pop
|
420
|
+
end
|
421
|
+
|
422
|
+
# Evaluate the binary operation expr and store the result in register
|
423
|
+
def eval_binop expr, register
|
424
|
+
op = expr[0]
|
425
|
+
|
426
|
+
# Emulation for div and mod, which ARM does not have instructions for
|
427
|
+
case op
|
428
|
+
when :div
|
429
|
+
func = :"__aeabi_idiv"
|
430
|
+
import func unless @imports.has_key? func
|
431
|
+
call func, expr[1], expr[2]
|
432
|
+
emit "cpy #{register}, r0\n" if register != :r0
|
433
|
+
return
|
434
|
+
when :mod
|
435
|
+
func = :"__aeabi_idivmod"
|
436
|
+
import func unless @imports.has_key? func
|
437
|
+
call func, expr[1], expr[2]
|
438
|
+
emit "cpy #{register}, r1\n" if register != :r1
|
439
|
+
return
|
440
|
+
end
|
441
|
+
|
442
|
+
x = load_value expr[1], :a4
|
443
|
+
y = load_value expr[2], @TEMPORARY
|
444
|
+
|
445
|
+
case op
|
446
|
+
when :bsr
|
447
|
+
emit "lsr #{register}, #{x}, #{y}\n"
|
448
|
+
when :or
|
449
|
+
emit "orr #{register}, #{x}, #{y}\n"
|
450
|
+
when :rol
|
451
|
+
emit "rsb #{y}, #{y}, #32\n"
|
452
|
+
emit "ror #{register}, #{x}, #{y}\n"
|
453
|
+
when :shl
|
454
|
+
emit "lsl #{register}, #{x}, #{y}\n"
|
455
|
+
when :shr
|
456
|
+
emit "lsr #{register}, #{x}, #{y}\n"
|
457
|
+
when :xor
|
458
|
+
emit "eor #{register}, #{x}, #{y}\n"
|
459
|
+
else
|
460
|
+
emit "#{expr[0]} #{register}, #{x}, #{y}\n"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# Evaluates the expression +expr+ and stores the result in +register+.
|
465
|
+
def eval_expr expr, register
|
466
|
+
if expr.length == 1
|
467
|
+
# Load value
|
468
|
+
load_value_into_register expr[0], register
|
469
|
+
else
|
470
|
+
# Evaluate expression
|
471
|
+
op = expr[0]
|
472
|
+
case op
|
473
|
+
when :'auto-bytes'
|
474
|
+
auto_bytes expr[1], register
|
475
|
+
when :'auto-words'
|
476
|
+
auto_words expr[1], register
|
477
|
+
when :call
|
478
|
+
call *expr[1..-1]
|
479
|
+
emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
|
480
|
+
when :'get-byte'
|
481
|
+
get_byte expr[1], expr[2], register
|
482
|
+
when :'get-word'
|
483
|
+
get_word expr[1], expr[2], register
|
484
|
+
when :not
|
485
|
+
load_value_into_register expr[1], register
|
486
|
+
emit "mvn #{@TEMPORARY}, #0\n"
|
487
|
+
emit "eor #{register}, #{register}, #{@TEMPORARY}\n"
|
488
|
+
else
|
489
|
+
if binop? op
|
490
|
+
eval_binop expr, register
|
491
|
+
else
|
492
|
+
raise "Not a magic word: #{op}"
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
# Export symbols from the current section
|
499
|
+
def export *symbols
|
500
|
+
symbols.each { |sym| emit ".globl #{sym}\n" }
|
501
|
+
end
|
502
|
+
|
503
|
+
# Add a function to the current section
|
504
|
+
def function formals, *code
|
505
|
+
nlocals = count_locals code
|
506
|
+
begin_function formals, nlocals
|
507
|
+
code.each { |action| add section, action }
|
508
|
+
end_function
|
509
|
+
end
|
510
|
+
|
511
|
+
# Load byte from _base_ + _offset_ into _register_
|
512
|
+
def get_byte base, offset, register
|
513
|
+
# If base is an integer, but offset isn't, swap them
|
514
|
+
if !integer?(offset) && integer?(base)
|
515
|
+
base, offset = [offset, base]
|
516
|
+
end
|
517
|
+
|
518
|
+
if integer? offset
|
519
|
+
base_reg = load_value base
|
520
|
+
if offset == 0
|
521
|
+
emit "ldrb #{register}, [#{base_reg}]\n"
|
522
|
+
else
|
523
|
+
emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
|
524
|
+
end
|
525
|
+
else
|
526
|
+
base_reg = load_value base
|
527
|
+
offset_reg = load_value offset, :a4
|
528
|
+
emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
# Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
|
533
|
+
def get_word base, offset, register
|
534
|
+
if integer? offset
|
535
|
+
base_reg = load_value base
|
536
|
+
if offset == 0
|
537
|
+
emit "ldr #{register}, [#{base_reg}]\n"
|
538
|
+
else
|
539
|
+
emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
|
540
|
+
end
|
541
|
+
else
|
542
|
+
base_reg = load_value base
|
543
|
+
offset_reg = load_value offset, :a4
|
544
|
+
emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
# Test if a symbol refers to a global
|
549
|
+
def global? symbol
|
550
|
+
symbol?(symbol) && @environment[symbol] == nil
|
551
|
+
end
|
552
|
+
|
553
|
+
# Jump to a label.
|
554
|
+
def goto label
|
555
|
+
emit "b #{label}\n"
|
556
|
+
|
557
|
+
# If we have constants that need to be emitted, do so now
|
558
|
+
emit_constants
|
559
|
+
end
|
560
|
+
|
561
|
+
# Grows the current frame by n words, plus padding to
|
562
|
+
# respect alignment rules.
|
563
|
+
def grow_frame nwords
|
564
|
+
increment = stack_align(nwords * @WORDSIZE)
|
565
|
+
emit "sub sp, sp, \##{increment}\n"
|
566
|
+
@frame_size = @frame_size + increment
|
567
|
+
@frame_offset = @frame_offset + increment
|
568
|
+
end
|
569
|
+
|
570
|
+
# Start the false path of a conditional.
|
571
|
+
def ifelse
|
572
|
+
emit "# else\n"
|
573
|
+
newlabel = @environment.gensym
|
574
|
+
goto newlabel
|
575
|
+
lbl = @if_labels.pop
|
576
|
+
label lbl
|
577
|
+
@if_labels.push newlabel
|
578
|
+
end
|
579
|
+
|
580
|
+
# Test if x is equal to y
|
581
|
+
def ifeq x, y
|
582
|
+
common_if :ifeq, x, y
|
583
|
+
end
|
584
|
+
|
585
|
+
# Test if x is greater than or equal to y
|
586
|
+
def ifge x, y
|
587
|
+
common_if :ifge, x, y
|
588
|
+
end
|
589
|
+
|
590
|
+
# Test if x is strictly greater than y
|
591
|
+
def ifgt x, y
|
592
|
+
common_if :ifgt, x, y
|
593
|
+
end
|
594
|
+
|
595
|
+
# Test if x is less than or equal to y
|
596
|
+
def ifle x, y
|
597
|
+
common_if :ifle, x, y
|
598
|
+
end
|
599
|
+
|
600
|
+
# Test if x is strictly less than y
|
601
|
+
def iflt x, y
|
602
|
+
common_if :iflt, x, y
|
603
|
+
end
|
604
|
+
|
605
|
+
# Test if x different from y
|
606
|
+
def ifne x, y
|
607
|
+
common_if :ifne, x, y
|
608
|
+
end
|
609
|
+
|
610
|
+
# Import labels into the current section
|
611
|
+
def import *symbols
|
612
|
+
# Record imported labels in @imports
|
613
|
+
symbols.each { |sym| @imports[sym] = sym }
|
614
|
+
end
|
615
|
+
|
616
|
+
# Test if a value is an integer
|
617
|
+
def integer? value
|
618
|
+
value.kind_of? Integer
|
619
|
+
end
|
620
|
+
|
621
|
+
# Emit a label
|
622
|
+
def label name
|
623
|
+
emit "#{name}:\n"
|
624
|
+
end
|
625
|
+
|
626
|
+
# Introduce a new local variable
|
627
|
+
def let symbol, *expr
|
628
|
+
n = @environment.locals
|
629
|
+
register = local_register n
|
630
|
+
|
631
|
+
if register
|
632
|
+
# We will use a register to store the value
|
633
|
+
@environment.add_local symbol, register
|
634
|
+
eval_expr expr, register
|
635
|
+
else
|
636
|
+
# We will use the stack to store the value
|
637
|
+
offset = local_offset n
|
638
|
+
@environment.add_local symbol, offset
|
639
|
+
eval_expr expr, @TEMPORARY
|
640
|
+
emit "str #{@TEMPORARY}, #{offset_reference offset}\n"
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
# Load the value at the given address.
|
645
|
+
def load_at address, register = @TEMPORARY
|
646
|
+
load_value_into_register address, register
|
647
|
+
emit "ldr #{register}, [#{register}]\n"
|
648
|
+
register
|
649
|
+
end
|
650
|
+
|
651
|
+
# Load a value into a register.
|
652
|
+
# Returns the name of the register.
|
653
|
+
# If the value was already in a register, the name of that
|
654
|
+
# register is returned.
|
655
|
+
# Else, the value is loaded into a register and the name of
|
656
|
+
# that register is returned. The register to use in that case
|
657
|
+
# may be specified using the optional second argument.
|
658
|
+
def load_value x, register = @TEMPORARY
|
659
|
+
if integer? x
|
660
|
+
if x >= 0 && x <= 255
|
661
|
+
emit "mov #{register}, \##{x}\n"
|
662
|
+
return register
|
663
|
+
elsif x >= -255 && x < 0
|
664
|
+
emit "mvn #{register}, \##{-(x + 1)}\n"
|
665
|
+
return register
|
666
|
+
else
|
667
|
+
lbl = add_constant x
|
668
|
+
emit "ldr #{register}, #{lbl}\n"
|
669
|
+
return register
|
670
|
+
end
|
671
|
+
elsif symbol? x
|
672
|
+
binding = @environment[x]
|
673
|
+
if binding.kind_of? String
|
674
|
+
# Value is already in a register. Return register name.
|
675
|
+
return binding
|
676
|
+
elsif binding.kind_of? Integer
|
677
|
+
# Value is on the stack. Load from the stack.
|
678
|
+
emit "ldr #{register}, #{offset_reference binding}\n"
|
679
|
+
return register
|
680
|
+
else
|
681
|
+
# Assume global
|
682
|
+
lbl = add_constant x
|
683
|
+
emit "ldr #{register}, #{lbl}\n"
|
684
|
+
return register
|
685
|
+
end
|
686
|
+
elsif at_expr? x
|
687
|
+
load_at x[1], register
|
688
|
+
else
|
689
|
+
raise "Don't know how to load #{x.inspect}"
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
# Load a value into a specific register
|
694
|
+
def load_value_into_register x, register
|
695
|
+
reg = load_value x, register
|
696
|
+
if reg != register
|
697
|
+
emit "cpy #{register}, #{reg}\n"
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
# Returns the fp-relative reference for the nth (0-based) local.
|
702
|
+
def local_offset n
|
703
|
+
-@INITIAL_FRAME_SIZE - (n * @WORDSIZE)
|
704
|
+
end
|
705
|
+
|
706
|
+
# Return the register in which the nth local (0-based) is stored, or
|
707
|
+
# nil if not stored in a register
|
708
|
+
def local_register n
|
709
|
+
if register_local? n
|
710
|
+
n = n + number_of_register_arguments
|
711
|
+
if n < 5
|
712
|
+
"v#{n + 1}"
|
713
|
+
else
|
714
|
+
"v#{n + 2}"
|
715
|
+
end
|
716
|
+
else
|
717
|
+
nil
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
# Calculate the number of register arguments,
|
722
|
+
# given the total number of arguments.
|
723
|
+
def number_of_register_arguments n = @environment.args
|
724
|
+
[n, @NREGISTER_ARGS].min
|
725
|
+
end
|
726
|
+
|
727
|
+
# Calculate the number of stack arguments,
|
728
|
+
# given the total number of arguments.
|
729
|
+
def number_of_stack_arguments n = @environment.args
|
730
|
+
[0, n - @NREGISTER_ARGS].max
|
731
|
+
end
|
732
|
+
|
733
|
+
# Given an offset, returns an fp-relative reference.
|
734
|
+
def offset_reference offset
|
735
|
+
"[#{@FP}, \##{offset}]"
|
736
|
+
end
|
737
|
+
|
738
|
+
# Returns true if the nth (0-based) argument is stored in a register
|
739
|
+
def register_arg? n
|
740
|
+
n < @NREGISTER_ARGS
|
741
|
+
end
|
742
|
+
|
743
|
+
# Returns true if the nth (0-based) local is stored in a register
|
744
|
+
def register_local? n
|
745
|
+
(n + number_of_register_arguments) < @NREGISTER_LOCALS
|
746
|
+
end
|
747
|
+
|
748
|
+
# Returns from a function.
|
749
|
+
#
|
750
|
+
# _words_ may contain an expression to be evaluated. The result
|
751
|
+
# of the evaluation is returned from the function.
|
752
|
+
def ret *words
|
753
|
+
emit "# return #{words.join ' '}\n"
|
754
|
+
# Compute return value and store it in @RETURN
|
755
|
+
eval_expr(words, @RETURN) unless words.empty?
|
756
|
+
# Go to epilogue
|
757
|
+
goto @function_end_label
|
758
|
+
end
|
759
|
+
|
760
|
+
# Set a variable to the result of evaluating an expression
|
761
|
+
def set symbol, *expr
|
762
|
+
if at_expr? symbol
|
763
|
+
eval_expr expr, :r3
|
764
|
+
register = load_value symbol[1]
|
765
|
+
emit "str r3, [#{register}]\n"
|
766
|
+
else
|
767
|
+
x = @environment[symbol]
|
768
|
+
if x == nil
|
769
|
+
raise "Cannot change value of constant #{symbol}"
|
770
|
+
elsif x.kind_of? String
|
771
|
+
eval_expr expr, x
|
772
|
+
else
|
773
|
+
eval_expr expr, @TEMPORARY
|
774
|
+
emit "str #{@TEMPORARY}, #{offset_reference x}\n"
|
775
|
+
end
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
# Set the byte at _base_ + _offset_ to _value_
|
780
|
+
def set_byte base, offset, value
|
781
|
+
emit "# set-byte #{base} #{offset} #{value}\n"
|
782
|
+
# If base is an integer, but offset isn't, swap them
|
783
|
+
if !integer?(offset) && integer?(base)
|
784
|
+
base, offset = [offset, base]
|
785
|
+
end
|
786
|
+
|
787
|
+
if integer? offset
|
788
|
+
base_reg = load_value base, :a4
|
789
|
+
load_value_into_register value, @TEMPORARY
|
790
|
+
if offset == 0
|
791
|
+
emit "strb #{@TEMPORARY}, [#{base_reg}]\n"
|
792
|
+
else
|
793
|
+
emit "strb #{@TEMPORARY}, [#{base_reg}, \##{offset}]\n"
|
794
|
+
end
|
795
|
+
else
|
796
|
+
eval_binop [:add, base, offset], :a4
|
797
|
+
load_value_into_register value, @TEMPORARY
|
798
|
+
emit "strb #{@TEMPORARY}, [a4]\n"
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
# Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
|
803
|
+
def set_word base, offset, value
|
804
|
+
emit "# set-word #{base} #{offset} #{value}\n"
|
805
|
+
# If base is an integer, but offset isn't, swap them
|
806
|
+
if !integer?(offset) && integer?(base)
|
807
|
+
base, offset = [offset, base]
|
808
|
+
end
|
809
|
+
|
810
|
+
if integer? offset
|
811
|
+
base_reg = load_value base, :a4
|
812
|
+
load_value_into_register value, @TEMPORARY
|
813
|
+
if offset == 0
|
814
|
+
emit "str #{@TEMPORARY}, [#{base_reg}]\n"
|
815
|
+
else
|
816
|
+
emit "str #{@TEMPORARY}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
|
817
|
+
end
|
818
|
+
else
|
819
|
+
load_value_into_register base, :a4
|
820
|
+
load_value_into_register offset, @TEMPORARY
|
821
|
+
emit "add a4, a4, #{@TEMPORARY}, LSL #2\n"
|
822
|
+
load_value_into_register value, @TEMPORARY
|
823
|
+
emit "str #{@TEMPORARY}, [a4]\n"
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
# Given n, returns the nearest multiple of @STACK_ALIGNMENT
|
828
|
+
# that is >= n.
|
829
|
+
def stack_align n
|
830
|
+
(n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
|
831
|
+
end
|
832
|
+
|
833
|
+
# Define a string with the given value
|
834
|
+
def string value
|
835
|
+
code = ''
|
836
|
+
value.each_byte do |b|
|
837
|
+
if b == 92
|
838
|
+
code << "\\\\"
|
839
|
+
elsif b >= 32 && b < 127 && b != 34
|
840
|
+
code << b.chr
|
841
|
+
else
|
842
|
+
code << sprintf("\\%03o", b)
|
843
|
+
end
|
844
|
+
end
|
845
|
+
emit ".ascii \"#{code}\"\n"
|
846
|
+
end
|
847
|
+
|
848
|
+
# Test if a value is a symbol
|
849
|
+
def symbol? value
|
850
|
+
value.kind_of? Symbol
|
851
|
+
end
|
852
|
+
|
853
|
+
# Test if op is a symmetric binary operation (i.e. it will yield the
|
854
|
+
# same result if the order of its source operands is changed).
|
855
|
+
def symmetric_binop? op
|
856
|
+
[:add, :and, :mul, :or, :xor].member? op
|
857
|
+
end
|
858
|
+
|
859
|
+
# Call a function, re-using the current call frame if possible.
|
860
|
+
def tail_call func, *args
|
861
|
+
emit "# tail-call #{func} #{args.join ' '}\n"
|
862
|
+
|
863
|
+
# Compute number of stack arguments
|
864
|
+
nstackargs = number_of_stack_arguments args.length
|
865
|
+
# If we need more stack arguments than we have now,
|
866
|
+
# perform a normal call and return
|
867
|
+
if nstackargs > number_of_stack_arguments(@environment.args)
|
868
|
+
emit "# Not enough space for proper tail call; using regular call\n"
|
869
|
+
ret :call, func, *args
|
870
|
+
end
|
871
|
+
|
872
|
+
# We will assign arguments from left to right.
|
873
|
+
# Find places that we will overwrite before we read them,
|
874
|
+
# and store their values in some newly allocated stack space.
|
875
|
+
old_frame_offset = @frame_offset
|
876
|
+
old_frame_size = @frame_size
|
877
|
+
overwritten = {}
|
878
|
+
(@NREGISTER_ARGS...args.length).each do |i|
|
879
|
+
arg = args[i]
|
880
|
+
arg = arg[1] if at_expr? arg
|
881
|
+
if symbol?(arg)
|
882
|
+
binding = @environment[arg]
|
883
|
+
if binding[0] == :arg && binding[1] >= @NREGISTER_ARGS &&
|
884
|
+
binding[1] < i
|
885
|
+
# Argument i is a stack argument, but the value we will assign
|
886
|
+
# it is a stack argument that comes before it, so we will
|
887
|
+
# have overwritten it by the time we get to it.
|
888
|
+
overwritten[arg] = nil
|
889
|
+
end
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
unless overwritten.empty?
|
894
|
+
# Allocate space for arguments to be saved
|
895
|
+
grow_frame overwritten.length
|
896
|
+
# Save values
|
897
|
+
offset = 0
|
898
|
+
overwritten.each_key do |key|
|
899
|
+
reg = load_value key
|
900
|
+
emit "str #{reg}, [sp, \##{offset}]\n"
|
901
|
+
overwritten[key] = offset
|
902
|
+
offset = offset + @WORDSIZE
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
# Assign arguments
|
907
|
+
args.each_index do |i|
|
908
|
+
arg = args[i]
|
909
|
+
if register_arg? i
|
910
|
+
load_value_into_register arg, "a#{i + 1}"
|
911
|
+
else
|
912
|
+
# Test if this is a value we saved
|
913
|
+
sym = at_expr?(arg) ? arg[1] : arg
|
914
|
+
saved = overwritten[sym]
|
915
|
+
if saved
|
916
|
+
# Saved value, load from stack
|
917
|
+
reg = @TEMPORARY
|
918
|
+
emit "ldr #{reg}, [sp, \##{saved}]\n"
|
919
|
+
else
|
920
|
+
# Regular value, use load_value
|
921
|
+
reg = load_value arg
|
922
|
+
end
|
923
|
+
emit "str #{reg}, #{arg_reference i}\n"
|
924
|
+
end
|
925
|
+
end
|
926
|
+
|
927
|
+
# Load address of function to be called into @TEMPORARY
|
928
|
+
load_value_into_register func, @TEMPORARY
|
929
|
+
|
930
|
+
# Destroy current activation frame and enter func
|
931
|
+
destroy_frame false
|
932
|
+
emit "bx #{@TEMPORARY}\n"
|
933
|
+
emit_constants
|
934
|
+
end
|
935
|
+
|
936
|
+
# Define a word with the given value
|
937
|
+
def word value
|
938
|
+
emit ".int #{value}\n"
|
939
|
+
end
|
940
|
+
|
941
|
+
# Write generated code to the given IO object.
|
942
|
+
def write io
|
943
|
+
@sections.each do |section,code|
|
944
|
+
unless code.empty?
|
945
|
+
io.puts ".section #{section.to_s}"
|
946
|
+
io.puts code
|
947
|
+
io.puts
|
948
|
+
end
|
949
|
+
end
|
950
|
+
end
|
951
|
+
end
|
952
|
+
|
953
|
+
# Register class for little endian ARM
|
954
|
+
Voodoo::CodeGenerator.register_generator ARMGasGenerator,
|
955
|
+
:architecture => :arm,
|
956
|
+
:format => :gas
|
957
|
+
end
|
data/lib/voodoo/parser.rb
CHANGED
@@ -305,7 +305,7 @@ module Voodoo
|
|
305
305
|
def parse_symbol
|
306
306
|
wrap_exceptions do
|
307
307
|
name = ''
|
308
|
-
while
|
308
|
+
while lookahead != :eof
|
309
309
|
case lookahead
|
310
310
|
when "\\"
|
311
311
|
name << parse_escape
|
@@ -525,6 +525,8 @@ module Voodoo
|
|
525
525
|
# Else, nil is returned.
|
526
526
|
def try_parse_token
|
527
527
|
case lookahead
|
528
|
+
when :eof
|
529
|
+
nil
|
528
530
|
when /\d|-/
|
529
531
|
# Digit; parse number
|
530
532
|
parse_number
|
@@ -606,8 +608,10 @@ module Voodoo
|
|
606
608
|
raise
|
607
609
|
rescue => e
|
608
610
|
# Some other error; wrap in ParserInternalError.
|
609
|
-
|
610
|
-
|
611
|
+
wrapped = ParserInternalError.new(e, @input_name, @line,
|
612
|
+
@column, @text)
|
613
|
+
wrapped.set_backtrace e.backtrace
|
614
|
+
raise wrapped
|
611
615
|
end
|
612
616
|
end
|
613
617
|
|
metadata
CHANGED
@@ -1,96 +1,81 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: voodoo
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 1.0.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.2
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Robbert Haarman
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2012-01-29 00:00:00 -08:00
|
19
|
-
default_executable:
|
12
|
+
date: 2012-11-15 00:00:00.000000000 Z
|
20
13
|
dependencies: []
|
14
|
+
description: ! 'The Voodoo compiler is an implementation of the Voodoo programming
|
21
15
|
|
22
|
-
description: |
|
23
|
-
The Voodoo compiler is an implementation of the Voodoo programming
|
24
16
|
language, a simple low-level programming language.
|
25
|
-
|
17
|
+
|
18
|
+
|
26
19
|
The compiler can be used both as a stand-alone program to
|
20
|
+
|
27
21
|
compile Voodoo source code and as a Ruby module to generate code on
|
22
|
+
|
28
23
|
the fly. Multiple target platforms are supported.
|
29
24
|
|
25
|
+
'
|
30
26
|
email: voodoo@inglorion.net
|
31
|
-
executables:
|
27
|
+
executables:
|
32
28
|
- voodooc
|
33
29
|
extensions: []
|
34
|
-
|
35
30
|
extra_rdoc_files: []
|
36
|
-
|
37
|
-
files:
|
31
|
+
files:
|
38
32
|
- bin/voodooc
|
39
|
-
- lib/voodoo.rb
|
40
|
-
- lib/voodoo/parser.rb
|
41
|
-
- lib/voodoo/code_generator.rb
|
42
|
-
- lib/voodoo/compiler.rb
|
43
33
|
- lib/voodoo/config.rb
|
34
|
+
- lib/voodoo/parser.rb
|
44
35
|
- lib/voodoo/validator.rb
|
36
|
+
- lib/voodoo/generators/arm_gas_generator.rb~
|
45
37
|
- lib/voodoo/generators/common_code_generator.rb
|
46
|
-
- lib/voodoo/generators/mips_gas_generator.rb
|
47
|
-
- lib/voodoo/generators/i386_elf_generator.rb
|
48
|
-
- lib/voodoo/generators/command_postprocessor.rb
|
49
38
|
- lib/voodoo/generators/mips_elf_generator.rb
|
50
|
-
- lib/voodoo/generators/
|
51
|
-
- lib/voodoo/generators/
|
52
|
-
- lib/voodoo/generators/arm_gas_generator.rb
|
53
|
-
- lib/voodoo/generators/amd64_nasm_generator.rb
|
39
|
+
- lib/voodoo/generators/command_postprocessor.rb
|
40
|
+
- lib/voodoo/generators/nasm_generator.rb
|
54
41
|
- lib/voodoo/generators/nasm_elf_generator.rb
|
55
42
|
- lib/voodoo/generators/amd64_elf_generator.rb
|
56
|
-
- lib/voodoo/generators/
|
43
|
+
- lib/voodoo/generators/arm_elf_generator.rb
|
44
|
+
- lib/voodoo/generators/arm_gas_generator.rb
|
57
45
|
- lib/voodoo/generators/i386_nasm_generator.rb
|
58
|
-
|
46
|
+
- lib/voodoo/generators/mips_gas_generator.rb
|
47
|
+
- lib/voodoo/generators/gas_elf_generator.rb
|
48
|
+
- lib/voodoo/generators/i386_elf_generator.rb
|
49
|
+
- lib/voodoo/generators/amd64_nasm_generator.rb
|
50
|
+
- lib/voodoo/code_generator.rb
|
51
|
+
- lib/voodoo/compiler.rb
|
52
|
+
- lib/voodoo.rb
|
59
53
|
homepage: http://inglorion.net/software/voodoo/
|
60
54
|
licenses: []
|
61
|
-
|
62
55
|
post_install_message:
|
63
|
-
rdoc_options:
|
56
|
+
rdoc_options:
|
64
57
|
- -m
|
65
58
|
- Voodoo
|
66
59
|
- -t
|
67
60
|
- Voodoo Code Generator API Documentation
|
68
|
-
require_paths:
|
61
|
+
require_paths:
|
69
62
|
- lib
|
70
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
64
|
none: false
|
72
|
-
requirements:
|
73
|
-
- -
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
|
76
|
-
|
77
|
-
- 0
|
78
|
-
version: "0"
|
79
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
70
|
none: false
|
81
|
-
requirements:
|
82
|
-
- -
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
|
85
|
-
segments:
|
86
|
-
- 0
|
87
|
-
version: "0"
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
88
75
|
requirements: []
|
89
|
-
|
90
76
|
rubyforge_project: voodoo
|
91
|
-
rubygems_version: 1.
|
77
|
+
rubygems_version: 1.8.23
|
92
78
|
signing_key:
|
93
79
|
specification_version: 3
|
94
80
|
summary: Compiler for the Voodoo programming language
|
95
81
|
test_files: []
|
96
|
-
|