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}