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