scope.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. from abc import *
  2. from typing import *
  3. from dataclasses import *
  4. from inspect import signature
  5. from sccd.action_lang.static.types import *
  6. from sccd.common.exceptions import *
  7. from sccd.util.visitable import *
  8. import itertools
  9. import termcolor
  10. from collections import defaultdict
  11. class ScopeError(ModelStaticError):
  12. def __init__(self, scope, msg):
  13. super().__init__(msg + '\n\n' + str(scope))
  14. # Stateless stuff we know about a variable existing within a scope.
  15. @dataclass(frozen=True)
  16. class _Variable(ABC):
  17. __slots__ = ["_name", "offset", "type", "const", "initial_value"]
  18. _name: str # only used to print error messages
  19. offset: int # Offset within variable's scope. Always >= 0.
  20. type: SCCDType
  21. const: bool
  22. initial_value: 'Expression'
  23. @property
  24. def name(self):
  25. return self._name
  26. # return termcolor.colored(self._name, 'yellow')
  27. def __str__(self):
  28. return "+%d: %s%s: %s" % (self.offset, "(const) "if self.const else "", self.name, str(self.type))
  29. # _scope_ctr = 0
  30. # Stateless stuff we know about a scope (= set of named values)
  31. class Scope(Visitable):
  32. __slots__ = ["name", "parent", "parent_offset", "names", "variables"]
  33. def __init__(self, name: str, parent: 'Scope'):
  34. # global _scope_ctr
  35. # self.id = _scope_ctr # just a unique ID within the AST (for code generation)
  36. # _scope_ctr += 1
  37. self.name = name
  38. self.parent = parent
  39. self.children = defaultdict(list) # mapping from offset to child scope
  40. if parent is not None:
  41. # Position of the start of this scope, seen from the parent scope
  42. self.parent_offset = parent.size()
  43. # Append to parent
  44. parent.children[self.parent_offset].append(self)
  45. else:
  46. self.parent_offset = None # value should never be used
  47. # Mapping from name to Value
  48. self.names: Dict[str, _Variable] = {}
  49. # All non-constant values, ordered by memory position
  50. self.variables: List[_Variable] = []
  51. self.deepest_lookup = 0
  52. def size(self) -> int:
  53. return len(self.variables)
  54. def __str__(self):
  55. s = " scope: '%s'\n" % self.name
  56. is_empty = True
  57. for v in reversed(self.variables):
  58. s += " %s\n" % str(v)
  59. is_empty = False
  60. if is_empty:
  61. s += " ø\n"
  62. if self.parent:
  63. s += self.parent.__str__()
  64. return s
  65. def __repr__(self):
  66. return "Scope(%s)" % self.name
  67. def _internal_lookup(self, name, offset=0) -> Optional[Tuple['Scope', int, _Variable]]:
  68. try:
  69. return (self, offset, self.names[name])
  70. except KeyError:
  71. if self.parent is not None:
  72. got_it = self.parent._internal_lookup(name, offset - self.parent_offset)
  73. if got_it:
  74. scope, off, v = got_it
  75. self.deepest_lookup = max(self.deepest_lookup, self.nested_levels(off))
  76. return got_it
  77. else:
  78. return None
  79. def nested_levels(self, offset):
  80. if offset >= 0:
  81. return 0
  82. else:
  83. if self.parent is not None:
  84. return 1 + self.parent.nested_levels(offset + self.parent_offset)
  85. else:
  86. return 0
  87. # Create name in this scope
  88. # Precondition: _internal_lookup of name returns 'None'
  89. def _internal_add(self, name, type, const, initial_value: 'Expression') -> int:
  90. offset = len(self.variables)
  91. var = _Variable(name, offset, type, const, initial_value)
  92. self.names[name] = var
  93. self.variables.append(var)
  94. return offset
  95. # This is what we do when we encounter an assignment expression:
  96. # Add name to current scope if it doesn't exist yet in current or any parent scope.
  97. # Or assign to existing variable, if the name already exists, if the types match.
  98. # Returns tuple:
  99. # - offset relative to the beginning of this scope (may be a postive or negative number).
  100. # - whether a new variable was declared (and initialized)
  101. def put_lvalue(self, name: str, type: SCCDType, value: 'Expression') -> (int, bool):
  102. found = self._internal_lookup(name)
  103. if found:
  104. scope, scope_offset, var = found
  105. if var.type == type:
  106. # Cannot assign to const
  107. if var.const:
  108. raise ScopeError(self, "Cannot assign to %s: %s of scope '%s': Variable is constant." % (var.name, str(var.type), scope.name))
  109. # Assign to existing variable
  110. return (scope_offset + var.offset, False)
  111. else:
  112. # Types don't match
  113. raise ScopeError(self, "Cannot assign %s to %s: %s of scope '%s'" %(str(type), var.name, str(var.type), scope.name))
  114. else:
  115. # Declare new variable
  116. return (self._internal_add(name, type, const=False, initial_value=value), True)
  117. # Lookup name in this scope and its ancestors. Raises exception if not found.
  118. # Returns offset relative to the beginning of this scope, just like put_lvalue, and also the type of the variable.
  119. def get_rvalue(self, name: str) -> Tuple[int, SCCDType]:
  120. found = self._internal_lookup(name)
  121. if not found:
  122. raise ScopeError(self, "Name '%s' not found in any scope." % name)
  123. scope, scope_offset, var = found
  124. return (scope_offset + var.offset, var.type)
  125. # Attempt to declare given name in this scope.
  126. # Similar to put_lvalue, but only succeeds if the name does not exist yet in any scope.
  127. # Returns offset relative to this scope (always a positive number since this function only creates new variables in this scope)
  128. def declare(self, name: str, type: SCCDType, const: bool = False) -> int:
  129. found = self._internal_lookup(name)
  130. if found:
  131. scope, scope_offset, var = found
  132. raise ScopeError(self, "Cannot declare '%s' in scope '%s': Name already exists in scope '%s'" % (var.name, self.name, scope.name))
  133. return self._internal_add(name, type, const, initial_value=None)