Skip to content

Commit

Permalink
feat: ✨ 对NAL格式新增「循环等待预期」功能
Browse files Browse the repository at this point in the history
增强自动化测试功能:加入「循环等待预期」魔法注释,允许「每批循环一定步数、等待并检查预期,超过指定步数未有『符合预期之输出』⇒报错」的灵活测试逻辑
  • Loading branch information
ARCJ137442 committed Apr 19, 2024
1 parent 6bb800e commit 2ce701f
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 93 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "babel_nar"
version = "0.21.2"
version = "0.22.0"
edition = "2021"
description = """
Implementation and application supports of the NAVM model
Expand Down
61 changes: 32 additions & 29 deletions src/bin/babelnar_cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub fn main_args(cwd: IoResult<PathBuf>, args: impl Iterator<Item = String>) ->

/// 单元测试
#[cfg(test)]
#[allow(unused_attributes)]
mod tests {
use super::*;
use babel_nar::tests::config_paths::*;
Expand Down Expand Up @@ -158,6 +159,7 @@ mod tests {
/// * 🎯正常用户命令行交互体验
$(#[$attr_root])*
#[test]
#[ignore = "仅作试运行用,不用于自动化测试"]
pub fn main_shell() -> Result<()> {
main($cin_path, &[])
}
Expand All @@ -167,6 +169,7 @@ mod tests {
/// * 🎯复现先前基于Matriangle环境的NARS实验
$(#[$attr_root])*
#[test]
#[ignore = "仅作试运行用,不用于自动化测试"]
pub fn main_matriangle_server() -> Result<()> {
// 以默认参数启动
main_configs($cin_path, &[MATRIANGLE_SERVER])
Expand All @@ -190,27 +193,27 @@ mod tests {
ONA;

/// 简单演绎
/// * 📝✅【2024-04-19 13:28:21】成功
/// * 📝✅【2024-04-19 22:49:51】成功
nal_de => NAL_SIMPLE_DEDUCTION

/// 高阶演绎
/// * 📝✅【2024-04-07 14:56:04】成功
/// * 📝✅【2024-04-19 22:50:04】成功
nal_hi => NAL_HIGHER_DEDUCTION

/// 自变量消除
/// * 📝✅【2024-04-07 16:03:47】成功
/// * 📝✅【2024-04-19 22:50:53】成功
nal_ie => NAL_I_VAR_ELIMINATION

/// 时间归纳
/// * 📝✅【2024-04-07 15:22:28】成功
/// * 📝✅【2024-04-19 22:52:45】成功
nal_te => NAL_TEMPORAL_INDUCTION

/// 简单操作
/// * 📝❌【2024-04-07 16:15:53】失败:推理不出任何内容
/// * 📝❌【2024-04-19 22:55:35】失败:推理不出任何内容
nal_so => NAL_SIMPLE_OPERATION

/// 操作
/// * 📝✅【2024-04-07 14:57:50】成功,但少许问题
/// * 📝✅【2024-04-19 23:01:52】成功,但少许问题
/// * 📝【2024-04-07 14:17:21】目前ONA面对其中的「经验问句」没有回答
/// * ⚠️在启用`REG left`注册操作后,反而从成功变为失败
nal_op => NAL_OPERATION
Expand All @@ -226,19 +229,19 @@ mod tests {
OPENNARS;

/// 简单演绎
/// * 📝✅【2024-04-07 14:59:37】成功
/// * 📝✅【2024-04-19 22:49:02】成功(步数性能上不佳)
nal_de => NAL_SIMPLE_DEDUCTION

/// 高阶演绎
/// * 📝✅【2024-04-07 14:59:44】成功
/// * 📝✅【2024-04-19 22:48:56】成功
nal_hi => NAL_HIGHER_DEDUCTION

/// 自变量消除
/// * 📝✅【2024-04-07 16:01:15】成功
nal_ie => NAL_I_VAR_ELIMINATION

/// 时间归纳
/// * 📝✅【2024-04-07 15:22:28】成功
/// * 📝✅【2024-04-19 22:52:35】成功(步数性能上不佳)
nal_te => NAL_TEMPORAL_INDUCTION

/// 简单操作
Expand All @@ -260,27 +263,27 @@ mod tests {
OPENNARS_158;

/// 简单演绎
/// * 📝✅【2024-04-19 13:28:27】成功
/// * 📝✅【2024-04-19 23:02:59】成功
nal_de => NAL_SIMPLE_DEDUCTION

/// 高阶演绎
/// * 📝✅【2024-04-19 13:39:16】成功
/// * 📝✅【2024-04-19 23:03:06】成功
nal_hi => NAL_HIGHER_DEDUCTION

/// 自变量消除
/// * 📝✅【2024-04-19 13:39:23】成功
/// * 📝✅【2024-04-19 23:03:15】成功
nal_ie => NAL_I_VAR_ELIMINATION

/// 时间归纳
/// * 📝❌【2024-04-19 13:30:25】失败:语法层面就不支持
/// * 📝❌【2024-04-19 23:03:20】失败:语法层面就不支持
nal_te => NAL_TEMPORAL_INDUCTION

/// 简单操作
/// * 📝❌【2024-04-19 13:30:25】失败:没有任何输出
/// * 📝❌【2024-04-19 23:03:37】失败:语法层面就不支持
nal_so => NAL_SIMPLE_OPERATION

/// 操作
/// * 📝❌【2024-04-19 13:30:25】失败:语法层面就不支持
/// * 📝❌【2024-04-19 23:03:48】失败:语法层面就不支持
nal_op => NAL_OPERATION
}
}
Expand All @@ -293,27 +296,28 @@ mod tests {
PYNARS;

/// 简单演绎
/// * 📝✅【2024-04-07 17:11:22】成功
/// * 📝✅【2024-04-19 23:04:24】成功
nal_de => NAL_SIMPLE_DEDUCTION

/// 高阶演绎
/// * 📝✅【2024-04-07 17:11:36】成功
/// * 📝✅【2024-04-19 23:04:33】成功
nal_hi => NAL_HIGHER_DEDUCTION

/// 自变量消除
/// * 📝❌【2024-04-07 16:01:15】失败:啥推理都没有
/// * 📝❌【2024-04-19 23:05:32】失败:啥推理都没有
nal_ie => NAL_I_VAR_ELIMINATION

/// 时间归纳
/// * 📝❌【2024-04-07 16:13:52】失败:只会回答`<C-->D>. :\: %1.000;0.900%`
/// * 📝❌【2024-04-19 23:06:43】失败:只会回答`<C-->D>. :\: %1.000;0.900%`
nal_te => NAL_TEMPORAL_INDUCTION

/// 简单操作
/// * 📝❌【2024-04-07 16:13:42】失败:没有任何回答
/// * 📝❌【2024-04-19 23:06:48】失败:没有任何回答
nal_so => NAL_SIMPLE_OPERATION

/// 操作
/// * 📝❌【2024-04-07 14:39:49】目前仍测试失败
/// * 📝❌【2024-04-19 23:07:11】目前仍测试失败
/// * 📄【2024-04-19 23:07:27】只会回答`ANSWER:<{SELF}-->(/, ^left, _)>. :\: %1.000;0.900%`
/// * 📌PyNARS自身对NAL-7、NAL-8支持尚不完善
/// * 📌PyNARS中操作`left`并非默认已注册
/// * ❌【2024-04-07 14:41:54】补充:追加了也不行
Expand All @@ -322,6 +326,7 @@ mod tests {
}

/// 测试/CXinJS
/// * 📝【2024-04-19 23:10:28】用来试探「自动测试脚本」的下限
mod cxin_js {
use super::*;

Expand All @@ -330,31 +335,29 @@ mod tests {

/// 简单演绎
/// * 📝❌【2024-04-07 14:37:49】失败:导出了结论,但没法回答
/// * 📄只能导出`<B-->C>. %1;0.9%`
nal_de => NAL_SIMPLE_DEDUCTION

/// 高阶演绎
/// * 📝❌【2024-04-07 14:37:49】失败:只能导出到`<A-->B>?`
/// * 📝❌【2024-04-19 23:08:44】失败:只能导出到`<A-->B>?`
/// * 📌即便是五百步,也推不出来
nal_hi => NAL_HIGHER_DEDUCTION

/// 自变量消除
/// * 📝❌【2024-04-07 16:01:15】失败:仅推理到`<A-->C>?`,并且遇到「XXX is not a function」错误
/// * 📝❌【2024-04-19 23:09:21】失败:仅推理到`<A-->C>?`,并且遇到「XXX is not a function」错误
nal_ie => NAL_I_VAR_ELIMINATION

/// 时间归纳
/// * 📝❌失败:解析即报错——不支持`=/>`
/// * 📝❌【2024-04-19 23:09:34】失败:解析即报错——不支持`=/>`
nal_te => NAL_TEMPORAL_INDUCTION

/// 简单操作
/// * 📝❌【2024-04-07 16:16:24】失败:推理不出任何内容
/// * 📝❌【2024-04-19 23:09:47】失败:推理不出任何内容
/// * 💭还会把「目标」解析成「判断」……
nal_so => NAL_SIMPLE_OPERATION

/// 操作
/// * 📝❌目前仍测试失败
/// * 📌PyNARS自身对NAL-7、NAL-8支持尚不完善
/// * 📌PyNARS中操作`left`并非默认已注册
/// * 📝❌【2024-04-07 14:37:49】失败:自身就不支持
/// * 📝❌【2024-04-19 23:10:21】失败:自身就不支持
nal_op => NAL_OPERATION
}
}
Expand Down
66 changes: 56 additions & 10 deletions src/test_tools/nal_format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,7 @@ fn fold_pest(pair: Pair<Rule>) -> Result<NALInput> {
// 取其中第一个`comment_raw`元素 | 一定只有唯一一个`comment_raw`
let duration_raw = pair.into_inner().next().unwrap().as_str().trim();
// 尝试解析时间
let duration = first! {
// 毫秒→微秒→纳秒→秒 | 对于「秒」分「整数」「浮点」两种
duration_raw.ends_with("ms") => Duration::from_millis(duration_raw.strip_suffix("ms").unwrap().parse()?),
duration_raw.ends_with("μs") => Duration::from_micros(duration_raw.strip_suffix("μs").unwrap().parse()?),
duration_raw.ends_with("ns") => Duration::from_nanos(duration_raw.strip_suffix("ns").unwrap().parse()?),
duration_raw.ends_with('s') && duration_raw.contains('.') => Duration::try_from_secs_f64(duration_raw.strip_suffix('s').unwrap().parse()?)?,
duration_raw.ends_with('s') => Duration::from_secs(duration_raw.strip_suffix('s').unwrap().parse()?),
// 否则报错
_ => return Err(anyhow::anyhow!("未知的睡眠时间参数 {duration_raw:?}"))
};
let duration = parse_duration(duration_raw)?;
// * 封装
let input = NALInput::Sleep(duration);
Ok(input)
Expand All @@ -138,6 +129,48 @@ fn fold_pest(pair: Pair<Rule>) -> Result<NALInput> {
let file_path = pair.into_inner().next().unwrap().as_str().into();
Ok(NALInput::SaveOutputs(file_path))
}
// 魔法注释/循环预期
Rule::comment_expect_cycle => {
let mut pairs = pair.into_inner();
// 取其中的「最大步数」
let max_cycles = pipe! {
pairs.next().unwrap()
=> .as_str()
=> {.parse::<usize>()}#
=> {?}#
};
// 取其中的「每次步长」
let step_cycles = pipe! {
pairs.next().unwrap()
=> .as_str()
=> {.parse::<usize>()}#
=> {?}#
};
// 取其中的「输出预期」
let step_duration = pairs.next();
let step_duration = match step_duration {
Some(step_duration) => {
// 尝试解析时间
let step_duration = parse_duration(step_duration.as_str())?;
// 封装
Some(step_duration)
}
None => None,
};
// 取其中的「输出预期」
let output_expectation = pipe! {
pairs.next().unwrap()
=> fold_pest_output_expectation
=> {?}#
};
// 构造 & 返回
Ok(NALInput::ExpectCycle(
max_cycles,
step_cycles,
step_duration,
output_expectation,
))
}
// 魔法注释/终止
Rule::comment_terminate => {
// 预置默认值
Expand Down Expand Up @@ -250,6 +283,19 @@ fn fold_pest_output_operation(pair: Pair<Rule>) -> Result<Operation> {
})
}

fn parse_duration(duration_raw: &str) -> Result<Duration> {
Ok(first! {
// 毫秒→微秒→纳秒→秒 | 对于「秒」分「整数」「浮点」两种
duration_raw.ends_with("ms") => Duration::from_millis(duration_raw.strip_suffix("ms").unwrap().parse()?),
duration_raw.ends_with("μs") => Duration::from_micros(duration_raw.strip_suffix("μs").unwrap().parse()?),
duration_raw.ends_with("ns") => Duration::from_nanos(duration_raw.strip_suffix("ns").unwrap().parse()?),
duration_raw.ends_with('s') && duration_raw.contains('.') => Duration::try_from_secs_f64(duration_raw.strip_suffix('s').unwrap().parse()?)?,
duration_raw.ends_with('s') => Duration::from_secs(duration_raw.strip_suffix('s').unwrap().parse()?),
// 否则报错
_ => return Err(anyhow::anyhow!("未知的睡眠时间参数 {duration_raw:?}"))
})
}

/// 单元测试
#[cfg(test)]
pub mod tests {
Expand Down
16 changes: 15 additions & 1 deletion src/test_tools/nal_format/nal_grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ cyc_uint = { ASCII_DIGIT+ }
/// 注释(静默)
/// * 🚩包括「输出预期」等「魔法注释」
comment = _{
comment_head ~ (comment_navm_cmd | comment_sleep | comment_await | comment_expect_contains | comment_save_outputs | comment_terminate | comment_raw)
comment_head ~ (comment_navm_cmd | comment_sleep | comment_await | comment_expect_contains | comment_save_outputs | comment_expect_cycle | comment_terminate | comment_raw)
}

/// 注释的头部字符(静默)
Expand Down Expand Up @@ -76,6 +76,20 @@ comment_save_outputs = {
"'save-outputs:" ~ output_expectation
}

/// 有关「循环等待预期」的「魔法注释」
/// ✨阻塞主线程,循环指定周期,并在其中检查预期;
/// * 每步进「步长」个周期后,检查NAVM输出预期,有⇒终止,打印输出`expect-cycle(【次数】): 【输出】`
/// * 检查后,若存在时间,则等待指定时间
/// * 若循环后仍无,上报「预期不符」
comment_expect_cycle = {
// 额外的前缀
"'expect-cycle" ~ "(" ~ cyc_uint ~ "," ~ cyc_uint ~ ("," ~ comment_expect_cycle_step_time)? ~ "):" ~ output_expectation
}

/// 「循环等待预期」中的「每步后等待时间」
/// * 🎯解决「输入CIN后,CIN输出需要时间,来不及反应」的问题
comment_expect_cycle_step_time = { (!")" ~ !"," ~ ANY)* }

/// 有关「终止」的「魔法注释」
/// ✨终止NAVM虚拟机
/// * 📄参数:选项、理由
Expand Down
9 changes: 9 additions & 0 deletions src/test_tools/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ pub enum NALInput {
/// * 📄对应OpenNARS中常有的`''outputMustContain('')`
ExpectContains(OutputExpectation),

/// 对「输出含有」的循环预期
/// * 📄语法示例:`''expect-cycle(500, 10, 0.1s): ANSWER <A --> C>.`
/// * 🎯用于「在『最大步数』的限定下循环尝试获取『期望的输出』,未获得预期输出⇒预期失败」
/// * 🚩循环指定周期(最大步数),并在其中检查预期;
/// * 每步进1周期后,检查NAVM输出预期,有⇒终止,打印输出`expect-cycle(【次数】): 【输出】`
/// * 若循环后仍无,视作「预期不符」
/// * 📄在「最大步数=0」的情形之下,`expect-cycle(0)`等价于[`expect-contains`](NALInput::ExpectContains)
ExpectCycle(usize, usize, Option<Duration>, OutputExpectation),

/// 保存「输出缓存」到指定文件
/// * 📄语法示例:`''save-outputs: outputs.log`
/// * 🎯用于「将现有所有输出以『NAVM输出的JSON格式』存档至指定文件中」
Expand Down
33 changes: 31 additions & 2 deletions src/test_tools/vm_interact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
use std::{ops::ControlFlow, path::Path};

use crate::cli_support::error_handling_boost::error_anyhow;
use crate::cli_support::{error_handling_boost::error_anyhow, io::output_print::OutputType};

use super::{NALInput, OutputExpectation, OutputExpectationError};
use anyhow::Result;
use nar_dev_utils::{if_return, ResultBoost};
use navm::{output::Output, vm::VmRuntime};
use navm::{cmd::Cmd, output::Output, vm::VmRuntime};

/// * 🎯统一存放与「Narsese预期识别」有关的代码
/// * 🚩【2024-04-02 22:49:12】从[`crate::runtimes::command_vm::runtime::tests`]中迁移而来
Expand Down Expand Up @@ -266,6 +266,35 @@ pub fn put_nal(
// }
// }
}
// 检查在指定的「最大步数」内,是否有NAVM输出符合预期(弹性步数`0~最大步数`)
NALInput::ExpectCycle(max_cycles, step_cycles, step_duration, expectation) => {
let mut cycles = 0;
while cycles < max_cycles {
// 推理步进
vm.input_cmd(Cmd::CYC(step_cycles))?;
cycles += step_cycles;
// 等待指定时长
if let Some(duration) = step_duration {
std::thread::sleep(duration);
}
// 先尝试拉取所有输出到「输出缓存」
while let Some(output) = vm.try_fetch_output()? {
output_cache.put(output)?;
}
// 然后读取并匹配缓存
let result = output_cache.for_each(|output| match expectation.matches(output) {
true => ControlFlow::Break(true),
false => ControlFlow::Continue(()),
})?;
// 匹配到一个⇒提前返回Ok
if let Some(true) = result {
OutputType::Info.print_line(&format!("expect-cycle({cycles}): {expectation}"));
return Ok(());
}
}
// 步进完所有步数,仍未有匹配⇒返回Err
Err(OutputExpectationError::ExpectedNotExists(expectation).into())
}
// 保存(所有)输出
// * 🚩输出到一个文本文件中
// * ✨复合JSON「对象数组」格式
Expand Down
Loading

0 comments on commit 2ce701f

Please sign in to comment.