//! 「统一`.nal`格式」语法
//! * 🎯从常见的`.nal`文件中解析出「NAVM测试语句」
//! * 🎯在「ASCII CommonNarsese」之下,附加注释与「测试预期」语法
//! * 📝原则:凡是无法对应到「解析结果」(此处是NALInput结构)的,都给「静默」unwrap
/// 空白符 | 所有非换行的Unicode空白符,解析前忽略
/// * 🚩【2024-03-31 23:40:41】现在将「行分割」交给Rust预处理
/// * 📌此处对「换行符」的特殊处理,基本不会用到
WHITESPACE = _{ !"\n" ~ WHITE_SPACE }
/// 多个`.nal`输入
/// * 🚩【2024-03-31 23:41:33】目前不启用,将「行分割」交给Rust预处理
nal_inputs = !{
"\n"* ~ nal_input ~ ("\n"+ ~ nal_input)*
}
/// 入口/单个`.nal`输入(静默展开)
/// * 🚩以数字代替`CYC`指令,并兼容原`.nal`语法
/// * 🚩以直接的Narsese代替`NSE`指令,并兼容原`.nal`语法
/// * 🚩在注释中扩展新语法
nal_input = _{
(cyc_uint | comment | narsese)
}
/// 直接对应CYC指令的用法
/// * ✅`CYC`的语法糖
cyc_uint = { ASCII_DIGIT+ }
/// 注释(静默)
/// * 🚩包括「输出预期」等「魔法注释」
comment = _{
comment_head ~ (comment_navm_cmd | comment_sleep | comment_await | comment_expect_contains | comment_save_outputs | comment_expect_cycle | comment_terminate | comment_raw)
}
/// 注释的头部字符(静默)
comment_head = _{ "'" }
/// 有关「置入命令」的「魔法注释」
/// * ✨允许构建并向NAVM置入指令
/// * 📄用`'/VOL 0`代替非通用的`*volume=0`
/// * 📄亦可直接用三个`'`:`'''VOL 0`
/// * 🚩【2024-04-02 23:44:06】现在使用`$`要求「特殊前缀」紧挨内容,避免误认「被注释掉的注释」为指令
/// * ✅使用正谓词`&LETTER`要求必须是`/文字`而非其它(如`//`)
comment_navm_cmd = ${
// 特殊前缀`/`或`''`
("/" | "''") ~ &LETTER ~ comment_raw
}
/// 有关「睡眠等待」的「魔法注释」
/// * ✨允许构建并向NAVM置入指令
/// * 📄用`'/VOL 0`代替非通用的`*volume=0`
/// * 具体的「时间格式」留给Rust侧
comment_sleep = !{
// 额外的前缀
"'sleep:" ~ WHITESPACE* ~ comment_raw
}
/// 有关「输出等待」的「魔法注释」
/// ✨阻塞主线程,等待NAVM的某个输出再继续
comment_await = {
// 额外的前缀
"'await:" ~ output_expectation
}
/// 有关「输出预期(包含)」的「魔法注释」
/// ✨检查NAVM的所有输出,返回「是否有符合预期的输出」的[`Result`]
comment_expect_contains = {
// 额外的前缀
"'expect-contains:" ~ output_expectation
}
/// 有关「保存输出」的「魔法注释」
/// ✨存储缓存的所有输出到指定路径下的文件(阻塞主线程)
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虚拟机
/// * 📄参数:选项、理由
/// * 选项:决定执行的条件
/// * 📌无 ⇒ 无条件强制退出
/// * 📌有 ⇒ 有条件,或其它副作用
/// * 理由:决定返回是否「正常」
/// * 📌无理由 ⇒ 虚拟机返回 `Ok`
/// * 📌有理由 ⇒ 虚拟机返回 `Err(终止理由)`
comment_terminate = {
// 额外的前缀 | 可选的「错误」参数
"'terminate" ~ ("(" ~ comment_terminate_option ~ ")")? ~ (":" ~ comment_raw)?
}
/// 虚拟机终止指令的选项
/// * 🎯控制终止的前提条件:可以在「终止」后交由用户输入
comment_terminate_option = @{ "if-no-user" }
/// 原始注释语法:纯粹的行注释
/// * ✅`REM`的语法糖
comment_raw = @{ (!"\n" ~ ANY)* }
/// 输出预期
/// * 📌只描述「预期的内容」,与「具体的使用方式」无关
/// * 🚩【2024-03-31 17:10:03】目前不包含对「原始内容」的预期:并非跨CIN通用
output_expectation = {
output_type? ~ narsese? ~ output_operation?
}
/// NAVM输出的「类型」
/// * 🚩直接使用内容
/// * 📝原子操作配合空格识别
output_type = @{ (!WHITE_SPACE ~ ANY)* }
/// NAVM输出中「操作」的一种表征形式
/// * 🚩刻意与CommonNarsese语法不一致,以便省去「XX=」前缀进行识别
output_operation = {
"(" ~ "^" ~ atom_content ~ "," ~ term_list? ~ ")"
}
/// Narsese | 优先级:任务 > 语句 > 词项
/// * 🚩不使用「静默规则」,让剩下的语法树作为「Narsese边界匹配」用
/// * 🚩不能使用「原子规则」匹配整个字符串:会导致匹配失败
/// * ✅`NSE`的语法糖
narsese = {
task
| sentence
| term
}
/// 任务:有预算的语句
task = {
budget ~ sentence
}
/// 预算值 | 不包括「空字串」隐含的「空预算」
budget = {
"$" ~ budget_content ~ "$"
}
/// 预算值内容
budget_content = _{
(truth_budget_term ~ (";" ~ truth_budget_term)* ~ ";"*)
| "" // 空预算(但带括号)
}
/// 通用于真值、预算值的项 | 用作内部数值,不约束取值范围
truth_budget_term = @{ (ASCII_DIGIT | ".")+ }
/// 语句 = 词项 标点 时间戳? 真值?
sentence = {
term ~ punctuation ~ stamp? ~ truth?
}
/// 词项 = 陈述 | 复合 | 原子
term = _{
statement
| compound
| atom
}
/// 陈述 = <词项 系词 词项>
statement = {
"<" ~ term ~ copula ~ term ~ ">"
}
/// 陈述系词
copula = @{
(punct_sym ~ "-" ~ punct_sym) // 继承/相似/实例/属性/实例属性
| (punct_sym ~ "=" ~ punct_sym) // 蕴含/等价
| ("=" ~ punct_sym ~ ">") // 时序性蕴含
| ("<" ~ punct_sym ~ ">") // 时序性等价
}
/// 标点符号 | 用于「原子词项前缀」「复合词项连接词」和「陈述系词」
punct_sym = { (PUNCTUATION | SYMBOL) }
/// 复合 = (连接词, 词项...) | {外延集...} | [内涵集...]
compound = {
compound_common
| ext_set
| int_set
}
/// 通用的复合词项
compound_common = { ("(" ~ connecter ~ "," ~ term_list ~ ")") }
/// 通用的「词项列表」 | 静默展开
term_list = _{ term ~ ("," ~ term)* }
/// 外延集 | 📌【2024-03-29 09:39:39】pest代码折叠中会丢掉所有「不被规则捕获的字符串信息」
ext_set = { "{" ~ term_list ~ "}" }
/// 内涵集
int_set = { "[" ~ term_list ~ "]" }
/// 复合词项连接词
connecter = @{ punct_sym ~ (!"," ~ punct_sym)* }
/// 原子 = 前缀(可选) 内容
atom = {
placeholder // 占位符
| (atom_prefix ~ atom_content) // 变量/间隔/操作……
| atom_content // 词语
}
/// 占位符 = 纯下划线字符串
placeholder = @{ "_"+ }
/// 原子词项前缀
atom_prefix = @{ (!"<" ~ !"(" ~ !"[" ~ !"{" ~ punct_sym)+ }
/// 原子词项内容 | 已避免与「复合词项系词」相冲突
atom_content = @{ atom_char ~ (!copula ~ atom_char)* }
/// 能作为「原子词项内容」的字符
atom_char = { LETTER | NUMBER | "_" | "-" }
/// 标点
punctuation = { (PUNCTUATION | SYMBOL) }
/// 时间戳 | 空时间戳会直接在「语句」中缺省
stamp = {
":" ~ (!":" ~ ANY)+ ~ ":"
}
/// 真值 | 空真值会直接在「语句」中缺省
truth = {
"%" ~ (truth_budget_term ~ (";" ~ truth_budget_term)* ~ ";"*) ~ "%"
}