diff --git a/Cargo.lock b/Cargo.lock index 2aa28c7..20a7791 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,7 +74,7 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "babel_nar" -version = "0.24.0" +version = "0.24.1" dependencies = [ "anyhow", "clap", diff --git a/Cargo.toml b/Cargo.toml index 75e78f6..78a4e93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "babel_nar" -version = "0.24.0" +version = "0.24.1" edition = "2021" description = """ Implementation and application supports of the NAVM model diff --git a/src/test_tools/vm_interact/mod.rs b/src/test_tools/vm_interact/mod.rs index ff07cd5..ca6fcd8 100644 --- a/src/test_tools/vm_interact/mod.rs +++ b/src/test_tools/vm_interact/mod.rs @@ -7,9 +7,11 @@ use nar_dev_utils::{if_return, ResultBoost}; use navm::{cmd::Cmd, output::Output, vm::VmRuntime}; use std::{ops::ControlFlow, path::Path}; +// Narsese预期 mod narsese_expectation; pub use narsese_expectation::*; +// 词项判等 mod term_equal; /// 实现/预期匹配功能 diff --git a/src/test_tools/vm_interact/narsese_expectation.rs b/src/test_tools/vm_interact/narsese_expectation.rs index 86007f4..fb790b9 100644 --- a/src/test_tools/vm_interact/narsese_expectation.rs +++ b/src/test_tools/vm_interact/narsese_expectation.rs @@ -1,22 +1,23 @@ //! * 🎯统一存放与「Narsese预期识别」有关的代码 //! * 🚩【2024-04-02 22:49:12】从[`crate::runtimes::command_vm::runtime::tests`]中迁移而来 +use super::term_equal::*; +use anyhow::Result; use nar_dev_utils::if_return; use narsese::{ - api::{GetBudget, GetPunctuation, GetStamp, GetTruth, NarseseValue}, + api::NarseseValue, conversion::{ inter_type::lexical_fold::TryFoldInto, - string::impl_enum::format_instances::FORMAT_ASCII as FORMAT_ASCII_ENUM, + string::impl_enum::{format_instances::FORMAT_ASCII as FORMAT_ASCII_ENUM, NarseseFormat}, }, enum_narsese::{ - Budget as EnumBudget, Narsese as EnumNarsese, Sentence as EnumSentence, Task as EnumTask, + Budget as EnumBudget, Punctuation as EnumPunctuation, Stamp as EnumStamp, Truth as EnumTruth, }, lexical::{Narsese, Sentence as LexicalSentence, Task as LexicalTask, Term}, }; use navm::output::Operation; - -use super::term_equal::*; +use util::macro_once; /// 判断「输出是否(在Narsese语义层面)符合预期」 /// * 🎯词法Narsese⇒枚举Narsese,以便从语义上判断 @@ -41,80 +42,139 @@ fn _is_expected_narsese(mut expected: Narsese, mut out: Narsese) -> bool { if_return! { !semantical_equal_mut(get_term_mut(&mut expected), get_term_mut(&mut out)) => false }; - // 临时折叠预期 - let expected = - (expected.try_fold_into(&FORMAT_ASCII_ENUM)).expect("作为预期的词法Narsese无法折叠!"); - // 与预期一致 - let out = out.try_fold_into(&FORMAT_ASCII_ENUM); // 必须复制:折叠消耗自身 - match out { - Ok(out) => is_expected_enum_residual(&expected, &out), - Err(..) => false, + // * 🚩折叠剩余部分,并开始判断 + let fold = PartialFoldResult::try_from; + match (fold(expected), fold(out)) { + // * 🚩若均解析成功⇒进一步判等 + (Ok(expected), Ok(out)) => out.is_expected_out(&expected), + // * 🚩任一解析失败⇒直接失败 + _ => false, } } -/// 判断「输出是否(在Narsese层面)符合预期」 -/// * 🎯预期词项⇒只比较词项,语句⇒只比较语句,…… +/// 临时的「部分折叠结果」 +/// * 📌用于非词项判等 +/// * 🎯性能提升:避免重复折叠词项 +#[derive(Debug, Clone, Default)] +struct PartialFoldResult { + truth: Option, + stamp: Option, + budget: Option, + punctuation: Option, +} + +/// ! 判等即「预期判断」 +/// * 🎯判断「输出是否(在Narsese层面)符合预期」 /// * 🚩【2024-06-11 16:02:10】目前对「词项比对」使用特殊逻辑,而对其它结构照常比较 -/// * ❓TODO: 【2024-06-11 21:22:15】是否需要避免重复折叠 -fn is_expected_enum_residual(expected: &EnumNarsese, out: &EnumNarsese) -> bool { - use NarseseValue::*; - match ((expected), (out)) { - // 词项⇒只比较词项 - // ! 🚩【2024-06-11 16:05:45】现在直接在词法层面判等,能运行至此都是已经词项相等的(枚举Narsese的集合相对难以统一) - (Term(_term), ..) => true, /* is_expected_term(term, out.get_term()) */ - // 语句⇒只比较语句 - // ! 仍然不能直接判等:真值/预算值 - (Sentence(s_exp), Sentence(s_out) | Task(EnumTask(s_out, ..))) => { - is_expected_sentence(s_exp, s_out) +/// * ✅均已经考虑「没有值可判断」的情况 +impl PartialFoldResult { + fn is_expected_out(&self, out: &Self) -> bool { + macro_once! { + /// 一系列针对Option解包的条件判断: + /// * 🚩均为Some⇒展开内部代码逻辑 + /// * 🚩均为None⇒直接返回true + /// * 🚩其它情况⇒直接返回false + macro both_and { + ($( { $($code:tt)* } ) && *) => { + $( + both_and!(@SINGLE $($code)*) + )&&* + }; + (@SINGLE $l_i:ident @ $l:expr, $r_i:ident @ $r:expr => $($code:tt)*) => { + match ($l.as_ref(), $r.as_ref()) { + (Some($l_i), Some($r_i)) => { + $($code)* + }, + (None, None) => true, + _ => false, + } + }; + } + // * 🚩开始判等逻辑 + { + // 标点一致 + expected @ self.punctuation, + out @ out.punctuation => + expected == out // * 🚩简单枚举类型:直接判等 + } && { + // 时间戳一致 + expected @ self.stamp, + out @ out.stamp => + expected == out // * 🚩简单枚举类型:直接判等 + } && { + // 真值一致 + expected @ self.truth, + out @ out.truth => + is_expected_truth(expected, out) // * 🚩特殊情况(需兼容)特殊处理 + } && { + // 预算值一致 + expected @ self.budget, + out @ out.budget => + is_expected_budget(expected, out) // * 🚩特殊情况(需兼容)特殊处理 + } } - // 任务⇒直接判断 - // ! 仍然不能直接判等:真值/预算值 - (Task(t_exp), Task(t_out)) => is_expected_task(t_exp, t_out), - // 所有其它情况⇒都是假 - (..) => false, } } -/// 判断输出的任务是否与预期任务相同 -/// * 🎯用于细粒度判断「预算值」「语句」的预期 -pub fn is_expected_task(expected: &EnumTask, out: &EnumTask) -> bool { - // 预算 - is_expected_budget(expected.get_budget(), out.get_budget()) - // 语句 - && is_expected_sentence(expected.get_sentence(), out.get_sentence()) -} - -/// 判断输出的语句是否与预期语句相同 -/// * 🎯用于细粒度判断「真值」的预期 -pub fn is_expected_sentence(expected: &EnumSentence, out: &EnumSentence) -> bool { - // 词项 | ✅已经在词法层面判等 - // (is_expected_term(expected.get_term(),out.get_term())) && - // 标点相等 - expected.get_punctuation() == out.get_punctuation() - // 时间戳相等 - && expected.get_stamp()== out.get_stamp() - // 真值兼容 | 需要考虑「没有真值可判断」的情况 - && match (expected.get_truth(),out.get_truth()) { - // 都有⇒判断「真值是否符合预期」 - (Some(t_e), Some(t_o)) => is_expected_truth(t_e, t_o), - // 都没⇒肯定真 - (None, None) => true, - // 有一个没有⇒肯定假 - _ => false, +impl TryFrom for PartialFoldResult { + type Error = (); + /// 从「词法Narsese」中折叠 + /// * 🚩折叠除词项以外的其它字段 + /// * 🚩【2024-06-12 01:54:13】转换失败⇒判等失败⇒返回false「不符预期」 + /// + fn try_from(narsese: Narsese) -> Result { + // * 🚩缩减代码长度的常量 + const FORMAT: &NarseseFormat<&str> = &FORMAT_ASCII_ENUM; + /// * 🚩工具宏:封装「尝试做,不行就抛Err」的逻辑 + macro_rules! some_try { + ($v:expr) => { + Some(match $v { + Ok(v) => v, + Err(..) => return Err(()), + }) + }; } + // * 🚩批量匹配折叠 + let value = match narsese { + // * 🚩词项⇒全空 + NarseseValue::Term(..) => Self::default(), + // * 🚩语句⇒真值、时间戳、标点 + NarseseValue::Sentence(LexicalSentence { + punctuation, + stamp, + truth, + .. + }) => Self { + truth: some_try!(truth.try_fold_into(FORMAT)), + stamp: some_try!(FORMAT.parse(&stamp)), + budget: None, + punctuation: some_try!(FORMAT.parse(&punctuation)), + }, + // * 🚩任务⇒语句+预算值 + NarseseValue::Task(LexicalTask { + budget, + sentence: + LexicalSentence { + punctuation, + stamp, + truth, + .. + }, + }) => Self { + truth: some_try!(truth.try_fold_into(FORMAT)), + stamp: some_try!(FORMAT.parse(&stamp)), + budget: some_try!(budget.try_fold_into(FORMAT)), + punctuation: some_try!(FORMAT.parse(&punctuation)), + }, + }; + Ok(value) + } } -// ! 🚩【2024-06-11 16:03:50】现在直接在词法层面判等Narsese词项 -// /// 判断输出的词项是否与预期词项相同 -// /// * 🎯用于独立出「词项预期」功能 -// /// * 🚩【2024-04-02 22:55:13】目前直接判等 -// pub fn is_expected_term(expected: &EnumTerm, out: &EnumTerm) -> bool { -// // expected == out -// } - /// 判断「输出是否在真值层面符合预期」 /// * 🎯空真值的语句,应该符合「固定真值的语句」的预期——相当于「通配符」 -pub fn is_expected_truth(expected: &EnumTruth, out: &EnumTruth) -> bool { +#[inline] +fn is_expected_truth(expected: &EnumTruth, out: &EnumTruth) -> bool { match (expected, out) { // 预期空真值⇒通配 (EnumTruth::Empty, ..) => true, @@ -129,7 +189,8 @@ pub fn is_expected_truth(expected: &EnumTruth, out: &EnumTruth) -> bool { /// 判断「输出是否在预算值层面符合预期」 /// * 🎯空预算的语句,应该符合「固定预算值的语句」的预期——相当于「通配符」 -pub fn is_expected_budget(expected: &EnumBudget, out: &EnumBudget) -> bool { +#[inline] +fn is_expected_budget(expected: &EnumBudget, out: &EnumBudget) -> bool { match (expected, out) { // 预期空预算⇒通配 (EnumBudget::Empty, ..) => true,