q-language 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/Rakefile +10 -0
- data/lib/q-language.rb +9 -0
- data/lib/q-language/device.rb +166 -0
- data/lib/q-language/environment.rb +358 -0
- data/lib/q-language/methods/array.rb +523 -0
- data/lib/q-language/methods/block.rb +26 -0
- data/lib/q-language/methods/class.rb +24 -0
- data/lib/q-language/methods/dynamic.rb +82 -0
- data/lib/q-language/methods/false.rb +47 -0
- data/lib/q-language/methods/hash.rb +351 -0
- data/lib/q-language/methods/implicit.rb +345 -0
- data/lib/q-language/methods/module.rb +39 -0
- data/lib/q-language/methods/nil.rb +47 -0
- data/lib/q-language/methods/number.rb +118 -0
- data/lib/q-language/methods/object.rb +155 -0
- data/lib/q-language/methods/string.rb +157 -0
- data/lib/q-language/methods/time.rb +110 -0
- data/lib/q-language/methods/token.rb +72 -0
- data/lib/q-language/methods/true.rb +14 -0
- data/lib/q-language/node.rb +45 -0
- data/lib/q-language/object.rb +125 -0
- data/lib/q-language/parser.rb +104 -0
- data/lib/q-language/writer.rb +90 -0
- data/test/methods/test_array.rb +191 -0
- data/test/methods/test_block.rb +66 -0
- data/test/methods/test_class.rb +34 -0
- data/test/methods/test_dynamic.rb +158 -0
- data/test/methods/test_false.rb +60 -0
- data/test/methods/test_hash.rb +10 -0
- data/test/methods/test_implicit.rb +332 -0
- data/test/methods/test_module.rb +55 -0
- data/test/methods/test_nil.rb +60 -0
- data/test/methods/test_number.rb +10 -0
- data/test/methods/test_object.rb +157 -0
- data/test/methods/test_string.rb +271 -0
- data/test/methods/test_time.rb +181 -0
- data/test/methods/test_token.rb +92 -0
- data/test/methods/test_true.rb +16 -0
- data/test/test.rb +23 -0
- metadata +103 -0
data/.gemtest
ADDED
File without changes
|
data/Rakefile
ADDED
data/lib/q-language.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright © 2010-2011 Jesse Sielaff
|
4
|
+
#
|
5
|
+
|
6
|
+
class Q_Device
|
7
|
+
def initialize (script)
|
8
|
+
@script = script
|
9
|
+
parse_script_into_nodes
|
10
|
+
|
11
|
+
@max_nodes ||= 3000
|
12
|
+
@max_method_nodes ||= 3000
|
13
|
+
|
14
|
+
@max_array_length ||= 100_000
|
15
|
+
@max_hash_length ||= 100_000
|
16
|
+
@max_string_length ||= 100_000
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :max_nodes, :max_method_nodes
|
20
|
+
attr_accessor :max_array_length, :max_hash_length, :max_string_length
|
21
|
+
attr_reader :script
|
22
|
+
|
23
|
+
# • User method
|
24
|
+
# Removes a Node of the given target type from the Q_Device's tree, rewrites
|
25
|
+
# the Q script based on the new tree, then returns the removed Node. All Nodes
|
26
|
+
# of the given type will be targeted with equal probability. If no target type
|
27
|
+
# is given, Node may be of any type. If no Node of the given type is found,
|
28
|
+
# returns nil.
|
29
|
+
#
|
30
|
+
def delete_node (target_type = :all)
|
31
|
+
parse_script_into_nodes if @nodes_need_reloading
|
32
|
+
|
33
|
+
candidate_nodes = nodes(target_type) - [@tree]
|
34
|
+
return nil unless target_node = candidate_nodes.sample
|
35
|
+
|
36
|
+
target_node.block.nodes.delete(target_node)
|
37
|
+
nodes.delete(target_node)
|
38
|
+
nodes(target_node.node_type).delete(target_node)
|
39
|
+
|
40
|
+
rewrite_script
|
41
|
+
|
42
|
+
target_node
|
43
|
+
end
|
44
|
+
|
45
|
+
# Executes the script in a new Q_Environment with the given variables and
|
46
|
+
# implicit receivers, then returns the Q_Environment.
|
47
|
+
#
|
48
|
+
def execute (variables = {}, *implicit)
|
49
|
+
parse_script_into_nodes if @nodes_need_reloading
|
50
|
+
|
51
|
+
object = Class.new.new
|
52
|
+
object.class.send(:define_method, :to_q) { QDynamic.new(self) }
|
53
|
+
|
54
|
+
begin
|
55
|
+
options = [@max_nodes, @max_method_nodes, @max_array_length, @max_hash_length, @max_string_length]
|
56
|
+
env = Q_Environment.new(variables, *implicit, object, options)
|
57
|
+
env.evaluate(@tree)
|
58
|
+
rescue Q_Environment::TooManyNodes
|
59
|
+
end
|
60
|
+
|
61
|
+
env
|
62
|
+
end
|
63
|
+
|
64
|
+
# • User method
|
65
|
+
# Returns a Node of the given type. All Nodes may be chosen with equal
|
66
|
+
# probability. If no node of the given is found, returns nil.
|
67
|
+
#
|
68
|
+
def get_node (target_type = :all)
|
69
|
+
parse_script_into_nodes if @nodes_need_reloading
|
70
|
+
nodes(target_type).sample
|
71
|
+
end
|
72
|
+
|
73
|
+
# • User method
|
74
|
+
# Inserts the given Node into a random position in a random block in the tree,
|
75
|
+
# rewrites the Q script based on the new tree, then returns the Node. All
|
76
|
+
# blocks in the tree will be targeted with equal probability.
|
77
|
+
#
|
78
|
+
def insert_node (new_node)
|
79
|
+
parse_script_into_nodes if @nodes_need_reloading
|
80
|
+
|
81
|
+
if new_node.is_a? String
|
82
|
+
new_node = Q_Parser.new(new_node).parse.first
|
83
|
+
end
|
84
|
+
|
85
|
+
nodes_in_target_block = nodes(:block).sample.nodes
|
86
|
+
node_position = rand(nodes_in_target_block.length + 1)
|
87
|
+
|
88
|
+
nodes_in_target_block.insert(node_position, new_node)
|
89
|
+
nodes.push(new_node)
|
90
|
+
nodes(new_node.node_type).push(new_node)
|
91
|
+
|
92
|
+
rewrite_script
|
93
|
+
|
94
|
+
new_node
|
95
|
+
end
|
96
|
+
|
97
|
+
# • User method
|
98
|
+
# Executes the given block, rewrites the Q script to reflect any changes to
|
99
|
+
# the tree, then returns the result of the block.
|
100
|
+
#
|
101
|
+
def modify
|
102
|
+
parse_script_into_nodes if @nodes_need_reloading
|
103
|
+
|
104
|
+
result = yield
|
105
|
+
|
106
|
+
rewrite_script
|
107
|
+
@nodes_need_reloading = true
|
108
|
+
|
109
|
+
result
|
110
|
+
end
|
111
|
+
|
112
|
+
# • User method
|
113
|
+
# Returns the Array containing all Nodes of the given type.
|
114
|
+
#
|
115
|
+
def nodes (type = :all)
|
116
|
+
parse_script_into_nodes if @nodes_need_reloading
|
117
|
+
instance_variable_get :"@#{type}_nodes"
|
118
|
+
end
|
119
|
+
|
120
|
+
# Parses the Q script, then stores the resulting tree and all its Nodes.
|
121
|
+
#
|
122
|
+
def parse_script_into_nodes
|
123
|
+
@tree, @block_nodes, @literal_nodes, @method_nodes, @variable_nodes = Q_Parser.new(@script).parse
|
124
|
+
@all_nodes = @block_nodes + @literal_nodes + @method_nodes + @variable_nodes
|
125
|
+
@nodes_need_reloading = false
|
126
|
+
end
|
127
|
+
|
128
|
+
# • User method
|
129
|
+
# Replaces a Node of the given target type from the Q_Device's tree with the
|
130
|
+
# given Node, rewrites the Q script based on the new tree, then returns the
|
131
|
+
# removed Node. All Nodes of the given type will be targeted with equal
|
132
|
+
# probability. If no target type is given, removed Node may be of any type. If
|
133
|
+
# no target Node of the given type is found, returns nil.
|
134
|
+
#
|
135
|
+
def replace_node (new_node, target_type = :all)
|
136
|
+
parse_script_into_nodes if @nodes_need_reloading
|
137
|
+
|
138
|
+
candidate_nodes = nodes(target_type) - [@tree]
|
139
|
+
return nil unless target_node = candidate_nodes.sample
|
140
|
+
|
141
|
+
if new_node.is_a? String
|
142
|
+
new_node = Q_Parser.new(new_node).parse.first
|
143
|
+
end
|
144
|
+
|
145
|
+
nodes_in_parent_block = target_node.block.nodes
|
146
|
+
node_position = nodes_in_parent_block.index(target_node)
|
147
|
+
|
148
|
+
nodes_in_parent_block[node_position] = new_node
|
149
|
+
|
150
|
+
nodes.delete(target_node)
|
151
|
+
nodes.push(new_node)
|
152
|
+
|
153
|
+
nodes(target_node.node_type).delete(target_node)
|
154
|
+
nodes(new_node.node_type).push(new_node)
|
155
|
+
|
156
|
+
rewrite_script
|
157
|
+
|
158
|
+
target_node
|
159
|
+
end
|
160
|
+
|
161
|
+
# Converts the tree into script form, then stores the script.
|
162
|
+
#
|
163
|
+
def rewrite_script
|
164
|
+
@script = @tree.to_script
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,358 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright © 2010-2011 Jesse Sielaff
|
4
|
+
#
|
5
|
+
|
6
|
+
class Q_Environment
|
7
|
+
def initialize (variables = {}, *implicit, object, options)
|
8
|
+
@successful_methods = Hash.new {|h,k| h[k] = 0 }
|
9
|
+
@failed_methods = Hash.new {|h,k| h[k] = 0 }
|
10
|
+
@discarded_objects = Hash.new {|h,k| h[k] = 0 }
|
11
|
+
@nodes_evaluated = Hash.new {|h,k| h[k] = 0 }
|
12
|
+
|
13
|
+
@max_nodes = options[0]
|
14
|
+
@max_method_nodes = options[1]
|
15
|
+
|
16
|
+
@max_array_length = options[2]
|
17
|
+
@max_hash_length = options[3]
|
18
|
+
@max_string_length = options[4]
|
19
|
+
|
20
|
+
@variables = variables.to_hash
|
21
|
+
@implicit = implicit
|
22
|
+
@frame_stack = [object]
|
23
|
+
|
24
|
+
@scope = { queue: [], method_stack: [], unassigned_variables: [], prev: nil }
|
25
|
+
@method_results = []
|
26
|
+
end
|
27
|
+
|
28
|
+
TooManyNodes = Class.new(Exception)
|
29
|
+
|
30
|
+
attr_accessor :max_nodes, :max_method_nodes
|
31
|
+
attr_accessor :max_array_length, :max_hash_length, :max_string_length
|
32
|
+
attr_reader :successful_methods, :failed_methods, :discarded_objects, :nodes_evaluated
|
33
|
+
|
34
|
+
# Returns a Proc used for iterating over objects in the queue while looking
|
35
|
+
# for QObject method arguments. The returned Proc uses two parameters: an
|
36
|
+
# object from the queue, and that object's index in the queue. If the object
|
37
|
+
# parameter is an instance of the given QObject class and has not yet been
|
38
|
+
# used as the method receiver or as a method argument, the Proc adds the index
|
39
|
+
# parameter to the given indices Array and marks the object's index in the
|
40
|
+
# queue as used.
|
41
|
+
#
|
42
|
+
def arg_search_block (q_class, indices)
|
43
|
+
proc do |object, i|
|
44
|
+
next if (i == @receiver_index) or @arg_indices.include?(i)
|
45
|
+
|
46
|
+
if object.to_q.is_a?(q_class)
|
47
|
+
@arg_indices.push(i)
|
48
|
+
indices.push(i)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Stores the queue indices of the arguments required by the given
|
55
|
+
# method_args_hash in @arg_indices, then returns true. If the queue does not
|
56
|
+
# contain sufficient objects to fulfill the method argument requirements,
|
57
|
+
# returns false.
|
58
|
+
#
|
59
|
+
def args? (method_args_hash)
|
60
|
+
left_indices = []
|
61
|
+
splat_indices = []
|
62
|
+
right_indices = []
|
63
|
+
queue = @scope[:queue]
|
64
|
+
|
65
|
+
return false unless method_args_hash[:reqs_left].all? do |q_class|
|
66
|
+
queue.each_with_index.any? &arg_search_block(q_class, left_indices)
|
67
|
+
end
|
68
|
+
|
69
|
+
return false unless method_args_hash[:reqs_right].all? do |q_class|
|
70
|
+
queue.each_with_index.reverse_each.any? &arg_search_block(q_class, right_indices)
|
71
|
+
end
|
72
|
+
|
73
|
+
if q_class = method_args_hash[:splat]
|
74
|
+
queue.each_with_index &arg_search_block(q_class, splat_indices)
|
75
|
+
end
|
76
|
+
|
77
|
+
@arg_indices = left_indices + splat_indices + right_indices
|
78
|
+
true
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns a Proc object to be used when calling QObject methods with required
|
82
|
+
# block arguments. The returned Proc uses two optional parameters: the first
|
83
|
+
# is a single argument object (default is nil) that will be associated with
|
84
|
+
# the last unassigned variable name appearing before the block in the script;
|
85
|
+
# the second is the Q_Environment in which to evaluate the block Node embedded
|
86
|
+
# in the Proc object (default is the Q_Environment that created the Proc).
|
87
|
+
#
|
88
|
+
def block_arg (block_node)
|
89
|
+
return unless block_node
|
90
|
+
|
91
|
+
parameter_name = @scope[:unassigned_variables].pop
|
92
|
+
|
93
|
+
lambda do |parameter_object = nil, env = self|
|
94
|
+
env.instance_eval do
|
95
|
+
if has_old_value = @variables.has_key?(parameter_name)
|
96
|
+
old_value = @variables[parameter_name]
|
97
|
+
end
|
98
|
+
|
99
|
+
set(parameter_name, parameter_object)
|
100
|
+
|
101
|
+
b, j = @scope[:break], @scope[:jump]
|
102
|
+
|
103
|
+
@scope = { queue: [], method_stack: [], unassigned_variables: [], prev: @scope }
|
104
|
+
|
105
|
+
return_value = evaluate(block_node)
|
106
|
+
b = @scope[:break] || b
|
107
|
+
j = @scope[:jump] || j
|
108
|
+
|
109
|
+
@scope = @scope[:prev]
|
110
|
+
|
111
|
+
@scope[:break] = b
|
112
|
+
@scope[:jump] = j
|
113
|
+
|
114
|
+
has_old_value ? set(parameter_name, old_value) : unset(parameter_name)
|
115
|
+
|
116
|
+
return_value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns true if a break flag is set in the current scope, nil otherwise.
|
122
|
+
#
|
123
|
+
def break?
|
124
|
+
@scope[:break]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Sets a jump flag in the current scope and a break flag in the previous
|
128
|
+
# scope.
|
129
|
+
#
|
130
|
+
def break!
|
131
|
+
jump!
|
132
|
+
@scope[:prev][:break] = true
|
133
|
+
end
|
134
|
+
|
135
|
+
# Evaluates the Node. For a literal Node, calls queue_push with the literal
|
136
|
+
# object. For a method Node, calls method_push with the method name. For a
|
137
|
+
# variable node, calls variable_push with the variable name. For a block Node,
|
138
|
+
# first tries to run any pending method that would succeed with a block
|
139
|
+
# argument; if no method succeeds, adds a new scope level and evaluates each
|
140
|
+
# Node within the block in that scope. Returns the result of calling
|
141
|
+
# queue_push with either the method result or with the the frontmost object
|
142
|
+
# from the nested scope's queue.
|
143
|
+
#
|
144
|
+
def evaluate (node)
|
145
|
+
raise TooManyNodes if @nodes_evaluated[:total] >= @max_nodes
|
146
|
+
|
147
|
+
@nodes_evaluated[node.node_type] += 1
|
148
|
+
@nodes_evaluated[:total] += 1
|
149
|
+
|
150
|
+
case node.node_type
|
151
|
+
when :literal then queue_push(node.value)
|
152
|
+
when :method then method_push(node.value)
|
153
|
+
when :variable then variable_push(node.value)
|
154
|
+
when :block
|
155
|
+
if method?(node)
|
156
|
+
return queue_push(@method_results.pop)
|
157
|
+
end
|
158
|
+
|
159
|
+
@scope = { queue: [], method_stack: [], unassigned_variables: [], prev: @scope }
|
160
|
+
|
161
|
+
node.value.each do |node|
|
162
|
+
evaluate node
|
163
|
+
break if @scope[:jump]
|
164
|
+
end
|
165
|
+
|
166
|
+
return_value = @scope[:queue].shift
|
167
|
+
|
168
|
+
@scope[:method_stack].each {|name| @failed_methods[name] += 1 }
|
169
|
+
@scope[:queue].each {|obj| @discarded_objects[obj.class] += 1 }
|
170
|
+
@scope = @scope[:prev]
|
171
|
+
|
172
|
+
return queue_push(return_value)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Returns the result of calling the given block with the given object as the
|
177
|
+
# value of self.
|
178
|
+
#
|
179
|
+
def frame (object, &block)
|
180
|
+
@frame_stack.push(object)
|
181
|
+
result = yield
|
182
|
+
@frame_stack.pop
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
186
|
+
# • User method
|
187
|
+
# Returns the object associated with the given variable name, or nil if there
|
188
|
+
# is no such object.
|
189
|
+
#
|
190
|
+
def get (name)
|
191
|
+
@variables[name]
|
192
|
+
end
|
193
|
+
|
194
|
+
# Sets a jump flag in the current scope.
|
195
|
+
#
|
196
|
+
def jump!
|
197
|
+
@scope[:jump] = true
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns the name of the last variable used in the current scope that was
|
201
|
+
# associated with an object, or nil if no such variable exists.
|
202
|
+
#
|
203
|
+
def last_assigned_variable
|
204
|
+
@scope[:last_assigned_variable]
|
205
|
+
end
|
206
|
+
|
207
|
+
# Pushes the result of calling the top method in the pending methods stack
|
208
|
+
# using the combination of sufficient receiver and arguments found frontmost
|
209
|
+
# in the queue into @method_results, then returns true. If no receiver or
|
210
|
+
# insufficient arguments are found in the queue, returns false. If a
|
211
|
+
# block_node argument is given, attempts to call QObject methods requiring a
|
212
|
+
# block argument.
|
213
|
+
#
|
214
|
+
def method? (block_node = nil)
|
215
|
+
return false unless name = @scope[:method_stack].last
|
216
|
+
|
217
|
+
q_receiver = nil
|
218
|
+
potential_receivers = @scope[:queue] + @implicit + [QImplicit.new(@scope[:queue])]
|
219
|
+
|
220
|
+
potential_receivers.each_with_index do |potential_receiver, receiver_i|
|
221
|
+
@receiver_index = receiver_i
|
222
|
+
q_potential_receiver = potential_receiver.to_q
|
223
|
+
|
224
|
+
next unless q_potential_receiver.respond_to?(name)
|
225
|
+
|
226
|
+
method_args_hash = q_potential_receiver.method(name).owner::MethodArguments[name]
|
227
|
+
|
228
|
+
next if method_args_hash[:block] && !block_node
|
229
|
+
|
230
|
+
@arg_indices = []
|
231
|
+
|
232
|
+
if args?(method_args_hash)
|
233
|
+
q_receiver = q_potential_receiver
|
234
|
+
break
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
return false unless q_receiver
|
239
|
+
|
240
|
+
@method_results << q_send(q_receiver, block_node)
|
241
|
+
|
242
|
+
true
|
243
|
+
end
|
244
|
+
|
245
|
+
# Pops the top method name from the pending method stack, and increments that
|
246
|
+
# method's success count by 1.
|
247
|
+
#
|
248
|
+
def method_pop
|
249
|
+
name = @scope[:method_stack].pop
|
250
|
+
@successful_methods[name] += 1
|
251
|
+
name
|
252
|
+
end
|
253
|
+
|
254
|
+
# If the given method succeeds, calls queue_push with the result; otherwise,
|
255
|
+
# adds the given method name to the top of the pending method stack.
|
256
|
+
#
|
257
|
+
def method_push (name)
|
258
|
+
@scope[:method_stack].push(name)
|
259
|
+
queue_push(@method_results.pop) if method?
|
260
|
+
end
|
261
|
+
|
262
|
+
# • User method
|
263
|
+
# Returns the outermost frame object for this Q_Environment.
|
264
|
+
#
|
265
|
+
def object
|
266
|
+
@frame_stack.first
|
267
|
+
end
|
268
|
+
|
269
|
+
# Returns the sanitized result of calling the current method with the given
|
270
|
+
# q_receiver, the given block_node as its block arg, and the objects in the
|
271
|
+
# queue at the indices currently stored in @arg_indices as its arguments.
|
272
|
+
#
|
273
|
+
def q_send (q_receiver, block_node)
|
274
|
+
arg_objects = @arg_indices.map {|i| @scope[:queue][i] }
|
275
|
+
indices = @arg_indices + [@receiver_index]
|
276
|
+
@scope[:queue].reject!.each_with_index {|x,i| indices.include? i }
|
277
|
+
|
278
|
+
q_receiver.instance_variable_set(:@__environment__, self)
|
279
|
+
sanitize(q_receiver.__send__(method_pop, *arg_objects, &block_arg(block_node)))
|
280
|
+
end
|
281
|
+
|
282
|
+
# Adds the given object to the end of the queue. Associates all unassigned
|
283
|
+
# variables with the object, then tests whether any pending methods succeed
|
284
|
+
# with the new object in the queue. If so, calls queue_push again with the
|
285
|
+
# result of the successful method; otherwise, returns the given object.
|
286
|
+
#
|
287
|
+
def queue_push (object)
|
288
|
+
@scope[:queue] << object
|
289
|
+
@scope[:unassigned_variables].each {|v| set(v, object) }.clear
|
290
|
+
|
291
|
+
method? ? queue_push(@method_results.pop) : object
|
292
|
+
end
|
293
|
+
|
294
|
+
# Returns a new Q_Environment referencing the same variables, object, and
|
295
|
+
# implicit receivers as this Q_Environment, but with new runtime statistics,
|
296
|
+
# for use in executing methods outside the context of the original Q_Device.
|
297
|
+
#
|
298
|
+
def replicate
|
299
|
+
options = [@max_nodes, @max_method_nodes, @max_array_length, @max_hash_length, @max_string_length]
|
300
|
+
Q_Environment.new(@variables, *@implicit, @object, options)
|
301
|
+
end
|
302
|
+
|
303
|
+
# • User method
|
304
|
+
# Returns the return value of the outermost block.
|
305
|
+
#
|
306
|
+
def return_value
|
307
|
+
@scope[:queue].first
|
308
|
+
end
|
309
|
+
|
310
|
+
# If the given object is an Array, Hash, or String, returns the object
|
311
|
+
# shortened to the maximum length; otherwise, returns the object unchanged.
|
312
|
+
#
|
313
|
+
def sanitize (obj)
|
314
|
+
case obj
|
315
|
+
when Array then obj.slice!(0...max_array_length) if obj.length > @max_array_length
|
316
|
+
when Hash then obj.pop while obj.length > @max_hash_length
|
317
|
+
when String then obj.slice!(0...max_string_length) if obj.length > @max_string_length
|
318
|
+
end
|
319
|
+
|
320
|
+
obj
|
321
|
+
end
|
322
|
+
|
323
|
+
# Returns the value of self for the current frame.
|
324
|
+
#
|
325
|
+
def self
|
326
|
+
@frame_stack.last
|
327
|
+
end
|
328
|
+
|
329
|
+
# • User method
|
330
|
+
# Associates the given variable name with the given object, then returns the
|
331
|
+
# object.
|
332
|
+
#
|
333
|
+
def set (name, object)
|
334
|
+
return unless name
|
335
|
+
@variables[name] = object
|
336
|
+
end
|
337
|
+
|
338
|
+
# Unsets the named variable, then returns the object previously associated
|
339
|
+
# with the variable.
|
340
|
+
#
|
341
|
+
def unset (name)
|
342
|
+
@variables.delete(name)
|
343
|
+
end
|
344
|
+
|
345
|
+
# If there is an object already associated with the given variable name, marks
|
346
|
+
# that variable name as the last assigned variable, and then calls queue_push
|
347
|
+
# with that object. Otherwise, adds the variable name to the list of
|
348
|
+
# unassigned variables.
|
349
|
+
#
|
350
|
+
def variable_push (variable)
|
351
|
+
if object = get(variable)
|
352
|
+
@scope[:last_assigned_variable] = variable
|
353
|
+
queue_push(object)
|
354
|
+
else
|
355
|
+
@scope[:unassigned_variables].push(variable)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|