1.1 为什么需要 Lark?

在编程世界里,解析(parsing)是一个基础而又关键的主题。无论是编译器、解释器、配置文件解析器,还是数据格式转换工具,都离不开“将文本转化为结构化数据”的过程。

传统上,我们可能会使用正则表达式来完成解析任务。但随着需求复杂化,正则表达式逐渐力不从心。例如:

  • 当需要解析嵌套结构(如 JSON、XML、HTML)时,正则难以应对。
  • 当语法规则具有递归特性(例如括号匹配、函数调用嵌套)时,正则表达式显得笨拙。
  • 当需要进行语义分析(如表达式求值、编程语言的编译)时,正则更无法胜任。

于是,专门的解析工具和库应运而生。Python 生态里有不少选择,比如 PLYpyparsingANTLR 的 Python 接口等。而 Lark 则是近年来备受欢迎的一款解析库。

Lark 的优势

  1. 简洁:语法定义接近 EBNF 标准,易读易写。
  2. 强大:支持 Earley、LALR 等算法,能处理几乎所有上下文无关语法。
  3. 灵活:能输出解析树(Parse Tree),并可通过 Transformer 转换为语义树(AST)。
  4. 现代:支持 Python 3.x,性能优化合理,API 友好。
  5. 跨场景:可用来编写 DSL、编译器前端、数据解析器、甚至聊天机器人语言解释器。

换句话说,如果你需要在 Python 里写一个语法解析器,Lark 是极佳的选择。


1.2 Lark 能做什么?

举几个典型的应用场景,让你快速感受 Lark 的能力:

  1. 算术表达式解析与求值

    • 输入:1 + 2 * (3 + 4)
    • 输出:解析树 → AST → 结果 15
  2. 配置文件解析

    • 输入:

      [database]
      host = localhost
      port = 3306
      
    • Lark 可以轻松定义一个语法来读取并转化为 Python 字典。

  3. DSL(领域专用语言)

    • 例如编写一个简化版的 SQL 子集解析器:

      SELECT name, age FROM users WHERE age > 18
      
    • 经过 Lark 解析后,可以直接生成查询语法树,供后端执行。

  4. 自定义编程语言

    • 许多学习编译原理的同学,会使用 Lark 来构建自己的“小语言”。

💡 小贴士:
如果你曾经在大学里学过《编译原理》,可能对“词法分析器”“语法分析器”这些术语印象深刻。Lark 就是一个帮你把“语法分析器”写出来的工具,而无需你手写复杂的递归下降代码。


1.3 Lark 的解析算法

Lark 内置了多种解析算法,最常用的有两种:

  • Earley 算法

    • 功能最强大,能处理所有上下文无关语法(包括有二义性的语法)。
    • 适合写通用解析器,如自然语言或复杂的 DSL。
    • 但性能相对一般。
  • LALR(1) 算法

    • 功能稍弱,但能覆盖绝大多数编程语言语法。
    • 速度快,适合高性能需求。

在使用 Lark 时,可以通过参数指定解析算法,例如:

from lark import Lark

grammar = """
start: "hello" NAME
NAME: /[a-zA-Z_][a-zA-Z0-9_]*/
"""

parser = Lark(grammar, parser="earley")
print(parser.parse("hello world"))

1.4 安装 Lark

安装 Lark 非常简单,可以直接使用 pip:

pip install lark

如果你想使用最新的开发版本,可以从 GitHub 安装:

pip install git+https://github.com/lark-parser/lark

安装完成后,你可以在 Python 中尝试:

import lark
print(lark.__version__)

如果能正常输出版本号,说明安装成功。


1.5 第一个 Lark 程序

我们来写一个最简单的 Lark 示例:解析算术表达式

语法定义

from lark import Lark

grammar = """
start: expr

?expr: expr "+" term   -> add
     | expr "-" term   -> sub
     | term

?term: term "*" factor -> mul
     | term "/" factor -> div
     | factor

?factor: NUMBER
       | "(" expr ")"

%import common.NUMBER
%import common.WS
%ignore WS
"""

parser = Lark(grammar, parser="lalr")

在这个语法中:

  • start 是入口规则。
  • expr 定义了加减法。
  • term 定义了乘除法。
  • factor 定义了数字和括号。
  • %import 表示引入内置的通用规则。
  • %ignore WS 表示忽略空格。

解析与输出

tree = parser.parse("1 + 2 * (3 + 4)")
print(tree.pretty())

输出结果为:

add
  NUMBER  1
  mul
    NUMBER  2
    add
      NUMBER  3
      NUMBER  4

这就是一个标准的 解析树


1.6 从解析树到计算结果

解析树只是结构信息,如果我们想计算结果,需要定义一个 Transformer

from lark import Transformer

class CalculateTree(Transformer):
    def add(self, items):
        return items[0] + items[1]
    def sub(self, items):
        return items[0] - items[1]
    def mul(self, items):
        return items[0] * items[1]
    def div(self, items):
        return items[0] / items[1]
    def NUMBER(self, items):
        return float(items)

calc = CalculateTree()
result = calc.transform(tree)
print(result)

运行结果:

15.0

至此,我们就用 Lark 实现了一个 迷你计算器

💡 小贴士:
这里的 Transformer 相当于“语义分析”的过程,把纯粹的结构树转化为有意义的操作。你也可以用它来生成字节码、SQL 语句、甚至机器学习模型的中间表示。


1.7 Lark 与正则的比较

很多初学者会问:我用正则表达式不也能做解析吗?为什么要用 Lark?

我们来对比一下。

用正则解析算术表达式

假设要匹配 1 + 2 * (3 + 4),正则的复杂度非常高,而且要处理括号嵌套基本不可能。

用 Lark

只需要清晰的语法规则,就能自然地解析括号嵌套。

换句话说:

  • 正则适合小任务:匹配 Email、URL、简单格式化。
  • Lark 适合大任务:编程语言、DSL、复杂文件格式。

1.8 Lark 的内置工具

Lark 提供了不少便利的内置工具:

  1. 通用 Token

    • NUMBERWORDWS 等常见正则模式直接可用。
  2. 调试工具

    • tree.pretty():输出可视化的解析树。
    • parser.parse_interactive():逐步解析,方便调试复杂语法。
  3. Error Handling

    • 可以捕获 UnexpectedInput 异常,提供语法错误提示。

1.9 小结

在本章中,我们学习了:

  • Lark 的基本概念与优势。
  • 如何安装并验证 Lark。
  • 使用 Lark 定义最简单的语法。
  • 如何将文本解析为解析树,并进一步转化为有意义的结果。
  • Lark 与正则的区别与优势。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐