0Pricing
TypeScript Academy · 课时

解析器组合子概念

将小型解析器组合成更大的解析器。

解析器组合子概念 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 解析器组合子概念
  2. 类型级字符串拆分
  3. 使用模板字面量进行解析
  4. 小型类型级路由解析器
← 返回 TypeScript Academy