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}