返回

揭秘递归下降分析,让你轻松实现 HTML 解析

前端

前言

本文将会从上下文无关文法开始介绍,从使用 BNF 语法到理解递归下降分析思想,最后实现一个简单的 html 解析器收尾。本文的亮点是使用 typescript 编写组合子编译器,对于前端开发人员来说,使用起来会更加得心应手。

认识上下文无关文法

上下文无关文法(Context-Free Grammar,CFG)是一种形式语言的定义方式,它由终结符、非终结符、产生式和开始符号组成。终结符是语言中的基本单元,而非终结符是语言中可以被其他符号替换的符号。产生式定义了如何用非终结符派生出终结符或其他非终结符。开始符号是文法的入口点。

例如,以下 CFG 定义了一个简单的算术表达式语言:

<expr> ::= <term> | <expr> + <term>
<term> ::= <factor> | <term> * <factor>
<factor> ::= <number> | (<expr>)
<number> ::= [0-9]+

在这个 CFG 中,终结符包括数字、加号、乘号和括号。非终结符包括 expr、term 和 factor。开始符号是 expr。

递归下降分析

递归下降分析(Recursive Descent Parsing)是一种自顶向下的语法分析方法。它从文法的开始符号开始,然后根据产生式逐层向下推导,直到推导出所有终结符。

例如,对于表达式 1 + 2 * 3,递归下降分析的步骤如下:

  1. 从开始符号 expr 开始。
  2. 根据产生式 <expr> ::= <term> | <expr> + <term>,将 expr 推导出 term + expr。
  3. 根据产生式 <term> ::= <factor> | <term> * <factor>,将 term 推导出 factor * term。
  4. 根据产生式 <factor> ::= <number> | (<expr>),将 factor 推导出 number。
  5. 将 number 推导出终结符 1。
  6. 将 factor 推导出终结符 2。
  7. 将 term 推导出终结符 2 * 3。
  8. 将 expr 推导出终结符 1 + 2 * 3。

使用 ts-parsec 构建 HTML 解析器

ts-parsec 是一个用 TypeScript 编写的组合子解析器库。它提供了丰富的组合子,可以轻松地构建各种各样的解析器。

以下是一个使用 ts-parsec 构建的简单的 HTML 解析器:

import { Parser } from "ts-parsec";

const htmlParser = Parser.letter().many().sepBy(Parser.whitespace()).map(
  (tokens) => tokens.join("")
);

这个解析器可以解析一个 HTML 文档,并提取出其中的所有文本内容。

结语

递归下降分析是一种简单而强大的语法分析方法。它易于理解和实现,而且可以解析各种各样的语言。ts-parsec 是一个功能强大的组合子解析器库,它可以帮助我们轻松地构建各种各样的解析器。