golang高效扫描解析LL(1)语法的插件库prattle的使用
Golang高效扫描解析LL(1)语法的插件库Prattle的使用
概述
Prattle是一个通用的、支持Unicode的词法扫描器和自上而下的运算符优先级解析器,适用于解析LL(1)语法。扫描器和解析器可以独立使用。
安装
go get -u github.com/askeladdk/prattle
快速开始
使用Scanner扫描文本
使用Scanner
通过ScanFunc
扫描源文本生成一系列Token
。使用Expect*
、Skip
和Advance
方法来扫描标记。
// 准备扫描器
scanner := prattle.Scanner{
// Scan扫描下一个标记并返回其类型
Scan: func(s *prattle.Scanner) (kind int) {
// 跳过所有空白字符
s.ExpectAny(unicode.IsSpace)
s.Skip()
// 扫描下一个标记
switch {
case s.Done(): // 当所有输入都被消耗时停止
return 0
case s.Expect('+'): // 扫描加法运算符
return 1
case s.ExpectOne(unicode.IsDigit): // 扫描由一个或多个数字组成的数字
s.ExpectAny(unicode.IsDigit)
return 2
}
// 无效标记
s.Advance()
return -1
},
}
使用Parser解析语法
使用Parser
和Driver
将Scanner
生成的标记与ParseFunc
关联。首先定义Driver
。
// 定义解析Driver
type driver struct {
stack []int
}
func (d *driver) push(i int) {
d.stack = append(d.stack, i)
}
func (d *driver) pop() (i int) {
n := len(d.stack)
i, d.stack = d.stack[n-1], d.stack[:n-1]
return
}
func (d *driver) number(p *prattle.Parser, t prattle.Token) error {
n, _ := strconv.Atoi(t.Text)
d.push(n)
return nil
}
func (d *driver) add(p *prattle.Parser, t prattle.Token) error {
// 解析右侧运算符
_ = p.Parse(d.Precedence(t.Kind))
right := d.pop()
left := d.pop()
sum := left + right
fmt.Printf("%d + %d = %d\n", left, right, sum)
d.push(sum)
return nil
}
func (d *driver) Prefix(kind int) prattle.ParseFunc {
return d.number
}
func (d *driver) Infix(kind int) prattle.ParseFunc {
return d.add
}
func (d *driver) Precedence(kind int) int {
return kind
}
func (d *driver) ParseError(t prattle.Token) error {
return fmt.Errorf("%s", t)
}
// 准备解析器
parser := prattle.Parser{
Driver: &driver{},
}
初始化并解析表达式
source := "1 + 23 + 456 + 7890"
scanner.InitWithString(source)
parser.Init(&scanner).Parse(0)
// 输出:
// 1 + 23 = 24
// 24 + 456 = 480
// 480 + 7890 = 8370
完整示例
以下是一个完整的计算器示例,展示了如何使用Prattle解析简单的数学表达式:
package main
import (
"fmt"
"strconv"
"unicode"
"github.com/askeladdk/prattle"
)
func main() {
// 定义扫描器
scanner := prattle.Scanner{
Scan: func(s *prattle.Scanner) (kind int) {
// 跳过空白
s.ExpectAny(unicode.IsSpace)
s.Skip()
// 扫描标记
switch {
case s.Done():
return 0
case s.Expect('+'):
return 1
case s.ExpectOne(unicode.IsDigit):
s.ExpectAny(unicode.IsDigit)
return 2
}
// 无效标记
s.Advance()
return -1
},
}
// 定义解析Driver
type driver struct {
stack []int
}
func (d *driver) push(i int) {
d.stack = append(d.stack, i)
}
func (d *driver) pop() (i int) {
n := len(d.stack)
i, d.stack = d.stack[n-1], d.stack[:n-1]
return
}
func (d *driver) number(p *prattle.Parser, t prattle.Token) error {
n, _ := strconv.Atoi(t.Text)
d.push(n)
return nil
}
func (d *driver) add(p *prattle.Parser, t prattle.Token) error {
_ = p.Parse(d.Precedence(t.Kind))
right := d.pop()
left := d.pop()
sum := left + right
fmt.Printf("%d + %d = %d\n", left, right, sum)
d.push(sum)
return nil
}
func (d *driver) Prefix(kind int) prattle.ParseFunc {
return d.number
}
func (d *driver) Infix(kind int) prattle.ParseFunc {
return d.add
}
func (d *driver) Precedence(kind int) int {
return kind
}
func (d *driver) ParseError(t prattle.Token) error {
return fmt.Errorf("%s", t)
}
// 初始化并解析
parser := prattle.Parser{
Driver: &driver{},
}
source := "1 + 23 + 456 + 7890"
scanner.InitWithString(source)
if err := parser.Init(&scanner).Parse(0); err != nil {
fmt.Println("Parse error:", err)
}
}
这个示例展示了Prattle的基本用法,包括如何定义扫描器、解析驱动以及如何将它们组合起来解析简单的加法表达式。
更多关于golang高效扫描解析LL(1)语法的插件库prattle的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang高效扫描解析LL(1)语法的插件库prattle的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Prattle - Golang中的高效LL(1)语法解析库
Prattle是一个轻量级的Go语言库,专门用于解析LL(1)语法。它设计简洁高效,适合构建小型领域特定语言(DSL)或配置文件解析器。
核心特性
- 纯Go实现,无外部依赖
- 支持LL(1)语法解析
- 轻量级且高效
- 易于集成到现有项目中
基本使用示例
首先安装Prattle:
go get github.com/fasterthanlime/prattle
简单算术表达式解析
package main
import (
"fmt"
"strings"
"github.com/fasterthanlime/prattle"
)
func main() {
// 定义语法规则
grammar := `
Expr = Term (('+' | '-') Term)*
Term = Factor (('*' | '/') Factor)*
Factor = Number | '(' Expr ')'
Number = [0-9]+
`
// 创建解析器
parser, err := prattle.NewParser(strings.NewReader(grammar))
if err != nil {
panic(err)
}
// 解析输入
input := "3 + 5 * (10 - 4)"
ast, err := parser.ParseString(input)
if err != nil {
panic(err)
}
// 打印AST
fmt.Printf("AST: %#v\n", ast)
}
自定义AST处理
package main
import (
"fmt"
"strconv"
"strings"
"github.com/fasterthanlime/prattle"
)
type Node struct {
Type string
Value string
Children []*Node
}
func parse(input string) (float64, error) {
grammar := `
Expr = Term (('+' | '-') Term)*
Term = Factor (('*' | '/') Factor)*
Factor = Number | '(' Expr ')'
Number = [0-9]+ ('.' [0-9]+)?
`
parser, err := prattle.NewParser(strings.NewReader(grammar))
if err != nil {
return 0, err
}
ast, err := parser.ParseString(input)
if err != nil {
return 0, err
}
return eval(ast), nil
}
func eval(node *prattle.Node) float64 {
switch node.Type {
case "Number":
val, _ := strconv.ParseFloat(node.Value, 64)
return val
case "Expr", "Term":
result := eval(node.Children[0])
for i := 1; i < len(node.Children); i += 2 {
op := node.Children[i].Value
rhs := eval(node.Children[i+1])
switch op {
case "+":
result += rhs
case "-":
result -= rhs
case "*":
result *= rhs
case "/":
result /= rhs
}
}
return result
case "Factor":
if node.Children[0].Value == "(" {
return eval(node.Children[1])
}
return eval(node.Children[0])
default:
return 0
}
}
func main() {
result, err := parse("3.5 + 2 * (10 - 4) / 3")
if err != nil {
panic(err)
}
fmt.Printf("Result: %.2f\n", result) // 输出: Result: 7.50
}
高级用法
错误处理
func parseWithErrorHandling(input string) (*prattle.Node, error) {
grammar := `
Expr = Term (('+' | '-') Term)*
Term = Factor (('*' | '/') Factor)*
Factor = Number | '(' Expr ')'
Number = [0-9]+
`
parser, err := prattle.NewParser(strings.NewReader(grammar))
if err != nil {
return nil, fmt.Errorf("语法定义错误: %v", err)
}
ast, err := parser.ParseString(input)
if err != nil {
// 获取更详细的错误信息
if parseErr, ok := err.(*prattle.ParseError); ok {
return nil, fmt.Errorf("解析错误 (位置 %d): %v", parseErr.Pos, parseErr.Message)
}
return nil, fmt.Errorf("解析错误: %v", err)
}
return ast, nil
}
自定义词法分析
Prattle默认提供基本的词法分析功能,但也可以自定义:
func customLexer(input string) []prattle.Token {
// 实现自定义的词法分析逻辑
// 返回Token切片
return nil
}
func main() {
grammar := `...`
parser, _ := prattle.NewParser(strings.NewReader(grammar))
input := "custom input"
tokens := customLexer(input)
ast, err := parser.Parse(tokens)
if err != nil {
panic(err)
}
// 处理AST
}
性能优化技巧
- 重用Parser实例:创建Parser实例有一定开销,可以重用
- 预编译语法:如果语法不变,可以预编译并保存Parser
- 简化语法:LL(1)语法越简单,解析越快
var (
grammar = `...`
parser *prattle.Parser
once sync.Once
)
func getParser() (*prattle.Parser, error) {
var err error
once.Do(func() {
parser, err = prattle.NewParser(strings.NewReader(grammar))
})
return parser, err
}
限制
- 仅支持LL(1)语法,不适合更复杂的语法结构
- 错误报告相对基础
- 文档和社区支持不如更成熟的解析器生成器
对于更复杂的需求,可以考虑Antlr或Yacc等工具,但对于简单的LL(1)语法解析,Prattle是一个轻量高效的解决方案。