voodoo 0.6.3 → 0.7.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/lib/voodoo/generators/amd64_nasm_generator.rb +4 -0
- data/lib/voodoo/generators/common_code_generator.rb +51 -1
- data/lib/voodoo/generators/i386_nasm_generator.rb +4 -0
- data/lib/voodoo/generators/mips_gas_generator.rb +72 -9
- data/lib/voodoo/generators/nasm_generator.rb +75 -15
- data/lib/voodoo/parser.rb +53 -12
- metadata +5 -5
@@ -55,11 +55,14 @@ module Voodoo
|
|
55
55
|
# of the following methods, which must be implemented by subclasses:
|
56
56
|
#
|
57
57
|
# - #align
|
58
|
+
# - #asr
|
59
|
+
# - #bsr
|
58
60
|
# - #byte
|
59
61
|
# - #call
|
60
62
|
# - #end_if
|
61
63
|
# - #export
|
62
64
|
# - #begin_function
|
65
|
+
# - #block
|
63
66
|
# - #ifelse
|
64
67
|
# - #ifeq
|
65
68
|
# - #ifge
|
@@ -71,9 +74,13 @@ module Voodoo
|
|
71
74
|
# - #label
|
72
75
|
# - #let
|
73
76
|
# - #ret
|
77
|
+
# - #rol
|
78
|
+
# - #ror
|
74
79
|
# - #set
|
75
80
|
# - #set_byte
|
76
81
|
# - #set_word
|
82
|
+
# - #shl
|
83
|
+
# - #shr
|
77
84
|
# - #string
|
78
85
|
# - #word
|
79
86
|
#
|
@@ -82,6 +89,8 @@ module Voodoo
|
|
82
89
|
code.each do |action|
|
83
90
|
keyword, args = action[0], action[1..-1]
|
84
91
|
case keyword
|
92
|
+
when :block
|
93
|
+
block *args
|
85
94
|
when :function
|
86
95
|
function args[0], *args[1..-1]
|
87
96
|
when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
|
@@ -121,6 +130,34 @@ module Voodoo
|
|
121
130
|
add :functions, [:function, formals] + code
|
122
131
|
end
|
123
132
|
|
133
|
+
# Processes code in its own block. Local variables can be
|
134
|
+
# introduced inside the block. They will be deleted at the
|
135
|
+
# end of the block.
|
136
|
+
def block *code
|
137
|
+
begin_block *code
|
138
|
+
code.each { |action| add section, action }
|
139
|
+
end_block
|
140
|
+
end
|
141
|
+
|
142
|
+
# Invokes block with each statement in the given list of statements.
|
143
|
+
# This iterator also descends into nested statements, calling
|
144
|
+
# block first with the outer statement, and then for each inner
|
145
|
+
# statement.
|
146
|
+
def each_statement statements, &block
|
147
|
+
statements.each do |statement|
|
148
|
+
yield statement
|
149
|
+
case statement[0]
|
150
|
+
when :block
|
151
|
+
each_statement statement[1..-1], &block
|
152
|
+
when :function
|
153
|
+
each_statement statement[2..-1], &block
|
154
|
+
when :ifeq, :ifle, :iflt, :ifge, :ifgt, :ifne
|
155
|
+
each_statement statement[2], &block
|
156
|
+
each_statement(statement[3], &block) if statement.length > 3
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
124
161
|
# Add a function to the current section
|
125
162
|
def function formals, *code
|
126
163
|
begin_function *formals
|
@@ -197,8 +234,11 @@ module Voodoo
|
|
197
234
|
class Environment
|
198
235
|
@@gensym_counter = 0
|
199
236
|
|
200
|
-
attr_reader :args, :locals, :symbols
|
237
|
+
attr_reader :args, :locals, :parent, :symbols
|
201
238
|
|
239
|
+
# Creates a new environment.
|
240
|
+
# If parent is specified, it will become the new environment's parent.
|
241
|
+
# The new environment inherits all symbols from the parent environment.
|
202
242
|
def initialize parent = nil
|
203
243
|
## Parent environment
|
204
244
|
@parent = parent
|
@@ -210,37 +250,47 @@ module Voodoo
|
|
210
250
|
@locals = parent ? parent.locals : 0
|
211
251
|
end
|
212
252
|
|
253
|
+
# Adds symbol as an argument in this environment.
|
213
254
|
def add_arg symbol
|
214
255
|
@symbols[symbol] = [:arg, @args]
|
215
256
|
@args = @args + 1
|
216
257
|
end
|
217
258
|
|
259
|
+
# Adds each of symbols to the arguments in
|
260
|
+
# this environment.
|
218
261
|
def add_args symbols
|
219
262
|
symbols.each { |sym| add_arg sym }
|
220
263
|
end
|
221
264
|
|
265
|
+
# Adds symbol as a local variable in this environment.
|
222
266
|
def add_local symbol
|
223
267
|
@symbols[symbol] = [:local, @locals]
|
224
268
|
@locals = @locals + 1
|
225
269
|
end
|
226
270
|
|
271
|
+
# Adds each of symbols to the local variables in
|
272
|
+
# this environment.
|
227
273
|
def add_locals symbols
|
228
274
|
symbols.each { |sym| add_local sym }
|
229
275
|
end
|
230
276
|
|
277
|
+
# Generates a new, unique symbol.
|
231
278
|
def gensym
|
232
279
|
Environment.gensym
|
233
280
|
end
|
234
281
|
|
282
|
+
# Looks up symbol in this environment.
|
235
283
|
def [] symbol
|
236
284
|
@symbols[symbol]
|
237
285
|
end
|
238
286
|
|
287
|
+
# Generates a new, unique symbol.
|
239
288
|
def self.gensym
|
240
289
|
@@gensym_counter = @@gensym_counter + 1
|
241
290
|
"_G#{@@gensym_counter}".to_sym
|
242
291
|
end
|
243
292
|
|
293
|
+
# Returns an initial, top-level environment.
|
244
294
|
def self.initial_environment
|
245
295
|
Environment.new
|
246
296
|
end
|
@@ -160,7 +160,7 @@ module Voodoo
|
|
160
160
|
|
161
161
|
# Test if op is a binary operation
|
162
162
|
def assymetric_binop? op
|
163
|
-
[:div, :mod, :sub].member?(op)
|
163
|
+
[:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
|
164
164
|
end
|
165
165
|
|
166
166
|
# Test if a value is an at-expression
|
@@ -168,6 +168,17 @@ module Voodoo
|
|
168
168
|
value.respond_to?(:[]) && value[0] == :'@'
|
169
169
|
end
|
170
170
|
|
171
|
+
# Begins a new block.
|
172
|
+
def begin_block *code
|
173
|
+
emit "# begin block\n"
|
174
|
+
# If we are at top-level, create a frame
|
175
|
+
if @environment == @top_level
|
176
|
+
create_frame count_locals(code)
|
177
|
+
end
|
178
|
+
environment = Environment.new @environment
|
179
|
+
@environment = environment
|
180
|
+
end
|
181
|
+
|
171
182
|
# Emit function prologue and declare _formals_ as function arguments
|
172
183
|
def begin_function formals, nlocals
|
173
184
|
if @environment != @top_level
|
@@ -282,6 +293,25 @@ module Voodoo
|
|
282
293
|
emit "nop\n"
|
283
294
|
end
|
284
295
|
|
296
|
+
# Counts the number of local variables created in
|
297
|
+
# a sequence of statements.
|
298
|
+
def count_locals statements
|
299
|
+
count = 0
|
300
|
+
each_statement(statements) do |statement|
|
301
|
+
if statement[0] == :let
|
302
|
+
# let introduces a single local
|
303
|
+
count = count + 1
|
304
|
+
end
|
305
|
+
end
|
306
|
+
count
|
307
|
+
end
|
308
|
+
|
309
|
+
# Creates an activation frame which can store nlocals local variables
|
310
|
+
def create_frame nlocals
|
311
|
+
@frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
|
312
|
+
emit "addiu $sp, $sp, -#{@frame_size}\n"
|
313
|
+
end
|
314
|
+
|
285
315
|
# Emit function prologue.
|
286
316
|
def emit_function_prologue formals = [], nlocals = 0
|
287
317
|
# Calculate new value for $gp
|
@@ -295,9 +325,7 @@ module Voodoo
|
|
295
325
|
emit "sw $#{n + @REGISTER_ARG_BASE}, #{n * @WORDSIZE}($29)\n"
|
296
326
|
end
|
297
327
|
|
298
|
-
|
299
|
-
@frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
|
300
|
-
emit "addiu $sp, $sp, -#{@frame_size}\n"
|
328
|
+
create_frame nlocals
|
301
329
|
|
302
330
|
# Save return address
|
303
331
|
emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
|
@@ -333,6 +361,19 @@ module Voodoo
|
|
333
361
|
@environment = @top_level
|
334
362
|
end
|
335
363
|
|
364
|
+
# Ends the current block.
|
365
|
+
def end_block
|
366
|
+
emit "# end block\n"
|
367
|
+
|
368
|
+
# If we are returning to top level, restore stack pointer
|
369
|
+
if @environment.parent == @top_level
|
370
|
+
emit "addiu $sp, $sp, #{@frame_size}\n"
|
371
|
+
end
|
372
|
+
|
373
|
+
# Restore old value of @environment
|
374
|
+
@environment = @environment.parent
|
375
|
+
end
|
376
|
+
|
336
377
|
# End a conditional.
|
337
378
|
def end_if
|
338
379
|
label = @if_labels.pop
|
@@ -348,6 +389,10 @@ module Voodoo
|
|
348
389
|
temporaries.delete y
|
349
390
|
|
350
391
|
case expr[0]
|
392
|
+
when :asr
|
393
|
+
emit "srav #{register}, #{x}, #{y}\n"
|
394
|
+
when :bsr
|
395
|
+
emit "srlv #{register}, #{x}, #{y}\n"
|
351
396
|
when :div
|
352
397
|
emit "div $0, #{x}, #{y}\n"
|
353
398
|
emit "mflo #{register}\n"
|
@@ -357,6 +402,20 @@ module Voodoo
|
|
357
402
|
when :mul
|
358
403
|
emit "mult #{x}, #{y}\n"
|
359
404
|
emit "mflo #{register}\n"
|
405
|
+
when :rol
|
406
|
+
emit "subu #{@TEMPORARY}, $0, #{y}\n"
|
407
|
+
emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
|
408
|
+
emit "sllv #{register}, #{x}, #{y}\n"
|
409
|
+
emit "or #{register}, #{register}, #{@TEMPORARY}\n"
|
410
|
+
when :ror
|
411
|
+
emit "subu #{@TEMPORARY}, $0, #{y}\n"
|
412
|
+
emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
|
413
|
+
emit "srlv #{register}, #{x}, #{y}\n"
|
414
|
+
emit "or #{register}, #{register}, #{@TEMPORARY}\n"
|
415
|
+
when :shl
|
416
|
+
emit "sllv #{register}, #{x}, #{y}\n"
|
417
|
+
when :shr
|
418
|
+
emit "srlv #{register}, #{x}, #{y}\n"
|
360
419
|
else
|
361
420
|
emit "#{expr[0]} #{register}, #{x}, #{y}\n"
|
362
421
|
end
|
@@ -397,9 +456,7 @@ module Voodoo
|
|
397
456
|
|
398
457
|
# Add a function to the current section
|
399
458
|
def function formals, *code
|
400
|
-
|
401
|
-
nlocals = 0
|
402
|
-
code.each { |action| nlocals = nlocals + 1 if action[0] == :let }
|
459
|
+
nlocals = count_locals code
|
403
460
|
begin_function formals, nlocals
|
404
461
|
code.each { |action| add section, action }
|
405
462
|
end_function
|
@@ -514,9 +571,13 @@ module Voodoo
|
|
514
571
|
n = @environment.locals
|
515
572
|
@environment.add_local symbol
|
516
573
|
|
517
|
-
# Extend stack frame, if necessary
|
574
|
+
# Extend stack frame, if necessary.
|
575
|
+
# This should never be necessary, because we allocate enough
|
576
|
+
# space in the top-level function or block.
|
518
577
|
if @environment.locals > max_locals
|
519
578
|
$stderr.puts "WARNING: Stack frame too small. Possible BUG"
|
579
|
+
$stderr.puts "Frame size #{@frame_size}, max locals #{max_locals}" +
|
580
|
+
", actual locals #{@environment.locals}"
|
520
581
|
# Extend frame by 8 bytes, to keep $sp a multiple of 8
|
521
582
|
emit "addiu $sp, $sp, -8\n"
|
522
583
|
@frame_size = @frame_size + 8
|
@@ -525,11 +586,13 @@ module Voodoo
|
|
525
586
|
register = local_register n
|
526
587
|
ref = local_reference n
|
527
588
|
if register
|
589
|
+
# We will use a register to store the value
|
528
590
|
# Save current value of register
|
529
591
|
emit "sw #{register}, #{ref}\n"
|
530
592
|
# Set new value
|
531
593
|
eval_expr expr, register
|
532
594
|
else
|
595
|
+
# We will use the stack to store the value
|
533
596
|
eval_expr expr, @TEMPORARY
|
534
597
|
emit "sw #{@TEMPORARY}, #{ref}\n"
|
535
598
|
end
|
@@ -651,7 +714,7 @@ module Voodoo
|
|
651
714
|
def ret *words
|
652
715
|
emit "# return #{words.join ' '}\n"
|
653
716
|
# Compute return value and store it in @RETURN
|
654
|
-
eval_expr
|
717
|
+
eval_expr(words, @RETURN) unless words.empty?
|
655
718
|
# Go to epilogue
|
656
719
|
goto @function_end_label
|
657
720
|
end
|
@@ -21,7 +21,7 @@ module Voodoo
|
|
21
21
|
# - @SCRATCH_REG
|
22
22
|
# - @WORD_NAME
|
23
23
|
# - @WORDSIZE
|
24
|
-
# - @AX, @BX, @CX, and @
|
24
|
+
# - @AX, @BX, @CX, @DX, @BP, and @SP
|
25
25
|
class NasmGenerator < CommonCodeGenerator
|
26
26
|
def initialize params = {}
|
27
27
|
super params
|
@@ -166,11 +166,43 @@ module Voodoo
|
|
166
166
|
# of the evaluation is returned from the function.
|
167
167
|
def ret *words
|
168
168
|
emit "; return #{words.join ' '}\n"
|
169
|
-
eval_expr words
|
169
|
+
eval_expr(words) unless words.empty?
|
170
170
|
emit_function_epilogue
|
171
171
|
emit "ret\n"
|
172
172
|
end
|
173
173
|
|
174
|
+
#
|
175
|
+
# == Blocks
|
176
|
+
#
|
177
|
+
|
178
|
+
# Begins a new block.
|
179
|
+
def begin_block *code
|
180
|
+
emit "; begin block\n"
|
181
|
+
# If entering a block at top level,
|
182
|
+
# Save @BP, then set @BP to @SP
|
183
|
+
if @environment == @top_level
|
184
|
+
emit "push #{@BP}\n"
|
185
|
+
emit "mov #{@BP}, #{@SP}\n"
|
186
|
+
end
|
187
|
+
environment = Environment.new @environment
|
188
|
+
@environment = environment
|
189
|
+
end
|
190
|
+
|
191
|
+
# Ends the current block.
|
192
|
+
def end_block
|
193
|
+
emit "; end block\n"
|
194
|
+
|
195
|
+
# De-allocate block's variables
|
196
|
+
nvars = @environment.locals - @environment.parent.locals
|
197
|
+
emit "add #{@SP}, #{nvars * @WORDSIZE}\n" unless nvars == 0
|
198
|
+
|
199
|
+
# Restore old value of @environment
|
200
|
+
@environment = @environment.parent
|
201
|
+
|
202
|
+
# If returning to top level, restore old @BP
|
203
|
+
emit "pop #{@BP}\n" if @environment == @top_level
|
204
|
+
end
|
205
|
+
|
174
206
|
#
|
175
207
|
# == Conditionals
|
176
208
|
#
|
@@ -192,7 +224,8 @@ module Voodoo
|
|
192
224
|
|
193
225
|
# Test if op is a binary operation
|
194
226
|
def binop? op
|
195
|
-
[:div, :mod, :sub].member?(op) ||
|
227
|
+
[:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op) ||
|
228
|
+
symmetric_operation?(op)
|
196
229
|
end
|
197
230
|
|
198
231
|
# Test if a value is an integer
|
@@ -416,18 +449,24 @@ module Voodoo
|
|
416
449
|
return mod(target, x, y) if op == :mod
|
417
450
|
return mul(target, x, y) if op == :mul
|
418
451
|
|
419
|
-
target_ref = load_value target, @
|
452
|
+
target_ref = load_value target, @BX
|
420
453
|
x_ref = load_value x, @DX
|
421
|
-
y_ref = load_value y, @
|
454
|
+
y_ref = load_value y, @CX
|
455
|
+
|
456
|
+
mnemonic = action_to_mnemonic op
|
457
|
+
if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
|
458
|
+
emit "mov cl, #{y_ref}\n" unless y_ref == @CX
|
459
|
+
y_ref = 'cl'
|
460
|
+
end
|
422
461
|
|
423
462
|
if memory_operand?(target_ref)
|
424
463
|
if memory_operand?(x_ref) || memory_operand?(y_ref)
|
425
|
-
emit "mov #{@
|
426
|
-
emit "#{
|
427
|
-
emit "mov #{target_ref}, #{@
|
464
|
+
emit "mov #{@AX}, #{x_ref}\n"
|
465
|
+
emit "#{mnemonic} #{@AX}, #{y_ref}\n"
|
466
|
+
emit "mov #{target_ref}, #{@AX}\n"
|
428
467
|
else
|
429
468
|
emit "mov #{@WORD_NAME} #{target_ref}, #{x_ref}\n"
|
430
|
-
emit "#{
|
469
|
+
emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
|
431
470
|
end
|
432
471
|
else
|
433
472
|
raise "Can't happen: target_ref is #{target_ref.inspect}"
|
@@ -443,13 +482,17 @@ module Voodoo
|
|
443
482
|
return mod2(target, y) if op == :mod
|
444
483
|
return mul2(target, y) if op == :mul
|
445
484
|
|
446
|
-
target_ref = load_value target, @
|
447
|
-
y_ref = load_value y, @
|
448
|
-
|
449
|
-
|
450
|
-
emit "
|
485
|
+
target_ref = load_value target, @AX
|
486
|
+
y_ref = load_value y, @CX
|
487
|
+
mnemonic = action_to_mnemonic op
|
488
|
+
if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
|
489
|
+
emit "mov cl, #{y_ref}\n" unless y_ref == @CX
|
490
|
+
emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, cl\n"
|
491
|
+
elsif memory_operand?(target_ref) && memory_operand?(y_ref)
|
492
|
+
emit "mov #{@CX}, #{y_ref}\n"
|
493
|
+
emit "#{mnemonic} #{target_ref}, #{@CX}\n"
|
451
494
|
else
|
452
|
-
emit "#{
|
495
|
+
emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
|
453
496
|
end
|
454
497
|
end
|
455
498
|
|
@@ -510,6 +553,7 @@ module Voodoo
|
|
510
553
|
|
511
554
|
# Evaluate an expression.
|
512
555
|
# The result is stored in _register_ (@RETURN_REG by default).
|
556
|
+
# The following registers may be clobbered: @AX, @BX, @CX, @DX
|
513
557
|
def eval_expr words, register = @RETURN_REG
|
514
558
|
if words.length == 1
|
515
559
|
if words[0] == 0
|
@@ -520,6 +564,10 @@ module Voodoo
|
|
520
564
|
else
|
521
565
|
op = words[0]
|
522
566
|
case op
|
567
|
+
when :asr, :bsr, :rol, :ror, :shl, :shr
|
568
|
+
load_value_into_register words[2], @CX
|
569
|
+
load_value_into_register words[1], register
|
570
|
+
emit "#{action_to_mnemonic op} #{register}, cl\n"
|
523
571
|
when :call
|
524
572
|
call *words[1..-1]
|
525
573
|
when :div
|
@@ -666,6 +714,18 @@ module Voodoo
|
|
666
714
|
# == Miscellaneous
|
667
715
|
#
|
668
716
|
|
717
|
+
# Translates a Voodoo action name to an x86 mnemonic
|
718
|
+
def action_to_mnemonic action
|
719
|
+
case action
|
720
|
+
when :asr
|
721
|
+
:sar
|
722
|
+
when :bsr
|
723
|
+
:shr
|
724
|
+
else
|
725
|
+
action
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
669
729
|
# Emit a comment
|
670
730
|
def comment text
|
671
731
|
emit ";#{text}\n"
|
data/lib/voodoo/parser.rb
CHANGED
@@ -139,16 +139,10 @@ module Voodoo
|
|
139
139
|
body = parse_body :function
|
140
140
|
[:function, words[1..-1], body]
|
141
141
|
elsif is_conditional?(words[0])
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
true_body = true_body[0..-2]
|
147
|
-
false_body = parse_body :conditional
|
148
|
-
else
|
149
|
-
false_body = []
|
150
|
-
end
|
151
|
-
[words[0], words[1..-1], true_body, false_body]
|
142
|
+
parse_conditional1 words[0], words[1..-1]
|
143
|
+
elsif words[0] == :block
|
144
|
+
body = parse_body :block
|
145
|
+
[:block] + body
|
152
146
|
else
|
153
147
|
# Statement or data declaration; simply return it
|
154
148
|
words
|
@@ -173,7 +167,7 @@ module Voodoo
|
|
173
167
|
break
|
174
168
|
elsif kind == :conditional && statement[0] == :else
|
175
169
|
# Done parsing body, but there is another one coming up
|
176
|
-
body <<
|
170
|
+
body << statement
|
177
171
|
break
|
178
172
|
else
|
179
173
|
# Parsed a statement. Add it to body.
|
@@ -293,6 +287,53 @@ module Voodoo
|
|
293
287
|
def is_conditional? symbol
|
294
288
|
[:ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne].member? symbol
|
295
289
|
end
|
296
|
-
end
|
297
290
|
|
291
|
+
#
|
292
|
+
# Private methods
|
293
|
+
#
|
294
|
+
private
|
295
|
+
|
296
|
+
# Parses a conditional statement
|
297
|
+
def parse_conditional1 condition, operands
|
298
|
+
# Parse first clause and condition for next clause
|
299
|
+
consequent, next_condition = split_if_clause parse_body(:conditional)
|
300
|
+
if next_condition == nil
|
301
|
+
# No next clause
|
302
|
+
alternative = []
|
303
|
+
elsif next_condition == :else
|
304
|
+
# Next clause is else without if
|
305
|
+
alternative = parse_body :conditional
|
306
|
+
else
|
307
|
+
# Next clause is else with if
|
308
|
+
alternative = [parse_conditional1(next_condition[0],
|
309
|
+
next_condition[1])]
|
310
|
+
end
|
311
|
+
[condition, operands, consequent, alternative]
|
312
|
+
end
|
313
|
+
|
314
|
+
# Splits a parsed if-clause into two parts:
|
315
|
+
# 1. The list of statements making up the clause proper
|
316
|
+
# 2. The condition for the next clause:
|
317
|
+
# - If there is no next clause, nil
|
318
|
+
# - If the next clause is introduced by else without a condition, :else
|
319
|
+
# - If the next clause is introduced by else iflt x y, [:iflt [:x, :y]]
|
320
|
+
# - And so on for other if.. instances
|
321
|
+
def split_if_clause clause
|
322
|
+
last = clause[-1]
|
323
|
+
if last.respond_to?(:[]) && last.length > 0 && last[0] == :else
|
324
|
+
clause = clause[0..-2]
|
325
|
+
if last.length > 1
|
326
|
+
# Else if
|
327
|
+
[clause, [last[1], last[2..-1]]]
|
328
|
+
else
|
329
|
+
# Only else
|
330
|
+
[clause, :else]
|
331
|
+
end
|
332
|
+
else
|
333
|
+
# No else
|
334
|
+
[clause, nil]
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
298
339
|
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: 3
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 7
|
9
|
+
- 0
|
10
|
+
version: 0.7.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: 2011-01-15 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|