rust.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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_var(state: State) -> str:
  8. if state.opt.full_name == "/":
  9. return "root" # no technical reason, it's just clearer than "s_"
  10. else:
  11. return "s" + snake_case(state)
  12. def ident_type(state: State) -> str:
  13. if state.opt.full_name == "/":
  14. return "Root" # no technical reason, it's just clearer than "State_"
  15. else:
  16. return "State" + snake_case(state)
  17. def ident_enum_variant(state: State) -> str:
  18. # We know the direct children of a state must have unique names relative to each other,
  19. # and enum variants are scoped locally, so we can use the short name here:
  20. return "S_" + state.short_name
  21. def ident_field(state: State) -> str:
  22. return "s" + snake_case(state)
  23. def ident_source_target(state: State) -> str:
  24. # drop the first '_' (this is safe, the root state itself can never be source or target)
  25. return snake_case(state)[1:]
  26. def ident_transition(t: Transition) -> str:
  27. return "transition%d_FROM_%s_TO_%s" % (t.opt.id, ident_source_target(t.source), ident_source_target(t.target))
  28. def compile_to_rust(tree: StateTree):
  29. # Write 'current state' types
  30. def write_state_type(state: State, children: List[State]):
  31. if isinstance(state, HistoryState):
  32. return None # we got no time for pseudo-states!
  33. def as_struct():
  34. print("#[allow(non_camel_case_types)]")
  35. print("struct %s {" % ident_type(state))
  36. for child in children:
  37. print(" %s: %s," % (ident_field(child), ident_type(child)))
  38. print("}")
  39. def as_enum():
  40. print("#[allow(non_camel_case_types)]")
  41. print("enum %s {" % ident_type(state))
  42. for child in children:
  43. print(" %s(%s)," % (ident_enum_variant(child), ident_type(child)))
  44. print("}")
  45. if isinstance(state, ParallelState):
  46. print("// And-state")
  47. as_struct()
  48. elif isinstance(state, State):
  49. if len(state.children) > 0:
  50. print("// Or-state")
  51. as_enum() # Or-state
  52. else:
  53. # Basic state: write as empty struct
  54. #
  55. # An empty struct in Rust is a type with one possible value.
  56. # An empty struct is a Zero-Sized Type.
  57. #
  58. # An empty enum is also a valid type in Rust, but no instances
  59. # of it can be created. Also called an "uninhabited type".
  60. print("// Basic state")
  61. as_struct()
  62. # The above if-else construction hints at the fact that we would have
  63. # better used empty And-states to model basic states, instead of empty Or-states...
  64. print()
  65. return state
  66. # Write "enter/exit state" functions
  67. # This fragment should be moved to a library:
  68. print("pub trait State {")
  69. print(" fn enter_actions();")
  70. print(" fn exit_actions();")
  71. print(" fn enter_default();")
  72. print("}")
  73. print()
  74. def write_enter_exit(state: State, children: List[State]):
  75. if isinstance(state, HistoryState):
  76. return None # we got no time for pseudo-states!
  77. print("impl State for %s {" % ident_type(state))
  78. print(" fn enter_actions() {")
  79. print(" // TODO: execute enter actions")
  80. print(" println!(\"enter %s\");" % state.opt.full_name);
  81. print(" }")
  82. print(" fn exit_actions() {")
  83. print(" // TODO: execute exit actions")
  84. print(" println!(\"exit %s\");" % state.opt.full_name);
  85. print(" }")
  86. print(" fn enter_default() {")
  87. print(" %s::enter_actions();" % ident_type(state))
  88. for child in children:
  89. print(" %s::enter_default();" % ident_type(child))
  90. print(" }")
  91. print("}")
  92. print()
  93. return state
  94. # Write "enter default state" functions
  95. def write_enter_default(state: State, children: List[State]):
  96. if isinstance(state, HistoryState):
  97. return None # we got no time for pseudo-states!
  98. # We use Rust's Default-trait to record default states,
  99. # this way, constructing a state instance without parameters will initialize it as the default state.
  100. print("impl Default for %s {" % ident_type(state))
  101. print(" fn default() -> Self {")
  102. if isinstance(state, ParallelState):
  103. print(" return Self {")
  104. for child in children:
  105. print(" %s: Default::default()," % (ident_field(child)))
  106. print(" };")
  107. elif isinstance(state, State):
  108. if state.default_state is not None:
  109. # Or-state
  110. print(" return Self::%s(Default::default());" % (ident_enum_variant(state.default_state)))
  111. else:
  112. # Basic state
  113. print(" return Self{};")
  114. print(" }")
  115. print("}")
  116. print()
  117. return state
  118. visit_tree(tree.root, lambda s: s.children,
  119. child_first=[
  120. write_state_type,
  121. write_enter_exit,
  122. write_enter_default,
  123. ])
  124. # Write statechart type
  125. print("pub struct Statechart {")
  126. print(" current_state: %s," % ident_type(tree.root))
  127. print(" // TODO: history values")
  128. print(" // TODO: timers")
  129. print("}")
  130. print()
  131. class IndentingWriter:
  132. def __init__(self, spaces = 0):
  133. self.spaces = spaces
  134. def indent(self):
  135. self.spaces += 2
  136. def dedent(self):
  137. self.spaces -= 2
  138. def print(self, str):
  139. print(' '*self.spaces + str)
  140. print("impl Statechart {")
  141. print(" fn big_step(&mut self) {")
  142. print(" let %s = &mut self.current_state;" % ident_var(tree.root))
  143. w = IndentingWriter(4)
  144. def write_transitions(state: State):
  145. if isinstance(state, HistoryState):
  146. return None # we got no time for pseudo-states!
  147. # Many of the states to exit can be computed statically (i.e. they are always the same)
  148. # The one we cannot compute statically are:
  149. #
  150. # (1) The descendants of S2, S3, etc. if S1 is part of the "exit path":
  151. #
  152. # A ---> And-state
  153. # / \ \
  154. # S1 S2 S3 ...
  155. #
  156. # (2) The descendants of S, if S is the transition target
  157. def write_exit(exit_path: List[State]):
  158. if len(exit_path) == 0:
  159. w.print("%s.exit();" % ident_var(s))
  160. else:
  161. s = exit_path[0]
  162. if isinstance(s, HistoryState):
  163. raise Exception("Can't deal with history yet!")
  164. elif isinstance(s, ParallelState):
  165. for c in reversed(s.children):
  166. if exit_path[1] is c:
  167. write_exit(exit_path[1:]) # continue recursively
  168. else:
  169. w.print("%s.exit();" % ident_var(c))
  170. elif isinstance(s, State):
  171. if s.default_state is not None:
  172. # Or-state
  173. write_exit(exit_path[1:]) # continue recursively with the next child on the exit path
  174. w.print("%s::exit_actions();" % ident_type(s))
  175. def write_new_configuration(enter_path: List[State]):
  176. if len(enter_path) > 0:
  177. s = enter_path[0]
  178. if len(enter_path) == 1:
  179. # Construct target state.
  180. # Whatever the type of parent (And/Or/Basic), just construct the default value:
  181. w.print("let new_%s: %s = Default::default();" % (ident_var(s), ident_type(s)))
  182. else:
  183. if isinstance(s, ParallelState):
  184. for c in s.children:
  185. if enter_path[1] is c:
  186. write_new_configuration(enter_path[1:]) # recurse
  187. else:
  188. # Other children's default states are constructed
  189. w.print("let new_%s: %s = Default::default();" % (ident_var(c), ident_type(c)))
  190. # Construct struct
  191. w.print("let new_%s = %s{%s:%s, ..Default::default()};" % (ident_var(s), ident_type(s), ident_field(enter_path[1]), ident_var(enter_path[1])))
  192. elif isinstance(s, State):
  193. if len(s.children) > 0:
  194. # Or-state
  195. write_new_configuration(enter_path[1:]) # recurse
  196. w.print("let new_%s = %s::%s(new_%s);" % (ident_var(s), ident_type(s), ident_enum_variant(enter_path[1]), ident_var(enter_path[1])))
  197. else:
  198. # The following should never occur
  199. # The parser should have rejected the model before we even get here
  200. raise Exception("Basic state in the middle of enter path")
  201. def write_enter(enter_path: List[State]):
  202. if len(enter_path) > 0:
  203. s = enter_path[0]
  204. if len(enter_path) == 1:
  205. # Target state.
  206. w.print("%s::enter_default();" % ident_type(s))
  207. else:
  208. if isinstance(s, ParallelState):
  209. for c in s.children:
  210. if enter_path[1] is c:
  211. w.print("%s::enter_actions();" % ident_type(c))
  212. write_enter(enter_path[1:]) # continue recursively
  213. else:
  214. w.print("%s::enter_default();" % ident_type(c))
  215. elif isinstance(s, State):
  216. if len(s.children) > 0:
  217. # Or-state
  218. w.print("%s::enter_actions();" & ident_type(s))
  219. write_enter(enter_path[1:]) # continue recursively with the next child on the enter path
  220. else:
  221. # The following should never occur
  222. # The parser should have rejected the model before we even get here
  223. raise Exception("Basic state in the middle of enter path")
  224. def parent():
  225. for t in state.transitions:
  226. w.print("// Outgoing transition")
  227. # TODO: optimize: static calculation for Or-state ancestors of transition's source
  228. # Path from arena to source, including source but not including arena
  229. exit_path_bm = t.opt.arena.opt.descendants & (t.source.opt.state_id_bitmap | t.source.opt.ancestors) # bitmap
  230. exit_path = list(tree.bitmap_to_states(exit_path_bm)) # list of states
  231. # Path from arena to target, including target but not including arena
  232. enter_path_bm = t.opt.arena.opt.descendants & (t.target.opt.state_id_bitmap | t.target.opt.ancestors) # bitmap
  233. enter_path = list(tree.bitmap_to_states(enter_path_bm)) # list of states
  234. w.print("// Exit states")
  235. write_exit(exit_path)
  236. w.print("// TODO: execute transition's actions here")
  237. w.print("println!(\"%s\");" % str(t))
  238. w.print("// Construct new states")
  239. write_new_configuration([t.opt.arena] + enter_path)
  240. w.print("// Enter states")
  241. write_enter(enter_path)
  242. w.print("// Update arena configuration")
  243. w.print("*%s = new_%s;" % (ident_var(t.opt.arena), ident_var(t.opt.arena)))
  244. def child():
  245. if isinstance(state, ParallelState):
  246. for child in state.children:
  247. w.print("// Orthogonal region")
  248. w.print("let %s = &mut %s.%s;" % (ident_var(child), ident_var(state), ident_field(child)))
  249. write_transitions(child)
  250. elif isinstance(state, State):
  251. if state.default_state is not None:
  252. w.print("match %s {" % ident_var(state))
  253. for child in state.children:
  254. w.indent()
  255. w.print("%s::%s(%s) => {" % (ident_type(state), ident_enum_variant(child), ident_var(child)))
  256. w.indent()
  257. write_transitions(child)
  258. w.dedent()
  259. w.print("},")
  260. w.dedent()
  261. w.print("};")
  262. # TODO: This is where parent/child-first semantic variability should be implemented
  263. # For now, it's always "parent first"
  264. parent()
  265. child()
  266. write_transitions(tree.root)
  267. print(" }")
  268. print("}")
  269. print()
  270. # See if it works
  271. print("fn main() {")
  272. print(" let mut sc = Statechart{current_state: Default::default()};")
  273. print(" Root::enter_default();")
  274. print(" sc.big_step();")
  275. print(" sc.big_step();")
  276. # print(" sc.current_state.exit();")
  277. print("}")
  278. print()