rogue_parser 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,725 @@
1
+ require 'stringio'
2
+ require 'racc/parser'
3
+ require 'sexp'
4
+ require 'strscan'
5
+
6
+ # WHY do I have to do this?!?
7
+ class Regexp
8
+ ONCE = 0 # 16 # ?
9
+ ENC_NONE = /x/n.options
10
+ ENC_EUC = /x/e.options
11
+ ENC_SJIS = /x/s.options
12
+ ENC_UTF8 = /x/u.options
13
+ end
14
+
15
+ class Fixnum
16
+ def ord
17
+ self # I hate ruby 1.9 string changes
18
+ end
19
+ end
20
+
21
+ class StringScanner
22
+ # if ENV['TALLY'] then
23
+ # alias :old_getch :getch
24
+ # def getch
25
+ # warn({:getch => caller[0]}.inspect)
26
+ # old_getch
27
+ # end
28
+ # end
29
+
30
+ def current_line # HAHA fuck you (HACK)
31
+ string[0..pos][/\A.*__LINE__/m].split(/\n/).size
32
+ end
33
+
34
+ def lineno
35
+ string[0..pos].split(/\n/).size
36
+ end
37
+
38
+ def unread c
39
+ return if c.nil? # UGH
40
+ warn({:unread => caller[0]}.inspect) if ENV['TALLY']
41
+ string[pos, 0] = c
42
+ end
43
+
44
+ def unread_many str
45
+ warn({:unread_many => caller[0]}.inspect) if ENV['TALLY']
46
+ string[pos, 0] = str
47
+ end
48
+
49
+ def was_begin_of_line
50
+ pos <= 2 or string[pos-2] == ?\n
51
+ end
52
+ end
53
+
54
+ class RubyParser < Racc::Parser
55
+ VERSION = '1.0.1'
56
+
57
+ attr_accessor :lexer, :in_def, :in_single, :file
58
+ attr_reader :env, :comments
59
+
60
+ def append_to_block head, tail # FIX: wtf is this?!? switch to block_append
61
+ return head if tail.nil?
62
+ return tail if head.nil?
63
+
64
+ head = s(:block, head) unless head.node_type == :block
65
+ head << tail
66
+ end
67
+
68
+ def arg_add(node1, node2)
69
+ return s(:array, node2) unless node1
70
+ return node1 << node2 if node1[0] == :array
71
+ return s(:argspush, node1, node2)
72
+ end
73
+
74
+ def arg_blk_pass node1, node2
75
+ if node2 then
76
+ node2.insert 1, node1
77
+ return node2
78
+ else
79
+ node1
80
+ end
81
+ end
82
+
83
+ def arg_concat node1, node2
84
+ return node2.nil? ? node1 : s(:argscat, node1, node2)
85
+ end
86
+
87
+ def aryset receiver, index
88
+ s(:attrasgn, receiver, :"[]=", index)
89
+ end
90
+
91
+ def assignable(lhs, value = nil)
92
+ id = lhs.to_sym
93
+ id = id.to_sym if Sexp === id
94
+
95
+ raise SyntaxError, "Can't change the value of #{id}" if
96
+ id.to_s =~ /^(?:self|nil|true|false|__LINE__|__FILE__)$/
97
+
98
+ result = case id.to_s
99
+ when /^@@/ then
100
+ asgn = in_def || in_single > 0
101
+ s((asgn ? :cvasgn : :cvdecl), id)
102
+ when /^@/ then
103
+ s(:iasgn, id)
104
+ when /^\$/ then
105
+ s(:gasgn, id)
106
+ when /^[A-Z]/ then
107
+ s(:cdecl, id)
108
+ else
109
+ case self.env[id]
110
+ when :lvar then
111
+ s(:lasgn, id)
112
+ when :dvar, nil then
113
+ if self.env.current[id] == :dvar then
114
+ s(:dasgn_curr, id)
115
+ elsif self.env[id] == :dvar then
116
+ self.env.use(id)
117
+ s(:dasgn, id)
118
+ elsif ! self.env.dynamic? then
119
+ s(:lasgn, id)
120
+ else
121
+ s(:dasgn_curr, id)
122
+ end
123
+ else
124
+ raise "wtf? unknown type: #{self.env[id]}"
125
+ end
126
+ end
127
+
128
+ self.env[id] = (self.env.dynamic? ? :dvar : :lvar) unless self.env[id]
129
+
130
+ result << value if value
131
+
132
+ return result
133
+ end
134
+
135
+ def block_append(head, tail, strip_tail_block=false)
136
+ return head unless tail
137
+ return tail unless head
138
+
139
+ case head[0]
140
+ when :lit, :str then
141
+ return tail
142
+ end
143
+
144
+ head = remove_begin(head)
145
+ head = s(:block, head) unless head[0] == :block
146
+
147
+ if strip_tail_block and Sexp === tail and tail[0] == :block then
148
+ head.push(*tail.values)
149
+ else
150
+ head << tail
151
+ end
152
+ end
153
+
154
+ def cond node
155
+ return nil if node.nil?
156
+ node = value_expr node
157
+
158
+ case node.first
159
+ when :dregex then
160
+ return s(:match2, node, s(:gvar, "$_".to_sym))
161
+ when :regex then
162
+ return s(:match, node)
163
+ when :lit then
164
+ if Regexp === node.last then
165
+ return s(:match, node)
166
+ else
167
+ return node
168
+ end
169
+ when :and then
170
+ return s(:and, cond(node[1]), cond(node[2]))
171
+ when :or then
172
+ return s(:or, cond(node[1]), cond(node[2]))
173
+ when :dot2 then
174
+ label = "flip#{node.hash}"
175
+ env[label] = self.env.dynamic? ? :dvar : :lvar
176
+ return s(:flip2, node[1], node[2])
177
+ when :dot3 then
178
+ label = "flip#{node.hash}"
179
+ env[label] = self.env.dynamic? ? :dvar : :lvar
180
+ return s(:flip3, node[1], node[2])
181
+ else
182
+ return node
183
+ end
184
+ end
185
+
186
+ ##
187
+ # for pure ruby systems only
188
+
189
+ def do_parse
190
+ _racc_do_parse_rb(_racc_setup, false)
191
+ end if ENV['PURE_RUBY']
192
+
193
+ def get_match_node lhs, rhs
194
+ if lhs then
195
+ case lhs[0]
196
+ when :dregx, :dregx_once then
197
+ return s(:match2, lhs, rhs)
198
+ when :lit then
199
+ return s(:match2, lhs, rhs) if Regexp === lhs.last
200
+ end
201
+ end
202
+
203
+ if rhs then
204
+ case rhs[0]
205
+ when :dregx, :dregx_once then
206
+ return s(:match3, rhs, lhs)
207
+ when :lit then
208
+ return s(:match3, rhs, lhs) if Regexp === rhs.last
209
+ end
210
+ end
211
+
212
+ return s(:call, lhs, :"=~", s(:array, rhs))
213
+ end
214
+
215
+ def gettable(id)
216
+ id = id.to_sym if Sexp === id # HACK
217
+ id = id.to_sym if String === id # HACK
218
+
219
+ return s(:self) if id == :self
220
+ return s(:nil) if id == :nil
221
+ return s(:true) if id == :true
222
+ return s(:false) if id == :false
223
+ return s(:str, self.file) if id == :"__FILE__"
224
+ return s(:lit, lexer.src.current_line) if id == :"__LINE__"
225
+
226
+ result = case id.to_s
227
+ when /^@@/ then
228
+ s(:cvar, id)
229
+ when /^@/ then
230
+ s(:ivar, id)
231
+ when /^\$/ then
232
+ s(:gvar, id)
233
+ when /^[A-Z]/ then
234
+ s(:const, id)
235
+ else
236
+ type = env[id]
237
+ if type then
238
+ s(type, id)
239
+ elsif env.dynamic? and :dvar == env[id] then
240
+ s(:dvar, id)
241
+ else
242
+ s(:vcall, id)
243
+ end
244
+ end
245
+
246
+ return result if result
247
+
248
+ raise "identifier #{id.inspect} is not valid"
249
+ end
250
+
251
+ def initialize
252
+ super
253
+ self.lexer = RubyLexer.new
254
+ self.in_def = false
255
+ self.in_single = 0
256
+ @env = Environment.new
257
+ @comments = []
258
+ end
259
+
260
+ def list_append list, item # TODO: nuke me *sigh*
261
+ return s(:array, item) unless list
262
+ list << item
263
+ end
264
+
265
+ def literal_concat head, tail
266
+ return tail unless head
267
+ return head unless tail
268
+
269
+ htype, ttype = head[0], tail[0]
270
+
271
+ head = s(:dstr, '', head) if htype == :evstr
272
+
273
+ case ttype
274
+ when :str then
275
+ if htype == :str
276
+ head[-1] << tail[-1]
277
+ elsif htype == :dstr and head.size == 2 then
278
+ head[-1] << tail[-1]
279
+ else
280
+ head << tail
281
+ end
282
+ when :dstr then
283
+ if htype == :str then
284
+ tail[1] = head[-1] + tail[1]
285
+ head = tail
286
+ else
287
+ tail[0] = :array
288
+ tail[1] = s(:str, tail[1])
289
+ tail.delete_at 1 if tail[1] == s(:str, '')
290
+
291
+ head.push(*tail[1..-1])
292
+ end
293
+ when :evstr then
294
+ head[0] = :dstr if htype == :str
295
+ if head.size == 2 and tail[1][0] == :str then
296
+ head[-1] << tail[1][-1]
297
+ head[0] = :str if head.size == 2 # HACK ?
298
+ else
299
+ head.push(tail)
300
+ end
301
+ end
302
+
303
+ return head
304
+ end
305
+
306
+ def logop(type, left, right) # TODO: rename logical_op
307
+ left = value_expr left
308
+
309
+ if left and left[0] == type and not left.paren then
310
+ node, second = left, nil
311
+
312
+ while (second = node[2]) && second[0] == type and not second.paren do
313
+ node = second
314
+ end
315
+
316
+ node[2] = s(type, second, right)
317
+
318
+ return left
319
+ end
320
+
321
+ return s(type, left, right)
322
+ end
323
+
324
+ def new_call recv, meth, args = nil # REFACTOR - merge with fcall
325
+ if args && args[0] == :block_pass then
326
+ new_args = args.array(true) || args.argscat(true) || args.splat(true)
327
+ call = s(:call, recv, meth)
328
+ call << new_args if new_args
329
+ args << call
330
+
331
+ return args
332
+ end
333
+ result = s(:call, recv, meth)
334
+ result << args if args
335
+ result
336
+ end
337
+
338
+ def new_fcall meth, args
339
+ if args and args[0] == :block_pass then
340
+ new_args = args.array(true) || args.argscat(true) || args.splat(true)
341
+ call = s(:fcall, meth)
342
+ call << new_args if new_args
343
+ args << call
344
+ return args
345
+ end
346
+
347
+ r = s(:fcall, meth)
348
+ r << args if args and args != s(:array)
349
+ r
350
+ end
351
+
352
+ def new_super args
353
+ if args && args.node_type == :block_pass then
354
+ t, body, bp = args
355
+ result = s(t, bp, s(:super, body))
356
+ else
357
+ result = s(:super)
358
+ result << args if args and args != s(:array)
359
+ end
360
+ result
361
+ end
362
+
363
+ def new_yield(node)
364
+ if node then
365
+ raise SyntaxError, "Block argument should not be given." if
366
+ node.node_type == :block_pass
367
+
368
+ node = node.last if node.node_type == :array and node.size == 2
369
+ end
370
+
371
+ return s(:yield, node)
372
+ end
373
+
374
+ def next_token
375
+ if self.lexer.advance then
376
+ [self.lexer.token, self.lexer.yacc_value]
377
+ else
378
+ return [false, '$end']
379
+ end
380
+ end
381
+
382
+ def node_assign(lhs, rhs)
383
+ return nil unless lhs
384
+
385
+ rhs = value_expr rhs
386
+
387
+ case lhs[0]
388
+ when :gasgn, :iasgn, :lasgn, :dasgn, :dasgn_curr,
389
+ :masgn, :cdecl, :cvdecl, :cvasgn then
390
+ lhs << rhs
391
+ when :attrasgn, :call then
392
+ args = lhs.array(true) || lhs.argscat(true) || lhs.splat(true) # FIX: fragile
393
+ lhs << arg_add(args, rhs)
394
+ end
395
+
396
+ lhs
397
+ end
398
+
399
+ def parse(str, file = "(string)")
400
+ raise "bad val: #{str.inspect}" unless String === str
401
+
402
+ self.file = file
403
+ self.lexer.src = str
404
+
405
+ @yydebug = ENV.has_key? 'DEBUG'
406
+
407
+ do_parse
408
+ end
409
+
410
+ def remove_begin node
411
+ node = node[-1] if node and node[0] == :begin and node.size == 2
412
+ node
413
+ end
414
+
415
+ def ret_args node
416
+ if node then
417
+ raise SyntaxError, "block argument should not be given" if
418
+ node[0] == :block_pass
419
+
420
+ node = node.last if node[0] == :array && node.size == 2
421
+ # HACK matz wraps ONE of the FOUR splats in a newline to
422
+ # distinguish. I use paren for now. ugh
423
+ node = s(:svalue, node) if node[0] == :splat and not node.paren
424
+ end
425
+
426
+ node
427
+ end
428
+
429
+ def value_expr node # HACK
430
+ node = remove_begin node
431
+ node[2] = value_expr(node[2]) if node and node[0] == :if
432
+ node
433
+ end
434
+
435
+ def void_stmts node
436
+ return nil unless node
437
+ return node unless node[0] == :block
438
+
439
+ node[1..-2] = node[1..-2].map { |n| remove_begin(n) }
440
+ node
441
+ end
442
+
443
+ def warning s
444
+ # do nothing for now
445
+ end
446
+ end
447
+
448
+ class Keyword
449
+ class KWtable
450
+ attr_accessor :name, :id, :state
451
+ def initialize(name, id=[], state=nil)
452
+ @name = name
453
+ @id = id
454
+ @state = state
455
+ end
456
+
457
+ def id0
458
+ self.id.first
459
+ end
460
+
461
+ def id1
462
+ self.id.last
463
+ end
464
+ end
465
+
466
+ TOTAL_KEYWORDS = 40
467
+ MIN_WORD_LENGTH = 2
468
+ MAX_WORD_LENGTH = 8
469
+ MIN_HASH_VALUE = 6
470
+ MAX_HASH_VALUE = 55
471
+ # maximum key range = 50, duplicates = 0
472
+
473
+ ASSO_VALUES = [
474
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
475
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
476
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
477
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
478
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
479
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
480
+ 56, 56, 56, 11, 56, 56, 36, 56, 1, 37,
481
+ 31, 1, 56, 56, 56, 56, 29, 56, 1, 56,
482
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
483
+ 56, 56, 56, 56, 56, 1, 56, 32, 1, 2,
484
+ 1, 1, 4, 23, 56, 17, 56, 20, 9, 2,
485
+ 9, 26, 14, 56, 5, 1, 1, 16, 56, 21,
486
+ 20, 9, 56, 56, 56, 56, 56, 56, 56, 56,
487
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
488
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
489
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
490
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
491
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
492
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
493
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
494
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
495
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
496
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
497
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
498
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
499
+ 56, 56, 56, 56, 56, 56
500
+ ]
501
+
502
+ ##
503
+ # :expr_beg = ignore newline, +/- is a sign.
504
+ # :expr_end = newline significant, +/- is a operator.
505
+ # :expr_arg = newline significant, +/- is a operator.
506
+ # :expr_cmdarg = newline significant, +/- is a operator.
507
+ # :expr_endarg = newline significant, +/- is a operator.
508
+ # :expr_mid = newline significant, +/- is a operator.
509
+ # :expr_fname = ignore newline, no reserved words.
510
+ # :expr_dot = right after . or ::, no reserved words.
511
+ # :expr_class = immediate after class, no here document.
512
+
513
+ WORDLIST = [
514
+ [""], [""], [""], [""], [""], [""],
515
+ ["end", [:kEND, :kEND ], :expr_end ],
516
+ ["else", [:kELSE, :kELSE ], :expr_beg ],
517
+ ["case", [:kCASE, :kCASE ], :expr_beg ],
518
+ ["ensure", [:kENSURE, :kENSURE ], :expr_beg ],
519
+ ["module", [:kMODULE, :kMODULE ], :expr_beg ],
520
+ ["elsif", [:kELSIF, :kELSIF ], :expr_beg ],
521
+ ["def", [:kDEF, :kDEF ], :expr_fname ],
522
+ ["rescue", [:kRESCUE, :kRESCUE_MOD ], :expr_mid ],
523
+ ["not", [:kNOT, :kNOT ], :expr_beg ],
524
+ ["then", [:kTHEN, :kTHEN ], :expr_beg ],
525
+ ["yield", [:kYIELD, :kYIELD ], :expr_arg ],
526
+ ["for", [:kFOR, :kFOR ], :expr_beg ],
527
+ ["self", [:kSELF, :kSELF ], :expr_end ],
528
+ ["false", [:kFALSE, :kFALSE ], :expr_end ],
529
+ ["retry", [:kRETRY, :kRETRY ], :expr_end ],
530
+ ["return", [:kRETURN, :kRETURN ], :expr_mid ],
531
+ ["true", [:kTRUE, :kTRUE ], :expr_end ],
532
+ ["if", [:kIF, :kIF_MOD ], :expr_beg ],
533
+ ["defined?", [:kDEFINED, :kDEFINED ], :expr_arg ],
534
+ ["super", [:kSUPER, :kSUPER ], :expr_arg ],
535
+ ["undef", [:kUNDEF, :kUNDEF ], :expr_fname ],
536
+ ["break", [:kBREAK, :kBREAK ], :expr_mid ],
537
+ ["in", [:kIN, :kIN ], :expr_beg ],
538
+ ["do", [:kDO, :kDO ], :expr_beg ],
539
+ ["nil", [:kNIL, :kNIL ], :expr_end ],
540
+ ["until", [:kUNTIL, :kUNTIL_MOD ], :expr_beg ],
541
+ ["unless", [:kUNLESS, :kUNLESS_MOD ], :expr_beg ],
542
+ ["or", [:kOR, :kOR ], :expr_beg ],
543
+ ["next", [:kNEXT, :kNEXT ], :expr_mid ],
544
+ ["when", [:kWHEN, :kWHEN ], :expr_beg ],
545
+ ["redo", [:kREDO, :kREDO ], :expr_end ],
546
+ ["and", [:kAND, :kAND ], :expr_beg ],
547
+ ["begin", [:kBEGIN, :kBEGIN ], :expr_beg ],
548
+ ["__LINE__", [:k__LINE__, :k__LINE__ ], :expr_end ],
549
+ ["class", [:kCLASS, :kCLASS ], :expr_class ],
550
+ ["__FILE__", [:k__FILE__, :k__FILE__ ], :expr_end ],
551
+ ["END", [:klEND, :klEND ], :expr_end ],
552
+ ["BEGIN", [:klBEGIN, :klBEGIN ], :expr_end ],
553
+ ["while", [:kWHILE, :kWHILE_MOD ], :expr_beg ],
554
+ [""], [""], [""], [""], [""], [""], [""], [""], [""],
555
+ [""],
556
+ ["alias", [:kALIAS, :kALIAS ], :expr_fname ],
557
+ ].map { |args| KWtable.new(*args) }
558
+
559
+ def self.hash_keyword(str, len)
560
+ hval = len
561
+
562
+ case hval
563
+ when 2, 1 then
564
+ hval += ASSO_VALUES[str[0].ord]
565
+ else
566
+ hval += ASSO_VALUES[str[2].ord]
567
+ hval += ASSO_VALUES[str[0].ord]
568
+ end
569
+
570
+ hval += ASSO_VALUES[str[len - 1].ord]
571
+ return hval
572
+ end
573
+
574
+ def self.keyword(str, len = str.size)
575
+ if len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH then
576
+ key = hash_keyword(str, len)
577
+ if key <= MAX_HASH_VALUE && key >= 0 then
578
+ s = WORDLIST[key].name
579
+ return WORDLIST[key] if str == s
580
+ end
581
+ end
582
+
583
+ return nil
584
+ end
585
+ end
586
+
587
+ class Environment
588
+ attr_reader :env, :dyn
589
+ attr_accessor :init
590
+
591
+ def [] k
592
+ self.all[k]
593
+ end
594
+
595
+ def []= k, v
596
+ raise "no" if v == true
597
+ self.current[k] = v
598
+ end
599
+
600
+ def all
601
+ idx = @dyn.index false
602
+ @env[0..idx].reverse.inject { |env, scope| env.merge scope }
603
+ end
604
+
605
+ def current
606
+ @env.first
607
+ end
608
+
609
+ def dynamic
610
+ idx = @dyn.index false
611
+ @env[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
612
+ end
613
+
614
+ def dynamic?
615
+ @dyn[0] != false
616
+ end
617
+
618
+ def extend dyn = false
619
+ @dyn.unshift dyn
620
+ @env.unshift({})
621
+ @use.unshift({})
622
+ end
623
+
624
+ def initialize dyn = false
625
+ @dyn = []
626
+ @env = []
627
+ @use = []
628
+ @init = false
629
+ self.extend
630
+ end
631
+
632
+ def unextend
633
+ @dyn.shift
634
+ @env.shift
635
+ @use.shift
636
+ raise "You went too far unextending env" if @env.empty?
637
+ end
638
+
639
+ def use id
640
+ @env.each_with_index do |env, i|
641
+ if env[id] then
642
+ @use[i][id] = true
643
+ end
644
+ end
645
+ end
646
+
647
+ def used? id
648
+ idx = @dyn.index false # REFACTOR
649
+ u = @use[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
650
+ u[id]
651
+ end
652
+ end
653
+
654
+ class StackState
655
+ attr_reader :stack
656
+
657
+ def initialize(name)
658
+ @name = name
659
+ @stack = [false]
660
+ end
661
+
662
+ def inspect
663
+ "StackState(#{@name}, #{@stack.inspect})"
664
+ end
665
+
666
+ def is_in_state
667
+ @stack.last
668
+ end
669
+
670
+ def lexpop
671
+ raise if @stack.size == 0
672
+ a = @stack.pop
673
+ b = @stack.pop
674
+ @stack.push(a || b)
675
+ end
676
+
677
+ def pop
678
+ r = @stack.pop
679
+ @stack.push false if @stack.size == 0
680
+ r
681
+ end
682
+
683
+ def push val
684
+ raise if val != true and val != false
685
+ @stack.push val
686
+ end
687
+ end
688
+
689
+ ############################################################
690
+ # HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
691
+
692
+ class Symbol
693
+ def is_argument # TODO: phase this out
694
+ return self == :expr_arg || self == :expr_cmdarg
695
+ end
696
+ end
697
+
698
+ class Sexp
699
+ attr_writer :paren
700
+ attr_accessor :comments
701
+
702
+ def node_type
703
+ first
704
+ end
705
+
706
+ def paren
707
+ @paren ||= false
708
+ end
709
+
710
+ def value
711
+ raise "multi item sexp" if size > 2
712
+ last
713
+ end
714
+
715
+ def to_sym
716
+ self.value.to_sym
717
+ end
718
+
719
+ def values
720
+ self[1..-1]
721
+ end
722
+ end
723
+
724
+ # END HACK
725
+ ############################################################