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