1extern crate alloc;
2
3use alloc::{
4 boxed::Box,
5 collections::BTreeMap,
6 string::{String, ToString as _},
7 vec,
8 vec::Vec,
9};
10use core::fmt::{self, Display};
11
12use aranya_crypto::policy::CmdId;
13use aranya_policy_ast::{self as ast, Identifier, ident};
14use aranya_policy_module::{
15 ActionDef, CodeMap, CommandDef, ExitReason, Fact, FactKey, FactValue, HashableValue,
16 Instruction, KVPair, Label, LabelType, Module, ModuleData, ModuleV0, Struct, Target, TryAsMut,
17 UnsupportedVersion, Value, ValueConversionError, named::NamedMap,
18};
19use buggy::{Bug, BugExt as _};
20use heapless::Vec as HVec;
21
22#[cfg(feature = "bench")]
23use crate::bench::{Stopwatch, bench_aggregate};
24use crate::{
25 ActionContext, CommandContext, OpenContext, PolicyContext, SealContext,
26 error::{MachineError, MachineErrorType},
27 io::MachineIO,
28 scope::ScopeManager,
29 stack::Stack,
30};
31
32const STACK_SIZE: usize = 100;
33
34fn validate_fact_schema(fact: &Fact, schema: &ast::FactDefinition) -> bool {
37 if fact.name != schema.identifier.name {
38 return false;
39 }
40
41 for key in &fact.keys {
42 let Some(key_value) = schema
43 .key
44 .iter()
45 .find(|k| k.identifier.name == key.identifier)
46 else {
47 return false;
48 };
49
50 if !key.value.vtype().matches(&key_value.field_type.kind) {
51 return false;
52 }
53 }
54
55 for value in &fact.values {
56 let Some(schema_value) = schema
58 .value
59 .iter()
60 .find(|v| v.identifier.name == value.identifier)
61 else {
62 return false;
63 };
64
65 let Some(value_type) = value.value.vtype() else {
67 return false;
68 };
69 if !value_type.matches(&schema_value.field_type.kind) {
70 return false;
71 }
72 }
73 true
74}
75
76fn fact_match(query: &Fact, keys: &[FactKey], values: &[FactValue]) -> bool {
83 if !keys.starts_with(&query.keys) {
84 return false;
85 }
86
87 for qv in &query.values {
88 if let Some(v) = values.iter().find(|v| v.identifier == qv.identifier) {
89 if v.value != qv.value {
91 return false;
92 }
93 } else {
94 return false;
96 }
97 }
98
99 true
100}
101
102#[must_use]
107#[derive(Debug, PartialEq, Eq)]
108pub enum MachineStatus {
109 Executing,
111 Exited(ExitReason),
113}
114
115impl Display for MachineStatus {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 Self::Executing => write!(f, "Executing"),
119 Self::Exited(reason) => write!(f, "Exited: {}", reason),
120 }
121 }
122}
123
124#[derive(Clone, Debug, Eq, PartialEq)]
130pub struct Machine {
131 pub progmem: Vec<Instruction>,
133 pub labels: BTreeMap<Label, usize>,
135 pub action_defs: NamedMap<ActionDef>,
137 pub command_defs: NamedMap<CommandDef>,
139 pub fact_defs: BTreeMap<Identifier, ast::FactDefinition>,
141 pub struct_defs: BTreeMap<Identifier, Vec<ast::FieldDefinition>>,
143 pub enum_defs: BTreeMap<Identifier, BTreeMap<Identifier, i64>>,
145 pub codemap: Option<CodeMap>,
147 pub globals: BTreeMap<Identifier, Value>,
149}
150
151impl Machine {
152 pub fn new<I>(instructions: I) -> Self
154 where
155 I: IntoIterator<Item = Instruction>,
156 {
157 Self {
158 progmem: Vec::from_iter(instructions),
159 labels: BTreeMap::new(),
160 action_defs: NamedMap::new(),
161 command_defs: NamedMap::new(),
162 fact_defs: BTreeMap::new(),
163 struct_defs: BTreeMap::new(),
164 enum_defs: BTreeMap::new(),
165 codemap: None,
166 globals: BTreeMap::new(),
167 }
168 }
169
170 pub fn from_codemap(codemap: CodeMap) -> Self {
172 Self {
173 progmem: vec![],
174 labels: BTreeMap::new(),
175 action_defs: NamedMap::new(),
176 command_defs: NamedMap::new(),
177 fact_defs: BTreeMap::new(),
178 struct_defs: BTreeMap::new(),
179 enum_defs: BTreeMap::new(),
180 codemap: Some(codemap),
181 globals: BTreeMap::new(),
182 }
183 }
184
185 pub fn from_module(m: Module) -> Result<Self, UnsupportedVersion> {
187 match m.data {
188 ModuleData::V0(m) => Ok(Self {
189 progmem: m.progmem.into(),
190 labels: m.labels,
191 action_defs: m.action_defs,
192 command_defs: m.command_defs,
193 fact_defs: m.fact_defs,
194 struct_defs: m.struct_defs,
195 enum_defs: m.enum_defs,
196 codemap: m.codemap,
197 globals: m.globals,
198 }),
199 }
200 }
201
202 pub fn into_module(self) -> Module {
204 Module {
205 data: ModuleData::V0(ModuleV0 {
206 progmem: self.progmem.into_boxed_slice(),
207 labels: self.labels,
208 action_defs: self.action_defs,
209 command_defs: self.command_defs,
210 fact_defs: self.fact_defs,
211 struct_defs: self.struct_defs,
212 enum_defs: self.enum_defs,
213 codemap: self.codemap,
214 globals: self.globals,
215 }),
216 }
217 }
218
219 pub fn parse_enum(&self, value: &str) -> Result<Value, MachineError> {
221 let Some((name, variant)) = value.split_once("::") else {
222 return Err(MachineError::new(MachineErrorType::invalid_type(
223 "<Enum>::<Variant>",
224 value,
225 "invalid enum reference",
226 )));
227 };
228
229 let (name, variants) = self
230 .enum_defs
231 .get_key_value(name)
232 .ok_or_else(|| MachineErrorType::NotDefined(alloc::format!("enum {name}")))?;
233 let int_value = variants.get(variant).ok_or_else(|| {
234 MachineErrorType::NotDefined(alloc::format!("no value `{variant}` in enum `{name}`"))
235 })?;
236
237 Ok(Value::Enum(name.clone(), *int_value))
238 }
239
240 pub fn create_run_state<'a, M>(&'a self, io: &'a mut M, ctx: CommandContext) -> RunState<'a, M>
242 where
243 M: MachineIO<MachineStack>,
244 {
245 RunState::new(self, io, ctx)
246 }
247
248 pub fn call_action<Args, M>(
250 &mut self,
251 name: Identifier,
252 args: Args,
253 io: &mut M,
254 ctx: CommandContext,
255 ) -> Result<ExitReason, MachineError>
256 where
257 Args: IntoIterator,
258 Args::Item: Into<Value>,
259 M: MachineIO<MachineStack>,
260 {
261 let mut rs = self.create_run_state(io, ctx);
262 rs.call_action(name, args)
263 }
264
265 pub fn call_command_policy<M>(
267 &mut self,
268 this_data: Struct,
269 envelope: Struct,
270 io: &mut M,
271 ctx: CommandContext,
272 ) -> Result<ExitReason, MachineError>
273 where
274 M: MachineIO<MachineStack>,
275 {
276 let mut rs = self.create_run_state(io, ctx);
277 rs.call_command_policy(this_data, envelope)
278 }
279}
280
281impl Display for Machine {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 writeln!(f, "Program memory:")?;
284 for (addr, instr) in self.progmem.iter().enumerate() {
285 writeln!(f, " {:4} {}", addr, instr)?;
286 }
287 writeln!(f, "Labels:")?;
288 for (k, v) in &self.labels {
289 writeln!(f, " {}: {:?}", k, v)?;
290 }
291 writeln!(f, "Fact definitions:")?;
292 for (k, v) in &self.fact_defs {
293 writeln!(f, " {}: {:?}", k, v)?;
294 }
295 writeln!(f, "Struct definitions:")?;
296 for (k, v) in &self.struct_defs {
297 writeln!(f, " {}: {:?}", k, v)?;
298 }
299 Ok(())
300 }
301}
302
303pub struct RunState<'a, M: MachineIO<MachineStack>> {
310 machine: &'a Machine,
312 scope: ScopeManager<'a>,
314 pub stack: MachineStack,
316 call_state: Vec<usize>,
318 pc: usize,
320 pub io: &'a mut M,
322 ctx: CommandContext,
324 query_iter_stack: Vec<M::QueryIterator>,
326 #[cfg(feature = "bench")]
327 stopwatch: Stopwatch,
328}
329
330impl<'a, M> RunState<'a, M>
331where
332 M: MachineIO<MachineStack>,
333{
334 pub fn new(machine: &'a Machine, io: &'a mut M, ctx: CommandContext) -> Self {
336 RunState {
337 machine,
338 scope: ScopeManager::new(&machine.globals),
339 stack: MachineStack(HVec::new()),
340 call_state: Vec::new(),
341 pc: 0,
342 io,
343 ctx,
344 query_iter_stack: vec![],
345 #[cfg(feature = "bench")]
346 stopwatch: Stopwatch::new(),
347 }
348 }
349
350 pub fn get_context(&self) -> &CommandContext {
352 &self.ctx
353 }
354
355 pub fn set_context(&mut self, ctx: CommandContext) {
359 self.ctx = ctx;
360 }
361
362 pub fn update_context_with_new_head(&mut self, new_head_id: CmdId) -> Result<(), Bug> {
364 self.ctx = self.ctx.with_new_head(new_head_id)?;
365 Ok(())
366 }
367
368 pub fn source_location(&self) -> Option<String> {
371 let source_span = self
372 .machine
373 .codemap
374 .as_ref()?
375 .span_from_instruction(self.pc)
376 .ok();
377 if let Some(span) = source_span {
378 let (row, col) = span.start_linecol();
379 Some(alloc::format!(
380 "at row {} col {}:\n\t{}",
381 row,
382 col,
383 span.as_str()
384 ))
385 } else {
386 None
387 }
388 }
389
390 fn err(&self, err_type: MachineErrorType) -> MachineError {
393 MachineError::from_position(err_type, self.pc, self.machine.codemap.as_ref())
394 }
395
396 pub fn reset(&mut self) {
399 self.scope.clear();
400 self.stack.clear();
401 self.pc = 0;
402 }
403
404 pub fn pc(&self) -> usize {
406 self.pc
407 }
408
409 fn ipush<V>(&mut self, value: V) -> Result<(), MachineError>
412 where
413 V: Into<Value>,
414 {
415 self.stack.push(value).map_err(|e| self.err(e))
416 }
417
418 fn ipop<V>(&mut self) -> Result<V, MachineError>
421 where
422 V: TryFrom<Value, Error = ValueConversionError>,
423 {
424 self.stack.pop().map_err(|e| self.err(e))
425 }
426
427 fn ipop_value(&mut self) -> Result<Value, MachineError> {
430 self.stack.pop_value().map_err(|e| self.err(e))
431 }
432
433 fn ipeek<V>(&mut self) -> Result<&mut V, MachineError>
436 where
437 V: ?Sized,
438 Value: TryAsMut<V, Error = ValueConversionError>,
439 {
440 let pc = self.pc;
445 self.stack
446 .peek()
447 .map_err(|e| MachineError::from_position(e, pc, self.machine.codemap.as_ref()))
448 }
449
450 fn validate_struct_schema(&mut self, s: &Struct) -> Result<(), MachineError> {
454 #[cfg(feature = "bench")]
455 self.stopwatch.start("validate_struct_schema");
456
457 let mk_err = || self.err(MachineErrorType::InvalidSchema(s.name.clone()));
458
459 match self.machine.struct_defs.get(&s.name) {
460 Some(fields) => {
461 for f in &s.fields {
464 if !fields.iter().any(|v| &v.identifier.name == f.0) {
465 return Err(mk_err());
466 }
467 }
468 for f in fields {
471 match s.fields.get(&f.identifier.name) {
472 Some(v) => {
473 if !v.fits_type(&f.field_type) {
474 return Err(mk_err());
475 }
476 }
477 None => {
478 return Err(mk_err());
479 }
480 }
481 }
482
483 #[cfg(feature = "bench")]
484 self.stopwatch.stop();
485
486 Ok(())
487 }
488 None => Err(mk_err()),
489 }
490 }
491
492 pub fn step(&mut self) -> Result<MachineStatus, MachineError> {
495 if self.pc() >= self.machine.progmem.len() {
496 return Err(self.err(MachineErrorType::InvalidAddress(ident!("pc"))));
497 }
498 let instruction = self.machine.progmem[self.pc()].clone();
501
502 match instruction {
503 Instruction::SaveSP => {
504 self.call_state.push(self.stack.len());
505 }
506 Instruction::RestoreSP => {
507 let saved_sp = self.call_state.pop().ok_or_else(|| {
508 self.err(MachineErrorType::BadState("no saved stack pointer"))
509 })?;
510 match self
511 .stack
512 .len()
513 .cmp(&saved_sp.checked_add(1).assume("stack size < isize::MAX")?)
514 {
515 core::cmp::Ordering::Less => {
516 return Err(self.err(MachineErrorType::BadState(
517 "callable has consumed too many stack values",
518 )));
519 }
520 core::cmp::Ordering::Equal => {}
521 core::cmp::Ordering::Greater => {
522 let v = self.stack.pop_value()?;
523 while self.stack.len() > saved_sp {
524 self.stack.pop_value()?;
525 }
526 self.stack.push_value(v)?;
527 }
528 }
529 }
530 Instruction::Const(v) => {
531 self.ipush(v)?;
532 }
533 Instruction::Def(key) => {
534 let value = self.ipop_value()?;
535 self.scope.set(key, value)?;
536 }
537 Instruction::Get(key) => {
538 let value = self.scope.get(&key)?;
539 self.ipush(value)?;
540 }
541 Instruction::Dup => {
542 let v = self.stack.peek_value()?.clone();
543 self.ipush(v)?;
544 }
545 Instruction::Pop => {
546 let _ = self.stack.pop_value();
547 }
548 Instruction::Block => self.scope.enter_block().map_err(|e| self.err(e))?,
549 Instruction::End => self.scope.exit_block().map_err(|e| self.err(e))?,
550 Instruction::Jump(t) => match t {
551 Target::Unresolved(label) => {
552 return Err(self.err(MachineErrorType::UnresolvedTarget(label)));
553 }
554 Target::Resolved(n) => {
555 self.pc = n;
560 return Ok(MachineStatus::Executing);
561 }
562 },
563 Instruction::Branch(t) => {
564 let conditional = self.ipop()?;
565 if conditional {
566 match t {
567 Target::Unresolved(label) => {
568 return Err(self.err(MachineErrorType::UnresolvedTarget(label)));
569 }
570 Target::Resolved(n) => {
571 self.pc = n;
572 return Ok(MachineStatus::Executing);
573 }
574 }
575 }
576 }
577 Instruction::Next => todo!(),
578 Instruction::Last => todo!(),
579 Instruction::Call(t) => match t {
580 Target::Unresolved(label) => {
581 return Err(self.err(MachineErrorType::UnresolvedTarget(label)));
582 }
583 Target::Resolved(n) => {
584 self.scope.enter_function();
585 self.call_state.push(self.pc);
588 self.pc = n;
589 return Ok(MachineStatus::Executing);
590 }
591 },
592 Instruction::Return => {
593 if self.call_state.is_empty() {
595 return Ok(MachineStatus::Exited(ExitReason::Normal));
596 }
597 self.pc = self
598 .call_state
599 .pop()
600 .ok_or_else(|| self.err(MachineErrorType::CallStack))?;
601 self.scope.exit_function().map_err(|e| self.err(e))?;
602 }
603 Instruction::ExtCall(module, proc) => {
604 self.io.call(module, proc, &mut self.stack, &self.ctx)?;
605 }
606 Instruction::Exit(reason) => return Ok(MachineStatus::Exited(reason)),
607 Instruction::Add | Instruction::Sub => {
608 let b: i64 = self.ipop()?;
609 let a: i64 = self.ipop()?;
610 let r = match instruction {
611 Instruction::Add => a.checked_add(b),
612 Instruction::Sub => a.checked_sub(b),
613 _ => unreachable!(),
614 };
615 self.ipush(r)?;
617 }
618 Instruction::SaturatingAdd | Instruction::SaturatingSub => {
619 let b: i64 = self.ipop()?;
620 let a: i64 = self.ipop()?;
621 let r = match instruction {
622 Instruction::SaturatingAdd => a.saturating_add(b),
623 Instruction::SaturatingSub => a.saturating_sub(b),
624 _ => unreachable!(),
625 };
626 self.ipush(r)?;
627 }
628 Instruction::Not => {
629 let a: &mut bool = self.ipeek()?;
630 *a = !*a;
631 }
632 Instruction::Gt | Instruction::Lt | Instruction::Eq => {
633 let b = self.ipop_value()?;
634 let a = self.ipop_value()?;
635 let v = match instruction {
636 Instruction::Gt => match (&a, &b) {
637 (Value::Int(ia), Value::Int(ib)) => ia > ib,
638 _ => {
639 let a_type = a.type_name();
640 let b_type = b.type_name();
641 return Err(self.err(MachineErrorType::invalid_type(
642 "Int, Int",
643 alloc::format!("{a_type}, {b_type}"),
644 "Greater-than comparison",
645 )));
646 }
647 },
648 Instruction::Lt => match (&a, &b) {
649 (Value::Int(ia), Value::Int(ib)) => ia < ib,
650 _ => {
651 let a_type = a.type_name();
652 let b_type = b.type_name();
653 return Err(self.err(MachineErrorType::invalid_type(
654 "Int, Int",
655 alloc::format!("{a_type}, {b_type}"),
656 "Less-than comparison",
657 )));
658 }
659 },
660 Instruction::Eq => a == b,
664 _ => unreachable!(),
665 };
666 self.ipush(v)?;
667 }
668 Instruction::FactNew(name) => {
669 let fact = Fact::new(name);
670 self.ipush(fact)?;
671 }
672 Instruction::FactKeySet(varname) => {
673 let v: HashableValue = self.ipop()?;
674 let f: &mut Fact = self.ipeek()?;
675 f.set_key(varname, v);
676 }
677 Instruction::FactValueSet(varname) => {
678 let value = self.ipop_value()?;
679 let f: &mut Fact = self.ipeek()?;
680 f.set_value(varname, value);
681 }
682 Instruction::StructNew(name) => {
683 let fields = BTreeMap::new();
684 self.ipush(Struct { name, fields })?;
685 }
686 Instruction::StructSet(field_name) => {
687 let value = self.ipop_value()?;
688 let mut s: Struct = self.ipop()?;
689 let struct_def_fields = self
692 .machine
693 .struct_defs
694 .get(&s.name)
695 .ok_or_else(|| self.err(MachineErrorType::InvalidSchema(s.name.clone())))?;
696 if !struct_def_fields
697 .iter()
698 .any(|f| f.identifier.name == field_name)
699 {
700 return Err(self.err(MachineErrorType::InvalidStructMember(field_name)));
701 }
702 s.fields.insert(field_name, value);
703 self.ipush(s)?;
704 }
705 Instruction::StructGet(varname) => {
706 let mut s: Struct = self.ipop()?;
707 let v = s
708 .fields
709 .remove(&varname)
710 .ok_or_else(|| self.err(MachineErrorType::InvalidStructMember(varname)))?;
711 self.ipush(v)?;
712 }
713 Instruction::MStructSet(n) => {
714 let n: usize = n.into();
715 let mut field_name_value_pairs = Vec::with_capacity(n);
716
717 for _ in 0..n {
718 let field_val = self.ipop_value()?;
719 let field_name = self.ipop::<Identifier>()?;
720
721 field_name_value_pairs.push((field_name, field_val));
722 }
723
724 let mut target: Struct = self.ipop()?;
725 let struct_def_fields =
726 self.machine.struct_defs.get(&target.name).ok_or_else(|| {
727 self.err(MachineErrorType::InvalidSchema(target.name.clone()))
728 })?;
729
730 for (field_name, field_val) in field_name_value_pairs {
731 let Some(field_defn) = struct_def_fields
732 .iter()
733 .find(|f| f.identifier.name == field_name)
734 else {
735 return Err(self.err(MachineErrorType::InvalidStructMember(field_name)));
736 };
737
738 if !field_val.fits_type(&field_defn.field_type) {
739 return Err(self.err(MachineErrorType::InvalidStructMember(field_name)));
740 }
741
742 target.fields.insert(field_name, field_val);
743 }
744 self.ipush(target)?;
745 }
746 Instruction::MStructGet(n) => {
747 let field_names = (0..n.into())
748 .map(|_| self.ipop::<Identifier>())
749 .collect::<Result<Vec<_>, _>>()?;
750 let mut s: Struct = self.ipop()?;
751
752 for field_name in field_names {
753 let v = s.fields.remove(&field_name).ok_or_else(|| {
754 self.err(MachineErrorType::InvalidStructMember(field_name.clone()))
755 })?;
756
757 self.ipush(field_name)?;
758 self.ipush(v)?;
759 }
760 }
761 Instruction::Publish => {
762 let command_struct: Struct = self.ipop()?;
763 self.validate_struct_schema(&command_struct)?;
764 self.ipush(Value::Struct(command_struct))?;
765
766 self.pc = self.pc.checked_add(1).assume("self.pc + 1 must not wrap")?;
767 return Ok(MachineStatus::Exited(ExitReason::Yield));
768 }
769 Instruction::Create => {
770 let f: Fact = self.ipop()?;
771 self.io.fact_insert(f.name, f.keys, f.values)?;
772 }
773 Instruction::Delete => {
774 let f: Fact = self.ipop()?;
775 self.io.fact_delete(f.name, f.keys)?;
776 }
777 Instruction::Update => {
778 let fact_to: Fact = self.ipop()?;
779 let mut fact_from: Fact = self.ipop()?;
780 let mut replaced_fact = {
781 let mut iter = self.io.fact_query(fact_from.name.clone(), fact_from.keys)?;
782 iter.next().ok_or_else(|| {
783 self.err(MachineErrorType::InvalidFact(fact_from.name.clone()))
784 })??
785 };
786
787 if !fact_from.values.is_empty() {
788 let replaced_fact_values = &mut replaced_fact.1;
789
790 replaced_fact_values
791 .sort_unstable_by(|v1, v2| v1.identifier.cmp(&v2.identifier));
792 fact_from
793 .values
794 .sort_unstable_by(|v1, v2| v1.identifier.cmp(&v2.identifier));
795
796 if replaced_fact_values.as_slice() != fact_from.values.as_slice() {
797 return Err(self.err(MachineErrorType::InvalidFact(fact_from.name.clone())));
798 }
799 }
800
801 self.io.fact_delete(fact_from.name, replaced_fact.0)?;
802 self.io
803 .fact_insert(fact_to.name, fact_to.keys, fact_to.values)?;
804 }
805 Instruction::Emit => {
806 let s: Struct = self.ipop()?;
807 self.validate_struct_schema(&s)?;
808 let fields = s.fields.into_iter().map(|(k, v)| KVPair::new(k, v));
809 let (command, recall) = match &self.ctx {
810 CommandContext::Policy(ctx) => (ctx.id, false),
811 CommandContext::Recall(ctx) => (ctx.id, true),
812 _ => {
813 return Err(
814 self.err(MachineErrorType::BadState("Emit: wrong command context"))
815 );
816 }
817 };
818 self.io.effect(s.name, fields, command, recall);
819 }
820 Instruction::Query => {
821 let qf: Fact = self.ipop()?;
822
823 self.validate_fact_literal(&qf)?;
825
826 let result = {
827 let mut iter = self.io.fact_query(qf.name.clone(), qf.keys.clone())?;
828 iter.find_map(|r| match r {
830 Ok(f) => {
831 if fact_match(&qf, &f.0, &f.1) {
832 Some(Ok(f))
833 } else {
834 None
835 }
836 }
837 Err(e) => Some(Err(e)),
838 })
839 };
840 let maybe_fact = result.transpose()?.map(|f| {
841 let mut fields: Vec<KVPair> = vec![];
842 fields.append(&mut f.0.into_iter().map(Into::into).collect());
843 fields.append(&mut f.1.into_iter().map(Into::into).collect());
844 Struct::new(qf.name, fields)
845 });
846 self.ipush(maybe_fact)?;
847 }
848 Instruction::FactCount(limit) => {
849 let fact: Fact = self.ipop()?;
850 self.validate_fact_literal(&fact)?;
851
852 let mut count = 0;
853 {
854 let mut iter = self.io.fact_query(fact.name.clone(), fact.keys.clone())?;
855
856 while count < limit {
857 let Some(r) = iter.next() else { break };
858 match r {
859 Ok(f) => {
860 if fact_match(&fact, &f.0, &f.1) {
861 count = count
862 .checked_add(1)
863 .assume("should be able to increment fact counter")?;
864 }
865 }
866 Err(e) => return Err(self.err(MachineErrorType::IO(e))),
867 }
868 }
869 }
870
871 self.ipush(Value::Int(count))?;
872 }
873 Instruction::QueryStart => {
874 let fact: Fact = self.ipop()?;
875 self.validate_fact_literal(&fact)?;
876 let iter = self.io.fact_query(fact.name, fact.keys)?;
877 self.query_iter_stack.push(iter);
878 }
879 Instruction::QueryNext(ident) => {
880 let iter = self.query_iter_stack.last_mut().ok_or_else(|| {
882 MachineError::from_position(
883 MachineErrorType::BadState("QueryNext: no results"),
884 self.pc,
885 self.machine.codemap.as_ref(),
886 )
887 })?;
888 match iter.next() {
890 Some(result) => {
891 let (k, v) = result?;
892 let mut fields: Vec<KVPair> = vec![];
893 fields.append(&mut k.into_iter().map(Into::into).collect());
894 fields.append(&mut v.into_iter().map(Into::into).collect());
895 let s = Struct::new(ident.clone(), fields);
896 self.scope.set(ident, Value::Struct(s))?;
897 self.ipush(Value::Bool(false))?;
898 }
899 None => {
900 self.query_iter_stack.pop();
902 self.ipush(Value::Bool(true))?;
903 }
904 }
905 }
906 Instruction::Serialize => {
907 let CommandContext::Seal(SealContext { name, .. }) = &self.ctx else {
908 return Err(self.err(MachineErrorType::BadState(
909 "Serialize: expected seal context",
910 )));
911 };
912 let name = name.clone();
913
914 let command_struct: Struct = self.ipop()?;
915 if command_struct.name != name {
916 return Err(MachineError::from_position(
917 MachineErrorType::BadState(
918 "Serialize: context name doesn't match command name",
919 ),
920 self.pc,
921 self.machine.codemap.as_ref(),
922 ));
923 }
924 let bytes = postcard::to_allocvec(&command_struct).map_err(|_| {
925 self.err(MachineErrorType::Unknown(String::from(
926 "could not serialize command Struct",
927 )))
928 })?;
929 self.ipush(bytes)?;
930 }
931 Instruction::Deserialize => {
932 let CommandContext::Open(OpenContext { name, .. }) = &self.ctx else {
933 return Err(MachineError::from_position(
934 MachineErrorType::InvalidInstruction,
935 self.pc,
936 self.machine.codemap.as_ref(),
937 ));
938 };
939 let name = name.clone();
940
941 let bytes: Vec<u8> = self.ipop()?;
942 let s: Struct = postcard::from_bytes(&bytes).map_err(|_| {
943 MachineError::from_position(
944 MachineErrorType::Unknown(String::from("could not deserialize Struct")),
945 self.pc,
946 self.machine.codemap.as_ref(),
947 )
948 })?;
949 if name != s.name.as_str() {
950 return Err(MachineError::from_position(
951 MachineErrorType::InvalidInstruction,
952 self.pc,
953 self.machine.codemap.as_ref(),
954 ));
955 }
956 self.ipush(s)?;
957 }
958 Instruction::Some => {
959 let value = self.ipop_value()?;
960 self.ipush(Value::Option(Some(Box::new(value))))?;
961 }
962 Instruction::Unwrap => {
963 let value = self.ipop_value()?;
964 if let Value::Option(opt) = value {
965 if let Some(inner) = opt {
966 self.ipush(*inner)?;
967 } else {
968 return Err(self.err(MachineErrorType::Unknown("unwrapped None".into())));
969 }
970 } else {
971 return Err(self.err(MachineErrorType::invalid_type(
972 "Option[_]",
973 value.type_name(),
974 "Option[T] -> T",
975 )));
976 }
977 }
978 Instruction::Meta(_m) => {}
979 Instruction::Cast(identifier) => {
980 let value = self.ipop_value()?;
981 match value {
982 Value::Struct(s) => {
983 let rhs_struct =
985 self.machine.struct_defs.get(&identifier).ok_or_else(|| {
986 self.err(MachineErrorType::NotDefined(alloc::format!(
987 "struct `{}`",
988 identifier
989 )))
990 })?;
991
992 for field in rhs_struct {
994 let field_name = &field.identifier;
995 let field_type = &field.field_type;
996
997 let value = s.fields.get(&field_name.name).ok_or_else(|| {
999 self.err(MachineErrorType::Unknown(alloc::format!(
1000 "cannot cast to `struct {}`: missing field `{}`",
1001 identifier,
1002 field_name
1003 )))
1004 })?;
1005
1006 if !value.fits_type(field_type) {
1008 return Err(self.err(MachineErrorType::Unknown(alloc::format!(
1009 "cannot cast to `struct {}`: field `{}` has wrong type (expected `{}`, found `{}`)",
1010 identifier, field_name, field_type, value.type_name()
1011 ))));
1012 }
1013 }
1014
1015 let mut s = s;
1017 s.name = identifier.clone();
1018 self.ipush(Value::Struct(s))?;
1019 }
1020 _ => {
1021 return Err(self.err(MachineErrorType::invalid_type(
1022 "Struct",
1023 value.type_name(),
1024 "Cast LHS",
1025 )));
1026 }
1027 }
1028 }
1029 }
1030
1031 self.pc = self.pc.checked_add(1).assume("self.pc + 1 must not wrap")?;
1032
1033 Ok(MachineStatus::Executing)
1034 }
1035
1036 pub fn run(&mut self) -> Result<ExitReason, MachineError> {
1040 loop {
1041 #[cfg(feature = "bench")]
1042 if let Some(instruction) = self.machine.progmem.get(self.pc())
1043 && let Some(name) = instruction.to_string().split_whitespace().next()
1044 {
1045 self.stopwatch.start(name);
1046 }
1047
1048 let result = self
1049 .step()
1050 .map_err(|err| err.with_position(self.pc, self.machine.codemap.as_ref()))?;
1051
1052 #[cfg(feature = "bench")]
1053 if !self.stopwatch.measurement_stack.is_empty() {
1054 self.stopwatch.stop();
1055 }
1056
1057 if let MachineStatus::Exited(reason) = result {
1058 #[cfg(feature = "bench")]
1059 bench_aggregate(&mut self.stopwatch);
1060 return Ok(reason);
1061 }
1062 }
1063 }
1064
1065 pub fn set_pc_by_label(&mut self, label: &Label) -> Result<(), MachineError> {
1067 let addr: &usize = self
1068 .machine
1069 .labels
1070 .get(label)
1071 .ok_or_else(|| self.err(MachineErrorType::InvalidAddress(label.name.clone())))?;
1072 self.pc = *addr;
1073 Ok(())
1074 }
1075
1076 pub fn setup_command(
1078 &mut self,
1079 label_type: LabelType,
1080 this_data: Struct,
1081 ) -> Result<(), MachineError> {
1082 let name = this_data.name.clone();
1083
1084 #[cfg(feature = "bench")]
1085 self.stopwatch
1086 .start(format!("setup_command: {}", name).as_str());
1087
1088 self.setup_function(&Label::new(name.clone(), label_type))?;
1089
1090 let command_def = self
1092 .machine
1093 .command_defs
1094 .get(&name)
1095 .ok_or_else(|| self.err(MachineErrorType::NotDefined(name.to_string())))?;
1096
1097 if this_data.fields.len() != command_def.fields.len() {
1098 return Err(self.err(MachineErrorType::Unknown(alloc::format!(
1099 "command `{}` expects {} field(s), but `this` contains {}",
1100 name,
1101 command_def.fields.len(),
1102 this_data.fields.len()
1103 ))));
1104 }
1105 for (name, value) in &this_data.fields {
1106 let expected_type = &command_def
1107 .fields
1108 .get(name)
1109 .ok_or_else(|| self.err(MachineErrorType::InvalidStructMember(name.clone())))?
1110 .ty;
1111
1112 if !value.fits_type(expected_type) {
1113 return Err(self.err(MachineErrorType::invalid_type(
1114 expected_type.to_string(),
1115 value.type_name(),
1116 "invalid function argument",
1117 )));
1118 }
1119 }
1120
1121 self.ipush(this_data)?;
1122
1123 #[cfg(feature = "bench")]
1124 self.stopwatch.stop();
1125
1126 Ok(())
1127 }
1128
1129 pub fn call_command_policy(
1134 &mut self,
1135 this_data: Struct,
1136 envelope: Struct,
1137 ) -> Result<ExitReason, MachineError> {
1138 if !matches!(&self.ctx, CommandContext::Policy(PolicyContext{name: ctx_name,..}) if *ctx_name == this_data.name)
1139 {
1140 return Err(MachineErrorType::ContextMismatch.into());
1141 }
1142 self.setup_command(LabelType::CommandPolicy, this_data)?;
1143 self.ipush(envelope)?;
1144 self.run()
1145 }
1146
1147 pub fn call_command_recall(
1151 &mut self,
1152 this_data: Struct,
1153 envelope: Struct,
1154 ) -> Result<ExitReason, MachineError> {
1155 if !matches!(&self.ctx, CommandContext::Recall(PolicyContext{name: ctx_name,..}) if *ctx_name == this_data.name)
1156 {
1157 return Err(MachineErrorType::ContextMismatch.into());
1158 }
1159 self.setup_command(LabelType::CommandRecall, this_data)?;
1160 self.ipush(envelope)?;
1161 self.run()
1162 }
1163
1164 fn setup_function(&mut self, label: &Label) -> Result<(), MachineError> {
1165 self.set_pc_by_label(label)?;
1166 self.call_state.clear();
1167 self.scope.clear();
1168
1169 Ok(())
1170 }
1171
1172 pub fn setup_action<Args>(&mut self, name: Identifier, args: Args) -> Result<(), MachineError>
1174 where
1175 Args: IntoIterator,
1176 Args::Item: Into<Value>,
1177 {
1178 #[cfg(feature = "bench")]
1179 self.stopwatch
1180 .start(format!("setup_action: {}", name).as_str());
1181
1182 let action_def = self
1184 .machine
1185 .action_defs
1186 .get(&name)
1187 .ok_or_else(|| MachineError::new(MachineErrorType::NotDefined(name.to_string())))?;
1188 let args: Vec<Value> = args.into_iter().map(Into::into).collect();
1189 if args.len() != action_def.params.len() {
1190 return Err(MachineError::new(MachineErrorType::Unknown(
1191 alloc::format!(
1192 "action `{}` expects {} argument(s), but was called with {}",
1193 name,
1194 action_def.params.len(),
1195 args.len()
1196 ),
1197 )));
1198 }
1199 for (arg, param) in args.iter().zip(action_def.params.iter()) {
1200 if !arg.fits_type(¶m.ty) {
1201 return Err(MachineError::new(MachineErrorType::invalid_type(
1202 param.ty.to_string(),
1203 arg.type_name(),
1204 "invalid function argument",
1205 )));
1206 }
1207 }
1208
1209 self.setup_function(&Label::new(name, LabelType::Action))?;
1210 for a in args {
1211 self.ipush(a)?;
1212 }
1213
1214 #[cfg(feature = "bench")]
1215 self.stopwatch.stop();
1216
1217 Ok(())
1218 }
1219
1220 pub fn call_action<Args>(
1227 &mut self,
1228 name: Identifier,
1229 args: Args,
1230 ) -> Result<ExitReason, MachineError>
1231 where
1232 Args: IntoIterator,
1233 Args::Item: Into<Value>,
1234 {
1235 if !matches!(&self.ctx, CommandContext::Action(ActionContext{name: ctx_name,..}) if *ctx_name == name)
1236 {
1237 return Err(MachineErrorType::ContextMismatch.into());
1238 }
1239 self.setup_action(name, args)?;
1240 self.run()
1241 }
1242
1243 pub fn call_seal(&mut self, this_data: Struct) -> Result<ExitReason, MachineError> {
1247 let name = this_data.name.clone();
1248 if !matches!(&self.ctx, CommandContext::Seal(SealContext{name: ctx_name,..}) if *ctx_name == name)
1249 {
1250 return Err(MachineErrorType::ContextMismatch.into());
1251 }
1252 self.setup_function(&Label::new(name, LabelType::CommandSeal))?;
1253
1254 self.ipush(this_data)?;
1258 self.run()
1259 }
1260
1261 pub fn call_open(
1263 &mut self,
1264 name: Identifier,
1265 envelope: Struct,
1266 ) -> Result<ExitReason, MachineError> {
1267 if !matches!(&self.ctx, CommandContext::Open(OpenContext{name: ctx_name,..}) if *ctx_name == name)
1268 {
1269 return Err(MachineErrorType::ContextMismatch.into());
1270 }
1271 self.setup_function(&Label::new(name, LabelType::CommandOpen))?;
1272 self.ipush(envelope)?;
1273 self.run()
1274 }
1275
1276 pub fn consume_return(mut self) -> Result<Value, MachineError> {
1278 self.stack
1279 .pop_value()
1280 .map_err(|t| MachineError::from_position(t, self.pc, self.machine.codemap.as_ref()))
1281 }
1282
1283 fn validate_fact_literal(&mut self, fact: &Fact) -> Result<(), MachineError> {
1284 #[cfg(feature = "bench")]
1285 self.stopwatch.start("validate_fact_literal");
1286
1287 if !self
1288 .machine
1289 .fact_defs
1290 .get(&fact.name)
1291 .is_some_and(|schema| validate_fact_schema(fact, schema))
1292 {
1293 return Err(MachineError::from_position(
1294 MachineErrorType::InvalidSchema(fact.name.clone()),
1295 self.pc,
1296 self.machine.codemap.as_ref(),
1297 ));
1298 }
1299
1300 #[cfg(feature = "bench")]
1301 self.stopwatch.stop();
1302
1303 Ok(())
1304 }
1305}
1306
1307pub struct MachineStack(pub(crate) HVec<Value, STACK_SIZE>);
1309
1310impl MachineStack {
1311 pub const fn new() -> Self {
1313 Self(HVec::new())
1314 }
1315
1316 pub fn len(&self) -> usize {
1318 self.0.len()
1319 }
1320
1321 pub fn is_empty(&self) -> bool {
1323 self.0.len() == 0
1324 }
1325
1326 fn clear(&mut self) {
1327 self.0.clear();
1328 }
1329
1330 pub fn into_vec(self) -> Vec<Value> {
1332 self.0.into_iter().collect()
1333 }
1334}
1335
1336impl Stack for MachineStack {
1337 fn push_value(&mut self, value: Value) -> Result<(), MachineErrorType> {
1338 self.0
1339 .push(value)
1340 .map_err(|_| MachineErrorType::StackOverflow)
1341 }
1342
1343 fn pop_value(&mut self) -> Result<Value, MachineErrorType> {
1344 self.0.pop().ok_or(MachineErrorType::StackUnderflow)
1345 }
1346
1347 fn peek_value(&mut self) -> Result<&mut Value, MachineErrorType> {
1348 self.0.last_mut().ok_or(MachineErrorType::StackUnderflow)
1349 }
1350}
1351
1352impl Default for MachineStack {
1353 fn default() -> Self {
1354 Self::new()
1355 }
1356}
1357
1358impl<M> Display for RunState<'_, M>
1359where
1360 M: MachineIO<MachineStack>,
1361{
1362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1363 writeln!(f, "# Name table:")?;
1364 for (k, v) in &self.machine.labels {
1365 writeln!(f, " {}: {:?}", k, v)?;
1366 }
1367 write!(f, "# Current defs")?;
1368 if !self.call_state.is_empty() {
1369 write!(f, " ({} stacked)", self.call_state.len())?;
1370 }
1371 writeln!(f, ":")?;
1372 for (k, v) in self.scope.locals() {
1373 writeln!(f, " {}: {}", k, v)?;
1374 }
1375 writeln!(f, "# Stack:")?;
1376 for v in &self.stack.0 {
1377 write!(f, "{} ", v)?;
1378 }
1379 writeln!(f)?;
1380 writeln!(f, "# Program:")?;
1381 for (addr, instr) in self.machine.progmem.iter().enumerate() {
1382 for (k, v) in &self.machine.labels {
1383 if *v == addr {
1384 writeln!(f, "{}:", k)?;
1385 }
1386 }
1387 if addr == self.pc() {
1388 write!(f, "*")?;
1389 } else {
1390 write!(f, " ")?;
1391 }
1392 writeln!(f, " {:4} {}", addr, instr)?;
1393 }
1394 Ok(())
1395 }
1396}