use anyhow::Result;
use narsese::conversion::string::impl_lexical::format_instances::FORMAT_ASCII;
use narust_158::{
inference::{match_task_and_belief, process_direct, reason, transform_task, InferenceEngine},
parameters::DEFAULT_PARAMETERS,
vm::alpha::{LauncherAlpha, SavCallback},
};
use navm::{
cmd::Cmd,
output::Output,
vm::{VmLauncher, VmRuntime},
};
use std::{
io::{stdout, Write},
path::Path,
};
pub fn launcher_void() -> impl VmLauncher {
LauncherAlpha::new("nar_158", DEFAULT_PARAMETERS, InferenceEngine::VOID)
}
pub fn launcher_echo() -> impl VmLauncher {
LauncherAlpha::new("nar_158", DEFAULT_PARAMETERS, InferenceEngine::ECHO)
}
pub fn launcher_dev() -> impl VmLauncher {
const ENGINE: InferenceEngine = InferenceEngine::new(
process_direct,
transform_task,
match_task_and_belief,
reason,
);
LauncherAlpha::new("nar_158", DEFAULT_PARAMETERS, ENGINE)
}
fn create_runtime() -> Result<impl VmRuntime> {
let vm = launcher_dev();
vm.launch()
}
fn shell(
mut runtime: impl VmRuntime,
mut inputs: impl Iterator<Item = Result<Option<String>>>,
) -> Result<()> {
loop {
let input = match inputs.next() {
None => return Ok(()),
Some(Err(e)) => return Err(e),
Some(Ok(None)) => {
eprintln!("Program exited with EOF.");
break Ok(());
}
Some(Ok(Some(input))) => input,
};
let input = input.trim();
if input.is_empty() {
continue;
}
if let Some(cmd) = interpret_cmd(input) {
runtime.input_cmd(cmd)?;
}
while let Some(output) = runtime.try_fetch_output()? {
if let Some(output) = shell_intercept_output(output)? {
shell_print_output(output);
}
}
}
}
fn interpret_cmd(input: &str) -> Option<Cmd> {
if let Ok(n) = input.parse::<usize>() {
return Some(Cmd::CYC(n));
}
if let Ok(cmd) = Cmd::parse(input) {
match cmd {
Cmd::LOA { target, path } => {
let data = match try_load_file_content(path) {
Ok(data) => data,
Err(err) => {
eprintln!("NAVM LOA cmd load error: {err}");
return None;
}
};
return Some(Cmd::LOA { target, path: data });
}
Cmd::Custom { .. } => {}
_ => return Some(cmd),
}
}
if let Ok(Ok(task)) = FORMAT_ASCII
.parse(input)
.map(|value| value.try_into_task_compatible())
{
return Some(Cmd::NSE(task));
}
eprintln!("NAVM cmd parse error: {input:?}");
None
}
fn try_load_file_content(path: impl AsRef<str>) -> anyhow::Result<String> {
let path = path.as_ref();
if Path::new(path).exists() {
let content = std::fs::read_to_string(path)?;
return Ok(content);
}
Err(anyhow::anyhow!("File not found: {path}"))
}
fn shell_intercept_output(output: Output) -> anyhow::Result<Option<Output>> {
let output = match output.try_into_sav_callback() {
Ok((path, data)) if path.is_empty() => Output::format_sav_callback(path, data),
Ok((path, data)) => {
let result = save_file(&path, &data);
let message = match result {
Ok(..) => format!(
"Data has been saved to {path:?} with {} bytes",
data.as_bytes().len()
),
Err(e) => format!("Failed to save data to {path:?}! Error: {e}"),
};
let out = Output::INFO { message };
return Ok(Some(out));
}
Err(output) => output,
};
Ok(Some(output))
}
fn save_file(path: impl Into<String>, data: &str) -> Result<()> {
use std::fs::File;
let mut file = File::create(path.into())?;
file.write_all(data.as_bytes())?;
Ok(())
}
fn shell_print_output(output: Output) {
use Output::*;
match &output {
IN { content, narsese }
| OUT {
content_raw: content,
narsese,
}
| ANSWER {
content_raw: content,
narsese,
}
| ACHIEVED {
content_raw: content,
narsese,
} => match narsese {
Some(narsese) => {
println!("[{}] {}", output.get_type(), FORMAT_ASCII.format(narsese))
}
None => println!("[{}] {}", output.get_type(), content),
},
ERROR {
description: content,
}
| INFO { message: content }
| COMMENT { content }
| TERMINATED {
description: content,
}
| OTHER { content } => println!("[{}] {}", output.get_type(), content),
EXE {
content_raw,
operation,
} => println!("[{}] {} by '{}'", output.get_type(), operation, content_raw),
output @ UNCLASSIFIED { .. } => {
println!("{}", output.to_json_string());
stdout().flush().unwrap();
}
}
}
pub fn shell_iter_inputs(
inputs: impl IntoIterator<Item = String>,
) -> impl Iterator<Item = Result<Option<String>>> {
inputs.into_iter().map(|content| Ok(Some(content)))
}
pub fn shell_iter_stdin() -> impl Iterator<Item = Result<Option<String>>> {
let mut buffer = String::new();
let stdin = std::io::stdin();
std::iter::from_fn(move || {
let bytes = match stdin.read_line(&mut buffer) {
Ok(b) => b,
Err(e) => return Some(Err(e.into())),
};
if bytes == 0 {
return Some(Ok(None));
}
let input = buffer.clone();
buffer.clear();
Some(Ok(Some(input)))
})
}
pub fn set_max_volume(vm: &mut impl VmRuntime) -> Result<()> {
vm.input_cmd(Cmd::VOL(100))?;
vm.try_fetch_output()?; Ok(())
}
pub fn main() -> Result<()> {
let runtime = create_runtime()?;
shell(runtime, shell_iter_stdin())?;
Ok(())
}