1#![cfg_attr(coverage_nightly, coverage(off))]
3
4use super::*;
5
6pub struct PQLRunner {}
7
8impl PQLRunner {
9 #[allow(clippy::missing_panics_doc)]
13 pub fn try_run_stmt(stmt: &ast::Stmt<'_>) -> PQLResult<RunnerOutput> {
14 let mut rng = rand::rng();
15 let mut vm = Vm::from_stmt(stmt)?;
16 let n_trails = vm.static_data.n_trails;
17 let game = vm.static_data.game;
18
19 let mut output = RunnerOutput::new(game, &stmt.selectors);
20
21 let where_program = match &stmt.where_clause {
22 Some(expr) => Some(vm::compile_where(&mut vm, expr)?),
23 None => None,
24 };
25
26 let programs = stmt
27 .selectors
28 .iter()
29 .map(|s| vm::compile_selector(&mut vm, s))
30 .collect::<PQLResult<Vec<_>>>()?;
31
32 while output.n_succ < n_trails {
33 if output.n_fail == n_trails {
34 return Err(((0, 1), VmError::SamplingFailed).into());
36 }
37
38 match vm.sample(&mut rng) {
39 Some(()) => {
40 if let Some(wp) = &where_program {
41 let keep = matches!(
42 wp.execute(&mut vm.as_context())?,
43 VmStackValue::Bool(true)
44 );
45
46 if !keep {
47 output.n_fail += 1;
49 continue;
50 }
51 }
52
53 for (idx, program) in programs.iter().enumerate() {
54 output.push_value(
55 idx,
56 program.execute(&mut vm.as_context())?,
57 );
58 }
59 output.n_succ += 1;
60 }
61 None => output.n_fail += 1,
62 }
63 }
64
65 Ok(output)
66 }
67
68 pub fn run<S: io::Write, T: io::Write>(
70 src: &str,
71 stream_out: &mut S,
72 stream_err: &mut T,
73 ) -> io::Result<()> {
74 match parse_pql(src) {
75 Ok(stmts) => {
76 for (i, stmt) in stmts.iter().enumerate() {
77 if i > 0 {
78 writeln!(stream_out, "{:-<80}", "")?;
79 }
80
81 match Self::try_run_stmt(stmt) {
82 Ok(output) => {
83 output.report_to_stream(stmt, stream_out)?;
84 writeln!(stream_out, "{} trials", output.n_succ)?;
85 }
86 Err(err) => {
87 writeln!(
88 stream_err,
89 "{err:?} {}",
90 &src[err.loc.0..err.loc.1]
91 )?;
92 }
93 }
94 }
95 }
96 Err(err) => writeln!(stream_err, "{err:?}")?,
97 }
98 Ok(())
99 }
100}