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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +6 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +55 -0
- data/Rakefile +10 -0
- data/bin/console +6 -0
- data/bin/setup +6 -0
- data/exe/irb +11 -0
- data/irb.gemspec +26 -0
- data/lib/irb.rb +798 -0
- data/lib/irb/cmd/chws.rb +34 -0
- data/lib/irb/cmd/fork.rb +39 -0
- data/lib/irb/cmd/help.rb +42 -0
- data/lib/irb/cmd/load.rb +67 -0
- data/lib/irb/cmd/nop.rb +39 -0
- data/lib/irb/cmd/pushws.rb +41 -0
- data/lib/irb/cmd/subirb.rb +43 -0
- data/lib/irb/completion.rb +244 -0
- data/lib/irb/context.rb +425 -0
- data/lib/irb/ext/change-ws.rb +46 -0
- data/lib/irb/ext/history.rb +119 -0
- data/lib/irb/ext/loader.rb +129 -0
- data/lib/irb/ext/multi-irb.rb +265 -0
- data/lib/irb/ext/save-history.rb +105 -0
- data/lib/irb/ext/tracer.rb +72 -0
- data/lib/irb/ext/use-loader.rb +74 -0
- data/lib/irb/ext/workspaces.rb +67 -0
- data/lib/irb/extend-command.rb +306 -0
- data/lib/irb/frame.rb +81 -0
- data/lib/irb/help.rb +37 -0
- data/lib/irb/init.rb +302 -0
- data/lib/irb/input-method.rb +192 -0
- data/lib/irb/inspector.rb +132 -0
- data/lib/irb/lc/.document +4 -0
- data/lib/irb/lc/error.rb +32 -0
- data/lib/irb/lc/help-message +49 -0
- data/lib/irb/lc/ja/encoding_aliases.rb +11 -0
- data/lib/irb/lc/ja/error.rb +31 -0
- data/lib/irb/lc/ja/help-message +52 -0
- data/lib/irb/locale.rb +182 -0
- data/lib/irb/magic-file.rb +38 -0
- data/lib/irb/notifier.rb +232 -0
- data/lib/irb/output-method.rb +92 -0
- data/lib/irb/ruby-lex.rb +1180 -0
- data/lib/irb/ruby-token.rb +267 -0
- data/lib/irb/slex.rb +282 -0
- data/lib/irb/src_encoding.rb +7 -0
- data/lib/irb/version.rb +17 -0
- data/lib/irb/workspace.rb +143 -0
- data/lib/irb/ws-for-case-2.rb +15 -0
- data/lib/irb/xmp.rb +170 -0
- 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
|
data/lib/irb/notifier.rb
ADDED
@@ -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
|
data/lib/irb/ruby-lex.rb
ADDED
@@ -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:
|