use super::structs::*;
use anyhow::{Ok, Result};
use nar_dev_utils::{first, pipe};
use narsese::{
conversion::string::impl_lexical::format_instances::FORMAT_ASCII,
lexical::{Narsese, Sentence, Task},
};
use navm::{cmd::Cmd, output::Operation};
use pest::{iterators::Pair, Parser};
use pest_derive::Parser;
use std::{result::Result::Err as StdErr, result::Result::Ok as StdOk, time::Duration};
#[derive(Parser)] #[grammar = "src/test_tools/nal_format/nal_grammar.pest"]
pub struct NALParser;
pub fn parse(input: &str) -> Vec<Result<NALInput>> {
input
.split('\n')
.map(str::trim)
.filter(|line| !line.is_empty())
.map(parse_single)
.collect::<Vec<_>>()
}
pub fn parse_single(line: &str) -> Result<NALInput> {
pipe! {
line
=> NALParser::parse(Rule::nal_input, _)
=> {?}#
=> .next()
=> .unwrap()
=> fold_pest
}
}
fn fold_pest(pair: Pair<Rule>) -> Result<NALInput> {
let pair_str = pair.as_str();
match pair.as_rule() {
Rule::cyc_uint => {
let n: usize = pair_str.parse()?;
let input = NALInput::Put(Cmd::CYC(n));
Ok(input)
}
Rule::narsese => {
let narsese = pair_str;
let narsese = FORMAT_ASCII.parse(narsese)?.try_into_task_compatible()?;
let input = NALInput::Put(Cmd::NSE(narsese));
Ok(input)
}
Rule::comment_raw => {
let comment = pair_str.into();
let input = NALInput::Put(Cmd::REM { comment });
Ok(input)
}
Rule::comment_navm_cmd => {
let comment_raw = pair.into_inner().next().unwrap();
let line = comment_raw.as_str().trim();
let input = NALInput::Put(Cmd::parse(line)?);
Ok(input)
}
Rule::comment_sleep => {
let duration_raw = pair.into_inner().next().unwrap().as_str().trim();
let duration = parse_duration(duration_raw)?;
let input = NALInput::Sleep(duration);
Ok(input)
}
Rule::comment_await => {
let output_expectation = pair.into_inner().next().unwrap();
let output_expectation = fold_pest_output_expectation(output_expectation)?;
Ok(NALInput::Await(output_expectation))
}
Rule::comment_expect_contains => {
let output_expectation = pair.into_inner().next().unwrap();
let output_expectation = fold_pest_output_expectation(output_expectation)?;
Ok(NALInput::ExpectContains(output_expectation))
}
Rule::comment_save_outputs => {
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 => {
let mut if_not_user = false;
let mut result = StdOk(());
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::comment_terminate_option => {
if inner.as_str() == "if-no-user" {
if_not_user = true;
}
}
Rule::comment_raw => {
result = StdErr(inner.as_str().trim().into())
}
_ => unreachable!("不该被匹配到的规则\tpair = {inner:?}"),
}
}
Ok(NALInput::Terminate {
if_not_user,
result,
})
}
_ => unreachable!("不该被匹配到的规则\tpair = {pair:?}"),
}
}
#[inline]
fn fold_pest_output_expectation(pair: Pair<Rule>) -> Result<OutputExpectation> {
let mut result = OutputExpectation::default();
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::output_type => {
let output_type = inner.as_str().into();
result.output_type = Some(output_type);
}
Rule::narsese => {
let narsese = inner.as_str();
let narsese = FORMAT_ASCII.parse(narsese)?;
result.narsese = Some(narsese);
}
Rule::output_operation => result.operation = Some(fold_pest_output_operation(inner)?),
_ => unreachable!("不该被匹配到的规则\tpair = {inner:?}"),
}
}
Ok(result)
}
#[inline]
fn fold_pest_output_operation(pair: Pair<Rule>) -> Result<Operation> {
let mut pairs = pair.into_inner();
let operator_name = pairs.next().unwrap().as_str().to_owned();
let mut params = vec![];
for inner in pairs {
let term = match FORMAT_ASCII.parse(inner.as_str())? {
Narsese::Term(term)
| Narsese::Sentence(Sentence { term, .. })
| Narsese::Task(Task {
sentence: Sentence { term, .. },
..
}) => term,
};
params.push(term);
}
Ok(Operation {
operator_name,
params,
})
}
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 {
use super::*;
use nar_dev_utils::{for_in_ifs, list};
pub const TESTSET: &str = "\
' 用于测试CIN的「简单演绎推理」
' * 📝利用现有`Narsese`语法
'
' 输出预期
' * 📝统一的NAL测试语法:`''expect-contains: 【输出类别】 【其它内容】`
' * 📄预期「回答」:`''expect-contains: ANSWER 【CommonNarsese】`
' * 📄预期「操作」:`''expect-contains: EXE (^【操作名】, 【操作参数(CommonNarsese词项)】)`
'/VOL 0
<A --> B>.
<B --> C>.
<A --> C>?
5
''sleep: 1s
''expect-contains: ANSWER <A --> C>.
A3. :|:
<(*, {SELF}, (*, P1, P2)) --> ^left>. :|:
G3. :|:
A3. :|:
G3! :|:
''sleep: 500ms
10
''expect-contains: EXE (^left, {SELF}, (*, P1, P2))
''terminate(if-no-user)";
#[test]
fn test_parse() {
_test_parse("<A --> B>.");
_test_parse("5");
_test_parse("'这是一个注释");
_test_parse("'/VOL 0");
_test_parse("'''VOL 0");
_test_parse("''await: OUT <A --> B>.");
_test_parse("''sleep: 500ms");
_test_parse("''sleep: 5000μs");
_test_parse("''sleep: 600ns");
_test_parse("''terminate(if-no-user): 异常的退出消息!");
_test_parse(TESTSET);
}
fn _test_parse(input: &str) {
let results = parse(input);
let results = list![
(r.expect("解析失败!"))
for r in (results)
];
for_in_ifs! {
{println!("{:?}", r);}
for r in (results)
}
}
}