|
@@ -3,6 +3,7 @@ from sccd.statechart.static.tree import *
|
|
|
from sccd.util.visit_tree import *
|
|
|
from sccd.statechart.static.statechart import *
|
|
|
from sccd.statechart.static.globals import *
|
|
|
+from sccd.util.indenting_writer import *
|
|
|
|
|
|
# Conversion functions from abstract syntax elements to identifiers in Rust
|
|
|
|
|
@@ -43,7 +44,7 @@ def ident_arena_label(state: State) -> str:
|
|
|
return "arena" + snake_case(state)
|
|
|
|
|
|
|
|
|
-def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
+def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
|
|
|
|
|
|
tree = sc.tree
|
|
|
|
|
@@ -63,25 +64,25 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
return None # we got no time for pseudo-states!
|
|
|
|
|
|
def as_struct():
|
|
|
- print("#[allow(non_camel_case_types)]")
|
|
|
- print("struct %s {" % ident_type(state))
|
|
|
+ w.writeln("#[allow(non_camel_case_types)]")
|
|
|
+ w.writeln("struct %s {" % ident_type(state))
|
|
|
for child in children:
|
|
|
- print(" %s: %s," % (ident_field(child), ident_type(child)))
|
|
|
- print("}")
|
|
|
+ w.writeln(" %s: %s," % (ident_field(child), ident_type(child)))
|
|
|
+ w.writeln("}")
|
|
|
|
|
|
def as_enum():
|
|
|
- print("#[allow(non_camel_case_types)]")
|
|
|
- print("enum %s {" % ident_type(state))
|
|
|
+ w.writeln("#[allow(non_camel_case_types)]")
|
|
|
+ w.writeln("enum %s {" % ident_type(state))
|
|
|
for child in children:
|
|
|
- print(" %s(%s)," % (ident_enum_variant(child), ident_type(child)))
|
|
|
- print("}")
|
|
|
+ w.writeln(" %s(%s)," % (ident_enum_variant(child), ident_type(child)))
|
|
|
+ w.writeln("}")
|
|
|
|
|
|
if isinstance(state, ParallelState):
|
|
|
- print("// And-state")
|
|
|
+ w.writeln("// And-state")
|
|
|
as_struct()
|
|
|
elif isinstance(state, State):
|
|
|
if len(state.children) > 0:
|
|
|
- print("// Or-state")
|
|
|
+ w.writeln("// Or-state")
|
|
|
as_enum() # Or-state
|
|
|
else:
|
|
|
# Basic state: write as empty struct
|
|
@@ -91,54 +92,56 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
#
|
|
|
# An empty enum is also a valid type in Rust, but no instances
|
|
|
# of it can be created. Also called an "uninhabited type".
|
|
|
- print("// Basic state")
|
|
|
+ w.writeln("// Basic state")
|
|
|
as_struct()
|
|
|
|
|
|
# The above if-else construction hints at the fact that we would have
|
|
|
# better used empty And-states to model basic states, instead of empty Or-states...
|
|
|
|
|
|
- print()
|
|
|
+ w.writeln()
|
|
|
return state
|
|
|
|
|
|
|
|
|
# Write "enter/exit state" functions
|
|
|
|
|
|
# This fragment should be moved to a library:
|
|
|
- print("pub trait State {")
|
|
|
- print(" fn enter_actions();")
|
|
|
- print(" fn exit_actions();")
|
|
|
- print(" fn enter_default();")
|
|
|
- print("}")
|
|
|
- print()
|
|
|
+ w.writeln("pub trait State {")
|
|
|
+ w.writeln(" fn enter_actions();")
|
|
|
+ w.writeln(" fn exit_actions();")
|
|
|
+ w.writeln(" fn enter_default();")
|
|
|
+ w.writeln("}")
|
|
|
+ w.writeln()
|
|
|
|
|
|
def write_enter_exit(state: State, children: List[State]):
|
|
|
if isinstance(state, HistoryState):
|
|
|
return None # we got no time for pseudo-states!
|
|
|
|
|
|
- print("impl State for %s {" % ident_type(state))
|
|
|
+ w.writeln("impl State for %s {" % ident_type(state))
|
|
|
|
|
|
- print(" fn enter_actions() {")
|
|
|
- print(" // TODO: execute enter actions here")
|
|
|
- print(" println!(\"enter %s\");" % state.opt.full_name);
|
|
|
- print(" }")
|
|
|
+ w.writeln(" fn enter_actions() {")
|
|
|
+ w.writeln(" println!(\"enter %s\");" % state.opt.full_name);
|
|
|
+ for a in state.enter:
|
|
|
+ w.writeln(" println!(\"%s\");" % a.render())
|
|
|
+ w.writeln(" }")
|
|
|
|
|
|
- print(" fn exit_actions() {")
|
|
|
- print(" // TODO: execute exit actions here")
|
|
|
- print(" println!(\"exit %s\");" % state.opt.full_name);
|
|
|
- print(" }")
|
|
|
+ w.writeln(" fn exit_actions() {")
|
|
|
+ w.writeln(" println!(\"exit %s\");" % state.opt.full_name);
|
|
|
+ for a in state.exit:
|
|
|
+ w.writeln(" println!(\"%s\");" % a.render())
|
|
|
+ w.writeln(" }")
|
|
|
|
|
|
- print(" fn enter_default() {")
|
|
|
- print(" %s::enter_actions();" % ident_type(state))
|
|
|
+ w.writeln(" fn enter_default() {")
|
|
|
+ w.writeln(" %s::enter_actions();" % ident_type(state))
|
|
|
if isinstance(state, ParallelState):
|
|
|
for child in children:
|
|
|
- print(" %s::enter_default();" % ident_type(child))
|
|
|
+ w.writeln(" %s::enter_default();" % ident_type(child))
|
|
|
else:
|
|
|
if state.default_state is not None:
|
|
|
- print(" %s::enter_default();" % ident_type(state.default_state))
|
|
|
- print(" }")
|
|
|
+ w.writeln(" %s::enter_default();" % ident_type(state.default_state))
|
|
|
+ w.writeln(" }")
|
|
|
|
|
|
- print("}")
|
|
|
- print()
|
|
|
+ w.writeln("}")
|
|
|
+ w.writeln()
|
|
|
return state
|
|
|
|
|
|
|
|
@@ -151,25 +154,25 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
# We use Rust's Default-trait to record default states,
|
|
|
# this way, constructing a state instance without parameters will initialize it as the default state.
|
|
|
|
|
|
- print("impl Default for %s {" % ident_type(state))
|
|
|
- print(" fn default() -> Self {")
|
|
|
+ w.writeln("impl Default for %s {" % ident_type(state))
|
|
|
+ w.writeln(" fn default() -> Self {")
|
|
|
|
|
|
if isinstance(state, ParallelState):
|
|
|
- print(" return Self {")
|
|
|
+ w.writeln(" return Self {")
|
|
|
for child in children:
|
|
|
- print(" %s: Default::default()," % (ident_field(child)))
|
|
|
- print(" };")
|
|
|
+ w.writeln(" %s: Default::default()," % (ident_field(child)))
|
|
|
+ w.writeln(" };")
|
|
|
elif isinstance(state, State):
|
|
|
if state.default_state is not None:
|
|
|
# Or-state
|
|
|
- print(" return Self::%s(Default::default());" % (ident_enum_variant(state.default_state)))
|
|
|
+ w.writeln(" return Self::%s(Default::default());" % (ident_enum_variant(state.default_state)))
|
|
|
else:
|
|
|
# Basic state
|
|
|
- print(" return Self{};")
|
|
|
+ w.writeln(" return Self{};")
|
|
|
|
|
|
- print(" }")
|
|
|
- print("}")
|
|
|
- print()
|
|
|
+ w.writeln(" }")
|
|
|
+ w.writeln("}")
|
|
|
+ w.writeln()
|
|
|
return state
|
|
|
|
|
|
visit_tree(tree.root, lambda s: s.children,
|
|
@@ -181,37 +184,38 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
|
|
|
# Write event type
|
|
|
|
|
|
- print("#[allow(non_camel_case_types)]")
|
|
|
- print("enum Event {")
|
|
|
+ w.writeln("#[allow(non_camel_case_types)]")
|
|
|
+ w.writeln("enum Event {")
|
|
|
for event_name in (globals.events.names[i] for i in bm_items(sc.internal_events)):
|
|
|
- print(" %s," % event_name)
|
|
|
- print("}")
|
|
|
- print()
|
|
|
+ w.writeln(" %s," % event_name)
|
|
|
+ w.writeln("}")
|
|
|
+ w.writeln()
|
|
|
|
|
|
# Write statechart type
|
|
|
- print("pub struct Statechart {")
|
|
|
- print(" current_state: %s," % ident_type(tree.root))
|
|
|
- print(" // TODO: history values")
|
|
|
- print(" // TODO: timers")
|
|
|
- print("}")
|
|
|
- print()
|
|
|
-
|
|
|
- class IndentingWriter:
|
|
|
- def __init__(self, spaces = 0):
|
|
|
- self.spaces = spaces
|
|
|
- def indent(self):
|
|
|
- self.spaces += 2
|
|
|
- def dedent(self):
|
|
|
- self.spaces -= 2
|
|
|
- def print(self, str):
|
|
|
- print(' '*self.spaces + str)
|
|
|
-
|
|
|
- print("impl Statechart {")
|
|
|
- print(" fn fair_step(&mut self, event: Option<Event>) {")
|
|
|
- print(" println!(\"fair step\");")
|
|
|
- print(" let %s = &mut self.current_state;" % ident_var(tree.root))
|
|
|
-
|
|
|
- w = IndentingWriter(4)
|
|
|
+ w.writeln("pub struct Statechart {")
|
|
|
+ w.writeln(" current_state: %s," % ident_type(tree.root))
|
|
|
+ w.writeln(" // TODO: history values")
|
|
|
+ w.writeln(" // TODO: timers")
|
|
|
+ w.writeln("}")
|
|
|
+ w.writeln()
|
|
|
+
|
|
|
+ w.writeln("impl Default for Statechart {")
|
|
|
+ w.writeln(" fn default() -> Self {")
|
|
|
+ w.writeln(" return Self{")
|
|
|
+ w.writeln(" current_state: Default::default(),")
|
|
|
+ # w.writeln(" history: Default::default(),")
|
|
|
+ # w.writeln(" timers: Default::default(),")
|
|
|
+ w.writeln(" };")
|
|
|
+ w.writeln(" }")
|
|
|
+ w.writeln("}")
|
|
|
+
|
|
|
+ w.writeln("impl Statechart {")
|
|
|
+ w.writeln(" fn fair_step(&mut self, event: Option<Event>) {")
|
|
|
+ w.writeln(" println!(\"fair step\");")
|
|
|
+ w.writeln(" let %s = &mut self.current_state;" % ident_var(tree.root))
|
|
|
+
|
|
|
+ w.indent()
|
|
|
+ w.indent()
|
|
|
|
|
|
def write_transitions(state: State):
|
|
|
if isinstance(state, HistoryState):
|
|
@@ -229,7 +233,7 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
# (2) The descendants of S, if S is the transition target
|
|
|
def write_exit(exit_path: List[State]):
|
|
|
if len(exit_path) == 0:
|
|
|
- w.print("%s.exit();" % ident_var(s))
|
|
|
+ w.writeln("%s.exit();" % ident_var(s))
|
|
|
else:
|
|
|
s = exit_path[0]
|
|
|
if isinstance(s, HistoryState):
|
|
@@ -239,12 +243,12 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
if exit_path[1] is c:
|
|
|
write_exit(exit_path[1:]) # continue recursively
|
|
|
else:
|
|
|
- w.print("%s.exit();" % ident_var(c))
|
|
|
+ w.writeln("%s.exit();" % ident_var(c))
|
|
|
elif isinstance(s, State):
|
|
|
if s.default_state is not None:
|
|
|
# Or-state
|
|
|
write_exit(exit_path[1:]) # continue recursively with the next child on the exit path
|
|
|
- w.print("%s::exit_actions();" % ident_type(s))
|
|
|
+ w.writeln("%s::exit_actions();" % ident_type(s))
|
|
|
|
|
|
def write_new_configuration(enter_path: List[State]):
|
|
|
if len(enter_path) > 0:
|
|
@@ -252,7 +256,7 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
if len(enter_path) == 1:
|
|
|
# Construct target state.
|
|
|
# Whatever the type of parent (And/Or/Basic), just construct the default value:
|
|
|
- w.print("let new_%s: %s = Default::default();" % (ident_var(s), ident_type(s)))
|
|
|
+ w.writeln("let new_%s: %s = Default::default();" % (ident_var(s), ident_type(s)))
|
|
|
else:
|
|
|
if isinstance(s, ParallelState):
|
|
|
for c in s.children:
|
|
@@ -260,15 +264,15 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
write_new_configuration(enter_path[1:]) # recurse
|
|
|
else:
|
|
|
# Other children's default states are constructed
|
|
|
- w.print("let new_%s: %s = Default::default();" % (ident_var(c), ident_type(c)))
|
|
|
+ w.writeln("let new_%s: %s = Default::default();" % (ident_var(c), ident_type(c)))
|
|
|
# Construct struct
|
|
|
- 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])))
|
|
|
+ w.writeln("let new_%s = %s{%s:%s, ..Default::default()};" % (ident_var(s), ident_type(s), ident_field(enter_path[1]), ident_var(enter_path[1])))
|
|
|
|
|
|
elif isinstance(s, State):
|
|
|
if len(s.children) > 0:
|
|
|
# Or-state
|
|
|
write_new_configuration(enter_path[1:]) # recurse
|
|
|
- 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])))
|
|
|
+ w.writeln("let new_%s = %s::%s(new_%s);" % (ident_var(s), ident_type(s), ident_enum_variant(enter_path[1]), ident_var(enter_path[1])))
|
|
|
else:
|
|
|
# The following should never occur
|
|
|
# The parser should have rejected the model before we even get here
|
|
@@ -280,19 +284,19 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
s = enter_path[0]
|
|
|
if len(enter_path) == 1:
|
|
|
# Target state.
|
|
|
- w.print("%s::enter_default();" % ident_type(s))
|
|
|
+ w.writeln("%s::enter_default();" % ident_type(s))
|
|
|
else:
|
|
|
if isinstance(s, ParallelState):
|
|
|
for c in s.children:
|
|
|
if enter_path[1] is c:
|
|
|
- w.print("%s::enter_actions();" % ident_type(c))
|
|
|
+ w.writeln("%s::enter_actions();" % ident_type(c))
|
|
|
write_enter(enter_path[1:]) # continue recursively
|
|
|
else:
|
|
|
- w.print("%s::enter_default();" % ident_type(c))
|
|
|
+ w.writeln("%s::enter_default();" % ident_type(c))
|
|
|
elif isinstance(s, State):
|
|
|
if len(s.children) > 0:
|
|
|
# Or-state
|
|
|
- w.print("%s::enter_actions();" & ident_type(s))
|
|
|
+ w.writeln("%s::enter_actions();" & ident_type(s))
|
|
|
write_enter(enter_path[1:]) # continue recursively with the next child on the enter path
|
|
|
else:
|
|
|
# The following should never occur
|
|
@@ -301,12 +305,12 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
|
|
|
def parent():
|
|
|
for t in state.transitions:
|
|
|
- w.print("// Outgoing transition")
|
|
|
+ w.writeln("// Outgoing transition")
|
|
|
|
|
|
if t.trigger is not EMPTY_TRIGGER:
|
|
|
if len(t.trigger.enabling) > 1:
|
|
|
raise Exception("Multi-event triggers currently unsupported")
|
|
|
- w.print("if let Some(Event::%s) = event {" % t.trigger.enabling[0].name)
|
|
|
+ w.writeln("if let Some(Event::%s) = event {" % t.trigger.enabling[0].name)
|
|
|
w.indent()
|
|
|
|
|
|
# 1. Execute transition's actions
|
|
@@ -319,55 +323,59 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
enter_path_bm = t.opt.arena.opt.descendants & (t.target.opt.state_id_bitmap | t.target.opt.ancestors) # bitmap
|
|
|
enter_path = list(tree.bitmap_to_states(enter_path_bm)) # list of states
|
|
|
|
|
|
- w.print("// Exit actions")
|
|
|
+ w.writeln("println!(\"fire %s\");" % str(t))
|
|
|
+
|
|
|
+ w.writeln("// Exit actions")
|
|
|
write_exit(exit_path)
|
|
|
|
|
|
- w.print("// Transition's actions")
|
|
|
- w.print("println!(\"%s\");" % str(t))
|
|
|
+ if len(t.actions) > 0:
|
|
|
+ w.writeln("// Transition's actions")
|
|
|
+ for a in t.actions:
|
|
|
+ w.writeln("println!(%s);" % a.render())
|
|
|
|
|
|
- w.print("// Enter actions")
|
|
|
+ w.writeln("// Enter actions")
|
|
|
write_enter(enter_path)
|
|
|
|
|
|
# 2. Update state
|
|
|
|
|
|
# A state configuration is just a value
|
|
|
- w.print("// Build new state configuration")
|
|
|
+ w.writeln("// Build new state configuration")
|
|
|
write_new_configuration([t.opt.arena] + enter_path)
|
|
|
|
|
|
- w.print("// Update arena configuration")
|
|
|
- w.print("*%s = new_%s;" % (ident_var(t.opt.arena), ident_var(t.opt.arena)))
|
|
|
+ w.writeln("// Update arena configuration")
|
|
|
+ w.writeln("*%s = new_%s;" % (ident_var(t.opt.arena), ident_var(t.opt.arena)))
|
|
|
|
|
|
# This arena is done:
|
|
|
- w.print("break '%s;" % (ident_arena_label(t.opt.arena)))
|
|
|
+ w.writeln("break '%s;" % (ident_arena_label(t.opt.arena)))
|
|
|
|
|
|
if t.trigger is not EMPTY_TRIGGER:
|
|
|
w.dedent()
|
|
|
- w.print("}")
|
|
|
+ w.writeln("}")
|
|
|
|
|
|
def child():
|
|
|
if isinstance(state, ParallelState):
|
|
|
for child in state.children:
|
|
|
- w.print("// Orthogonal region")
|
|
|
- w.print("let %s = &mut %s.%s;" % (ident_var(child), ident_var(state), ident_field(child)))
|
|
|
+ w.writeln("// Orthogonal region")
|
|
|
+ w.writeln("let %s = &mut %s.%s;" % (ident_var(child), ident_var(state), ident_field(child)))
|
|
|
write_transitions(child)
|
|
|
elif isinstance(state, State):
|
|
|
if state.default_state is not None:
|
|
|
- w.print("'%s: loop {" % ident_arena_label(state))
|
|
|
+ w.writeln("'%s: loop {" % ident_arena_label(state))
|
|
|
w.indent()
|
|
|
- w.print("match %s {" % ident_var(state))
|
|
|
+ w.writeln("match %s {" % ident_var(state))
|
|
|
for child in state.children:
|
|
|
w.indent()
|
|
|
- # w.print("%s::%s(%s) => {" % (ident_type(state), ident_enum_variant(child), ident_var(child)))
|
|
|
- w.print("%s::%s(_) => {" % (ident_type(state), ident_enum_variant(child)))
|
|
|
+ # w.writeln("%s::%s(%s) => {" % (ident_type(state), ident_enum_variant(child), ident_var(child)))
|
|
|
+ w.writeln("%s::%s(_) => {" % (ident_type(state), ident_enum_variant(child)))
|
|
|
w.indent()
|
|
|
write_transitions(child)
|
|
|
w.dedent()
|
|
|
- w.print("},")
|
|
|
+ w.writeln("},")
|
|
|
w.dedent()
|
|
|
- w.print("};")
|
|
|
- w.print("break;")
|
|
|
+ w.writeln("};")
|
|
|
+ w.writeln("break;")
|
|
|
w.dedent()
|
|
|
- w.print("}")
|
|
|
+ w.writeln("}")
|
|
|
|
|
|
# TODO: This is where parent/child-first semantic variability should be implemented
|
|
|
# For now, it's always "parent first"
|
|
@@ -376,17 +384,19 @@ def compile_statechart(sc: Statechart, globals: Globals):
|
|
|
|
|
|
write_transitions(tree.root)
|
|
|
|
|
|
- print(" }")
|
|
|
- print("}")
|
|
|
- print()
|
|
|
+ w.dedent()
|
|
|
+ w.dedent()
|
|
|
+
|
|
|
+ w.writeln(" }")
|
|
|
+ w.writeln("}")
|
|
|
+ w.writeln()
|
|
|
|
|
|
|
|
|
- # See if it works
|
|
|
- print("fn main() {")
|
|
|
- print(" let mut sc = Statechart{current_state: Default::default()};")
|
|
|
- print(" Root::enter_default();")
|
|
|
- print(" sc.fair_step(None);")
|
|
|
- print(" sc.fair_step(None);")
|
|
|
- # print(" sc.current_state.exit();")
|
|
|
- print("}")
|
|
|
- print()
|
|
|
+ # # See if it works
|
|
|
+ # w.writeln("fn main() {")
|
|
|
+ # w.writeln(" let mut sc: Statechart = Default::default();")
|
|
|
+ # w.writeln(" Root::enter_default();")
|
|
|
+ # w.writeln(" sc.fair_step(None);")
|
|
|
+ # w.writeln(" sc.fair_step(None);")
|
|
|
+ # w.writeln("}")
|
|
|
+ # w.writeln()
|