1mod expression;
4
5use self::expression::{expression, infer_type};
6use crate::parser::{
7 Executable, If, OmgBaseType, OmgType, OmgTypeDef, OmgTypes, Param, Parser, Scxml, Send, State,
8 Target,
9};
10use anyhow::{Context, anyhow, bail};
11use boa_interner::Interner;
12use log::{info, trace, warn};
13use scan_core::{channel_system::*, *};
14use scan_pmtl::{Pmtl, PmtlOracle};
15use std::collections::{BTreeMap, HashMap, HashSet};
16
17#[derive(Debug, Clone)]
22pub struct ScxmlModel {
23 pub fsm_names: HashMap<u16, String>,
26 pub parameters: HashMap<Channel, (PgId, PgId, usize)>,
28 pub int_queues: HashSet<Channel>,
29 pub ext_queues: HashMap<Channel, PgId>,
30 pub events: Vec<(String, Option<OmgTypeDef>)>,
31 pub port_vars: Vec<(String, OmgType, Vec<Expression<Atom>>)>,
32 pub ports: Vec<Channel>,
33 pub assumes: Vec<String>,
34 pub guarantees: Vec<String>,
35 pub omg_types: OmgTypes,
36}
37
38#[derive(Debug, Clone)]
39struct FsmBuilder {
40 pg_id: PgId,
41 ext_queue: Channel,
42}
43
44#[derive(Debug, Clone)]
45struct EventBuilder {
46 name: String,
48 params: BTreeMap<String, Option<OmgType>>,
49 senders: HashSet<PgId>,
50 receivers: HashSet<PgId>,
51}
52
53#[derive(Default)]
55pub struct ModelBuilder {
56 cs: ChannelSystemBuilder,
57 fsm_names: HashMap<u16, String>,
64 fsm_builders: HashMap<String, FsmBuilder>,
65 events: Vec<EventBuilder>,
68 event_indexes: HashMap<String, usize>,
69 parameter_channels: HashMap<(PgId, PgId, usize), Channel>,
70 guarantees: Vec<(String, Pmtl<usize>)>,
72 assumes: Vec<(String, Pmtl<usize>)>,
73 predicates: Vec<BooleanExpr<Atom>>,
74 port_vars: HashMap<String, (OmgType, Vec<Expression<Atom>>)>,
76 ports: Vec<(Channel, Vec<Val>)>,
78 int_queues: HashSet<Channel>,
80}
81
82impl ModelBuilder {
83 pub fn build(
89 mut parser: Parser,
90 properties: &[String],
91 all_properties: bool,
92 ) -> anyhow::Result<(TransitionSystem, PmtlOracle, ScxmlModel)> {
93 let mut model_builder = ModelBuilder::default();
94 model_builder
95 .prebuild_processes(&mut parser)
96 .context("failed prebuilding processes")?;
97
98 info!(target: "build", "Visit process list");
99 for id in model_builder.fsm_builders.keys() {
101 if !parser.processes.contains_key(id) {
102 parser.processes.insert(
103 id.clone(),
104 Scxml {
105 name: id.clone(),
106 initial: String::from("init"),
107 datamodel: Vec::new(),
108 states: HashMap::from([(
109 String::from("init"),
110 State {
111 id: String::from("init"),
112 transitions: Vec::new(),
113 on_entry: Vec::new(),
114 on_exit: Vec::new(),
115 },
116 )]),
117 },
118 );
119 }
120 }
121 for (id, fsm) in parser.processes.iter() {
122 model_builder
123 .build_fsm(fsm, &mut parser.interner, &mut parser.types)
124 .with_context(|| format!("failed building FSM '{id}'"))?;
125 }
126
127 model_builder
128 .build_ports(&mut parser)
129 .context("failed building ports")?;
130 model_builder
131 .build_properties(&mut parser, properties, all_properties)
132 .context("failed building properties")?;
133
134 let model = model_builder.build_model(parser);
135
136 Ok(model)
137 }
138
139 fn event_index(&mut self, id: &str) -> usize {
140 self.event_indexes.get(id).cloned().unwrap_or_else(|| {
141 let index = self.events.len();
142 self.events.push(EventBuilder {
143 name: id.to_string(),
144 params: BTreeMap::new(),
145 senders: HashSet::new(),
146 receivers: HashSet::new(),
147 });
148 self.event_indexes.insert(id.to_owned(), index);
149 index
150 })
151 }
152
153 fn add_fsm_builder(&mut self, id: &str) -> anyhow::Result<&FsmBuilder> {
154 if self.fsm_builders.contains_key(id) {
155 bail!("FSM {id} already exists");
156 } else {
157 let pg_id = self.cs.new_program_graph();
158 let ext_queue = self
159 .cs
160 .new_channel(vec![Type::Natural, Type::Natural], None);
161 let fsm = FsmBuilder { pg_id, ext_queue };
162 self.fsm_builders.insert(id.to_string(), fsm);
163 self.fsm_names.insert(pg_id.into(), id.to_string());
164 }
165 Ok(self.fsm_builders.get(id).expect("just inserted"))
166 }
167
168 fn prebuild_processes(&mut self, parser: &mut Parser) -> anyhow::Result<()> {
169 for (id, _fsm) in parser.processes.iter_mut() {
170 let _ = self.add_fsm_builder(id).expect("add FSM builder");
171 }
172 for (id, fsm) in parser.processes.iter_mut() {
173 let pg_id = self.fsm_builders.get(id).expect("just inserted").pg_id;
174 self.prebuild_fsm(pg_id, fsm, &parser.interner, &parser.types)
175 .with_context(|| format!("failed pre-processing of fsm {id}",))?;
176 }
177 for eb in &self.events {
178 for (param, t) in &eb.params {
179 if t.is_none() {
180 bail!("param {param} of event {} needs type annotation", eb.name);
181 }
182 }
183 }
184 Ok(())
185 }
186
187 fn prebuild_fsm(
188 &mut self,
189 pg_id: PgId,
190 fmt: &mut Scxml,
191 interner: &Interner,
192 omg_types: &OmgTypes,
193 ) -> anyhow::Result<()> {
194 let mut vars: HashMap<String, OmgType> = HashMap::new();
195 for data in &fmt.datamodel {
196 if let Some(r#type) = &data.omg_type
197 && !matches!(r#type, OmgType::Array(_, None))
199 {
200 vars.insert(data.id.to_owned(), r#type.clone());
201 } else if let Some(expr) = data.expression.as_ref() {
202 let r#type = infer_type(expr, &vars, interner, data.omg_type.as_ref(), omg_types)?;
203 vars.insert(data.id.to_owned(), r#type);
204 }
205 }
206 for (_, state) in fmt.states.iter_mut() {
207 for exec in state.on_entry.iter_mut() {
208 self.prebuild_exec(pg_id, exec, &vars, interner, omg_types)
209 .with_context(|| {
210 format!(
211 "failed pre-processing of executable on entry of state {}",
212 state.id
213 )
214 })?;
215 }
216 for (index, transition) in state.transitions.iter_mut().enumerate() {
217 if let Some(ref event) = transition.event {
218 let event_index = self.event_index(event);
220 let builder = self.events.get_mut(event_index).expect("index must exist");
221 builder.receivers.insert(pg_id);
222 }
223 for exec in transition.effects.iter_mut() {
224 self.prebuild_exec(pg_id, exec, &vars, interner, omg_types).with_context(|| {
225 format!("failed pre-processing of executable in transition {index} of state {}", state.id)
226 })?;
227 }
228 }
229 for exec in state.on_exit.iter_mut() {
230 self.prebuild_exec(pg_id, exec, &vars, interner, omg_types)
231 .with_context(|| {
232 format!(
233 "failed pre-processing of executable on exit of state {}",
234 state.id
235 )
236 })?;
237 }
238 }
239 Ok(())
240 }
241
242 fn prebuild_exec(
243 &mut self,
244 pg_id: PgId,
245 executable: &mut Executable,
246 vars: &HashMap<String, OmgType>,
247 interner: &Interner,
248 omg_types: &OmgTypes,
249 ) -> anyhow::Result<()> {
250 match executable {
251 Executable::Assign {
252 location: _,
253 expr: _,
254 } => Ok(()),
255 Executable::Raise { event } => {
256 let event_index = self.event_index(event);
259 let builder = self.events.get_mut(event_index).expect("index must exist");
260 builder.senders.insert(pg_id);
261 builder.receivers.insert(pg_id);
262 Ok(())
263 }
264 Executable::Send(Send {
265 event,
266 target,
267 delay: _,
268 params,
269 }) => {
270 let event_index = self.event_index(event);
271 self.events
273 .get_mut(event_index)
274 .expect("index must exist")
275 .senders
276 .insert(pg_id);
277 if let Some(Target::Id(target)) = target {
280 let target_id = if let Some(fsm) = self.fsm_builders.get(target) {
281 fsm.pg_id
282 } else {
283 let fsm_name = self
287 .fsm_names
288 .get(&pg_id.into())
289 .ok_or_else(|| anyhow!("FSM {pg_id:?} not found"))?;
290 warn!(
291 "target FSM '{target}' for sent event '{event}' in FSM '{fsm_name}' does not exist; creating a new one",
292 );
293 self.add_fsm_builder(target)
294 .expect("add new fsm builder")
295 .pg_id
296 };
297 self.events
298 .get_mut(event_index)
299 .expect("index must exist")
300 .receivers
301 .insert(target_id);
302 }
303 for param in params {
304 let builder = self.events.get(event_index).expect("index must exist");
306 if let Some(Some(t)) = builder.params.get(¶m.name)
307 && !matches!(t, OmgType::Array(_, None))
309 {
310 if let Some(omg) = param.omg_type.as_ref() {
311 if t != omg {
312 bail!(
313 "type parameter mismatch: {t:?} != {omg:?} for parameter {}",
314 param.name
315 );
316 }
317 } else {
318 let _ = param.omg_type.insert(t.clone());
319 }
320 } else if let Some(t) = param.omg_type.as_ref()
321 && !matches!(t, OmgType::Array(_, None))
323 {
324 let builder = self.events.get_mut(event_index).expect("index must exist");
325 builder.params.insert(param.name.clone(), Some(t.clone()));
326 } else if let Ok(t) = infer_type(
327 ¶m.expr,
328 vars,
329 interner,
330 param.omg_type.as_ref(),
331 omg_types,
332 ) {
333 let _ = param.omg_type.insert(t.clone());
334 let builder = self.events.get_mut(event_index).expect("index must exist");
335 builder.params.insert(param.name.clone(), Some(t));
336 } else {
337 let builder = self.events.get_mut(event_index).expect("index must exist");
339 builder.params.insert(param.name.clone(), None);
340 }
341 }
342 Ok(())
343 }
344 Executable::If(If {
345 r#elif: elifs,
346 r#else,
347 ..
348 }) => {
349 for (_, executables) in elifs {
351 for executable in executables {
352 self.prebuild_exec(pg_id, executable, vars, interner, omg_types)
353 .context("failed pre-processing executable content in <if> element")?;
354 }
355 }
356 for executable in r#else.iter_mut().flatten() {
357 self.prebuild_exec(pg_id, executable, vars, interner, omg_types)
358 .context("failed pre-processing executable content in <else> element")?;
359 }
360 Ok(())
361 }
362 }
363 }
364
365 fn build_fsm(
366 &mut self,
367 scxml: &Scxml,
368 interner: &mut Interner,
369 omg_types: &mut OmgTypes,
370 ) -> anyhow::Result<()> {
371 trace!(target: "build", "build FSM {}", scxml.name);
372 let pg_builder = self
374 .fsm_builders
375 .get(&scxml.name)
376 .unwrap_or_else(|| panic!("builder for {} must already exist", scxml.name));
377 let pg_id = pg_builder.pg_id;
378 let ext_queue = pg_builder.ext_queue;
379 let mut vars: HashMap<String, (OmgType, Vec<(Var, Type)>)> = HashMap::new();
382 for data in scxml.datamodel.iter() {
383 let mut omg_type = data
384 .omg_type
385 .clone()
386 .ok_or_else(|| anyhow!("data {} has unknown type", data.id))?;
387 if matches!(omg_type, OmgType::Array(_, None)) {
389 omg_type = data
390 .expression
391 .as_ref()
392 .ok_or_else(|| anyhow!("expression for data '{}' required", data.id))
393 .and_then(|expr| {
394 infer_type(expr, &HashMap::new(), interner, Some(&omg_type), omg_types)
395 })?;
396 }
397 let vars_types = if let Some(expr) = data.expression.as_ref() {
398 expression(expr, interner, &vars, Some(&omg_type), omg_types)?
399 .iter()
400 .map(|expr| {
401 expr.eval_constant().map_err(CsError::Type).and_then(|val| {
402 self.cs.new_var(pg_id, val).map(|var| (var, expr.r#type()))
403 })
404 })
405 .collect::<Result<Vec<(Var, Type)>, _>>()?
406 } else {
407 omg_type
408 .to_scan_types(omg_types).with_context(|| format!("failed converting type {omg_type:?} of location {} to Scan native types", data.id))?
409 .into_iter()
410 .map(|t| {
411 (
412 self.cs
413 .new_var(pg_id, t.default_value())
414 .expect("new var"),
415 t,
416 )
417 })
418 .collect::<Vec<(Var, Type)>>()
419 };
420 vars.insert(data.id.to_owned(), (omg_type.clone(), vars_types));
421 }
422 let initial_loc = self
424 .cs
425 .new_initial_location(pg_id)
426 .expect("program graph must exist");
427 let mut states = HashMap::new();
431 states.insert(scxml.initial.to_owned(), initial_loc);
433 let current_event_var = self
436 .cs
437 .new_var(pg_id, Val::from(Natural::MAX))
438 .expect("program graph exists!");
439 let origin_var = self
442 .cs
443 .new_var(pg_id, Val::from(Natural::MAX))
444 .expect("program graph exists!");
445 let int_queue = self.cs.new_channel(vec![Type::Natural], None);
447 let _ = self.int_queues.insert(int_queue);
449 let dequeue_int = self
450 .cs
451 .new_receive(pg_id, int_queue, vec![current_event_var])
452 .expect("hand-coded args");
453 let set_int_origin = self.cs.new_action(pg_id).expect("program graph exists!");
455 self.cs
456 .add_effect(
457 pg_id,
458 set_int_origin,
459 origin_var,
460 CsExpression::from(u16::from(pg_id) as Natural),
461 )
462 .expect("hand-coded args");
463 let dequeue_ext = self
465 .cs
466 .new_receive(pg_id, ext_queue, vec![current_event_var, origin_var])
467 .expect("hand-coded args");
468
469 let mut params_vars: BTreeMap<(usize, String), (OmgType, Vec<(Var, Type)>)> =
472 BTreeMap::new(); let mut params_actions: HashMap<(PgId, usize), Action> = HashMap::new(); for (event_index, event_builder) in self
475 .events
476 .iter()
477 .enumerate()
478 .filter(|(_, eb)| eb.receivers.contains(&pg_id) && !eb.senders.is_empty())
480 .map(|(index, eb)| (index, eb.clone()))
481 .collect::<Vec<_>>()
483 {
484 let mut param_vars_vec = Vec::new();
485 let mut param_types_vec = Vec::new();
486 for (param_name, param_type) in event_builder.params.iter() {
488 let param_omg_type = param_type
489 .as_ref()
490 .ok_or_else(|| anyhow!("type of param {param_name} not found"))?;
491 let param_vars_types = param_omg_type
493 .to_scan_types(omg_types).with_context(|| format!("failed converting type {param_omg_type:?} of param {param_name} to Scan native types"))?
494 .into_iter()
495 .map(|t| {
496 (
497 self.cs
498 .new_var(pg_id, t.default_value())
499 .expect("new var"),
500 t,
501 )
502 })
503 .collect::<Vec<(Var, Type)>>();
504 param_vars_vec.extend(param_vars_types.iter().map(|(v, _)| *v));
505 param_types_vec.extend(param_vars_types.iter().map(|(_, t)| *t));
506 let old = params_vars.insert(
507 (event_index, param_name.to_owned()),
508 (param_omg_type.clone(), param_vars_types),
509 );
510 assert!(old.is_none());
511 }
512 if !param_vars_vec.is_empty() {
513 for &sender_id in event_builder.senders.iter() {
514 let params_channel = *self
516 .parameter_channels
517 .entry((sender_id, pg_id, event_index))
518 .or_insert_with(|| self.cs.new_channel(param_types_vec.clone(), None));
519 let read = self
521 .cs
522 .new_receive(pg_id, params_channel, param_vars_vec.clone())
523 .with_context(|| {
524 format!(
525 "failed building receiver for params of event '{}'",
526 event_builder.name
527 )
528 })?;
529 let old = params_actions.insert((sender_id, event_index), read);
530 assert!(old.is_none());
531 }
532 }
533 }
534 let param_vars = params_vars;
536 let param_actions = params_actions;
537
538 for (state_id, state) in scxml.states.iter() {
540 trace!(target: "build", "build state {state_id}");
541 let start_loc = *states
544 .entry(state_id.to_owned())
545 .or_insert_with(|| self.cs.new_location(pg_id).expect("program graph exists!"));
546 let mut onentry_loc = start_loc;
547 for executable in state.on_entry.iter() {
549 onentry_loc = self
552 .add_executable(
553 executable,
554 pg_id,
555 int_queue,
556 onentry_loc,
557 &vars,
558 interner,
559 omg_types,
560 )
561 .with_context(|| {
562 format!(
563 "failed building executable content on entry of state {}",
564 state.id
565 )
566 })?;
567 }
568 let onentry_loc = onentry_loc;
570
571 let mut null_trans = onentry_loc;
573 let int_queue_loc = self.cs.new_location(pg_id).expect("program graph exists!");
575 let ext_queue_loc = self.cs.new_location(pg_id).expect("program graph exists!");
577 let mut eventful_trans = self.cs.new_location(pg_id).expect("program graph exists!");
579 {
581 let int_origin_loc = self.cs.new_location(pg_id).expect("program graph exists!");
583 self.cs
585 .add_transition(pg_id, int_queue_loc, dequeue_int, int_origin_loc, None)
586 .expect("hand-coded args");
587 self.cs
589 .add_transition(pg_id, int_origin_loc, set_int_origin, eventful_trans, None)
590 .expect("hand-coded args");
591 }
592 {
597 let empty_int_queue = self
598 .cs
599 .new_probe_empty_queue(pg_id, int_queue)
600 .expect("hand-coded args");
601 self.cs
602 .add_transition(pg_id, int_queue_loc, empty_int_queue, ext_queue_loc, None)
603 .expect("hand-coded args");
604 }
605 let ext_event_processing_param =
607 self.cs.new_location(pg_id).expect("program graph exists!");
608 self.cs
610 .add_transition(
611 pg_id,
612 ext_queue_loc,
613 dequeue_ext,
614 ext_event_processing_param,
615 None,
616 )
617 .expect("hand-coded args");
618 let mut known_events = Vec::new();
620 for (event_index, event_builder) in self
625 .events
626 .iter()
627 .enumerate()
628 .filter(|(_, eb)| eb.receivers.contains(&pg_id) && !eb.senders.is_empty())
630 {
631 for &sender_id in &event_builder.senders {
632 let is_event_sender = BooleanExpr::NatEqual(
634 NaturalExpr::from(event_index as Natural),
635 NaturalExpr::Var(current_event_var),
636 ) & BooleanExpr::NatEqual(
637 NaturalExpr::from(u16::from(sender_id) as Natural),
638 NaturalExpr::Var(origin_var),
639 );
640 known_events.push(is_event_sender.to_owned());
642 if let Some(&read_params) = param_actions.get(&(sender_id, event_index)) {
643 self.cs
644 .add_transition(
645 pg_id,
646 ext_event_processing_param,
647 read_params,
648 eventful_trans,
649 Some(is_event_sender),
650 )
651 .expect("hand-coded args");
652 } else {
653 self.cs
654 .add_autonomous_transition(
655 pg_id,
656 ext_event_processing_param,
657 eventful_trans,
658 Some(is_event_sender),
659 )
660 .expect("hand-coded args");
661 }
662 }
663 }
664 let unknown_event = if known_events.is_empty() {
666 None
667 } else {
668 Some(!(BooleanExpr::Or(known_events)))
669 };
670 self.cs
671 .add_autonomous_transition(
672 pg_id,
673 ext_event_processing_param,
674 eventful_trans,
675 unknown_event,
676 )
677 .expect("has to work");
678
679 for (transition_index, transition) in state.transitions.iter().enumerate() {
681 if let Some(ref event_name) = transition.event {
683 let event_index = *self
684 .event_indexes
685 .get(event_name)
686 .expect("event must be registered");
687 if self.events[event_index].senders.is_empty() {
688 warn!(
689 "event '{event_name}' in FSM '{}' is never sent, skipping",
690 self.fsm_names.get(&pg_id.into()).expect("PG name")
691 );
692 continue;
693 }
694 }
695 trace!(
696 target: "build",
697 "build {} transition to {}",
698 transition
699 .event.as_deref()
700 .unwrap_or("eventless"),
701 transition.target
702 );
703 let target_loc = *states
705 .entry(transition.target.to_owned())
706 .or_insert_with(|| self.cs.new_location(pg_id).expect("pg_id should exist"));
707
708 if let Some(event_name) = transition.event.as_ref() {
710 let event_index = *self
711 .event_indexes
712 .get(event_name)
713 .expect("event must be registered");
714 omg_types.type_defs.extend([
715 (
716 String::from("_EventDataType"),
717 OmgTypeDef::Structure(BTreeMap::from_iter(
718 param_vars
719 .iter()
720 .filter(|((ev_ix, _), _)| *ev_ix == event_index)
721 .map(|((_, param_name), (t, _))| {
722 (param_name.to_owned(), t.clone())
723 }),
724 )),
725 ),
726 (
727 String::from("_EventType"),
728 OmgTypeDef::Structure(BTreeMap::from_iter([
729 (String::from("origin"), OmgType::Base(OmgBaseType::Uri)),
730 (
731 String::from("data"),
732 OmgType::Custom(String::from("_EventDataType")),
733 ),
734 ])),
735 ),
736 ]);
737 let mut event_vars = self.events[event_index]
738 .params
739 .keys()
740 .flat_map(|param_name| {
741 ¶m_vars
742 .get(&(event_index, param_name.clone()))
743 .expect("param")
744 .1
745 })
746 .cloned()
747 .collect::<Vec<_>>();
748 event_vars.push((origin_var, Type::Natural));
749 vars.insert(
750 String::from("_event"),
751 (OmgType::Custom(String::from("_EventType")), event_vars),
752 );
753 }
754 let cond: Option<Vec<CsExpression>> = transition
758 .cond
759 .as_ref()
760 .map(|cond| {
761 expression(
762 cond,
763 interner,
764 &vars,
765 Some(&OmgBaseType::Boolean.into()),
766 omg_types,
767 ).with_context(|| format!("failed building conditional expression for transition #{transition_index} in state {}", state.id))
768 })
769 .transpose()?;
770 if cond.as_ref().is_some_and(|cond| cond.len() != 1) {
771 bail!("condition is not a boolean expression");
772 }
773 let cond = cond.map(|cond| cond.first().expect("length 1").clone());
774 let cond = cond
775 .map(|cond| {
776 if let Expression::Boolean(bool_expr) = cond {
777 Ok(bool_expr)
778 } else {
779 bail!("condition is not a boolean expression")
780 }
781 })
782 .transpose()?;
783
784 let check_trans_loc;
787 let next_trans_loc = self.cs.new_location(pg_id).expect("{pg_id:?} exists");
789
790 let guard;
793 if let Some(event_name) = transition.event.as_ref() {
795 let event_index = *self
796 .event_indexes
797 .get(event_name)
798 .expect("event must be registered");
799 let event_match = BooleanExpr::NatEqual(
801 NaturalExpr::Var(current_event_var),
802 NaturalExpr::from(event_index as Natural),
803 );
804 guard = Some(cond.map_or(event_match.clone(), |cond| event_match & cond));
806 check_trans_loc = eventful_trans;
808 eventful_trans = next_trans_loc;
810 } else {
811 guard = cond;
814 check_trans_loc = null_trans;
816 null_trans = next_trans_loc;
818 }
819
820 let mut exec_trans_loc = self.cs.new_location(pg_id)?;
823 self.cs.add_autonomous_transition(
824 pg_id,
825 check_trans_loc,
826 exec_trans_loc,
827 guard.to_owned(),
828 )?;
829 for exec in state.on_exit.iter() {
832 exec_trans_loc = self
833 .add_executable(
834 exec,
835 pg_id,
836 int_queue,
837 exec_trans_loc,
838 &vars,
839 interner,
840 omg_types,
841 )
842 .with_context(|| {
843 format!(
844 "failed building executable content on exit of state {}",
845 state.id
846 )
847 })?;
848 }
849 for exec in transition.effects.iter() {
850 exec_trans_loc = self
851 .add_executable(
852 exec,
853 pg_id,
854 int_queue,
855 exec_trans_loc,
856 &vars,
857 interner,
858 omg_types,
859 )
860 .with_context(|| {
861 format!(
862 "failed building executable content of transition #{transition_index} of state {}",
863 state.id
864 )
865 })?;
866 }
867 self.cs
870 .add_autonomous_transition(pg_id, exec_trans_loc, target_loc, None)
871 .expect("has to work");
872 if let Some(guard) = guard {
876 self.cs
877 .add_autonomous_transition(
878 pg_id,
879 check_trans_loc,
880 next_trans_loc,
881 Some(!guard),
882 )
883 .expect("cannot fail because guard was already checked");
884 }
885 }
886
887 self.cs
890 .add_autonomous_transition(pg_id, null_trans, int_queue_loc, None)?;
891 self.cs
893 .add_autonomous_transition(pg_id, eventful_trans, int_queue_loc, None)?;
894 }
895 Ok(())
896 }
897
898 fn add_executable(
900 &mut self,
901 executable: &Executable,
902 pg_id: PgId,
903 int_queue: Channel,
904 loc: Location,
905 vars: &HashMap<String, (OmgType, Vec<(Var, Type)>)>,
906 interner: &Interner,
907 omg_types: &mut OmgTypes,
908 ) -> Result<Location, anyhow::Error> {
909 match executable {
910 Executable::Raise { event } => {
911 let event_idx = self.event_index(event);
913 let raise = self.cs.new_send(
914 pg_id,
915 int_queue,
916 vec![CsExpression::from(event_idx as Natural)],
917 )?;
918 let next_loc = self.cs.new_location(pg_id)?;
919 self.cs.add_transition(pg_id, loc, raise, next_loc, None)?;
921 Ok(next_loc)
922 }
923 Executable::Send(Send {
924 event,
925 target,
926 delay,
927 params: send_params,
928 }) => {
929 let mut send_params = send_params.clone();
931 send_params.sort_unstable_by_key(|p| p.name.clone());
932
933 let event_idx = *self
934 .event_indexes
935 .get(event)
936 .ok_or(anyhow!("event not found"))?;
937 let mut loc = loc;
938 if let Some(delay) = delay {
939 let clock = self.cs.new_clock(pg_id).expect("new clock");
941 let reset = self.cs.new_action(pg_id).expect("action");
942 self.cs.add_reset(pg_id, reset, clock).expect("add reset");
943 let next_loc = self
944 .cs
945 .new_timed_location(pg_id, &[(clock, TimeRange::new(0..*delay + 1))])
946 .expect("PG exists");
947 self.cs
948 .add_transition(pg_id, loc, reset, next_loc, None)
949 .expect("params are right");
950 loc = next_loc;
951 let next_loc = self.cs.new_location(pg_id).expect("PG exists");
952 self.cs
953 .add_autonomous_timed_transition(
954 pg_id,
955 loc,
956 next_loc,
957 None,
958 &[(clock, TimeRange::new(*delay..))],
959 )
960 .expect("autonomous timed transition");
961 loc = next_loc;
962 }
963 if let Some(target) = target {
964 let done_loc = self.cs.new_location(pg_id)?;
965 let targets;
966 let target_expr;
967 match target {
968 Target::Id(target) => {
969 let target_builder = self
970 .fsm_builders
971 .get(target)
972 .ok_or(anyhow!(format!("target {target} not found")))?;
973 targets = vec![target_builder.pg_id];
974 target_expr =
975 Some(NaturalExpr::from(u16::from(target_builder.pg_id) as Natural));
976 }
977 Target::Expr(targetexpr) => {
978 let target_exprs = expression(
979 targetexpr,
980 interner,
981 vars,
982 Some(&OmgBaseType::Uri.into()),
983 omg_types,
984 ).with_context(|| format!("failed building target expression of <send event=\"{event}\"> element"))?;
985 if target_exprs.len() != 1 {
986 bail!("epression is not a target");
987 }
988 target_expr = if let Some(Expression::Natural(nat_expr)) =
989 target_exprs.first().cloned()
990 {
991 Some(nat_expr)
992 } else {
993 bail!("targetexpr not a target expression")
994 };
995 targets = self.events[event_idx].receivers.iter().cloned().collect();
996 }
997 }
998 for target_id in targets {
999 let target_name = self.fsm_names.get(&target_id.into()).unwrap();
1000 let target_builder =
1001 self.fsm_builders.get(target_name).expect("it must exist");
1002 let target_ext_queue = target_builder.ext_queue;
1003 let send_event = self
1004 .cs
1005 .new_send(
1006 pg_id,
1007 target_ext_queue,
1008 vec![
1009 CsExpression::from(event_idx as Natural),
1010 CsExpression::from(u16::from(pg_id) as Natural),
1011 ],
1012 )
1013 .expect("params are hard-coded");
1014
1015 let mut next_loc = self.cs.new_location(pg_id).expect("PG exists");
1017 self.cs
1018 .add_transition(
1019 pg_id,
1020 loc,
1021 send_event,
1022 next_loc,
1023 target_expr.as_ref().map(|target_expr| {
1024 BooleanExpr::NatEqual(
1025 NaturalExpr::from(u16::from(target_id) as Natural),
1026 target_expr.to_owned(),
1027 )
1028 }),
1029 )
1030 .expect("params are right");
1031
1032 if !send_params.is_empty() {
1034 next_loc = self
1036 .send_params(
1037 pg_id,
1038 target_id,
1039 &send_params,
1040 event_idx,
1041 next_loc,
1042 vars,
1043 interner,
1044 omg_types,
1045 )
1046 .with_context(|| {
1047 format!("failed sending params for event '{event}'")
1048 })?;
1049 }
1050 self.cs
1052 .add_autonomous_transition(pg_id, next_loc, done_loc, None)
1053 .expect("hand-made args");
1054 }
1055 Ok(done_loc)
1057 } else {
1058 let targets = self.events[event_idx]
1061 .receivers
1062 .iter()
1063 .cloned()
1064 .collect::<Vec<_>>();
1065 let mut next_loc = loc;
1066 for target in targets {
1067 let target_name = self.fsm_names.get(&target.into()).cloned();
1068 next_loc = self.add_executable(
1069 &Executable::Send(Send {
1070 event: event.to_owned(),
1071 target: target_name.map(Target::Id),
1072 delay: *delay,
1073 params: send_params.to_owned(),
1074 }),
1075 pg_id,
1076 int_queue,
1077 next_loc,
1078 vars,
1079 interner,
1080 omg_types,
1081 )?;
1082 }
1083 Ok(next_loc)
1084 }
1085 }
1086 Executable::Assign { location, expr } => {
1087 let (omg_type, scan_vars) =
1089 vars.get(location).ok_or(anyhow!("undefined variable"))?;
1090 let expr = expression(expr, interner, vars, Some(omg_type), omg_types)
1091 .with_context(|| {
1092 format!(
1093 "failed building expression in <assign> element for location {location}"
1094 )
1095 })?;
1096 let assign = self.cs.new_action(pg_id).expect("PG exists");
1097 scan_vars
1098 .iter()
1099 .zip(expr)
1100 .try_for_each(|((var, scan_type), expr)| {
1101 if expr.r#type() == *scan_type {
1102 self.cs.add_effect(pg_id, assign, *var, expr)
1103 } else {
1104 Err(CsError::Type(TypeError::TypeMismatch))
1105 }
1106 })
1107 .with_context(|| {
1108 format!("failed building assignments for location '{location}'")
1109 })?;
1110 let next_loc = self.cs.new_location(pg_id).unwrap();
1111 self.cs.add_transition(pg_id, loc, assign, next_loc, None)?;
1112 Ok(next_loc)
1113 }
1114 Executable::If(If { r#elif, r#else, .. }) => {
1115 let end_loc = self.cs.new_location(pg_id).unwrap();
1117 let mut curr_loc = loc;
1118 for (cond, execs) in r#elif {
1119 let mut next_loc = self.cs.new_location(pg_id).unwrap();
1120 let cond = expression(
1121 cond,
1122 interner,
1123 vars,
1124 Some(&OmgBaseType::Boolean.into()),
1125 omg_types,
1126 )
1127 .context("failed building condition expression in <if> element")?;
1128 if cond.len() != 1 {
1129 bail!("<cond> is not a boolean expression");
1130 }
1131 let cond = if let Expression::Boolean(bool_expr) =
1132 cond.first().expect("len equals 1").clone()
1133 {
1134 bool_expr
1135 } else {
1136 bail!("targetexpr not a target expression")
1137 };
1138 self.cs.add_autonomous_transition(
1139 pg_id,
1140 curr_loc,
1141 next_loc,
1142 Some(cond.to_owned()),
1143 )?;
1144 for exec in execs {
1145 next_loc = self
1146 .add_executable(
1147 exec, pg_id, int_queue, next_loc, vars, interner, omg_types,
1148 )
1149 .context("failed building executable content in <if> element")?;
1150 }
1151 self.cs
1153 .add_autonomous_transition(pg_id, next_loc, end_loc, None)?;
1154 let old_loc = curr_loc;
1156 curr_loc = self.cs.new_location(pg_id).unwrap();
1157 self.cs
1158 .add_autonomous_transition(pg_id, old_loc, curr_loc, Some(!cond))
1159 .unwrap();
1160 }
1161 for executable in r#else.iter().flatten() {
1163 curr_loc = self
1164 .add_executable(
1165 executable, pg_id, int_queue, curr_loc, vars, interner, omg_types,
1166 )
1167 .context("failed building executable content in <else> element")?;
1168 }
1169 self.cs
1170 .add_autonomous_transition(pg_id, curr_loc, end_loc, None)?;
1171 Ok(end_loc)
1172 }
1173 }
1174 }
1175
1176 fn send_params(
1178 &mut self,
1179 pg_id: PgId,
1180 target_id: PgId,
1181 params: &[Param],
1182 event_idx: usize,
1183 param_loc: Location,
1184 vars: &HashMap<String, (OmgType, Vec<(Var, Type)>)>,
1185 interner: &Interner,
1186 omg_types: &mut OmgTypes,
1187 ) -> Result<Location, anyhow::Error> {
1188 let mut exprs = Vec::new();
1190 for (p, param_type) in self.events[event_idx].params.clone().iter() {
1191 if let Some(param) = params.iter().find(|param| param.name == *p) {
1193 assert_eq!(¶m.omg_type, param_type);
1194 let param_type = param_type
1195 .as_ref()
1196 .ok_or_else(|| anyhow!("unknown type for param {p}"))?;
1197 let expr = expression(¶m.expr, interner, vars, Some(param_type), omg_types)
1199 .with_context(|| {
1200 format!(
1201 "failed building expr for param '{}' of type {param_type:?}",
1202 param.name
1203 )
1204 })?;
1205 exprs.extend_from_slice(&expr);
1206 } else {
1207 warn!("missing param {p}, sending default value");
1208 let expr = param_type
1210 .as_ref()
1211 .ok_or_else(|| anyhow!("unknown type for param {p}"))?
1212 .to_scan_types(omg_types)
1213 .with_context(|| format!("failed converting param '{p}' type to Scan types"))?;
1214 exprs.extend(
1215 expr.iter()
1216 .map(|scan_type| Expression::from(scan_type.default_value())),
1217 );
1218 }
1219 }
1220 let scan_types = exprs.iter().map(|expr| expr.r#type()).collect::<Vec<_>>();
1222 let param_chn = *self
1223 .parameter_channels
1224 .entry((pg_id, target_id, event_idx))
1225 .or_insert_with(|| self.cs.new_channel(scan_types, None));
1226 let pass_param = self.cs.new_send(pg_id, param_chn, exprs)?;
1228 let next_loc = self.cs.new_location(pg_id).expect("PG exists");
1229 self.cs
1230 .add_transition(pg_id, param_loc, pass_param, next_loc, None)
1231 .expect("hand-made params are correct");
1232 Ok(next_loc)
1233 }
1234
1235 fn build_ports(&mut self, parser: &mut Parser) -> anyhow::Result<()> {
1236 for (port_id, port) in parser.properties.ports.iter() {
1237 let origin_builder = self
1238 .fsm_builders
1239 .get(&port.origin)
1240 .ok_or_else(|| anyhow!("missing origin fsm {} for port {port_id}", port.origin))?;
1241 let origin = origin_builder.pg_id;
1242 let target_builder = self
1243 .fsm_builders
1244 .get(&port.target)
1245 .ok_or_else(|| anyhow!("missing target fsm {} for port {port_id}", port.target))?;
1246 let target = target_builder.pg_id;
1247 let event_id = *self
1248 .event_indexes
1249 .get(&port.event)
1250 .ok_or_else(|| anyhow!("missing event {} for port {port_id}", port.event))?;
1251 let event_builder = &self.events[event_id];
1252 if let Some((param, init)) = &port.param {
1253 let param_start_idx = event_builder
1254 .params
1255 .range(..param.clone())
1256 .map(|(_, omg_type)| {
1257 omg_type
1258 .as_ref()
1259 .ok_or_else(|| {
1260 anyhow!("type of param {param} for port {port_id} not found")
1261 })
1262 .and_then(|omg_type| omg_type.size(&parser.types))
1263 })
1264 .sum::<anyhow::Result<usize>>()?;
1265 let param_type = event_builder
1266 .params
1267 .get(param)
1268 .ok_or_else(|| {
1269 anyhow!(
1270 "param {param} of event {} in port {port_id} not found",
1271 port.event
1272 )
1273 })?
1274 .clone();
1275 let init = expression::<Var, Expression<Var>>(
1276 init,
1277 &parser.interner,
1278 &HashMap::new(),
1279 param_type.as_ref(),
1280 &mut parser.types,
1281 )
1282 .with_context(|| {
1283 format!("failed building default value expression for port {port_id}")
1284 })?
1285 .iter()
1286 .map(|expr| expr.eval_constant())
1287 .collect::<Result<Vec<_>, _>>()
1288 .with_context(|| format!("failed evaluating default value for port {port_id}"))?;
1289 let channel = self
1290 .parameter_channels
1291 .get(&(origin, target, event_id))
1292 .expect("parameters' channel for event in port");
1293 self.port_vars.insert(
1294 port_id.to_owned(),
1295 (
1296 param_type.ok_or_else(|| {
1297 anyhow!("type of param {param} of event {} not found", port.event)
1298 })?,
1299 init.iter()
1300 .enumerate()
1301 .map(|(param_idx, init)| {
1302 Expression::from_var(
1303 Atom::State(*channel, param_start_idx + param_idx),
1304 init.r#type(),
1305 )
1306 })
1307 .collect(),
1308 ),
1309 );
1310
1311 let index = match self.ports.binary_search_by_key(channel, |(ch, _)| *ch) {
1312 Ok(index) => index,
1313 Err(index) => {
1314 let default = event_builder
1315 .params
1316 .values()
1317 .flat_map(|omg_type| {
1318 omg_type
1319 .as_ref()
1320 .ok_or_else(|| {
1321 anyhow!(
1322 "type of param {param} for port {port_id} not found"
1323 )
1324 })
1325 .expect("omg type")
1326 .to_scan_types(&parser.types)
1327 .expect("omg type to scan type")
1328 .into_iter()
1329 .map(|t| t.default_value())
1330 })
1331 .collect::<Vec<Val>>();
1332 self.ports.insert(index, (*channel, default));
1333 index
1334 }
1335 };
1336 init.iter().enumerate().for_each(|(param_idx, init)| {
1337 self.ports[index].1[param_start_idx + param_idx] = *init
1338 });
1339 } else {
1340 if let Some(&channel) = self.parameter_channels.get(&(origin, target, event_id)) {
1344 self.port_vars.insert(
1345 port_id.to_owned(),
1346 (
1347 OmgType::Base(OmgBaseType::Boolean),
1348 vec![Expression::Boolean(BooleanExpr::Var(Atom::Event(channel)))],
1349 ),
1350 );
1351 } else {
1352 let ext_queue = target_builder.ext_queue;
1353 self.port_vars.insert(
1354 port_id.to_owned(),
1355 (
1356 OmgType::Base(OmgBaseType::Boolean),
1357 vec![Expression::Boolean(BooleanExpr::And(vec![
1358 BooleanExpr::Var(Atom::Event(ext_queue)),
1359 BooleanExpr::NatEqual(
1360 NaturalExpr::Var(Atom::State(ext_queue, 0)),
1361 NaturalExpr::Const(event_id as Natural),
1362 ),
1363 BooleanExpr::NatEqual(
1364 NaturalExpr::Var(Atom::State(ext_queue, 1)),
1365 NaturalExpr::Const(u16::from(origin) as Natural),
1366 ),
1367 ]))],
1368 ),
1369 );
1370 if let Err(index) = self.ports.binary_search_by_key(&ext_queue, |(ch, _)| *ch) {
1372 self.ports.insert(
1373 index,
1374 (
1375 ext_queue,
1376 vec![Val::from(Natural::MAX), Val::from(Natural::MAX)],
1377 ),
1378 );
1379 }
1380 }
1381 }
1382 }
1383 Ok(())
1384 }
1385
1386 fn build_properties(
1387 &mut self,
1388 parser: &mut Parser,
1389 properties: &[String],
1390 all_properties: bool,
1391 ) -> anyhow::Result<()> {
1392 for predicate in parser.properties.predicates.iter() {
1393 let predicate = expression(
1394 predicate,
1395 &parser.interner,
1396 &self.port_vars,
1397 Some(&OmgType::Base(OmgBaseType::Boolean)),
1398 &mut parser.types,
1399 )
1400 .with_context(|| format!("failed building predicate {predicate:?}"))?;
1401 if predicate.len() != 1 {
1402 bail!("predicate is not a boolean expression");
1403 } else if let Expression::Boolean(predicate) =
1404 predicate.first().expect("len of predicate is 1")
1405 {
1406 self.predicates.push(predicate.clone());
1407 } else {
1408 bail!("predicate is not a boolean expression");
1409 }
1410 }
1411 if !all_properties {
1412 parser
1413 .properties
1414 .guarantees
1415 .retain(|(name, _)| properties.contains(name));
1416 }
1417 self.guarantees = parser.properties.guarantees.clone();
1418 self.assumes = parser.properties.assumes.clone();
1419 Ok(())
1420 }
1421
1422 fn build_model(mut self, parser: Parser) -> (TransitionSystem, PmtlOracle, ScxmlModel) {
1423 let cs = self.cs.build();
1424 let mut model = TransitionSystem::new(cs);
1425 let port_vars = self
1426 .port_vars
1427 .into_iter()
1428 .map(|(name, (omg_type, exprs))| (name, omg_type, exprs))
1429 .collect::<Vec<_>>();
1430 self.ports.sort_unstable_by_key(|(c, _)| *c);
1431 for (channel, init) in &self.ports {
1432 model.add_port(*channel, init.clone()).expect("add port");
1433 }
1434 for pred_expr in self.predicates {
1435 model.add_predicate(pred_expr).expect("add predicate");
1436 }
1437 model.shrink();
1439 let (guarantee_names, guarantees): (Vec<_>, Vec<_>) = self.guarantees.into_iter().unzip();
1440 let (assume_names, assumes): (Vec<_>, Vec<_>) = self.assumes.into_iter().unzip();
1441 let oracle = PmtlOracle::new(assumes.as_slice(), guarantees.as_slice());
1442 let mut events = Vec::from_iter(self.event_indexes);
1443 events.sort_unstable_by_key(|(_, idx)| *idx);
1444 let events = events
1445 .into_iter()
1446 .enumerate()
1447 .map(|(enum_i, (name, idx))| {
1448 assert_eq!(enum_i, idx);
1449 let params = &self.events[idx].params;
1450 let params_types =
1451 (!params.is_empty()).then_some(OmgTypeDef::Structure(BTreeMap::from_iter(
1452 params
1453 .iter()
1454 .map(|(name, t)| (name.clone(), t.as_ref().unwrap().clone())),
1455 )));
1456 (name, params_types)
1457 })
1458 .collect();
1459
1460 (
1461 model,
1462 oracle,
1463 ScxmlModel {
1464 fsm_names: self.fsm_names,
1465 parameters: self
1466 .parameter_channels
1467 .into_iter()
1468 .map(|((src, trg, event), chn)| (chn, (src, trg, event)))
1469 .collect(),
1470 ext_queues: self
1471 .fsm_builders
1472 .values()
1473 .map(|b| (b.ext_queue, b.pg_id))
1474 .collect(),
1475 int_queues: self.int_queues,
1476 events,
1477 port_vars,
1478 assumes: assume_names,
1479 guarantees: guarantee_names,
1480 omg_types: parser.types,
1481 ports: self.ports.iter().map(|(c, _)| *c).collect(),
1482 },
1483 )
1484 }
1485}