跳到正文

Go

Go 是默认 target。生成器会运行 goimports 格式化输出,initializer 必须提供合法的 package 声明。

最小可运行示例

在空目录中手写下面两个文件。parser.go 由 pegtool 生成,不需要手工创建。

identifier.peg

peg
{
package main

type ParserCustomData struct{}

func ParseIdentifier(input []byte) (string, error) {
    value, err := parse("input.txt", input)
    if err != nil {
        return "", err
    }
    return value.(string), nil
}
}

Start = value:<Identifier> !. { return value }
Identifier = [\pL_] [\pL\p{Nd}_]*

main.go

go
package main

import "fmt"

func main() {
    value, err := ParseIdentifier([]byte("name_世界"))
    if err != nil {
        panic(err)
    }
    fmt.Println(value)
}

生成并运行:

powershell
go mod init example.com/identifier
pegtool -t go -optimize-parser -o parser.go identifier.peg
go run .

输出:

text
name_世界

公开 API 与自定义数据

生成 runtime 的基础符号是包内的 parsenewParseroptionmemoized。上例在 initializer 中把内部 parse 包装成了项目需要的 ParseIdentifier。需要 ParseFileParseReader 或其他签名时,也可以在 initializer 或普通 Go 文件中包装。

即使不需要状态,也必须定义空的 ParserCustomData。需要注入数据时,可以提供项目级 wrapper:

go
func ParseWithData(filename string, input []byte, data *ParserCustomData) (any, error) {
    p := newParser(filename, input)
    p.setCustomData(data)
    return p.parse(g)
}

action 和 predicate 通过 c.data 访问它。此数据不会随 PEG 回溯自动恢复。

Action 与错误

peg
Integer = [0-9]+ {
    value, err := strconv.Atoi(string(c.text))
    if err != nil {
        p.addErr(err)
        return nil
    }
    return value
}

Go action 返回一个值,不使用旧式的 (value, error) 双返回值。面向用户的 no-match 消息可通过错误处理中的 formatter 自定义。

Memoization

Go 的 -optimize-parser 当前仍保留命名规则 memoization,因此两者可以同时使用:

go
type Option = option

func Memoize(enabled bool) Option {
    return memoized(enabled)
}

memo 只适合规则结果在一次 parse 中由输入位置稳定决定的情况。依赖可变 ParserCustomData 的 predicate 应谨慎使用。完整选择方法见性能参数选择

多份文法共享 runtime 时,使用多文件与入口规则中的 Go 同包方案。

基于 BSD 3-Clause License 发布