gocc 实战:手把手用 50 行 BNF 写出一个四则运算计算器

📅 2026/8/21 18:11:25
gocc 实战:手把手用 50 行 BNF 写出一个四则运算计算器
gocc 实战手把手用 50 行 BNF 写出一个四则运算计算器【免费下载链接】goccParser / Scanner Generator项目地址: https://gitcode.com/gh_mirrors/go/gocc想要快速入门 gocc 这个 Go 语言写的 Parser / Scanner Generator解析器生成器吗本文将从零开始手把手带你用一份约 50 行的 BNF 语法文件生成一个支持加减乘除和括号的四则运算计算器。不需要手写词法分析器也不用手写递归下降解析器gocc 会替你自动生成完整可运行的 Go 代码让你 10 分钟就能跑通第一个属于自己的编译器前端。下面我们就从 gocc 的安装开始一步步完成这个实战项目。gocc 是什么为什么值得用它gocc 是一个完全用 Go 编写的编译器工具包compiler kit它的核心能力是从一个 BNF 语法文件生成词法分析器Lexer/Scanner和语法分析器Parser。词法分析器本质是 DFA确定性有限自动机支持 UTF-8 输入负责把字符串切成一个个 token语法分析器本质是 PDA下推自动机识别 LR(1) 文法并支持自动解决 shift/reduce 和 reduce/reduce 冲突。你只需要把精力放在定义语法规则和编写语义动作上剩下的琐碎工作全部交给 gocc。这也让它成为学习编译原理、快速验证语法的绝佳工具。gocc 一键安装方法在动手之前先确认你已经装好了 Go 环境1.16 以上即可。然后克隆源码并编译安装git clone https://gitcode.com/gh_mirrors/go/gocc cd gocc go install .安装完成后确保gocc命令在你的 PATH 中输入gocc -h能打印帮助信息即表示成功。三步搭建计算器项目结构我们采用和官方 calc 示例一致的目录结构创建以下三个文件即可calc.bnf语法文件唯一需要你手写的核心calc_test.go测试文件验证计算器是否正确Makefile一键重新生成代码的快捷方式。参考官方示例文件calc.bnf、calc_test.go、Makefile。手写 50 行 BNF 语法文件核心实战下面是整个项目的灵魂——calc.bnf它分为词法部分和语法部分/* Lexical part */ _digit : 0-9 ; int64 : 1-9 {_digit} ; !whitespace : | \t | \n | \r ; /* Syntax part */ import ( github.com/goccmack/gocc/example/calc/token github.com/goccmack/gocc/example/calc/util ) 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) ;别被吓到拆开看其实非常简单词法规则怎么写数字与空白_digit : 0-9 ;定义了一个辅助符号下划线开头表示单个数字int64 : 1-9 {_digit} ;定义整数 token首位不能是 0后面可以跟任意多个数字{...}表示重复!whitespace : ... ;以!开头表示忽略该符号空白字符不会进入 token 流这样我们就能写12而不是1 2。语法规则与语义动作怎么写 ... 之间的内容就是语义动作Action Expression语法上等价于 Go 的表达式列表要求返回(Attrib, error)$0、$1、$2分别代表产生式右侧第 0、1、2 个符号的返回值Expr Term $0.(int64) $2.(int64), nil 表示加法把左操作数Expr和右操作数Term相加返回Factor : ( Expr ) $1, nil 表示括号$1就是括号内的 Expr 值int64 util.IntValue($0.(*token.Token).Lit) 表示把数字 token 的文本转成 int64IntValue是 gocc 自动生成的工具函数见 litconv.go。注意这里把加法和乘法分成了Expr和Term两层这正是处理运算优先级的经典手法乘法先结合、优先级更高所以1 2 * 3的结果是7而不是9。一键生成词法分析器与解析器代码文件写好后执行gocc calc.bnfgocc 会自动在当前目录下生成lexer、parser、token、util、errors五个子包。以后改了语法想重新生成直接make regenerate即可见 Makefile。如果生成过程中出现 LR(1) 冲突可以用gocc -a calc.bnf让 gocc 自动解析冲突。写测试验证计算器两分钟跑通最后用一段测试代码验证计算结果是否正确参考官方 calc_test.gofunc Test1(t *testing.T) { p : parser.NewParser() for _, ts : range testData { s : lexer.NewLexer([]byte(ts.src)) sum, err : p.Parse(s) if err ! nil { t.Error(err) } if sum ! ts.expect { t.Errorf(Error: %s %d. Expected %d\n, ts.src, sum, ts.expect) } } }核心用法只有三步lexer.NewLexer([]byte(输入字符串))创建词法分析器parser.NewParser()创建解析器p.Parse(s)解析并返回结果。运行go test -v如果输出PASS恭喜你你的第一个 gocc 四则运算计算器已经成功诞生了进阶从计算器到真正的编译器这个小计算器虽然简单却包含了编译器前端的完整骨架。想进一步深入官方仓库里还有更多值得参考的实战示例bools/example.bnf带 AST 构建的布尔表达式示例errorrecovery/er.bnf错误恢复机制演示sr/sr.bnf 与 rr/rr.bnfshift/reduce 与 reduce/reduce 冲突处理示例usercontext/example.bnf自定义解析上下文的进阶玩法。这些示例都用相同的目录结构组织看懂一个就能看懂全部。而 gocc 的完整语法规范定义在 spec/gocc2.ebnf即用 BNF 描述 BNF非常有趣。总结从安装 gocc、编写 50 行 BNF到一键生成代码并测试通过你已经完整体验了 gocc 这个 Parser / Scanner Generator 的整个工作流程。用 gocc 生成解析器的最大好处是语法规则即代码改一行 BNF 就能重新生成新的解析器让词法分析与语法分析的开发效率提升一个量级。现在就去动手试试吧把上面的 BNF 改成支持减法、除法和更多运算符你会发现 gocc 的世界比想象中更有趣。【免费下载链接】goccParser / Scanner Generator项目地址: https://gitcode.com/gh_mirrors/go/gocc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考