irb 1.0.0

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.
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: