plotnik_vm/engine/
effect.rs

1//! Runtime effects for VM execution.
2//!
3//! Runtime effects carry actual node references, unlike bytecode EffectOp
4//! which only stores opcode + payload.
5
6use arborium_tree_sitter::Node;
7
8/// Runtime effect produced by VM execution.
9///
10/// Unlike bytecode `EffectOp`, runtime effects carry actual node references
11/// for materialization. Lifetime `'t` denotes the parsed tree-sitter tree.
12#[derive(Debug)]
13pub enum RuntimeEffect<'t> {
14    /// Capture a node reference.
15    Node(Node<'t>),
16    /// Extract source text from a node.
17    Text(Node<'t>),
18    /// Begin array scope.
19    Arr,
20    /// Push current value to array.
21    Push,
22    /// End array scope.
23    EndArr,
24    /// Begin object scope.
25    Obj,
26    /// Set field at member index.
27    Set(u16),
28    /// End object scope.
29    EndObj,
30    /// Begin enum variant at variant index.
31    Enum(u16),
32    /// End enum variant.
33    EndEnum,
34    /// Clear current value.
35    Clear,
36    /// Null placeholder (for optional/alternation).
37    Null,
38}
39
40/// Effect log with truncation support for backtracking.
41#[derive(Debug)]
42pub struct EffectLog<'t>(Vec<RuntimeEffect<'t>>);
43
44impl<'t> EffectLog<'t> {
45    /// Create an empty effect log.
46    pub fn new() -> Self {
47        Self(Vec::new())
48    }
49
50    /// Push an effect to the log.
51    #[inline]
52    pub fn push(&mut self, effect: RuntimeEffect<'t>) {
53        self.0.push(effect);
54    }
55
56    /// Get current length (used as watermark for backtracking).
57    #[inline]
58    pub fn len(&self) -> usize {
59        self.0.len()
60    }
61
62    /// Check if empty.
63    #[inline]
64    #[allow(dead_code)]
65    pub fn is_empty(&self) -> bool {
66        self.0.is_empty()
67    }
68
69    /// Truncate to watermark (for backtracking).
70    #[inline]
71    pub fn truncate(&mut self, watermark: usize) {
72        self.0.truncate(watermark);
73    }
74
75    /// Get effects as slice.
76    pub fn as_slice(&self) -> &[RuntimeEffect<'t>] {
77        &self.0
78    }
79
80    /// Consume into vec.
81    #[allow(dead_code)]
82    pub fn into_vec(self) -> Vec<RuntimeEffect<'t>> {
83        self.0
84    }
85}
86
87impl Default for EffectLog<'_> {
88    fn default() -> Self {
89        Self::new()
90    }
91}