mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 11:34:29 -07:00
116 lines
2.9 KiB
Ruby
116 lines
2.9 KiB
Ruby
class Binding
|
||
attr_accessor :variables
|
||
attr_accessor :compiled_code
|
||
attr_accessor :lexical_scope
|
||
attr_accessor :proc_environment
|
||
attr_accessor :location
|
||
attr_accessor :receiver
|
||
|
||
def from_proc?
|
||
@proc_environment
|
||
end
|
||
|
||
def self.self_context(recv, variables)
|
||
Rubinius::Type.object_equal(recv, Kernel) ? recv : variables.self
|
||
end
|
||
private :self_context
|
||
|
||
# Create a new Binding object. MRI does not allow .new to be called, so
|
||
# we used .setup(). Any code can use this as they wish, provided they have
|
||
# all the right arguments.
|
||
#
|
||
# +variables+ is a Rubinius::VariableScope object
|
||
# +code+ is a Rubinius::CompiledCode object
|
||
# +lexical_scope+ is a Rubinius::LexicalScope object
|
||
#
|
||
# See Kernel#binding in core/eval.rb for a simple example of creating a
|
||
# Binding object.
|
||
#
|
||
def self.setup(variables, code, lexical_scope, recv=nil, location=nil)
|
||
bind = allocate()
|
||
|
||
bind.receiver = self_context(recv, variables)
|
||
bind.variables = variables
|
||
bind.compiled_code = code
|
||
bind.lexical_scope = lexical_scope
|
||
bind.location = location
|
||
|
||
return bind
|
||
end
|
||
|
||
def line_number
|
||
if proc_environment
|
||
proc_environment.line
|
||
else
|
||
location ? location.line : 1
|
||
end
|
||
end
|
||
|
||
# Evaluates the Ruby expression(s) in string, in the binding‘s context.
|
||
# If the optional filename and lineno parameters are present,
|
||
# they will be used when reporting syntax errors.
|
||
def eval(expr, filename=nil, lineno=nil)
|
||
lineno ||= filename ? 1 : line_number
|
||
|
||
Kernel.eval(expr, self, filename, lineno)
|
||
end
|
||
|
||
def local_variables
|
||
variables.local_variables
|
||
end
|
||
|
||
def local_variable_set(name, value)
|
||
unless name.is_a?(Symbol)
|
||
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||
end
|
||
|
||
vars = variables
|
||
|
||
# If a local variable is defined in a parent scope we should update the
|
||
# variable in said scope and all child scopes, instead of _only_ setting it
|
||
# in the current scope.
|
||
while vars
|
||
meth = vars.method
|
||
|
||
if meth.local_names.include?(name)
|
||
return vars.set_local(meth.local_slot(name), value)
|
||
elsif vars.eval_local_defined?(name)
|
||
return vars.set_eval_local(name, value)
|
||
end
|
||
|
||
vars = vars.parent
|
||
end
|
||
|
||
variables.set_eval_local(name, value)
|
||
end
|
||
|
||
def local_variable_get(name)
|
||
unless name.is_a?(Symbol)
|
||
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||
end
|
||
|
||
vars = variables
|
||
|
||
while vars
|
||
meth = vars.method
|
||
|
||
if meth.local_names.include?(name)
|
||
return vars.locals[meth.local_slot(name)]
|
||
elsif vars.eval_local_defined?(name)
|
||
return vars.get_eval_local(name)
|
||
end
|
||
|
||
vars = vars.parent
|
||
end
|
||
|
||
raise NameErrror, "local variable #{name.inspect} not defined for #{inspect}"
|
||
end
|
||
|
||
def local_variable_defined?(name)
|
||
unless name.is_a?(Symbol)
|
||
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||
end
|
||
|
||
variables.local_defined?(name)
|
||
end
|
||
end
|