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