xee_interpreter/interpreter/
state.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4use ahash::HashMap;
5use ahash::HashMapExt;
6use arrayvec::ArrayVec;
7use xot::Xot;
8
9use crate::error;
10use crate::function;
11use crate::sequence;
12use crate::stack;
13
14const FRAMES_MAX: usize = 64;
15
16#[derive(Debug, Clone)]
17pub(crate) struct Frame {
18    function: function::InlineFunctionId,
19    base: usize,
20    pub(crate) ip: usize,
21}
22
23impl Frame {
24    pub(crate) fn function(&self) -> function::InlineFunctionId {
25        self.function
26    }
27    pub(crate) fn base(&self) -> usize {
28        self.base
29    }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash)]
33struct RegexKey {
34    pattern: String,
35    flags: String,
36}
37
38#[derive(Debug)]
39pub struct State<'a> {
40    stack: Vec<stack::Value>,
41    build_stack: Vec<BuildStackEntry>,
42    frames: ArrayVec<Frame, FRAMES_MAX>,
43    regex_cache: RefCell<HashMap<RegexKey, Rc<regexml::Regex>>>,
44    pub(crate) xot: &'a mut Xot,
45}
46
47#[derive(Debug)]
48struct ItemBuildStackEntry {
49    build_stack: Vec<sequence::Item>,
50}
51
52#[derive(Debug)]
53struct BuildStackEntry {
54    item: ItemBuildStackEntry,
55}
56
57impl BuildStackEntry {
58    fn new() -> Self {
59        Self {
60            item: ItemBuildStackEntry {
61                build_stack: Vec::new(),
62            },
63        }
64    }
65
66    fn push(&mut self, item: sequence::Item) {
67        self.item.build_stack.push(item);
68    }
69
70    fn extend<I: Iterator<Item = sequence::Item>>(
71        &mut self,
72        items: impl IntoIterator<Item = sequence::Item, IntoIter = I>,
73    ) {
74        self.item.build_stack.extend(items);
75    }
76}
77
78impl From<BuildStackEntry> for sequence::Sequence {
79    fn from(build: BuildStackEntry) -> Self {
80        sequence::Sequence::new(build.item.build_stack)
81    }
82}
83
84impl<'a> State<'a> {
85    pub(crate) fn new(xot: &'a mut Xot) -> Self {
86        Self {
87            stack: vec![],
88            build_stack: vec![],
89            frames: ArrayVec::new(),
90            regex_cache: RefCell::new(HashMap::new()),
91            xot,
92        }
93    }
94
95    pub(crate) fn push<T>(&mut self, sequence: T)
96    where
97        T: Into<sequence::Sequence>,
98    {
99        let sequence: sequence::Sequence = sequence.into();
100        self.stack.push(sequence.into());
101    }
102
103    pub(crate) fn push_value<T>(&mut self, value: T)
104    where
105        T: Into<stack::Value>,
106    {
107        self.stack.push(value.into());
108    }
109
110    pub(crate) fn build_new(&mut self) {
111        self.build_stack.push(BuildStackEntry::new());
112    }
113
114    pub(crate) fn build_push(&mut self) -> error::Result<()> {
115        let value = self.pop()?;
116        let build = self.build_stack.last_mut().unwrap();
117        match value {
118            sequence::Sequence::Empty(_) => {}
119            sequence::Sequence::One(item) => build.push(item.into_item()),
120            // any other sequence
121            sequence => build.extend(sequence.iter()),
122        }
123        Ok(())
124    }
125
126    pub(crate) fn build_complete(&mut self) {
127        let build = self.build_stack.pop().unwrap();
128        self.stack.push(build.into());
129    }
130
131    pub(crate) fn push_var(&mut self, index: usize) {
132        self.stack
133            .push(self.stack[self.frame().base + index].clone());
134    }
135
136    pub(crate) fn push_closure_var(&mut self, index: usize) -> error::Result<()> {
137        let function = self.function()?;
138        let closure_vars = function.closure_vars();
139        self.stack.push(closure_vars[index].clone());
140        Ok(())
141    }
142
143    pub(crate) fn set_var(&mut self, index: usize) {
144        let base = self.frame().base;
145        self.stack[base + index] = self.stack.pop().unwrap();
146    }
147
148    #[inline]
149    pub(crate) fn pop(&mut self) -> error::Result<sequence::Sequence> {
150        self.pop_value().try_into()
151    }
152
153    #[inline]
154    pub(crate) fn pop_value(&mut self) -> stack::Value {
155        self.stack.pop().unwrap()
156    }
157
158    pub(crate) fn function(&self) -> error::Result<function::Function> {
159        // the function is always just below the base
160        let value = &self.stack[self.frame().base - 1];
161        match value {
162            stack::Value::Sequence(sequence) => sequence.clone().try_into(),
163            stack::Value::Absent => Err(error::Error::XPDY0002),
164        }
165    }
166
167    pub(crate) fn push_start_frame(&mut self, function_id: function::InlineFunctionId) {
168        self.frames.push(Frame {
169            function: function_id,
170            ip: 0,
171            base: 0,
172        });
173    }
174
175    pub(crate) fn push_frame(
176        &mut self,
177        function_id: function::InlineFunctionId,
178        arity: usize,
179    ) -> error::Result<()> {
180        if self.frames.len() >= self.frames.capacity() {
181            return Err(error::Error::StackOverflow);
182        }
183        self.frames.push(Frame {
184            function: function_id,
185            ip: 0,
186            base: self.stack.len() - arity,
187        });
188        Ok(())
189    }
190
191    pub(crate) fn frame(&self) -> &Frame {
192        self.frames.last().unwrap()
193    }
194
195    pub(crate) fn frame_mut(&mut self) -> &mut Frame {
196        self.frames.last_mut().unwrap()
197    }
198
199    pub(crate) fn jump(&mut self, displacement: i32) {
200        self.frame_mut().ip = (self.frame().ip as i32 + displacement) as usize;
201    }
202
203    pub(crate) fn callable(&self, arity: usize) -> error::Result<function::Function> {
204        let value = &self.stack[self.stack.len() - (arity + 1)];
205        match value {
206            stack::Value::Sequence(sequence) => sequence.clone().try_into(),
207            stack::Value::Absent => Err(error::Error::XPDY0002),
208        }
209    }
210
211    pub(crate) fn arguments(&self, arity: usize) -> &[stack::Value] {
212        &self.stack[self.stack.len() - arity..]
213    }
214
215    pub(crate) fn truncate_arguments(&mut self, arity: usize) {
216        self.stack.truncate(self.stack.len() - arity);
217    }
218
219    pub(crate) fn inline_return(&mut self, start_base: usize) -> bool {
220        let return_value = self.stack.pop().unwrap();
221
222        // truncate the stack to the base
223        let base = self.frame().base;
224        self.stack.truncate(base);
225
226        // pop off the function id we just called
227        // for the outer main function this is the context item
228        if !self.stack.is_empty() {
229            self.stack.pop();
230        }
231
232        // push back return value
233        self.stack.push(return_value);
234
235        // now pop off the frame
236        self.frames.pop();
237
238        // if the start base is the same as the base we just popped off,
239        // we are done
240        base == start_base
241    }
242
243    pub(crate) fn top(&self) -> error::Result<sequence::Sequence> {
244        self.stack.last().unwrap().try_into()
245    }
246
247    pub fn stack(&self) -> &[stack::Value] {
248        &self.stack
249    }
250
251    pub fn regex(&self, pattern: &str, flags: &str) -> error::Result<Rc<regexml::Regex>> {
252        // TODO: would be nice if we could not do to_string here but use &str
253        // but unfortunately otherwise lifetime issues bubble up all the way to
254        // the library bindings if we do so
255        let key = RegexKey {
256            pattern: pattern.to_string(),
257            flags: flags.to_string(),
258        };
259        let mut cache = self.regex_cache.borrow_mut();
260        let entry = cache.entry(key);
261        match entry {
262            std::collections::hash_map::Entry::Occupied(entry) => Ok(entry.get().clone()),
263            std::collections::hash_map::Entry::Vacant(entry) => {
264                let v = entry.insert(Rc::new(regexml::Regex::xpath(pattern, flags)?));
265                Ok(v.clone())
266            }
267        }
268    }
269
270    pub fn xot(&self) -> &Xot {
271        self.xot
272    }
273
274    pub fn xot_mut(&mut self) -> &mut Xot {
275        self.xot
276    }
277}