open_pql/vm/
instruction.rs

1use super::*;
2
3#[derive(Debug, Clone, From)]
4pub enum VmInstruction {
5    Call(&'static dyn PQLFn),
6    BinOp(VmBinOp),
7    Push(VmStackValue),
8    #[from(skip)]
9    Read(VmStoreVarIdx),
10    #[from(skip)]
11    Write(VmStoreVarIdx),
12    CastBoolToLong,
13}
14
15impl VmInstruction {
16    pub(crate) fn execute(
17        &self,
18        buffer: &mut VmBuffer,
19        store: &mut VmStore,
20        stack: &mut VmStack,
21    ) -> Result<(), PQLError> {
22        match self {
23            Self::Push(v) => stack.push(*v),
24
25            Self::Call(function) => {
26                let val = function.evaluate(buffer, store, stack)?;
27
28                stack.push(val);
29            }
30
31            Self::BinOp(op) => op.execute(buffer, store, stack)?,
32
33            Self::CastBoolToLong => {
34                let b: PQLBoolean = stack.downcast_pop()?;
35
36                stack.push(PQLLong::from(b).into());
37            }
38
39            Self::Read(i) => {
40                let v: &VmStackValue = store.downcast_get(*i)?;
41
42                stack.push(*v);
43            }
44
45            Self::Write(i) => {
46                let ptr: &mut VmStackValue = store.downcast_get_mut(*i)?;
47                let v = stack.pop()?;
48
49                *ptr = v;
50            }
51        }
52
53        Ok(())
54    }
55}
56
57pub fn init(
58    selectors: &[Selector],
59    game: PQLGame,
60    player_names: &[&str],
61) -> Result<(VmInstructions, VmStore), PQLError> {
62    let mut instructions = vec![];
63    let mut store = VmStore::default();
64    let deps = InitDeps { game, player_names };
65
66    for selector in selectors {
67        let rtn_type = push_expr(
68            &selector.expr,
69            &mut instructions,
70            &mut store,
71            &deps,
72            None,
73        )?;
74
75        push_selector(
76            selector,
77            &mut instructions,
78            &mut store,
79            &deps,
80            rtn_type,
81        )?;
82    }
83
84    Ok((instructions, store))
85}
86
87#[cfg_attr(coverage_nightly, coverage(off))]
88#[cfg(test)]
89pub mod tests {
90    use pql_parser::parser::SelectorParser;
91
92    use super::*;
93    use crate::*;
94
95    impl PartialEq for VmInstruction {
96        fn eq(&self, other: &Self) -> bool {
97            match (self, other) {
98                (Self::BinOp(l), Self::BinOp(r)) => l == r,
99                (Self::Push(l), Self::Push(r)) => l == r,
100                (Self::Read(l), Self::Read(r))
101                | (Self::Write(l), Self::Write(r)) => l == r,
102                (Self::CastBoolToLong, Self::CastBoolToLong) => true,
103
104                (Self::Call(l), Self::Call(r)) => {
105                    format!("{l:?}") == format!("{r:?}")
106                }
107
108                _ => false,
109            }
110        }
111    }
112
113    fn exec<S, T>(ins: VmInstruction, xs: S, ys: T) -> (VmStore, VmStack)
114    where
115        S: IntoIterator<Item = VmValue>,
116        T: IntoIterator<Item = VmStackValue>,
117    {
118        let mut buffer = VmBuffer::default();
119        let mut store = VmStore::default();
120        let mut stack = VmStack::default();
121
122        for x in xs {
123            store.try_push(x).unwrap();
124        }
125
126        for y in ys {
127            stack.push(y);
128        }
129
130        ins.execute(&mut buffer, &mut store, &mut stack).unwrap();
131
132        (store, stack)
133    }
134
135    #[test]
136    fn test_execute_push() {
137        let v = PQLLong::from(0).into();
138        let (_, mut stack) = exec(VmInstruction::Push(v), [], []);
139
140        assert_eq!(v, stack.pop().unwrap());
141    }
142
143    #[test]
144    fn test_execute_read() {
145        let v: VmStackValue = PQLLong::from(0).into();
146        let (_, mut stack) =
147            exec(VmInstruction::Read(0.into()), [v.into()], []);
148
149        assert_eq!(v, stack.pop().unwrap());
150    }
151
152    #[test]
153    fn test_execute_write() {
154        let v0: VmStackValue = PQLLong::from(0).into();
155        let v1: VmStackValue = PQLLong::from(1).into();
156        let (store, mut stack) =
157            exec(VmInstruction::Write(0.into()), [v0.into()], [v1]);
158
159        assert_eq!(v1, *store.downcast_get::<&VmStackValue>(0.into()).unwrap());
160        assert!(stack.pop().is_err());
161    }
162
163    #[test]
164    fn test_execute_cast_bool() {
165        let (_, mut stack) =
166            exec(VmInstruction::CastBoolToLong, [], [true.into()]);
167
168        assert_eq!(1, stack.downcast_pop::<PQLLong>().unwrap());
169    }
170
171    fn exec_binop<A1, A2>(op: VmBinOp, l: A1, r: A2) -> VmStackValue
172    where
173        VmStackValue: From<A1> + From<A2>,
174    {
175        let mut buffer = VmBuffer::default();
176        let mut store = VmStore::default();
177        let mut stack = VmStack::default();
178
179        stack.push(r.into());
180        stack.push(l.into());
181
182        let ins = VmInstruction::BinOp(op);
183
184        ins.execute(&mut buffer, &mut store, &mut stack).unwrap();
185
186        stack.pop().unwrap()
187    }
188
189    #[quickcheck]
190    fn test_execute_binop_arith(l: VmStackValueNum, r: VmStackValueNum) {
191        use VmBinOpArith::*;
192        let v0 = PQLDouble::from(0.0).into();
193
194        let sum = exec_binop(VmBinOp::Arith(Add), l, r);
195        let diff = exec_binop(VmBinOp::Arith(Sub), l, r);
196        let prod = exec_binop(VmBinOp::Arith(Mul), l, r);
197
198        assert_eq!(sum, (l + r).into());
199        assert_eq!(diff, (l - r).into());
200        assert_eq!(prod, (l * r).into());
201
202        if r != v0 {
203            let quo = exec_binop(VmBinOp::Arith(Div), l, r);
204            assert_eq!(quo, (l / r).into());
205        }
206    }
207
208    #[test]
209    fn test_execute_binop_cmp() {
210        use VmBinOpCmp::*;
211        let t: VmStackValue = true.into();
212
213        assert_eq!(t, exec_binop(VmBinOp::Cmp(Ge), Rank::RA, Rank::RK));
214        assert_eq!(t, exec_binop(VmBinOp::Cmp(Gt), Rank::RA, Rank::RK));
215        assert_eq!(t, exec_binop(VmBinOp::Cmp(Le), Rank::RQ, Rank::RK));
216        assert_eq!(t, exec_binop(VmBinOp::Cmp(Lt), Rank::RQ, Rank::RK));
217    }
218
219    #[test]
220    fn test_execute_call() {
221        let mut buffer = VmBuffer::default();
222        let mut store = VmStore::default();
223        let mut stack = VmStack::default();
224
225        let ins = VmInstruction::Call(&(functions::turn_card as fn(_) -> _));
226
227        ins.execute(&mut buffer, &mut store, &mut stack).unwrap();
228
229        assert_eq!(
230            PQLCard::default(),
231            stack.downcast_pop::<PQLCard>().unwrap()
232        );
233    }
234
235    fn assert_err<S, T>(ins: VmInstruction, xs: S, ys: T)
236    where
237        S: IntoIterator<Item = VmValue>,
238        T: IntoIterator<Item = VmStackValue>,
239    {
240        let mut buffer = VmBuffer::default();
241        let mut store = VmStore::default();
242        let mut stack = VmStack::default();
243
244        for x in xs {
245            store.try_push(x).unwrap();
246        }
247
248        for y in ys {
249            stack.push(y);
250        }
251
252        assert!(ins.execute(&mut buffer, &mut store, &mut stack).is_err());
253    }
254
255    #[test]
256    fn test_execute_error() {
257        let v: VmStackValue = PQLLong::from(0).into();
258        let s: VmValue = VmValue::Str(String::new());
259
260        assert_err(VmInstruction::Read(0.into()), [s.clone()], []);
261        assert_err(VmInstruction::Write(0.into()), [s], []);
262        assert_err(VmInstruction::Write(0.into()), [v.into()], []);
263
264        assert_err(VmInstruction::CastBoolToLong, [], [v]);
265        assert_err(
266            VmInstruction::BinOp(VmBinOp::Arith(VmBinOpArith::Add)),
267            [],
268            [],
269        );
270    }
271
272    #[test]
273    fn test_init() {
274        fn s(s: &str) -> Selector {
275            SelectorParser::new().parse(s).unwrap()
276        }
277
278        let g = PQLGame::default();
279        let i = 0.into();
280        let (ins, _) = init(&[s("count(1 > 0)")], g, &[]).unwrap();
281
282        assert_eq!(
283            ins,
284            [
285                VmStackValue::INT_ZERO.into(),
286                VmStackValue::INT_ONE.into(),
287                VmBinOpCmp::Gt.into(),
288                VmInstruction::CastBoolToLong,
289                VmInstruction::Read(i),
290                VmBinOpArith::Add.into(),
291                VmInstruction::Write(i),
292            ]
293        );
294
295        assert!(init(&[s("count(_ > 0)")], g, &[]).is_err());
296        assert!(init(&[s("count(1)")], g, &[]).is_err());
297    }
298}