1use crate::files::{Data, Files, Resolve};
4use crate::{Diagnostic, Error, FailureMode, Options, Outcome};
5use probl_sema::ir;
6use probl_syntax::SourceFile;
7use std::sync::Arc;
8
9pub fn compile(name: &str, source: &str) -> Result<Program, Error> {
13 let file = Arc::new(SourceFile::new(name, source));
14 let (program, diagnostics) = probl_sema::compile(source);
15 match program {
16 Some(ir) => Ok(Program {
17 ir: Arc::new(ir),
18 warnings: diagnostics.into_iter().map(|d| Diagnostic::new(d, &file)).collect(),
19 file,
20 }),
21 None => Err(Error::compile(diagnostics, &file)),
22 }
23}
24
25#[derive(Clone)]
28pub struct Program {
29 ir: Arc<ir::Program>,
30 file: Arc<SourceFile>,
31 warnings: Vec<Diagnostic>,
32}
33
34#[non_exhaustive]
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub enum Mode {
38 Auto,
41 Enumerate,
43 #[non_exhaustive]
45 Sample { runs: u64, seed: u64 },
46 #[non_exhaustive]
49 Beam { worlds: u64 },
50 #[non_exhaustive]
53 Particles { runs: u64, seed: u64 },
54}
55
56impl Program {
57 pub fn warnings(&self) -> &[Diagnostic] {
59 &self.warnings
60 }
61
62 pub fn mode(&self) -> Mode {
64 match self.ir.settings.mode {
65 ir::Mode::Auto => Mode::Auto,
66 ir::Mode::Enumerate => Mode::Enumerate,
67 ir::Mode::Sample { runs, seed } => Mode::Sample { runs, seed },
68 ir::Mode::Beam { worlds } => Mode::Beam { worlds },
69 ir::Mode::Particles { runs, seed } => Mode::Particles { runs, seed },
70 }
71 }
72
73 pub fn failure_mode(&self) -> Option<FailureMode> {
75 self.ir.settings.on_error.map(FailureMode::new)
76 }
77
78 pub fn reads_data(&self) -> bool {
81 !self.ir.inputs.is_empty()
82 }
83
84 pub fn load(&self, files: &mut dyn Files, options: &Options) -> Result<Data, Error> {
88 let mut snapshots = probl_engine::data::Snapshots::default();
89 let cancel = options.cancel.as_ref().map(|c| c.flag());
90 probl_engine::data::load(
91 &self.ir,
92 &mut Resolve(files),
93 &mut snapshots,
94 &options.limits.input(),
95 cancel,
96 )
97 .map(Data::new)
98 .map_err(|e| Error::runtime(e, &self.file))
99 }
100
101 pub fn run(&self, options: &Options) -> Result<Outcome, Error> {
106 let mut printed = Vec::new();
107 let result = {
108 let mut print = |line: &str| printed.push(line.to_string());
109 self.run_with(options, &mut print)
110 };
111 match result {
112 Ok(mut outcome) => {
113 outcome.printed = printed;
114 Ok(outcome)
115 }
116 Err(mut e) => {
117 if let Some(outcome) = e.partial_mut() {
118 outcome.printed = printed;
119 }
120 Err(e)
121 }
122 }
123 }
124
125 pub fn run_with(&self, options: &Options, print: &mut (dyn FnMut(&str) + Send)) -> Result<Outcome, Error> {
127 let mut engine = options.engine(&self.ir);
128 engine.inputs = self.inputs(options)?;
129 #[cfg(target_arch = "wasm32")]
130 let result = probl_engine::run_on_this_thread(&self.ir, &engine, print);
131 #[cfg(not(target_arch = "wasm32"))]
132 let result = probl_engine::run(&self.ir, &engine, print);
133 let outcome = result.map_err(|e| Error::runtime(e, &self.file))?;
134 let outcome = Outcome::new(outcome, &self.ir, &self.file);
135 match outcome.failures().is_empty() {
136 true => Ok(outcome),
137 false => Err(Error::failed(outcome)),
138 }
139 }
140
141 fn inputs(&self, options: &Options) -> Result<Option<Arc<probl_engine::data::Inputs>>, Error> {
143 let span = self.ir.inputs.first().map(|i| i.span).unwrap_or_default();
144 match &options.data {
145 Some(data) if !data.inputs.fit(&self.ir) => Err(Error::usage(
146 span,
147 "the data was loaded for another program",
148 "load this program's data with its `Program::load`",
149 &self.file,
150 )),
151 Some(data) => Ok(Some(data.inputs.clone())),
152 None if self.reads_data() => Err(Error::usage(
153 span,
154 "the program reads data, which wasn't loaded",
155 "load it with `Program::load`, and give it to the run with `Options::data`",
156 &self.file,
157 )),
158 None => Ok(None),
159 }
160 }
161
162 pub(crate) fn report_count(&self) -> usize {
164 self.ir.reports.len()
165 }
166}
167
168impl std::fmt::Debug for Program {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 f.debug_struct("Program")
171 .field("name", &self.file.name)
172 .field("mode", &self.mode())
173 .field("reports", &self.report_count())
174 .field("warnings", &self.warnings)
175 .finish()
176 }
177}