irb 1.1.0.pre.3
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/LICENSE.txt +22 -0
- data/README.md +55 -0
- data/exe/irb +11 -0
- data/irb.gemspec +29 -0
- data/lib/irb.rb +855 -0
- data/lib/irb/cmd/chws.rb +34 -0
- data/lib/irb/cmd/fork.rb +39 -0
- data/lib/irb/cmd/help.rb +46 -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/color.rb +218 -0
- data/lib/irb/completion.rb +339 -0
- data/lib/irb/context.rb +459 -0
- data/lib/irb/ext/change-ws.rb +46 -0
- data/lib/irb/ext/history.rb +120 -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 +117 -0
- data/lib/irb/ext/tracer.rb +72 -0
- data/lib/irb/ext/use-loader.rb +77 -0
- data/lib/irb/ext/workspaces.rb +67 -0
- data/lib/irb/extend-command.rb +328 -0
- data/lib/irb/frame.rb +81 -0
- data/lib/irb/help.rb +37 -0
- data/lib/irb/init.rb +312 -0
- data/lib/irb/input-method.rb +298 -0
- data/lib/irb/inspector.rb +142 -0
- data/lib/irb/lc/.document +4 -0
- data/lib/irb/lc/error.rb +32 -0
- data/lib/irb/lc/help-message +50 -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 +492 -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 +190 -0
- data/lib/irb/ws-for-case-2.rb +15 -0
- data/lib/irb/xmp.rb +170 -0
- metadata +133 -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,492 @@
|
|
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 "ripper"
|
15
|
+
|
16
|
+
# :stopdoc:
|
17
|
+
class RubyLex
|
18
|
+
|
19
|
+
extend Exception2MessageMapper
|
20
|
+
def_exception(:TerminateLineInput, "Terminate Line Input")
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@exp_line_no = @line_no = 1
|
24
|
+
@indent = 0
|
25
|
+
@continue = false
|
26
|
+
@line = ""
|
27
|
+
@prompt = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# io functions
|
31
|
+
def set_input(io, p = nil, &block)
|
32
|
+
@io = io
|
33
|
+
if @io.respond_to?(:check_termination)
|
34
|
+
@io.check_termination do |code|
|
35
|
+
code.gsub!(/\s*\z/, '').concat("\n")
|
36
|
+
ltype, indent, continue, code_block_open = check_state(code)
|
37
|
+
if ltype or indent > 0 or continue or code_block_open
|
38
|
+
false
|
39
|
+
else
|
40
|
+
true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
if @io.respond_to?(:dynamic_prompt)
|
45
|
+
@io.dynamic_prompt do |lines|
|
46
|
+
lines << '' if lines.empty?
|
47
|
+
result = []
|
48
|
+
lines.each_index { |i|
|
49
|
+
c = lines[0..i].map{ |l| l + "\n" }.join
|
50
|
+
ltype, indent, continue, code_block_open = check_state(c)
|
51
|
+
result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + i)
|
52
|
+
}
|
53
|
+
result
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if p.respond_to?(:call)
|
57
|
+
@input = p
|
58
|
+
elsif block_given?
|
59
|
+
@input = block
|
60
|
+
else
|
61
|
+
@input = Proc.new{@io.gets}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_prompt(p = nil, &block)
|
66
|
+
p = block if block_given?
|
67
|
+
if p.respond_to?(:call)
|
68
|
+
@prompt = p
|
69
|
+
else
|
70
|
+
@prompt = Proc.new{print p}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_auto_indent(context)
|
75
|
+
if @io.respond_to?(:auto_indent) and context.auto_indent_mode
|
76
|
+
@io.auto_indent do |lines, line_index, byte_pointer, is_newline|
|
77
|
+
if is_newline
|
78
|
+
md = lines[line_index - 1].match(/(\A +)/)
|
79
|
+
prev_spaces = md.nil? ? 0 : md[1].count(' ')
|
80
|
+
@tokens = Ripper.lex(lines[0..line_index].join("\n"))
|
81
|
+
depth_difference = check_newline_depth_difference
|
82
|
+
prev_spaces + depth_difference * 2
|
83
|
+
else
|
84
|
+
code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
|
85
|
+
last_line = lines[line_index]&.byteslice(0, byte_pointer)
|
86
|
+
code += last_line if last_line
|
87
|
+
@tokens = Ripper.lex(code)
|
88
|
+
corresponding_token_depth = check_corresponding_token_depth
|
89
|
+
if corresponding_token_depth
|
90
|
+
corresponding_token_depth
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def check_state(code)
|
100
|
+
@tokens = Ripper.lex(code)
|
101
|
+
ltype = process_literal_type
|
102
|
+
indent = process_nesting_level
|
103
|
+
continue = process_continue
|
104
|
+
code_block_open = check_code_block(code)
|
105
|
+
[ltype, indent, continue, code_block_open]
|
106
|
+
end
|
107
|
+
|
108
|
+
def prompt
|
109
|
+
if @prompt
|
110
|
+
@prompt.call(@ltype, @indent, @continue, @line_no)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def initialize_input
|
115
|
+
@ltype = nil
|
116
|
+
@indent = 0
|
117
|
+
@continue = false
|
118
|
+
@line = ""
|
119
|
+
@exp_line_no = @line_no
|
120
|
+
@code_block_open = false
|
121
|
+
end
|
122
|
+
|
123
|
+
def each_top_level_statement
|
124
|
+
initialize_input
|
125
|
+
catch(:TERM_INPUT) do
|
126
|
+
loop do
|
127
|
+
begin
|
128
|
+
prompt
|
129
|
+
unless l = lex
|
130
|
+
throw :TERM_INPUT if @line == ''
|
131
|
+
else
|
132
|
+
@line_no += l.count("\n")
|
133
|
+
next if l == "\n"
|
134
|
+
@line.concat l
|
135
|
+
if @code_block_open or @ltype or @continue or @indent > 0
|
136
|
+
next
|
137
|
+
end
|
138
|
+
end
|
139
|
+
if @line != "\n"
|
140
|
+
@line.force_encoding(@io.encoding)
|
141
|
+
yield @line, @exp_line_no
|
142
|
+
end
|
143
|
+
break if @io.eof?
|
144
|
+
@line = ''
|
145
|
+
@exp_line_no = @line_no
|
146
|
+
|
147
|
+
@indent = 0
|
148
|
+
rescue TerminateLineInput
|
149
|
+
initialize_input
|
150
|
+
prompt
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def lex
|
157
|
+
line = @input.call
|
158
|
+
if @io.respond_to?(:check_termination)
|
159
|
+
return line # multiline
|
160
|
+
end
|
161
|
+
code = @line + (line.nil? ? '' : line)
|
162
|
+
code.gsub!(/\s*\z/, '').concat("\n")
|
163
|
+
@tokens = Ripper.lex(code)
|
164
|
+
@continue = process_continue
|
165
|
+
@code_block_open = check_code_block(code)
|
166
|
+
@indent = process_nesting_level
|
167
|
+
@ltype = process_literal_type
|
168
|
+
line
|
169
|
+
end
|
170
|
+
|
171
|
+
def process_continue
|
172
|
+
# last token is always newline
|
173
|
+
if @tokens.size >= 2 and @tokens[-2][1] == :on_regexp_end
|
174
|
+
# end of regexp literal
|
175
|
+
return false
|
176
|
+
elsif @tokens.size >= 2 and @tokens[-2][1] == :on_semicolon
|
177
|
+
return false
|
178
|
+
elsif @tokens.size >= 2 and @tokens[-2][1] == :on_kw and ['begin', 'else', 'ensure'].include?(@tokens[-2][2])
|
179
|
+
return false
|
180
|
+
elsif @tokens.size >= 3 and @tokens[-3][1] == :on_symbeg and @tokens[-2][1] == :on_ivar
|
181
|
+
# This is for :@a or :@1 because :@1 ends with EXPR_FNAME
|
182
|
+
return false
|
183
|
+
elsif @tokens.size >= 2 and @tokens[-2][1] == :on_ivar and @tokens[-2][2] =~ /\A@\d+\z/
|
184
|
+
# This is for @1
|
185
|
+
return false
|
186
|
+
elsif @tokens.size >= 2 and @tokens[-2][1] == :on_cvar and @tokens[-1][1] == :on_int
|
187
|
+
# This is for @@1 or :@@1 and ends with on_int because it's syntax error
|
188
|
+
return false
|
189
|
+
elsif !@tokens.empty? and @tokens.last[2] == "\\\n"
|
190
|
+
return true
|
191
|
+
elsif @tokens.size >= 1 and @tokens[-1][1] == :on_heredoc_end # "EOH\n"
|
192
|
+
return false
|
193
|
+
elsif @tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and @tokens[-2][3].anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME)
|
194
|
+
# end of literal except for regexp
|
195
|
+
return true
|
196
|
+
end
|
197
|
+
false
|
198
|
+
end
|
199
|
+
|
200
|
+
def check_code_block(code)
|
201
|
+
return true if @tokens.empty?
|
202
|
+
if @tokens.last[1] == :on_heredoc_beg
|
203
|
+
return true
|
204
|
+
end
|
205
|
+
|
206
|
+
begin # check if parser error are available
|
207
|
+
verbose, $VERBOSE = $VERBOSE, nil
|
208
|
+
case RUBY_ENGINE
|
209
|
+
when 'jruby'
|
210
|
+
JRuby.compile_ir(code)
|
211
|
+
else
|
212
|
+
RubyVM::InstructionSequence.compile(code)
|
213
|
+
end
|
214
|
+
rescue SyntaxError => e
|
215
|
+
case e.message
|
216
|
+
when /unterminated (?:string|regexp) meets end of file/
|
217
|
+
# "unterminated regexp meets end of file"
|
218
|
+
#
|
219
|
+
# example:
|
220
|
+
# /
|
221
|
+
#
|
222
|
+
# "unterminated string meets end of file"
|
223
|
+
#
|
224
|
+
# example:
|
225
|
+
# '
|
226
|
+
return true
|
227
|
+
when /syntax error, unexpected end-of-input/
|
228
|
+
# "syntax error, unexpected end-of-input, expecting keyword_end"
|
229
|
+
#
|
230
|
+
# example:
|
231
|
+
# if ture
|
232
|
+
# hoge
|
233
|
+
# if false
|
234
|
+
# fuga
|
235
|
+
# end
|
236
|
+
return true
|
237
|
+
when /syntax error, unexpected keyword_end/
|
238
|
+
# "syntax error, unexpected keyword_end"
|
239
|
+
#
|
240
|
+
# example:
|
241
|
+
# if (
|
242
|
+
# end
|
243
|
+
#
|
244
|
+
# example:
|
245
|
+
# end
|
246
|
+
return false
|
247
|
+
when /syntax error, unexpected '\.'/
|
248
|
+
# "syntax error, unexpected '.'"
|
249
|
+
#
|
250
|
+
# example:
|
251
|
+
# .
|
252
|
+
return false
|
253
|
+
when /unexpected tREGEXP_BEG/
|
254
|
+
# "syntax error, unexpected tREGEXP_BEG, expecting keyword_do or '{' or '('"
|
255
|
+
#
|
256
|
+
# example:
|
257
|
+
# method / f /
|
258
|
+
return false
|
259
|
+
when /numbered parameter outside block/
|
260
|
+
# "numbered parameter outside block"
|
261
|
+
#
|
262
|
+
# example:
|
263
|
+
# :@1
|
264
|
+
return false
|
265
|
+
end
|
266
|
+
ensure
|
267
|
+
$VERBOSE = verbose
|
268
|
+
end
|
269
|
+
|
270
|
+
if defined?(Ripper::EXPR_BEG)
|
271
|
+
last_lex_state = @tokens.last[3]
|
272
|
+
if last_lex_state.allbits?(Ripper::EXPR_BEG)
|
273
|
+
return false
|
274
|
+
elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
|
275
|
+
return true
|
276
|
+
elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
|
277
|
+
return true
|
278
|
+
elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
|
279
|
+
return true
|
280
|
+
elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
|
281
|
+
return true
|
282
|
+
elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
|
283
|
+
return false
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
false
|
288
|
+
end
|
289
|
+
|
290
|
+
def process_nesting_level
|
291
|
+
indent = 0
|
292
|
+
@tokens.each_with_index { |t, index|
|
293
|
+
case t[1]
|
294
|
+
when :on_lbracket, :on_lbrace, :on_lparen
|
295
|
+
indent += 1
|
296
|
+
when :on_rbracket, :on_rbrace, :on_rparen
|
297
|
+
indent -= 1
|
298
|
+
when :on_kw
|
299
|
+
next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
|
300
|
+
case t[2]
|
301
|
+
when 'do'
|
302
|
+
if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_CMDARG)
|
303
|
+
# method_with_bock do; end
|
304
|
+
indent += 1
|
305
|
+
else
|
306
|
+
# while cond do; end # also "until" or "for"
|
307
|
+
# This "do" doesn't increment indent because "while" already
|
308
|
+
# incremented.
|
309
|
+
end
|
310
|
+
when 'def', 'case', 'for', 'begin', 'class', 'module'
|
311
|
+
indent += 1
|
312
|
+
when 'if', 'unless', 'while', 'until'
|
313
|
+
# postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
|
314
|
+
indent += 1 unless t[3].allbits?(Ripper::EXPR_LABEL)
|
315
|
+
when 'end'
|
316
|
+
indent -= 1
|
317
|
+
end
|
318
|
+
end
|
319
|
+
# percent literals are not indented
|
320
|
+
}
|
321
|
+
indent
|
322
|
+
end
|
323
|
+
|
324
|
+
def check_newline_depth_difference
|
325
|
+
depth_difference = 0
|
326
|
+
@tokens.each_with_index do |t, index|
|
327
|
+
case t[1]
|
328
|
+
when :on_ignored_nl, :on_nl
|
329
|
+
if index != (@tokens.size - 1)
|
330
|
+
depth_difference = 0
|
331
|
+
end
|
332
|
+
next
|
333
|
+
when :on_sp
|
334
|
+
next
|
335
|
+
end
|
336
|
+
case t[1]
|
337
|
+
when :on_lbracket, :on_lbrace, :on_lparen
|
338
|
+
depth_difference += 1
|
339
|
+
when :on_rbracket, :on_rbrace, :on_rparen
|
340
|
+
depth_difference -= 1
|
341
|
+
when :on_kw
|
342
|
+
next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
|
343
|
+
case t[2]
|
344
|
+
when 'do'
|
345
|
+
if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_CMDARG)
|
346
|
+
# method_with_bock do; end
|
347
|
+
depth_difference += 1
|
348
|
+
else
|
349
|
+
# while cond do; end # also "until" or "for"
|
350
|
+
# This "do" doesn't increment indent because "while" already
|
351
|
+
# incremented.
|
352
|
+
end
|
353
|
+
when 'def', 'case', 'for', 'begin', 'class', 'module'
|
354
|
+
depth_difference += 1
|
355
|
+
when 'if', 'unless', 'while', 'until'
|
356
|
+
# postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
|
357
|
+
unless t[3].allbits?(Ripper::EXPR_LABEL)
|
358
|
+
depth_difference += 1
|
359
|
+
end
|
360
|
+
when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in'
|
361
|
+
depth_difference += 1
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
depth_difference
|
366
|
+
end
|
367
|
+
|
368
|
+
def check_corresponding_token_depth
|
369
|
+
corresponding_token_depth = nil
|
370
|
+
is_first_spaces_of_line = true
|
371
|
+
is_first_printable_of_line = true
|
372
|
+
spaces_of_nest = []
|
373
|
+
spaces_at_line_head = 0
|
374
|
+
@tokens.each_with_index do |t, index|
|
375
|
+
corresponding_token_depth = nil
|
376
|
+
case t[1]
|
377
|
+
when :on_ignored_nl, :on_nl
|
378
|
+
spaces_at_line_head = 0
|
379
|
+
is_first_spaces_of_line = true
|
380
|
+
is_first_printable_of_line = true
|
381
|
+
next
|
382
|
+
when :on_sp
|
383
|
+
spaces_at_line_head = t[2].count(' ') if is_first_spaces_of_line
|
384
|
+
is_first_spaces_of_line = false
|
385
|
+
next
|
386
|
+
end
|
387
|
+
case t[1]
|
388
|
+
when :on_lbracket, :on_lbrace, :on_lparen
|
389
|
+
spaces_of_nest.push(spaces_at_line_head)
|
390
|
+
when :on_rbracket, :on_rbrace, :on_rparen
|
391
|
+
if is_first_printable_of_line
|
392
|
+
corresponding_token_depth = spaces_of_nest.pop
|
393
|
+
else
|
394
|
+
spaces_of_nest.pop
|
395
|
+
corresponding_token_depth = nil
|
396
|
+
end
|
397
|
+
when :on_kw
|
398
|
+
next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
|
399
|
+
case t[2]
|
400
|
+
when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
|
401
|
+
spaces_of_nest.push(spaces_at_line_head)
|
402
|
+
when 'if', 'unless', 'while', 'until'
|
403
|
+
# postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
|
404
|
+
unless t[3].allbits?(Ripper::EXPR_LABEL)
|
405
|
+
spaces_of_nest.push(spaces_at_line_head)
|
406
|
+
end
|
407
|
+
when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in'
|
408
|
+
corresponding_token_depth = spaces_of_nest.last
|
409
|
+
when 'end'
|
410
|
+
if is_first_printable_of_line
|
411
|
+
corresponding_token_depth = spaces_of_nest.pop
|
412
|
+
else
|
413
|
+
spaces_of_nest.pop
|
414
|
+
corresponding_token_depth = nil
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
is_first_spaces_of_line = false
|
419
|
+
is_first_printable_of_line = false
|
420
|
+
end
|
421
|
+
corresponding_token_depth
|
422
|
+
end
|
423
|
+
|
424
|
+
def check_string_literal
|
425
|
+
i = 0
|
426
|
+
start_token = []
|
427
|
+
end_type = []
|
428
|
+
while i < @tokens.size
|
429
|
+
t = @tokens[i]
|
430
|
+
case t[1]
|
431
|
+
when :on_tstring_beg
|
432
|
+
start_token << t
|
433
|
+
end_type << [:on_tstring_end, :on_label_end]
|
434
|
+
when :on_regexp_beg
|
435
|
+
start_token << t
|
436
|
+
end_type << :on_regexp_end
|
437
|
+
when :on_symbeg
|
438
|
+
acceptable_single_tokens = %i{on_ident on_const on_op on_cvar on_ivar on_gvar on_kw}
|
439
|
+
if (i + 1) < @tokens.size and acceptable_single_tokens.all?{ |t| @tokens[i + 1][1] != t }
|
440
|
+
start_token << t
|
441
|
+
end_type << :on_tstring_end
|
442
|
+
end
|
443
|
+
when :on_backtick
|
444
|
+
start_token << t
|
445
|
+
end_type << :on_tstring_end
|
446
|
+
when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg
|
447
|
+
start_token << t
|
448
|
+
end_type << :on_tstring_end
|
449
|
+
when :on_heredoc_beg
|
450
|
+
start_token << t
|
451
|
+
end_type << :on_heredoc_end
|
452
|
+
when *end_type.last
|
453
|
+
start_token.pop
|
454
|
+
end_type.pop
|
455
|
+
end
|
456
|
+
i += 1
|
457
|
+
end
|
458
|
+
start_token.last.nil? ? '' : start_token.last
|
459
|
+
end
|
460
|
+
|
461
|
+
def process_literal_type
|
462
|
+
start_token = check_string_literal
|
463
|
+
case start_token[1]
|
464
|
+
when :on_tstring_beg
|
465
|
+
case start_token[2]
|
466
|
+
when ?" then ?"
|
467
|
+
when /^%.$/ then ?"
|
468
|
+
when /^%Q.$/ then ?"
|
469
|
+
when ?' then ?'
|
470
|
+
when /^%q.$/ then ?'
|
471
|
+
end
|
472
|
+
when :on_regexp_beg then ?/
|
473
|
+
when :on_symbeg then ?:
|
474
|
+
when :on_backtick then ?`
|
475
|
+
when :on_qwords_beg then ?]
|
476
|
+
when :on_words_beg then ?]
|
477
|
+
when :on_qsymbols_beg then ?]
|
478
|
+
when :on_symbols_beg then ?]
|
479
|
+
when :on_heredoc_beg
|
480
|
+
start_token[2] =~ /<<[-~]?(['"`])[_a-zA-Z0-9]+\1/
|
481
|
+
case $1
|
482
|
+
when ?" then ?"
|
483
|
+
when ?' then ?'
|
484
|
+
when ?` then ?`
|
485
|
+
else ?"
|
486
|
+
end
|
487
|
+
else
|
488
|
+
nil
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
# :startdoc:
|