lib/irb.rb


DEFINITIONS

This source file includes following functions.


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