Class: Nudge::Interpreter
- Inherits:
-
Object
- Object
- Nudge::Interpreter
- Defined in:
- lib/interpreter/interpreter.rb
Overview
The Interpreter class executes the Push3-like language loop:
-
Pop the top item off the
:exec
Stack -
If it is a(n)…
-
… InstructionPoint, execute that instruction’s go() method;
-
… ValuePoint, push its value to the Stack it names;
-
… ReferencePoint (a reference to a Variable or Name), …
* ... if it's bound to a value, push the bound value onto the <b>:exec</b> Stack; * ... if it's not bound, push the name itself onto the <b>:name</b> Stack;
-
… CodeblockPoint, push its #contents (in the same order) back onto the :exec Stack
-
… NilPoint, do nothing
-
This cycle repeats until one of the termination conditions is met:
* nothing more remains on the <tt>:exec/tt> stack
* the number of cycles meets or exceeds the <tt>step_limit</tt>
* the wall-clock time meets or exceeds the <tt>time_limit</tt>
Instance Attribute Summary collapse
-
#code_char_limit ⇒ Object
Returns the value of attribute code_char_limit.
-
#evaluate_references ⇒ Object
Returns the value of attribute evaluate_references.
-
#instructions_library ⇒ Object
Returns the value of attribute instructions_library.
-
#last_name ⇒ Object
Returns the value of attribute last_name.
-
#names ⇒ Object
Returns the value of attribute names.
-
#program ⇒ Object
Returns the value of attribute program.
-
#sensors ⇒ Object
Returns the value of attribute sensors.
-
#stacks ⇒ Object
Returns the value of attribute stacks.
-
#start_time ⇒ Object
Returns the value of attribute start_time.
-
#step_limit ⇒ Object
Returns the value of attribute step_limit.
-
#steps ⇒ Object
Returns the value of attribute steps.
-
#time_limit ⇒ Object
Returns the value of attribute time_limit.
-
#types ⇒ Object
Returns the value of attribute types.
-
#variables ⇒ Object
Returns the value of attribute variables.
Instance Method Summary collapse
-
#active?(item) ⇒ Boolean
Convenience method that checks to see whether an Instruction or NudgeType class is currently in the active state.
-
#bind_name(name, value) ⇒ Object
Given a string and a ProgramPoint, binds a name with that name to that ProgramPoint.
-
#bind_variable(name, value) ⇒ Object
Given a string and a ProgramPoint, binds a variable with that name to that ProgramPoint.
-
#clear_stacks ⇒ Object
Deletes all items from all stacks.
-
#depth(stackname) ⇒ Object
Returns the count of items in a given stack.
-
#disable(item) ⇒ Object
Convenience method that can be called with either an Instruction or NudgeType class as an argument.
-
#disable_all_instructions ⇒ Object
Completely empties the set of active Instructions.
-
#disable_all_types ⇒ Object
Completely empties the set of NudgeTypes in play.
-
#enable(item) ⇒ Object
Convenience method that can be called with either an Instruction or NudgeType class as an argument.
-
#enable_all_instructions ⇒ Object
activates every Instruction subclass defined in any library.
-
#enable_all_types ⇒ Object
activates every NudgeType subclass defined in any library.
-
#fire_all_sensors ⇒ Object
Iterates through the Interpreter#sensors hash, #calling each one and passing in the current state of the Interpreter as an argument.
-
#initialize(program = nil, params = {}) ⇒ Interpreter
constructor
A new instance of Interpreter.
-
#instructions ⇒ Object
Returns an Array containing the class names of all active instructions.
-
#lookup(name) ⇒ Object
given a string, checks the hash of defined variables, then the names (local variables), returning the bound value, or nil if it is not found.
-
#next_name ⇒ Object
generates an arbitrary string for naming new local variables, by incrememnting from the starting point “aaa001”.
-
#notDone? ⇒ Boolean
Checks to see if either stopping condition applies: 1.
-
#peek(stackname) ⇒ Object
Returns a link to the top item in a given stack (not its value).
-
#peek_value(stackname) ⇒ Object
Returns a link to the value of the top item in a given stack.
-
#pop(stackname) ⇒ Object
Removes the top item from a given stack and returns it.
-
#pop_value(stackname) ⇒ Object
Removes the top item from a given stack and returns its value.
-
#push(stackname, value = "") ⇒ Object
Adds a new ValuePoint item, with the given value, to the named stack.
-
#references ⇒ Object
returns an Array of all strings defined as variables or names.
-
#register_sensor(name, &block) ⇒ Object
Create a new sensor with the given name, binding the associated block argument.
-
#reset(program = nil) ⇒ Object
Resets the Interpreter state: * clears all the Stacks (including the :exec Stack) * loads a new program, * parses the program * if it parses, pushes it onto the :exec Stack * (and if it doesn’t parse, leaves all stacks empty) * resets the @step counter * resets the name assignments * resets the start_time (intentional redundancy) * resets a number of state variables.
-
#reset_names ⇒ Object
removes all local variable definitions.
-
#reset_sensors ⇒ Object
Delete all sensors.
-
#reset_variables ⇒ Object
removes all global variable definitions.
-
#run ⇒ Object
invoke self.step() until a termination condition is true.
-
#step ⇒ Object
Execute one cycle of the Push3 interpreter rule: 1.
-
#unbind_name(name) ⇒ Object
removes the named local variable from the Hash that defines them.
-
#unbind_variable(name) ⇒ Object
removes the named global variable from the Hash that defines them.
Constructor Details
#initialize(program = nil, params = {}) ⇒ Interpreter
Returns a new instance of Interpreter.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/interpreter/interpreter.rb', line 28 def initialize(program = nil, params = {}) initialProgram = program @program = initialProgram @types = params[:types] || NudgeType.all_types @step_limit = params[:step_limit] || 3000 @time_limit = params[:time_limit] || 60.0 # seconds @code_char_limit = params[:code_char_limit] || 2000 @sensors = Hash.new instructions = params[:instructions] || Instruction.all_instructions @instructions_library = Hash.new {|hash, key| raise InstructionPoint::InstructionNotFoundError, "#{key} is not an active instruction in this context"} instructions.each {|i| self.enable(i)} # private parts @names = Hash.new @variables = Hash.new @steps = 0 @last_name = "refAAAAA" @evaluate_references = true @stacks = Hash.new {|hash, key| hash[key] = Stack.new(key) } # set it all up here self.reset(initialProgram) end |
Instance Attribute Details
#code_char_limit ⇒ Object
Returns the value of attribute code_char_limit.
24 25 26 |
# File 'lib/interpreter/interpreter.rb', line 24 def code_char_limit @code_char_limit end |
#evaluate_references ⇒ Object
Returns the value of attribute evaluate_references.
22 23 24 |
# File 'lib/interpreter/interpreter.rb', line 22 def evaluate_references @evaluate_references end |
#instructions_library ⇒ Object
Returns the value of attribute instructions_library.
21 22 23 |
# File 'lib/interpreter/interpreter.rb', line 21 def instructions_library @instructions_library end |
#last_name ⇒ Object
Returns the value of attribute last_name.
22 23 24 |
# File 'lib/interpreter/interpreter.rb', line 22 def last_name @last_name end |
#names ⇒ Object
Returns the value of attribute names.
21 22 23 |
# File 'lib/interpreter/interpreter.rb', line 21 def names @names end |
#program ⇒ Object
Returns the value of attribute program.
20 21 22 |
# File 'lib/interpreter/interpreter.rb', line 20 def program @program end |
#sensors ⇒ Object
Returns the value of attribute sensors.
23 24 25 |
# File 'lib/interpreter/interpreter.rb', line 23 def sensors @sensors end |
#stacks ⇒ Object
Returns the value of attribute stacks.
21 22 23 |
# File 'lib/interpreter/interpreter.rb', line 21 def stacks @stacks end |
#start_time ⇒ Object
Returns the value of attribute start_time.
25 26 27 |
# File 'lib/interpreter/interpreter.rb', line 25 def start_time @start_time end |
#step_limit ⇒ Object
Returns the value of attribute step_limit.
20 21 22 |
# File 'lib/interpreter/interpreter.rb', line 20 def step_limit @step_limit end |
#steps ⇒ Object
Returns the value of attribute steps.
20 21 22 |
# File 'lib/interpreter/interpreter.rb', line 20 def steps @steps end |
#time_limit ⇒ Object
Returns the value of attribute time_limit.
25 26 27 |
# File 'lib/interpreter/interpreter.rb', line 25 def time_limit @time_limit end |
#types ⇒ Object
Returns the value of attribute types.
21 22 23 |
# File 'lib/interpreter/interpreter.rb', line 21 def types @types end |
#variables ⇒ Object
Returns the value of attribute variables.
21 22 23 |
# File 'lib/interpreter/interpreter.rb', line 21 def variables @variables end |
Instance Method Details
#active?(item) ⇒ Boolean
Convenience method that checks to see whether an Instruction or NudgeType class is currently in the active state. Returns a boolean.
197 198 199 200 201 202 203 |
# File 'lib/interpreter/interpreter.rb', line 197 def active?(item) if item.superclass == Instruction @instructions_library.include?(item) elsif item.include? NudgeType @types.include?(item) end end |
#bind_name(name, value) ⇒ Object
Given a string and a ProgramPoint, binds a name with that name to that ProgramPoint
215 216 217 218 219 |
# File 'lib/interpreter/interpreter.rb', line 215 def bind_name(name, value) raise(ArgumentError, "Names can only be bound to ProgramPoints") unless value.kind_of?(ProgramPoint) @names[name] = value end |
#bind_variable(name, value) ⇒ Object
Given a string and a ProgramPoint, binds a variable with that name to that ProgramPoint
207 208 209 210 211 |
# File 'lib/interpreter/interpreter.rb', line 207 def bind_variable(name, value) raise(ArgumentError, "Variables can only be bound to ProgramPoints") unless value.kind_of?(ProgramPoint) @variables[name] = value end |
#clear_stacks ⇒ Object
Deletes all items from all stacks
81 82 83 |
# File 'lib/interpreter/interpreter.rb', line 81 def clear_stacks @stacks = Hash.new {|hash, key| hash[key] = Stack.new(key) } end |
#depth(stackname) ⇒ Object
Returns the count of items in a given stack
87 88 89 |
# File 'lib/interpreter/interpreter.rb', line 87 def depth(stackname) @stacks[stackname].depth end |
#disable(item) ⇒ Object
Convenience method that can be called with either an Instruction or NudgeType class as an argument. If an Instruction, that class is removed from the Interpreter’s #instruction_library. If a NudgeType, that class is removed to the list of types that can be used to generate random code.
271 272 273 274 275 276 277 |
# File 'lib/interpreter/interpreter.rb', line 271 def disable(item) if item.superclass == Instruction @instructions_library.delete(item) elsif item.include? NudgeType @types.delete(item) end end |
#disable_all_instructions ⇒ Object
Completely empties the set of active Instructions. The interpreter will recognize InstructionPoints, but will not invoke their #go methods when it does.
282 283 284 |
# File 'lib/interpreter/interpreter.rb', line 282 def disable_all_instructions @instructions_library = Hash.new end |
#disable_all_types ⇒ Object
Completely empties the set of NudgeTypes in play. ValuePoints the Interpreter encounters will still be recognized in code, and will still be pushed to the appropriate stack, but new ValuePoints (made by various code-generating methods) will not be created.
290 291 292 |
# File 'lib/interpreter/interpreter.rb', line 290 def disable_all_types @types = [] end |
#enable(item) ⇒ Object
Convenience method that can be called with either an Instruction or NudgeType class as an argument. If an Instruction, that class is added to the Interpreter’s #instruction_library. If a NudgeType, that class is added to the list of types that can be used to generate random code.
186 187 188 189 190 191 192 |
# File 'lib/interpreter/interpreter.rb', line 186 def enable(item) if item.superclass == Instruction @instructions_library[item] = item.new(self) elsif item.include? NudgeType @types |= [item] end end |
#enable_all_instructions ⇒ Object
activates every Instruction subclass defined in any library
254 255 256 257 258 |
# File 'lib/interpreter/interpreter.rb', line 254 def enable_all_instructions Instruction.all_instructions.each do |i| @instructions_library[i] = i.new(self) end end |
#enable_all_types ⇒ Object
activates every NudgeType subclass defined in any library
262 263 264 |
# File 'lib/interpreter/interpreter.rb', line 262 def enable_all_types @types = NudgeType.all_types end |
#fire_all_sensors ⇒ Object
Iterates through the Interpreter#sensors hash, #calling each one and passing in the current state of the Interpreter as an argument
311 312 313 314 315 316 |
# File 'lib/interpreter/interpreter.rb', line 311 def fire_all_sensors @sensors.inject({}) do |result, (key, value)| result[key] = @sensors[key].call(self) result end end |
#instructions ⇒ Object
Returns an Array containing the class names of all active instructions
154 155 156 |
# File 'lib/interpreter/interpreter.rb', line 154 def instructions @instructions_library.keys end |
#lookup(name) ⇒ Object
given a string, checks the hash of defined variables, then the names (local variables), returning the bound value, or nil if it is not found
171 172 173 |
# File 'lib/interpreter/interpreter.rb', line 171 def lookup(name) @variables[name] || @names[name] end |
#next_name ⇒ Object
generates an arbitrary string for naming new local variables, by incrememnting from the starting point “aaa001”
224 225 226 |
# File 'lib/interpreter/interpreter.rb', line 224 def next_name @last_name = @last_name.next end |
#notDone? ⇒ Boolean
Checks to see if either stopping condition applies:
-
Is the :exec stack empty?
-
Are the number of steps greater than self.step_limit?
-
Has the total time since recorded self.start_time exceeded self.time_limit?
129 130 131 132 133 |
# File 'lib/interpreter/interpreter.rb', line 129 def notDone? @stacks[:exec].depth > 0 && @steps < @step_limit && (Time.now-@start_time)<@time_limit end |
#peek(stackname) ⇒ Object
Returns a link to the top item in a given stack (not its value)
93 94 95 |
# File 'lib/interpreter/interpreter.rb', line 93 def peek(stackname) @stacks[stackname].peek end |
#peek_value(stackname) ⇒ Object
Returns a link to the value of the top item in a given stack
99 100 101 102 |
# File 'lib/interpreter/interpreter.rb', line 99 def peek_value(stackname) item = @stacks[stackname].peek item.nil? ? nil : item.value end |
#pop(stackname) ⇒ Object
Removes the top item from a given stack and returns it
106 107 108 |
# File 'lib/interpreter/interpreter.rb', line 106 def pop(stackname) @stacks[stackname].pop end |
#pop_value(stackname) ⇒ Object
Removes the top item from a given stack and returns its value
112 113 114 115 |
# File 'lib/interpreter/interpreter.rb', line 112 def pop_value(stackname) item = @stacks[stackname].pop item.nil? ? nil : item.value end |
#push(stackname, value = "") ⇒ Object
Adds a new ValuePoint item, with the given value, to the named stack
119 120 121 |
# File 'lib/interpreter/interpreter.rb', line 119 def push(stackname, value="") @stacks[stackname].push(ValuePoint.new(stackname, value)) end |
#references ⇒ Object
returns an Array of all strings defined as variables or names
177 178 179 |
# File 'lib/interpreter/interpreter.rb', line 177 def references @names.merge(@variables).keys end |
#register_sensor(name, &block) ⇒ Object
Create a new sensor with the given name, binding the associated block argument. All sensors are called, in the order registered, when the Interpreter#run cycle terminates normally.
297 298 299 300 |
# File 'lib/interpreter/interpreter.rb', line 297 def register_sensor(name, &block) raise(ArgumentError, "Sensor name #{name} is not a string") unless name.kind_of?(String) @sensors[name] = block end |
#reset(program = nil) ⇒ Object
Resets the Interpreter state:
-
clears all the Stacks (including the :exec Stack)
-
loads a new program,
* parses the program * if it parses, pushes it onto the <b>:exec</b> Stack * (and if it doesn't parse, leaves all stacks empty)
-
resets the @step counter
-
resets the name assignments
-
resets the start_time (intentional redundancy)
-
resets a number of state variables
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/interpreter/interpreter.rb', line 65 def reset(program=nil) @program = program self.clear_stacks self.reset_names self.reset_sensors if !program.nil? @stacks[:exec].push(NudgeProgram.new(program).linked_code) end @steps = 0 @start_time = Time.now @evaluate_references = true end |
#reset_names ⇒ Object
removes all local variable definitions
248 249 250 |
# File 'lib/interpreter/interpreter.rb', line 248 def reset_names @names = Hash.new end |
#reset_sensors ⇒ Object
Delete all sensors.
304 305 306 |
# File 'lib/interpreter/interpreter.rb', line 304 def reset_sensors @sensors = Hash.new end |
#reset_variables ⇒ Object
removes all global variable definitions
242 243 244 |
# File 'lib/interpreter/interpreter.rb', line 242 def reset_variables @variables = Hash.new end |
#run ⇒ Object
invoke self.step() until a termination condition is true
160 161 162 163 164 165 166 |
# File 'lib/interpreter/interpreter.rb', line 160 def run @start_time = Time.now while notDone? self.step end fire_all_sensors end |
#step ⇒ Object
Execute one cycle of the Push3 interpreter rule:
-
check termination conditions with self.notDone()?
-
pop one item from
:exec
-
call that item’s #go method
-
increment the step counter self#steps
Note that the start_time attribute is not adjusted; if called a long time after resetting, it may time out unexpectedly.
144 145 146 147 148 149 150 |
# File 'lib/interpreter/interpreter.rb', line 144 def step if notDone? nextPoint = @stacks[:exec].pop nextPoint.go(self) @steps += 1 end end |
#unbind_name(name) ⇒ Object
removes the named local variable from the Hash that defines them
236 237 238 |
# File 'lib/interpreter/interpreter.rb', line 236 def unbind_name(name) @names.delete(name) end |
#unbind_variable(name) ⇒ Object
removes the named global variable from the Hash that defines them
230 231 232 |
# File 'lib/interpreter/interpreter.rb', line 230 def unbind_variable(name) @variables.delete(name) end |