use crate::runtimes::TranslateError;
use anyhow::{anyhow, Result};
use nar_dev_utils::{pipe, JoinTo};
use narsese::{
api::ExtractTerms,
conversion::string::{
impl_enum::format_instances::FORMAT_ASCII as FORMAT_ASCII_ENUM,
impl_lexical::format_instances::FORMAT_ASCII,
},
lexical::{Narsese, Term},
};
use navm::{
cmd::Cmd,
output::{Operation, Output},
};
use regex::{Captures, Regex};
pub fn input_translate(cmd: Cmd) -> Result<String> {
let content = match cmd {
Cmd::NSE(..) => cmd.tail(),
Cmd::CYC(n) => n.to_string(),
Cmd::VOL(n) => format!("/volume {n}"),
Cmd::REG { name, .. } => format!("/register {name}"),
Cmd::REM { .. } => String::new(),
_ => return Err(TranslateError::UnsupportedInput(cmd).into()),
};
Ok(content)
}
pub fn preprocess(s: &str) -> String {
let re = Regex::new(r"\u{1b}\[[0-9;]*m").unwrap();
pipe! {
s
=> [re.replace_all](_, "")
=> .trim()
=> .to_string()
}
}
fn try_get_output_type(preprocessed: &str) -> Option<String> {
let re2 = Regex::new(r"[0-9\s|]*(\w+)\s*:").unwrap();
pipe! {
preprocessed
=> [re2.captures](_)
=> .map(|captures|captures[1].into())
}
}
fn try_get_narsese(preprocessed: &str) -> Result<Narsese> {
let re_trim_and_budget =
Regex::new(r"^[^0-9.]*([0-9.]+)[\s|]+([0-9.]+)[\s|]+([0-9.]+)[\s|]+\w+\s*:\s*").unwrap();
let trimmed = re_trim_and_budget
.replace(preprocessed, |s: &Captures| {
let mut budget = FORMAT_ASCII_ENUM.task.budget_brackets.0.to_string();
let mut s = s.iter();
s.next();
s.flatten()
.map(|c| c.as_str())
.join_to(&mut budget, FORMAT_ASCII_ENUM.task.budget_separator);
budget + FORMAT_ASCII_ENUM.task.budget_brackets.1
})
.to_string();
let parsed_narsese = FORMAT_ASCII.parse(&trimmed)?;
Ok(parsed_narsese)
}
#[cfg(not(test))]
fn get_narsese(preprocessed: &str) -> Result<Option<Narsese>> {
use nar_dev_utils::ResultBoost;
Ok(try_get_narsese(preprocessed).ok_or_run(|e| println!("尝试解析Narsese错误:{e}")))
}
#[cfg(test)]
fn get_narsese(preprocessed: &str) -> Result<Option<Narsese>> {
Ok(Some(try_get_narsese(preprocessed)?))
}
fn try_get_operation(preprocessed: &str) -> Result<Operation> {
let re_operation = Regex::new(r"EXE\s*:\s*(.+) = ").unwrap();
let op = re_operation
.captures(preprocessed)
.unwrap()
.get(1)
.unwrap()
.as_str();
let op = FORMAT_ASCII.parse(op).unwrap().try_into_term().unwrap();
match op {
Term::Statement {
subject, predicate, ..
} => {
let params = subject.extract_terms_to_vec();
let operator_name = match *predicate {
Term::Atom { name, .. } => name,
_ => return Err(anyhow!("陈述谓词不是原子词项")),
};
Ok(Operation {
operator_name,
params,
})
}
_ => Err(anyhow::anyhow!("无效的「操作表示」词项:{op:?}")),
}
}
#[cfg(not(test))]
fn get_operation(preprocessed: &str) -> Operation {
try_get_operation(preprocessed).unwrap_or_else(|e| {
println!("尝试从「{preprocessed}」解析Narsese操作错误:{e}");
Operation {
operator_name: "".into(),
params: vec![],
}
})
}
#[cfg(test)]
fn get_operation(preprocessed: &str) -> Operation {
try_get_operation(preprocessed)
.unwrap_or_else(|e| panic!("无法从「{preprocessed}」解析出Narsese操作:{e}"))
}
pub fn output_translate(content: String) -> Result<Output> {
let content = preprocess(&content);
let head = pipe! {
&content
=> try_get_output_type
=> .map(|s|s.to_lowercase())
};
let head = match &head {
Some(s) => s,
None => "",
};
let output = match head {
"answer" => Output::ANSWER {
narsese: get_narsese(&content)?,
content_raw: content,
},
"achieved" => Output::ACHIEVED {
narsese: get_narsese(&content)?,
content_raw: content,
},
"out" => Output::OUT {
narsese: get_narsese(&content)?,
content_raw: content,
},
"input" | "in" => Output::IN {
narsese: get_narsese(&content)?,
content,
},
"info" => Output::INFO { message: content },
"exe" => Output::EXE {
operation: get_operation(&content),
content_raw: content,
},
"err" | "error" => Output::ERROR {
description: content,
},
_ => Output::OTHER { content },
};
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_try_get_output() {
test("\u{1b}[48;2;110;10;10m 0.78 \u{1b}[49m\u{1b}[48;2;10;41;10m 0.25 \u{1b}[49m\u{1b}[48;2;10;10;125m 0.90 \u{1b}[49m\u{1b}[33mOUT :\u{1b}[39m<A-->C>. %1.000;0.810%\r\n");
test("|0.80|0.50|0.95| IN : A. %1.000;0.900%");
test("\u{1b}[90mInput: \u{1b}[39m\u{1b}[48;2;124;10;10m 0.90 \u{1b}[49m\u{1b}[48;2;10;124;10m 0.90 \u{1b}[49m\u{1b}[48;2;10;10;137m 1.00 \u{1b}[49m\u{1b}[36mIN :\u{1b}[39m<A-->C>?\r\n");
test("0.98 0.90 0.90 ANSWER:<A-->C>. %1.000;0.810%");
fn test(inp: &str) {
let preprocessed = preprocess(inp);
let _ = " 0.78 0.25 0.90 OUT :<A-->C>. %1.000;0.810%\r\n";
dbg!(&preprocessed);
let t = try_get_output_type(&preprocessed);
dbg!(&t);
dbg!(try_get_narsese(&preprocessed).expect("Narsese解析失败!"));
}
}
#[test]
fn test_try_get_operation() {
test(" \u{1b}[49m \u{1b}[49m \u{1b}[49m\u{1b}[32mEXE :\u{1b}[39m<(*, 0)-->^op> = $0.022;0.232;0.926$ <(*, 0)-->^op>! :\\: %1.000;0.853% {7: 2, 0, 1}\r\n");
test(" \u{1b}[49m \u{1b}[49m \u{1b}[49m\u{1b}[32mEXE :\u{1b}[39m<(*, 0, 1, 2, 3)-->^op> = $0.000;0.339;0.950$ <(*, 0, 1, 2, 3)-->^op>! %1.000;0.853% {None: 7, 4, 5}\r\n");
fn test(inp: &str) {
let inp = preprocess(inp);
let op = try_get_operation(&inp).unwrap();
dbg!(op);
}
}
}