use neorusticus::PrologEngine;
use std::io::{self, Write};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Neorusticus Prolog Interactive Shell ===");
println!("Enter Prolog clauses (ending with .) or queries (ending with ? or .)");
println!("Type 'help' for commands, 'quit' to exit.\n");
let mut engine = PrologEngine::with_limits(100);
let stdin = io::stdin();
loop {
print!("?- ");
io::stdout().flush()?;
let mut input = String::new();
stdin.read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
continue;
}
match input {
"quit" | "exit" | "halt" => {
println!("Goodbye!");
break;
}
"help" => {
print_help();
continue;
}
"stats" => {
println!("{}", engine.get_stats());
continue;
}
"clear" => {
engine = PrologEngine::with_limits(100);
println!("Database cleared.");
continue;
}
_ => {}
}
let mut complete_input = input.to_string();
while !complete_input.ends_with('.') && !complete_input.ends_with('?') {
print!(" ");
io::stdout().flush()?;
let mut continuation = String::new();
stdin.read_line(&mut continuation)?;
complete_input.push(' ');
complete_input.push_str(continuation.trim());
}
if complete_input.ends_with('?') {
handle_query(&mut engine, &complete_input);
} else if complete_input.ends_with('.') {
handle_clause(&mut engine, &complete_input);
} else {
println!("Error: Query must end with '?'");
}
}
Ok(())
}
fn print_help() {
println!("Commands:");
println!(" help - Show this help message");
println!(" quit - Exit the shell");
println!(" stats - Show engine statistics");
println!(" clear - Clear the database");
println!();
println!("Usage:");
println!(" Facts: parent(tom, bob).");
println!(" Rules: grandparent(X, Z) :- parent(X, Y), parent(Y, Z).");
println!(" Queries: parent(tom, X)? or parent(tom, X).");
println!();
println!("Built-in predicates:");
println!(" Arithmetic: is/2, =:=/2, =\\=/2, >/2, </2, >=/2, =</2");
println!(" Unification: =/2, \\=/2");
println!(" Type checking: var/1, nonvar/1, atom/1, number/1, compound/1");
println!(" Lists: append/3, member/2, length/2");
println!(" Control: true/0, fail/0, !/0 (cut)");
}
fn handle_clause(engine: &mut PrologEngine, input: &str) {
println!("Adding clause: {}", input.trim());
match engine.parse_and_add(input) {
Ok(()) => println!("Clause added successfully."),
Err(e) => {
println!("Parse error: {}", e);
engine.print_error(&e);
}
}
}
fn handle_query(engine: &mut PrologEngine, input: &str) {
println!("Executing query: {}", input.trim());
let variables = extract_query_variables(input);
match engine.parse_query(input) {
Ok(solutions) => {
if solutions.is_empty() {
println!("false.");
} else {
println!("Solutions:");
engine.print_solutions(&solutions, &variables);
if solutions.len() >= engine.get_stats().max_solutions {
println!("(Maximum solutions reached - there may be more)");
}
}
}
Err(e) => {
println!("Query error: {}", e);
engine.print_boxed_error(&e);
}
}
}
fn extract_query_variables(input: &str) -> Vec<String> {
let mut variables = Vec::new();
let mut chars = input.chars().peekable();
while let Some(ch) = chars.next() {
if ch.is_ascii_uppercase() || ch == '_' {
let mut var_name = String::new();
var_name.push(ch);
while let Some(&next_ch) = chars.peek() {
if next_ch.is_alphanumeric() || next_ch == '_' {
var_name.push(chars.next().unwrap());
} else {
break;
}
}
if !variables.contains(&var_name) && var_name != "_" {
variables.push(var_name);
}
}
}
variables
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_looks_like_query() {
assert!(looks_like_query("parent(tom, X).")); assert!(looks_like_query("X is 2 + 3.")); assert!(looks_like_query("?- parent(tom, bob).")); assert!(looks_like_query("X = Y."));
assert!(!looks_like_query("parent(tom, bob).")); assert!(!looks_like_query("parent(X, Y) :- father(X, Y).")); assert!(!looks_like_query("happy(mary).")); assert!(!looks_like_query("likes(john, pizza).")); }
#[test]
fn test_extract_query_variables() {
let vars = extract_query_variables("parent(tom, X).");
assert_eq!(vars, vec!["X"]);
let vars = extract_query_variables("append(A, B, C).");
assert_eq!(vars, vec!["A", "B", "C"]);
let vars = extract_query_variables("X is Y + Z.");
assert_eq!(vars, vec!["X", "Y", "Z"]);
let vars = extract_query_variables("parent(tom, bob).");
assert!(vars.is_empty());
}
}