* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / irb.rb
blob7fe3d7dc51b47d6c9ef427c41aa2149b9f09f20c
2 #   irb.rb - irb main module
3 #       $Release Version: 0.9.5 $
4 #       $Revision$
5 #       by Keiju ISHITSUKA(keiju@ruby-lang.org)
7 # --
11 require "e2mmap"
13 require "irb/init"
14 require "irb/context"
15 require "irb/extend-command"
16 #require "irb/workspace"
18 require "irb/ruby-lex"
19 require "irb/input-method"
20 require "irb/locale"
22 STDOUT.sync = true
24 module IRB
25   @RCS_ID='-$Id$-'
27   class Abort < Exception;end
29   #
30   @CONF = {}
32   def IRB.conf
33     @CONF
34   end
36   # IRB version method
37   def IRB.version
38     if v = @CONF[:VERSION] then return v end
40     require "irb/version"
41     rv = @RELEASE_VERSION.sub(/\.0/, "")
42     @CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE)
43   end
45   def IRB.CurrentContext
46     IRB.conf[:MAIN_CONTEXT]
47   end
49   # initialize IRB and start TOP_LEVEL irb
50   def IRB.start(ap_path = nil)
51     $0 = File::basename(ap_path, ".rb") if ap_path
53     IRB.setup(ap_path)
55     if @CONF[:SCRIPT]
56       irb = Irb.new(nil, @CONF[:SCRIPT])
57     else
58       irb = Irb.new
59     end
61     @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
62     @CONF[:MAIN_CONTEXT] = irb.context
64     trap("SIGINT") do
65       irb.signal_handle
66     end
67     
68     catch(:IRB_EXIT) do
69       irb.eval_input
70     end
71 #    print "\n"
72   end
74   def IRB.irb_exit(irb, ret)
75     throw :IRB_EXIT, ret
76   end
78   def IRB.irb_abort(irb, exception = Abort)
79     if defined? Thread
80       irb.context.thread.raise exception, "abort then interrupt!!"
81     else
82       raise exception, "abort then interrupt!!"
83     end
84   end
86   #
87   # irb interpreter main routine 
88   #
89   class Irb
90     def initialize(workspace = nil, input_method = nil, output_method = nil)
91       @context = Context.new(self, workspace, input_method, output_method)
92       @context.main.extend ExtendCommandBundle
93       @signal_status = :IN_IRB
95       @scanner = RubyLex.new
96       @scanner.exception_on_syntax_error = false
97     end
98     attr_reader :context
99     attr_accessor :scanner
101     def eval_input
102       @scanner.set_prompt do
103         |ltype, indent, continue, line_no|
104         if ltype
105           f = @context.prompt_s
106         elsif continue
107           f = @context.prompt_c
108         elsif indent > 0
109           f = @context.prompt_n
110         else @context.prompt_i
111           f = @context.prompt_i
112         end
113         f = "" unless f
114         if @context.prompting?
115           @context.io.prompt = p = prompt(f, ltype, indent, line_no)
116         else
117           @context.io.prompt = p = ""
118         end
119         if @context.auto_indent_mode
120           unless ltype
121             ind = prompt(@context.prompt_i, ltype, indent, line_no)[/.*\z/].size +
122               indent * 2 - p.size
123             ind += 2 if continue
124             @context.io.prompt = p + " " * ind if ind > 0
125           end
126         end
127       end
128        
129       @scanner.set_input(@context.io) do
130         signal_status(:IN_INPUT) do
131           if l = @context.io.gets
132             print l if @context.verbose?
133           else
134             if @context.ignore_eof? and @context.io.readable_atfer_eof?
135               l = "\n"
136               if @context.verbose?
137                 printf "Use \"exit\" to leave %s\n", @context.ap_name
138               end
139             end
140           end
141           l
142         end
143       end
145       @scanner.each_top_level_statement do |line, line_no|
146         signal_status(:IN_EVAL) do
147           begin
148             line.untaint
149             @context.evaluate(line, line_no)
150             output_value if @context.echo?
151             exc = nil
152           rescue Interrupt => exc
153           rescue SystemExit, SignalException
154             raise
155           rescue Exception => exc
156           end
157           if exc
158             print exc.class, ": ", exc, "\n"
159             if exc.backtrace[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
160                 !(SyntaxError === exc)
161               irb_bug = true 
162             else
163               irb_bug = false
164             end
166             messages = []
167             lasts = []
168             levels = 0
169             for m in exc.backtrace
170               m = @context.workspace.filter_backtrace(m) unless irb_bug
171               if m
172                 if messages.size < @context.back_trace_limit
173                   messages.push "\tfrom "+m
174                 else
175                   lasts.push "\tfrom "+m
176                   if lasts.size > @context.back_trace_limit
177                     lasts.shift 
178                     levels += 1
179                   end
180                 end
181               end
182             end
183             print messages.join("\n"), "\n"
184             unless lasts.empty?
185               printf "... %d levels...\n", levels if levels > 0
186               print lasts.join("\n")
187             end
188             print "Maybe IRB bug!!\n" if irb_bug
189           end
190           if $SAFE > 2
191             abort "Error: irb does not work for $SAFE level higher than 2"
192           end
193         end
194       end
195     end
197     def suspend_name(path = nil, name = nil)
198       @context.irb_path, back_path = path, @context.irb_path if path
199       @context.irb_name, back_name = name, @context.irb_name if name
200       begin
201         yield back_path, back_name
202       ensure
203         @context.irb_path = back_path if path
204         @context.irb_name = back_name if name
205       end
206     end
208     def suspend_workspace(workspace)
209       @context.workspace, back_workspace = workspace, @context.workspace
210       begin
211         yield back_workspace
212       ensure
213         @context.workspace = back_workspace
214       end
215     end
217     def suspend_input_method(input_method)
218       back_io = @context.io
219       @context.instance_eval{@io = input_method}
220       begin
221         yield back_io
222       ensure
223         @context.instance_eval{@io = back_io}
224       end
225     end
227     def suspend_context(context)
228       @context, back_context = context, @context
229       begin
230         yield back_context
231       ensure
232         @context = back_context
233       end
234     end
236     def signal_handle
237       unless @context.ignore_sigint?
238         print "\nabort!!\n" if @context.verbose?
239         exit
240       end
242       case @signal_status
243       when :IN_INPUT
244         print "^C\n"
245         raise RubyLex::TerminateLineInput
246       when :IN_EVAL
247         IRB.irb_abort(self)
248       when :IN_LOAD
249         IRB.irb_abort(self, LoadAbort)
250       when :IN_IRB
251         # ignore
252       else
253         # ignore other cases as well
254       end
255     end
257     def signal_status(status)
258       return yield if @signal_status == :IN_LOAD
260       signal_status_back = @signal_status
261       @signal_status = status
262       begin
263         yield
264       ensure
265         @signal_status = signal_status_back
266       end
267     end
269     def prompt(prompt, ltype, indent, line_no)
270       p = prompt.dup
271       p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
272         case $2
273         when "N"
274           @context.irb_name
275         when "m"
276           @context.main.to_s
277         when "M"
278           @context.main.inspect
279         when "l"
280           ltype
281         when "i"
282           if $1 
283             format("%" + $1 + "d", indent)
284           else
285             indent.to_s
286           end
287         when "n"
288           if $1 
289             format("%" + $1 + "d", line_no)
290           else
291             line_no.to_s
292           end
293         when "%"
294           "%"
295         end
296       end
297       p
298     end
300     def output_value
301       if @context.inspect?
302         printf @context.return_format, @context.last_value.inspect
303       else
304         printf @context.return_format, @context.last_value
305       end
306     end
308     def inspect
309       ary = []
310       for iv in instance_variables
311         case (iv = iv.to_s)
312         when "@signal_status"
313           ary.push format("%s=:%s", iv, @signal_status.id2name)
314         when "@context"
315           ary.push format("%s=%s", iv, eval(iv).__to_s__)
316         else
317           ary.push format("%s=%s", iv, eval(iv))
318         end
319       end
320       format("#<%s: %s>", self.class, ary.join(", "))
321     end
322   end
324   # Singleton method
325   def @CONF.inspect
326     IRB.version unless self[:VERSION]
328     array = []
329     for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
330       case k
331       when :MAIN_CONTEXT, :__TMP__EHV__
332         array.push format("CONF[:%s]=...myself...", k.id2name)
333       when :PROMPT
334         s = v.collect{
335           |kk, vv|
336           ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
337           format(":%s=>{%s}", kk.id2name, ss.join(", "))
338         }
339         array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
340       else
341         array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
342       end
343     end
344     array.join("\n")
345   end