rust.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. from typing import *
  2. import io
  3. import itertools
  4. from sccd.action_lang.codegen.rust import *
  5. from sccd.statechart.static.tree import *
  6. from sccd.util.visit_tree import *
  7. from sccd.statechart.static.statechart import *
  8. from sccd.statechart.static.globals import *
  9. from sccd.statechart.static import priority
  10. from sccd.util.indenting_writer import *
  11. # Hardcoded limit on number of sub-rounds of combo and big step to detect never-ending superrounds.
  12. # TODO: make this a model parameter, also allowing for +infinity
  13. LIMIT = 1000
  14. # Conversion functions from abstract syntax elements to identifiers in Rust
  15. def snake_case(state: State) -> str:
  16. return state.full_name.replace('/', '_');
  17. def ident_var(state: State) -> str:
  18. if state.full_name == "/":
  19. return "root" # no technical reason, it's just clearer than "s_"
  20. else:
  21. return "s" + snake_case(state)
  22. def ident_type(state: State) -> str:
  23. if state.full_name == "/":
  24. return "Root" # no technical reason, it's just clearer than "State_"
  25. else:
  26. return "State" + snake_case(state)
  27. def ident_enum_variant(state: State) -> str:
  28. # We know the direct children of a state must have unique names relative to each other,
  29. # and enum variants are scoped locally, so we can use the short name here.
  30. # Furthermore, the XML parser asserts that state ids are valid identifiers in Rust.
  31. return "S_" + state.short_name
  32. def ident_field(state: State) -> str:
  33. return "s" + snake_case(state)
  34. def ident_source_target(state: State) -> str:
  35. # drop the first '_' (this is safe, the root state itself can never be source or target)
  36. return snake_case(state)[1:]
  37. def ident_arena_label(state: State) -> str:
  38. if state.full_name == "/":
  39. return "arena_root"
  40. else:
  41. return "arena" + snake_case(state)
  42. def ident_arena_const(state: State) -> str:
  43. if state.full_name == "/":
  44. return "ARENA_ROOT"
  45. else:
  46. return "ARENA" + snake_case(state)
  47. def ident_history_field(state: HistoryState) -> str:
  48. return "history" + snake_case(state.parent) # A history state records history value for its parent
  49. def ident_event_type(event_name: str) -> str:
  50. if event_name[0] == '+':
  51. return "After" + event_name.replace('+', '')
  52. else:
  53. return "Event_" + event_name
  54. def ident_event_enum_variant(event_name: str) -> str:
  55. if event_name[0] == '+':
  56. return "A" + event_name[1:]
  57. else:
  58. return "E_" + event_name
  59. def ident_event_field(event_name: str) -> str:
  60. if event_name[0] == '+':
  61. return "a" + event_name[1:]
  62. else:
  63. return "e_" + event_name
  64. class StatechartRustGenerator(ActionLangRustGenerator):
  65. def __init__(self, w, globals):
  66. super().__init__(w)
  67. self.globals = globals
  68. self.parallel_state_cache = {}
  69. self.tree = None
  70. self.state_stack = []
  71. self.trigger = None
  72. def get_parallel_states(self, state):
  73. try:
  74. return self.parallel_state_cache[state]
  75. except KeyError:
  76. parallel_states = []
  77. while state.parent is not None:
  78. if isinstance(state.parent.type, AndState):
  79. for sibling in state.parent.children:
  80. if sibling is not state:
  81. parallel_states.append(sibling)
  82. state = state.parent
  83. self.parallel_state_cache[state] = parallel_states
  84. return parallel_states
  85. def get_parallel_states_tuple(self, state):
  86. parallel_states = self.get_parallel_states(state)
  87. if len(parallel_states) > 0:
  88. return "(" + ", ".join("*"+ident_var(s) for s in parallel_states) + ", )"
  89. else:
  90. return "()"
  91. def get_parallel_states_tuple_type(self, state):
  92. parallel_states = self.get_parallel_states(state)
  93. if len(parallel_states) > 0:
  94. return "(%s, )" % ", ".join(ident_type(s) for s in parallel_states)
  95. else:
  96. return "()"
  97. def get_parallel_states_pattern_matched(self, state):
  98. parallel_states = self.get_parallel_states(state)
  99. if len(parallel_states) > 0:
  100. return "(%s, )" % ", ".join("%s: %s" % (ident_var(s), ident_type(s)) for s in parallel_states)
  101. else:
  102. return "()"
  103. def visit_InStateMacroExpansion(self, instate):
  104. source = instate.ref.source
  105. target = instate.ref.target
  106. self.w.writeln("{ // macro expansion for @in(\"%s\")" % target.full_name)
  107. self.w.indent()
  108. # Non-exhaustive set of current states, given that 'source' is a current state
  109. parents = self.get_parallel_states(source)
  110. # Deconstruct state configuration tuple
  111. self.w.write("let (")
  112. for parent in parents:
  113. self.w.write("ref ")
  114. self.w.write(ident_var(parent))
  115. self.w.write(", ")
  116. self.w.write(") = ")
  117. self.scope.write_rvalue("@conf", instate.offset, self.w)
  118. self.w.writeln(";")
  119. for parent in parents + [source]:
  120. if is_ancestor(parent=target, child=parent):
  121. # Special case: target is parent of a current state
  122. self.w.writeln("true") # Always a current state
  123. return
  124. if not is_ancestor(parent=parent, child=target):
  125. continue # Skip
  126. # Sequence of states. First is parent. Next is the child of parent on the path to target. Last item is target.
  127. path = list(self.tree.bitmap_to_states((parent.state_id_bitmap | parent.descendants) & (target.ancestors | target.state_id_bitmap)))
  128. def write_path(path, target):
  129. parent = path[0];
  130. if parent is target:
  131. self.w.writeln("true")
  132. else:
  133. child = path[1]
  134. if isinstance(parent.type, OrState):
  135. self.w.writeln("if let %s::%s(ref %s) = %s {" % (ident_type(parent), ident_enum_variant(child), ident_var(child), ident_var(parent)))
  136. self.w.indent()
  137. write_path(path[1:], target)
  138. self.w.dedent()
  139. self.w.writeln("} else { false }")
  140. elif isinstance(parent.type, AndState):
  141. self.w.writeln("let ref %s = %s.%s;" % (ident_var(child), ident_var(parent), ident_field(child)))
  142. write_path(path[1:], target)
  143. else:
  144. raise Exception("The impossible has happened")
  145. write_path(path, target)
  146. self.w.dedent()
  147. self.w.write("}")
  148. def visit_SCCDStateConfiguration(self, type):
  149. self.w.write(self.get_parallel_states_tuple_type(type.state))
  150. def write_event_params(self, event):
  151. for param_eval_func in event.params:
  152. param_eval_func.accept(self) # param eval is a function
  153. self.w.write("(")
  154. if self.trigger is not None:
  155. self.w.write("%s, " % (self.get_parallel_states_tuple(self.state_stack[-1])))
  156. self.write_trigger_params()
  157. self.write_parent_call_params(param_eval_func.scope, skip=0) # call it!
  158. self.w.write("), ")
  159. def write_trigger_params(self):
  160. for e in self.trigger.enabling:
  161. for p in e.params_decl:
  162. self.w.write("*%s, " % p.name)
  163. def visit_RaiseOutputEvent(self, raise_event):
  164. if DEBUG:
  165. self.w.writeln("eprintln!(\"raise out %s\");" % (raise_event.name))
  166. self.w.write("(output)(OutEvent::%s(" % (ident_event_enum_variant(raise_event.name)))
  167. self.write_event_params(raise_event)
  168. self.w.writeln("));")
  169. def visit_RaiseInternalEvent(self, raise_event):
  170. if DEBUG:
  171. self.w.writeln("eprintln!(\"raise internal %s\");" % (raise_event.name))
  172. if self.internal_queue:
  173. self.w.write("sched.set_timeout(%d, InEvent::%s(" % (0, ident_event_enum_variant(raise_event.name)))
  174. self.write_event_params(raise_event)
  175. self.w.writeln("));")
  176. else:
  177. self.w.write("internal.raise().%s = Some(%s(" % (ident_event_field(raise_event.name), (ident_event_type(raise_event.name))))
  178. self.write_event_params(raise_event)
  179. self.w.writeln("));")
  180. def visit_Code(self, a):
  181. self.w.write()
  182. a.block.accept(self) # block is a function
  183. self.w.write("(") # call it...
  184. if self.trigger is not None:
  185. self.w.write("%s, " % self.get_parallel_states_tuple(self.state_stack[-1]))
  186. self.write_trigger_params()
  187. self.write_parent_call_params(a.block.scope, skip=0) # call it!
  188. self.w.writeln(");")
  189. def visit_State(self, state):
  190. self.state_stack.append(state)
  191. # visit children first
  192. for c in state.real_children:
  193. c.accept(self)
  194. # Write 'current state' types
  195. if isinstance(state.type, AndState):
  196. self.w.writeln("// And-state")
  197. # We need Copy for states that will be recorded as history.
  198. self.w.writeln("#[derive(Default, Copy, Clone)]")
  199. self.w.writeln("struct %s {" % ident_type(state))
  200. for child in state.real_children:
  201. self.w.writeln(" %s: %s," % (ident_field(child), ident_type(child)))
  202. self.w.writeln("}")
  203. elif isinstance(state.type, OrState):
  204. self.w.writeln("// Or-state")
  205. self.w.writeln("#[derive(Copy, Clone)]")
  206. self.w.writeln("enum %s {" % ident_type(state))
  207. for child in state.real_children:
  208. self.w.writeln(" %s(%s)," % (ident_enum_variant(child), ident_type(child)))
  209. self.w.writeln("}")
  210. # Write "default" constructor
  211. # We use Rust's Default-trait to record default states,
  212. # this way, constructing a state instance without parameters will initialize it as the default state.
  213. if isinstance(state.type, OrState):
  214. self.w.writeln("impl Default for %s {" % ident_type(state))
  215. self.w.writeln(" fn default() -> Self {")
  216. self.w.writeln(" Self::%s(Default::default())" % (ident_enum_variant(state.type.default_state)))
  217. self.w.writeln(" }")
  218. self.w.writeln("}")
  219. # Implement trait 'State': enter/exit
  220. self.w.writeln("impl %s {" % ident_type(state))
  221. # Enter actions: Executes enter actions of only this state
  222. self.w.writeln(" fn enter_actions<Sched: statechart::Scheduler<InEvent=InEvent>>(timers: &mut Timers<Sched::TimerId>, data: &mut DataModel, internal: &mut InternalLifeline, sched: &mut Sched, output: &mut impl FnMut(OutEvent)) {")
  223. if DEBUG:
  224. self.w.writeln(" eprintln!(\"enter %s\");" % state.full_name);
  225. self.w.writeln(" let mut scope = data;")
  226. self.w.indent(); self.w.indent()
  227. for a in state.enter:
  228. a.accept(self)
  229. self.w.dedent(); self.w.dedent()
  230. for a in state.after_triggers:
  231. self.w.writeln(" timers[%d] = sched.set_timeout(%d, InEvent::%s());" % (a.after_id, a.delay.opt, ident_event_enum_variant(a.enabling[0].name)))
  232. self.w.writeln(" }")
  233. # Enter actions: Executes exit actions of only this state
  234. self.w.writeln(" fn exit_actions<Sched: statechart::Scheduler<InEvent=InEvent>>(timers: &mut Timers<Sched::TimerId>, data: &mut DataModel, internal: &mut InternalLifeline, sched: &mut Sched, output: &mut impl FnMut(OutEvent)) {")
  235. self.w.writeln(" let mut scope = data;")
  236. for a in state.after_triggers:
  237. self.w.writeln(" sched.unset_timeout(&timers[%d]);" % (a.after_id))
  238. self.w.indent(); self.w.indent()
  239. for a in state.exit:
  240. a.accept(self)
  241. if DEBUG:
  242. self.w.writeln(" eprintln!(\"exit %s\");" % state.full_name);
  243. self.w.dedent(); self.w.dedent()
  244. self.w.writeln(" }")
  245. # Enter default: Executes enter actions of entering this state and its default substates, recursively
  246. self.w.writeln(" fn enter_default<Sched: statechart::Scheduler<InEvent=InEvent>>(timers: &mut Timers<Sched::TimerId>, data: &mut DataModel, internal: &mut InternalLifeline, sched: &mut Sched, output: &mut impl FnMut(OutEvent)) {")
  247. self.w.writeln(" %s::enter_actions(timers, data, internal, sched, output);" % (ident_type(state)))
  248. if isinstance(state.type, AndState):
  249. for child in state.real_children:
  250. self.w.writeln(" %s::enter_default(timers, data, internal, sched, output);" % (ident_type(child)))
  251. elif isinstance(state.type, OrState):
  252. self.w.writeln(" %s::enter_default(timers, data, internal, sched, output);" % (ident_type(state.type.default_state)))
  253. self.w.writeln(" }")
  254. # Exit current: Executes exit actions of this state and current children, recursively
  255. self.w.writeln(" fn exit_current<Sched: statechart::Scheduler<InEvent=InEvent>>(&self, timers: &mut Timers<Sched::TimerId>, data: &mut DataModel, internal: &mut InternalLifeline, sched: &mut Sched, output: &mut impl FnMut(OutEvent)) {")
  256. # first, children (recursion):
  257. if isinstance(state.type, AndState):
  258. for child in state.real_children:
  259. self.w.writeln(" self.%s.exit_current(timers, data, internal, sched, output);" % (ident_field(child)))
  260. elif isinstance(state.type, OrState):
  261. self.w.writeln(" match self {")
  262. for child in state.real_children:
  263. self.w.writeln(" Self::%s(s) => { s.exit_current(timers, data, internal, sched, output); }," % (ident_enum_variant(child)))
  264. self.w.writeln(" }")
  265. # then, parent:
  266. self.w.writeln(" %s::exit_actions(timers, data, internal, sched, output);" % (ident_type(state)))
  267. self.w.writeln(" }")
  268. # Exit current: Executes enter actions of this state and current children, recursively
  269. self.w.writeln(" fn enter_current<Sched: statechart::Scheduler<InEvent=InEvent>>(&self, timers: &mut Timers<Sched::TimerId>, data: &mut DataModel, internal: &mut InternalLifeline, sched: &mut Sched, output: &mut impl FnMut(OutEvent)) {")
  270. # first, parent:
  271. self.w.writeln(" %s::enter_actions(timers, data, internal, sched, output);" % (ident_type(state)))
  272. # then, children (recursion):
  273. if isinstance(state.type, AndState):
  274. for child in state.real_children:
  275. self.w.writeln(" self.%s.enter_current(timers, data, internal, sched, output);" % (ident_field(child)))
  276. elif isinstance(state.type, OrState):
  277. self.w.writeln(" match self {")
  278. for child in state.real_children:
  279. self.w.writeln(" Self::%s(s) => { s.enter_current(timers, data, internal, sched, output); }," % (ident_enum_variant(child)))
  280. self.w.writeln(" }")
  281. self.w.writeln(" }")
  282. self.w.writeln("}")
  283. self.w.writeln()
  284. self.state_stack.pop()
  285. def visit_Statechart(self, sc):
  286. if sc.semantics.concurrency == Concurrency.MANY:
  287. raise UnsupportedFeature("concurrency")
  288. if sc.semantics.enabledness_memory_protocol != MemoryProtocol.SMALL_STEP or sc.semantics.assignment_memory_protocol != MemoryProtocol.SMALL_STEP:
  289. raise UnsupportedFeature("Memory protocol semantics")
  290. self.scope.push(sc.scope)
  291. self.w.writeln("use std::ops::Deref;")
  292. self.w.writeln("use std::ops::DerefMut;")
  293. self.w.writeln();
  294. self.w.writeln("use sccd::action_lang;")
  295. self.w.writeln("use sccd::inherit_struct;")
  296. self.w.writeln("use sccd::call_closure;")
  297. self.w.writeln("use sccd::statechart;")
  298. self.w.writeln("use sccd::statechart::EventLifeline;")
  299. self.w.writeln();
  300. priority_ordered_transitions = priority.priority_and_concurrency(sc) # may raise error
  301. tree = sc.tree
  302. self.tree = tree
  303. self.w.writeln("type Timers<TimerId> = [TimerId; %d];" % tree.timer_count)
  304. self.w.writeln()
  305. self.internal_queue = sc.semantics.internal_event_lifeline == InternalEventLifeline.QUEUE
  306. # Write event types
  307. if self.internal_queue:
  308. input_events = {key: val for key,val in itertools.chain(sc.in_events.items(), sc.internal_events.items())}
  309. internal_events = {}
  310. else:
  311. input_events = sc.in_events
  312. internal_events = sc.internal_events
  313. internal_same_round = (
  314. sc.semantics.internal_event_lifeline == InternalEventLifeline.REMAINDER or
  315. sc.semantics.internal_event_lifeline == InternalEventLifeline.SAME)
  316. self.w.writeln("// Input Events")
  317. self.w.writeln("#[cfg_attr(target_arch = \"wasm32\", wasm_bindgen)]")
  318. self.w.writeln("#[derive(Copy, Clone, Debug)]")
  319. self.w.writeln("pub enum InEvent {")
  320. for event_name in input_events:
  321. self.w.write(" %s(" % (ident_event_enum_variant(event_name)))
  322. for param_type in input_events[event_name]:
  323. param_type.accept(self)
  324. self.w.write(", ")
  325. self.w.writeln("),")
  326. self.w.writeln("}")
  327. self.w.writeln()
  328. self.w.writeln("// Internal Events")
  329. for event_name in internal_events:
  330. self.w.write("struct %s(" % ident_event_type(event_name))
  331. for param_type in internal_events[event_name]:
  332. param_type.accept(self)
  333. self.w.write(", ")
  334. self.w.writeln(");")
  335. # Implement internal events as a set
  336. self.w.writeln("#[derive(Default)]")
  337. self.w.writeln("struct Internal {")
  338. for event_name in internal_events:
  339. self.w.writeln(" %s: Option<%s>," % (ident_event_field(event_name), ident_event_type(event_name)))
  340. self.w.writeln("}")
  341. if self.internal_queue:
  342. # Treat internal events like input events
  343. self.w.writeln("type InternalLifeline = ();") # Unit type
  344. else:
  345. if internal_same_round:
  346. self.w.writeln("type InternalLifeline = statechart::SameRoundLifeline<Internal>;")
  347. else:
  348. self.w.writeln("type InternalLifeline = statechart::NextRoundLifeline<Internal>;")
  349. self.w.writeln()
  350. # Output events
  351. # output_event_names = self.globals.out_events.names
  352. self.w.writeln("// Output Events")
  353. self.w.writeln("#[cfg_attr(target_arch = \"wasm32\", wasm_bindgen)]")
  354. self.w.writeln("#[derive(Copy, Clone, Debug, PartialEq, Eq)]")
  355. self.w.writeln("pub enum OutEvent {")
  356. for event_name in sc.out_events:
  357. self.w.write(" %s(" % (ident_event_enum_variant(event_name)))
  358. for param_type in sc.out_events[event_name]:
  359. param_type.accept(self)
  360. self.w.write(", ")
  361. self.w.writeln("),")
  362. self.w.writeln("}")
  363. self.w.writeln()
  364. syntactic_maximality = (
  365. sc.semantics.big_step_maximality == Maximality.SYNTACTIC
  366. or sc.semantics.combo_step_maximality == Maximality.SYNTACTIC)
  367. # Write arena type
  368. arenas = {}
  369. for t in tree.transition_list:
  370. arenas.setdefault(t.arena, 2**len(arenas))
  371. for arena, bm in arenas.items():
  372. for d in tree.bitmap_to_states(arena.descendants):
  373. bm |= arenas.get(d, 0)
  374. arenas[arena] = bm
  375. self.w.writeln("// Transition arenas (bitmap type)")
  376. for size, typ in [(8, 'u8'), (16, 'u16'), (32, 'u32'), (64, 'u64'), (128, 'u128')]:
  377. if len(arenas) + 1 <= size:
  378. self.w.writeln("type Arenas = %s;" % typ)
  379. break
  380. else:
  381. raise UnsupportedFeature("Too many arenas! Cannot fit into an unsigned int.")
  382. self.w.writeln("const ARENA_NONE: Arenas = 0;")
  383. for arena, bm in arenas.items():
  384. self.w.writeln("const %s: Arenas = %s;" % (ident_arena_const(arena), bin(bm)))
  385. self.w.writeln("const ARENA_UNSTABLE: Arenas = %s; // indicates any transition fired with an unstable target" % bin(2**len(arenas.items())))
  386. self.w.writeln()
  387. # Write statechart type
  388. self.w.writeln("impl<Sched: statechart::Scheduler> Default for Statechart<Sched> {")
  389. self.w.writeln(" fn default() -> Self {")
  390. self.w.writeln(" // Initialize data model")
  391. self.w.writeln(" let scope = action_lang::Empty{};")
  392. self.w.indent(); self.w.indent();
  393. if sc.datamodel is not None:
  394. sc.datamodel.accept(self)
  395. datamodel_type = self.scope.commit(sc.scope.size(), self.w)
  396. self.w.dedent(); self.w.dedent();
  397. self.w.writeln(" Self {")
  398. self.w.writeln(" configuration: Default::default(),")
  399. for h in tree.history_states:
  400. self.w.writeln(" %s: Default::default()," % (ident_history_field(h)))
  401. self.w.writeln(" timers: Default::default(),")
  402. self.w.writeln(" data: scope,")
  403. self.w.writeln(" }")
  404. self.w.writeln(" }")
  405. self.w.writeln("}")
  406. self.w.writeln("type DataModel = %s;" % datamodel_type)
  407. self.w.writeln("pub struct Statechart<Sched: statechart::Scheduler> {")
  408. self.w.writeln(" configuration: %s," % ident_type(tree.root))
  409. # We always store a history value as 'deep' (also for shallow history).
  410. # TODO: We may save a tiny bit of space in some rare cases by storing shallow history as only the exited child of the Or-state.
  411. for h in tree.history_states:
  412. self.w.writeln(" %s: %s," % (ident_history_field(h), ident_type(h.parent)))
  413. self.w.writeln(" timers: Timers<Sched::TimerId>,")
  414. self.w.writeln(" data: DataModel,")
  415. self.w.writeln("}")
  416. self.w.writeln()
  417. # Function fair_step: a single "Take One" Maximality 'round' (= nonoverlapping arenas allowed to fire 1 transition)
  418. self.w.writeln("fn fair_step<Sched: statechart::Scheduler<InEvent=InEvent>>(sc: &mut Statechart<Sched>, input: &mut Option<InEvent>, internal: &mut InternalLifeline, sched: &mut Sched, output: &mut impl FnMut(OutEvent), dirty: Arenas) -> Arenas {")
  419. self.w.writeln(" let mut fired: Arenas = ARENA_NONE;")
  420. self.w.writeln(" let mut scope = &mut sc.data;")
  421. self.w.writeln(" let %s = &mut sc.configuration;" % ident_var(tree.root))
  422. self.w.indent()
  423. transitions_written = []
  424. def write_transitions(state: State):
  425. self.state_stack.append(state)
  426. # Many of the states to exit can be computed statically (i.e. they are always the same)
  427. # The one we cannot compute statically are:
  428. #
  429. # (1) The descendants of S2, S3, etc. if S1 is part of the "exit path":
  430. #
  431. # A ---> And-state on exit path
  432. # / \ \
  433. # S1 S2 S3 ...
  434. #
  435. # |
  436. # +--> S1 also on exit path
  437. #
  438. # (2) The descendants of S, if S is the transition target
  439. #
  440. # The same applies to entering states.
  441. # Writes statements that perform exit actions
  442. # in the correct order (children (last to first), then parent) for given 'exit path'.
  443. def write_exit(exit_path: List[State]):
  444. if len(exit_path) > 0:
  445. s = exit_path[0] # state to exit
  446. if len(exit_path) == 1:
  447. # Exit s:
  448. self.w.writeln("%s.exit_current(&mut sc.timers, scope, internal, sched, output);" % (ident_var(s)))
  449. else:
  450. # Exit children:
  451. if isinstance(s.type, AndState):
  452. for c in reversed(s.children):
  453. if exit_path[1] is c:
  454. write_exit(exit_path[1:]) # continue recursively
  455. else:
  456. self.w.writeln("%s.exit_current(&mut sc.timers, scope, internal, sched, output);" % (ident_var(c)))
  457. elif isinstance(s.type, OrState):
  458. write_exit(exit_path[1:]) # continue recursively with the next child on the exit path
  459. # Exit s:
  460. self.w.writeln("%s::exit_actions(&mut sc.timers, scope, internal, sched, output);" % (ident_type(s)))
  461. # Store history
  462. if s.deep_history:
  463. _, _, h = s.deep_history
  464. self.w.writeln("sc.%s = *%s; // Store deep history" % (ident_history_field(h), ident_var(s)))
  465. if s.shallow_history:
  466. _, h = s.shallow_history
  467. if isinstance(s.type, AndState):
  468. raise Exception("Shallow history makes no sense for And-state!")
  469. # Or-state:
  470. self.w.writeln("sc.%s = match %s { // Store shallow history" % (ident_history_field(h), ident_var(s)))
  471. for c in s.real_children:
  472. self.w.writeln(" %s::%s(_) => %s::%s(%s::default())," % (ident_type(s), ident_enum_variant(c), ident_type(s), ident_enum_variant(c), ident_type(c)))
  473. self.w.writeln("};")
  474. # Writes statements that perform enter actions
  475. # in the correct order (parent, children (first to last)) for given 'enter path'.
  476. def write_enter(enter_path: List[State]):
  477. if len(enter_path) > 0:
  478. s = enter_path[0] # state to enter
  479. if len(enter_path) == 1:
  480. # Target state.
  481. if isinstance(s, HistoryState):
  482. self.w.writeln("sc.%s.enter_current(&mut sc.timers, scope, internal, sched, output); // Enter actions for history state" %(ident_history_field(s)))
  483. else:
  484. self.w.writeln("%s::enter_default(&mut sc.timers, scope, internal, sched, output);" % (ident_type(s)))
  485. else:
  486. # Enter s:
  487. self.w.writeln("%s::enter_actions(&mut sc.timers, scope, internal, sched, output);" % (ident_type(s)))
  488. # Enter children:
  489. if isinstance(s.type, AndState):
  490. for c in s.children:
  491. if enter_path[1] is c:
  492. write_enter(enter_path[1:]) # continue recursively
  493. else:
  494. self.w.writeln("%s::enter_default(&mut sc.timers, scope, internal, sched, output);" % (ident_type(c)))
  495. elif isinstance(s.type, OrState):
  496. if len(s.children) > 0:
  497. write_enter(enter_path[1:]) # continue recursively with the next child on the enter path
  498. else:
  499. # If the following occurs, there's a bug in this source file
  500. raise Exception("Basic state in the middle of enter path")
  501. # The 'state' of a state is just a value in our compiled code.
  502. # When executing a transition, the value of the transition's arena changes.
  503. # This function writes statements that build a new value that can be assigned to the arena.
  504. def write_new_configuration(enter_path: List[State]):
  505. if len(enter_path) > 0:
  506. s = enter_path[0]
  507. if len(enter_path) == 1:
  508. # Construct target state.
  509. # And/Or/Basic state: Just construct the default value:
  510. self.w.writeln("let new_%s: %s = Default::default();" % (ident_var(s), ident_type(s)))
  511. else:
  512. next_child = enter_path[1]
  513. if isinstance(next_child, HistoryState):
  514. # No recursion
  515. self.w.writeln("let new_%s = sc.%s; // Restore history value" % (ident_var(s), ident_history_field(next_child)))
  516. else:
  517. if isinstance(s.type, AndState):
  518. for c in s.children:
  519. if next_child is c:
  520. write_new_configuration(enter_path[1:]) # recurse
  521. else:
  522. # Other children's default states are constructed
  523. self.w.writeln("let new_%s: %s = Default::default();" % (ident_var(c), ident_type(c)))
  524. # Construct struct
  525. self.w.writeln("let new_%s = %s{%s:new_%s, ..Default::default()};" % (ident_var(s), ident_type(s), ident_field(next_child), ident_var(next_child)))
  526. elif isinstance(s.type, OrState):
  527. if len(s.children) > 0:
  528. # Or-state
  529. write_new_configuration(enter_path[1:]) # recurse
  530. # Construct enum value
  531. self.w.writeln("let new_%s = %s::%s(new_%s);" % (ident_var(s), ident_type(s), ident_enum_variant(next_child), ident_var(next_child)))
  532. else:
  533. # If the following occurs, there's a bug in this source file
  534. raise Exception("Basic state in the middle of enter path")
  535. def parent():
  536. for i, t in enumerate(state.transitions):
  537. self.trigger = t.trigger
  538. self.w.writeln("// Outgoing transition %d" % i)
  539. # If a transition with an overlapping arena that is an ancestor of ours, we wouldn't arrive here because of the "break 'arena_label" statements.
  540. # However, an overlapping arena that is a descendant of ours will not have been detected.
  541. # Therefore, we must add an addition check in some cases:
  542. arenas_to_check = set()
  543. for earlier in transitions_written:
  544. if is_ancestor(parent=t.arena, child=earlier.arena):
  545. arenas_to_check.add(t.arena)
  546. if len(arenas_to_check) > 0:
  547. self.w.writeln("// A transition may have fired earlier that overlaps with our arena:")
  548. self.w.writeln("if fired & (%s) == ARENA_NONE {" % " | ".join(ident_arena_const(a) for a in arenas_to_check))
  549. self.w.indent()
  550. if t.trigger is not EMPTY_TRIGGER:
  551. condition = []
  552. for e in t.trigger.enabling:
  553. if e.name in input_events:
  554. condition.append("let Some(InEvent::%s(%s)) = &input" % (ident_event_enum_variant(e.name), ", ".join(p.name for p in e.params_decl)))
  555. elif e.name in internal_events:
  556. condition.append("let Some(%s(%s)) = &internal.current().%s" % (ident_event_type(e.name), ", ".join(p.name for p in e.params_decl), ident_event_field(e.name)))
  557. else:
  558. print(e.name)
  559. print(input_events)
  560. print(internal_events)
  561. raise Exception("Illegal event ID - Bug in SCCD :(")
  562. self.w.writeln("if %s {" % " && ".join(condition))
  563. self.w.indent()
  564. if t.guard is not None:
  565. self.w.write("if ")
  566. t.guard.accept(self) # guard is a function...
  567. self.w.write("(") # call it!
  568. self.w.write(self.get_parallel_states_tuple(t.source))
  569. self.w.write(", ")
  570. self.write_trigger_params()
  571. self.write_parent_call_params(t.guard.scope)
  572. self.w.write(")")
  573. self.w.writeln(" {")
  574. self.w.indent()
  575. # 1. Execute transition's actions
  576. # Path from arena to source, including source but not including arena
  577. exit_path_bm = t.arena.descendants & (t.source.state_id_bitmap | t.source.ancestors) # bitmap
  578. exit_path = list(tree.bitmap_to_states(exit_path_bm)) # list of states
  579. # Path from arena to target, including target but not including arena
  580. enter_path_bm = t.arena.descendants & (t.target.state_id_bitmap | t.target.ancestors) # bitmap
  581. enter_path = list(tree.bitmap_to_states(enter_path_bm)) # list of states
  582. if DEBUG:
  583. self.w.writeln("eprintln!(\"fire %s\");" % str(t))
  584. self.w.writeln("// Exit actions")
  585. write_exit(exit_path)
  586. if len(t.actions) > 0:
  587. self.w.writeln("// Transition's actions")
  588. for a in t.actions:
  589. a.accept(self)
  590. self.w.writeln("// Enter actions")
  591. write_enter(enter_path)
  592. # 2. Update state
  593. # A state configuration is just a value
  594. self.w.writeln("// Build new state configuration")
  595. write_new_configuration([t.arena] + enter_path)
  596. self.w.writeln("// Update arena configuration")
  597. self.w.writeln("*%s = new_%s;" % (ident_var(t.arena), ident_var(t.arena)))
  598. if not syntactic_maximality or t.target.stable:
  599. self.w.writeln("fired |= %s; // Stable target" % ident_arena_const(t.arena))
  600. else:
  601. self.w.writeln("fired |= ARENA_UNSTABLE; // Unstable target")
  602. if sc.semantics.input_event_lifeline == InputEventLifeline.FIRST_SMALL_STEP:
  603. self.w.writeln("// Input Event Lifeline: First Small Step")
  604. self.w.writeln("*input = Option::None;")
  605. if sc.semantics.internal_event_lifeline == InternalEventLifeline.NEXT_SMALL_STEP:
  606. self.w.writeln("// Internal Event Lifeline: Next Small Step")
  607. self.w.writeln("internal.cycle();")
  608. # This arena is done:
  609. self.w.writeln("break '%s;" % (ident_arena_label(t.arena)))
  610. if t.guard is not None:
  611. self.w.dedent()
  612. self.w.writeln("}")
  613. if t.trigger is not EMPTY_TRIGGER:
  614. self.w.dedent()
  615. self.w.writeln("}")
  616. if len(arenas_to_check) > 0:
  617. self.w.dedent()
  618. self.w.writeln("}")
  619. transitions_written.append(t)
  620. self.trigger = None
  621. def child():
  622. # Here is were we recurse and write the transition code for the children of our 'state'.
  623. if isinstance(state.type, AndState):
  624. for child in state.real_children:
  625. self.w.writeln("let %s = &mut %s.%s;" % (ident_var(child), ident_var(state), ident_field(child)))
  626. for child in state.real_children:
  627. self.w.writeln("// Orthogonal region")
  628. write_transitions(child)
  629. elif isinstance(state.type, OrState):
  630. if state.type.default_state is not None:
  631. if state in arenas:
  632. self.w.writeln("if (fired | dirty) & %s == ARENA_NONE {" % ident_arena_const(state))
  633. self.w.indent()
  634. self.w.writeln("'%s: loop {" % ident_arena_label(state))
  635. self.w.indent()
  636. self.w.writeln("match *%s {" % ident_var(state))
  637. for child in state.real_children:
  638. self.w.indent()
  639. self.w.writeln("%s::%s(ref mut %s) => {" % (ident_type(state), ident_enum_variant(child), ident_var(child)))
  640. self.w.indent()
  641. write_transitions(child)
  642. self.w.dedent()
  643. self.w.writeln("},")
  644. self.w.dedent()
  645. self.w.writeln("};")
  646. self.w.writeln("break;")
  647. self.w.dedent()
  648. self.w.writeln("}")
  649. if state in arenas:
  650. self.w.dedent()
  651. self.w.writeln("}")
  652. if sc.semantics.hierarchical_priority == HierarchicalPriority.SOURCE_PARENT:
  653. parent()
  654. child()
  655. elif sc.semantics.hierarchical_priority == HierarchicalPriority.SOURCE_CHILD:
  656. child()
  657. parent()
  658. elif sc.semantics.hierarchical_priority == HierarchicalPriority.NONE:
  659. # We're free to pick any semantics here, but let's not go too wild
  660. parent()
  661. child()
  662. else:
  663. raise UnsupportedFeature("Priority semantics %s" % sc.semantics.hierarchical_priority)
  664. self.state_stack.pop()
  665. write_transitions(tree.root)
  666. self.w.dedent()
  667. if DEBUG:
  668. self.w.writeln(" eprintln!(\"completed fair_step\");")
  669. self.w.writeln(" fired")
  670. self.w.writeln("}")
  671. # Write combo step and big step function
  672. def write_stepping_function(name: str, title: str, maximality: Maximality, substep: str, cycle_input: bool, cycle_internal: bool):
  673. self.w.writeln("fn %s<Sched: statechart::Scheduler<InEvent=InEvent>>(sc: &mut Statechart<Sched>, input: &mut Option<InEvent>, internal: &mut InternalLifeline, sched: &mut Sched, output: &mut impl FnMut(OutEvent), dirty: Arenas) -> Arenas {" % (name))
  674. self.w.writeln(" // %s Maximality: %s" % (title, maximality))
  675. if maximality == Maximality.TAKE_ONE:
  676. self.w.writeln(" %s(sc, input, internal, sched, output, dirty)" % (substep))
  677. else:
  678. self.w.writeln(" let mut fired: Arenas = dirty;")
  679. # self.w.writeln(" let mut e = input;")
  680. self.w.writeln(" let mut ctr: u16 = 0;")
  681. self.w.writeln(" loop {")
  682. if maximality == Maximality.TAKE_MANY:
  683. self.w.writeln(" let just_fired = %s(sc, input, internal, sched, output, ARENA_NONE);" % (substep))
  684. elif maximality == Maximality.SYNTACTIC:
  685. self.w.writeln(" let just_fired = %s(sc, input, internal, sched, output, fired);" % (substep))
  686. self.w.writeln(" if just_fired == ARENA_NONE { // did any transition fire? (incl. unstable)")
  687. self.w.writeln(" break;")
  688. self.w.writeln(" }")
  689. self.w.writeln(" ctr += 1;")
  690. self.w.writeln(" assert_ne!(ctr, %d, \"too many steps (limit reached)\");" % LIMIT)
  691. self.w.writeln(" fired |= just_fired;")
  692. if cycle_input:
  693. self.w.writeln(" // Input Event Lifeline: %s" % sc.semantics.input_event_lifeline)
  694. self.w.writeln(" *input = Option::None;")
  695. if cycle_internal:
  696. self.w.writeln(" // Internal Event Lifeline: %s" % sc.semantics.internal_event_lifeline)
  697. self.w.writeln(" internal.cycle();")
  698. self.w.writeln(" }")
  699. if DEBUG:
  700. self.w.writeln(" eprintln!(\"completed %s\");" % name)
  701. self.w.writeln(" fired")
  702. self.w.writeln("}")
  703. write_stepping_function("combo_step", "Combo-Step",
  704. maximality = sc.semantics.combo_step_maximality,
  705. substep = "fair_step",
  706. cycle_input = False,
  707. cycle_internal = False)
  708. write_stepping_function("big_step", "Big-Step",
  709. maximality = sc.semantics.big_step_maximality,
  710. substep = "combo_step",
  711. cycle_input = sc.semantics.input_event_lifeline == InputEventLifeline.FIRST_COMBO_STEP,
  712. cycle_internal = sc.semantics.internal_event_lifeline == InternalEventLifeline.NEXT_COMBO_STEP)
  713. self.w.writeln()
  714. # Implement 'SC' trait
  715. self.w.writeln("impl<Sched: statechart::Scheduler<InEvent=InEvent>> statechart::SC for Statechart<Sched> {")
  716. self.w.writeln(" type InEvent = InEvent;")
  717. self.w.writeln(" type OutEvent = OutEvent;")
  718. self.w.writeln(" type Sched = Sched;")
  719. self.w.writeln()
  720. self.w.writeln(" fn init(&mut self, sched: &mut Self::Sched, output: &mut impl FnMut(Self::OutEvent)) {")
  721. self.w.writeln(" %s::enter_default(&mut self.timers, &mut self.data, &mut Default::default(), sched, output)" % (ident_type(tree.root)))
  722. self.w.writeln(" }")
  723. self.w.writeln(" fn big_step(&mut self, mut input: Option<InEvent>, sched: &mut Self::Sched, output: &mut impl FnMut(Self::OutEvent)) {")
  724. self.w.writeln(" let mut internal: InternalLifeline = Default::default();")
  725. self.w.writeln(" big_step(self, &mut input, &mut internal, sched, output, ARENA_NONE);")
  726. self.w.writeln(" }")
  727. self.w.writeln("}")
  728. self.w.writeln()
  729. # Write state types
  730. tree.root.accept(self)
  731. self.write_decls()
  732. if DEBUG:
  733. self.w.writeln("use std::mem::size_of;")
  734. self.w.writeln("fn debug_print_sizes<Sched: statechart::Scheduler>() {")
  735. self.w.writeln(" eprintln!(\"------------------------\");")
  736. self.w.writeln(" eprintln!(\"Semantics: %s\");" % sc.semantics)
  737. self.w.writeln(" eprintln!(\"------------------------\");")
  738. self.w.writeln(" eprintln!(\"info: Statechart: {} bytes\", size_of::<Statechart<Sched>>());")
  739. self.w.writeln(" eprintln!(\"info: DataModel: {} bytes\", size_of::<DataModel>());")
  740. self.w.writeln(" eprintln!(\"info: Timers: {} bytes\", size_of::<Timers<Sched::TimerId>>());")
  741. self.w.writeln(" eprintln!(\"info: History: {} bytes\", %s);" % " + ".join(["0"] + list("size_of::<%s>()" % ident_type(h.parent) for h in tree.history_states)))
  742. def write_state_size(state, indent=0):
  743. self.w.writeln(" eprintln!(\"info: %sState %s: {} bytes\", size_of::<%s>());" % (" "*indent, state.full_name, ident_type(state)))
  744. for child in state.real_children:
  745. write_state_size(child, indent+1)
  746. write_state_size(tree.root)
  747. self.w.writeln(" eprintln!(\"info: InEvent: {} bytes\", size_of::<InEvent>());")
  748. self.w.writeln(" eprintln!(\"info: OutEvent: {} bytes\", size_of::<OutEvent>());")
  749. self.w.writeln(" eprintln!(\"info: Arenas: {} bytes\", size_of::<Arenas>());")
  750. self.w.writeln(" eprintln!(\"------------------------\");")
  751. self.w.writeln("}")
  752. self.w.writeln()
  753. self.tree = None