class Pione::Lang::DelegatableTable

DelegatableTable is a value table identified by two keys(package id and name).

Public Class Methods

new(parent, table=Hash.new {|h, k| h[k] = Hash.new}) click to toggle source
# File lib/pione/lang/environment.rb, line 5
def initialize(parent, table=Hash.new {|h, k| h[k] = Hash.new})
  @parent = parent # parent delegatable table
  @table = table   # 2d table
end

Public Instance Methods

bound?(package_id, name) click to toggle source

Return true if the name with the package id is bound.

# File lib/pione/lang/environment.rb, line 11
def bound?(package_id, name)
  (@table.has_key?(package_id) and @table[package_id][name]) || (@parent ? @parent.bound?(package_id, name) : false)
end
dumpable() click to toggle source
# File lib/pione/lang/environment.rb, line 119
def dumpable
  parent = @parent ? @parent.dumpable : nil
  table = Hash[*@table.to_a.flatten]
  self.class.new(parent, table)
end
get(env, ref) click to toggle source

Find value of the reference recursively. We will raise CircularReferenceError if the reference is circular.

# File lib/pione/lang/environment.rb, line 17
def get(env, ref)
  history = [ref.package_id, ref.name]

  # detect reference loop
  if env.reference_history.include?(history)
    raise CircularReferenceError.new(ref)
  end

  # push package id and name to history
  _env = env.set(reference_history: env.reference_history + [history])

  # get the expression and evaluate it
  if expr = get_value(env, ref)
    evaluate_value(_env, expr)
  else
    raise UnboundError.new(ref)
  end
end
get!(env, ref) click to toggle source
# File lib/pione/lang/environment.rb, line 36
def get!(env, ref)
  get(env, ref)
rescue UnboundError
  nil
end
get_value(env, ref) click to toggle source

Get the value expression corresponding to the reference in the table. This method is not circular.

# File lib/pione/lang/environment.rb, line 44
def get_value(env, ref)
  unless ref.package_id
    raise ArgumentError.new("package id is invalid: %s" % ref.inspect)
  end

  # when it is known reference
  if expr = @table[ref.package_id][ref.name]
    return expr
  end

  if bound?(ref.package_id, ref.name)
    # get value from parent table
    return @parent.get_value(env, ref) if @parent
  else
    # otherwise, find by parent package id
    env.find_ancestor_ids(ref.package_id).each do |ancestor_id|
      if val = get_value(env, ref.set(package_id: ancestor_id))
        return val
      end
    end
  end

  return nil
end
inspect() click to toggle source
# File lib/pione/lang/environment.rb, line 111
def inspect
  if @parent
    "#%s(%s,%s)" % [self.class.name.split("::").last, @table, @parent.inspect]
  else
    "#%s(%s)" % [self.class.name.split("::").last, @table]
  end
end
keys() click to toggle source

Return all reference in the table and the parent.

# File lib/pione/lang/environment.rb, line 86
def keys
  @table.keys.inject(@parent ? @parent.keys : []) do |res, k1|
    @table[k1].keys.inject(res) do |_res, k2|
      ref = make_reference(k1, k2)
      _res.include?(ref) ? _res : res << ref
    end
  end
end
select_names_by(env, package_id) click to toggle source

Return all names that related to the package ID and ancestors.

@return [Array<String>]

all names in the table
# File lib/pione/lang/environment.rb, line 99
def select_names_by(env, package_id)
  names = @parent ? @parent.select_names_by(package_id) : []
  target_ids = [package_id, *env.find_ancestor_ids(package_id)]
  target_ids.each_with_object(names) do |target_id, names|
    @table[target_id].keys.each do |name|
      if not(names.include?(name))
        names << name
      end
    end
  end
end
set(ref, val) click to toggle source

Update table with the name and value. We will raise RebindError if the reference is bound already.

# File lib/pione/lang/environment.rb, line 71
def set(ref, val)
  unless bound?(ref.package_id, ref.name)
    set!(ref, val)
  else
    raise RebindError.new(ref)
  end
end
set!(ref, val) click to toggle source

Update the table with the name and value. This method permits to overwrite the value, so you can ignore RebindError.

# File lib/pione/lang/environment.rb, line 81
def set!(ref, val)
  @table[ref.package_id][ref.name] = val
end