解析器组合子概念
将小型解析器组合成更大的解析器。
解析器组合子概念 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。
什么是解析器
解析器读取输入(通常是字符串),并生成结构化输出,或者失败。我们先从值层面开始建立直觉,然后再将相同的思路应用到类型系统中。
type Parser<T> = (input: string) => { value: T; rest: string } | null;
// Consumes part of the input, returns a value and the leftover一个微型解析器
最简单的解析器会匹配一个字面量前缀。如果输入以预期文本开头,它就成功并返回剩余字符串;否则返回 null。
function literal(prefix: string): Parser<string> {
return input =>
input.startsWith(prefix)
? { value: prefix, rest: input.slice(prefix.length) }
: null;
}
console.log(literal("ab")("abc"));组合器
组合器接收一些小型解析器,并构建出更大的解析器。这种组合式风格正是这种方法被称为解析器组合器的原因:您可以用简单、可复用的部分组装复杂解析器。
// Small parsers: digit, letter, literal
// Combinators: sequence, choice, many
// Compose them into: number, identifier, expression串联
串联组合器先运行一个解析器,再对剩余内容运行另一个解析器,并将两者的结果配对。只有两者都成功,整个串联操作才会成功。
function seq<A, B>(pa: Parser<A>, pb: Parser<B>): Parser<[A, B]> {
return input => {
const ra = pa(input);
if (!ra) return null;
const rb = pb(ra.rest);
if (!rb) return null;
return { value: [ra.value, rb.value], rest: rb.rest };
};
}使用序列
将两个字面量组合起来,按顺序解析它们。结果包含两个匹配片段和剩余输入。
const ab = seq(literal("a"), literal("b"));
const r = ab("abc");
console.log(r); // { value: ["a","b"], rest: "c" }选择
选择组合子会尝试第一个解析器;如果失败,则尝试第二个。这可以表示备选项,例如“数字或字母”。
function alt<T>(p1: Parser<T>, p2: Parser<T>): Parser<T> {
return input => p1(input) ?? p2(input);
}重复
many组合子会反复应用解析器,直到解析失败,并收集所有结果。它可以解析列表、连续的数字或空白。
function many<T>(p: Parser<T>): Parser<T[]> {
return input => {
const out: T[] = [];
let rest = input;
let r = p(rest);
while (r) { out.push(r.value); rest = r.rest; r = p(rest); }
return { value: out, rest };
};
}映射结果
map组合子会转换解析器的结果,但不会改变解析器消耗的内容,例如将匹配到的数字字符转换为数字。
function map<A, B>(p: Parser<A>, f: (a: A) => B): Parser<B> {
return input => {
const r = p(input);
return r ? { value: f(r.value), rest: r.rest } : null;
};
}逐步构建
利用这几个组合子(literal、seq、alt、many、map),您可以解析真正的语法:数字、标识符,甚至小型表达式语言。每一层都会组合其下方的层。
const digit = alt(literal("0"), literal("1")); // toy digit
const digits = many(digit);
console.log(digits("0110x"));从值到类型
关键的洞察是:相同的组合结构也适用于类型层面。在那里,“输入”是字符串字面量类型,解析器则是使用模板字面量推断的条件类型。接下来我们将进入这一层面。
// Value level: (input: string) => { value, rest } | null
// Type level: conditional types over string literal types为何进行类型级解析
类型级解析器让编译器能够理解字符串字面量的结构:路由路径、格式字符串、查询键。您在这里建立的组合子直觉,可以直接对应到本课程其余部分的类型级技术。
// Goal: parse "users/:id" into { id: string } at compile time快速检查
测试您对解析器组合子的理解。
回顾
您已经在值层面建立了对解析器的直觉。
- 解析器消耗输入并返回一个值和剩余输入,或者失败。
- 组合子(seq、alt、many、map)可以将小型解析器组合成大型解析器。
- 相同的结构也适用于类型层面。
下一步:在类型系统中拆分字符串。
常见问题解答
「解析器组合子概念」课时是免费的吗?
是的 — 「解析器组合子概念」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。
「解析器组合子概念」这节课中我会学到什么?
将小型解析器组合成更大的解析器。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 TypeScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「解析器组合子概念」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 TypeScript Academy 课中编写并运行代码吗?
能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 解析器组合子概念
- 类型级字符串拆分
- 使用模板字面量进行解析
- 小型类型级路由解析器