memory.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. from typing import *
  2. from dataclasses import *
  3. from sccd.util.bitmap import *
  4. from sccd.util.debug import *
  5. from sccd.action_lang.static.scope import *
  6. from sccd.common.exceptions import *
  7. from sccd.action_lang.static.expression import *
  8. @dataclass(frozen=True)
  9. class StackFrame:
  10. __slots__ = ["storage", "parent", "context", "scope"]
  11. # Values of variables in the frame.
  12. storage: List[Any]
  13. # The previous stack frame, forming a linked list of stack frames representing the "call stack".
  14. # The parent frame will become the "current frame" when this stack frame is popped.
  15. parent: Optional['StackFrame']
  16. # The sequence of 'context' values forms another linked list, often but not always identical to the linked list of 'parent's, for accessing nonlocal variables.
  17. # The 'context' is the stack frame in which the frame of the currently called function was declared.
  18. # This could be a stack frame that no longer exists on "the stack", like with a function closure, or some ancestor, for a recursive function.
  19. # If the current scope is not a function, this value is equal to the 'parent' field.
  20. context: Optional['StackFrame']
  21. # We need this to know where the offsets of this scope start relative to the offsets of the parent scope, and to print variable names in error messages.
  22. scope: Scope
  23. def __str__(self):
  24. def short_descr(frame):
  25. return "StackFrame(%s, len=%d, ...)" % (frame.scope.name, len(frame.storage)) if frame else "None"
  26. return "StackFrame(%s, len=%d, parent=%s, context=%s)" % (self.scope.name, len(self.storage), short_descr(self.parent), "parent" if self.context is self.parent else short_descr(self.context))
  27. class Memory(MemoryInterface):
  28. __slots__ = ["_current_frame"]
  29. def __init__(self):
  30. self._current_frame = None
  31. def current_frame(self) -> StackFrame:
  32. return self._current_frame
  33. # For function calls: context MAY differ from _current_frame if the function was
  34. # called from a different context from where it was created.
  35. # This enables function closures.
  36. def push_frame_w_context(self, scope: Scope, context: StackFrame):
  37. self._current_frame = StackFrame(
  38. storage=[None]*scope.size(),
  39. parent=self._current_frame,
  40. context=context,
  41. scope=scope)
  42. def push_frame(self, scope: Scope):
  43. self.push_frame_w_context(scope, self._current_frame)
  44. def pop_frame(self):
  45. self._current_frame = self._current_frame.parent
  46. def _get_frame(self, offset: int) -> Tuple[StackFrame, int]:
  47. frame = self._current_frame
  48. while offset < 0:
  49. offset += frame.scope.parent_offset
  50. frame = frame.context
  51. return (frame, offset)
  52. def load(self, offset: int) -> Any:
  53. frame, offset = self._get_frame(offset)
  54. return frame.storage[offset]
  55. def store(self, offset: int, value: Any):
  56. frame, offset = self._get_frame(offset)
  57. frame.storage[offset] = value