#
# environ.rb
#
#   Copyright (c) 1999 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
#

require 'amstd/to_s'


class Environment

  def initialize( name, parent, stack )
    @name   = name.dup
    @parent = parent
    @stack  = stack

    @values   = {}
    @parent   = nil
    @children = []
    do_initialize
  end

  attr :name
  attr :parent
  attr :children

  def warn( mes )
    $stderr.print 'Warning: '
    $stderr.puts mes
  end


  def set( name, val )
    targ, nm = gettarg( name )
    targ.in_set nm, val
  end

  alias s set

  def get( name )
    targ, nm = gettarg( name )
    ret = targ.in_get( nm )
    unless ret then
      warn "#{_name2str(name)} is nil"
    end

    ret
  end

  alias g get

  def g?( name )
    targ, nm = gettarg( name )
    ret = targ.in_get( nm )
    ret ? true : false
  end

  def get_env( name )
    @stack[ name ]
  end

  alias e get_env


  private

  def do_initialize
  end

  def gettarg( nm )
    name = _name2str( nm )
    a = name.split('.')
    if a.size > 1 then
p a
      unless env = @stack[ a[0] ] then
        raise ArgumentError, "non exist environment '#{env}'"
      end
      a[0] = env
    else
      a.unshift self
    end
    a
  end


  protected

  def in_set( n, v )
    @values[n] = v
  end

  def in_get( n )
    @values[n]
  end

end


class EnvironmentError < StandardError ; end

class EnvironmentStack

  def initialize
    @vals = {}
    @current = nil
    @prev = nil
    @stack = []
  end

  class << self
    def item_type( arg = nil )
      if arg then
        @item_type = arg
      else
        @item_type
      end
    end
  end

  item_type Environment

  attr :current
  attr :prev

  def []( name )
    @vals[ _name2str( name ) ]
  end

  def enter( name )
    name = _name2str( name )
    if e = @vals[ name ] then
      raise EnvironmentError, "environment name '#{name}' used twice"
    end

    paren = @current
    @vals[ name ] = @current = type.item_type.new( name, paren, self )
    if paren then
      paren.children.push @current
    end
    @stack.push @current

    @current
  end

  def leave( name = nil )
    unless e = @stack.pop then
      raise EnvironmentError, "too many leave than enter"
    end
    name = _name2str( name ) if name
    if name and e.name != name then
      puts "warning: leave from #{e.name}, but #{e.name} is required"
    end
    @prev = @current
    @current = @stack[-1]

    e
  end

  def environ( name, &block )
    e = enter( name )
    e.instance_eval &block
    leave e.name
  end

end
