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