1use std::{
2 env, fs,
3 io::{stdin, stdout, Write},
4 process::ExitCode,
5};
6
7use console::Term;
8use serde::{Deserialize, Serialize};
9
10use crate::{
11 heap::{
12 heap::{Cell, Heap},
13 query_heap::QueryHeap,
14 symbol_db::SymbolDB,
15 },
16 parser::{
17 build_tree::{TokenStream, TreeClause},
18 execute_tree::{build_clause, execute_tree},
19 tokeniser::tokenise,
20 },
21 predicate_modules::{load_predicate_module, PredicateModule},
22 program::predicate_table::PredicateTable,
23 resolution::proof::Proof,
24};
25
26#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
28pub struct Config {
29 pub max_depth: usize,
31 pub max_clause: usize,
33 pub max_pred: usize,
35 #[serde(default)]
37 pub debug: bool,
38}
39
40#[derive(Serialize, Deserialize, Debug)]
42pub struct BodyClause {
43 pub symbol: String,
45 pub arity: usize,
47}
48
49#[derive(Serialize, Deserialize, Debug)]
51pub struct Examples {
52 pub pos: Vec<String>,
54 pub neg: Vec<String>,
56}
57
58#[derive(Serialize, Deserialize, Debug)]
60pub struct SetUp {
61 pub config: Config,
62 pub body_predicates: Vec<BodyClause>,
63 pub files: Vec<String>,
64 pub examples: Option<Examples>,
65 #[serde(default)]
67 pub top_prog: bool,
68 #[serde(default)]
70 pub no_reduce: bool,
71}
72
73impl Examples {
74 pub fn to_query(&self) -> String {
78 let mut buffer = String::new();
79 for pos_ex in &self.pos {
80 buffer += pos_ex;
81 buffer += ",";
82 }
83 for neg_ex in &self.neg {
84 buffer += &format!("not({neg_ex}),");
85 }
86 buffer.pop();
87 buffer += ".";
88 buffer
89 }
90}
91
92pub struct App {
106 modules: Vec<&'static PredicateModule>,
107 config_path: String,
108 auto: bool,
109}
110
111impl App {
112 pub fn new() -> Self {
114 App {
115 modules: Vec::new(),
116 config_path: "setup.json".into(),
117 auto: false,
118 }
119 }
120
121 pub fn from_args() -> Self {
127 let args: Vec<String> = env::args().collect();
128 let auto = args.iter().any(|arg| arg == "--all" || arg == "-a");
129
130 let config_path = args
131 .iter()
132 .filter(|arg| !arg.starts_with('-') && *arg != &args[0])
133 .next()
134 .cloned()
135 .unwrap_or_else(|| "setup.json".into());
136
137 App {
138 modules: Vec::new(),
139 config_path,
140 auto,
141 }
142 }
143
144 pub fn add_module(mut self, module: &'static PredicateModule) -> Self {
146 self.modules.push(module);
147 self
148 }
149
150 pub fn config(mut self, path: impl Into<String>) -> Self {
152 self.config_path = path.into();
153 self
154 }
155
156 pub fn auto(mut self, value: bool) -> Self {
158 self.auto = value;
159 self
160 }
161
162 pub fn run(self) -> ExitCode {
167 let (config, predicate_table, heap, examples, top_prog, no_reduce) = self.load_setup();
168
169 match (examples, top_prog) {
170 (Some(examples), true) => {
171 crate::top_prog::run(examples, &predicate_table, heap, config, no_reduce)
172 }
173 (Some(examples), false) => {
174 start_query(
175 &examples.to_query(),
176 &predicate_table,
177 &heap,
178 config,
179 self.auto,
180 )
181 .unwrap();
182 ExitCode::SUCCESS
183 }
184 (None, _) => main_loop(config, predicate_table, &heap),
185 }
186 }
187
188 fn load_setup(
189 &self,
190 ) -> (
191 Config,
192 PredicateTable,
193 Vec<Cell>,
194 Option<Examples>,
195 bool,
196 bool,
197 ) {
198 let mut heap = Vec::new();
199 let mut predicate_table = PredicateTable::new();
200
201 for module in &self.modules {
202 load_predicate_module(&mut predicate_table, module);
203 }
204
205 let setup: SetUp = serde_json::from_str(
206 &fs::read_to_string(&self.config_path)
207 .unwrap_or_else(|_| panic!("Failed to read config file: {}", self.config_path)),
208 )
209 .unwrap_or_else(|e| panic!("Failed to parse config file '{}': {}", self.config_path, e));
210 let config = setup.config;
211
212 for file_path in setup.files {
213 load_file(file_path, &mut predicate_table, &mut heap);
214 }
215
216 for BodyClause { symbol, arity } in setup.body_predicates {
217 let sym = SymbolDB::set_const(symbol.clone());
218 predicate_table
219 .set_body((sym, arity), true)
220 .unwrap_or_else(|e| panic!("{e}: {symbol}/{arity}"));
221 }
222
223 (
224 config,
225 predicate_table,
226 heap,
227 setup.examples,
228 setup.top_prog,
229 setup.no_reduce,
230 )
231 }
232}
233
234fn load_file(file_path: String, predicate_table: &mut PredicateTable, heap: &mut Vec<Cell>) {
235 let file = fs::read_to_string(&file_path)
236 .unwrap_or_else(|_| panic!("Failed to read file: {}", file_path));
237 let syntax_tree = TokenStream::new(tokenise(file).unwrap())
238 .parse_all()
239 .unwrap();
240
241 execute_tree(syntax_tree, heap, predicate_table);
242}
243
244fn continue_proof(auto: bool) -> bool {
245 if auto {
246 return true;
247 }
248 let term = Term::stderr();
249 loop {
250 match term.read_key_raw().unwrap() {
251 console::Key::Enter | console::Key::Backspace | console::Key::Char('.') => {
252 return false
253 }
254 console::Key::Char(' ' | ';') | console::Key::Tab => return true,
255 _ => (),
256 }
257 }
258}
259
260fn start_query(
261 query_text: &str,
262 predicate_table: &PredicateTable,
263 heap: &[Cell],
264 config: Config,
265 auto: bool,
266) -> Result<(), String> {
267 let query = format!(":-{query_text}");
268 let literals = match TokenStream::new(tokenise(query)?).parse_clause()? {
269 Some(TreeClause::Directive(literals)) => literals,
270 _ => return Err(format!("Query: '{query_text}' incorrectly formatted")),
271 };
272
273 let mut query_heap = QueryHeap::new(heap, None);
274 let goals = build_clause(literals, None, None, &mut query_heap, true);
275 let mut vars = Vec::new();
276 for literal in goals.iter() {
277 vars.extend(query_heap.term_vars(*literal, false).iter().map(|addr| {
278 (
279 SymbolDB::get_var(*addr, query_heap.get_id()).unwrap(),
280 *addr,
281 )
282 }));
283 }
284 let mut proof = Proof::new(query_heap, &goals);
285
286 loop {
287 if proof.prove(&predicate_table, config) {
288 println!("TRUE");
289 for (symbol, addr) in &vars {
290 println!("{symbol} = {}", proof.heap.term_string(*addr))
291 }
292 if proof.hypothesis.len() != 0 {
294 println!("{}", proof.hypothesis.to_string(&proof.heap));
295 }
296 if !continue_proof(auto) {
297 break;
298 }
299 } else {
300 println!("FALSE");
301 break;
302 }
303 }
304
305 drop(proof);
306
307 Ok(())
308}
309
310fn main_loop(config: Config, predicate_table: PredicateTable, heap: &[Cell]) -> ExitCode {
311 let mut buffer = String::new();
312 loop {
313 if buffer.is_empty() {
314 print!("?-");
315 stdout().flush().unwrap();
316 }
317 match stdin().read_line(&mut buffer) {
318 Ok(_) => {
319 if buffer.contains('.') {
320 match start_query(&buffer, &predicate_table, heap, config, false) {
321 Ok(_) => buffer.clear(),
322 Err(error) => println!("{error}"),
323 }
324 } else {
325 continue;
326 }
327 }
328 Err(error) => {
329 println!("error: {error}");
330 break;
331 }
332 }
333 }
334 ExitCode::SUCCESS
335}