rogue_parser 1.0.1

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.
@@ -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
+ ############################################################