class Fable::CallStack
Attributes
start_of_root[RW]
thread_counter[RW]
threads[RW]
Public Class Methods
new(story_context_or_call_stack)
click to toggle source
# File lib/fable/call_stack.rb, line 5 def initialize(story_context_or_call_stack) if story_context_or_call_stack.is_a?(Story) start_of_root = Pointer.start_of(story_context_or_call_stack.root_content_container) reset! elsif story_context_or_call_stack.is_a?(CallStack) call_stack_to_copy = story_context_or_call_stack self.threads = [] call_stack_to_copy.threads.each do |thread| self.threads << thread.copy end self.thread_container = call_stack_to_copy.thread_counter self.start_of_root = call_stack_to_copy.start_of_root end end
Public Instance Methods
call_stack()
click to toggle source
# File lib/fable/call_stack.rb, line 141 def call_stack current_thread.call_stack end
call_stack_trace()
click to toggle source
# File lib/fable/call_stack.rb, line 190 def call_stack_trace result = StringIO.new self.threads.each_with_index do |thread, i| is_current_thread = thread == current_thread result << "=== THREAD #{i}/#{threads.count} #{'(current)' if is_current_thread }\n" thread.call_stack.each do |element| case element.type when :function result << " [FUNCTION] " when :tunnel result << " [TUNNEL] " end pointer = element.current_pointer if !pointer.null_pointer? result << "<SOMEWHERE IN #{pointer.container.path.to_s}>\n" end end end result.rewind result.read end
can_pop?(type = nil)
click to toggle source
# File lib/fable/call_stack.rb, line 79 def can_pop?(type = nil) return false if call_stack.size <= 1 return true if type.nil? return current_element.type == type end
can_pop_thread?()
click to toggle source
# File lib/fable/call_stack.rb, line 59 def can_pop_thread? threads.size > 1 && !element_is_evaluate_from_game? end
context_for_variable_named(name)
click to toggle source
Find the most appropriate context for this variable. Are we referencing a temporary or global variable? Note that the compiler will have warned us about possible conflicts, so anything that happens here should be safe
# File lib/fable/call_stack.rb, line 126 def context_for_variable_named(name) # Current temporary context? # (Shouldn't attempt to access contexts higher in the callstack) if current_element.temporary_variables.has_key?(name) return current_element_index + 1 else # Global return 0 end end
current_element()
click to toggle source
# File lib/fable/call_stack.rb, line 37 def current_element thread = threads.last thread.call_stack.last end
current_element_index()
click to toggle source
# File lib/fable/call_stack.rb, line 42 def current_element_index call_stack.size - 1 end
current_thread()
click to toggle source
# File lib/fable/call_stack.rb, line 46 def current_thread threads.last end
current_thread=(value)
click to toggle source
# File lib/fable/call_stack.rb, line 50 def current_thread=(value) if threads.size != 1 raise StoryError, "Shouldn't be directly setting the current thread when we have a stack of them" end threads.clear threads << value end
depth()
click to toggle source
# File lib/fable/call_stack.rb, line 33 def depth elements.size end
element_is_evaluate_from_game?()
click to toggle source
# File lib/fable/call_stack.rb, line 63 def element_is_evaluate_from_game? current_element.type == PushPopType::TYPES[:function_evaluation_from_game] end
elements()
click to toggle source
# File lib/fable/call_stack.rb, line 29 def elements call_stack end
fork_thread!()
click to toggle source
# File lib/fable/call_stack.rb, line 151 def fork_thread! forked_thread = current_thread.copy self.thread_counter += 1 forked_thread.thread_index = self.thread_counter return forked_thread end
from_hash!(hash_to_use, story_context)
click to toggle source
# File lib/fable/call_stack.rb, line 167 def from_hash!(hash_to_use, story_context) self.threads = [] hash_to_use["threads"].each do |thread_object| self.threads << Thread.new(thread_object, story_context) end self.thread_counter = hash_to_use["threadCounter"] self.start_of_root = Pointer.start_of(story_context.root_content_container) self end
get_temporary_variable_with_name(name, context_index = -1)
click to toggle source
Get variable value, dereferencing a variable pointer if necessary
# File lib/fable/call_stack.rb, line 94 def get_temporary_variable_with_name(name, context_index = -1) if context_index == -1 context_index = current_element_index + 1 end context_element = call_stack[context_index - 1] return context_element.temporary_variables[name] end
pop!(type=nil)
click to toggle source
# File lib/fable/call_stack.rb, line 85 def pop!(type=nil) if can_pop?(type) call_stack.pop else raise Error, "Mismatched push/pop in Callstack" end end
pop_thread!()
click to toggle source
# File lib/fable/call_stack.rb, line 159 def pop_thread! if can_pop_thread? threads.delete(current_thread) else raise Error, "Can't pop thread" end end
push(type, options = {external_evaluation_stack_height: 0, output_stream_length_when_pushed: 0})
click to toggle source
# File lib/fable/call_stack.rb, line 67 def push(type, options = {external_evaluation_stack_height: 0, output_stream_length_when_pushed: 0}) external_evaluation_stack_height = options[:external_evaluation_stack_height] || 0 output_stream_length_when_pushed = options[:output_stream_length_when_pushed] || 0 # When pushing to callstack, maintain the current content path, but jump # out of expressions by default element = Element.new(type, current_element.current_pointer, in_expression_evaluation: false) element.evaluation_stack_height_when_pushed = external_evaluation_stack_height element.function_start_in_output_stream = output_stream_length_when_pushed self.call_stack << element end
push_thread!()
click to toggle source
# File lib/fable/call_stack.rb, line 145 def push_thread! new_thread = current_thread.copy self.thread_counter += 1 self.threads << new_thread end
reset!()
click to toggle source
# File lib/fable/call_stack.rb, line 22 def reset! new_thread = Thread.new new_thread.call_stack << Element.new(:tunnel, self.start_of_root) self.threads = [new_thread] self.thread_counter = 0 end
set_temporary_variable(name, value, declare_new, context_index = -1)
click to toggle source
# File lib/fable/call_stack.rb, line 104 def set_temporary_variable(name, value, declare_new, context_index = -1) if context_index == -1 context_index = current_element_index + 1 end context_element = call_stack[context_index - 1] if !declare_new && !context_element.temporary_variables.has_key?(name) raise Error, "Could not find temporary variable to set: #{name}" end if context_element.temporary_variables.has_key?(name) old_value = context_element.temporary_variables[name] ListValue.retain_list_origins_for_assignment(old_value, value) end context_element.temporary_variables[name] = value end
thread_with_index(index)
click to toggle source
# File lib/fable/call_stack.rb, line 137 def thread_with_index(index) threads.find{|thread| thread.thread_index == index} end
to_hash()
click to toggle source
# File lib/fable/call_stack.rb, line 179 def to_hash export = {} export["threads"] = [] self.threads.each do |thread| export["threads"] << thread.to_hash end export["threadCounter"] = self.thread_counter export end