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(test)]
88pub mod tests {
89    use pql_parser::parser::SelectorParser;
90
91    use super::*;
92    use crate::*;
93
94    impl PartialEq for VmInstruction {
95        fn eq(&self, other: &Self) -> bool {
96            match (self, other) {
97                (Self::BinOp(l), Self::BinOp(r)) => l == r,
98                (Self::Push(l), Self::Push(r)) => l == r,
99                (Self::Read(l), Self::Read(r))
100                | (Self::Write(l), Self::Write(r)) => l == r,
101                (Self::CastBoolToLong, Self::CastBoolToLong) => true,
102
103                (Self::Call(l), Self::Call(r)) => {
104                    format!("{l:?}") == format!("{r:?}")
105                }
106
107                _ => false,
108            }
109        }
110    }
111
112    fn exec<S, T>(ins: VmInstruction, xs: S, ys: T) -> (VmStore, VmStack)
113    where
114        S: IntoIterator<Item = VmValue>,
115        T: IntoIterator<Item = VmStackValue>,
116    {
117        let mut buffer = VmBuffer::default();
118        let mut store = VmStore::default();
119        let mut stack = VmStack::default();
120
121        for x in xs {
122            store.try_push(x).unwrap();
123        }
124
125        for y in ys {
126            stack.push(y);
127        }
128
129        ins.execute(&mut buffer, &mut store, &mut stack).unwrap();
130
131        (store, stack)
132    }
133
134    #[test]
135    fn test_execute_push() {
136        let v = PQLLong::from(0).into();
137        let (_, mut stack) = exec(VmInstruction::Push(v), [], []);
138
139        assert_eq!(v, stack.pop().unwrap());
140    }
141
142    #[test]
143    fn test_execute_read() {
144        let v: VmStackValue = PQLLong::from(0).into();
145        let (_, mut stack) =
146            exec(VmInstruction::Read(0.into()), [v.into()], []);
147
148        assert_eq!(v, stack.pop().unwrap());
149    }
150
151    #[test]
152    fn test_execute_write() {
153        let v0: VmStackValue = PQLLong::from(0).into();
154        let v1: VmStackValue = PQLLong::from(1).into();
155        let (store, mut stack) =
156            exec(VmInstruction::Write(0.into()), [v0.into()], [v1]);
157
158        assert_eq!(v1, *store.downcast_get::<&VmStackValue>(0.into()).unwrap());
159        assert!(stack.pop().is_err());
160    }
161
162    #[test]
163    fn test_execute_cast_bool() {
164        let (_, mut stack) =
165            exec(VmInstruction::CastBoolToLong, [], [true.into()]);
166
167        assert_eq!(1, stack.downcast_pop::<PQLLong>().unwrap());
168    }
169
170    fn exec_binop<A1, A2>(op: VmBinOp, l: A1, r: A2) -> VmStackValue
171    where
172        VmStackValue: From<A1> + From<A2>,
173    {
174        let mut buffer = VmBuffer::default();
175        let mut store = VmStore::default();
176        let mut stack = VmStack::default();
177
178        stack.push(r.into());
179        stack.push(l.into());
180
181        let ins = VmInstruction::BinOp(op);
182
183        ins.execute(&mut buffer, &mut store, &mut stack).unwrap();
184
185        stack.pop().unwrap()
186    }
187
188    #[quickcheck]
189    fn test_execute_binop_arith(l: VmStackValueNum, r: VmStackValueNum) {
190        use VmBinOpArith::*;
191        let v0 = PQLDouble::from(0.0).into();
192
193        let sum = exec_binop(VmBinOp::Arith(Add), l, r);
194        let diff = exec_binop(VmBinOp::Arith(Sub), l, r);
195        let prod = exec_binop(VmBinOp::Arith(Mul), l, r);
196
197        assert_eq!(sum, (l + r).into());
198        assert_eq!(diff, (l - r).into());
199        assert_eq!(prod, (l * r).into());
200
201        if r != v0 {
202            let quo = exec_binop(VmBinOp::Arith(Div), l, r);
203            assert_eq!(quo, (l / r).into());
204        }
205    }
206
207    #[test]
208    fn test_execute_binop_cmp() {
209        use VmBinOpCmp::*;
210        let t: VmStackValue = true.into();
211
212        assert_eq!(t, exec_binop(VmBinOp::Cmp(Ge), Rank::RA, Rank::RK));
213        assert_eq!(t, exec_binop(VmBinOp::Cmp(Gt), Rank::RA, Rank::RK));
214        assert_eq!(t, exec_binop(VmBinOp::Cmp(Le), Rank::RQ, Rank::RK));
215        assert_eq!(t, exec_binop(VmBinOp::Cmp(Lt), Rank::RQ, Rank::RK));
216    }
217
218    #[test]
219    fn test_execute_call() {
220        let mut buffer = VmBuffer::default();
221        let mut store = VmStore::default();
222        let mut stack = VmStack::default();
223
224        buffer.board.turn = Some(PQLCard::default());
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}