zh CN 错误处理 - chiba233/yumeDSL GitHub Wiki
错误处理
解析器永远不会抛异常。遇到写错的标签?降级成纯文本,照常输出。
对外暴露错误回调的是 parseRichText / stripRichText;parseStructural 的公开 API 故意不承诺错误语义。
错误处理流程
DSL 源文本
│
▼
解析器扫描
│
├─ 标签正常 → TextToken(正常输出)
│
└─ 标签有问题 → 降级为纯文本
│
├─ 有 onError? → 调用回调,传 ParseError
└─ 没有? → 静默丢弃,继续解析
核心原则:输入再烂也不崩,只是降级。
onError 回调
onError 是渲染层 API 的公开能力,用来收集最终解释后的错误语义:
const errors: ParseError[] = [];
parseRichText("$$bold(unclosed", {
onError: (error) => errors.push(error),
});
// errors[0]:
// {
// code: "INLINE_NOT_CLOSED",
// message: "(L1:C1) Inline tag not closed: >>>$$bold(<<< unclosed",
// line: 1,
// column: 1,
// snippet: " >>>$$bold(<<< unclosed"
// }
如果省略 onError:错误被静默丢弃。解析器永远不会抛出异常。
stripRichText("$$bold(unclosed", {
onError: (error) => console.log(error.code),
});
// INLINE_NOT_CLOSED
为什么 parseStructural 没有公开 onError
parseStructural 的职责是给你结构视图,不是给你最终的错误语义。
parseStructural负责“源码里长什么样”parseRichText/stripRichText负责“最终怎么解释、怎么降级、该报什么错”
所以 parseStructural 的公开 options 里故意没有 onError。
内部扫描阶段确实会经过错误通道,但那是给上层 API 复用的内部实现细节,不是 public contract。
ParseError 接口
interface ParseError {
code: ErrorCode;
message: string;
line: number;
column: number;
snippet: string;
}
| 字段 | 什么意思 |
|---|---|
code |
错误码(ErrorCode 联合类型)。程序逻辑应该基于 code 分支,适合穷举 switch |
message |
人类可读的错误描述,带 (L1:C1) 前缀和 >>>...<<< 标记。主要给人看,格式可能在未来版本调整,不要解析它 |
line / column |
从 1 开始。开了 trackPositions 时复用同一张行偏移表 |
snippet |
错误周围的上下文片段,>>> <<< 标出问题范围 |
错误码
type ErrorCode =
| "DEPTH_LIMIT"
| "UNEXPECTED_CLOSE"
| "INLINE_NOT_CLOSED"
| "SHORTHAND_NOT_CLOSED"
| "BLOCK_NOT_CLOSED"
| "BLOCK_CLOSE_MALFORMED"
| "RAW_NOT_CLOSED"
| "RAW_CLOSE_MALFORMED";
| 错误码 | 什么情况 | 触发示例 |
|---|---|---|
DEPTH_LIMIT |
嵌套太深,超过 depthLimit | 51 层 $$a($$b(... |
UNEXPECTED_CLOSE |
游离的关闭标签,找不到匹配的开标签。若 )$$ 后紧跟合法标签头则不报(1.3.7 起) |
单独出现的 )$$ |
INLINE_NOT_CLOSED |
inline 标签开了但没关 | $$bold(unclosed |
SHORTHAND_NOT_CLOSED |
隐式 inline 简写开了但没关(1.3 起) | 启用 implicitInlineShorthand 时的 bold(unclosed |
BLOCK_NOT_CLOSED |
block 标签缺少关闭标记 | $$info()* 有内容但没有 *end$$ |
BLOCK_CLOSE_MALFORMED |
block 关闭标记在但格式不对 | *end$$ 没有独占一行 |
RAW_NOT_CLOSED |
raw 标签缺少关闭标记 | $$code()% 有内容但没有 %end$$ |
RAW_CLOSE_MALFORMED |
raw 关闭标记在但格式不对 | %end$$ 没有独占一行 |
优雅降级
解析器永远不会在格式错误的输入上抛异常。不同场景的降级方式不同:
未注册标签
降级行为取决于标签形式:
- Inline
$$unknown(hi)$$→ 只输出内部内容hi(标签定界符被去掉,内容作为纯文本保留) - Raw/Block
$$unknown()%\nstuff\n%end$$→ 整段原始标记作为字面文本输出(解析器根本不识别这个标签,所以所有定界符原样保留)
// inline:内容被展开
dsl.parse("$$unknown(hello)$$");
// → [{ type: "text", value: "hello" }]
// raw/block:整段标记原样保留
dsl.parse("$$unknown()%\nstuff\n%end$$");
// → [{ type: "text", value: "$$unknown()%\nstuff\n%end$$" }]
不支持的形式
Handler 存在但不支持用户写的形式时(比如 handler 只有 inline,用户写了 raw 形式),整段原始标记作为字面文本输出:
// code handler 只有 raw,用户写了 inline
dsl.parse("$$code(ts)$$");
// → [{ type: "text", value: "$$code(ts)$$" }]
allowForms 限制
被 allowForms 禁用的形式等同于不支持——按不支持形式处理,整段原始标记作为字面文本输出。
未关闭标签
解析器报告错误(通过 onError),开标记作为纯文本恢复。不抛出异常。
1.1.1 之后的错误语义整理
parseRichText 移除 4 个误报
parseRichText 之前在遇到 complex form 标签(raw/block 语法)的某些退化场景时,
会错误地上报 INLINE_NOT_CLOSED。这 4 个误报已移除——正确走 complex form 退化路径的标签不再触发错误。
到 1.1.3 以及当前版本,这些复杂形态错误码已经稳定下来,例如:
$$raw-code(js)%\nconst x = 1→RAW_NOT_CLOSED$$info(T)*\nhello→BLOCK_NOT_CLOSED$$raw-code(ts)%\nconst x = 1\n%end$$ trailing→RAW_CLOSE_MALFORMED$$info()*\nhello\n*end$$ trailing→BLOCK_CLOSE_MALFORMED