StateLinker.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. namespace csharp_sccd_compiler
  5. {
  6. public class StateLinker : Visitor
  7. {
  8. StateChart visiting_statechart;
  9. StateChartNode visiting_node;
  10. Lexer lexer;
  11. public StateLinker()
  12. {
  13. this.lexer = new Lexer();
  14. }
  15. public override void visit(ClassDiagram class_diagram)
  16. {
  17. foreach (Class c in class_diagram.classes)
  18. c.accept(this);
  19. }
  20. public override void visit(Class c)
  21. {
  22. c.statechart.accept(this);
  23. }
  24. public override void visit(StateChart statechart)
  25. {
  26. this.visiting_statechart = statechart;
  27. foreach (StateChartNode node in statechart.basics.Concat(statechart.composites))
  28. node.accept(this);
  29. }
  30. public override void visit(StateChartNode node)
  31. {
  32. this.visiting_node = node;
  33. node.enter_action.accept(this);
  34. node.exit_action.accept(this);
  35. foreach (StateChartTransition transition in node.transitions)
  36. transition.accept(this);
  37. }
  38. public override void visit(StateChartTransition transition)
  39. {
  40. try
  41. {
  42. transition.target.accept(this);
  43. }
  44. catch (StateReferenceException exception)
  45. {
  46. throw new StateReferenceException(string.Format("Transition from <{0}> has invalid target.", this.visiting_node.full_name), exception);
  47. }
  48. try
  49. {
  50. transition.action.accept(this);
  51. }
  52. catch (StateReferenceException exception)
  53. {
  54. throw new StateReferenceException(string.Format("Transition from <{0}> has invalid action.", this.visiting_node.full_name), exception);
  55. }
  56. try
  57. {
  58. if (transition.guard != null)
  59. transition.guard.accept(this);
  60. }
  61. catch (StateReferenceException exception)
  62. {
  63. throw new StateReferenceException(string.Format("Transition from <{0}> has invalid guard.", this.visiting_node.full_name), exception);
  64. }
  65. }
  66. public override void visit(StateReference state_reference)
  67. {
  68. state_reference.target_nodes = new List<StateChartNode>();
  69. StateChartNode current_node = null; //Will be used to find the target state(s)
  70. Stack<StateChartNode> split_stack = new Stack<StateChartNode>(); // user for branching
  71. this.lexer.setInput(state_reference.state_reference_string);
  72. foreach (Token token in this.lexer.iterateTokens())
  73. {
  74. if (current_node == null) //current_node is not set yet or has been reset, the CHILD token can now have a special meaning
  75. {
  76. if (token.type == Token.Type.SLASH)
  77. {
  78. current_node = this.visiting_statechart.root; //Root detected
  79. continue;
  80. }
  81. else
  82. current_node = this.visiting_node;
  83. }
  84. if (token.type == Token.Type.DOT)
  85. {
  86. Token next_token = this.lexer.nextToken();//Advance to next token
  87. if (next_token == null || next_token.type == Token.Type.SLASH)
  88. continue;//CURRENT operator "." detected
  89. else if (next_token.type == Token.Type.DOT)
  90. {
  91. next_token = this.lexer.nextToken();//Advance to next token
  92. if (next_token == null || next_token.type == Token.Type.SLASH)
  93. {
  94. current_node = current_node.parent; //PARENT operator ".." detected
  95. if (current_node == null)
  96. throw new StateReferenceException(string.Format("Illegal use of PARENT \"..\" operator at position {0} in state reference. Root of statechart reached.", token.pos));
  97. }
  98. else
  99. throw new StateReferenceException(string.Format("Illegal use of PARENT \"..\" operator at position {0} in state reference.", token.pos));
  100. }
  101. else
  102. throw new StateReferenceException(string.Format("Illegal use of CURRENT \"..\" operator at position {0} in state reference.", token.pos));
  103. }
  104. else if (token.type == Token.Type.SLASH)
  105. continue;
  106. else if (token.type == Token.Type.WORD)
  107. {
  108. //Trying to advance to next child state
  109. string child_name = token.val;
  110. bool found = false;
  111. foreach (StateChartNode child in current_node.children)
  112. {
  113. if (child.name == child_name)
  114. {
  115. found = true;
  116. current_node = child;
  117. break;
  118. }
  119. }
  120. if (!found)
  121. throw new StateReferenceException(string.Format("Refering to non exiting node at posisition {0} in state reference.", token.pos));
  122. }
  123. else if (token.type == Token.Type.LBRACKET)
  124. split_stack.Push(current_node);
  125. else if (token.type == Token.Type.RBRACKET)
  126. {
  127. if (split_stack.Count > 0)
  128. split_stack.Pop();
  129. else
  130. throw new StateReferenceException(string.Format("Invalid token at position {0} in state reference.", token.pos));
  131. }
  132. else if (token.type == Token.Type.COMMA)
  133. {
  134. state_reference.target_nodes.Add(current_node);
  135. if (split_stack.Count > 0)
  136. current_node = split_stack.Peek();
  137. else
  138. current_node = null;
  139. }
  140. else
  141. throw new StateReferenceException(string.Format("Invalid token at position {0} in state reference.", token.pos));
  142. }
  143. if (split_stack.Count != 0 || current_node == null) //RB missing or extra COMMA
  144. throw new StateReferenceException("State reference ends unexpectedly.");
  145. //TODO better validation of the target! When is it a valid state configuration?
  146. foreach (StateChartNode node in state_reference.target_nodes)
  147. {
  148. if (object.ReferenceEquals(current_node,node))
  149. throw new StateReferenceException("A state reference can't reference the same node more than once.");
  150. if (node.isDescendantOrAncestorOf(current_node))
  151. throw new StateReferenceException("A state reference can't reference a node and one of its descendants.");
  152. }
  153. state_reference.target_nodes.Add(current_node);
  154. if (state_reference.target_nodes.Count == 0)
  155. throw new StateReferenceException("Meaningless state reference.");
  156. }
  157. public override void visit(EnterExitAction enter_exit_action)
  158. {
  159. if (enter_exit_action.action != null)
  160. enter_exit_action.action.accept(this);
  161. }
  162. public override void visit(InStateCall in_state_call)
  163. {
  164. try
  165. {
  166. in_state_call.state_reference.accept(this);
  167. }
  168. catch (StateReferenceException exception)
  169. {
  170. throw new StateReferenceException(string.Format("Invalid state reference for {0} macro.", Constants.INSTATE_SEQ), exception);
  171. }
  172. }
  173. public override void visit(Assign assign)
  174. {
  175. assign.expression.accept(this);
  176. }
  177. }
  178. }