q-language 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|