use rust_lstar::knowledge_base::{KnowledgeBaseStats, KnowledgeBaseTrait};
use rust_lstar::query::OutputQuery;
use rust_lstar::*;
use std::sync::{Arc, Mutex};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Custom Knowledge Base Example ===");
println!("Simulating a banking ATM protocol\n");
let kb = Arc::new(Mutex::new(ATMKnowledgeBase::new()));
let vocabulary = vec![
"INSERT_CARD".to_string(),
"ENTER_PIN".to_string(),
"REQUEST_WITHDRAW".to_string(),
"EJECT_CARD".to_string(),
"TIMEOUT".to_string(),
];
let mut lstar = LSTAR::new(vocabulary, kb.clone(), 8, None, None);
match lstar.learn() {
Ok(automata) => {
println!("\n=== Learned ATM Protocol ===");
println!("{}", automata.build_dot_code());
println!("\nATM protocol learned successfully!");
}
Err(e) => eprintln!("Error: {}", e),
}
let kb_guard = kb.lock().unwrap();
println!("\nKnowledge Base Stats:\n{}", kb_guard.stats);
Ok(())
}
struct ATMKnowledgeBase {
state: ATMState,
stats: KnowledgeBaseStats,
}
#[derive(Clone, Copy)]
enum ATMState {
Idle,
CardInserted,
Authenticated,
Ready,
Dispensing,
}
impl ATMKnowledgeBase {
fn new() -> Self {
ATMKnowledgeBase {
state: ATMState::Idle,
stats: KnowledgeBaseStats::new(),
}
}
fn process_input(&self, command: &str, current_state: ATMState) -> (ATMState, &'static str) {
match current_state {
ATMState::Idle => match command {
"INSERT_CARD" => (ATMState::CardInserted, "CARD_ACCEPTED"), _ => (ATMState::Idle, "INVALID_OP"), },
ATMState::CardInserted => match command {
"ENTER_PIN" => (ATMState::Authenticated, "PIN_VERIFIED"), "EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"), _ => (ATMState::CardInserted, "RETRY"), },
ATMState::Authenticated => match command {
"REQUEST_WITHDRAW" => (ATMState::Ready, "ENTER_AMOUNT"), "EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"), "TIMEOUT" => (ATMState::Idle, "SESSION_TIMEOUT"), _ => (ATMState::Authenticated, "INVALID_COMMAND"), },
ATMState::Ready => match command {
"REQUEST_WITHDRAW" => (ATMState::Dispensing, "DISPENSING"), "EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"), _ => (ATMState::Ready, "WAIT"), },
ATMState::Dispensing => match command {
"EJECT_CARD" => (ATMState::Idle, "CARD_EJECTED"), _ => (ATMState::Dispensing, "DISPENSING"), },
}
}
}
impl KnowledgeBaseTrait for ATMKnowledgeBase {
fn resolve_query(&mut self, query: &mut OutputQuery) -> Result<(), String> {
self.stats.increment_nb_query();
self.stats.add_nb_letter(query.input_word.len());
self.stats.increment_nb_submitted_query();
self.stats.add_nb_submitted_letter(query.input_word.len());
self.state = ATMState::Idle;
let mut outputs = Vec::new();
for input_letter in query.input_word.letters() {
let symbol = input_letter.symbols();
let command = symbol.as_str();
let (next_state, response) = self.process_input(command, self.state);
self.state = next_state;
outputs.push(Letter::new(response));
}
query.set_result(Word::from_letters(outputs));
Ok(())
}
fn add_word(&mut self, _input: &Word, _output: &Word) -> Result<(), String> {
Ok(())
}
}