From a24c12be1fee48c1f0c1bf8ece1ab42d071ace8c Mon Sep 17 00:00:00 2001 From: ARCJ137442 <61109168+ARCJ137442@users.noreply.github.com> Date: Tue, 26 Mar 2024 02:07:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20:sparkles:=20=E5=A4=A7=E5=B9=85?= =?UTF-8?q?=E6=8E=A8=E8=BF=9BCIN=E8=BD=AC=E8=AF=91=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=EF=BC=8C=E9=83=A8=E5=88=86=E8=A7=A3=E5=86=B3=E3=80=8C=E6=96=B9?= =?UTF-8?q?=E8=A8=80=E9=97=AE=E9=A2=98=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🏗️OpenNARS:基本实现除「特定方言语法」外的Narsese解析 - 顺带一并更新实用库,新增并应用「管道宏」`pipe!` 🏗️ONA:同OpenNARS ✅NARS-Python:基本实现输入转译,但未有成功截获到输出 - 为此引入可选依赖`lazy_static` --- .vscode/settings.json | 4 +- Cargo.lock | 52 +++++- Cargo.toml | 13 +- README.md | 5 +- src/cin_implements/nars_python/dialect.rs | 36 ++++ src/cin_implements/nars_python/mod.rs | 55 +++++- src/cin_implements/nars_python/translators.rs | 17 +- src/cin_implements/ona/translators.rs | 164 ++++++++++++++++-- src/cin_implements/opennars/translators.rs | 121 ++++++++++--- src/runtime/command_vm/runtime.rs | 2 +- 10 files changed, 411 insertions(+), 58 deletions(-) create mode 100644 src/cin_implements/nars_python/dialect.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 2262744..9cc6258 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,9 @@ "editor.formatOnSave": true, "cSpell.words": [ "Errno", + "rfind", "runpy", - "traceback" + "traceback", + "tstate" ] } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 9e6fa75..d64b2b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,24 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "babel_nar" -version = "0.5.0" +version = "0.6.0" dependencies = [ + "lazy_static", "nar_dev_utils", "narsese", "navm", + "regex", ] [[package]] @@ -17,9 +28,15 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + [[package]] name = "nar_dev_utils" -version = "0.17.0" +version = "0.19.0" [[package]] name = "narsese" @@ -31,8 +48,37 @@ dependencies = [ [[package]] name = "navm" -version = "0.2.0" +version = "0.3.0" dependencies = [ "nar_dev_utils", "narsese", ] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" diff --git a/Cargo.toml b/Cargo.toml index ebe83a1..1dcc00e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,19 @@ [package] name = "babel_nar" -version = "0.5.0" +version = "0.6.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +regex = "1.10.4" + +# 用于实现「静态含闭包常量」 +# * 🎯初次引入:NARS-Python 方言格式 +# * 🔗:https://stackoverflow.com/questions/73260997/rust-boxed-closure-in-global-variable +[dependencies.lazy_static] +version = "1.4.0" +optional = true [dependencies.nar_dev_utils] # 【2024-03-13 21:17:55】实用库现在独立为`nar_dev_utils` @@ -38,7 +46,8 @@ features = [] # ! 【2024-03-21 09:24:51】暂时没有特性 default = [] # 大杂烩 bundled = [ - "cin_implements" + "cin_implements", + "lazy_static" # 这个「词法Narsese」也在用 ] # 各个独立的特性 # # 具体接口实现: diff --git a/README.md b/README.md index 92e1a06..8e8850d 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,14 @@ ## 各CIN对接情况 -🕒最后更新时间:【2024-03-25 14:10:36】 +🕒最后更新时间:【2024-03-26 01:43:28】 | CIN | 实现方法 | 进程安全 | 输入转译 | 输出转译 | | :---------- | :---------: | :--: | :--: | :--: | | OpenNARS | `java -jar` | ✅ | ✅ | 🚧 | | ONA | 直接启动exe | ✅ | ✅ | 🚧 | | PyNARS | `python -m` | ✅ | 🚧 | 🚧 | -| NARS-Python | 直接启动exe | ❓ | 🚧 | 🚧 | +| NARS-Python | 直接启动exe | ❓ | ✅ | ❌ | | OpenJunars | `julia` | ✅ | ❌ | ❌ | 注: @@ -39,6 +39,7 @@ - 🚧输入输出转译功能仍然在从[BabelNAR_Implements](https://github.com/ARCJ137442/BabelNAR_Implements.jl)迁移 - ❓NARS-Python的exe界面可能会在终止后延时关闭 - ❌基于`julia`启动OpenJunars脚本`launch.jl`时,对「输出捕获」尚未有成功记录 +- ❌目前对NARS-Python的「输出捕获」尚未有成功记录 ## 参考 diff --git a/src/cin_implements/nars_python/dialect.rs b/src/cin_implements/nars_python/dialect.rs new file mode 100644 index 0000000..7150131 --- /dev/null +++ b/src/cin_implements/nars_python/dialect.rs @@ -0,0 +1,36 @@ +//! 用于存储NARS-Python的方言格式 +//! * 🚩【2024-03-26 01:31:44】本质上就是陈述括弧改变了而已 + +use narsese::conversion::string::impl_lexical::{ + format_instances::create_format_ascii, NarseseFormat, +}; +use narsese::lexical::Narsese; + +#[cfg(feature = "lazy_static")] +lazy_static::lazy_static! { + /// NARS-Python的方言格式 + /// * 🚩仅在`lazy_static`启用时开启 + pub static ref FORMAT: NarseseFormat = create_format_nars_python(); +} + +pub fn create_format_nars_python() -> NarseseFormat { + let mut f = create_format_ascii(); + f.statement.brackets = ("(".into(), ")".into()); + f +} + +/// 获取NARS-Python的方言格式 +/// * 🚩使用`lazy_static`定义的静态常量,无需重复初始化 +/// * 🚩否则总是创建一个新的「Narsese格式」 +#[cfg(feature = "lazy_static")] +pub fn format_in_nars_python(narsese: &Narsese) -> String { + FORMAT.format_narsese(narsese) +} + +/// 获取NARS-Python的方言格式 +/// * 🚩否则总是创建一个新的「Narsese格式」 +#[cfg(not(feature = "lazy_static"))] +pub fn format_in_nars_python(narsese: &Narsese) -> String { + // 创建格式,并立即格式化Narsese + create_format_nars_python().format_narsese(narsese) +} diff --git a/src/cin_implements/nars_python/mod.rs b/src/cin_implements/nars_python/mod.rs index 9eee0cd..b572e96 100644 --- a/src/cin_implements/nars_python/mod.rs +++ b/src/cin_implements/nars_python/mod.rs @@ -4,6 +4,8 @@ // 转译器 util::mod_and_pub_use! { + // 方言(Narsese格式) + dialect // 转译器 translators // 启动器 @@ -15,7 +17,11 @@ util::mod_and_pub_use! { mod tests { use super::*; use crate::runtime::{test::EXE_PATH_NARS_PYTHON, CommandVmRuntime}; - use navm::vm::{VmLauncher, VmRuntime}; + use narsese::conversion::string::impl_lexical::shortcuts::*; + use navm::{ + cmd::Cmd, + vm::{VmLauncher, VmRuntime}, + }; #[test] fn test() { @@ -28,13 +34,54 @@ mod tests { } /// 测试/NARS-Python - pub(crate) fn _test_nars_python(vm: CommandVmRuntime) { - // TODO: 实际的测试代码 + /// * ❌【2024-03-26 01:42:14】目前还没法真正截取到输出 + pub(crate) fn _test_nars_python(mut vm: CommandVmRuntime) { + // 等待几秒钟,让exe的界面显示出来 + std::thread::sleep(std::time::Duration::from_secs(2)); + + vm.input_cmd(Cmd::NSE(nse_task!( B>.))) + .expect("无法输入NAVM指令"); + vm.input_cmd(Cmd::NSE(nse_task!( C>.))) + .expect("无法输入NAVM指令"); + vm.input_cmd(Cmd::NSE(nse_task!( C>?))) + .expect("无法输入NAVM指令"); - // 等待四秒钟,让exe的界面显示出来 std::thread::sleep(std::time::Duration::from_secs(4)); // 终止虚拟机运行时 vm.terminate().expect("无法终止虚拟机"); } + + /* // ! 【2024-03-26 01:44:27】NARS-Python输出崩溃的内容: + running 1 test + Started process: 65784 + Traceback (most recent call last): + File "main.py", line 122, in + File "main.py", line 118, in main + File "NARS.py", line 54, in run + File "NARS.py", line 63, in do_working_cycle + File "InputChannel.py", line 74, in process_pending_sentence + File "InputChannel.py", line 87, in process_sentence + File "NARS.py", line 247, in process_task + File "NARS.py", line 323, in process_question_task + File "NARS.py", line 491, in process_sentence_semantic_inference + File "NARSInferenceEngine.py", line 73, in do_semantic_inference_two_premise + AttributeError: 'NoneType' object has no attribute 'frequency' + [38676] Failed to execute script 'main' due to unhandled exception! + Fatal Python error: could not acquire lock for <_io.BufferedReader name=''> at interpreter shutdown, possibly due to daemon threads + Python runtime state: finalizing (tstate=00000213FB525D60) + + Thread 0x00017e0c (most recent call first): + File "InputChannel.py", line 25 in get_user_input + File "threading.py", line 870 in run + File "threading.py", line 932 in _bootstrap_inner + File "threading.py", line 890 in _bootstrap + + Current thread 0x00013918 (most recent call first): + + 成功: 已终止 PID 为 65784 的进程。 + test cin_implements::nars_python::tests::test ... ok + + test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 10 filtered out; finished in 6.56s + */ } diff --git a/src/cin_implements/nars_python/translators.rs b/src/cin_implements/nars_python/translators.rs index fc475d8..ed1065c 100644 --- a/src/cin_implements/nars_python/translators.rs +++ b/src/cin_implements/nars_python/translators.rs @@ -3,21 +3,30 @@ //! * 📌基于命令行输入输出的字符串读写 //! * ✨NAVM指令→字符串 //! * ✨字符串→NAVM输出 +//! +//! ## 输出样例 +//! +//! * `EXE: ^left based on desirability: 0.9` +//! * `PROCESSED GOAL: SentenceID:2081:ID ({SELF} --> [SAFE])! :|: %1.00;0.03%from SentenceID:2079:ID ({SELF} --> [SAFE])! :|: %1.00;0.00%,SentenceID:2080:ID ({SELF} --> [SAFE])! :|: %1.00;0.02%,` +//! * `PREMISE IS TRUE: ((*,{SELF}) --> ^right)` +//! * `PREMISE IS SIMPLIFIED ({SELF} --> [SAFE]) FROM (&|,({SELF} --> [SAFE]),((*,{SELF}) --> ^right))` +use narsese::lexical::Narsese; use navm::{ cmd::Cmd, output::{Operation, Output}, }; use util::ResultS; +use super::format_in_nars_python; + /// NARS-Python的「输入转译」函数 /// * 🎯用于将统一的「NAVM指令」转译为「NARS-Python输入」 -/// -/// TODO: ⚠️其有一种不同的语法,需要细致解析 pub fn input_translate(cmd: Cmd) -> ResultS { let content = match cmd { - // 直接使用「末尾」,此时将自动格式化任务(可兼容「空预算」的形式) - Cmd::NSE(..) => cmd.tail(), + // 使用「末尾」将自动格式化任务(可兼容「空预算」的形式) + // * ✅【2024-03-26 01:44:49】目前采用特定的「方言格式」解决格式化问题 + Cmd::NSE(narsese) => format_in_nars_python(&Narsese::Task(narsese)), // CYC指令:运行指定周期数 // ! NARS-Python Shell同样是自动步进的 Cmd::CYC(n) => n.to_string(), diff --git a/src/cin_implements/ona/translators.rs b/src/cin_implements/ona/translators.rs index bcb81e6..f222f49 100644 --- a/src/cin_implements/ona/translators.rs +++ b/src/cin_implements/ona/translators.rs @@ -3,12 +3,28 @@ //! * 📌基于命令行输入输出的字符串读写 //! * ✨NAVM指令→字符串 //! * ✨字符串→NAVM输出 +//! +//! ## 输出样例 +//! +//! * `Input: <<(* x) --> ^left> ==> A>. Priority=1.000000 Truth: frequency=1.000000, confidence=0.900000` +//! * `Derived: <<(* x) --> ^left> ==> good>>. Priority=0.245189 Truth: frequency=1.000000, confidence=0.810000` +//! * `Answer: C>. creationTime=2 Truth: frequency=1.000000, confidence=0.447514` +//! * `Answer: None.` +//! * `^deactivate executed with args` +//! * `^left executed with args (* {SELF})` +//! * `^left executed with args ({SELF} * x)` +//! * `decision expectation=0.616961 implication: <((<{SELF} --> [left_blocked]> &/ ^say) &/ <(* {SELF}) --> ^left>) =/> <{SELF} --> [SAFE]>>. Truth: frequency=0.978072 confidence=0.394669 dt=1.000000 precondition: <{SELF} --> [left_blocked]>. :|: Truth: frequency=1.000000 confidence=0.900000 occurrenceTime=50` +use narsese::{ + conversion::string::impl_lexical::{format_instances::FORMAT_ASCII, structs::ParseResult}, + lexical::Narsese, +}; use navm::{ cmd::Cmd, output::{Operation, Output}, }; -use util::ResultS; +use regex::Regex; +use util::{pipe, ResultBoost, ResultS}; /// ONA的「输入转译」函数 /// * 🎯用于将统一的「NAVM指令」转译为「ONA Shell输入」 @@ -32,32 +48,146 @@ pub fn input_translate(cmd: Cmd) -> ResultS { /// ONA的「输出转译」函数 /// * 🎯用于将ONA Shell的输出(字符串)转译为「NAVM输出」 /// * 🚩直接根据选取的「头部」进行匹配 -pub fn output_translate(content: String) -> ResultS { +pub fn output_translate(content_raw: String) -> ResultS { // 根据冒号分隔一次,然后得到「头部」 - let head = content.split_once(':').unwrap_or(("", "")).0.to_lowercase(); + let (head, tail) = content_raw.split_once(':').unwrap_or(("", "")); // 根据「头部」生成输出 - let output = match &*head { + let output = match head.to_lowercase().as_str() { "answer" => Output::ANSWER { - content_raw: content, - // TODO: 有待捕获转译 - narsese: None, + // 先提取其中的Narsese | ⚠️借用了`content_raw` + // * 🚩ONA会输出带有误导性的`Answer: None.` + // * 看起来是回答,实际上不是 + narsese: match content_raw.contains("Answer: None.") { + true => None, + false => try_parse_narsese(tail) + .ok_or_run(|e| println!("【ERR/{head}】在解析Narsese时出现错误:{e}")), + }, + // 然后传入整个内容 + content_raw, }, "derived" => Output::OUT { - content_raw: content, - // TODO: 有待捕获转译 - narsese: None, + // 先提取其中的Narsese | ⚠️借用了`content_raw` + narsese: try_parse_narsese(tail) + .ok_or_run(|e| println!("【ERR/{head}】在解析Narsese时出现错误:{e}")), + // 然后传入整个内容 + content_raw, }, - "input" => Output::IN { content }, - "exe" => Output::EXE { - content_raw: content, - // TODO: 有待捕获转译 - operation: Operation::new("UNKNOWN", [].into_iter()), + "input" => Output::IN { + content: content_raw, }, "err" | "error" => Output::ERROR { - description: content, + description: content_raw, + }, + // * 🚩对于「操作」的特殊语法 + _ if content_raw.contains("executed") => Output::EXE { + operation: parse_operation_ona(&content_raw), + content_raw, + }, + // 若是连续的「头部」⇒识别为「未归类」类型 + _ if !content_raw.contains(char::is_whitespace) => Output::UNCLASSIFIED { + r#type: head.into(), + content: content_raw, + }, + // 其它 + _ => Output::OTHER { + content: content_raw, }, - _ => Output::OTHER { content }, }; // 返回 Ok(output) } + +/// (ONA)从原始输出中解析操作 +pub fn parse_operation_ona(content_raw: &str) -> Operation { + println!("截获到操作:{content_raw:?}"); + Operation { + // TODO: 有待分析 + head: "UNKNOWN".into(), + params: vec![content_raw.into()], + } +} + +/// (尝试)从输出中解析出Narsese +pub fn try_parse_narsese(tail: &str) -> ResultS { + // 提取并解析Narsese字符串 + pipe! { + tail + // 重整 + => #{&} + => reform_output_to_narsese + // 解析 + => #{&} + => parse_narsese_ona + // 转换错误 | 解析失败⇒返回错误信息 | 返回None + => .transform_err(|err| format!("输出「OUT」解析失败:{err}")) + } +} + +/// 重整ONA输出到合法Narsese +/// * 🎯通过「重整→正确解析」的方式,实现初步输出解析兼容 +/// * 🚩【2024-03-25 21:38:39】目前使用正则表达式[`regex`]库 +/// * 🚩【2024-03-25 21:38:52】目前仅基于正则表达式做文本替换 +/// * 📌参数`tail`不附带`Answer:`等部分 +fn reform_output_to_narsese(out: &str) -> String { + // 构造正则表达式(实现中只会编译一次) // + // 匹配ONA输出中的「真值」 + let re_truth = Regex::new(r"Truth:\s*frequency=([0-9.]+),\s*confidence=([0-9.]+)").unwrap(); + // 匹配ONA输出的「创建时间」 + let re_creation_t = Regex::new(r"creationTime=([0-9.]+)\s+").unwrap(); + + // 两次替换 // + pipe! { + out + // 重建真值表达式 + => [re_truth.replace_all](_, |caps: ®ex::Captures<'_>| { + // * 第`0`个是正则表达式匹配的整个内容 + let f = &caps[1]; + let c = &caps[2]; + // 重建CommonNarsese合法的真值 + format!("%{f};{c}%") + }) + => #{&} + // 删去非必要的「创建时间」 + => [re_creation_t.replace_all](_, |_: ®ex::Captures<'_>| "") + // 返回字符串 // + => .into() + } +} + +/// 以OpenNARS的语法解析出Narsese +/// * 🚩【2024-03-25 21:08:34】目前是直接调用ASCII解析器 +/// +/// TODO: 兼容ONA的方言语法 +/// * 📌重点在「用空格分隔乘积词项/中缀情形」的语法 +/// * 📄`(* {SELF})` +/// * 📄`({SELF} * x)` +fn parse_narsese_ona(target: &str) -> ParseResult { + FORMAT_ASCII.parse(target) +} + +/// 单元测试 +#[cfg(test)] +mod test { + use super::*; + use util::asserts; + + /// 测试/正则重整 + #[test] + fn test_regex_reform() { + let inp = " C>. creationTime=2 Truth: frequency=1.000000, confidence=0.447514"; + let s = pipe! { + inp + => reform_output_to_narsese + => .chars() + => .into_iter() + => .filter(|c|!c.is_whitespace()) + // => .collect::() // ! ❌暂时不支持「完全限定语法」 + } + .collect::(); + + // 断言 + asserts! { + s => "C>.%1.000000;0.447514%", + } + } +} diff --git a/src/cin_implements/opennars/translators.rs b/src/cin_implements/opennars/translators.rs index 8019b0a..2231c1d 100644 --- a/src/cin_implements/opennars/translators.rs +++ b/src/cin_implements/opennars/translators.rs @@ -4,12 +4,27 @@ //! * 📌基于命令行输入输出的字符串读写 //! * ✨NAVM指令→字符串 //! * ✨字符串→NAVM输出 +//! +//! ## 输出样例 +//! +//! * `IN: B>. %1.00;0.90% {-1 : (-7995324758518856376,0)}` +//! * `OUT: B>. %1.00;0.90% {-1 : (-7995324758518856376,0)}` +//! * `Answer: C>. %1.00;0.81% {1584885193 : (-7995324758518856376,0);(-7995324758518856376,1)}` +//! * `EXE: $1.00;0.99;1.00$ ^left([{SELF}])=null` +//! * `ANTICIPATE: <{SELF} --> [SAFE]>` +//! * `CONFIRM: <{SELF} --> [SAFE]><{SELF} --> [SAFE]>` +//! * `DISAPPOINT: <{SELF} --> [SAFE]>` +//! * `Executed based on: $0.2904;0.1184;0.7653$ <(&/,<{SELF} --> [right_blocked]>,+7,(^left,{SELF}),+55) =/> <{SELF} --> [SAFE]>>. %1.00;0.53%` +use narsese::{ + conversion::string::impl_lexical::{format_instances::FORMAT_ASCII, structs::ParseResult}, + lexical::Narsese, +}; use navm::{ cmd::Cmd, output::{Operation, Output}, }; -use util::ResultS; +use util::{ResultBoost, ResultS}; /// OpenNARS的「输入转译」函数 /// * 🎯用于将统一的「NAVM指令」转译为「OpenNARS Shell输入」 @@ -33,37 +48,95 @@ pub fn input_translate(cmd: Cmd) -> ResultS { /// OpenNARS的「输出转译」函数 /// * 🎯用于将OpenNARS Shell的输出(字符串)转译为「NAVM输出」 /// * 🚩直接根据选取的「头部」进行匹配 -pub fn output_translate(content: String) -> ResultS { +pub fn output_translate(content_raw: String) -> ResultS { // 根据冒号分隔一次,然后得到「头部」 - let head = content.split_once(':').unwrap_or(("", "")).0.to_lowercase(); + let (head, tail) = content_raw.split_once(':').unwrap_or(("", &content_raw)); // 根据「头部」生成输出 - let output = match &*head { - "answer" => Output::ANSWER { - content_raw: content, - // TODO: 有待捕获转译 - narsese: None, + let output = match &*head.to_uppercase() { + "IN" => Output::IN { + content: content_raw, + }, + "OUT" => { + // 返回 + Output::OUT { + // 先提取其中的Narsese | ⚠️借用了`content_raw` + narsese: strip_parse_narsese(tail) + .ok_or_run(|e| println!("【ERR/{head}】在解析Narsese时出现错误:{e}")), + // 然后传入整个内容 + content_raw, + } + } + "ANSWER" => Output::ANSWER { + // 先提取其中的Narsese | ⚠️借用了`content_raw` + narsese: strip_parse_narsese(tail) + .ok_or_run(|e| println!("【ERR/{head}】在解析Narsese时出现错误:{e}")), + // 然后传入整个内容 + content_raw, + }, + "EXE" => Output::EXE { + operation: parse_operation_opennars(&content_raw), + content_raw, }, - "out" => Output::OUT { - content_raw: content, - // TODO: 有待捕获转译 - narsese: None, + "ANTICIPATE" => Output::ANTICIPATE { + // 先提取其中的Narsese | ⚠️借用了`content_raw` + narsese: strip_parse_narsese(tail) + .ok_or_run(|e| println!("【ERR/{head}】在解析Narsese时出现错误:{e}")), + // 然后传入整个内容 + content_raw, }, - "in" => Output::IN { content }, - "anticipate" => Output::ANTICIPATE { - content_raw: content, - // TODO: 有待捕获转译 - narsese: None, + "ERR" | "ERROR" => Output::ERROR { + description: content_raw, }, - "exe" => Output::EXE { - content_raw: content, - // TODO: 有待捕获转译 - operation: Operation::new("UNKNOWN", [].into_iter()), + // * 🚩利用OpenNARS常见输出「全大写」的特征,兼容「confirm」与「disappoint」 + upper if head == upper => Output::UNCLASSIFIED { + r#type: head.to_string(), + content: content_raw, }, - "err" | "error" => Output::ERROR { - description: content, + // 其它 + _ => Output::OTHER { + content: content_raw, }, - _ => Output::OTHER { content }, }; // 返回 Ok(output) } + +/// 在OpenNARS输出中解析出「NARS操作」 +/// +/// TODO: 结合正则表达式进行解析 +pub fn parse_operation_opennars(content_raw: &str) -> Operation { + // use regex::Regex; + Operation { + // TODO: 有待捕获转译 + head: "UNKNOWN".into(), + params: vec![content_raw.into()], + } +} + +/// 切分尾部字符串,并(尝试)从中解析出Narsese +fn strip_parse_narsese(tail: &str) -> ResultS { + // 提取并解析Narsese字符串 + let narsese = tail + // 去尾 + .rfind('{') + // 截取 & 解析 + .map(|right_index| parse_narsese_opennars(&tail[..right_index])); + // 提取解析结果 + match narsese { + // 解析成功⇒提取 & 返回 + Some(Ok(narsese)) => Ok(narsese), + // 解析失败⇒打印错误日志 | 返回None + Some(Err(err)) => Err(format!("输出「OUT」解析失败:{err}")), + // 未找到括号的情况 + None => Err("输出「OUT」解析失败:未找到「{」".into()), + } +} + +/// 以OpenNARS的语法解析出Narsese +/// * 🚩【2024-03-25 21:08:34】目前是直接调用ASCII解析器 +/// +/// TODO: 兼容OpenNARS特有之语法 +/// * 📌重点在其简写的「操作」语法`(^left, {SELF}, x)` => `<(*, {SELF}, x) --> ^left>` +fn parse_narsese_opennars(input: &str) -> ParseResult { + FORMAT_ASCII.parse(input) +} diff --git a/src/runtime/command_vm/runtime.rs b/src/runtime/command_vm/runtime.rs index aca5810..79bc4a8 100644 --- a/src/runtime/command_vm/runtime.rs +++ b/src/runtime/command_vm/runtime.rs @@ -118,7 +118,7 @@ pub(crate) mod test { // 展示输出 match &output { // 特别显示「回答」 - Output::ANSWER { content_raw, .. } => println!("捕获到回答!内容:{content_raw}"), + Output::ANSWER { .. } => println!("捕获到回答!内容:{output:?}"), _ => println!("捕获到其它输出!内容:{output:?}"), } // 包含⇒结束