state_linker.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. from visitor import Visitor
  2. from sccd_constructs import INSTATE_SEQ
  3. from compiler_exceptions import CompilerException
  4. from lexer import Lexer, Token, TokenType
  5. class StateReferenceException(CompilerException):
  6. pass
  7. class StateLinker(Visitor):
  8. def __init__(self):
  9. self.visiting_statechart = None
  10. self.visiting_node = None
  11. self.lexer = Lexer()
  12. def visit_ClassDiagram(self, class_diagram):
  13. for c in class_diagram.classes :
  14. c.accept(self)
  15. def visit_Class(self, c):
  16. if c.statechart:
  17. c.statechart.accept(self)
  18. def visit_StateChart(self, statechart):
  19. self.visiting_statechart = statechart
  20. for node in statechart.basics + statechart.composites:
  21. node.accept(self)
  22. def visit_StateChartNode(self, node):
  23. self.visiting_node = node
  24. node.enter_action.accept(self)
  25. node.exit_action.accept(self)
  26. for transition in node.transitions :
  27. transition.accept(self)
  28. def visit_StateChartTransition(self, transition):
  29. try :
  30. transition.target.accept(self)
  31. except StateReferenceException as exception :
  32. raise StateReferenceException("Transition from <" + self.visiting_node.full_name + "> has invalid target. " + exception.message)
  33. try :
  34. transition.action.accept(self)
  35. except StateReferenceException as exception :
  36. raise StateReferenceException("Transition from <" + self.visiting_node.full_name + "> has invalid action. " + exception.message)
  37. try :
  38. if transition.guard :
  39. transition.guard.accept(self)
  40. except StateReferenceException as exception :
  41. raise StateReferenceException("Transition from <" + self.visiting_node.full_name + "> has invalid guard. " + exception.message)
  42. def visit_StateReference(self, state_reference):
  43. state_reference.target_nodes = []
  44. current_node = None #Will be used to find the target state(s)
  45. split_stack = [] #used for branching
  46. self.lexer.input(state_reference.path_string)
  47. for token in self.lexer.tokens() :
  48. if current_node == None : #current_node is not set yet or has been reset, the CHILD token can now have a special meaning
  49. if token.type == TokenType.SLASH :
  50. #Root detected
  51. current_node = self.visiting_statechart.root
  52. #Token consumed so continue
  53. continue
  54. else :
  55. current_node = self.visiting_node
  56. if token.type == TokenType.DOT :
  57. #Advance to next token
  58. token = self.lexer.nextToken()
  59. if token is None or token.type == TokenType.SLASH :
  60. #CURRENT operator "." detected
  61. continue
  62. elif token.type == TokenType.DOT :
  63. #Advance to next token
  64. token = self.lexer.nextToken()
  65. if token is None or token.type == TokenType.SLASH :
  66. #PARENT operator ".." detected
  67. current_node = current_node.parent
  68. if current_node is None :
  69. raise StateReferenceException("Illegal use of PARENT \"..\" operator at position " + str(token.pos) + " in state reference. Root of statechart reached.")
  70. else :
  71. raise StateReferenceException("Illegal use of PARENT \"..\" operator at position " + str(token.pos) + " in state reference.")
  72. else :
  73. raise StateReferenceException("Illegal use of CURRENT \".\" operator at position " + str(token.pos) + " in state reference.")
  74. elif token.type == TokenType.SLASH :
  75. continue
  76. elif token.type == TokenType.WORD :
  77. #try to advance to next child state
  78. cname = token.val
  79. found = False
  80. for child in current_node.children :
  81. if child.name == cname :
  82. found = True
  83. current_node = child
  84. break
  85. if not found :
  86. raise StateReferenceException("Refering to non exiting node at posisition " + str(token.pos) + " in state reference.")
  87. elif token.type == TokenType.LBRACKET :
  88. split_stack.append(current_node)
  89. elif token.type == TokenType.RBRACKET :
  90. if len(split_stack) > 0 :
  91. split_stack.pop()
  92. else :
  93. raise StateReferenceException("Invalid token at position " + str(token.pos) + " in state reference.")
  94. elif token.type == TokenType.COMMA :
  95. state_reference.target_nodes.append(current_node)
  96. if len(split_stack) > 0 :
  97. current_node = split_stack[-1]
  98. else :
  99. current_node = None
  100. else :
  101. raise StateReferenceException("Invalid token at position " + str(token.pos) + " in state reference.")
  102. if (len(split_stack) != 0) or (current_node is None) : #RB missing or extra COMMA
  103. raise StateReferenceException("State reference ends unexpectedly.")
  104. #TODO better validation of the target! When is it a valid state configuration?
  105. for node in state_reference.target_nodes :
  106. if current_node == node :
  107. raise StateReferenceException("A state reference can't reference the same node more than once.")
  108. if node.isDescendantOrAncestorOf(current_node) :
  109. raise StateReferenceException("A state reference can't reference a node and one of its descendants.");
  110. state_reference.target_nodes.append(current_node)
  111. if len(state_reference.target_nodes) == 0 :
  112. raise StateReferenceException("Meaningless state reference.")
  113. def visit_Expression(self, expression):
  114. for part in expression.expression_parts :
  115. part.accept(self)
  116. def visit_EnterExitAction(self, action):
  117. if action.action :
  118. action.action.accept(self)
  119. def visit_Action(self, action):
  120. for subaction in action.sub_actions :
  121. subaction.accept(self)
  122. def visit_InStateCall(self, call):
  123. try :
  124. call.target.accept(self)
  125. except StateReferenceException :
  126. raise StateReferenceException("Invalid state reference for " + INSTATE_SEQ + " call.")
  127. def visit_Assign(self, assign):
  128. assign.expression.accept(self)