DSL 错误处理博物馆:Fallback 机制实录
|3 天前
本文并非基础的 DSL 语法教程,而是为您展示一个特殊的场景:当代码出现各种不规范或错误的语法时,这套解析器将如何进行优雅降级(Fallback)。 本文的结构统一为: 欢迎来到 DSL 错误处理博物馆。
入馆前样板间:正常渲染对照组在进入故障展厅前,我们先看几段语法完全正常的示例。这有助于在后续对比中更容易看清“哪里出了问题”以及“出错后的退化形态”。
源码 显示结果 今天的心情是 很好,也可以点开 这个链接。 说明:这是基础的行内正常渲染,不涉及任何 Fallback 或错误恢复机制。
Healthy Inline DemoyumeDSL
今天的心情是 $$bold(很好)$$,也可以点开 $$link(https://example.com | 这个链接)$$。
源码 显示结果 说明:块级标签在参数完整时,会直接按预期渲染。
Healthy Block DemoyumeDSL
$$info(正常示例 | 这一块标题和正文都合法)$$
源码 显示结果 发布日期:2026/03/23 相对时间:3 天前 完整时间: 2026年3月23日 - 星期一 限制语言: 2026年3月23日 - 星期一 佛历时间: 2569/03/23 说明:对于要求“固定参数数量”的标签,只要参数合法,即会直接产出格式化后的文本。
Healthy Date + FromNow DemoyumeDSL
发布日期:$$date(2026-03-23|YYYY/MM/DD)$$
相对时间:$$fromNow(2026-03-23T00:00:00.000Z)$$
完整时间: $$date(2026-03-23|LL - dddd)$$
限制语言: $$date(2026-03-23|LL - dddd | zh)$$
佛历时间: $$date(2026-03-23|BBBB/MM/DD)$$
源码 显示结果 说明:即便在内联形态下,只要语法合法,正文内依旧可以继续嵌套其他标签。
Healthy Collapse DemoyumeDSL
$$collapse(点我展开看看 | 这里是折叠区里的正常正文,里面也可以继续写 $$bold(加粗文本)$$。)$$
点我展开看看
这里是折叠区里的正常正文,里面也可以继续写 加粗文本。
展厅 1:遇到未知标签不会崩溃,只会安静“去壳”源码 显示结果 hello world 说明:未知标签不会作为正式语义节点被渲染,但解析器会保留其内部的普通文本。
Unknown TagyumeDSL
$$unknown(hello world)$$
源码 显示结果 hello world and stars 说明:外层未知的标签壳被剥离,但内部嵌套的合法标签依然会正常工作。
Unknown Tag + Nested Known TagyumeDSL
$$unknown(hello $$bold(world)$$ and $$underline(stars)$$)$$
展厅 2:行内标签未闭合,直接退化为普通文本源码 显示结果 $$bold(hello
Inline Not ClosedyumeDSL
$$bold(hello
说明:因为没有检测到闭合符 ),解析器不会强行补全,而是将这段代码作为普通文本直接输出。
源码 显示结果 $$link(https://example.com | Example 说明:发生退化时,解析器会尽量保留原始的开头片段,不会吞噬已输入的参数。
Inline Not Closed With ParamsyumeDSL
$$link(https://example.com | Example
源码 显示结果 $$link(https://example.com | before ok tail 说明:外层标签因未闭合而退化,但内部已经成功闭合的标签内容会被完整保留并拼回最终文本。
Inline Opened, Nested, Then EOFyumeDSL
$$link(https://example.com | before $$bold(ok)$$ tail
展厅 3:块级标签未闭合,整段原样返回源码 显示结果 $$raw-code(yumeDSL | cursed demo)% const a = 1 const b = 2 说明:缺少真正的结束行,整段内容将放弃解析,退化为普通文本。
Raw Not ClosedyumeDSL
$$raw-code(yumeDSL | cursed demo)%
const a = 1
const b = 2
源码 显示结果 $$collapse(点我展开)* 第一层 第二层 说明:Block 同理,如果没有找到正确的 *end$$,就会统一作为普通文本处理。
Block Not ClosedyumeDSL
$$collapse(点我展开)*
第一层
第二层
展厅 4:格式不规范的闭合(Malformed Close)这是近期专门收紧的一项逻辑:并非所有的“没闭合”都该被粗暴归结为 Not closed。如果您确实写了结束符,但出现了错误的缩进或携带了多余的尾随字符,系统会为其赋予独立的语义提示。
源码 显示结果 $$info(标题)* hello *end$$ 说明:这不是一个合法的闭合行,因为前面多出了空格。解析器会精准上报 Malformed block close,而不是泛泛地提示未闭合。
Block Close With IndentyumeDSL
$$info(标题)*
hello
*end$$
源码 显示结果 $$info(标题)* hello *end$$ extra cheese 说明:闭合符必须独占整行。后面跟着其他字符同样会被判定为格式错误。
Block Close With TrailingyumeDSL
$$info(标题)*
hello
*end$$ extra cheese
源码 显示结果 $$raw-code(yumeDSL | title)% hello %end$$ 说明:Raw 标签的闭合逻辑也是一致的,外观相似并不等同于语法合法。
Raw Close With IndentyumeDSL
$$raw-code(yumeDSL | title)%
hello
%end$$
源码 显示结果 $$raw-code(yumeDSL | title)% hello %end$$ hahaha 说明:只要该行不完全等于 %end$$,就会触发 Malformed close。
Raw Close With TrailingyumeDSL
$$raw-code(yumeDSL | title)%
hello
%end$$ hahaha
展厅 5:意外的孤立闭合符不会阻断全局源码 显示结果 前面什么都没开 )$$ 后面继续活着 说明:孤立的右括号不会导致整篇文章解析失败,它会被当作普通字符保留,解析器会继续平稳处理后续内容。
Unexpected CloseyumeDSL
前面什么都没开
)$$
后面继续活着
展厅 6:转义符拥有最高优先级这部分展示的是:只要明确使用了转义符,解析器就会严格按“字面字符”处理,不再将其误判为结束符或分隔符。
源码 显示结果 这里有一个右括号 ) 说明:遇到转义斜杠后,括号只作为普通文本渲染,不再承担闭合标签的功能。
Escaped Right ParenthesisyumeDSL
$$bold(这里有一个右括号 \))$$
源码 显示结果 https://example.com | still text 说明:竖线被转义后,取消了其“参数分隔”的语义,成为纯文本的一部分。
Escaped PipeyumeDSL
$$link(https://example.com \| still text)$$
源码 显示结果 说明:带有转义符的 %end$$ 仅为正文内容,真正的解析结束取决于后续那行完整且干净的 %end$$。
Escaped Raw CloseyumeDSL
$$raw-code(yumeDSL | demo)%
\%end$$
真的结束要等下一行
%end$$
demoyumeDSL
%end$$
真的结束要等下一行
展厅 7:局部错误后的自我恢复机制许多解析器在遇到中间段落错误时会直接中断,导致后续内容一并作废。但这里的策略是:在能恢复的地方继续恢复,避免因局部错误拖垮整段内容。
源码 显示结果 $$bold(bad) $$good 说明:前面失效的部分会原样输出,一旦后续遇到合法的语法起点,解析器会立即恢复正常工作。
Broken Then GoodyumeDSL
$$bold(bad) $$$$thin(good)$$
源码 显示结果 $$bold(unclosedok good 说明:外层标签因未闭合而失效,但内部已经成功闭合的合法标签依然会正常产出结果。
Unclosed Prefix Then GoodyumeDSL
$$bold(unclosed $$thin(ok)$$ $$underline(good)$$
展厅 8:精准区分 Malformed close 与 Not closed很多系统会将所有解析失败统称为“未闭合”,但这不利于精准排查问题。我们对此进行了严格区分:
直观理解: 说明:这项区分对于排查长文档的排版错误非常有效,能帮助作者迅速定位是“漏写”还是“写错”。
结语:支持 Fallback 不代表纵容错误面对异常输入,许多系统的做法是直接报错阻断。而这套 DSL 的设计哲学并非为了纵容语法错误,而是确保:
最终呈现效果: 一句话总结:这套系统的底线是,即使面临不规范的输入,也要尽可能体面地维持运转并呈现内容。