Class: Q_Environment
- Inherits:
-
Object
- Object
- Q_Environment
- Defined in:
- lib/q-language/environment.rb
Overview
Copyright © 2010-2011 Jesse Sielaff
Constant Summary collapse
- TooManyNodes =
Class.new(Exception)
Instance Attribute Summary collapse
-
#discarded_objects ⇒ Object
readonly
Returns the value of attribute discarded_objects.
-
#failed_methods ⇒ Object
readonly
Returns the value of attribute failed_methods.
-
#max_array_length ⇒ Object
Returns the value of attribute max_array_length.
-
#max_hash_length ⇒ Object
Returns the value of attribute max_hash_length.
-
#max_method_nodes ⇒ Object
Returns the value of attribute max_method_nodes.
-
#max_nodes ⇒ Object
Returns the value of attribute max_nodes.
-
#max_string_length ⇒ Object
Returns the value of attribute max_string_length.
-
#nodes_evaluated ⇒ Object
readonly
Returns the value of attribute nodes_evaluated.
-
#successful_methods ⇒ Object
readonly
Returns the value of attribute successful_methods.
Instance Method Summary collapse
-
#arg_search_block(q_class, indices) ⇒ Object
Returns a Proc used for iterating over objects in the queue while looking for QObject method arguments.
-
#args?(method_args_hash) ⇒ Boolean
Stores the queue indices of the arguments required by the given method_args_hash in @arg_indices, then returns true.
-
#block_arg(block_node) ⇒ Object
Returns a Proc object to be used when calling QObject methods with required block arguments.
-
#break! ⇒ Object
Sets a jump flag in the current scope and a break flag in the previous scope.
-
#break? ⇒ Boolean
Returns true if a break flag is set in the current scope, nil otherwise.
-
#evaluate(node) ⇒ Object
Evaluates the Node.
-
#frame(object, &block) ⇒ Object
Returns the result of calling the given block with the given object as the value of self.
-
#get(name) ⇒ Object
• User method Returns the object associated with the given variable name, or nil if there is no such object.
-
#initialize(variables = {}, *implicit, object, options) ⇒ Q_Environment
constructor
A new instance of Q_Environment.
-
#jump! ⇒ Object
Sets a jump flag in the current scope.
-
#last_assigned_variable ⇒ Object
Returns the name of the last variable used in the current scope that was associated with an object, or nil if no such variable exists.
-
#method?(block_node = nil) ⇒ Boolean
Pushes the result of calling the top method in the pending methods stack using the combination of sufficient receiver and arguments found frontmost in the queue into @method_results, then returns true.
-
#method_pop ⇒ Object
Pops the top method name from the pending method stack, and increments that method’s success count by 1.
-
#method_push(name) ⇒ Object
If the given method succeeds, calls queue_push with the result; otherwise, adds the given method name to the top of the pending method stack.
-
#object ⇒ Object
• User method Returns the outermost frame object for this Q_Environment.
-
#q_send(q_receiver, block_node) ⇒ Object
Returns the sanitized result of calling the current method with the given q_receiver, the given block_node as its block arg, and the objects in the queue at the indices currently stored in @arg_indices as its arguments.
-
#queue_push(object) ⇒ Object
Adds the given object to the end of the queue.
-
#replicate ⇒ Object
Returns a new Q_Environment referencing the same variables, object, and implicit receivers as this Q_Environment, but with new runtime statistics, for use in executing methods outside the context of the original Q_Device.
-
#return_value ⇒ Object
• User method Returns the return value of the outermost block.
-
#sanitize(obj) ⇒ Object
If the given object is an Array, Hash, or String, returns the object shortened to the maximum length; otherwise, returns the object unchanged.
-
#self ⇒ Object
Returns the value of self for the current frame.
-
#set(name, object) ⇒ Object
• User method Associates the given variable name with the given object, then returns the object.
-
#unset(name) ⇒ Object
Unsets the named variable, then returns the object previously associated with the variable.
-
#variable_push(variable) ⇒ Object
If there is an object already associated with the given variable name, marks that variable name as the last assigned variable, and then calls queue_push with that object.
Constructor Details
#initialize(variables = {}, *implicit, object, options) ⇒ Q_Environment
Returns a new instance of Q_Environment.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/q-language/environment.rb', line 7 def initialize (variables = {}, *implicit, object, ) @successful_methods = Hash.new {|h,k| h[k] = 0 } @failed_methods = Hash.new {|h,k| h[k] = 0 } @discarded_objects = Hash.new {|h,k| h[k] = 0 } @nodes_evaluated = Hash.new {|h,k| h[k] = 0 } @max_nodes = [0] @max_method_nodes = [1] @max_array_length = [2] @max_hash_length = [3] @max_string_length = [4] @variables = variables.to_hash @implicit = implicit @frame_stack = [object] @scope = { queue: [], method_stack: [], unassigned_variables: [], prev: nil } @method_results = [] end |
Instance Attribute Details
#discarded_objects ⇒ Object (readonly)
Returns the value of attribute discarded_objects.
32 33 34 |
# File 'lib/q-language/environment.rb', line 32 def discarded_objects @discarded_objects end |
#failed_methods ⇒ Object (readonly)
Returns the value of attribute failed_methods.
32 33 34 |
# File 'lib/q-language/environment.rb', line 32 def failed_methods @failed_methods end |
#max_array_length ⇒ Object
Returns the value of attribute max_array_length.
31 32 33 |
# File 'lib/q-language/environment.rb', line 31 def max_array_length @max_array_length end |
#max_hash_length ⇒ Object
Returns the value of attribute max_hash_length.
31 32 33 |
# File 'lib/q-language/environment.rb', line 31 def max_hash_length @max_hash_length end |
#max_method_nodes ⇒ Object
Returns the value of attribute max_method_nodes.
30 31 32 |
# File 'lib/q-language/environment.rb', line 30 def max_method_nodes @max_method_nodes end |
#max_nodes ⇒ Object
Returns the value of attribute max_nodes.
30 31 32 |
# File 'lib/q-language/environment.rb', line 30 def max_nodes @max_nodes end |
#max_string_length ⇒ Object
Returns the value of attribute max_string_length.
31 32 33 |
# File 'lib/q-language/environment.rb', line 31 def max_string_length @max_string_length end |
#nodes_evaluated ⇒ Object (readonly)
Returns the value of attribute nodes_evaluated.
32 33 34 |
# File 'lib/q-language/environment.rb', line 32 def nodes_evaluated @nodes_evaluated end |
#successful_methods ⇒ Object (readonly)
Returns the value of attribute successful_methods.
32 33 34 |
# File 'lib/q-language/environment.rb', line 32 def successful_methods @successful_methods end |
Instance Method Details
#arg_search_block(q_class, indices) ⇒ Object
Returns a Proc used for iterating over objects in the queue while looking for QObject method arguments. The returned Proc uses two parameters: an object from the queue, and that object’s index in the queue. If the object parameter is an instance of the given QObject class and has not yet been used as the method receiver or as a method argument, the Proc adds the index parameter to the given indices Array and marks the object’s index in the queue as used.
42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/q-language/environment.rb', line 42 def arg_search_block (q_class, indices) proc do |object, i| next if (i == @receiver_index) or @arg_indices.include?(i) if object.to_q.is_a?(q_class) @arg_indices.push(i) indices.push(i) true end end end |
#args?(method_args_hash) ⇒ Boolean
Stores the queue indices of the arguments required by the given method_args_hash in @arg_indices, then returns true. If the queue does not contain sufficient objects to fulfill the method argument requirements, returns false.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/q-language/environment.rb', line 59 def args? (method_args_hash) left_indices = [] splat_indices = [] right_indices = [] queue = @scope[:queue] return false unless method_args_hash[:reqs_left].all? do |q_class| queue.each_with_index.any? &arg_search_block(q_class, left_indices) end return false unless method_args_hash[:reqs_right].all? do |q_class| queue.each_with_index.reverse_each.any? &arg_search_block(q_class, right_indices) end if q_class = method_args_hash[:splat] queue.each_with_index &arg_search_block(q_class, splat_indices) end @arg_indices = left_indices + splat_indices + right_indices true end |
#block_arg(block_node) ⇒ Object
Returns a Proc object to be used when calling QObject methods with required block arguments. The returned Proc uses two optional parameters: the first is a single argument object (default is nil) that will be associated with the last unassigned variable name appearing before the block in the script; the second is the Q_Environment in which to evaluate the block Node embedded in the Proc object (default is the Q_Environment that created the Proc).
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/q-language/environment.rb', line 88 def block_arg (block_node) return unless block_node parameter_name = @scope[:unassigned_variables].pop lambda do |parameter_object = nil, env = self| env.instance_eval do if has_old_value = @variables.has_key?(parameter_name) old_value = @variables[parameter_name] end set(parameter_name, parameter_object) b, j = @scope[:break], @scope[:jump] @scope = { queue: [], method_stack: [], unassigned_variables: [], prev: @scope } return_value = evaluate(block_node) b = @scope[:break] || b j = @scope[:jump] || j @scope = @scope[:prev] @scope[:break] = b @scope[:jump] = j has_old_value ? set(parameter_name, old_value) : unset(parameter_name) return_value end end end |
#break! ⇒ Object
Sets a jump flag in the current scope and a break flag in the previous scope.
130 131 132 133 |
# File 'lib/q-language/environment.rb', line 130 def break! jump! @scope[:prev][:break] = true end |
#break? ⇒ Boolean
Returns true if a break flag is set in the current scope, nil otherwise.
123 124 125 |
# File 'lib/q-language/environment.rb', line 123 def break? @scope[:break] end |
#evaluate(node) ⇒ Object
Evaluates the Node. For a literal Node, calls queue_push with the literal object. For a method Node, calls method_push with the method name. For a variable node, calls variable_push with the variable name. For a block Node, first tries to run any pending method that would succeed with a block argument; if no method succeeds, adds a new scope level and evaluates each Node within the block in that scope. Returns the result of calling queue_push with either the method result or with the the frontmost object from the nested scope’s queue.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/q-language/environment.rb', line 144 def evaluate (node) raise TooManyNodes if @nodes_evaluated[:total] >= @max_nodes @nodes_evaluated[node.node_type] += 1 @nodes_evaluated[:total] += 1 case node.node_type when :literal then queue_push(node.value) when :method then method_push(node.value) when :variable then variable_push(node.value) when :block if method?(node) return queue_push(@method_results.pop) end @scope = { queue: [], method_stack: [], unassigned_variables: [], prev: @scope } node.value.each do |node| evaluate node break if @scope[:jump] end return_value = @scope[:queue].shift @scope[:method_stack].each {|name| @failed_methods[name] += 1 } @scope[:queue].each {|obj| @discarded_objects[obj.class] += 1 } @scope = @scope[:prev] return queue_push(return_value) end end |
#frame(object, &block) ⇒ Object
Returns the result of calling the given block with the given object as the value of self.
179 180 181 182 183 184 |
# File 'lib/q-language/environment.rb', line 179 def frame (object, &block) @frame_stack.push(object) result = yield @frame_stack.pop result end |
#get(name) ⇒ Object
• User method Returns the object associated with the given variable name, or nil if there is no such object.
190 191 192 |
# File 'lib/q-language/environment.rb', line 190 def get (name) @variables[name] end |
#jump! ⇒ Object
Sets a jump flag in the current scope.
196 197 198 |
# File 'lib/q-language/environment.rb', line 196 def jump! @scope[:jump] = true end |
#last_assigned_variable ⇒ Object
Returns the name of the last variable used in the current scope that was associated with an object, or nil if no such variable exists.
203 204 205 |
# File 'lib/q-language/environment.rb', line 203 def last_assigned_variable @scope[:last_assigned_variable] end |
#method?(block_node = nil) ⇒ Boolean
Pushes the result of calling the top method in the pending methods stack using the combination of sufficient receiver and arguments found frontmost in the queue into @method_results, then returns true. If no receiver or insufficient arguments are found in the queue, returns false. If a block_node argument is given, attempts to call QObject methods requiring a block argument.
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/q-language/environment.rb', line 214 def method? (block_node = nil) return false unless name = @scope[:method_stack].last q_receiver = nil potential_receivers = @scope[:queue] + @implicit + [QImplicit.new(@scope[:queue])] potential_receivers.each_with_index do |potential_receiver, receiver_i| @receiver_index = receiver_i q_potential_receiver = potential_receiver.to_q next unless q_potential_receiver.respond_to?(name) method_args_hash = q_potential_receiver.method(name).owner::MethodArguments[name] next if method_args_hash[:block] && !block_node @arg_indices = [] if args?(method_args_hash) q_receiver = q_potential_receiver break end end return false unless q_receiver @method_results << q_send(q_receiver, block_node) true end |
#method_pop ⇒ Object
Pops the top method name from the pending method stack, and increments that method’s success count by 1.
248 249 250 251 252 |
# File 'lib/q-language/environment.rb', line 248 def method_pop name = @scope[:method_stack].pop @successful_methods[name] += 1 name end |
#method_push(name) ⇒ Object
If the given method succeeds, calls queue_push with the result; otherwise, adds the given method name to the top of the pending method stack.
257 258 259 260 |
# File 'lib/q-language/environment.rb', line 257 def method_push (name) @scope[:method_stack].push(name) queue_push(@method_results.pop) if method? end |
#object ⇒ Object
• User method Returns the outermost frame object for this Q_Environment.
265 266 267 |
# File 'lib/q-language/environment.rb', line 265 def object @frame_stack.first end |
#q_send(q_receiver, block_node) ⇒ Object
Returns the sanitized result of calling the current method with the given q_receiver, the given block_node as its block arg, and the objects in the queue at the indices currently stored in @arg_indices as its arguments.
273 274 275 276 277 278 279 280 |
# File 'lib/q-language/environment.rb', line 273 def q_send (q_receiver, block_node) arg_objects = @arg_indices.map {|i| @scope[:queue][i] } indices = @arg_indices + [@receiver_index] @scope[:queue].reject!.each_with_index {|x,i| indices.include? i } q_receiver.instance_variable_set(:@__environment__, self) sanitize(q_receiver.__send__(method_pop, *arg_objects, &block_arg(block_node))) end |
#queue_push(object) ⇒ Object
Adds the given object to the end of the queue. Associates all unassigned variables with the object, then tests whether any pending methods succeed with the new object in the queue. If so, calls queue_push again with the result of the successful method; otherwise, returns the given object.
287 288 289 290 291 292 |
# File 'lib/q-language/environment.rb', line 287 def queue_push (object) @scope[:queue] << object @scope[:unassigned_variables].each {|v| set(v, object) }.clear method? ? queue_push(@method_results.pop) : object end |
#replicate ⇒ Object
Returns a new Q_Environment referencing the same variables, object, and implicit receivers as this Q_Environment, but with new runtime statistics, for use in executing methods outside the context of the original Q_Device.
298 299 300 301 |
# File 'lib/q-language/environment.rb', line 298 def replicate = [@max_nodes, @max_method_nodes, @max_array_length, @max_hash_length, @max_string_length] Q_Environment.new(@variables, *@implicit, @object, ) end |
#return_value ⇒ Object
• User method Returns the return value of the outermost block.
306 307 308 |
# File 'lib/q-language/environment.rb', line 306 def return_value @scope[:queue].first end |
#sanitize(obj) ⇒ Object
If the given object is an Array, Hash, or String, returns the object shortened to the maximum length; otherwise, returns the object unchanged.
313 314 315 316 317 318 319 320 321 |
# File 'lib/q-language/environment.rb', line 313 def sanitize (obj) case obj when Array then obj.slice!(0...max_array_length) if obj.length > @max_array_length when Hash then obj.pop while obj.length > @max_hash_length when String then obj.slice!(0...max_string_length) if obj.length > @max_string_length end obj end |
#self ⇒ Object
Returns the value of self for the current frame.
325 326 327 |
# File 'lib/q-language/environment.rb', line 325 def self @frame_stack.last end |
#set(name, object) ⇒ Object
• User method Associates the given variable name with the given object, then returns the object.
333 334 335 336 |
# File 'lib/q-language/environment.rb', line 333 def set (name, object) return unless name @variables[name] = object end |
#unset(name) ⇒ Object
Unsets the named variable, then returns the object previously associated with the variable.
341 342 343 |
# File 'lib/q-language/environment.rb', line 341 def unset (name) @variables.delete(name) end |
#variable_push(variable) ⇒ Object
If there is an object already associated with the given variable name, marks that variable name as the last assigned variable, and then calls queue_push with that object. Otherwise, adds the variable name to the list of unassigned variables.
350 351 352 353 354 355 356 357 |
# File 'lib/q-language/environment.rb', line 350 def variable_push (variable) if object = get(variable) @scope[:last_assigned_variable] = variable queue_push(object) else @scope[:unassigned_variables].push(variable) end end |