use crate::{
control::Reasoner,
inference::{match_task_and_belief, process_direct, reason, transform_task, InferenceEngine},
language::Term,
ok,
parameters::{Parameters, DEFAULT_PARAMETERS},
util::AResult,
};
use nar_dev_utils::{list, unwrap_or_return};
use narsese::{api::GetTerm, conversion::string::impl_lexical::format_instances::FORMAT_ASCII};
use navm::{cmd::Cmd, output::Output};
pub fn expect_output_eq_term(output: &Output, expected: &Term) -> bool {
let lexical_term = unwrap_or_return!(
?output.get_narsese().map(GetTerm::get_term).cloned()
=> false );
let lexical_str = FORMAT_ASCII.format(&lexical_term);
let out = unwrap_or_return!(
@Term::from_lexical(lexical_term),
e => {
eprintln!("要与预期相比对的词项 {lexical_str:?} 解析失败:{e}");
true
}
);
out == *expected
}
pub fn expect_output_eq_term_lexical(output: &Output, lexical: narsese::lexical::Term) -> bool {
let expected = Term::from_lexical(lexical).expect("要预期的词法不正确");
expect_output_eq_term(output, &expected)
}
#[macro_export]
macro_rules! expect_narsese_term {
($type:ident $term:literal in outputs) => {
move |output|
matches!(output, navm::output::Output::$type {..}) && $crate::inference::tests::tools::expect_output_eq_term_lexical(
&output, narsese::lexical_nse_term!(@PARSE $term)
)
};
}
pub fn create_reasoner(parameters: Parameters, engine: InferenceEngine) -> Reasoner {
Reasoner::new("test", parameters, engine)
}
pub fn set_max_volume(reasoner: &mut Reasoner) {
reasoner.set_volume(100);
}
pub fn create_reasoner_from_engine(engine: InferenceEngine) -> Reasoner {
let mut reasoner = create_reasoner(DEFAULT_PARAMETERS, engine);
set_max_volume(&mut reasoner);
reasoner
}
impl Reasoner {
pub(crate) fn input_cmd(&mut self, cmd: Cmd) -> AResult<()> {
use Cmd::*;
match cmd {
NSE(task) => self.input_task(task),
CYC(steps) => self.cycle(steps),
VOL(volume) => self.set_volume(volume),
RES { .. } => self.reset(),
REM { .. } => (),
INF { source } if source == "summary" => self.report_info(self.report_summary()),
INF { .. } => (),
_ => return Err(anyhow::anyhow!("不支持的NAVM指令:{cmd}")),
}
ok!()
}
pub(crate) fn input_cmds(&mut self, cmds: &str) {
for cmd in cmds
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.map(|line| Cmd::parse(line).expect("NAVM指令{line}解析失败"))
{
let cmd_s = cmd.to_string();
self.input_cmd(cmd)
.unwrap_or_else(|_| panic!("NAVM指令「{cmd_s}」输入失败"));
}
}
pub(crate) fn input_cmds_soft(&mut self, cmds: &str) {
for cmd in cmds
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.filter_map(|line| Cmd::parse(line).ok())
{
let cmd_s = cmd.to_string();
self.input_cmd(cmd)
.unwrap_or_else(|_| eprintln!("【警告】NAVM指令「{cmd_s}」输入失败"));
}
}
pub(crate) fn fetch_outputs(&mut self) -> Vec<Output> {
list![
output
while let Some(output) = (self.take_output())
]
}
#[must_use]
pub(crate) fn input_cmds_and_fetch_out(&mut self, cmds: &str) -> Vec<Output> {
self.input_cmds(cmds);
self.fetch_outputs()
}
pub(crate) fn input_fetch_print_expect(
&mut self,
cmds: &str,
expect: impl Fn(&Output) -> bool,
) -> Vec<Output> {
self.input_cmds(cmds);
let outs = self.fetch_outputs();
print_outputs(&outs);
expect_outputs(&outs, expect);
outs
}
}
pub fn print_outputs<'a>(outs: impl IntoIterator<Item = &'a Output>) {
outs.into_iter().for_each(|output| {
println!(
"[{}]{}\nas narsese {:?}\n",
output.type_name(),
output.get_content(),
output.get_narsese()
)
})
}
pub fn expect_outputs<'a>(
outputs: impl IntoIterator<Item = &'a Output>,
expect: impl Fn(&Output) -> bool,
) -> &'a Output {
outputs
.into_iter()
.find(|&output| expect(output))
.expect("没有找到期望的输出")
}
pub fn expect_outputs_contains_term<'a>(
outputs: impl IntoIterator<Item = &'a Output>,
expected: impl Into<narsese::lexical::Term>,
) -> &'a Output {
let expected = Term::from_lexical(expected.into()).expect("要预期的词法不正确");
outputs
.into_iter()
.find(|&output| expect_output_eq_term(output, &expected))
.unwrap_or_else(|| panic!("没有找到期望的输出「{expected}」"))
}
pub const ENGINE_DEV: InferenceEngine = InferenceEngine::new(
process_direct,
transform_task,
match_task_and_belief,
reason,
);
pub fn expectation_test(inputs: impl AsRef<str>, expectation: impl Fn(&Output) -> bool) {
let mut vm = create_reasoner_from_engine(ENGINE_DEV);
vm.input_fetch_print_expect(
inputs.as_ref(),
expectation,
);
}
#[macro_export]
macro_rules! expectation_test {
(
$(#[$attr:meta])*
$name:ident :
$inputs:expr
=> $($expectations:tt)*
) => {
$(#[$attr])*
#[test]
fn $name() {
$crate::inference::tests::tools::expectation_test(
$inputs,
$crate::expect_narsese_term!($($expectations)*),
)
}
};
}
#[macro_export]
macro_rules! expectation_tests {
(
$(
$(#[$attr:meta])*
$name:ident : {
$inputs:expr
=> $($expectations:tt)*
}
)*
) => {
$(
$crate::expectation_test! {
$(#[$attr])*
$name :
$inputs
=> $($expectations)*
}
)*
};
}