快速上手 gocc:10 分钟创建你的第一个 BNF 语法文件并生成解析器
【免费下载链接】goccParser / Scanner Generator项目地址: https://gitcode.com/gh_mirrors/go/gocc
gocc 是一款用 Go 语言编写的解析器生成器(Parser / Scanner Generator)。你只需写好一份 BNF 语法文件,gocc 就能自动生成词法分析器(lexer)和 LR(1) 语法分析器(parser),再也不用手写一堆繁琐的解析代码。本文将从零开始,带你用 10 分钟完成 gocc 安装、编写 BNF 语法文件、生成解析器并跑通测试,新手也能快速掌握这门"用语法换代码"的高效技能。
gocc 是什么?新手为什么要学解析器生成器
gocc 本质是一个"编译器生成器"(Compiler Compiler):你把语法规则写进 BNF 语法文件,它帮你把词法分析器和语法分析器全部生成出来。它生成的词法分析器是能识别正则语言的 DFA(支持 UTF-8 输入),语法分析器是识别 LR(1) 语言的 PDA,并且可以自动解决 shift/reduce 和 reduce/reduce 冲突。
很多知名 Go 项目都在用它处理复杂文本格式,例如 Graphviz DOT 语言解析、LLVM IR 解析等。学会 gocc,意味着任何"自定义语言、配置文件、DSL"你都能自己快速实现。
gocc 快速安装:一条命令编译出命令行工具
首先确保本机已安装 Go(建议 1.20+),然后把 gocc 源码克隆到本地并编译安装:
git clone https://gitcode.com/gh_mirrors/go/gocc cd gocc go install安装完成后,把$(go env GOPATH)/bin加入 PATH 环境变量,执行gocc -h看到用法说明即代表安装成功。后续我们只需调用gocc xxx.bnf一个命令即可。
编写 BNF 语法文件:从一个计算器语法开始
BNF 语法文件是整个流程的核心输入。以项目自带的计算器为例,文件内容位于example/calc/calc.bnf,分为词法部分和语法部分:
/* Lexical part */ _digit : '0'-'9' ; int64 : '1'-'9' {_digit} ; !whitespace : ' ' | '\t' | '\n' | '\r' ; /* Syntax part */ Calc : Expr; Expr : Expr "+" Term << $0.(int64) + $2.(int64), nil >> | Term ; Term : Term "*" Factor << $0.(int64) * $2.(int64), nil >> | Factor ; Factor : "(" Expr ")" << $1, nil >> | int64 << util.IntValue($0.(*token.Token).Lit) >> ;要点速记:
- 词法部分用引号字面量、
'a'-'z'字符区间和{}重复来定义记号,以!开头的规则会被忽略(如空白符); - 语法部分用
:定义产生式,|表示多个候选分支; << ... >>是语义动作(SDT 规则),$0、$1代表对应位置的语法符号值,你可以在这里直接拼接出结果。
完整的 BNF 语法规范见spec/gocc2.ebnf,更多写法可参考example/bools/example.bnf。
用 gocc 生成解析器:一条命令自动产出五个包
在存放calc.bnf的目录下执行:
gocc calc.bnfgocc 会自动生成lexer、parser、token、util、errors等 Go 包,对应项目示例example/calc/下的目录结构。生成完毕直接go build ./...即可编译通过,无需任何手工改动。
如果你只需要语法分析、不需要词法分析(例如输入已经由别的工具切分好),可以加-no_lexer参数,参考example/nolexer/。
在 Go 代码中调用 gocc 生成的解析器
生成之后,调用方式非常简单——先创建 lexer,再喂给 parser 解析。参考example/calc/calc_test.go的写法:
p := parser.NewParser() s := lexer.NewLexer([]byte("1 + 2 * 3")) sum, err := p.Parse(s)p.Parse返回的sum就是你 BNF 中语义动作算出的结果(这里是7)。如果语法错误,会返回包含行列号的错误信息,错误处理包在errors/目录中。
运行测试:验证你的解析结果
在项目目录下执行:
go test -v ./...gocc 项目自带大量可运行示例,每个示例都有测试文件。建议你逐个运行example/下的make regenerate(重新生成解析器)和go test(跑测试),亲眼看看 BNF 与生成代码之间的对应关系。
gocc 常用命令行参数速查表
| 参数 | 作用 |
|---|---|
-a | 自动解决 LR(1) 冲突 |
-o <目录> | 指定输出目录 |
-p <包名> | 指定生成的 Go 包名 |
-no_lexer | 不生成词法分析器 |
-debug_parser | 开启解析器调试日志 |
-v | 输出详细过程(含 LR(1) 项集) |
-u | 允许存在不可达的产生式 |
-zip | 压缩 action 表与 goto 表 |
参数定义可查看源码internal/config/config.go。
进阶玩法:语义动作、AST 与更多内置示例
- 构建 AST:复杂项目建议用语义动作构造抽象语法树,参考
example/astx/、example/bools/ast/ast.go; - 错误恢复:想让解析器在出错后继续解析?参考
example/errorrecovery/; - 自定义上下文:通过
$Context在解析时传递上下文数据(如符号表),参考example/usercontext/及其README.md; - 冲突处理:gocc 生成的 LR(1) 冲突报告与自动解决,可对比
example/sr/(shift/reduce)与example/rr/(reduce/reduce)两个示例; - 调试利器:加
-v参数会输出LR1_sets.txt、first.txt、terminals.txt等中间文件,帮你看清分析表的每一步。
详细的原理与语法说明可阅读官方文档doc/gocc_user_guide.pdf(源码为doc/gocc_user_guide.tex)。
总结
gocc 让"写一个自己的解析器"从数周的苦工变成 10 分钟的小任务:写好 BNF 语法文件 → 执行gocc→ 调用生成的解析器,三步即可完成。无论你是想解析配置格式、做一个小型 DSL,还是学习编译原理,gocc 都是绝佳的起点。现在就打开example/calc/calc.bnf,动手改一个属于自己的语法吧!
【免费下载链接】goccParser / Scanner Generator项目地址: https://gitcode.com/gh_mirrors/go/gocc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考