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