1use alloc::boxed::Box;
18use alloc::collections::VecDeque;
19use alloc::format;
20use alloc::string::{String, ToString};
21use alloc::vec::Vec;
22
23use crate::bitset::StateSet;
24use crate::compile::{CompiledChart, ROOT, StateIdx, TransIdx};
25use crate::datamodel::{
26 Datamodel, Environment, EvalError, Event, EventType, SessionInfo, StateQuery, Value,
27};
28use crate::ir::{Binding, Content, Data, Exec, ExecKind, Invoke, Param, Send, StateKind};
29use crate::kernel::{self, History};
30use crate::trace::{MacrostepTrace, MicrostepTrace, TraceInput, TransitionRef};
31
32pub const DEFAULT_MICROSTEP_BUDGET: u32 = 10_000;
34pub const SCXML_EVENT_PROCESSOR: &str = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
36pub const SCXML_INVOKE_TYPE: &str = "http://www.w3.org/TR/scxml/";
38
39#[derive(Clone, Debug, PartialEq, Eq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub struct Options {
43 pub microstep_budget: u32,
46 pub send_types: Vec<String>,
48 pub invoke_types: Vec<String>,
50}
51
52impl Default for Options {
53 fn default() -> Self {
54 Options {
55 microstep_budget: DEFAULT_MICROSTEP_BUDGET,
56 send_types: alloc::vec![SCXML_EVENT_PROCESSOR.to_string(), "scxml".to_string()],
57 invoke_types: alloc::vec![
58 SCXML_INVOKE_TYPE.to_string(),
59 "http://www.w3.org/TR/scxml".to_string(),
60 "scxml".to_string(),
61 ],
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69pub enum Status {
70 Running,
72 Terminated,
74}
75
76#[derive(Clone, Debug, PartialEq, Eq)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79pub struct Invocation {
80 pub state: StateIdx,
82 pub index: usize,
84 pub invokeid: String,
86 pub autoforward: bool,
88 pub completed: bool,
90}
91
92#[derive(Clone, Debug, PartialEq)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100pub struct Session {
101 pub sessionid: String,
103 pub status: Status,
105 pub configuration: StateSet,
107 pub history: History,
109 pub internal_queue: VecDeque<Event>,
111 pub invocations: Vec<Invocation>,
113 pub data_initialized: StateSet,
115 pub send_counter: u64,
117 pub invoke_counter: u64,
119 pub macrosteps: u64,
121 pub options: Options,
123}
124
125impl Session {
126 pub fn is_terminated(&self) -> bool {
128 self.status == Status::Terminated
129 }
130
131 pub fn active(&self, chart: &CompiledChart) -> Vec<String> {
133 chart.ids(&self.configuration)
134 }
135
136 pub fn is_active(&self, chart: &CompiledChart, id: &str) -> bool {
138 chart
139 .index_of(id)
140 .is_some_and(|i| self.configuration.contains(i))
141 }
142}
143
144#[derive(Clone, Debug, Default, PartialEq)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147pub struct StartInput {
148 pub info: SessionInfo,
150 pub env: Environment,
152 pub options: Options,
154 #[cfg_attr(feature = "serde", serde(default))]
159 pub data: Vec<(String, Value)>,
160 #[cfg_attr(feature = "serde", serde(default))]
167 pub sessions: Option<Vec<String>>,
168}
169
170#[derive(Clone, Debug, PartialEq)]
172#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
173pub enum InputKind {
174 Event(Event),
176 Cancel,
178}
179
180#[derive(Clone, Debug, PartialEq)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183pub struct Input {
184 pub env: Environment,
186 pub kind: InputKind,
188 #[cfg_attr(feature = "serde", serde(default))]
195 pub sessions: Option<Vec<String>>,
196}
197
198impl Input {
199 pub fn event(event: Event, env: Environment) -> Self {
201 Input {
202 env,
203 kind: InputKind::Event(event),
204 sessions: None,
205 }
206 }
207
208 pub fn cancel(env: Environment) -> Self {
210 Input {
211 env,
212 kind: InputKind::Cancel,
213 sessions: None,
214 }
215 }
216}
217
218#[derive(Clone, Debug, PartialEq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221pub struct SendRequest {
222 pub sendid: String,
224 pub event: Event,
227 pub target: Option<String>,
229 pub type_: String,
231 pub delay_ms: u64,
233}
234
235#[derive(Clone, Debug, PartialEq)]
237#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
238pub struct InvokeRequest {
239 pub invokeid: String,
241 pub state: String,
243 pub type_: String,
245 pub src: Option<String>,
247 pub content: Option<Value>,
249 pub params: Value,
251 pub autoforward: bool,
253}
254
255#[derive(Clone, Debug, PartialEq)]
258#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
259pub enum Effect {
260 Send(SendRequest),
262 Cancel {
264 sendid: String,
266 },
267 Invoke(InvokeRequest),
269 CancelInvoke {
272 invokeid: String,
274 },
275 Forward {
277 invokeid: String,
279 event: Event,
281 },
282 Log {
284 label: Option<String>,
286 value: Option<Value>,
288 },
289 SessionDone {
291 data: Value,
293 cancelled: bool,
295 },
296}
297
298#[derive(Clone, Debug, PartialEq)]
300pub struct Outcome {
301 pub session: Session,
303 pub effects: Vec<Effect>,
305 pub trace: MacrostepTrace,
307}
308
309#[derive(Clone, Debug, PartialEq)]
311pub enum StartError {
312 UnsupportedDatamodel {
314 name: String,
316 },
317 DatamodelMismatch {
319 chart: String,
321 given: String,
323 },
324 Datamodel(EvalError),
326 ChartRefused {
329 datamodel: String,
331 problems: Vec<String>,
333 },
334 BudgetExceeded {
336 budget: u32,
338 partial: Box<Outcome>,
340 },
341}
342
343#[derive(Clone, Debug, PartialEq)]
345pub enum StepError {
346 Terminated,
348 BudgetExceeded {
350 budget: u32,
352 partial: Box<Outcome>,
354 },
355}
356
357fn new_session(chart: &CompiledChart, sessionid: String, options: Options) -> Session {
359 Session {
360 sessionid,
361 status: Status::Running,
362 configuration: chart.empty_set(),
363 history: History::new(chart.len()),
364 internal_queue: VecDeque::new(),
365 invocations: Vec::new(),
366 data_initialized: chart.empty_set(),
367 send_counter: 0,
368 invoke_counter: 0,
369 macrosteps: 0,
370 options,
371 }
372}
373
374pub fn start(
381 chart: &CompiledChart,
382 input: StartInput,
383 dm: &mut dyn Datamodel,
384) -> Result<Outcome, StartError> {
385 if dm.name() != chart.datamodel() {
386 return Err(StartError::DatamodelMismatch {
387 chart: chart.datamodel().to_string(),
388 given: dm.name().to_string(),
389 });
390 }
391 dm.init_session(&input.info)
392 .map_err(StartError::Datamodel)?;
393 dm.set_environment(&input.env);
394 let session = new_session(chart, input.info.sessionid.clone(), input.options);
395 let invoker_data = input.data;
396 let mut d = Driver::new(
397 chart,
398 session,
399 dm,
400 TraceInput::Start {
401 sessionid: input.info.sessionid,
402 },
403 );
404 d.sessions = input.sessions;
405 let budget = d.s.options.microstep_budget;
406 match d.run_start(&invoker_data) {
407 Ok(()) => Ok(d.finish()),
408 Err(Budget) => Err(StartError::BudgetExceeded {
409 budget,
410 partial: Box::new(d.finish()),
411 }),
412 }
413}
414
415pub fn step(
420 chart: &CompiledChart,
421 session: Session,
422 input: Input,
423 dm: &mut dyn Datamodel,
424) -> Result<Outcome, StepError> {
425 if session.status == Status::Terminated {
426 return Err(StepError::Terminated);
427 }
428 dm.set_environment(&input.env);
429 let trace_input = match &input.kind {
430 InputKind::Event(e) => TraceInput::Event(e.clone()),
431 InputKind::Cancel => TraceInput::Cancel,
432 };
433 let budget = session.options.microstep_budget;
434 let mut d = Driver::new(chart, session, dm, trace_input);
435 d.sessions = input.sessions;
436 let r = match input.kind {
437 InputKind::Cancel => {
438 d.exit_interpreter(true);
439 Ok(())
440 }
441 InputKind::Event(ev) => d.run_external(ev),
442 };
443 match r {
444 Ok(()) => Ok(d.finish()),
445 Err(Budget) => Err(StepError::BudgetExceeded {
446 budget,
447 partial: Box::new(d.finish()),
448 }),
449 }
450}
451
452struct Budget;
454
455struct Stop;
457
458struct Active<'a> {
460 chart: &'a CompiledChart,
461 config: &'a StateSet,
462}
463
464impl StateQuery for Active<'_> {
465 fn in_state(&self, id: &str) -> bool {
466 self.chart
467 .index_of(id)
468 .is_some_and(|i| self.config.contains(i))
469 }
470
471 fn active_states(&self) -> Option<Vec<String>> {
472 Some(
473 self.chart
474 .states()
475 .iter()
476 .enumerate()
477 .filter(|(i, _)| *i != 0 && self.config.contains(*i))
478 .map(|(_, s)| s.id.clone())
479 .collect(),
480 )
481 }
482}
483
484pub const EXECUTION_ERROR_MESSAGE: &str = "execution error";
486pub const COMMUNICATION_ERROR_MESSAGE: &str = "target session does not exist";
488
489fn platform_error(name: &str, message: &str, sendid: Option<String>) -> Event {
492 let kind = name.strip_prefix("error.").unwrap_or(name);
493 Event {
494 name: name.to_string(),
495 type_: EventType::Platform,
496 sendid,
497 data: Value::Object(Vec::from([
498 (String::from("kind"), Value::String(kind.to_string())),
499 (String::from("message"), Value::String(message.to_string())),
500 ])),
501 ..Event::default()
502 }
503}
504
505struct Capture<'d> {
508 inner: &'d mut dyn Datamodel,
509 last: Option<EvalError>,
510}
511
512impl Capture<'_> {
513 fn note<T>(&mut self, r: Result<T, EvalError>) -> Result<T, EvalError> {
514 self.last = r.as_ref().err().cloned();
515 r
516 }
517}
518
519impl Datamodel for Capture<'_> {
520 fn name(&self) -> &str {
521 self.inner.name()
522 }
523 fn init_session(&mut self, info: &SessionInfo) -> Result<(), EvalError> {
524 let r = self.inner.init_session(info);
525 self.note(r)
526 }
527 fn set_environment(&mut self, env: &Environment) {
528 self.inner.set_environment(env)
529 }
530 fn declare(&mut self, id: &str) -> Result<(), EvalError> {
531 let r = self.inner.declare(id);
532 self.note(r)
533 }
534 fn set_event(&mut self, event: &Event) {
535 self.inner.set_event(event)
536 }
537 fn check_event(&self, event: &Event) -> Result<(), EvalError> {
538 self.inner.check_event(event)
539 }
540 fn eval_cond(&mut self, expr: &str, states: &dyn StateQuery) -> Result<bool, EvalError> {
541 let r = self.inner.eval_cond(expr, states);
542 self.note(r)
543 }
544 fn eval_value(&mut self, expr: &str, states: &dyn StateQuery) -> Result<Value, EvalError> {
545 let r = self.inner.eval_value(expr, states);
546 self.note(r)
547 }
548 fn eval_content(&mut self, content: &str) -> Result<Value, EvalError> {
549 let r = self.inner.eval_content(content);
550 self.note(r)
551 }
552 fn assign_expr(
553 &mut self,
554 location: &str,
555 expr: &str,
556 states: &dyn StateQuery,
557 ) -> Result<(), EvalError> {
558 let r = self.inner.assign_expr(location, expr, states);
559 self.note(r)
560 }
561 fn assign_value(&mut self, location: &str, value: Value) -> Result<(), EvalError> {
562 let r = self.inner.assign_value(location, value);
563 self.note(r)
564 }
565 fn bind_var(&mut self, name: &str, value: Value) -> Result<(), EvalError> {
566 let r = self.inner.bind_var(name, value);
567 self.note(r)
568 }
569 fn is_location(&mut self, location: &str) -> bool {
570 self.last = None;
571 self.inner.is_location(location)
572 }
573 fn exec_script(&mut self, script: &str, states: &dyn StateQuery) -> Result<(), EvalError> {
574 let r = self.inner.exec_script(script, states);
575 self.note(r)
576 }
577 fn foreach_items(
578 &mut self,
579 array: &str,
580 states: &dyn StateQuery,
581 ) -> Result<Vec<Value>, EvalError> {
582 let r = self.inner.foreach_items(array, states);
583 self.note(r)
584 }
585 fn snapshot(&self) -> Value {
586 self.inner.snapshot()
587 }
588 fn restore(&mut self, snapshot: &Value) -> Result<(), EvalError> {
589 let r = self.inner.restore(snapshot);
590 self.note(r)
591 }
592}
593
594pub fn parse_delay(text: &str) -> Option<u64> {
597 let t = text.trim();
598 let (num, factor) = if let Some(n) = t.strip_suffix("ms") {
599 (n.trim(), 1u64)
600 } else if let Some(n) = t.strip_suffix('s') {
601 (n.trim(), 1000u64)
602 } else {
603 (t, 1u64)
604 };
605 if num.is_empty() {
606 return None;
607 }
608 let (int, frac) = match num.split_once('.') {
609 Some((i, f)) => (i, f),
610 None => (num, ""),
611 };
612 if (int.is_empty() && frac.is_empty())
613 || !int.bytes().all(|b| b.is_ascii_digit())
614 || !frac.bytes().all(|b| b.is_ascii_digit())
615 {
616 return None;
617 }
618 let mut ms: u64 = 0;
619 for b in int.bytes() {
620 ms = ms.checked_mul(10)?.checked_add(u64::from(b - b'0'))?;
621 }
622 ms = ms.checked_mul(factor)?;
623 let mut scale = factor;
625 for b in frac.bytes() {
626 scale /= 10;
627 if scale == 0 {
628 break;
629 }
630 ms = ms.checked_add(u64::from(b - b'0') * scale)?;
631 }
632 Some(ms)
633}
634
635fn value_to_string(v: Value) -> Result<String, EvalError> {
636 match v {
637 Value::String(s) => Ok(s),
638 Value::Bool(b) => Ok(if b { "true" } else { "false" }.to_string()),
639 Value::Number(n) => {
640 if n.is_finite() && n == (n as i64) as f64 {
641 Ok(format!("{}", n as i64))
642 } else {
643 Ok(format!("{n}"))
644 }
645 }
646 other => Err(EvalError::new(format!("expected a string, got {other:?}"))),
647 }
648}
649
650struct Driver<'c, 'd> {
652 chart: &'c CompiledChart,
653 s: Session,
654 dm: Capture<'d>,
655 effects: Vec<Effect>,
656 to_invoke: StateSet,
657 running: bool,
658 terminated_now: bool,
659 microsteps: Vec<MicrostepTrace>,
660 used: u32,
661 input: TraceInput,
662 sessions: Option<Vec<String>>,
664}
665
666impl<'c, 'd> Driver<'c, 'd> {
667 fn new(
668 chart: &'c CompiledChart,
669 s: Session,
670 dm: &'d mut dyn Datamodel,
671 input: TraceInput,
672 ) -> Self {
673 Driver {
674 chart,
675 s,
676 dm: Capture {
677 inner: dm,
678 last: None,
679 },
680 effects: Vec::new(),
681 to_invoke: chart.empty_set(),
682 running: true,
683 terminated_now: false,
684 microsteps: Vec::new(),
685 used: 0,
686 input,
687 sessions: None,
688 }
689 }
690
691 fn finish(mut self) -> Outcome {
692 let trace = MacrostepTrace {
693 index: self.s.macrosteps,
694 input: self.input,
695 microsteps: self.microsteps,
696 configuration: self.chart.ids(&self.s.configuration),
697 effects: self.effects.clone(),
698 terminated: self.terminated_now,
699 };
700 self.s.macrosteps += 1;
701 Outcome {
702 session: self.s,
703 effects: self.effects,
704 trace,
705 }
706 }
707
708 fn queue_error(&mut self, sendid: Option<String>) {
713 let last = self.dm.last.take();
714 let message = last
715 .as_ref()
716 .map_or(EXECUTION_ERROR_MESSAGE, |e| e.message.as_str());
717 self.s
718 .internal_queue
719 .push_back(platform_error("error.execution", message, sendid));
720 }
721
722 fn eval_value(&mut self, expr: &str) -> Result<Value, EvalError> {
723 let q = Active {
724 chart: self.chart,
725 config: &self.s.configuration,
726 };
727 self.dm.eval_value(expr, &q)
728 }
729
730 fn eval_string(&mut self, expr: &str) -> Result<String, EvalError> {
731 self.eval_value(expr).and_then(value_to_string)
732 }
733
734 fn eval_cond(&mut self, expr: &str) -> Result<bool, EvalError> {
735 let q = Active {
736 chart: self.chart,
737 config: &self.s.configuration,
738 };
739 self.dm.eval_cond(expr, &q)
740 }
741
742 fn eval_payload(
743 &mut self,
744 namelist: &[String],
745 params: &[Param],
746 content: Option<&Content>,
747 ) -> Result<Value, EvalError> {
748 if let Some(c) = content {
749 return match (&c.expr, &c.body) {
750 (Some(x), _) => self.eval_value(x),
751 (None, Some(b)) => self.dm.eval_content(b),
752 (None, None) => Ok(Value::Undefined),
753 };
754 }
755 if namelist.is_empty() && params.is_empty() {
756 return Ok(Value::Undefined);
757 }
758 let mut pairs = Vec::new();
759 for name in namelist {
760 let v = self.eval_value(name)?;
761 pairs.push((name.clone(), v));
762 }
763 for p in params {
764 let v = match (&p.expr, &p.location) {
765 (Some(x), _) => self.eval_value(x)?,
766 (None, Some(l)) => self.eval_value(l)?,
767 (None, None) => Value::Undefined,
768 };
769 pairs.push((p.name.clone(), v));
770 }
771 Ok(Value::Object(pairs))
772 }
773
774 fn init_data(&mut self, items: &'c [Data]) {
775 self.init_data_with(items, &[]);
776 }
777
778 fn init_data_with(&mut self, items: &'c [Data], given: &[(String, Value)]) {
780 for d in items {
781 let r = if let Some((_, v)) = given.iter().find(|(n, _)| *n == d.id) {
782 self.dm.assign_value(&d.id, v.clone())
783 } else if let Some(e) = &d.expr {
784 self.eval_value(e)
785 .and_then(|v| self.dm.assign_value(&d.id, v))
786 } else if let Some(c) = &d.content {
787 self.dm
788 .eval_content(c)
789 .and_then(|v| self.dm.assign_value(&d.id, v))
790 } else if let Some(src) = &d.src {
791 Err(EvalError::new(format!("data src {src} was not resolved")))
792 } else {
793 Ok(())
794 };
795 if r.is_err() {
796 self.queue_error(None);
797 }
798 }
799 }
800
801 fn exec_block(&mut self, block: &'c [Exec]) -> Result<(), Stop> {
806 for e in block {
807 self.exec(e)?;
808 }
809 Ok(())
810 }
811
812 fn fail<T>(&mut self, sendid: Option<String>) -> Result<T, Stop> {
813 self.queue_error(sendid);
814 Err(Stop)
815 }
816
817 fn exec(&mut self, e: &'c Exec) -> Result<(), Stop> {
818 match &e.kind {
819 ExecKind::Raise { event } => {
820 self.s
821 .internal_queue
822 .push_back(Event::named(event.clone(), EventType::Internal));
823 Ok(())
824 }
825 ExecKind::Send(send) => self.exec_send(send),
826 ExecKind::Cancel { sendid, sendidexpr } => {
827 let id = match (sendid, sendidexpr) {
828 (Some(s), _) => s.clone(),
829 (None, Some(x)) => match self.eval_string(x) {
830 Ok(s) => s,
831 Err(_) => return self.fail(None),
832 },
833 (None, None) => return self.fail(None),
834 };
835 self.effects.push(Effect::Cancel { sendid: id });
836 Ok(())
837 }
838 ExecKind::Log { label, expr } => {
839 let value = match expr {
840 Some(x) => match self.eval_value(x) {
841 Ok(v) => Some(v),
842 Err(_) => return self.fail(None),
843 },
844 None => None,
845 };
846 self.effects.push(Effect::Log {
847 label: label.clone(),
848 value,
849 });
850 Ok(())
851 }
852 ExecKind::Assign {
853 location,
854 expr,
855 content,
856 } => {
857 let r = match (expr, content) {
858 (Some(x), _) => {
859 let q = Active {
860 chart: self.chart,
861 config: &self.s.configuration,
862 };
863 self.dm.assign_expr(location, x, &q)
864 }
865 (None, Some(c)) => self
866 .dm
867 .eval_content(c)
868 .and_then(|v| self.dm.assign_value(location, v)),
869 (None, None) => self.dm.assign_value(location, Value::Undefined),
870 };
871 if r.is_err() {
872 return self.fail(None);
873 }
874 Ok(())
875 }
876 ExecKind::Script(script) => {
877 let r = match &script.body {
878 Some(body) => {
879 let q = Active {
880 chart: self.chart,
881 config: &self.s.configuration,
882 };
883 self.dm.exec_script(body, &q)
884 }
885 None => Err(EvalError::new("script source was not resolved")),
886 };
887 if r.is_err() {
888 return self.fail(None);
889 }
890 Ok(())
891 }
892 ExecKind::If { branches } => {
893 for br in branches {
894 let take = match &br.cond {
895 None => true,
896 Some(c) => match self.eval_cond(c) {
897 Ok(b) => b,
898 Err(_) => return self.fail(None),
901 },
902 };
903 if take {
904 return self.exec_block(&br.body);
905 }
906 }
907 Ok(())
908 }
909 ExecKind::Foreach {
910 array,
911 item,
912 index,
913 body,
914 } => {
915 let items = {
916 let q = Active {
917 chart: self.chart,
918 config: &self.s.configuration,
919 };
920 self.dm.foreach_items(array, &q)
921 };
922 let items = match items {
923 Ok(v) => v,
924 Err(_) => return self.fail(None),
925 };
926 for (i, v) in items.into_iter().enumerate() {
927 if self.dm.bind_var(item, v).is_err() {
928 return self.fail(None);
929 }
930 if let Some(ix) = index
931 && self.dm.bind_var(ix, Value::Number(i as f64)).is_err()
932 {
933 return self.fail(None);
934 }
935 self.exec_block(body)?;
936 }
937 Ok(())
938 }
939 ExecKind::Extension(_) => Ok(()),
941 }
942 }
943
944 fn exec_send(&mut self, send: &'c Send) -> Result<(), Stop> {
945 let (sendid, visible) = match (&send.id, &send.idlocation) {
948 (Some(id), _) => (id.clone(), true),
949 (None, loc) => {
950 self.s.send_counter += 1;
951 let id = format!("{}.send.{}", self.s.sessionid, self.s.send_counter);
952 if let Some(loc) = loc
953 && self
954 .dm
955 .assign_value(loc, Value::String(id.clone()))
956 .is_err()
957 {
958 return self.fail(Some(id));
959 }
960 (id, loc.is_some())
961 }
962 };
963 let err_id = Some(sendid.clone());
964 let name = match (&send.event, &send.eventexpr) {
966 (Some(n), _) => n.clone(),
967 (None, Some(x)) => match self.eval_string(x) {
968 Ok(n) => n,
969 Err(_) => return self.fail(err_id),
970 },
971 (None, None) => String::new(),
972 };
973 let target = match (&send.target, &send.targetexpr) {
974 (Some(t), _) => Some(t.clone()),
975 (None, Some(x)) => match self.eval_string(x) {
976 Ok(t) => Some(t),
977 Err(_) => return self.fail(err_id),
978 },
979 (None, None) => None,
980 };
981 let type_ = match (&send.type_, &send.typeexpr) {
982 (Some(t), _) => t.clone(),
983 (None, Some(x)) => match self.eval_string(x) {
984 Ok(t) => t,
985 Err(_) => return self.fail(err_id),
986 },
987 (None, None) => SCXML_EVENT_PROCESSOR.to_string(),
988 };
989 let delay_text = match (&send.delay, &send.delayexpr) {
990 (Some(d), _) => Some(d.clone()),
991 (None, Some(x)) => match self.eval_string(x) {
992 Ok(d) => Some(d),
993 Err(_) => return self.fail(err_id),
994 },
995 (None, None) => None,
996 };
997 let delay_ms = match delay_text {
998 None => 0,
999 Some(t) => match parse_delay(&t) {
1000 Some(ms) => ms,
1001 None => return self.fail(err_id),
1002 },
1003 };
1004 let data = match self.eval_payload(&send.namelist, &send.params, send.content.as_ref()) {
1006 Ok(v) => v,
1007 Err(_) => return self.fail(err_id),
1008 };
1009 if !self.s.options.send_types.contains(&type_) {
1013 return self.fail(err_id);
1014 }
1015 let scxml = type_ == SCXML_EVENT_PROCESSOR || type_ == "scxml";
1016 if scxml
1017 && let Some(t) = &target
1018 && !(t.starts_with("#_") || t.contains(':'))
1019 {
1020 return self.fail(err_id);
1021 }
1022 if scxml
1025 && delay_ms == 0
1026 && let (Some(t), Some(known)) = (&target, &self.sessions)
1027 && let Some(id) = t.strip_prefix("#_scxml_")
1028 && !known.iter().any(|k| k == id)
1029 {
1030 self.s.internal_queue.push_back(platform_error(
1031 "error.communication",
1032 COMMUNICATION_ERROR_MESSAGE,
1033 err_id,
1034 ));
1035 return Ok(());
1036 }
1037 let exposed = if visible { Some(sendid.clone()) } else { None };
1038 if target.as_deref() == Some("#_internal") && delay_ms == 0 {
1039 self.s.internal_queue.push_back(Event {
1040 name,
1041 type_: EventType::Internal,
1042 sendid: exposed,
1043 data,
1044 ..Event::default()
1045 });
1046 return Ok(());
1047 }
1048 let event = Event {
1049 name,
1050 type_: EventType::External,
1051 sendid: exposed,
1052 origin: if scxml {
1053 Some(format!("#_scxml_{}", self.s.sessionid))
1054 } else {
1055 None
1056 },
1057 origintype: if scxml {
1058 Some(SCXML_EVENT_PROCESSOR.to_string())
1059 } else {
1060 None
1061 },
1062 invokeid: None,
1063 data,
1064 ..Event::default()
1065 };
1066 self.effects.push(Effect::Send(SendRequest {
1067 sendid,
1068 event,
1069 target,
1070 type_,
1071 delay_ms,
1072 }));
1073 Ok(())
1074 }
1075
1076 fn select(&mut self, event: Option<&str>) -> Vec<TransIdx> {
1079 let chart = self.chart;
1080 let config = &self.s.configuration;
1081 let history = &self.s.history;
1082 let queue = &mut self.s.internal_queue;
1083 let dm = &mut self.dm;
1084 kernel::select_transitions(chart, config, history, event, |t| {
1085 let Some(cond) = chart.transition(t).cond.as_deref() else {
1086 return true;
1087 };
1088 let q = Active { chart, config };
1089 match dm.eval_cond(cond, &q) {
1090 Ok(b) => b,
1091 Err(e) => {
1092 queue.push_back(platform_error("error.execution", &e.message, None));
1093 false
1094 }
1095 }
1096 })
1097 }
1098
1099 fn record(&mut self, event: Option<String>, ts: &[TransIdx]) {
1100 let transitions = ts
1101 .iter()
1102 .filter_map(|t| {
1103 let tr = self.chart.transition(*t);
1104 tr.index.map(|index| TransitionRef {
1105 source: self.chart.state(tr.source).id.clone(),
1106 index,
1107 })
1108 })
1109 .collect();
1110 self.microsteps.push(MicrostepTrace { event, transitions });
1111 }
1112
1113 fn charge(&mut self) -> Result<(), Budget> {
1118 if self.used >= self.s.options.microstep_budget {
1119 return Err(Budget);
1120 }
1121 self.used += 1;
1122 Ok(())
1123 }
1124
1125 fn microstep(&mut self, event: Option<String>, ts: &[TransIdx]) -> Result<(), Budget> {
1126 self.charge()?;
1127 self.record(event, ts);
1128 let chart = self.chart;
1129 let domains = kernel::transition_domains(chart, &self.s.history, ts);
1130 self.exit_states(ts);
1131 for &t in ts {
1132 let _ = self.exec_block(&chart.transition(t).body);
1133 }
1134 self.enter_states(ts, &domains);
1135 Ok(())
1136 }
1137
1138 fn exit_states(&mut self, ts: &[TransIdx]) {
1140 let chart = self.chart;
1141 let exit = kernel::compute_exit_set(chart, &self.s.configuration, &self.s.history, ts);
1142 self.to_invoke.difference_with(&exit);
1143 kernel::record_history(chart, &self.s.configuration, &exit, &mut self.s.history);
1144 for s in exit.to_vec_rev() {
1145 for b in &chart.state(s).onexit {
1146 let _ = self.exec_block(b);
1147 }
1148 self.cancel_invocations(s);
1149 self.s.configuration.remove(s);
1150 }
1151 }
1152
1153 fn cancel_invocations(&mut self, s: StateIdx) {
1154 let mut keep = Vec::new();
1155 for inv in core::mem::take(&mut self.s.invocations) {
1156 if inv.state == s {
1157 if !inv.completed {
1158 self.effects.push(Effect::CancelInvoke {
1159 invokeid: inv.invokeid,
1160 });
1161 }
1162 } else {
1163 keep.push(inv);
1164 }
1165 }
1166 self.s.invocations = keep;
1167 }
1168
1169 fn enter_states(&mut self, ts: &[TransIdx], domains: &[Option<StateIdx>]) {
1171 let chart = self.chart;
1172 let plan = kernel::compute_entry_set(chart, &self.s.history, ts, domains);
1173 let late = chart.binding() == Binding::Late;
1174 for s in plan.states.to_vec() {
1175 self.s.configuration.insert(s);
1176 self.to_invoke.insert(s);
1177 let st = chart.state(s);
1178 if late && !self.s.data_initialized.contains(s) {
1179 self.s.data_initialized.insert(s);
1180 self.init_data(&st.data);
1181 }
1182 for b in &st.onentry {
1183 let _ = self.exec_block(b);
1184 }
1185 if plan.default_entry.contains(s)
1186 && let Some(it) = st.initial
1187 {
1188 let _ = self.exec_block(&chart.transition(it).body);
1189 }
1190 if let Some(&(_, t)) = plan.default_history_content.iter().find(|(p, _)| *p == s) {
1191 let _ = self.exec_block(&chart.transition(t).body);
1192 }
1193 if st.kind == StateKind::Final {
1194 let parent = st.parent.unwrap_or(ROOT);
1195 if parent == ROOT {
1196 self.running = false;
1197 } else {
1198 let data = self.done_data(s);
1199 let pid = &chart.state(parent).id;
1200 self.s.internal_queue.push_back(Event {
1201 name: format!("done.state.{pid}"),
1202 type_: EventType::Platform,
1203 data,
1204 ..Event::default()
1205 });
1206 if let Some(gp) = chart.state(parent).parent
1207 && gp != ROOT
1208 && chart.state(gp).kind == StateKind::Parallel
1209 && chart
1210 .state(gp)
1211 .children
1212 .iter()
1213 .all(|c| kernel::is_in_final_state(chart, &self.s.configuration, *c))
1214 {
1215 let gid = &chart.state(gp).id;
1216 self.s.internal_queue.push_back(Event::named(
1217 format!("done.state.{gid}"),
1218 EventType::Platform,
1219 ));
1220 }
1221 }
1222 }
1223 }
1224 }
1225
1226 fn done_data(&mut self, s: StateIdx) -> Value {
1227 let chart = self.chart;
1228 let Some(dd) = &chart.state(s).donedata else {
1229 return Value::Undefined;
1230 };
1231 match self.eval_payload(&[], &dd.params, dd.content.as_ref()) {
1232 Ok(v) => v,
1233 Err(_) => {
1234 self.queue_error(None);
1235 Value::Undefined
1236 }
1237 }
1238 }
1239
1240 fn run_macrostep(&mut self) -> Result<(), Budget> {
1243 loop {
1244 while self.running {
1245 let mut enabled = self.select(None);
1246 let mut name = None;
1247 if enabled.is_empty() {
1248 match self.s.internal_queue.pop_front() {
1249 None => break,
1250 Some(ev) => {
1251 self.dm.set_event(&ev);
1252 enabled = self.select(Some(&ev.name));
1253 if enabled.is_empty() {
1254 self.charge()?;
1255 self.record(Some(ev.name), &[]);
1256 continue;
1257 }
1258 name = Some(ev.name);
1259 }
1260 }
1261 }
1262 self.microstep(name, &enabled)?;
1263 }
1264 if !self.running {
1265 self.exit_interpreter(false);
1266 return Ok(());
1267 }
1268 self.start_invokes();
1269 if self.s.internal_queue.is_empty() {
1270 return Ok(());
1271 }
1272 }
1273 }
1274
1275 fn run_start(&mut self, invoker_data: &[(String, Value)]) -> Result<(), Budget> {
1276 let chart = self.chart;
1277 for s in 0..chart.len() {
1279 for d in &chart.state(s).data {
1280 if self.dm.declare(&d.id).is_err() {
1281 self.queue_error(None);
1282 }
1283 }
1284 }
1285 match chart.binding() {
1286 Binding::Early => {
1287 for s in 0..chart.len() {
1288 let given = if s == ROOT { invoker_data } else { &[] };
1289 self.init_data_with(&chart.state(s).data, given);
1290 self.s.data_initialized.insert(s);
1291 }
1292 }
1293 Binding::Late => {
1294 self.init_data_with(&chart.state(ROOT).data, invoker_data);
1295 self.s.data_initialized.insert(ROOT);
1296 }
1297 }
1298 if let Some(script) = &chart.chart().script {
1299 let r = match &script.body {
1300 Some(body) => {
1301 let q = Active {
1302 chart,
1303 config: &self.s.configuration,
1304 };
1305 self.dm.exec_script(body, &q)
1306 }
1307 None => Err(EvalError::new("script source was not resolved")),
1308 };
1309 if r.is_err() {
1310 self.queue_error(None);
1311 }
1312 }
1313 if let Some(it) = chart.state(ROOT).initial {
1314 let domains = kernel::transition_domains(chart, &self.s.history, &[it]);
1315 self.enter_states(&[it], &domains);
1316 }
1317 self.run_macrostep()
1318 }
1319
1320 fn run_external(&mut self, ev: Event) -> Result<(), Budget> {
1322 if let Some(rest) = ev.name.strip_prefix("done.invoke.") {
1329 for inv in self.s.invocations.iter_mut() {
1330 if inv.invokeid == rest || ev.invokeid.as_deref() == Some(inv.invokeid.as_str()) {
1331 inv.completed = true;
1332 }
1333 }
1334 }
1335 self.dm.set_event(&ev);
1336 let mut invs = self.s.invocations.clone();
1337 invs.sort_by_key(|i| (i.state, i.index));
1338 let chart = self.chart;
1339 for inv in invs {
1340 if ev.invokeid.as_deref() == Some(inv.invokeid.as_str()) {
1341 let finalize = &chart.state(inv.state).invokes[inv.index].finalize;
1342 let _ = self.exec_block(finalize);
1343 }
1344 if inv.autoforward && !inv.completed {
1345 self.effects.push(Effect::Forward {
1346 invokeid: inv.invokeid.clone(),
1347 event: ev.clone(),
1348 });
1349 }
1350 }
1351 let enabled = self.select(Some(&ev.name));
1352 if enabled.is_empty() {
1353 self.record(Some(ev.name), &[]);
1354 } else {
1355 self.microstep(Some(ev.name), &enabled)?;
1356 }
1357 self.run_macrostep()
1358 }
1359
1360 fn start_invokes(&mut self) {
1361 let chart = self.chart;
1362 for s in self.to_invoke.to_vec() {
1363 for (i, inv) in chart.state(s).invokes.iter().enumerate() {
1364 self.invoke(s, i, inv);
1365 }
1366 }
1367 self.to_invoke.clear();
1368 }
1369
1370 fn invoke(&mut self, s: StateIdx, index: usize, inv: &'c Invoke) {
1371 let chart = self.chart;
1372 let state_id = &chart.state(s).id;
1373 let invokeid = match &inv.id {
1374 Some(id) => id.clone(),
1375 None => {
1376 self.s.invoke_counter += 1;
1377 let id = format!("{}.{}", state_id, self.s.invoke_counter);
1378 if let Some(loc) = &inv.idlocation
1379 && self
1380 .dm
1381 .assign_value(loc, Value::String(id.clone()))
1382 .is_err()
1383 {
1384 self.queue_error(None);
1385 return;
1386 }
1387 id
1388 }
1389 };
1390 let type_ = match (&inv.type_, &inv.typeexpr) {
1391 (Some(t), _) => Ok(t.clone()),
1392 (None, Some(x)) => self.eval_string(x),
1393 (None, None) => Ok(SCXML_INVOKE_TYPE.to_string()),
1394 };
1395 let src = match (&inv.src, &inv.srcexpr) {
1396 (Some(t), _) => Ok(Some(t.clone())),
1397 (None, Some(x)) => self.eval_string(x).map(Some),
1398 (None, None) => Ok(None),
1399 };
1400 let content = match &inv.content {
1401 Some(Content { expr: Some(x), .. }) => self.eval_value(x).map(Some),
1402 Some(Content {
1403 expr: None,
1404 body: Some(b),
1405 ..
1406 }) => Ok(Some(Value::String(b.clone()))),
1407 _ => Ok(None),
1408 };
1409 let params = self.eval_payload(&inv.namelist, &inv.params, None);
1410 let (Ok(type_), Ok(src), Ok(content), Ok(params)) = (type_, src, content, params) else {
1411 self.queue_error(None);
1412 return;
1413 };
1414 if !self.s.options.invoke_types.contains(&type_) {
1415 self.queue_error(None);
1416 return;
1417 }
1418 self.s.invocations.push(Invocation {
1419 state: s,
1420 index,
1421 invokeid: invokeid.clone(),
1422 autoforward: inv.autoforward,
1423 completed: false,
1424 });
1425 self.effects.push(Effect::Invoke(InvokeRequest {
1426 invokeid,
1427 state: state_id.clone(),
1428 type_,
1429 src,
1430 content,
1431 params,
1432 autoforward: inv.autoforward,
1433 }));
1434 }
1435
1436 fn exit_interpreter(&mut self, cancelled: bool) {
1440 let chart = self.chart;
1441 let mut data = Value::Undefined;
1442 for s in self.s.configuration.to_vec_rev() {
1443 for b in &chart.state(s).onexit {
1444 let _ = self.exec_block(b);
1445 }
1446 self.cancel_invocations(s);
1447 self.s.configuration.remove(s);
1448 if chart.state(s).kind == StateKind::Final && chart.state(s).parent == Some(ROOT) {
1449 data = self.done_data(s);
1450 }
1451 }
1452 self.effects.push(Effect::SessionDone { data, cancelled });
1453 self.s.internal_queue.clear();
1454 self.to_invoke.clear();
1455 self.s.status = Status::Terminated;
1456 self.running = false;
1457 self.terminated_now = true;
1458 }
1459}