Skip to main content

prune_lang/cli/
pipeline.rs

1use 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}