rust.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. from typing import *
  2. from sccd.statechart.static.tree import *
  3. from sccd.util.visit_tree import *
  4. # Conversion functions from abstract syntax elements to identifiers in Rust
  5. def snake_case(state: State) -> str:
  6. return state.opt.full_name.replace('/', '_');
  7. def ident_type(state: State) -> str:
  8. if state.opt.full_name == "/":
  9. return "Root" # no technical reason, just make this type 'pop out' a little
  10. else:
  11. return "State" + snake_case(state)
  12. def ident_enum_variant(state: State) -> str:
  13. # We know the direct children of a state must have unique names relative to each other,
  14. # and enum variants are scoped locally, so we can use the short name here:
  15. return "S_" + state.short_name
  16. def ident_field(state: State) -> str:
  17. return "s" + snake_case(state)
  18. def ident_source_target(state: State) -> str:
  19. # drop the first '_' (this is safe, the root state itself can never be source or target)
  20. return snake_case(state)[1:]
  21. def ident_transition(t: Transition) -> str:
  22. return "transition%d_FROM_%s_TO_%s" % (t.opt.id, ident_source_target(t.source), ident_source_target(t.target))
  23. def compile_to_rust(tree: StateTree):
  24. # Write 'current state' types
  25. def write_state_type(state: State, children: List[State]):
  26. if isinstance(state, HistoryState):
  27. return None # we got no time for pseudo-states!
  28. def as_struct():
  29. print("#[allow(non_camel_case_types)]")
  30. print("struct %s {" % ident_type(state))
  31. for child in children:
  32. print(" %s: %s," % (ident_field(child), ident_type(child)))
  33. print("}")
  34. def as_enum():
  35. print("#[allow(non_camel_case_types)]")
  36. print("enum %s {" % ident_type(state))
  37. for child in children:
  38. print(" %s(%s)," % (ident_enum_variant(child), ident_type(child)))
  39. print("}")
  40. if isinstance(state, ParallelState):
  41. print("// And-state")
  42. as_struct()
  43. elif isinstance(state, State):
  44. if len(state.children) > 0:
  45. print("// Or-state")
  46. as_enum() # Or-state
  47. else:
  48. # Basic state: write as empty struct
  49. #
  50. # An empty struct in Rust is a type with one possible value.
  51. # An empty struct is a Zero-Sized Type.
  52. #
  53. # An empty enum is also a valid type in Rust, but no instances
  54. # of it can be created. Also called an "uninhabited type".
  55. print("// Basic state")
  56. as_struct()
  57. # The above if-else construction hints at the fact that we would have
  58. # better used empty And-states to model basic states, instead of empty Or-states...
  59. print()
  60. return state
  61. print("pub trait State {")
  62. print(" fn enter_actions(&self);")
  63. print(" fn exit_actions(&self);")
  64. print(" fn enter(&self);")
  65. print(" fn exit(&self);")
  66. print(" fn fire(&mut self) -> bool;")
  67. print("}")
  68. print()
  69. # Write "enter/exit state" functions
  70. def write_enter_exit(state: State, children: List[State]):
  71. if isinstance(state, HistoryState):
  72. return None # we got no time for pseudo-states!
  73. print("impl State for %s {" % ident_type(state))
  74. print(" fn enter_actions(&self) {")
  75. print(" // TODO: execute enter actions")
  76. print(" println!(\"enter %s\");" % state.opt.full_name);
  77. print(" }")
  78. print(" fn exit_actions(&self) {")
  79. print(" // TODO: execute exit actions")
  80. print(" println!(\"exit %s\");" % state.opt.full_name);
  81. print(" }")
  82. print(" fn enter(&self) {")
  83. print(" self.enter_actions();")
  84. if isinstance(state, ParallelState):
  85. for child in children:
  86. print(" self.%s.enter();" % ident_field(child))
  87. elif isinstance(state, State):
  88. if len(children) > 0:
  89. print(" match self {")
  90. for child in children:
  91. print(" Self::%s(s) => s.enter()," % ident_enum_variant(child))
  92. print(" }")
  93. print(" }")
  94. print(" fn exit(&self) {")
  95. if isinstance(state, ParallelState):
  96. # For symmetry, we exit regions in opposite order of entering them
  97. # Not sure whether this is semantically "correct" or relevant!
  98. # (what are the semantics of statecharts, after all?)
  99. for child in reversed(children):
  100. print(" self.%s.exit();" % ident_field(child))
  101. elif isinstance(state, State):
  102. if len(children) > 0:
  103. print(" match self {")
  104. for child in children:
  105. print(" Self::%s(s) => s.exit()," % ident_enum_variant(child))
  106. print(" }")
  107. print(" self.exit_actions();")
  108. print(" }")
  109. # TODO: This is where parent/child-first should be implemented
  110. # For now, it's always "parent first"
  111. print(" fn fire(&mut self) -> bool {")
  112. for t in state.transitions:
  113. print(" println!(\"fire %s\");" % str(t))
  114. print(" return true;")
  115. break
  116. else:
  117. if isinstance(state, ParallelState):
  118. for child in children:
  119. print(" if self.%s.fire() {" % ident_field(child))
  120. print(" return true;")
  121. print(" }")
  122. print(" return false;")
  123. elif isinstance(state, State):
  124. if len(children) > 0:
  125. print(" return match self {")
  126. for child in children:
  127. print(" Self::%s(s) => s.fire()," % ident_enum_variant(child))
  128. print(" };")
  129. else:
  130. print(" return false;")
  131. print(" }")
  132. print("}")
  133. print()
  134. return state
  135. # Write "enter default state" functions
  136. def write_enter_default(state: State, children: List[State]):
  137. if isinstance(state, HistoryState):
  138. return None # we got no time for pseudo-states!
  139. # We use Rust's Default-trait to record default states,
  140. # this way, constructing a state instance without parameters will initialize it as the default state.
  141. print("impl Default for %s {" % ident_type(state))
  142. print(" fn default() -> Self {")
  143. if isinstance(state, ParallelState):
  144. print(" return Self {")
  145. for child in children:
  146. print(" %s: Default::default()," % (ident_field(child)))
  147. print(" };")
  148. elif isinstance(state, State):
  149. if state.default_state is not None:
  150. # Or-state
  151. print(" return Self::%s(Default::default());" % (ident_enum_variant(state.default_state)))
  152. else:
  153. # Basic state
  154. print(" return Self{};")
  155. print(" }")
  156. print("}")
  157. print()
  158. return state
  159. # # Write transition selection
  160. # def write_fire(state: State, children: List[State]):
  161. # if isinstance(state, HistoryState):
  162. # return None # we got no time for pseudo-states!
  163. visit_tree(tree.root, lambda s: s.children,
  164. child_first=[
  165. write_state_type,
  166. write_enter_exit,
  167. write_enter_default,
  168. ])
  169. # Write statechart type
  170. print("pub struct Statechart {")
  171. print(" current_state: %s," % ident_type(tree.root))
  172. print(" // TODO: history values")
  173. print(" // TODO: timers")
  174. print("}")
  175. print()
  176. # Write transitions
  177. for t in tree.transition_list:
  178. print("#[allow(non_snake_case)]")
  179. print("fn %s(sc: &mut Statechart) {" % (ident_transition(t)))
  180. # print(list(tree.bitmap_to_states(t.opt.arena.opt.ancestors)))
  181. path = ""
  182. # for s in tree.bitmap_to_states(t.opt.arena.opt.ancestors):
  183. # path +=
  184. # print(" sc.current_state.;")
  185. print("}")
  186. print()
  187. # See if it works
  188. print("fn main() {")
  189. print(" let mut sc = Statechart{current_state: Default::default()};")
  190. print(" sc.current_state.enter();")
  191. print(" sc.current_state.fire();")
  192. print(" sc.current_state.exit();")
  193. print("}")
  194. print()
  195. # 3. Write transition functions
  196. # 4. Write transition candidate generation function