state_linker.py 6.6 KB

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