#!/usr/local/bin/ruby
#
#   rbc.rb - 
#   	$Release Version: 0.8 $
#   	$Revision: 1.8 $
#   	$Date: 1998/03/11 05:43:00 $
#   	by Keiju ISHITSUKA(Nippon Rational Inc.)
#
# --
# Usage:
#
#   rbc.rb [options] file_name opts
#   options:
#	-d		    debug mode (not recommended)
#	-f		    does not read ~/.irbrc
#	-m		    bc mode (rational/matrix calc)
#	-r load-module	    same as `ruby -r'
#	--inspect	    use inspect for result output
#			    (default for non-bc mode)
#	--noinspect	    does not use inspect for result output
#	--noreadline	    does not use readline library
#			    (default: try to use readline)
#
# additional private method (as function):
#   exit, quit		    terminate the interpreter
#   inspect_mode(sw = nil)  toggle inspect mode
#   trace_load(sw = nil)    change trace mode for file loading using
#			    load/require.  (default: trace-mode on)
#
require "e2mmap.rb"

$stdout.sync = TRUE

module BC_APPLICATION__
  RCS_ID='-$Id: rbc.rb,v 1.8 1998/03/11 05:43:00 keiju Exp keiju $-'
  
  extend Exception2MessageMapper
  def_exception :UnrecognizedSwitch, "Unrecognized switch: %s"
  
  CONFIG = {}
  CONFIG[0] = $0
  CONFIG[:USE_READLINE] = TRUE
  CONFIG[:LOAD_MODULES] = []
  CONFIG[:INSPECT] = nil
  CONFIG[:TRACE_LOAD] = FALSE
  CONFIG[:RC] = TRUE

  CONFIG[:DEBUG] = FALSE

  while opt = ARGV.shift
    case opt
    when "-d"
      CONFIG[:DEBUG] = TRUE
    when "-m"
      CONFIG[:INSPECT] = FALSE if CONFIG[:INSPECT].nil?
      require "mathn.rb"
      include Math
    when "-r"
      opt = ARGV.shift
      CONFIG[:LOAD_MODULES].push opt if opt
    when "-f"
      opt = ARGV.shift
      CONFIG[:RC] = FALSE
    when "--inspect"
      CONFIG[:INSPECT] = TRUE
    when "--noinspect"
      CONFIG[:INSPECT] = FALSE
    when "--noreadline"
      CONFIG[:USE_READLINE] = FALSE
    when /^-/
      #	  print UnrecognizedSwitch.inspect, "\n"
      BC_APPLICATION__.fail UnrecognizedSwitch, opt
    else
      CONFIG[:USE_READLINE] = FALSE
      $0 = opt
      break
    end
  end
  CONFIG[:INSPECT] = TRUE if CONFIG[:INSPECT].nil?

  PROMPTi = "rbc%d> "
  PROMPTs = "rbc%d%s "
  PROMPTe = "rbc%d* "
  
  class BC
    def initialize
      lex_init
    end
    
    def eval_input(io, cont, bind)
      line = ''
      @io = io
      @ltype = nil
      @quoted = nil
      @indent = 0
      @lex_state = EXPR_BEG
      
      @io.prompt = format(PROMPTi, @indent)

      loop do
	@continue = FALSE
	l = @io.gets

	unless l
	  break if line == ''
	else
	  line = line + l
	  
	  lex(l) if l != "\n"
	  print @quoted.inspect, "\n" if CONFIG[:DEBUG]
	  if @ltype
	    @io.prompt = format(PROMPTs, @indent, @ltype)
	    next
	  elsif @continue
	    @io.prompt = format(PROMPTe, @indent)
	    next
	  elsif @indent > 0
	    @io.prompt = format(PROMPTi, @indent)
	    next
	  end
	end
	
	if line != "\n"
	  begin
	    if CONFIG[:INSPECT]
	      print((cont._=eval(line, bind)).inspect, "\n")
	    else
	      print((cont._=eval(line, bind)), "\n")
	    end
	  rescue
	    #	$! = 'exception raised' unless $!
	    #	print "ERR: ", $!, "\n"
	    $! = RuntimeError.new("exception raised") unless $!
	    print $!.type, ": ", $!, "\n"
	  end
	end
	break if not l
	line = ''
	indent = 0
	@io.prompt = format(PROMPTi, indent)
      end
      print "\n"
    end
    
    EXPR_BEG = :EXPR_BEG
    EXPR_MID = :EXPR_MID
    EXPR_END = :EXPR_END
    EXPR_ARG = :EXPR_ARG
    EXPR_FNAME = :EXPR_FNAME

    CLAUSE_STATE_TRANS = {
      "alias"	=>  EXPR_FNAME,
      "and"	=>  EXPR_BEG,
      "begin"	=>  EXPR_BEG,
      "case"	=>  EXPR_BEG,
      "class"	=>  EXPR_BEG,
      "def"	=>  EXPR_FNAME,
      "defined?"	=>  EXPR_END,
      "do"	=>  EXPR_BEG,
      "else"	=>  EXPR_BEG,
      "elsif"	=>  EXPR_BEG,
      "end"	=>  EXPR_END,
      "ensure"	=>  EXPR_BEG,
      "for"	=>  EXPR_BEG,
      "if"	=>  EXPR_BEG,
      "in"	=>  EXPR_BEG,
      "module"	=>  EXPR_BEG,
      "nil"	=>  EXPR_END,
      "not"	=>  EXPR_BEG,
      "or"	=>  EXPR_BEG,
      "rescue"	=>  EXPR_MID,
      "return"	=>  EXPR_MID,
      "self"	=>  EXPR_END,
      "super"	=>  EXPR_END,
      "then"	=>  EXPR_BEG,
      "undef"	=>  EXPR_FNAME,
      "unless"	=>  EXPR_BEG,
      "until"	=>  EXPR_BEG,
      "when"	=>  EXPR_BEG,
      "while"	=>  EXPR_BEG,
      "yield"	=>  EXPR_END
    }
    
    ENINDENT_CLAUSE = [
      "case", "class", "def", "do", "for", "if",
      "module", "unless", "until", "while", "begin" #, "when"
    ]
    DEINDENT_CLAUSE = ["end" #, "when"
    ]

    PARCENT_LTYPE = {
      "q" => "\'",
      "Q" => "\"",
      "x" => "\`",
      "r" => "\/"
    }
    
    PARCENT_PAREN = {
      "{" => "}",
      "[" => "]",
      "<" => ">",
      "(" => ")"
    }

    def lex_init()
      @OP = Trie.new
      @OP.def_rules("\0", "\004", "\032"){}
      @OP.def_rules(" ", "\t", "\f", "\r", "\13") do
	@space_seen = TRUE
	next
      end
      @OP.def_rule("#") do
	|op, rests|
	@ltype = "#"
	identify_comment(rests)
      end
      @OP.def_rule("\n") do
	print "\\n\n" if CONFIG[:DEBUG]
	if @lex_state == EXPR_BEG || @lex_state == EXPR_FNAME
	  @continue = TRUE
	else
	  @lex_state = EXPR_BEG
	end
      end
      @OP.def_rules("*", "*=", "**=", "**") {@lex_state = EXPR_BEG}
      @OP.def_rules("!", "!=", "!~") {@lex_state = EXPR_BEG}
      @OP.def_rules("=", "==", "===", "=~", "<=>") {@lex_state = EXPR_BEG}
      @OP.def_rules("<", "<=", "<<") {@lex_state = EXPR_BEG}
      @OP.def_rules(">", ">=", ">>") {@lex_state = EXPR_BEG}
      @OP.def_rules("'", '"') do
	|op, rests|
	@ltype = op
	@quoted = op
	identify_string(rests)
      end
      @OP.def_rules("`") do
	|op, rests|
	if @lex_state != EXPR_FNAME
	  @ltype = op
	  @quoted = op
	  identify_string(rests)
	end
      end
      @OP.def_rules('?') do
	|op, rests|
	@lex_state = EXPR_END
	identify_question(rests)
      end
      @OP.def_rules("&", "&&", "&=", "|", "||", "|=") do
	@lex_state = EXPR_BEG
      end
      @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) {}
      @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) {}
      @OP.def_rules("+=", "-=") {@lex_state = EXPR_BEG}
      @OP.def_rules("+", "-") do
	|op, rests|
	if @lex_state == EXPR_ARG
	  if @space_seen and rests[0] =~ /[0-9]/
	    identify_number(rests)
	  else
	    @lex_state = EXPR_BEG
	  end
	elsif @lex_state != EXPR_END and rests[0] =~ /[0-9]/
	  identify_number(rests)
	else
	  @lex_state = EXPR_BEG
	end
      end
      @OP.def_rule(".") do
	|op, rests|
	@lex_state = EXPR_BEG
	if rests[0] =~ /[0-9]/
	  rests.unshift op
	  identify_number(rests)
	else
	  # obj.if などの対応
	  identify_identifier(rests, TRUE)
	  @lex_state = EXPR_ARG
	end
      end
      @OP.def_rules("..", "...") {@lex_state = EXPR_BEG}

      lex_int2
    end
    
    def lex_int2
      @OP.def_rules("]", "}", ")") do
	@lex_state = EXPR_END
	@indent -= 1
      end
      @OP.def_rule(":") {|op,rests|
	identify_identifier(rests, TRUE)
      }
      @OP.def_rule("::") {|op,rests|
	identify_identifier(rests, TRUE);
      }
      @OP.def_rule("/") do
	|op, rests|
	if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
	  @ltype = op
	  @quoted = op
	  identify_string(rests)
	elsif rests[0] == '='
	  rests.shift
	  @lex_state = EXPR_BEG
	elsif @lex_state == EXPR_ARG and @space_seen and rests[0] =~ /\s/
	  @ltype = op
	  @quoted = op
	  identify_string(rests)
	else 
	  @lex_state = EXPR_BEG
	end
      end
      @OP.def_rules("^", "^=") {@lex_state = EXPR_BEG}
      @OP.def_rules(",", ";") {@lex_state = EXPR_BEG}
      @OP.def_rule("~") {@lex_state = EXPR_BEG}
      @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) {}
      @OP.def_rule("(") do
	@lex_state = EXPR_BEG
	@indent += 1
      end
      @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) {}
      @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) {}
      @OP.def_rule("[") do
	@indent += 1
	if @lex_state != EXPR_FNAME
	  @lex_state = EXPR_BEG
	end
      end
      @OP.def_rule("{") do
	@lex_state = EXPR_BEG
	@indent += 1
      end
      @OP.def_rule('\\') {|op, rests| identify_escape(rests)} #')
      @OP.def_rule('%') do
	|op, rests|
	if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
	  identify_quotation(rests)
	elsif rests[0] == '='
	  rests.shift
	elsif @lex_state == EXPR_ARG and @space_seen and rests[0] =~ /\s/
	  identify_quotation(rests)
	else
	  @lex_state = EXPR_BEG
	end
      end
      @OP.def_rule('$') do
	|op, rests|
	identify_gvar(rests)
      end
      @OP.def_rule('@') do
	|op, rests|
	if rests[0] =~ /[\w_]/
	  rests.unshift op
	  identify_identifier(rests)
	end
      end
      @OP.def_rule("def", proc{|op, chrs| /\s/ =~ chrs[0]}) do 
	|op, rests|
	@indent += 1
	@lex_state = EXPR_END
	until rests[0] == "\n" or rests[0] == ";"
	  rests.shift
	end
      end
      @OP.def_rule("") do
	|op, rests|
	printf "MATCH: start %s: %s\n", op, rests.inspect if CONFIG[:DEBUG]
	if rests[0] =~ /[0-9]/
	  identify_number(rests)
	elsif rests[0] =~ /[\w_]/
	  identify_identifier(rests)
	end
	printf "MATCH: end %s: %s\n", op, rests.inspect if CONFIG[:DEBUG]
      end
      
      p @OP if CONFIG[:DEBUG]
    end
    
    def lex(l)
      chrs = l.split(//)
      tokens = []
      
      case @ltype
      when "'", '"', '`', '/'
	identify_string(chrs)
	return if chrs.empty?
      when "#"
	identify_comment(chrs)
	return
      when "="
	if l =~ /^=end/
	  $ltype = nil
	  return
	end
      else
	if l =~ /^=begin/
	  $ltype = "="
	  return
	end
      end
      
      until chrs.empty?
	@space_seen = FALSE
	printf "perse: %s\n", chrs.join("") if CONFIG[:DEBUG]
	@OP.match(chrs)
	printf "lex_state: %s continue: %s\n", @lex_state.id2name, @continue if CONFIG[:DEBUG]
      end
    end
    
    def identify_gvar(chrs)
      @lex_state = EXPR_END
      
      ch = chrs.shift
      case ch
      when /[_~*$?!@/\\;,.=:<>"]/   #"
	return
	
      when "-"
	ch = chrs.shift
	return
	
      when "&", "`", "'", "+"
	return
	
      when /[1-9]/
	chrs.unshift ch
	v = "$"
	while (ch = chrs.shift) =~ /[0-9]/
	end
	chrs.unshift ch
	return
	
      when /\w/
	chrs.unshift ch
	chrs.unshift "$"
	identify_identifier(chrs)
	return
	
      else 
	chrs.unshift ch
	return
      end
    end
    
    def identify_identifier(chrs, escaped = FALSE)
      token = ""
      token.concat chrs.shift if chrs[0] =~ /[$@]/ or escaped
      while (ch = chrs.shift) =~ /\w|_/
	print ":", ch, ":" if CONFIG[:DEBUG]
	token.concat ch
      end
      chrs.unshift ch
      
      if ch == "!" or ch == "?"
	chrs.shift
	token.concat ch
      end
      # fix token
      
      if token =~ /^[$@]/ or escaped
	@lex_state = EXPR_END
	return
      end
      
      print token, "\n" if CONFIG[:DEBUG]
      if state = CLAUSE_STATE_TRANS[token]
	if @lex_state != EXPR_BEG and token =~ /^(if|unless|while|until)/
	  # 修飾子
	else
	  if ENINDENT_CLAUSE.include?(token)
	    @indent += 1
	  elsif DEINDENT_CLAUSE.include?(token)
	    @indent -= 1
	  end
	end
	@lex_state = state
	return
      end
      if @lex_state == EXPR_FNAME
	@lex_state = EXPR_END
	if chrs[0] == '='
	  chrs.shift
	end
      elsif @lex_state == EXPR_BEG
	@lex_state = EXPR_ARG
      else
	@lex_state = EXPR_END
      end
    end
    
    def identify_quotation(chrs)
      ch = chrs.shift
      if lt = PARCENT_LTYPE[ch]
	ch = chrs.shift
      else
	lt = "\""
      end
      if ch !~ /\W/
	chrs.unshift ch
	next
      end
      @ltype = lt
      unless @quoted = PARCENT_PAREN[ch]
	@quoted = ch
      end
      identify_string(chrs)
    end

    def identify_number(chrs)
      @lex_state = EXPR_END
      
      ch = chrs.shift
      case ch
      when /0/
	if (ch = chrs[0]) == "x"
	  chrs.shift
	  match = /[0-9a-f_]/
	else
	  match = /[0-7_]/
	end
	while ch = chrs.shift
	  if ch !~ match
	    chrs.unshift ch
	    break
	  end
	end
	return
      end
      
      while ch = chrs.shift
	case ch
	when /[0-9]/
	when "e", "E"
	  #	type = FLOAT
	  unless (ch = chrs.shift) == "+" or ch == "-"
	    chrs.unshift ch
	  end
	when "."
	  #	type = FLOAT
	when "_"
	else
	  chrs.unshift ch
	  return
	end
      end
    end
    
    def identify_question(chrs)
      @lex_state = EXPR_END
      
      if chrs.shift == "\\" #"
	identify_escape(chrs)
      end
    end
    
    def identify_string(chrs)
      while ch = chrs.shift
	if @quoted == ch
	  if @ltype == "/"
	    if chrs[0] =~ /i|o|n|e|s/
	      chrs.shift
	    end
	  end
	  @ltype = nil
	  @quoted = nil
	  @lex_state = EXPR_END
	  break
	elsif ch == '\\' #'
	  identify_escape(chrs)
	end
      end
    end
    
    def identify_comment(chrs)
      while ch = chrs.shift
	if ch == "\\" #"
	  identify_escape(chrs)
	end
	if ch == "\n"
	  @ltype = nil
	  chrs.unshift ch
	  break
	end
      end
    end
    
    def identify_escape(chrs)
      ch = chrs.shift
      case ch
      when "\n", "\r", "\f"
	@continue = TRUE
      when "\\", "n", "t", "r", "f", "v", "a", "e", "b" #"
      when /[0-7]/
	chrs.unshift ch
	3.times do
	  ch = chrs.shift
	  case ch
	  when /[0-7]/
	  when nil
	    break
	  else
	    chrs.unshift ch
	    break
	  end
	end
      when "x"
	2.times do
	  ch = chrs.shift
	  case ch
	  when /[0-9a-fA-F]/
	  when nil
	    break
	  else
	    chrs.unshift ch
	    break
	  end
	end
      when "M"
	if (ch = chrs.shift) != '-'
	  chrs.unshift ch
	elsif (ch = chrs.shift) == "\\" #"
	  identify_escape(chrs)
	end
	return
      when "C", "c", "^"
	if ch == "C" and (ch = chrs.shift) != "-"
	  chrs.unshift ch
	elsif (ch = chrs.shift) == "\\" #"
	  identify_escape(chrs)
	end
	return
      end
    end
  end
  
  class Trie
    extend Exception2MessageMapper
    def_exception :ErrNodeNothing, "node nothing"
    def_exception :ErrNodeAlreadyExists, "node already exists"

    class Node
      # postprocがなければ抽象ノード, nilじゃなければ具象ノード
      def initialize(preproc = nil, postproc = nil)
	@Tree = {}
	@preproc = preproc
	@postproc = postproc
      end

      attr :preproc, TRUE
      attr :postproc, TRUE
      
      def search(chrs, opt = nil)
	return self if chrs.empty?
	ch = chrs.shift
	if node = @Tree[ch]
	  node.search(chrs, opt)
	else
	  if opt
	    chrs.unshift ch
	    self.create_subnode(chrs)
	  else
	    Trie.fail ErrNodeNothing
	  end
	end
      end
      
      def create_subnode(chrs, preproc = nil, postproc = nil)
	if chrs.empty?
	  if @postproc
	    p node
	    Trie.fail ErrNodeAlreadyExists
	  else
	    print "Warn: change abstruct node to real node\n" if CONFIG[:DEBUG]
	    @preproc = preproc
	    @postproc = postproc
	  end
	  return self
	end
	
	ch = chrs.shift
	if node = @Tree[ch]
	  if chrs.empty?
	    if node.postproc
	      p node
	      Trie.fail ErrNodeAlreadyExists
	    else
	      print "Warn: change abstruct node to real node\n" if CONFIG[:DEBUG]
	      node.preproc = preproc
	      node.postproc = postproc
	    end
	  else
	    node.create_subnode(chrs, preproc, postproc)
	  end
	else
	  if chrs.empty?
	    node = Node.new(preproc, postproc)
	  else
	    node = Node.new
	    node.create_subnode(chrs, preproc, postproc)
	  end
	  @Tree[ch] = node
	end
	node
      end
      
      def match(chrs, op = "")
	print "match>: ", chrs, "op:", op, "\n" if CONFIG[:DEBUG]
	if chrs.empty?
	  if @preproc.nil? || @preproc.call(op, chrs)
	    printf "op1: %s\n", op if CONFIG[:DEBUG]
	    @postproc.call(op, chrs)
	    ""
	  else
	    nil
	  end
	else
	  ch = chrs.shift
	  if node = @Tree[ch]
	    if ret = node.match(chrs, op+ch)
	      return ch+ret
	    else
	      chrs.unshift ch
	      if @postproc and @preproc.nil? || @preproc.call(op, chrs)
		printf "op2: %s\n", op.inspect if CONFIG[:DEBUG]
		@postproc.call(op, chrs)
		return ""
	      else
		return nil
	      end
	    end
	  else
	    chrs.unshift ch
	    if @postproc and @preproc.nil? || @preproc.call(op, chrs)
	      printf "op3: %s\n", op if CONFIG[:DEBUG]
	      @postproc.call(op, chrs)
	      return ""
	    else
	      return nil
	    end
	  end
	end
      end
    end
    
    def initialize
      @head = Node.new("")
    end
    
    def def_rule(token, preproc = nil, postproc = nil)
#      print node.inspect, "\n" if CONFIG[:DEBUG]
      postproc = proc if iterator?
      node = create(token, preproc, postproc)
    end
    
    def def_rules(*tokens)
      if iterator?
	p = proc
      end
      for token in tokens
	def_rule(token, nil, p)
      end
    end
    
    def preporc(token, proc)
      node = search(token)
      node.preproc=proc
    end
    
    def postproc(token)
      node = search(token, proc)
      node.postproc=proc
    end
    
    def search(token)
      @head.search(token.split(//))
    end

    def create(token, preproc = nil, postproc = nil)
      @head.create_subnode(token.split(//), preproc, postproc)
    end
    
    def match(token)
      token = token.split(//) if token.kind_of?(String)
      ret = @head.match(token)
      printf "match end: %s:%s", ret, token.inspect if CONFIG[:DEBUG]
      ret
    end
    
    def inspect
      format("<Trie: @head = %s>", @head.inspect)
    end
  end
  
  if /^-tt(.*)$/ =~ ARGV[0]
#    Tracer.on
    case $1
    when "1"
      tr = Trie.new
      print "0: ", tr.inspect, "\n"
      tr.def_rule("=") {print "=\n"}
      print "1: ", tr.inspect, "\n"
      tr.def_rule("==") {print "==\n"}
      print "2: ", tr.inspect, "\n"
      
      print "case 1:\n"
      print tr.match("="), "\n"
      print "case 2:\n"
      print tr.match("=="), "\n"
      print "case 3:\n"
      print tr.match("=>"), "\n"
      
    when "2"
      tr = Trie.new
      print "0: ", tr.inspect, "\n"
      tr.def_rule("=") {print "=\n"}
      print "1: ", tr.inspect, "\n"
      tr.def_rule("==", proc{FALSE}) {print "==\n"}
      print "2: ", tr.inspect, "\n"
      
      print "case 1:\n"
      print tr.match("="), "\n"
      print "case 2:\n"
      print tr.match("=="), "\n"
      print "case 3:\n"
      print tr.match("=>"), "\n"
    end
    exit
  end
  
  module CONTEXT
    def _=(value)
      CONFIG[:_] = value
      eval "_=BC_APPLICATION__::CONFIG[:_]", CONFIG[:BIND]
    end
    
#    def _
#      eval "_", CONFIG[:BIND]
#    end
    
    def quit
      exit
    end
    
    def trace_load(opt = nil)
      if !opt.nil?
	CONFIG[:TRACE_LOAD] = opt
      else
	CONFIG[:TRACE_LOAD] = !CONFIG[:TRACE_LOAD]
      end
      print "Switch to load/require #{unless CONFIG[:TRACE_LOAD]; ' non';end} trace mode.\n"
      if CONFIG[:TRACE_LOAD]
	eval %{
	  class << self
	    alias load rbc_load
	    alias require rbc_require
	  end
	}
      else
	eval %{
	  class << self
	    alias load rbc_load_org
	    alias require rbc_require_org
	  end
	}
      end
      CONFIG[:TRACE_LOAD]
    end
    
    alias rbc_load_org load
    def rbc_load(file_name)
      return true if load_sub(file_name)
      raise LoadError, "No such file to load -- #{file_name}"
    end

    alias rbc_require_org require
    def rbc_require(file_name)
      rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
      return false if $".find{|f| f =~ rex}

      case file_name
      when /\.rb$/
	if load_sub(file_name)
	  $".push file_name
	  return true
	end
      when /\.(so|o|sl)$/
	rbc_require_org(file_name)
      end
      
      if load_sub(f = file_name + ".rb")
	$".push f
	return true
      end
      rbc_require_org(file_name)
    end

    def load_sub(fn)
      if fn =~ /^#{Regexp.quote(File::Separator)}/
	return false unless File.exist?(fn)
	BC.new.eval_input FileInputMethod.new(fn), self, CONFIG[:BIND]
	return true
      end
      
      for path in $:
	if File.exist?(f = File.join(path, fn))
	  BC.new.eval_input FileInputMethod.new(f), self, CONFIG[:BIND]
	  return true
	end
      end
      return false
    end

    def inspect_mode(opt = nil)
      if opt
	CONFIG[:INSPECT] = opt
      else
	CONFIG[:INSPECT] = !CONFIG[:INSPECT]
      end
      print "Switch to#{unless CONFIG[:INSPECT]; ' non';end} inspect mode.\n"
      CONFIG[:INSPECT]
    end
    
    def run(bind)
      CONFIG[:BIND] = bind

      if CONFIG[:RC]
	rc = File.expand_path("~/.irbrc")
	if File.exists?(rc)
	  begin
	    load rc
	  rescue
	    print "load error: #{rc}\n"
	    print $!.type, ": ", $!, "\n"
	    for err in $@[0, $@.size - 2]
	      print "\t", err, "\n"
	    end
	  end 
	end
      end
  
      if CONFIG[:TRACE_LOAD]
	trace_load true
      end
  
      for m in CONFIG[:LOAD_MODULES]
	begin
	  require m
	rescue
	  print $@[0], ":", $!.type, ": ", $!, "\n"
	end
      end

      if !$0.equal?(CONFIG[0])
	io = FileInputMethod.new($0)
      elsif defined? Readline
	io = ReadlineInputMethod.new
      else
	io = StdioInputMethod.new
      end

      BC.new.eval_input io, self, CONFIG[:BIND]
    end
  end
  
  class InputMethod
    attr :prompt, TRUE
    
    def gets
    end
    public :gets
  end
  
  class StdioInputMethod < InputMethod
    def gets
      print @prompt
      $stdin.gets
    end
  end
  
  class FileInputMethod < InputMethod
    def initialize(file)
      @io = open(file)
    end

    def gets
      l = @io.gets
      print @prompt, l
      l
    end
  end

  if CONFIG[:USE_READLINE]
    begin
      require "readline"
      print "use readline module\n"
      class ReadlineInputMethod < InputMethod
	include Readline 
	def gets
	  if l = readline(@prompt, TRUE)
	    l + "\n"
	  else
	    l
	  end
	end
      end
    rescue
      CONFIG[:USE_READLINE] = FALSE
    end
  end
end

extend BC_APPLICATION__::CONTEXT
run(binding)

