rust.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. from typing import *
  2. import io
  3. from sccd.action_lang.codegen.rust import *
  4. from sccd.statechart.static.tree import *
  5. from sccd.util.visit_tree import *
  6. from sccd.statechart.static.statechart import *
  7. from sccd.statechart.static.globals import *
  8. from sccd.statechart.static import priority
  9. from sccd.util.indenting_writer import *
  10. # Hardcoded limit on number of sub-rounds of combo and big step to detect never-ending superrounds.
  11. # TODO: make this a model parameter
  12. LIMIT = 1000
  13. # Conversion functions from abstract syntax elements to identifiers in Rust
  14. def snake_case(state: State) -> str:
  15. return state.full_name.replace('/', '_');
  16. def ident_var(state: State) -> str:
  17. if state.full_name == "/":
  18. return "root" # no technical reason, it's just clearer than "s_"
  19. else:
  20. return "s" + snake_case(state)
  21. def ident_type(state: State) -> str:
  22. if state.full_name == "/":
  23. return "Root" # no technical reason, it's just clearer than "State_"
  24. else:
  25. return "State" + snake_case(state)
  26. def ident_enum_variant(state: State) -> str:
  27. # We know the direct children of a state must have unique names relative to each other,
  28. # and enum variants are scoped locally, so we can use the short name here.
  29. # Furthermore, the XML parser asserts that state ids are valid identifiers in Rust.
  30. return "S" + state.short_name
  31. def ident_field(state: State) -> str:
  32. return "s" + snake_case(state)
  33. def ident_source_target(state: State) -> str:
  34. # drop the first '_' (this is safe, the root state itself can never be source or target)
  35. return snake_case(state)[1:]
  36. def ident_arena_label(state: State) -> str:
  37. if state.full_name == "/":
  38. return "arena_root"
  39. else:
  40. return "arena" + snake_case(state)
  41. def ident_arena_const(state: State) -> str:
  42. if state.full_name == "/":
  43. return "ARENA_ROOT"
  44. else:
  45. return "ARENA" + snake_case(state)
  46. def ident_history_field(state: HistoryState) -> str:
  47. return "history" + snake_case(state.parent) # A history state records history value for its parent
  48. def ident_event_type(event_name: str) -> str:
  49. if event_name[0] == '+':
  50. # after event
  51. return "After" + event_name.replace('+', '')
  52. else:
  53. return "Event_" + event_name
  54. def ident_event_field(event_name: str) -> str:
  55. return "e_" + event_name
  56. def compile_actions(actions: List[Action], w: IndentingWriter):
  57. for a in actions:
  58. if isinstance(a, RaiseOutputEvent):
  59. # TODO: evaluate event parameters
  60. w.writeln("(ctrl.output)(OutEvent{port:\"%s\", event:\"%s\"});" % (a.outport, a.name))
  61. elif isinstance(a, RaiseInternalEvent):
  62. w.writeln("internal.raise().%s = Some(%s{});" % (ident_event_field(a.name), (ident_event_type(a.name))))
  63. elif isinstance(a, Code):
  64. a.block.accept(RustGenerator(w))
  65. else:
  66. raise UnsupportedFeature(str(type(a).__qualname__))
  67. def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
  68. if sc.semantics.concurrency == Concurrency.MANY:
  69. raise UnsupportedFeature("concurrency")
  70. priority_ordered_transitions = priority.priority_and_concurrency(sc) # may raise error
  71. tree = sc.tree
  72. w.writeln("type Timers = [EntryId; %d];" % tree.timer_count)
  73. w.writeln()
  74. # Write event types
  75. input_events = sc.internal_events & ~sc.internally_raised_events
  76. internal_events = sc.internally_raised_events
  77. internal_queue = sc.semantics.internal_event_lifeline == InternalEventLifeline.QUEUE
  78. if internal_queue:
  79. raise UnsupportedFeature("queue-like internal event semantics")
  80. internal_same_round = (
  81. sc.semantics.internal_event_lifeline == InternalEventLifeline.REMAINDER or
  82. sc.semantics.internal_event_lifeline == InternalEventLifeline.SAME)
  83. w.writeln("// Input Events")
  84. w.writeln("#[derive(Copy, Clone)]")
  85. w.writeln("enum InEvent {")
  86. for event_name in (globals.events.names[i] for i in bm_items(input_events)):
  87. w.writeln(" %s," % ident_event_type(event_name))
  88. w.writeln("}")
  89. for event_name in (globals.events.names[i] for i in bm_items(internal_events)):
  90. w.writeln("// Internal Event")
  91. w.writeln("struct %s {" % ident_event_type(event_name))
  92. w.writeln(" // TODO: event parameters")
  93. w.writeln("}")
  94. if not internal_queue:
  95. # Implement internal events as a set
  96. w.writeln("// Set of (raised) internal events")
  97. w.writeln("#[derive(Default)]")
  98. # Bitmap would be more efficient, but for now struct will do:
  99. w.writeln("struct Internal {")
  100. for event_name in (globals.events.names[i] for i in bm_items(internal_events)):
  101. w.writeln(" %s: Option<%s>," % (ident_event_field(event_name), ident_event_type(event_name)))
  102. w.writeln("}")
  103. if internal_same_round:
  104. w.writeln("type InternalLifeline = SameRoundLifeline<Internal>;")
  105. else:
  106. w.writeln("type InternalLifeline = NextRoundLifeline<Internal>;")
  107. elif internal_type == "queue":
  108. pass
  109. # w.writeln("#[derive(Copy, Clone)]")
  110. # w.writeln("enum Internal {")
  111. # for event_name in (globals.events.names[i] for i in bm_items(internal_events)):
  112. # w.writeln(" %s," % ident_event_type(event_name))
  113. # w.writeln("}")
  114. w.writeln()
  115. # Write 'current state' types
  116. def write_state_type(state: State, children: List[State]):
  117. if isinstance(state.type, AndState):
  118. w.writeln("// And-state")
  119. # TODO: Only annotate Copy for states that will be recorded by deep history.
  120. w.writeln("#[derive(Default, Copy, Clone)]")
  121. w.writeln("struct %s {" % ident_type(state))
  122. for child in children:
  123. w.writeln(" %s: %s," % (ident_field(child), ident_type(child)))
  124. w.writeln("}")
  125. elif isinstance(state.type, OrState):
  126. w.writeln("// Or-state")
  127. w.writeln("#[derive(Copy, Clone)]")
  128. w.writeln("enum %s {" % ident_type(state))
  129. for child in children:
  130. w.writeln(" %s(%s)," % (ident_enum_variant(child), ident_type(child)))
  131. w.writeln("}")
  132. return state
  133. # Write "default" constructor
  134. def write_default(state: State, children: List[State]):
  135. # We use Rust's Default-trait to record default states,
  136. # this way, constructing a state instance without parameters will initialize it as the default state.
  137. if isinstance(state.type, OrState):
  138. w.writeln("impl Default for %s {" % ident_type(state))
  139. w.writeln(" fn default() -> Self {")
  140. w.writeln(" Self::%s(Default::default())" % (ident_enum_variant(state.type.default_state)))
  141. w.writeln(" }")
  142. w.writeln("}")
  143. return state
  144. # Implement trait 'State': enter/exit
  145. def write_enter_exit(state: State, children: List[State]):
  146. # w.writeln("impl<'a, OutputCallback: FnMut(OutEvent)> State<Timers, Controller<InEvent, OutputCallback>> for %s {" % ident_type(state))
  147. # w.writeln("impl<'a, OutputCallback: FnMut(OutEvent)> %s {" % ident_type(state))
  148. w.writeln("impl %s {" % ident_type(state))
  149. # Enter actions: Executes enter actions of only this state
  150. # w.writeln(" fn enter_actions(timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  151. w.writeln(" fn enter_actions<OutputCallback: FnMut(OutEvent)>(timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  152. w.writeln(" eprintln!(\"enter %s\");" % state.full_name);
  153. w.indent(); w.indent()
  154. compile_actions(state.enter, w)
  155. w.dedent(); w.dedent()
  156. for a in state.after_triggers:
  157. w.writeln(" timers[%d] = ctrl.set_timeout(%d, InEvent::%s);" % (a.after_id, a.delay.opt, ident_event_type(a.enabling[0].name)))
  158. w.writeln(" }")
  159. # Enter actions: Executes exit actions of only this state
  160. # w.writeln(" fn exit_actions(timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  161. w.writeln(" fn exit_actions<OutputCallback: FnMut(OutEvent)>(timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  162. w.writeln(" eprintln!(\"exit %s\");" % state.full_name);
  163. for a in state.after_triggers:
  164. w.writeln(" ctrl.unset_timeout(timers[%d]);" % (a.after_id))
  165. w.indent(); w.indent()
  166. compile_actions(state.exit, w)
  167. w.dedent(); w.dedent()
  168. w.writeln(" }")
  169. # Enter default: Executes enter actions of entering this state and its default substates, recursively
  170. # w.writeln(" fn enter_default(timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  171. w.writeln(" fn enter_default<OutputCallback: FnMut(OutEvent)>(timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  172. w.writeln(" %s::enter_actions(timers, internal, ctrl);" % (ident_type(state)))
  173. if isinstance(state.type, AndState):
  174. for child in children:
  175. w.writeln(" %s::enter_default(timers, internal, ctrl);" % (ident_type(child)))
  176. elif isinstance(state.type, OrState):
  177. w.writeln(" %s::enter_default(timers, internal, ctrl);" % (ident_type(state.type.default_state)))
  178. w.writeln(" }")
  179. # Exit current: Executes exit actions of this state and current children, recursively
  180. # w.writeln(" fn exit_current(&self, timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  181. w.writeln(" fn exit_current<OutputCallback: FnMut(OutEvent)>(&self, timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  182. # first, children (recursion):
  183. if isinstance(state.type, AndState):
  184. for child in children:
  185. w.writeln(" self.%s.exit_current(timers, internal, ctrl);" % (ident_field(child)))
  186. elif isinstance(state.type, OrState):
  187. w.writeln(" match self {")
  188. for child in children:
  189. w.writeln(" Self::%s(s) => { s.exit_current(timers, internal, ctrl); }," % (ident_enum_variant(child)))
  190. w.writeln(" }")
  191. # then, parent:
  192. w.writeln(" %s::exit_actions(timers, internal, ctrl);" % (ident_type(state)))
  193. w.writeln(" }")
  194. # Exit current: Executes enter actions of this state and current children, recursively
  195. # w.writeln(" fn enter_current(&self, timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  196. w.writeln(" fn enter_current<OutputCallback: FnMut(OutEvent)>(&self, timers: &mut Timers, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  197. # first, parent:
  198. w.writeln(" %s::enter_actions(timers, internal, ctrl);" % (ident_type(state)))
  199. # then, children (recursion):
  200. if isinstance(state.type, AndState):
  201. for child in children:
  202. w.writeln(" self.%s.enter_current(timers, internal, ctrl);" % (ident_field(child)))
  203. elif isinstance(state.type, OrState):
  204. w.writeln(" match self {")
  205. for child in children:
  206. w.writeln(" Self::%s(s) => { s.enter_current(timers, internal, ctrl); }," % (ident_enum_variant(child)))
  207. w.writeln(" }")
  208. w.writeln(" }")
  209. w.writeln("}")
  210. w.writeln()
  211. return state
  212. visit_tree(tree.root, lambda s: s.real_children,
  213. child_first=[
  214. write_state_type,
  215. write_default,
  216. write_enter_exit,
  217. ])
  218. syntactic_maximality = (
  219. sc.semantics.big_step_maximality == Maximality.SYNTACTIC
  220. or sc.semantics.combo_step_maximality == Maximality.SYNTACTIC)
  221. # Write arena type
  222. arenas = {}
  223. for t in tree.transition_list:
  224. arenas.setdefault(t.arena, 2**len(arenas))
  225. for arena, bm in arenas.items():
  226. for d in tree.bitmap_to_states(arena.descendants):
  227. bm |= arenas.get(d, 0)
  228. arenas[arena] = bm
  229. w.writeln("// Transition arenas (bitmap type)")
  230. # if syntactic_maximality:
  231. for size, typ in [(8, 'u8'), (16, 'u16'), (32, 'u32'), (64, 'u64'), (128, 'u128')]:
  232. if len(arenas) + 1 <= size:
  233. w.writeln("type Arenas = %s;" % typ)
  234. break
  235. else:
  236. raise UnsupportedFeature("Too many arenas! Cannot fit into an unsigned int.")
  237. w.writeln("const ARENA_NONE: Arenas = 0;")
  238. for arena, bm in arenas.items():
  239. w.writeln("const %s: Arenas = %s;" % (ident_arena_const(arena), bin(bm)))
  240. w.writeln("const ARENA_UNSTABLE: Arenas = %s; // indicates any transition fired with an unstable target" % bin(2**len(arenas.items())))
  241. # else:
  242. # w.writeln("type Arenas = bool;")
  243. # w.writeln("const ARENA_NONE: Arenas = false;")
  244. # for arena, bm in arenas.items():
  245. # w.writeln("const %s: Arenas = true;" % ident_arena_const(arena))
  246. # w.writeln("const ARENA_UNSTABLE: Arenas = false; // inapplicable to chosen semantics - all transition targets considered stable")
  247. w.writeln()
  248. # Write datamodel type
  249. RustGenerator(w).visit_Scope(sc.scope)
  250. # w.writeln("struct DataModel {")
  251. # w.writeln(" ")
  252. # w.writeln(")")
  253. # Write statechart type
  254. w.writeln("pub struct Statechart {")
  255. w.writeln(" current_state: %s," % ident_type(tree.root))
  256. # We always store a history value as 'deep' (also for shallow history).
  257. # 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.
  258. for h in tree.history_states:
  259. w.writeln(" %s: %s," % (ident_history_field(h), ident_type(h.parent)))
  260. w.writeln(" timers: Timers,")
  261. w.writeln(" data: Scope_instance,")
  262. w.writeln("}")
  263. w.writeln("impl Default for Statechart {")
  264. w.writeln(" fn default() -> Self {")
  265. w.writeln(" Self {")
  266. w.writeln(" current_state: Default::default(),")
  267. for h in tree.history_states:
  268. w.writeln(" %s: Default::default()," % (ident_history_field(h)))
  269. w.writeln(" timers: Default::default(),")
  270. w.writeln(" }")
  271. w.writeln(" }")
  272. w.writeln("}")
  273. w.writeln()
  274. # Function fair_step: a single "Take One" Maximality 'round' (= nonoverlapping arenas allowed to fire 1 transition)
  275. w.writeln("fn fair_step<OutputCallback: FnMut(OutEvent)>(sc: &mut Statechart, input: Option<InEvent>, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>, dirty: Arenas) -> Arenas {")
  276. w.writeln(" let mut fired: Arenas = ARENA_NONE;")
  277. # w.writeln(" let mut fired: Arenas = dirty;")
  278. w.writeln(" let %s = &mut sc.current_state;" % ident_var(tree.root))
  279. w.indent()
  280. transitions_written = []
  281. def write_transitions(state: State):
  282. # Many of the states to exit can be computed statically (i.e. they are always the same)
  283. # The one we cannot compute statically are:
  284. #
  285. # (1) The descendants of S2, S3, etc. if S1 is part of the "exit path":
  286. #
  287. # A ---> And-state on exit path
  288. # / \ \
  289. # S1 S2 S3 ...
  290. #
  291. # |
  292. # +--> S1 also on exit path
  293. #
  294. # (2) The descendants of S, if S is the transition target
  295. #
  296. # The same applies to entering states.
  297. # Writes statements that perform exit actions
  298. # in the correct order (children (last to first), then parent) for given 'exit path'.
  299. def write_exit(exit_path: List[State]):
  300. if len(exit_path) > 0:
  301. s = exit_path[0] # state to exit
  302. if len(exit_path) == 1:
  303. # Exit s:
  304. w.writeln("%s.exit_current(&mut sc.timers, internal, ctrl);" % (ident_var(s)))
  305. else:
  306. # Exit children:
  307. if isinstance(s.type, AndState):
  308. for c in reversed(s.children):
  309. if exit_path[1] is c:
  310. write_exit(exit_path[1:]) # continue recursively
  311. else:
  312. w.writeln("%s.exit_current(&mut sc.timers, internal, ctrl);" % (ident_var(c)))
  313. elif isinstance(s.type, OrState):
  314. write_exit(exit_path[1:]) # continue recursively with the next child on the exit path
  315. # Exit s:
  316. w.writeln("%s::exit_actions(&mut sc.timers, internal, ctrl);" % (ident_type(s)))
  317. # Store history
  318. if s.deep_history:
  319. _, _, h = s.deep_history
  320. w.writeln("sc.%s = *%s; // Store deep history" % (ident_history_field(h), ident_var(s)))
  321. if s.shallow_history:
  322. _, h = s.shallow_history
  323. if isinstance(s.type, AndState):
  324. raise Exception("Shallow history makes no sense for And-state!")
  325. # Or-state:
  326. w.writeln("sc.%s = match %s { // Store shallow history" % (ident_history_field(h), ident_var(s)))
  327. for c in s.real_children:
  328. 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)))
  329. w.writeln("};")
  330. # Writes statements that perform enter actions
  331. # in the correct order (parent, children (first to last)) for given 'enter path'.
  332. def write_enter(enter_path: List[State]):
  333. if len(enter_path) > 0:
  334. s = enter_path[0] # state to enter
  335. if len(enter_path) == 1:
  336. # Target state.
  337. if isinstance(s, HistoryState):
  338. w.writeln("sc.%s.enter_current(&mut sc.timers, internal, ctrl); // Enter actions for history state" %(ident_history_field(s)))
  339. else:
  340. w.writeln("%s::enter_default(&mut sc.timers, internal, ctrl);" % (ident_type(s)))
  341. else:
  342. # Enter s:
  343. w.writeln("%s::enter_actions(&mut sc.timers, internal, ctrl);" % (ident_type(s)))
  344. # Enter children:
  345. if isinstance(s.type, AndState):
  346. for c in s.children:
  347. if enter_path[1] is c:
  348. write_enter(enter_path[1:]) # continue recursively
  349. else:
  350. w.writeln("%s::enter_default(&mut sc.timers, internal, ctrl);" % (ident_type(c)))
  351. elif isinstance(s.type, OrState):
  352. if len(s.children) > 0:
  353. write_enter(enter_path[1:]) # continue recursively with the next child on the enter path
  354. else:
  355. # If the following occurs, there's a bug in this source file
  356. raise Exception("Basic state in the middle of enter path")
  357. # The 'state' of a state is just a value in our compiled code.
  358. # When executing a transition, the value of the transition's arena changes.
  359. # This function writes statements that build a new value that can be assigned to the arena.
  360. def write_new_configuration(enter_path: List[State]):
  361. if len(enter_path) > 0:
  362. s = enter_path[0]
  363. if len(enter_path) == 1:
  364. # Construct target state.
  365. # And/Or/Basic state: Just construct the default value:
  366. w.writeln("let new_%s: %s = Default::default();" % (ident_var(s), ident_type(s)))
  367. else:
  368. next_child = enter_path[1]
  369. if isinstance(next_child, HistoryState):
  370. # No recursion
  371. w.writeln("let new_%s = sc.%s; // Restore history value" % (ident_var(s), ident_history_field(next_child)))
  372. else:
  373. if isinstance(s.type, AndState):
  374. for c in s.children:
  375. if next_child is c:
  376. write_new_configuration(enter_path[1:]) # recurse
  377. else:
  378. # Other children's default states are constructed
  379. w.writeln("let new_%s: %s = Default::default();" % (ident_var(c), ident_type(c)))
  380. # Construct struct
  381. 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)))
  382. elif isinstance(s.type, OrState):
  383. if len(s.children) > 0:
  384. # Or-state
  385. write_new_configuration(enter_path[1:]) # recurse
  386. # Construct enum value
  387. w.writeln("let new_%s = %s::%s(new_%s);" % (ident_var(s), ident_type(s), ident_enum_variant(next_child), ident_var(next_child)))
  388. else:
  389. # If the following occurs, there's a bug in this source file
  390. raise Exception("Basic state in the middle of enter path")
  391. def parent():
  392. for i, t in enumerate(state.transitions):
  393. w.writeln("// Outgoing transition %d" % i)
  394. # 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.
  395. # However, an overlapping arena that is a descendant of ours will not have been detected.
  396. # Therefore, we must add an addition check in some cases:
  397. arenas_to_check = set()
  398. for earlier in transitions_written:
  399. if is_ancestor(parent=t.arena, child=earlier.arena):
  400. arenas_to_check.add(t.arena)
  401. if len(arenas_to_check) > 0:
  402. w.writeln("// A transition may have fired earlier that overlaps with our arena:")
  403. w.writeln("if fired & (%s) == ARENA_NONE {" % " | ".join(ident_arena_const(a) for a in arenas_to_check))
  404. w.indent()
  405. if t.trigger is not EMPTY_TRIGGER:
  406. condition = []
  407. for e in t.trigger.enabling:
  408. if bit(e.id) & input_events:
  409. condition.append("let Some(InEvent::%s) = &input" % ident_event_type(e.name))
  410. elif bit(e.id) & internal_events:
  411. condition.append("let Some(%s) = &internal.current().%s" % (ident_event_type(e.name), ident_event_field(e.name)))
  412. else:
  413. # Bug in SCCD :(
  414. raise Exception("Illegal event ID")
  415. w.writeln("if %s {" % " && ".join(condition))
  416. w.indent()
  417. if t.guard is not None:
  418. w.write("if ")
  419. t.guard.accept(RustGenerator(w))
  420. w.wnoln(" {")
  421. w.indent()
  422. # 1. Execute transition's actions
  423. # Path from arena to source, including source but not including arena
  424. exit_path_bm = t.arena.descendants & (t.source.state_id_bitmap | t.source.ancestors) # bitmap
  425. exit_path = list(tree.bitmap_to_states(exit_path_bm)) # list of states
  426. # Path from arena to target, including target but not including arena
  427. enter_path_bm = t.arena.descendants & (t.target.state_id_bitmap | t.target.ancestors) # bitmap
  428. enter_path = list(tree.bitmap_to_states(enter_path_bm)) # list of states
  429. w.writeln("eprintln!(\"fire %s\");" % str(t))
  430. w.writeln("// Exit actions")
  431. write_exit(exit_path)
  432. if len(t.actions) > 0:
  433. w.writeln("// Transition's actions")
  434. compile_actions(t.actions, w)
  435. w.writeln("// Enter actions")
  436. write_enter(enter_path)
  437. # 2. Update state
  438. # A state configuration is just a value
  439. w.writeln("// Build new state configuration")
  440. write_new_configuration([t.arena] + enter_path)
  441. w.writeln("// Update arena configuration")
  442. w.writeln("*%s = new_%s;" % (ident_var(t.arena), ident_var(t.arena)))
  443. if not syntactic_maximality or t.target.stable:
  444. w.writeln("fired |= %s; // Stable target" % ident_arena_const(t.arena))
  445. else:
  446. w.writeln("fired |= ARENA_UNSTABLE; // Unstable target")
  447. if sc.semantics.internal_event_lifeline == InternalEventLifeline.NEXT_SMALL_STEP:
  448. w.writeln("// Internal Event Lifeline: Next Small Step")
  449. w.writeln("internal.cycle();")
  450. # This arena is done:
  451. w.writeln("break '%s;" % (ident_arena_label(t.arena)))
  452. if t.guard is not None:
  453. w.dedent()
  454. w.writeln("}")
  455. if t.trigger is not EMPTY_TRIGGER:
  456. w.dedent()
  457. w.writeln("}")
  458. if len(arenas_to_check) > 0:
  459. w.dedent()
  460. w.writeln("}")
  461. transitions_written.append(t)
  462. def child():
  463. # Here is were we recurse and write the transition code for the children of our 'state'.
  464. if isinstance(state.type, AndState):
  465. for child in state.real_children:
  466. w.writeln("let %s = &mut %s.%s;" % (ident_var(child), ident_var(state), ident_field(child)))
  467. for child in state.real_children:
  468. w.writeln("// Orthogonal region")
  469. write_transitions(child)
  470. elif isinstance(state.type, OrState):
  471. if state.type.default_state is not None:
  472. if state in arenas:
  473. w.writeln("if (fired | dirty) & %s == ARENA_NONE {" % ident_arena_const(state))
  474. w.indent()
  475. w.writeln("'%s: loop {" % ident_arena_label(state))
  476. w.indent()
  477. w.writeln("match %s {" % ident_var(state))
  478. for child in state.real_children:
  479. w.indent()
  480. w.writeln("%s::%s(%s) => {" % (ident_type(state), ident_enum_variant(child), ident_var(child)))
  481. w.indent()
  482. write_transitions(child)
  483. w.dedent()
  484. w.writeln("},")
  485. w.dedent()
  486. w.writeln("};")
  487. w.writeln("break;")
  488. w.dedent()
  489. w.writeln("}")
  490. if state in arenas:
  491. w.dedent()
  492. w.writeln("}")
  493. if sc.semantics.hierarchical_priority == HierarchicalPriority.SOURCE_PARENT:
  494. parent()
  495. child()
  496. elif sc.semantics.hierarchical_priority == HierarchicalPriority.SOURCE_CHILD:
  497. child()
  498. parent()
  499. elif sc.semantics.hierarchical_priority == HierarchicalPriority.NONE:
  500. # We're free to pick any semantics here, but let's not go too wild
  501. parent()
  502. child()
  503. else:
  504. raise UnsupportedFeature("Priority semantics %s" % sc.semantics.hierarchical_priority)
  505. write_transitions(tree.root)
  506. w.dedent()
  507. w.writeln(" fired")
  508. w.writeln("}")
  509. # Write combo step and big step function
  510. def write_stepping_function(name: str, title: str, maximality: Maximality, substep: str, cycle_input: bool, cycle_internal: bool):
  511. w.writeln("fn %s<OutputCallback: FnMut(OutEvent)>(sc: &mut Statechart, input: Option<InEvent>, internal: &mut InternalLifeline, ctrl: &mut Controller<InEvent, OutputCallback>, dirty: Arenas) -> Arenas {" % (name))
  512. w.writeln(" let mut ctr: u16 = 0;")
  513. if maximality == Maximality.TAKE_ONE:
  514. w.writeln(" // %s Maximality: Take One" % title)
  515. w.writeln(" %s(sc, input, internal, ctrl, dirty)" % (substep))
  516. else:
  517. w.writeln(" let mut fired: Arenas = dirty;")
  518. w.writeln(" let mut e = input;")
  519. w.writeln(" loop {")
  520. if maximality == Maximality.TAKE_MANY:
  521. w.writeln(" // %s Maximality: Take Many" % title)
  522. w.writeln(" let just_fired = %s(sc, e, internal, ctrl, ARENA_NONE);" % (substep))
  523. elif maximality == Maximality.SYNTACTIC:
  524. w.writeln(" // %s Maximality: Syntactic" % title)
  525. w.writeln(" let just_fired = %s(sc, e, internal, ctrl, fired);" % (substep))
  526. w.writeln(" if just_fired == ARENA_NONE { // did any transition fire? (incl. unstable)")
  527. w.writeln(" break;")
  528. w.writeln(" }")
  529. w.writeln(" ctr += 1;")
  530. # w.writeln(" if ctr >= %d { panic!(\"too many steps (limit reached)\") };" % LIMIT)
  531. w.writeln(" assert_ne!(ctr, %d, \"too many steps (limit reached)\");" % LIMIT)
  532. w.writeln(" fired |= just_fired & !ARENA_UNSTABLE; // only record stable arenas")
  533. if cycle_input:
  534. w.writeln(" // Input Event Lifeline: %s" % sc.semantics.input_event_lifeline)
  535. w.writeln(" e = None;")
  536. if cycle_internal:
  537. w.writeln(" // Internal Event Lifeline: %s" % sc.semantics.internal_event_lifeline)
  538. w.writeln(" internal.cycle();")
  539. w.writeln(" }")
  540. w.writeln(" fired")
  541. w.writeln("}")
  542. write_stepping_function("combo_step", "Combo-Step",
  543. maximality = sc.semantics.combo_step_maximality,
  544. substep = "fair_step",
  545. cycle_input = False,
  546. cycle_internal = False)
  547. write_stepping_function("big_step", "Big-Step",
  548. maximality = sc.semantics.big_step_maximality,
  549. substep = "combo_step",
  550. cycle_input = sc.semantics.input_event_lifeline == InputEventLifeline.FIRST_COMBO_STEP,
  551. cycle_internal = sc.semantics.internal_event_lifeline == InternalEventLifeline.NEXT_COMBO_STEP)
  552. w.writeln()
  553. # Implement 'SC' trait
  554. w.writeln("impl<OutputCallback: FnMut(OutEvent)> SC<InEvent, Controller<InEvent, OutputCallback>> for Statechart {")
  555. w.writeln(" fn init(&mut self, ctrl: &mut Controller<InEvent, OutputCallback>) {")
  556. w.writeln(" %s::enter_default(&mut self.timers, &mut Default::default(), ctrl)" % (ident_type(tree.root)))
  557. w.writeln(" }")
  558. w.writeln(" fn big_step(&mut self, input: Option<InEvent>, c: &mut Controller<InEvent, OutputCallback>) {")
  559. w.writeln(" let mut internal: InternalLifeline = Default::default();")
  560. w.writeln(" big_step(self, input, &mut internal, c, ARENA_NONE);")
  561. w.writeln(" }")
  562. w.writeln("}")
  563. w.writeln()
  564. if DEBUG:
  565. w.writeln("use std::mem::size_of;")
  566. w.writeln("fn debug_print_sizes() {")
  567. w.writeln(" eprintln!(\"------------------------\");")
  568. w.writeln(" eprintln!(\"info: Statechart: {} bytes\", size_of::<Statechart>());")
  569. w.writeln(" eprintln!(\"info: Timers: {} bytes\", size_of::<Timers>());")
  570. def write_state_size(state):
  571. w.writeln(" eprintln!(\"info: State %s: {} bytes\", size_of::<%s>());" % (state.full_name, ident_type(state)))
  572. for child in state.real_children:
  573. write_state_size(child)
  574. write_state_size(tree.root)
  575. w.writeln(" eprintln!(\"info: InEvent: {} bytes\", size_of::<InEvent>());")
  576. w.writeln(" eprintln!(\"info: OutEvent: {} bytes\", size_of::<OutEvent>());")
  577. w.writeln(" eprintln!(\"info: Arenas: {} bytes\", size_of::<Arenas>());")
  578. w.writeln(" eprintln!(\"------------------------\");")
  579. w.writeln("}")
  580. w.writeln()