irb 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +55 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +6 -0
  9. data/bin/setup +6 -0
  10. data/exe/irb +11 -0
  11. data/irb.gemspec +26 -0
  12. data/lib/irb.rb +798 -0
  13. data/lib/irb/cmd/chws.rb +34 -0
  14. data/lib/irb/cmd/fork.rb +39 -0
  15. data/lib/irb/cmd/help.rb +42 -0
  16. data/lib/irb/cmd/load.rb +67 -0
  17. data/lib/irb/cmd/nop.rb +39 -0
  18. data/lib/irb/cmd/pushws.rb +41 -0
  19. data/lib/irb/cmd/subirb.rb +43 -0
  20. data/lib/irb/completion.rb +244 -0
  21. data/lib/irb/context.rb +425 -0
  22. data/lib/irb/ext/change-ws.rb +46 -0
  23. data/lib/irb/ext/history.rb +119 -0
  24. data/lib/irb/ext/loader.rb +129 -0
  25. data/lib/irb/ext/multi-irb.rb +265 -0
  26. data/lib/irb/ext/save-history.rb +105 -0
  27. data/lib/irb/ext/tracer.rb +72 -0
  28. data/lib/irb/ext/use-loader.rb +74 -0
  29. data/lib/irb/ext/workspaces.rb +67 -0
  30. data/lib/irb/extend-command.rb +306 -0
  31. data/lib/irb/frame.rb +81 -0
  32. data/lib/irb/help.rb +37 -0
  33. data/lib/irb/init.rb +302 -0
  34. data/lib/irb/input-method.rb +192 -0
  35. data/lib/irb/inspector.rb +132 -0
  36. data/lib/irb/lc/.document +4 -0
  37. data/lib/irb/lc/error.rb +32 -0
  38. data/lib/irb/lc/help-message +49 -0
  39. data/lib/irb/lc/ja/encoding_aliases.rb +11 -0
  40. data/lib/irb/lc/ja/error.rb +31 -0
  41. data/lib/irb/lc/ja/help-message +52 -0
  42. data/lib/irb/locale.rb +182 -0
  43. data/lib/irb/magic-file.rb +38 -0
  44. data/lib/irb/notifier.rb +232 -0
  45. data/lib/irb/output-method.rb +92 -0
  46. data/lib/irb/ruby-lex.rb +1180 -0
  47. data/lib/irb/ruby-token.rb +267 -0
  48. data/lib/irb/slex.rb +282 -0
  49. data/lib/irb/src_encoding.rb +7 -0
  50. data/lib/irb/version.rb +17 -0
  51. data/lib/irb/workspace.rb +143 -0
  52. data/lib/irb/ws-for-case-2.rb +15 -0
  53. data/lib/irb/xmp.rb +170 -0
  54. metadata +125 -0
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: false
2
+ module IRB
3
+ class << (MagicFile = Object.new)
4
+ # see parser_magic_comment in parse.y
5
+ ENCODING_SPEC_RE = %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
6
+
7
+ def open(path)
8
+ io = File.open(path, 'rb')
9
+ line = io.gets
10
+ line = io.gets if line[0,2] == "#!"
11
+ encoding = detect_encoding(line)
12
+ internal_encoding = encoding
13
+ encoding ||= IRB.default_src_encoding
14
+ io.rewind
15
+ io.set_encoding(encoding, internal_encoding)
16
+
17
+ if block_given?
18
+ begin
19
+ return (yield io)
20
+ ensure
21
+ io.close
22
+ end
23
+ else
24
+ return io
25
+ end
26
+ end
27
+
28
+ private
29
+ def detect_encoding(line)
30
+ return unless line[0] == ?#
31
+ line = line[1..-1]
32
+ line = $1 if line[/-\*-\s*(.*?)\s*-*-$/]
33
+ return nil unless ENCODING_SPEC_RE =~ line
34
+ encoding = $1
35
+ return encoding.sub(/-(?:mac|dos|unix)/i, '')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # notifier.rb - output methods used by irb
4
+ # $Release Version: 0.9.6$
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require "e2mmap"
14
+ require_relative "output-method"
15
+
16
+ module IRB
17
+ # An output formatter used internally by the lexer.
18
+ module Notifier
19
+ extend Exception2MessageMapper
20
+ def_exception :ErrUndefinedNotifier,
21
+ "undefined notifier level: %d is specified"
22
+ def_exception :ErrUnrecognizedLevel,
23
+ "unrecognized notifier level: %s is specified"
24
+
25
+ # Define a new Notifier output source, returning a new CompositeNotifier
26
+ # with the given +prefix+ and +output_method+.
27
+ #
28
+ # The optional +prefix+ will be appended to all objects being inspected
29
+ # during output, using the given +output_method+ as the output source. If
30
+ # no +output_method+ is given, StdioOutputMethod will be used, and all
31
+ # expressions will be sent directly to STDOUT without any additional
32
+ # formatting.
33
+ def def_notifier(prefix = "", output_method = StdioOutputMethod.new)
34
+ CompositeNotifier.new(prefix, output_method)
35
+ end
36
+ module_function :def_notifier
37
+
38
+ # An abstract class, or superclass, for CompositeNotifier and
39
+ # LeveledNotifier to inherit. It provides several wrapper methods for the
40
+ # OutputMethod object used by the Notifier.
41
+ class AbstractNotifier
42
+ # Creates a new Notifier object
43
+ def initialize(prefix, base_notifier)
44
+ @prefix = prefix
45
+ @base_notifier = base_notifier
46
+ end
47
+
48
+ # The +prefix+ for this Notifier, which is appended to all objects being
49
+ # inspected during output.
50
+ attr_reader :prefix
51
+
52
+ # A wrapper method used to determine whether notifications are enabled.
53
+ #
54
+ # Defaults to +true+.
55
+ def notify?
56
+ true
57
+ end
58
+
59
+ # See OutputMethod#print for more detail.
60
+ def print(*opts)
61
+ @base_notifier.print prefix, *opts if notify?
62
+ end
63
+
64
+ # See OutputMethod#printn for more detail.
65
+ def printn(*opts)
66
+ @base_notifier.printn prefix, *opts if notify?
67
+ end
68
+
69
+ # See OutputMethod#printf for more detail.
70
+ def printf(format, *opts)
71
+ @base_notifier.printf(prefix + format, *opts) if notify?
72
+ end
73
+
74
+ # See OutputMethod#puts for more detail.
75
+ def puts(*objs)
76
+ if notify?
77
+ @base_notifier.puts(*objs.collect{|obj| prefix + obj.to_s})
78
+ end
79
+ end
80
+
81
+ # Same as #ppx, except it uses the #prefix given during object
82
+ # initialization.
83
+ # See OutputMethod#ppx for more detail.
84
+ def pp(*objs)
85
+ if notify?
86
+ @base_notifier.ppx @prefix, *objs
87
+ end
88
+ end
89
+
90
+ # Same as #pp, except it concatenates the given +prefix+ with the #prefix
91
+ # given during object initialization.
92
+ #
93
+ # See OutputMethod#ppx for more detail.
94
+ def ppx(prefix, *objs)
95
+ if notify?
96
+ @base_notifier.ppx @prefix+prefix, *objs
97
+ end
98
+ end
99
+
100
+ # Execute the given block if notifications are enabled.
101
+ def exec_if
102
+ yield(@base_notifier) if notify?
103
+ end
104
+ end
105
+
106
+ # A class that can be used to create a group of notifier objects with the
107
+ # intent of representing a leveled notification system for irb.
108
+ #
109
+ # This class will allow you to generate other notifiers, and assign them
110
+ # the appropriate level for output.
111
+ #
112
+ # The Notifier class provides a class-method Notifier.def_notifier to
113
+ # create a new composite notifier. Using the first composite notifier
114
+ # object you create, sibling notifiers can be initialized with
115
+ # #def_notifier.
116
+ class CompositeNotifier < AbstractNotifier
117
+ # Create a new composite notifier object with the given +prefix+, and
118
+ # +base_notifier+ to use for output.
119
+ def initialize(prefix, base_notifier)
120
+ super
121
+
122
+ @notifiers = [D_NOMSG]
123
+ @level_notifier = D_NOMSG
124
+ end
125
+
126
+ # List of notifiers in the group
127
+ attr_reader :notifiers
128
+
129
+ # Creates a new LeveledNotifier in the composite #notifiers group.
130
+ #
131
+ # The given +prefix+ will be assigned to the notifier, and +level+ will
132
+ # be used as the index of the #notifiers Array.
133
+ #
134
+ # This method returns the newly created instance.
135
+ def def_notifier(level, prefix = "")
136
+ notifier = LeveledNotifier.new(self, level, prefix)
137
+ @notifiers[level] = notifier
138
+ notifier
139
+ end
140
+
141
+ # Returns the leveled notifier for this object
142
+ attr_reader :level_notifier
143
+ alias level level_notifier
144
+
145
+ # Sets the leveled notifier for this object.
146
+ #
147
+ # When the given +value+ is an instance of AbstractNotifier,
148
+ # #level_notifier is set to the given object.
149
+ #
150
+ # When an Integer is given, #level_notifier is set to the notifier at the
151
+ # index +value+ in the #notifiers Array.
152
+ #
153
+ # If no notifier exists at the index +value+ in the #notifiers Array, an
154
+ # ErrUndefinedNotifier exception is raised.
155
+ #
156
+ # An ErrUnrecognizedLevel exception is raised if the given +value+ is not
157
+ # found in the existing #notifiers Array, or an instance of
158
+ # AbstractNotifier
159
+ def level_notifier=(value)
160
+ case value
161
+ when AbstractNotifier
162
+ @level_notifier = value
163
+ when Integer
164
+ l = @notifiers[value]
165
+ Notifier.Raise ErrUndefinedNotifier, value unless l
166
+ @level_notifier = l
167
+ else
168
+ Notifier.Raise ErrUnrecognizedLevel, value unless l
169
+ end
170
+ end
171
+
172
+ alias level= level_notifier=
173
+ end
174
+
175
+ # A leveled notifier is comparable to the composite group from
176
+ # CompositeNotifier#notifiers.
177
+ class LeveledNotifier < AbstractNotifier
178
+ include Comparable
179
+
180
+ # Create a new leveled notifier with the given +base+, and +prefix+ to
181
+ # send to AbstractNotifier.new
182
+ #
183
+ # The given +level+ is used to compare other leveled notifiers in the
184
+ # CompositeNotifier group to determine whether or not to output
185
+ # notifications.
186
+ def initialize(base, level, prefix)
187
+ super(prefix, base)
188
+
189
+ @level = level
190
+ end
191
+
192
+ # The current level of this notifier object
193
+ attr_reader :level
194
+
195
+ # Compares the level of this notifier object with the given +other+
196
+ # notifier.
197
+ #
198
+ # See the Comparable module for more information.
199
+ def <=>(other)
200
+ @level <=> other.level
201
+ end
202
+
203
+ # Whether to output messages to the output method, depending on the level
204
+ # of this notifier object.
205
+ def notify?
206
+ @base_notifier.level >= self
207
+ end
208
+ end
209
+
210
+ # NoMsgNotifier is a LeveledNotifier that's used as the default notifier
211
+ # when creating a new CompositeNotifier.
212
+ #
213
+ # This notifier is used as the +zero+ index, or level +0+, for
214
+ # CompositeNotifier#notifiers, and will not output messages of any sort.
215
+ class NoMsgNotifier < LeveledNotifier
216
+ # Creates a new notifier that should not be used to output messages.
217
+ def initialize
218
+ @base_notifier = nil
219
+ @level = 0
220
+ @prefix = ""
221
+ end
222
+
223
+ # Ensures notifications are ignored, see AbstractNotifier#notify? for
224
+ # more information.
225
+ def notify?
226
+ false
227
+ end
228
+ end
229
+
230
+ D_NOMSG = NoMsgNotifier.new # :nodoc:
231
+ end
232
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # output-method.rb - output methods used by irb
4
+ # $Release Version: 0.9.6$
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require "e2mmap"
14
+
15
+ module IRB
16
+ # An abstract output class for IO in irb. This is mainly used internally by
17
+ # IRB::Notifier. You can define your own output method to use with Irb.new,
18
+ # or Context.new
19
+ class OutputMethod
20
+ extend Exception2MessageMapper
21
+ def_exception :NotImplementedError, "Need to define `%s'"
22
+
23
+
24
+ # Open this method to implement your own output method, raises a
25
+ # NotImplementedError if you don't define #print in your own class.
26
+ def print(*opts)
27
+ OutputMethod.Raise NotImplementedError, "print"
28
+ end
29
+
30
+ # Prints the given +opts+, with a newline delimiter.
31
+ def printn(*opts)
32
+ print opts.join(" "), "\n"
33
+ end
34
+
35
+ # Extends IO#printf to format the given +opts+ for Kernel#sprintf using
36
+ # #parse_printf_format
37
+ def printf(format, *opts)
38
+ if /(%*)%I/ =~ format
39
+ format, opts = parse_printf_format(format, opts)
40
+ end
41
+ print sprintf(format, *opts)
42
+ end
43
+
44
+ # Returns an array of the given +format+ and +opts+ to be used by
45
+ # Kernel#sprintf, if there was a successful Regexp match in the given
46
+ # +format+ from #printf
47
+ #
48
+ # %
49
+ # <flag> [#0- +]
50
+ # <minimum field width> (\*|\*[1-9][0-9]*\$|[1-9][0-9]*)
51
+ # <precision>.(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)?
52
+ # #<length modifier>(hh|h|l|ll|L|q|j|z|t)
53
+ # <conversion specifier>[diouxXeEfgGcsb%]
54
+ def parse_printf_format(format, opts)
55
+ return format, opts if $1.size % 2 == 1
56
+ end
57
+
58
+ # Calls #print on each element in the given +objs+, followed by a newline
59
+ # character.
60
+ def puts(*objs)
61
+ for obj in objs
62
+ print(*obj)
63
+ print "\n"
64
+ end
65
+ end
66
+
67
+ # Prints the given +objs+ calling Object#inspect on each.
68
+ #
69
+ # See #puts for more detail.
70
+ def pp(*objs)
71
+ puts(*objs.collect{|obj| obj.inspect})
72
+ end
73
+
74
+ # Prints the given +objs+ calling Object#inspect on each and appending the
75
+ # given +prefix+.
76
+ #
77
+ # See #puts for more detail.
78
+ def ppx(prefix, *objs)
79
+ puts(*objs.collect{|obj| prefix+obj.inspect})
80
+ end
81
+
82
+ end
83
+
84
+ # A standard output printer
85
+ class StdioOutputMethod < OutputMethod
86
+ # Prints the given +opts+ to standard output, see IO#print for more
87
+ # information.
88
+ def print(*opts)
89
+ STDOUT.print(*opts)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,1180 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # irb/ruby-lex.rb - ruby lexcal analyzer
4
+ # $Release Version: 0.9.6$
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require "e2mmap"
14
+ require_relative "slex"
15
+ require_relative "ruby-token"
16
+
17
+ # :stopdoc:
18
+ class RubyLex
19
+
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")
27
+
28
+ def_exception(:TerminateLineInput, "Terminate Line Input")
29
+
30
+ include RubyToken
31
+
32
+ class << self
33
+ attr_accessor :debug_level
34
+ def debug?
35
+ @debug_level > 0
36
+ end
37
+ end
38
+ @debug_level = 0
39
+
40
+ def initialize
41
+ lex_init
42
+ set_input(STDIN)
43
+
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 = []
51
+
52
+ @indent = 0
53
+ @indent_stack = []
54
+ @lex_state = EXPR_BEG
55
+ @space_seen = false
56
+ @here_header = false
57
+ @post_symbeg = false
58
+
59
+ @continue = false
60
+ @line = ""
61
+
62
+ @skip_space = false
63
+ @readed_auto_clean_up = false
64
+ @exception_on_syntax_error = true
65
+
66
+ @prompt = nil
67
+ end
68
+
69
+ attr_accessor :skip_space
70
+ attr_accessor :readed_auto_clean_up
71
+ attr_accessor :exception_on_syntax_error
72
+
73
+ attr_reader :seek
74
+ attr_reader :char_no
75
+ attr_reader :line_no
76
+ attr_reader :indent
77
+
78
+ # io functions
79
+ def set_input(io, p = nil, &block)
80
+ @io = io
81
+ if p.respond_to?(:call)
82
+ @input = p
83
+ elsif block_given?
84
+ @input = block
85
+ else
86
+ @input = Proc.new{@io.gets}
87
+ end
88
+ end
89
+
90
+ def get_readed
91
+ if idx = @readed.rindex("\n")
92
+ @base_char_no = @readed.size - (idx + 1)
93
+ else
94
+ @base_char_no += @readed.size
95
+ end
96
+
97
+ readed = @readed.join("")
98
+ @readed = []
99
+ readed
100
+ end
101
+
102
+ def getc
103
+ while @rests.empty?
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
121
+
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
131
+
132
+ def eof?
133
+ @io.eof?
134
+ end
135
+
136
+ def getc_of_rests
137
+ if @rests.empty?
138
+ nil
139
+ else
140
+ getc
141
+ end
142
+ end
143
+
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.rindex("\n")
156
+ @char_no = idx + 1
157
+ else
158
+ @char_no = @base_char_no + @readed.size
159
+ end
160
+ else
161
+ @char_no -= 1
162
+ end
163
+ end
164
+
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
172
+
173
+ def peek_match?(regexp)
174
+ while @rests.empty?
175
+ return false unless buf_input
176
+ end
177
+ regexp =~ @rests.join("")
178
+ end
179
+
180
+ def peek(i = 0)
181
+ while @rests.size <= i
182
+ return nil unless buf_input
183
+ end
184
+ @rests[i]
185
+ end
186
+
187
+ def buf_input
188
+ prompt
189
+ line = @input.call
190
+ return nil unless line
191
+ @rests.concat line.chars.to_a
192
+ true
193
+ end
194
+ private :buf_input
195
+
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
204
+
205
+ def prompt
206
+ if @prompt
207
+ @prompt.call(@ltype, @indent, @continue, @line_no)
208
+ end
209
+ end
210
+
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
+ @post_symbeg = false
222
+
223
+ prompt
224
+
225
+ @line = ""
226
+ @exp_line_no = @line_no
227
+ end
228
+
229
+ def each_top_level_statement
230
+ initialize_input
231
+ catch(:TERM_INPUT) do
232
+ loop do
233
+ begin
234
+ @continue = false
235
+ prompt
236
+ unless l = lex
237
+ throw :TERM_INPUT if @line == ''
238
+ else
239
+ @line.concat l
240
+ if @ltype or @continue or @indent > 0
241
+ next
242
+ end
243
+ end
244
+ if @line != "\n"
245
+ @line.force_encoding(@io.encoding)
246
+ yield @line, @exp_line_no
247
+ end
248
+ break unless l
249
+ @line = ''
250
+ @exp_line_no = @line_no
251
+
252
+ @indent = 0
253
+ @indent_stack = []
254
+ prompt
255
+ rescue TerminateLineInput
256
+ initialize_input
257
+ prompt
258
+ get_readed
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ def lex
265
+ continue = @continue
266
+ while tk = token
267
+ case tk
268
+ when TkNL, TkEND_OF_SCRIPT
269
+ @continue = continue unless continue.nil?
270
+ break unless @continue
271
+ when TkSPACE, TkCOMMENT
272
+ when TkSEMICOLON, TkBEGIN, TkELSE
273
+ @continue = continue = false
274
+ else
275
+ continue = nil
276
+ end
277
+ end
278
+ line = get_readed
279
+ if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
280
+ nil
281
+ else
282
+ line
283
+ end
284
+ end
285
+
286
+ def token
287
+ @prev_seek = @seek
288
+ @prev_line_no = @line_no
289
+ @prev_char_no = @char_no
290
+ begin
291
+ begin
292
+ tk = @OP.match(self)
293
+ @space_seen = tk.kind_of?(TkSPACE)
294
+ @lex_state = EXPR_END if @post_symbeg && tk.kind_of?(TkOp)
295
+ @post_symbeg = tk.kind_of?(TkSYMBEG)
296
+ rescue SyntaxError
297
+ raise if @exception_on_syntax_error
298
+ tk = TkError.new(@seek, @line_no, @char_no)
299
+ end
300
+ end while @skip_space and tk.kind_of?(TkSPACE)
301
+ if @readed_auto_clean_up
302
+ get_readed
303
+ end
304
+ tk
305
+ end
306
+
307
+ ENINDENT_CLAUSE = [
308
+ "case", "class", "def", "do", "for", "if",
309
+ "module", "unless", "until", "while", "begin"
310
+ ]
311
+ DEINDENT_CLAUSE = ["end"
312
+ ]
313
+
314
+ PERCENT_LTYPE = {
315
+ "q" => "\'",
316
+ "Q" => "\"",
317
+ "x" => "\`",
318
+ "r" => "/",
319
+ "w" => "]",
320
+ "W" => "]",
321
+ "i" => "]",
322
+ "I" => "]",
323
+ "s" => ":"
324
+ }
325
+
326
+ PERCENT_PAREN = {
327
+ "{" => "}",
328
+ "[" => "]",
329
+ "<" => ">",
330
+ "(" => ")"
331
+ }
332
+
333
+ Ltype2Token = {
334
+ "\'" => TkSTRING,
335
+ "\"" => TkSTRING,
336
+ "\`" => TkXSTRING,
337
+ "/" => TkREGEXP,
338
+ "]" => TkDSTRING,
339
+ ":" => TkSYMBOL
340
+ }
341
+ DLtype2Token = {
342
+ "\"" => TkDSTRING,
343
+ "\`" => TkDXSTRING,
344
+ "/" => TkDREGEXP,
345
+ }
346
+
347
+ def lex_init()
348
+ @OP = IRB::SLex.new
349
+ @OP.def_rules("\0", "\004", "\032") do |op, io|
350
+ Token(TkEND_OF_SCRIPT)
351
+ end
352
+
353
+ @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |op, io|
354
+ @space_seen = true
355
+ while getc =~ /[ \t\f\r\13]/; end
356
+ ungetc
357
+ Token(TkSPACE)
358
+ end
359
+
360
+ @OP.def_rule("#") do |op, io|
361
+ identify_comment
362
+ end
363
+
364
+ @OP.def_rule("=begin",
365
+ proc{|op, io| @prev_char_no == 0 && peek(0) =~ /\s/}) do
366
+ |op, io|
367
+ @ltype = "="
368
+ until getc == "\n"; end
369
+ until peek_equal?("=end") && peek(4) =~ /\s/
370
+ until getc == "\n"; end
371
+ end
372
+ gets
373
+ @ltype = nil
374
+ Token(TkRD_COMMENT)
375
+ end
376
+
377
+ @OP.def_rule("\n") do |op, io|
378
+ print "\\n\n" if RubyLex.debug?
379
+ case @lex_state
380
+ when EXPR_BEG, EXPR_FNAME, EXPR_DOT
381
+ @continue = true
382
+ else
383
+ @continue = false
384
+ @lex_state = EXPR_BEG
385
+ until (@indent_stack.empty? ||
386
+ [TkLPAREN, TkLBRACK, TkLBRACE,
387
+ TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
388
+ @indent_stack.pop
389
+ end
390
+ end
391
+ @here_header = false
392
+ @here_readed = []
393
+ Token(TkNL)
394
+ end
395
+
396
+ @OP.def_rules("*", "**",
397
+ "=", "==", "===",
398
+ "=~", "<=>",
399
+ "<", "<=",
400
+ ">", ">=", ">>",
401
+ "!", "!=", "!~") do
402
+ |op, io|
403
+ case @lex_state
404
+ when EXPR_FNAME, EXPR_DOT
405
+ @lex_state = EXPR_ARG
406
+ else
407
+ @lex_state = EXPR_BEG
408
+ end
409
+ Token(op)
410
+ end
411
+
412
+ @OP.def_rules("<<") do
413
+ |op, io|
414
+ tk = nil
415
+ if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
416
+ (@lex_state != EXPR_ARG || @space_seen)
417
+ c = peek(0)
418
+ if /[-~"'`\w]/ =~ c
419
+ tk = identify_here_document
420
+ end
421
+ end
422
+ unless tk
423
+ tk = Token(op)
424
+ case @lex_state
425
+ when EXPR_FNAME, EXPR_DOT
426
+ @lex_state = EXPR_ARG
427
+ else
428
+ @lex_state = EXPR_BEG
429
+ end
430
+ end
431
+ tk
432
+ end
433
+
434
+ @OP.def_rules("'", '"') do
435
+ |op, io|
436
+ identify_string(op)
437
+ end
438
+
439
+ @OP.def_rules("`") do
440
+ |op, io|
441
+ if @lex_state == EXPR_FNAME
442
+ @lex_state = EXPR_END
443
+ Token(op)
444
+ else
445
+ identify_string(op)
446
+ end
447
+ end
448
+
449
+ @OP.def_rules('?') do
450
+ |op, io|
451
+ if @lex_state == EXPR_END
452
+ @lex_state = EXPR_BEG
453
+ Token(TkQUESTION)
454
+ else
455
+ ch = getc
456
+ if @lex_state == EXPR_ARG && ch =~ /\s/
457
+ ungetc
458
+ @lex_state = EXPR_BEG;
459
+ Token(TkQUESTION)
460
+ else
461
+ if (ch == '\\')
462
+ read_escape
463
+ end
464
+ @lex_state = EXPR_END
465
+ Token(TkINTEGER)
466
+ end
467
+ end
468
+ end
469
+
470
+ @OP.def_rules("&", "&&", "|", "||") do
471
+ |op, io|
472
+ @lex_state = EXPR_BEG
473
+ Token(op)
474
+ end
475
+
476
+ @OP.def_rules("+=", "-=", "*=", "**=",
477
+ "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
478
+ |op, io|
479
+ @lex_state = EXPR_BEG
480
+ op =~ /^(.*)=$/
481
+ Token(TkOPASGN, $1)
482
+ end
483
+
484
+ @OP.def_rule("+@", proc{|op, io| @lex_state == EXPR_FNAME}) do
485
+ |op, io|
486
+ @lex_state = EXPR_ARG
487
+ Token(op)
488
+ end
489
+
490
+ @OP.def_rule("-@", proc{|op, io| @lex_state == EXPR_FNAME}) do
491
+ |op, io|
492
+ @lex_state = EXPR_ARG
493
+ Token(op)
494
+ end
495
+
496
+ @OP.def_rules("+", "-") do
497
+ |op, io|
498
+ catch(:RET) do
499
+ if @lex_state == EXPR_ARG
500
+ if @space_seen and peek(0) =~ /[0-9]/
501
+ throw :RET, identify_number
502
+ else
503
+ @lex_state = EXPR_BEG
504
+ end
505
+ elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
506
+ throw :RET, identify_number
507
+ else
508
+ @lex_state = EXPR_BEG
509
+ end
510
+ Token(op)
511
+ end
512
+ end
513
+
514
+ @OP.def_rule(".") do
515
+ |op, io|
516
+ @lex_state = EXPR_BEG
517
+ if peek(0) =~ /[0-9]/
518
+ ungetc
519
+ identify_number
520
+ else
521
+ # for "obj.if" etc.
522
+ @lex_state = EXPR_DOT
523
+ Token(TkDOT)
524
+ end
525
+ end
526
+
527
+ @OP.def_rules("..", "...") do
528
+ |op, io|
529
+ @lex_state = EXPR_BEG
530
+ Token(op)
531
+ end
532
+
533
+ lex_int2
534
+ end
535
+
536
+ def lex_int2
537
+ @OP.def_rules("]", "}", ")") do
538
+ |op, io|
539
+ @lex_state = EXPR_END
540
+ @indent -= 1
541
+ @indent_stack.pop
542
+ Token(op)
543
+ end
544
+
545
+ @OP.def_rule(":") do
546
+ |op, io|
547
+ if @lex_state == EXPR_END || peek(0) =~ /\s/
548
+ @lex_state = EXPR_BEG
549
+ Token(TkCOLON)
550
+ else
551
+ @lex_state = EXPR_FNAME
552
+ Token(TkSYMBEG)
553
+ end
554
+ end
555
+
556
+ @OP.def_rule("::") do
557
+ |op, io|
558
+ if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
559
+ @lex_state = EXPR_BEG
560
+ Token(TkCOLON3)
561
+ else
562
+ @lex_state = EXPR_DOT
563
+ Token(TkCOLON2)
564
+ end
565
+ end
566
+
567
+ @OP.def_rule("/") do
568
+ |op, io|
569
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
570
+ identify_string(op)
571
+ elsif peek(0) == '='
572
+ getc
573
+ @lex_state = EXPR_BEG
574
+ Token(TkOPASGN, "/") #/)
575
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
576
+ identify_string(op)
577
+ else
578
+ @lex_state = EXPR_BEG
579
+ Token("/") #/)
580
+ end
581
+ end
582
+
583
+ @OP.def_rules("^") do
584
+ |op, io|
585
+ @lex_state = EXPR_BEG
586
+ Token("^")
587
+ end
588
+
589
+ @OP.def_rules(",") do
590
+ |op, io|
591
+ @lex_state = EXPR_BEG
592
+ Token(op)
593
+ end
594
+
595
+ @OP.def_rules(";") do
596
+ |op, io|
597
+ @lex_state = EXPR_BEG
598
+ until (@indent_stack.empty? ||
599
+ [TkLPAREN, TkLBRACK, TkLBRACE,
600
+ TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
601
+ @indent_stack.pop
602
+ end
603
+ Token(op)
604
+ end
605
+
606
+ @OP.def_rule("~") do
607
+ |op, io|
608
+ @lex_state = EXPR_BEG
609
+ Token("~")
610
+ end
611
+
612
+ @OP.def_rule("~@", proc{|op, io| @lex_state == EXPR_FNAME}) do
613
+ |op, io|
614
+ @lex_state = EXPR_BEG
615
+ Token("~")
616
+ end
617
+
618
+ @OP.def_rule("(") do
619
+ |op, io|
620
+ @indent += 1
621
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
622
+ @lex_state = EXPR_BEG
623
+ tk_c = TkfLPAREN
624
+ else
625
+ @lex_state = EXPR_BEG
626
+ tk_c = TkLPAREN
627
+ end
628
+ @indent_stack.push tk_c
629
+ Token(tk_c)
630
+ end
631
+
632
+ @OP.def_rule("[]", proc{|op, io| @lex_state == EXPR_FNAME}) do
633
+ |op, io|
634
+ @lex_state = EXPR_ARG
635
+ Token("[]")
636
+ end
637
+
638
+ @OP.def_rule("[]=", proc{|op, io| @lex_state == EXPR_FNAME}) do
639
+ |op, io|
640
+ @lex_state = EXPR_ARG
641
+ Token("[]=")
642
+ end
643
+
644
+ @OP.def_rule("[") do
645
+ |op, io|
646
+ @indent += 1
647
+ if @lex_state == EXPR_FNAME
648
+ tk_c = TkfLBRACK
649
+ else
650
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
651
+ tk_c = TkLBRACK
652
+ elsif @lex_state == EXPR_ARG && @space_seen
653
+ tk_c = TkLBRACK
654
+ else
655
+ tk_c = TkfLBRACK
656
+ end
657
+ @lex_state = EXPR_BEG
658
+ end
659
+ @indent_stack.push tk_c
660
+ Token(tk_c)
661
+ end
662
+
663
+ @OP.def_rule("{") do
664
+ |op, io|
665
+ @indent += 1
666
+ if @lex_state != EXPR_END && @lex_state != EXPR_ARG
667
+ tk_c = TkLBRACE
668
+ else
669
+ tk_c = TkfLBRACE
670
+ end
671
+ @lex_state = EXPR_BEG
672
+ @indent_stack.push tk_c
673
+ Token(tk_c)
674
+ end
675
+
676
+ @OP.def_rule('\\') do
677
+ |op, io|
678
+ if getc == "\n"
679
+ @space_seen = true
680
+ @continue = true
681
+ Token(TkSPACE)
682
+ else
683
+ read_escape
684
+ Token("\\")
685
+ end
686
+ end
687
+
688
+ @OP.def_rule('%') do
689
+ |op, io|
690
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
691
+ identify_quotation
692
+ elsif peek(0) == '='
693
+ getc
694
+ Token(TkOPASGN, :%)
695
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
696
+ identify_quotation
697
+ else
698
+ @lex_state = EXPR_BEG
699
+ Token("%") #))
700
+ end
701
+ end
702
+
703
+ @OP.def_rule('$') do
704
+ |op, io|
705
+ identify_gvar
706
+ end
707
+
708
+ @OP.def_rule('@') do
709
+ |op, io|
710
+ if peek(0) =~ /[\w@]/
711
+ ungetc
712
+ identify_identifier
713
+ else
714
+ Token("@")
715
+ end
716
+ end
717
+
718
+ @OP.def_rule("") do
719
+ |op, io|
720
+ printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug?
721
+ if peek(0) =~ /[0-9]/
722
+ t = identify_number
723
+ elsif peek(0) =~ /[^\x00-\/:-@\[-^`{-\x7F]/
724
+ t = identify_identifier
725
+ end
726
+ printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug?
727
+ t
728
+ end
729
+
730
+ p @OP if RubyLex.debug?
731
+ end
732
+
733
+ def identify_gvar
734
+ @lex_state = EXPR_END
735
+
736
+ case ch = getc
737
+ when /[~_*$?!@\/\\;,=:<>".]/ #"
738
+ Token(TkGVAR, "$" + ch)
739
+ when "-"
740
+ Token(TkGVAR, "$-" + getc)
741
+ when "&", "`", "'", "+"
742
+ Token(TkBACK_REF, "$"+ch)
743
+ when /[1-9]/
744
+ while getc =~ /[0-9]/; end
745
+ ungetc
746
+ Token(TkNTH_REF)
747
+ when /\w/
748
+ ungetc
749
+ ungetc
750
+ identify_identifier
751
+ else
752
+ ungetc
753
+ Token("$")
754
+ end
755
+ end
756
+
757
+ def identify_identifier
758
+ token = ""
759
+ if peek(0) =~ /[$@]/
760
+ token.concat(c = getc)
761
+ if c == "@" and peek(0) == "@"
762
+ token.concat getc
763
+ end
764
+ end
765
+
766
+ while (ch = getc) =~ /[^\x00-\/:-@\[-^`{-\x7F]/
767
+ print ":", ch, ":" if RubyLex.debug?
768
+ token.concat ch
769
+ end
770
+ ungetc
771
+
772
+ if (ch == "!" || ch == "?") && token[0,1] =~ /\w/ && peek(0) != "="
773
+ token.concat getc
774
+ end
775
+
776
+ # almost fix token
777
+
778
+ case token
779
+ when /^\$/
780
+ return Token(TkGVAR, token)
781
+ when /^\@\@/
782
+ @lex_state = EXPR_END
783
+ # p Token(TkCVAR, token)
784
+ return Token(TkCVAR, token)
785
+ when /^\@/
786
+ @lex_state = EXPR_END
787
+ return Token(TkIVAR, token)
788
+ end
789
+
790
+ if @lex_state != EXPR_DOT
791
+ print token, "\n" if RubyLex.debug?
792
+
793
+ token_c, *trans = TkReading2Token[token]
794
+ if token_c
795
+ # reserved word?
796
+
797
+ if (@lex_state != EXPR_BEG &&
798
+ @lex_state != EXPR_FNAME &&
799
+ trans[1])
800
+ # modifiers
801
+ token_c = TkSymbol2Token[trans[1]]
802
+ @lex_state = trans[0]
803
+ else
804
+ if @lex_state != EXPR_FNAME and peek(0) != ':'
805
+ if ENINDENT_CLAUSE.include?(token)
806
+ # check for ``class = val'' etc.
807
+ valid = true
808
+ case token
809
+ when "class"
810
+ valid = false unless peek_match?(/^\s*(<<|\w|::)/)
811
+ when "def"
812
+ valid = false if peek_match?(/^\s*(([+\-\/*&\|^]|<<|>>|\|\||\&\&)=|\&\&|\|\|)/)
813
+ when "do"
814
+ valid = false if peek_match?(/^\s*([+\-\/*]?=|\*|<|>|\&)/)
815
+ when *ENINDENT_CLAUSE
816
+ valid = false if peek_match?(/^\s*([+\-\/*]?=|\*|<|>|\&|\|)/)
817
+ else
818
+ # no nothing
819
+ end
820
+ if valid
821
+ if token == "do"
822
+ if ![TkFOR, TkWHILE, TkUNTIL].include?(@indent_stack.last)
823
+ @indent += 1
824
+ @indent_stack.push token_c
825
+ end
826
+ else
827
+ @indent += 1
828
+ @indent_stack.push token_c
829
+ end
830
+ end
831
+
832
+ elsif DEINDENT_CLAUSE.include?(token)
833
+ @indent -= 1
834
+ @indent_stack.pop
835
+ end
836
+ @lex_state = trans[0]
837
+ else
838
+ @lex_state = EXPR_END
839
+ end
840
+ end
841
+ return Token(token_c, token)
842
+ end
843
+ end
844
+
845
+ if @lex_state == EXPR_FNAME
846
+ @lex_state = EXPR_END
847
+ if peek(0) == '='
848
+ token.concat getc
849
+ end
850
+ elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
851
+ @lex_state = EXPR_ARG
852
+ else
853
+ @lex_state = EXPR_END
854
+ end
855
+
856
+ if token[0, 1] =~ /[A-Z]/
857
+ return Token(TkCONSTANT, token)
858
+ elsif token[token.size - 1, 1] =~ /[!?]/
859
+ return Token(TkFID, token)
860
+ else
861
+ return Token(TkIDENTIFIER, token)
862
+ end
863
+ end
864
+
865
+ def identify_here_document
866
+ ch = getc
867
+ if ch == "-" || ch == "~"
868
+ ch = getc
869
+ indent = true
870
+ end
871
+ if /['"`]/ =~ ch
872
+ lt = ch
873
+ quoted = ""
874
+ while (c = getc) && c != lt
875
+ quoted.concat c
876
+ end
877
+ else
878
+ lt = '"'
879
+ quoted = ch.dup
880
+ while (c = getc) && c =~ /\w/
881
+ quoted.concat c
882
+ end
883
+ ungetc
884
+ end
885
+
886
+ ltback, @ltype = @ltype, lt
887
+ reserve = []
888
+ while ch = getc
889
+ reserve.push ch
890
+ if ch == "\\"
891
+ reserve.push ch = getc
892
+ elsif ch == "\n"
893
+ break
894
+ end
895
+ end
896
+
897
+ @here_header = false
898
+
899
+ line = ""
900
+ while ch = getc
901
+ if ch == "\n"
902
+ if line == quoted
903
+ break
904
+ end
905
+ line = ""
906
+ else
907
+ line.concat ch unless indent && line == "" && /\s/ =~ ch
908
+ if @ltype != "'" && ch == "#" && peek(0) == "{"
909
+ identify_string_dvar
910
+ end
911
+ end
912
+ end
913
+
914
+ @here_header = true
915
+ @here_readed.concat reserve
916
+ while ch = reserve.pop
917
+ ungetc ch
918
+ end
919
+
920
+ @ltype = ltback
921
+ @lex_state = EXPR_END
922
+ Token(Ltype2Token[lt])
923
+ end
924
+
925
+ def identify_quotation
926
+ ch = getc
927
+ if lt = PERCENT_LTYPE[ch]
928
+ ch = getc
929
+ elsif ch =~ /\W/
930
+ lt = "\""
931
+ else
932
+ RubyLex.fail SyntaxError, "unknown type of %string"
933
+ end
934
+ @quoted = ch unless @quoted = PERCENT_PAREN[ch]
935
+ identify_string(lt, @quoted)
936
+ end
937
+
938
+ def identify_number
939
+ @lex_state = EXPR_END
940
+
941
+ if peek(0) == "0" && peek(1) !~ /[.eE]/
942
+ getc
943
+ case peek(0)
944
+ when /[xX]/
945
+ ch = getc
946
+ match = /[0-9a-fA-F_]/
947
+ when /[bB]/
948
+ ch = getc
949
+ match = /[01_]/
950
+ when /[oO]/
951
+ ch = getc
952
+ match = /[0-7_]/
953
+ when /[dD]/
954
+ ch = getc
955
+ match = /[0-9_]/
956
+ when /[0-7]/
957
+ match = /[0-7_]/
958
+ when /[89]/
959
+ RubyLex.fail SyntaxError, "Invalid octal digit"
960
+ else
961
+ return Token(TkINTEGER)
962
+ end
963
+
964
+ len0 = true
965
+ non_digit = false
966
+ while ch = getc
967
+ if match =~ ch
968
+ if ch == "_"
969
+ if non_digit
970
+ RubyLex.fail SyntaxError, "trailing `#{ch}' in number"
971
+ else
972
+ non_digit = ch
973
+ end
974
+ else
975
+ non_digit = false
976
+ len0 = false
977
+ end
978
+ else
979
+ ungetc
980
+ if len0
981
+ RubyLex.fail SyntaxError, "numeric literal without digits"
982
+ end
983
+ if non_digit
984
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
985
+ end
986
+ break
987
+ end
988
+ end
989
+ return Token(TkINTEGER)
990
+ end
991
+
992
+ type = TkINTEGER
993
+ allow_point = true
994
+ allow_e = true
995
+ non_digit = false
996
+ while ch = getc
997
+ case ch
998
+ when /[0-9]/
999
+ non_digit = false
1000
+ when "_"
1001
+ non_digit = ch
1002
+ when allow_point && "."
1003
+ if non_digit
1004
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
1005
+ end
1006
+ type = TkFLOAT
1007
+ if peek(0) !~ /[0-9]/
1008
+ type = TkINTEGER
1009
+ ungetc
1010
+ break
1011
+ end
1012
+ allow_point = false
1013
+ when allow_e && "e", allow_e && "E"
1014
+ if non_digit
1015
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
1016
+ end
1017
+ type = TkFLOAT
1018
+ if peek(0) =~ /[+-]/
1019
+ getc
1020
+ end
1021
+ allow_e = false
1022
+ allow_point = false
1023
+ non_digit = ch
1024
+ else
1025
+ if non_digit
1026
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
1027
+ end
1028
+ ungetc
1029
+ break
1030
+ end
1031
+ end
1032
+ Token(type)
1033
+ end
1034
+
1035
+ def identify_string(ltype, quoted = ltype)
1036
+ @ltype = ltype
1037
+ @quoted = quoted
1038
+ subtype = nil
1039
+ begin
1040
+ nest = 0
1041
+ while ch = getc
1042
+ if @quoted == ch and nest == 0
1043
+ break
1044
+ elsif @ltype != "'" && ch == "#" && peek(0) == "{"
1045
+ identify_string_dvar
1046
+ elsif @ltype != "'" && @ltype != "]" && @ltype != ":" and ch == "#"
1047
+ subtype = true
1048
+ elsif ch == '\\' and @ltype == "'" #'
1049
+ case ch = getc
1050
+ when "\\", "\n", "'"
1051
+ else
1052
+ ungetc
1053
+ end
1054
+ elsif ch == '\\' #'
1055
+ read_escape
1056
+ end
1057
+ if PERCENT_PAREN.values.include?(@quoted)
1058
+ if PERCENT_PAREN[ch] == @quoted
1059
+ nest += 1
1060
+ elsif ch == @quoted
1061
+ nest -= 1
1062
+ end
1063
+ end
1064
+ end
1065
+ if @ltype == "/"
1066
+ while /[imxoesun]/ =~ peek(0)
1067
+ getc
1068
+ end
1069
+ end
1070
+ if subtype
1071
+ Token(DLtype2Token[ltype])
1072
+ else
1073
+ Token(Ltype2Token[ltype])
1074
+ end
1075
+ ensure
1076
+ @ltype = nil
1077
+ @quoted = nil
1078
+ @lex_state = EXPR_END
1079
+ end
1080
+ end
1081
+
1082
+ def identify_string_dvar
1083
+ begin
1084
+ getc
1085
+
1086
+ reserve_continue = @continue
1087
+ reserve_ltype = @ltype
1088
+ reserve_indent = @indent
1089
+ reserve_indent_stack = @indent_stack
1090
+ reserve_state = @lex_state
1091
+ reserve_quoted = @quoted
1092
+
1093
+ @ltype = nil
1094
+ @quoted = nil
1095
+ @indent = 0
1096
+ @indent_stack = []
1097
+ @lex_state = EXPR_BEG
1098
+
1099
+ loop do
1100
+ @continue = false
1101
+ prompt
1102
+ tk = token
1103
+ if @ltype or @continue or @indent >= 0
1104
+ next
1105
+ end
1106
+ break if tk.kind_of?(TkRBRACE)
1107
+ end
1108
+ ensure
1109
+ @continue = reserve_continue
1110
+ @ltype = reserve_ltype
1111
+ @indent = reserve_indent
1112
+ @indent_stack = reserve_indent_stack
1113
+ @lex_state = reserve_state
1114
+ @quoted = reserve_quoted
1115
+ end
1116
+ end
1117
+
1118
+ def identify_comment
1119
+ @ltype = "#"
1120
+
1121
+ while ch = getc
1122
+ if ch == "\n"
1123
+ @ltype = nil
1124
+ ungetc
1125
+ break
1126
+ end
1127
+ end
1128
+ return Token(TkCOMMENT)
1129
+ end
1130
+
1131
+ def read_escape
1132
+ case ch = getc
1133
+ when "\n", "\r", "\f"
1134
+ when "\\", "n", "t", "r", "f", "v", "a", "e", "b", "s" #"
1135
+ when /[0-7]/
1136
+ ungetc ch
1137
+ 3.times do
1138
+ case ch = getc
1139
+ when /[0-7]/
1140
+ when nil
1141
+ break
1142
+ else
1143
+ ungetc
1144
+ break
1145
+ end
1146
+ end
1147
+
1148
+ when "x"
1149
+ 2.times do
1150
+ case ch = getc
1151
+ when /[0-9a-fA-F]/
1152
+ when nil
1153
+ break
1154
+ else
1155
+ ungetc
1156
+ break
1157
+ end
1158
+ end
1159
+
1160
+ when "M"
1161
+ if (ch = getc) != '-'
1162
+ ungetc
1163
+ else
1164
+ if (ch = getc) == "\\" #"
1165
+ read_escape
1166
+ end
1167
+ end
1168
+
1169
+ when "C", "c" #, "^"
1170
+ if ch == "C" and (ch = getc) != "-"
1171
+ ungetc
1172
+ elsif (ch = getc) == "\\" #"
1173
+ read_escape
1174
+ end
1175
+ else
1176
+ # other characters
1177
+ end
1178
+ end
1179
+ end
1180
+ # :startdoc: