golang用Go编写的Go语言编译器工具包插件库gocc的使用
Golang用Go编写的Go语言编译器工具包插件库gocc的使用
简介
Gocc是一个用Go编写的Go编译器工具包。Gocc可以从BNF生成词法分析器和语法分析器,或者生成独立的DFA或语法分析器。
- 词法分析器是DFA,可以识别正则语言。Gocc词法分析器接受UTF-8输入
- Gocc语法分析器是PDA,可以识别LR-1语言。可选的LR1冲突处理可以自动解决shift/reduce和reduce/reduce冲突
安装
- 首先从Go官网下载并安装Go
- 设置GOPATH环境变量
- 在命令行运行:
go get github.com/goccmack/gocc
(go get会克隆gocc到GOPATH/src/github.com/goccmack/gocc并运行go install) - 或者克隆源码:
git clone https://github.com/goccmack/gocc
,然后运行go install github.com/goccmack/gocc
- 最后确保gocc二进制文件所在的bin目录在你的PATH环境变量中
快速开始
安装完成后,首先在包文件夹中创建你的BNF文件。
例如GOPATH/src/foo/bar.bnf:
/* 词法部分 */
id : 'a'-'z' {'a'-'z'} ;
!whitespace : ' ' | '\t' | '\n' | '\r' ;
/* 语法部分 */
<< import "foo/ast" >>
Hello: "hello" id << ast.NewWorld($1) >> ;
然后使用gocc运行:
cd $GOPATH/src/foo
gocc bar.bnf
这将在GOPATH/src/foo中生成扫描器、解析器和token包。
接下来在$GOPATH/src/foo/ast创建ast.go文件:
package ast
import (
"foo/token"
)
type Attrib interface {}
type World struct {
Name string
}
func NewWorld(id Attrib) (*World, error) {
return &World{string(id.(*token.Token).Lit)}, nil
}
func (this *World) String() string {
return "hello " + this.Name
}
最后,我们编写一个测试文件$GOPATH/src/foo/test/parse_test.go:
package test
import (
"foo/ast"
"foo/lexer"
"foo/parser"
"testing"
)
func TestWorld(t *testing.T) {
input := []byte(`hello gocc`)
lex := lexer.NewLexer(input)
p := parser.NewParser()
st, err := p.Parse(lex)
if err != nil {
panic(err)
}
w, ok := st.(*ast.World)
if !ok {
t.Fatalf("This is not a world")
}
if w.Name != `gocc` {
t.Fatalf("Wrong world %v", w.Name)
}
}
运行测试:
cd $GOPATH/src/foo/test
go test -v
BNF
Gocc BNF规范可以参考示例。
动作表达式和AST
动作表达式格式为<<
goccExpressionList >>
。goccExpressionList相当于goExpressionList。这个表达式列表应该返回一个Attrib和一个error,其中Attrib是:
type Attrib interface {}
解析后的BNF规则元素可以在expressionList中表示为$
+数字。
一些动作表达式示例:
<< $0, nil >>
<< ast.NewFoo($1) >>
<< ast.NewBar($3, $1) >>
<< ast.TRUE, nil >>
一些函数示例:
func NewFoo(a Attrib) (*Foo, error) { ... }
func NewBar(a, b Attrib) (*Bar, error) { ... }
示例项目
这些项目使用了gocc:
- gogo - Go到MIPS的编译器
- gonum/gonum - DOT解码器
- llir/llvm - LLVM IR库
- mewmew/uc - µC语言编译器
- gographviz - Graphviz DOT语言解析器
- katydid - 编码无关的验证语言
- skius/stringlang - StringLang语言的解释器
- miller - 类似awk、sed、cut的工具
- nesgo - NES的Go编译器
更多关于golang用Go编写的Go语言编译器工具包插件库gocc的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang用Go编写的Go语言编译器工具包插件库gocc的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
gocc - Go语言编译器工具包插件库使用指南
gocc 是一个用 Go 编写的 Go 语言编译器工具包插件库,它提供了一套完整的工具来构建编译器前端,包括词法分析器和语法分析器生成器。下面我将详细介绍 gocc 的使用方法。
安装 gocc
首先需要安装 gocc 工具:
go get github.com/goccmack/gocc
安装完成后,gocc
命令将被添加到您的 $GOPATH/bin
目录中。
基本概念
gocc 主要由以下几个部分组成:
- 词法分析器 (Lexer) - 将输入文本转换为标记(token)序列
- 语法分析器 (Parser) - 根据语法规则分析标记序列
- 抽象语法树 (AST) - 表示源代码结构的树状数据结构
创建语法定义文件
gocc 使用 .bnf
文件定义语法规则。例如,创建一个简单的计算器语法 calc.bnf
:
/* 词法定义 */
!whitespace : ' ' | '\t' | '\n' | '\r' ;
!comment : '#' {.} '\n' ;
digit : '0'-'9' ;
number : digit { digit } ;
/* 语法定义 */
<<
import (
"github.com/goccmack/gocc/example/calc/ast"
)
>>
Calc : Expr;
Expr :
Expr "+" Term << ast.NewPlus($0, $2) >>
| Term
;
Term :
Term "*" Factor << ast.NewMult($0, $2) >>
| Factor
;
Factor :
"(" Expr ")" << $1 >>
| number << ast.NewNum($0) >>
;
生成解析器
使用 gocc 命令生成解析器:
gocc calc.bnf
这将生成以下目录结构:
calc/
├── ast/ # 抽象语法树定义
├── lexer/ # 词法分析器
├── parser/ # 语法分析器
├── token/ # 标记定义
└── util/ # 工具函数
实现 AST 节点
在 ast/ast.go
中定义 AST 节点:
package ast
type Expr interface {
isExpr()
}
type (
Plus struct {
Left Expr
Right Expr
}
Mult struct {
Left Expr
Right Expr
}
Num struct {
Value string
}
)
func (Plus) isExpr() {}
func (Mult) isExpr() {}
func (Num) isExpr() {}
func NewPlus(left, right interface{}) Expr {
return Plus{left.(Expr), right.(Expr)}
}
func NewMult(left, right interface{}) Expr {
return Mult{left.(Expr), right.(Expr)}
}
func NewNum(num interface{}) Expr {
return Num{string(num.(*token.Token).Lit)}
}
使用生成的解析器
创建一个 main.go
来使用生成的解析器:
package main
import (
"fmt"
"os"
"github.com/goccmack/gocc/example/calc/lexer"
"github.com/goccmack/gocc/example/calc/parser"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: calc 'expression'")
return
}
input := []byte(os.Args[1])
lex := lexer.NewLexer(input)
p := parser.NewParser()
ast, err := p.Parse(lex)
if err != nil {
fmt.Println("Parse error:", err)
return
}
fmt.Printf("AST: %#v\n", ast)
}
高级特性
错误处理
gocc 提供了详细的错误报告功能,可以定位语法错误的位置。
自定义词法分析
可以通过实现 Lexer
接口来自定义词法分析行为。
语法冲突解决
gocc 支持通过优先级和结合性规则解决语法冲突:
%left '+';
%left '*';
实际应用示例
下面是一个更完整的计算器实现,包含求值功能:
// ast/eval.go
package ast
func (p Plus) Eval() int {
return p.Left.Eval() + p.Right.Eval()
}
func (m Mult) Eval() int {
return m.Left.Eval() * m.Right.Eval()
}
func (n Num) Eval() int {
var val int
fmt.Sscan(n.Value, &val)
return val
}
// main.go
func main() {
// ... 前面的解析代码
result := ast.(ast.Expr).Eval()
fmt.Printf("Result: %d\n", result)
}
总结
gocc 是一个功能强大且灵活的编译器工具包,特别适合需要自定义领域特定语言(DSL)的场景。它的主要优点包括:
- 纯 Go 实现,易于集成到 Go 项目中
- 生成的代码可读性强
- 支持复杂的语法规则
- 提供详细的错误报告
通过定义合适的语法规则和 AST 结构,您可以使用 gocc 构建各种语言的解析器,从简单的配置文件解析器到复杂的编程语言前端。
希望这个指南能帮助您开始使用 gocc。如需更复杂的功能,可以参考 gocc 的官方文档和示例代码。