Class: Pione::Lang::DelegatableTable

Inherits:
Object
  • Object
show all
Defined in:
lib/pione/lang/environment.rb

Overview

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

Direct Known Subclasses

RuleTable, VariableTable

Instance Method Summary collapse

Constructor Details

#initialize(parent, table = Hash.new {|h, k| h[k] = Hash.new}) ⇒ DelegatableTable

Returns a new instance of DelegatableTable.



5
6
7
8
# 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

Instance Method Details

#bound?(package_id, name) ⇒ Boolean

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

Returns:

  • (Boolean)


11
12
13
# 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

#dumpableObject



119
120
121
122
123
# 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) ⇒ Object

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



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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) ⇒ Object



36
37
38
39
40
# File 'lib/pione/lang/environment.rb', line 36

def get!(env, ref)
  get(env, ref)
rescue UnboundError
  nil
end

#get_value(env, ref) ⇒ Object

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



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 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

#inspectObject



111
112
113
114
115
116
117
# 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

#keysObject

Return all reference in the table and the parent.



86
87
88
89
90
91
92
93
# 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) ⇒ Array<String>

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

Returns:

  • (Array<String>)

    all names in the table



99
100
101
102
103
104
105
106
107
108
109
# 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) ⇒ Object

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



71
72
73
74
75
76
77
# 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) ⇒ Object

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



81
82
83
# File 'lib/pione/lang/environment.rb', line 81

def set!(ref, val)
  @table[ref.package_id][ref.name] = val
end