state_ref.py 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. from abc import abstractmethod
  2. from dataclasses import dataclass
  3. from typing import *
  4. class PathError(Exception):
  5. pass
  6. class PathItem:
  7. @abstractmethod
  8. def type(self):
  9. pass
  10. class ParentNode(PathItem):
  11. def type(self):
  12. return "PARENT_NODE"
  13. def __repr__(self):
  14. return "ParentNode()"
  15. class CurrentNode(PathItem):
  16. def type(self):
  17. return "CURRENT_NODE"
  18. def __repr__(self):
  19. return "CurrentNode()"
  20. @dataclass
  21. class Identifier(PathItem):
  22. value: str # state's short name
  23. def type(self):
  24. return "IDENTIFIER"
  25. def __repr__(self):
  26. return "Identifier(%s)" % self.value
  27. @dataclass
  28. class StatePath:
  29. is_absolute: bool
  30. sequence: List[PathItem]
  31. # Used by Transition and INSTATE-macro
  32. @dataclass(eq=False)
  33. class StateRef:
  34. source: 'State'
  35. path: StatePath
  36. target: Optional['State'] = None
  37. def resolve(self, root):
  38. if self.path.is_absolute:
  39. state = root
  40. else:
  41. state = self.source
  42. for item in self.path.sequence:
  43. item_type = item.type()
  44. if item_type == "PARENT_NODE":
  45. state = state.parent
  46. elif item_type == "CURRENT_NODE":
  47. continue
  48. elif item_type == "IDENTIFIER":
  49. try:
  50. state = [x for x in state.children if x.short_name == item.value][0]
  51. except IndexError as e:
  52. raise PathError("%s has no child \"%s\"." % ("Root state" if state.parent is None else '"%s"'%state.short_name, item.value)) from e
  53. if state.parent is None:
  54. raise PathError("Root cannot be target of StateRef.")
  55. self.target = state