prune_lang/cli/
pipeline.rs1use super::args::CliArgs;
2use super::diagnostic::{DiagLevel, Diagnostic};
3use super::*;
4use crate::cli::replay::ReplayWriter;
5use crate::logic::ast::Program;
6use crate::{interp, logic, syntax, tych};
7
8pub struct OutputWriter {
9 pub answer: Box<dyn Write>,
10 pub stat: Box<dyn Write>,
11 pub prog: Box<dyn Write>,
12}
13
14impl OutputWriter {
15 pub fn empty() -> OutputWriter {
16 OutputWriter {
17 answer: Box::new(io::empty()),
18 stat: Box::new(io::empty()),
19 prog: Box::new(io::empty()),
20 }
21 }
22}
23
24pub struct Pipeline<'a> {
25 pub args: &'a CliArgs,
26 pub src_path: PathBuf,
27 pub src_code: String,
28 pub msg_out: Box<dyn Write + 'a>,
29}
30
31impl<'a> Pipeline<'a> {
32 pub fn new(args: &'a CliArgs) -> Pipeline<'a> {
33 Pipeline {
34 args,
35 src_path: PathBuf::new(),
36 src_code: String::new(),
37 msg_out: Box::new(io::stderr()),
38 }
39 }
40
41 fn emit_diags<D: Into<Diagnostic>>(&mut self, diags: Vec<D>) -> bool {
42 let mut flag = false;
43 for diag in diags.into_iter() {
44 let diag = diag.into();
45 if diag.level == DiagLevel::Error
46 || (self.args.warn_as_err && diag.level == DiagLevel::Warn)
47 {
48 flag = true;
49 }
50 writeln!(
51 self.msg_out,
52 "{}",
53 diag.report(self.src_code.as_str(), self.args.verbosity)
54 )
55 .unwrap();
56 }
57 flag
58 }
59
60 pub fn run_compiler_pipline(&mut self) -> Result<Program, io::Error> {
61 self.read_source_code()?;
62
63 let mut prog = self.parse_program()?;
64
65 self.rename_pass(&mut prog)?;
66
67 self.check_pass(&mut prog)?;
68
69 let prog = self.compile_pass(&prog);
70
71 Ok(prog)
72 }
73
74 pub fn read_source_code(&mut self) -> Result<(), io::Error> {
75 let src_path = PathBuf::from(&self.args.input);
76 if let Ok(src_code) = std::fs::read_to_string(&src_path) {
77 self.src_path = src_path;
78 self.src_code = src_code;
79 Ok(())
80 } else {
81 Err(io::Error::new(
82 io::ErrorKind::NotFound,
83 format!("file \"{src_path:?}\" doesn't exist!"),
84 ))
85 }
86 }
87
88 pub fn parse_program(&mut self) -> Result<syntax::ast::Program, io::Error> {
89 let (prog, errs) = syntax::parser::parse_program(self.src_code.as_str());
90 if self.emit_diags(errs) {
91 return Err(io::Error::other("failed to parse program!"));
92 }
93 Ok(prog)
94 }
95
96 pub fn rename_pass(&mut self, prog: &mut syntax::ast::Program) -> Result<(), io::Error> {
97 let errs = tych::rename::rename_pass(prog);
98 if self.emit_diags(errs) {
99 return Err(io::Error::other("failed in binding analysis pass!"));
100 }
101 Ok(())
102 }
103
104 pub fn check_pass(&mut self, prog: &mut syntax::ast::Program) -> Result<(), io::Error> {
105 let errs = tych::check::check_pass(prog);
106 if self.emit_diags(errs) {
107 return Err(io::Error::other("failed in type checking pass!"));
108 }
109 Ok(())
110 }
111
112 pub fn compile_pass(&mut self, prog: &syntax::ast::Program) -> logic::ast::Program {
113 let mut prog = logic::compile::compile_pass(prog);
114
115 logic::elaborate::elaborate_pass(&mut prog);
116
117 prog
118 }
119
120 pub fn run_backend(&self, prog: &logic::ast::Program) -> Result<Vec<usize>, io::Error> {
121 let mut output = create_output_writer(self.args, &self.src_path)?;
122 writeln!(output.prog, "{prog}").unwrap();
123
124 let mut res_vec = Vec::new();
125 let mut runner = interp::runner::RunnerState::new(prog, &mut output, self.args);
126 for query_decl in &prog.querys {
127 for param in &query_decl.params {
128 runner.config_set_param(param);
129 }
130 let res = runner.run_iddfs_loop(query_decl.entry);
131 res_vec.push(res);
132 }
133 Ok(res_vec)
134 }
135}
136
137fn create_dump_dir(src_path: &PathBuf) -> Result<PathBuf, io::Error> {
138 use std::fs;
139
140 if src_path.extension().and_then(|ext| ext.to_str()) != Some("pr") {
141 return Err(io::Error::new(
142 io::ErrorKind::InvalidInput,
143 format!("source file extension is not \".pr\"!: {src_path:?}"),
144 ));
145 }
146
147 if !src_path.exists() {
148 return Err(io::Error::new(
149 io::ErrorKind::NotFound,
150 format!("file \"{src_path:?}\" doesn't exist!"),
151 ));
152 }
153
154 if !src_path.is_file() {
155 return Err(io::Error::new(
156 io::ErrorKind::InvalidInput,
157 format!("path \"{src_path:?}\" exists, but it is not a file!"),
158 ));
159 }
160
161 let file_stem = src_path.file_stem().unwrap().to_os_string();
162
163 let mut dir_path = src_path.clone();
164 dir_path.pop();
165 dir_path.push(file_stem);
166
167 if !dir_path.exists() {
168 fs::create_dir(&dir_path)?;
169 } else if !dir_path.is_dir() {
170 return Err(io::Error::new(
171 io::ErrorKind::AlreadyExists,
172 format!("path \"{dir_path:?}\" exist, but it is not a directory!"),
173 ));
174 }
175
176 Ok(dir_path)
177}
178
179fn create_output_writer(args: &CliArgs, src_path: &PathBuf) -> Result<OutputWriter, io::Error> {
180 let mut output = OutputWriter::empty();
181 if args.dump_file {
182 let dir_path = create_dump_dir(src_path)?;
183
184 let answer = File::create(dir_path.join("answer.txt"))?;
185 if args.show_output {
186 output.answer = Box::new(ReplayWriter::replay_stdout(answer));
187 } else {
188 output.answer = Box::new(answer);
189 }
190
191 let stat = File::create(dir_path.join("stat.txt"))?;
192 if args.show_stat {
193 output.stat = Box::new(ReplayWriter::replay_stdout(stat));
194 } else {
195 output.stat = Box::new(stat);
196 }
197
198 let prog = File::create(dir_path.join("prog.txt"))?;
199 if args.show_prog {
200 output.prog = Box::new(ReplayWriter::replay_stdout(prog));
201 } else {
202 output.prog = Box::new(prog);
203 }
204 } else {
205 if args.show_output {
206 output.answer = Box::new(io::stdout());
207 }
208
209 if args.show_stat {
210 output.stat = Box::new(io::stdout());
211 }
212
213 if args.show_prog {
214 output.prog = Box::new(io::stdout());
215 }
216 }
217 Ok(output)
218}
219
220pub fn run_cli_pipeline() -> Result<Vec<usize>, io::Error> {
221 let args = args::parse_cli_args();
222 let mut pipe = Pipeline::new(&args);
223 let prog = pipe.run_compiler_pipline()?;
224 let res = pipe.run_backend(&prog)?;
225 Ok(res)
226}
227
228pub fn run_test_pipeline(prog_name: PathBuf) -> Result<Vec<usize>, io::Error> {
229 let args = args::get_test_cli_args(prog_name);
230 let mut pipe = Pipeline::new(&args);
231 let prog = pipe.run_compiler_pipline()?;
232 let res = pipe.run_backend(&prog)?;
233 Ok(res)
234}
235
236pub fn run_test_diag_pipeline(prog_name: PathBuf, msg_out: &mut Vec<u8>) -> Result<(), io::Error> {
237 let args = args::get_test_cli_args(prog_name);
238 let mut pipe = Pipeline::new(&args);
239 pipe.msg_out = Box::new(msg_out);
240 let _prog = pipe.run_compiler_pipline()?;
241 Ok(())
242}
243
244pub fn run_bench_pipeline(
245 prog_name: PathBuf,
246 heuristic: args::Heuristic,
247 depth_limit: usize,
248) -> Result<Vec<usize>, io::Error> {
249 let args = args::get_bench_cli_args(prog_name, heuristic, depth_limit);
250 let mut pipe = Pipeline::new(&args);
251 let prog = pipe.run_compiler_pipline()?;
252 let res = pipe.run_backend(&prog)?;
253 Ok(res)
254}