voodoo 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|