Fix up Rubinius specific library specs.
[rbx.git] / lib / irb / ruby-lex.rb
bloba25a005ce86b442d94b223a2ac81c9b994cc1855
2 #   irb/ruby-lex.rb - ruby lexcal analizer
3 #       $Release Version: 0.9.5$
4 #       $Revision: 11708 $
5 #       $Date: 2007-02-12 15:01:19 -0800 (Mon, 12 Feb 2007) $
6 #       by Keiju ISHITSUKA(keiju@ruby-lang.org)
8 # --
10 #   
13 require "e2mmap"
14 require "irb/slex"
15 require "irb/ruby-token"
17 class RubyLex
18   @RCS_ID='-$Id: ruby-lex.rb 11708 2007-02-12 23:01:19Z shyouhei $-'
20   extend Exception2MessageMapper
21   def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
22   def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
23   def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
24   def_exception(:TkReading2TokenDuplicateError, 
25                 "key duplicate(token_n='%s', key='%s')")
26   def_exception(:SyntaxError, "%s")
28   def_exception(:TerminateLineInput, "Terminate Line Input")
29   
30   include RubyToken
32   class << self
33     attr_accessor :debug_level
34     def debug?
35       @debug_level > 0
36     end
37   end
38   @debug_level = 0
40   def initialize
41     lex_init
42     set_input(STDIN)
44     @seek = 0
45     @exp_line_no = @line_no = 1
46     @base_char_no = 0
47     @char_no = 0
48     @rests = []
49     @readed = []
50     @here_readed = []
52     @indent = 0
53     @indent_stack = []
54     @lex_state = EXPR_BEG
55     @space_seen = false
56     @here_header = false
57     
58     @continue = false
59     @line = ""
61     @skip_space = false
62     @readed_auto_clean_up = false
63     @exception_on_syntax_error = true
65     @prompt = nil
66   end
68   attr_accessor :skip_space
69   attr_accessor :readed_auto_clean_up
70   attr_accessor :exception_on_syntax_error
72   attr_reader :seek
73   attr_reader :char_no
74   attr_reader :line_no
75   attr_reader :indent
77   # io functions
78   def set_input(io, p = nil, &block)
79     @io = io
80     if p and p.respond_to?(:call)
81       @input = p
82     elsif block_given?
83       @input = block
84     else
85       @input = Proc.new{@io.gets}
86     end
87   end
89   def get_readed
90     if idx = @readed.reverse.index("\n")
91       @base_char_no = idx
92     else
93       @base_char_no += @readed.size
94     end
95     
96     readed = @readed.join("")
97     @readed = []
98     readed
99   end
101   def getc
102     while @rests.empty?
103 #      return nil unless buf_input
104       @rests.push nil unless buf_input
105     end
106     c = @rests.shift
107     if @here_header
108       @here_readed.push c
109     else
110       @readed.push c
111     end
112     @seek += 1
113     if c == "\n"
114       @line_no += 1 
115       @char_no = 0
116     else
117       @char_no += 1
118     end
119     c
120   end
122   def gets
123     l = ""
124     while c = getc
125       l.concat(c)
126       break if c == "\n"
127     end
128     return nil if l == "" and c.nil?
129     l
130   end
132   def eof?
133     @io.eof?
134   end
136   def getc_of_rests
137     if @rests.empty?
138       nil
139     else
140       getc
141     end
142   end
144   def ungetc(c = nil)
145     if @here_readed.empty?
146       c2 = @readed.pop
147     else
148       c2 = @here_readed.pop
149     end
150     c = c2 unless c
151     @rests.unshift c #c = 
152       @seek -= 1
153     if c == "\n"
154       @line_no -= 1 
155       if idx = @readed.reverse.index("\n")
156         @char_no = @readed.size - idx
157       else
158         @char_no = @base_char_no + @readed.size
159       end
160     else
161       @char_no -= 1
162     end
163   end
165   def peek_equal?(str)
166     chrs = str.split(//)
167     until @rests.size >= chrs.size
168       return false unless buf_input
169     end
170     @rests[0, chrs.size] == chrs
171   end
173   def peek_match?(regexp)
174     while @rests.empty?
175       return false unless buf_input
176     end
177     regexp =~ @rests.join("")
178   end
180   def peek(i = 0)
181     while @rests.size <= i
182       return nil unless buf_input
183     end
184     @rests[i]
185   end
187   def buf_input
188     prompt
189     line = @input.call
190     return nil unless line
191     @rests.concat line.split(//)
192     true
193   end
194   private :buf_input
196   def set_prompt(p = nil, &block)
197     p = block if block_given?
198     if p.respond_to?(:call)
199       @prompt = p
200     else
201       @prompt = Proc.new{print p}
202     end
203   end
205   def prompt
206     if @prompt
207       @prompt.call(@ltype, @indent, @continue, @line_no)
208     end
209   end
211   def initialize_input
212     @ltype = nil
213     @quoted = nil
214     @indent = 0
215     @indent_stack = []
216     @lex_state = EXPR_BEG
217     @space_seen = false
218     @here_header = false
219     
220     @continue = false
221     prompt
223     @line = ""
224     @exp_line_no = @line_no
225   end
226   
227   def each_top_level_statement
228     initialize_input
229     catch(:TERM_INPUT) do
230       loop do
231         begin
232           @continue = false
233           prompt
234           unless l = lex
235             throw :TERM_INPUT if @line == ''
236           else
237             #p l
238             @line.concat l
239             if @ltype or @continue or @indent > 0
240               next
241             end
242           end
243           if @line != "\n"
244             yield @line, @exp_line_no
245           end
246           break unless l
247           @line = ''
248           @exp_line_no = @line_no
250           @indent = 0
251           @indent_stack = []
252           prompt
253         rescue TerminateLineInput
254           initialize_input
255           prompt
256           get_readed
257         end
258       end
259     end
260   end
262   def lex
263     until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
264              !@continue or
265              tk.nil?)
266       #p tk
267       #p @lex_state
268       #p self
269     end
270     line = get_readed
271     #      print self.inspect
272     if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
273       nil
274     else
275       line
276     end
277   end
279   def token
280     #      require "tracer"
281     #      Tracer.on
282     @prev_seek = @seek
283     @prev_line_no = @line_no
284     @prev_char_no = @char_no
285     begin
286       begin
287         tk = @OP.match(self)
288         @space_seen = tk.kind_of?(TkSPACE)
289       rescue SyntaxError
290         raise if @exception_on_syntax_error
291         tk = TkError.new(@seek, @line_no, @char_no)
292       end
293     end while @skip_space and tk.kind_of?(TkSPACE)
294     if @readed_auto_clean_up
295       get_readed
296     end
297     #      Tracer.off
298     tk
299   end
300   
301   ENINDENT_CLAUSE = [
302     "case", "class", "def", "do", "for", "if",
303     "module", "unless", "until", "while", "begin" #, "when"
304   ]
305   DEINDENT_CLAUSE = ["end" #, "when"
306   ]
308   PERCENT_LTYPE = {
309     "q" => "\'",
310     "Q" => "\"",
311     "x" => "\`",
312     "r" => "/",
313     "w" => "]",
314     "W" => "]",
315     "s" => ":"
316   }
317   
318   PERCENT_PAREN = {
319     "{" => "}",
320     "[" => "]",
321     "<" => ">",
322     "(" => ")"
323   }
325   Ltype2Token = {
326     "\'" => TkSTRING,
327     "\"" => TkSTRING,
328     "\`" => TkXSTRING,
329     "/" => TkREGEXP,
330     "]" => TkDSTRING,
331     ":" => TkSYMBOL
332   }
333   DLtype2Token = {
334     "\"" => TkDSTRING,
335     "\`" => TkDXSTRING,
336     "/" => TkDREGEXP,
337   }
339   def lex_init()
340     @OP = IRB::SLex.new
341     @OP.def_rules("\0", "\004", "\032") do |op, io|
342       Token(TkEND_OF_SCRIPT)
343     end
345     @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |op, io|
346       @space_seen = true
347       while getc =~ /[ \t\f\r\13]/; end
348       ungetc
349       Token(TkSPACE)
350     end
352     @OP.def_rule("#") do |op, io|
353       identify_comment
354     end
356     @OP.def_rule("=begin",
357                  proc{|op, io| @prev_char_no == 0 && peek(0) =~ /\s/}) do 
358       |op, io|
359       @ltype = "="
360       until getc == "\n"; end
361       until peek_equal?("=end") && peek(4) =~ /\s/
362         until getc == "\n"; end
363       end
364       gets
365       @ltype = nil
366       Token(TkRD_COMMENT)
367     end
369     @OP.def_rule("\n") do |op, io|
370       print "\\n\n" if RubyLex.debug?
371       case @lex_state
372       when EXPR_BEG, EXPR_FNAME, EXPR_DOT
373         @continue = true
374       else
375         @continue = false
376         @lex_state = EXPR_BEG
377         until (@indent_stack.empty? || 
378                [TkLPAREN, TkLBRACK, TkLBRACE, 
379                  TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
380           @indent_stack.pop
381         end
382       end
383       @here_header = false
384       @here_readed = []
385       Token(TkNL)
386     end
388     @OP.def_rules("*", "**",    
389                   "=", "==", "===", 
390                   "=~", "<=>",  
391                   "<", "<=",
392                   ">", ">=", ">>") do
393       |op, io|
394       case @lex_state
395       when EXPR_FNAME, EXPR_DOT
396         @lex_state = EXPR_ARG
397       else
398         @lex_state = EXPR_BEG
399       end
400       Token(op)
401     end
403     @OP.def_rules("!", "!=", "!~") do
404       |op, io|
405       @lex_state = EXPR_BEG
406       Token(op)
407     end
409     @OP.def_rules("<<") do
410       |op, io|
411       tk = nil
412       if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
413           (@lex_state != EXPR_ARG || @space_seen)
414         c = peek(0)
415         if /\S/ =~ c && (/["'`]/ =~ c || /[\w_]/ =~ c || c == "-")
416           tk = identify_here_document
417         end
418       end
419       unless tk
420         tk = Token(op)
421         case @lex_state
422         when EXPR_FNAME, EXPR_DOT
423           @lex_state = EXPR_ARG
424         else
425           @lex_state = EXPR_BEG
426         end
427       end
428       tk
429     end
431     @OP.def_rules("'", '"') do
432       |op, io|
433       identify_string(op)
434     end
436     @OP.def_rules("`") do
437       |op, io|
438       if @lex_state == EXPR_FNAME
439         @lex_state = EXPR_END
440         Token(op)
441       else
442         identify_string(op)
443       end
444     end
446     @OP.def_rules('?') do
447       |op, io|
448       if @lex_state == EXPR_END
449         @lex_state = EXPR_BEG
450         Token(TkQUESTION)
451       else
452         ch = getc
453         if @lex_state == EXPR_ARG && ch =~ /\s/
454           ungetc
455           @lex_state = EXPR_BEG;
456           Token(TkQUESTION)
457         else
458           if (ch == '\\') 
459             read_escape
460           end
461           @lex_state = EXPR_END
462           Token(TkINTEGER)
463         end
464       end
465     end
467     @OP.def_rules("&", "&&", "|", "||") do
468       |op, io|
469       @lex_state = EXPR_BEG
470       Token(op)
471     end
472     
473     @OP.def_rules("+=", "-=", "*=", "**=", 
474                   "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
475       |op, io|
476       @lex_state = EXPR_BEG
477       op =~ /^(.*)=$/
478       Token(TkOPASGN, $1)
479     end
481     @OP.def_rule("+@", proc{|op, io| @lex_state == EXPR_FNAME}) do
482       |op, io|
483       @lex_state = EXPR_ARG
484       Token(op)
485     end
487     @OP.def_rule("-@", proc{|op, io| @lex_state == EXPR_FNAME}) do
488       |op, io|
489       @lex_state = EXPR_ARG
490       Token(op)
491     end
493     @OP.def_rules("+", "-") do
494       |op, io|
495       catch(:RET) do
496         if @lex_state == EXPR_ARG
497           if @space_seen and peek(0) =~ /[0-9]/
498             throw :RET, identify_number
499           else
500             @lex_state = EXPR_BEG
501           end
502         elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
503           throw :RET, identify_number
504         else
505           @lex_state = EXPR_BEG
506         end
507         Token(op)
508       end
509     end
511     @OP.def_rule(".") do
512       |op, io|
513       @lex_state = EXPR_BEG
514       if peek(0) =~ /[0-9]/
515         ungetc
516         identify_number
517       else
518         # for "obj.if" etc.
519         @lex_state = EXPR_DOT
520         Token(TkDOT)
521       end
522     end
524     @OP.def_rules("..", "...") do
525       |op, io|
526       @lex_state = EXPR_BEG
527       Token(op)
528     end
530     lex_int2
531   end
532   
533   def lex_int2
534     @OP.def_rules("]", "}", ")") do
535       |op, io|
536       @lex_state = EXPR_END
537       @indent -= 1
538       @indent_stack.pop
539       Token(op)
540     end
542     @OP.def_rule(":") do
543       |op, io|
544       if @lex_state == EXPR_END || peek(0) =~ /\s/
545         @lex_state = EXPR_BEG
546         Token(TkCOLON)
547       else
548         @lex_state = EXPR_FNAME;
549         Token(TkSYMBEG)
550       end
551     end
553     @OP.def_rule("::") do
554        |op, io|
555 #      p @lex_state.id2name, @space_seen
556       if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
557         @lex_state = EXPR_BEG
558         Token(TkCOLON3)
559       else
560         @lex_state = EXPR_DOT
561         Token(TkCOLON2)
562       end
563     end
565     @OP.def_rule("/") do
566       |op, io|
567       if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
568         identify_string(op)
569       elsif peek(0) == '='
570         getc
571         @lex_state = EXPR_BEG
572         Token(TkOPASGN, "/") #/)
573       elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
574         identify_string(op)
575       else 
576         @lex_state = EXPR_BEG
577         Token("/") #/)
578       end
579     end
581     @OP.def_rules("^") do
582       |op, io|
583       @lex_state = EXPR_BEG
584       Token("^")
585     end
587     #       @OP.def_rules("^=") do
588     #   @lex_state = EXPR_BEG
589     #   Token(OP_ASGN, :^)
590     #       end
591     
592     @OP.def_rules(",") do
593       |op, io|
594       @lex_state = EXPR_BEG
595       Token(op)
596     end
598     @OP.def_rules(";") do
599       |op, io|
600       @lex_state = EXPR_BEG
601       until (@indent_stack.empty? || 
602              [TkLPAREN, TkLBRACK, TkLBRACE, 
603                TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
604         @indent_stack.pop
605       end
606       Token(op)
607     end
609     @OP.def_rule("~") do
610       |op, io|
611       @lex_state = EXPR_BEG
612       Token("~")
613     end
615     @OP.def_rule("~@", proc{|op, io| @lex_state == EXPR_FNAME}) do
616       |op, io|
617       @lex_state = EXPR_BEG
618       Token("~")
619     end
620     
621     @OP.def_rule("(") do
622       |op, io|
623       @indent += 1
624       if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
625         @lex_state = EXPR_BEG
626         tk_c = TkfLPAREN
627       else
628         @lex_state = EXPR_BEG
629         tk_c = TkLPAREN
630       end
631       @indent_stack.push tk_c
632       tk = Token(tk_c)
633     end
635     @OP.def_rule("[]", proc{|op, io| @lex_state == EXPR_FNAME}) do
636       |op, io|
637       @lex_state = EXPR_ARG
638       Token("[]")
639     end
641     @OP.def_rule("[]=", proc{|op, io| @lex_state == EXPR_FNAME}) do
642       |op, io|
643       @lex_state = EXPR_ARG
644       Token("[]=")
645     end
647     @OP.def_rule("[") do
648       |op, io|
649       @indent += 1
650       if @lex_state == EXPR_FNAME
651         tk_c = TkfLBRACK
652       else
653         if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
654           tk_c = TkLBRACK
655         elsif @lex_state == EXPR_ARG && @space_seen
656           tk_c = TkLBRACK
657         else
658           tk_c = TkfLBRACK
659         end
660         @lex_state = EXPR_BEG
661       end
662       @indent_stack.push tk_c
663       Token(tk_c)
664     end
666     @OP.def_rule("{") do
667       |op, io|
668       @indent += 1
669       if @lex_state != EXPR_END && @lex_state != EXPR_ARG
670         tk_c = TkLBRACE
671       else
672         tk_c = TkfLBRACE
673       end
674       @lex_state = EXPR_BEG
675       @indent_stack.push tk_c
676       Token(tk_c)
677     end
679     @OP.def_rule('\\') do
680       |op, io|
681       if getc == "\n"
682         @space_seen = true
683         @continue = true
684         Token(TkSPACE)
685       else
686         ungetc
687         Token("\\")
688       end
689     end
691     @OP.def_rule('%') do
692       |op, io|
693       if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
694         identify_quotation
695       elsif peek(0) == '='
696         getc
697         Token(TkOPASGN, :%)
698       elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
699         identify_quotation
700       else
701         @lex_state = EXPR_BEG
702         Token("%") #))
703       end
704     end
706     @OP.def_rule('$') do
707       |op, io|
708       identify_gvar
709     end
711     @OP.def_rule('@') do
712       |op, io|
713       if peek(0) =~ /[\w_@]/
714         ungetc
715         identify_identifier
716       else
717         Token("@")
718       end
719     end
721     #       @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do 
722     #   |op, io|
723     #   @indent += 1
724     #   @lex_state = EXPR_FNAME
725     # # @lex_state = EXPR_END
726     # # until @rests[0] == "\n" or @rests[0] == ";"
727     # #   rests.shift
728     # # end
729     #       end
731     @OP.def_rule("") do
732       |op, io|
733       printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug?
734       if peek(0) =~ /[0-9]/
735         t = identify_number
736       elsif peek(0) =~ /[\w_]/
737         t = identify_identifier
738       end
739       printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug?
740       t
741     end
742     
743     p @OP if RubyLex.debug?
744   end
745   
746   def identify_gvar
747     @lex_state = EXPR_END
748     
749     case ch = getc
750     when /[~_*$?!@\/\\;,=:<>".]/   #"
751       Token(TkGVAR, "$" + ch)
752     when "-"
753       Token(TkGVAR, "$-" + getc)
754     when "&", "`", "'", "+"
755       Token(TkBACK_REF, "$"+ch)
756     when /[1-9]/
757       while getc =~ /[0-9]/; end
758       ungetc
759       Token(TkNTH_REF)
760     when /\w/
761       ungetc
762       ungetc
763       identify_identifier
764     else 
765       ungetc
766       Token("$")
767     end
768   end
769   
770   def identify_identifier
771     token = ""
772     if peek(0) =~ /[$@]/
773       token.concat(c = getc)
774       if c == "@" and peek(0) == "@"
775         token.concat getc
776       end
777     end
779     while (ch = getc) =~ /\w|_/
780       print ":", ch, ":" if RubyLex.debug?
781       token.concat ch
782     end
783     ungetc
784     
785     if (ch == "!" || ch == "?") && token[0,1] =~ /\w/ && peek(0) != "="
786       token.concat getc
787     end
789     # almost fix token
791     case token
792     when /^\$/
793       return Token(TkGVAR, token)
794     when /^\@\@/
795       @lex_state = EXPR_END
796       # p Token(TkCVAR, token)
797       return Token(TkCVAR, token)
798     when /^\@/
799       @lex_state = EXPR_END
800       return Token(TkIVAR, token)
801     end
802     
803     if @lex_state != EXPR_DOT
804       print token, "\n" if RubyLex.debug?
806       token_c, *trans = TkReading2Token[token]
807       if token_c
808         # reserved word?
810         if (@lex_state != EXPR_BEG &&
811             @lex_state != EXPR_FNAME &&
812             trans[1])
813           # modifiers
814           token_c = TkSymbol2Token[trans[1]]
815           @lex_state = trans[0]
816         else
817           if @lex_state != EXPR_FNAME
818             if ENINDENT_CLAUSE.include?(token)
819               # check for ``class = val'' etc.
820               valid = true
821               case token
822               when "class"
823                 valid = false unless peek_match?(/^\s*(<<|\w|::)/)
824               when "def"
825                 valid = false if peek_match?(/^\s*(([+-\/*&\|^]|<<|>>|\|\||\&\&)=|\&\&|\|\|)/)
826               when "do"
827                 valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&)/)
828               when *ENINDENT_CLAUSE
829                 valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&|\|)/)
830               else
831                 # no nothing
832               end
833               if valid
834                 if token == "do"
835                   if ![TkFOR, TkWHILE, TkUNTIL].include?(@indent_stack.last)
836                     @indent += 1
837                     @indent_stack.push token_c
838                   end
839                 else
840                   @indent += 1
841                   @indent_stack.push token_c
842                 end
843 #               p @indent_stack
844               end
846             elsif DEINDENT_CLAUSE.include?(token)
847               @indent -= 1
848               @indent_stack.pop
849             end
850             @lex_state = trans[0]
851           else
852             @lex_state = EXPR_END
853           end
854         end
855         return Token(token_c, token)
856       end
857     end
859     if @lex_state == EXPR_FNAME
860       @lex_state = EXPR_END
861       if peek(0) == '='
862         token.concat getc
863       end
864     elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
865       @lex_state = EXPR_ARG
866     else
867       @lex_state = EXPR_END
868     end
870     if token[0, 1] =~ /[A-Z]/
871       return Token(TkCONSTANT, token)
872     elsif token[token.size - 1, 1] =~ /[!?]/
873       return Token(TkFID, token)
874     else
875       return Token(TkIDENTIFIER, token)
876     end
877   end
879   def identify_here_document
880     ch = getc
881 #    if lt = PERCENT_LTYPE[ch]
882     if ch == "-"
883       ch = getc
884       indent = true
885     end
886     if /['"`]/ =~ ch
887       lt = ch
888       quoted = ""
889       while (c = getc) && c != lt
890         quoted.concat c
891       end
892     else
893       lt = '"'
894       quoted = ch.dup
895       while (c = getc) && c =~ /\w/
896         quoted.concat c
897       end
898       ungetc
899     end
901     ltback, @ltype = @ltype, lt
902     reserve = []
903     while ch = getc
904       reserve.push ch
905       if ch == "\\"
906         reserve.push ch = getc
907       elsif ch == "\n"
908         break
909       end
910     end
912     @here_header = false
913     while l = gets
914       l = l.sub(/(:?\r)?\n\z/, '')
915       if (indent ? l.strip : l) == quoted
916         break
917       end
918     end
920     @here_header = true
921     @here_readed.concat reserve
922     while ch = reserve.pop
923       ungetc ch
924     end
926     @ltype = ltback
927     @lex_state = EXPR_END
928     Token(Ltype2Token[lt])
929   end
930   
931   def identify_quotation
932     ch = getc
933     if lt = PERCENT_LTYPE[ch]
934       ch = getc
935     elsif ch =~ /\W/
936       lt = "\""
937     else
938       RubyLex.fail SyntaxError, "unknown type of %string"
939     end
940 #     if ch !~ /\W/
941 #       ungetc
942 #       next
943 #     end
944     #@ltype = lt
945     @quoted = ch unless @quoted = PERCENT_PAREN[ch]
946     identify_string(lt, @quoted)
947   end
949   def identify_number
950     @lex_state = EXPR_END
952     if peek(0) == "0" && peek(1) !~ /[.eE]/
953       getc
954       case peek(0)
955       when /[xX]/
956         ch = getc
957         match = /[0-9a-fA-F_]/
958       when /[bB]/
959         ch = getc
960         match = /[01_]/
961       when /[oO]/
962         ch = getc
963         match = /[0-7_]/
964       when /[dD]/
965         ch = getc
966         match = /[0-9_]/
967       when /[0-7]/
968         match = /[0-7_]/
969       when /[89]/
970         RubyLex.fail SyntaxError, "Illegal octal digit"
971       else 
972         return Token(TkINTEGER)
973       end
974       
975       len0 = true
976       non_digit = false
977       while ch = getc
978         if match =~ ch
979           if ch == "_"
980             if non_digit
981               RubyLex.fail SyntaxError, "trailing `#{ch}' in number"
982             else
983               non_digit = ch
984             end
985           else
986             non_digit = false
987             len0 = false
988           end
989         else
990           ungetc
991           if len0
992             RubyLex.fail SyntaxError, "numeric literal without digits"
993           end
994           if non_digit
995             RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
996           end
997           break
998         end
999       end
1000       return Token(TkINTEGER)
1001     end
1002     
1003     type = TkINTEGER
1004     allow_point = true
1005     allow_e = true
1006     non_digit = false
1007     while ch = getc
1008       case ch
1009       when /[0-9]/
1010         non_digit = false
1011       when "_"
1012         non_digit = ch
1013       when allow_point && "."
1014         if non_digit
1015           RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
1016         end
1017         type = TkFLOAT
1018         if peek(0) !~ /[0-9]/
1019           type = TkINTEGER
1020           ungetc
1021           break
1022         end
1023         allow_point = false
1024       when allow_e && "e", allow_e && "E"
1025         if non_digit
1026           RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
1027         end
1028         type = TkFLOAT
1029         if peek(0) =~ /[+-]/
1030           getc
1031         end
1032         allow_e = false
1033         allow_point = false
1034         non_digit = ch
1035       else
1036         if non_digit
1037           RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
1038         end
1039         ungetc
1040         break
1041       end
1042     end
1043     Token(type)
1044   end
1045   
1046   def identify_string(ltype, quoted = ltype)
1047     @ltype = ltype
1048     @quoted = quoted
1049     subtype = nil
1050     begin
1051       nest = 0
1052       while ch = getc
1053         if @quoted == ch and nest == 0
1054           break
1055         elsif @ltype != "'" && @ltype != "]" && @ltype != ":" and ch == "#"
1056           subtype = true
1057         elsif ch == '\\' #'
1058           read_escape
1059         end
1060         if PERCENT_PAREN.values.include?(@quoted) 
1061           if PERCENT_PAREN[ch] == @quoted
1062             nest += 1
1063           elsif ch == @quoted
1064             nest -= 1
1065           end
1066         end
1067       end
1068       if @ltype == "/"
1069         if peek(0) =~ /i|m|x|o|e|s|u|n/
1070           getc
1071         end
1072       end
1073       if subtype
1074         Token(DLtype2Token[ltype])
1075       else
1076         Token(Ltype2Token[ltype])
1077       end
1078     ensure
1079       @ltype = nil
1080       @quoted = nil
1081       @lex_state = EXPR_END
1082     end
1083   end
1084   
1085   def identify_comment
1086     @ltype = "#"
1088     while ch = getc
1089 #      if ch == "\\" #"
1090 #       read_escape
1091 #      end
1092       if ch == "\n"
1093         @ltype = nil
1094         ungetc
1095         break
1096       end
1097     end
1098     return Token(TkCOMMENT)
1099   end
1100   
1101   def read_escape
1102     case ch = getc
1103     when "\n", "\r", "\f"
1104     when "\\", "n", "t", "r", "f", "v", "a", "e", "b", "s" #"
1105     when /[0-7]/
1106       ungetc ch
1107       3.times do
1108         case ch = getc
1109         when /[0-7]/
1110         when nil
1111           break
1112         else
1113           ungetc
1114           break
1115         end
1116       end
1117       
1118     when "x"
1119       2.times do
1120         case ch = getc
1121         when /[0-9a-fA-F]/
1122         when nil
1123           break
1124         else
1125           ungetc
1126           break
1127         end
1128       end
1130     when "M"
1131       if (ch = getc) != '-'
1132         ungetc
1133       else
1134         if (ch = getc) == "\\" #"
1135           read_escape
1136         end
1137       end
1139     when "C", "c" #, "^"
1140       if ch == "C" and (ch = getc) != "-"
1141         ungetc
1142       elsif (ch = getc) == "\\" #"
1143         read_escape
1144       end
1145     else
1146       # other characters 
1147     end
1148   end