lib/interpreter/interpreter.rb in nudge-0.0.2 vs lib/interpreter/interpreter.rb in nudge-0.1.0

- old
+ new

@@ -1,73 +1,110 @@ +#encoding: utf-8 module Nudge - # The Interpreter class executes the Push3 language loop: + # The Interpreter class executes the Push3-like language loop: # 1. Pop the top item off the <b>:exec</b> Stack # 2. If it is a(n)... - # * ... Instruction, execute its go() method; - # * ... Literal or Sample, push its value to the Stack it names; - # * ... Reference (Variable or Name), ... + # * ... InstructionPoint, execute that instruction's go() method; + # * ... ValuePoint, push its value to the Stack it names; + # * ... ReferencePoint (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; - # * ... CodeBlock, push its #contents (in the same order) back onto the <b>:exec</b> Stack + # * ... CodeblockPoint, push its #contents (in the same order) back onto the <b>:exec</b> Stack + # * ... NilPoint, do nothing class Interpreter - attr_accessor :parser, :stepLimit, :steps + attr_accessor :program, :stepLimit, :steps attr_accessor :stacks, :instructions_library, :variables, :names, :types - attr_accessor :last_name, :evaluate_channels + attr_accessor :last_name, :evaluate_references + # A program to be interpreted can be passed in as an optional parameter def initialize(params = {}) - initialProgram = params[:program] || "" + initialProgram = params[:program] || nil + @program = initialProgram @types = params[:types] || NudgeType.all_types @stepLimit = params[:step_limit] || 3000 - instructions = params[:instructions] || [] - @instructions_library = 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 - @parser = NudgeLanguageParser.new() @names = Hash.new @variables = Hash.new @steps = 0 @last_name = "refAAAAA" - @evaluate_channels = true + @evaluate_references = true @stacks = Hash.new {|hash, key| hash[key] = Stack.new(key) } # set it all up here self.reset(initialProgram) end + # Resets the Interpreter state: # * clears all the Stacks (including the <b>:exec</b> 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. - def reset(program="") + def reset(program=nil) + @program = program self.clear_stacks + self.reset_names + if program + @stacks[:exec].push(NudgeProgram.new(program).linked_code) + end @steps = 0 - parsed = @parser.parse(program) - newCode = parsed.to_points if parsed - @stacks[:exec].push(newCode) - @evaluate_channels = true + @evaluate_references = true end def clear_stacks @stacks = Hash.new {|hash, key| hash[key] = Stack.new(key) } end + + def peek(stackname) + @stacks[stackname].peek + end + + + def peek_value(stackname) + item = @stacks[stackname].peek + item.nil? ? nil : item.value + end + + + def pop(stackname) + @stacks[stackname].pop + end + + + def pop_value(stackname) + item = @stacks[stackname].pop + item.nil? ? nil : item.value + end + + + def push(stackname, value="") + @stacks[stackname].push(ValuePoint.new(stackname, value)) + end + + + # Checks to see if either stopping condition applies: # 1. Is the <b>:exec</b> stack empty? # 2. Are the number of steps greater than self.stepLimit? def notDone? @stacks[:exec].depth > 0 && @steps < @stepLimit end + # Execute one cycle of the Push3 interpreter rule: # 1. check termination conditions with self.notDone()? # 2. pop one item from <b>:exec</b> # 3. call its go() method # 4. increment the step counter self#steps @@ -77,86 +114,98 @@ nextPoint.go(self) @steps += 1 end end + def instructions @instructions_library.keys end + # invoke self.step() until a termination condition is true def run while notDone? self.step end end + def lookup(name) @variables[name] || @names[name] end + def references @names.merge(@variables).keys end + def enable(item) if item.superclass == Instruction @instructions_library[item] = item.new(self) elsif item.include? NudgeType @types |= [item] end end + def active?(item) - puts "#{item.inspect} is the item" if item.superclass == Instruction @instructions_library.include?(item) - elsif item.include? NudgeType - puts "#{@types} is the type list" - + elsif item.include? NudgeType @types.include?(item) end end def bind_variable(name, value) - raise(ArgumentError, "Variables can only be bound to Literals") unless value.kind_of?(LiteralPoint) + raise(ArgumentError, "Variables can only be bound to ProgramPoints") unless + value.kind_of?(ProgramPoint) @variables[name] = value end + def bind_name(name, value) - raise(ArgumentError, "Names can only be bound to Literals") unless value.kind_of?(LiteralPoint) + raise(ArgumentError, "Names can only be bound to ProgramPoints") unless + value.kind_of?(ProgramPoint) @names[name] = value end + def next_name @last_name = @last_name.next end def unbind_variable(name) @variables.delete(name) end + def unbind_name(name) @names.delete(name) end + def reset_variables @variables = Hash.new end + def reset_names @names = Hash.new end + def enable_all_instructions Instruction.all_instructions.each do |i| @instructions_library[i] = i.new(self) end end + def enable_all_types @types = NudgeType.all_types end @@ -166,12 +215,14 @@ elsif item.include? NudgeType @types.delete(item) end end + def disable_all_instructions @instructions_library = Hash.new end + def disable_all_types @types = [] end end \ No newline at end of file