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::{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}