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