Go-如何创建解析器
我想构建一个解析器,但在理解如何实现这一点时遇到了一些问题 我要分析的示例字符串Go-如何创建解析器,go,Go,我想构建一个解析器,但在理解如何实现这一点时遇到了一些问题 我要分析的示例字符串 {key1 = value1 | key2 = {key3 = value3} | key4 = {key5 = { key6 = value6 }}} 我希望得到类似嵌套映射的输出 map[key1] = value1 map[key2] = (map[key3] = value3) map[key4] = (map[key5] = (map[key6] = value6)) 这怎么可能呢?我的目标是错误的吗
{key1 = value1 | key2 = {key3 = value3} | key4 = {key5 = { key6 = value6 }}}
我希望得到类似嵌套映射的输出
map[key1] = value1
map[key2] = (map[key3] = value3)
map[key4] = (map[key5] = (map[key6] = value6))
这怎么可能呢?我的目标是错误的吗?编写语法分析器是一个复杂的主题,太大了,一个答案无法涵盖 Rob Pike做了一次精彩的演讲,介绍了如何编写lexer,它是Go中解析器的一半: 您还应查看Go标准库中的解析器代码,以获取有关如何执行此操作的示例: 互联网上也有大量的解析资源。他们可能有其他语言的例子,但这只是翻译语法的问题
我建议阅读递归下降解析,例如,或自上而下解析,例如。使用标准goyacc工具怎么样?这是一个骨架: 梅因
这种特殊的格式与json非常相似。您可以使用以下代码来利用这种相似性:
var txt = `{key1 = "\"value1\"\n" | key2 = { key3 = 10 } | key4 = {key5 = { key6 = value6}}}`
var s scanner.Scanner
s.Init(strings.NewReader(txt))
var b []byte
loop:
for {
switch tok := s.Scan(); tok {
case scanner.EOF:
break loop
case '|':
b = append(b, ',')
case '=':
b = append(b, ':')
case scanner.Ident:
b = append(b, strconv.Quote(s.TokenText())...)
default:
b = append(b, s.TokenText()...)
}
}
var m map[string]interface{}
err := json.Unmarshal(b, &m)
if err != nil {
// handle error
}
fmt.Printf("%#v\n",m)
您想尝试为golang版本解析C吗?我为goparsec的unicode fork编写了一个runef Haskell parsec是一个强大的make解析器工具。第一个名为帕格斯的perl6解析器是由它编写的。我的golang版本不比yacc简单,但比yacc容易 对于本例,我编写的代码如下: 语法分析器 跑 输出
这个演示包括转义、令牌、字符串和键/值映射。您可以将解析器创建为包或应用程序。如果您愿意将输入转换为标准JSON格式,为什么要在有Go库为您完成繁重工作时创建解析器 给定以下输入文件/Users/lex/dev/go/data/jsoncfgo/fritjof.json: 输入文件 代码示例 输出 注释 jsoncfgo可以处理任何级别的嵌套JSON对象 详情请参阅: 请注意,目前处于2016年第四季度的测试阶段,于2017年第一季度发布 先前通过运行“go tool yacc”可用的yacc工具已被删除。 从Go 1.7开始,Go编译器不再使用它 它已移动到“工具”存储库,现在可在上获得
谢谢大家的回答。我还没有时间去研究它,但很快就会,稍后会更新。再次感谢!当前的第一个命令是:$go-tool yacc-o main.go main.yEasier如果您从ebnf->yacc执行,那么flavor with lex,例如,这是haskells parsec的端口吗?那太酷了\o/。我不知道存在一个用于Go的parsec。它提供大致相同的功能,还是只是parsec的一小部分?是的,它是。相对长度单位。。。等我回来吧。这几年我遇到了一些问题,比如抑郁症,新工作等等。顺其自然,我写了一个改进版的goparsec,名为goparsec2。GOC2更干净、更快速。但我还没有编写文档,测试显示了如何使用它。请接受我对文件不在的歉意。我相信我会写的。Go parsec是haskell parsec的模拟版。它支持一些parsec组件和状态定义。所以可以使用它创建规则解析器,比如
$ go tool yacc -o main.go main.y && go run main.go
map[key4:map[key5:map[key6:value6]] key1:value1 key2:map[key3:value3]]
$
var txt = `{key1 = "\"value1\"\n" | key2 = { key3 = 10 } | key4 = {key5 = { key6 = value6}}}`
var s scanner.Scanner
s.Init(strings.NewReader(txt))
var b []byte
loop:
for {
switch tok := s.Scan(); tok {
case scanner.EOF:
break loop
case '|':
b = append(b, ',')
case '=':
b = append(b, ':')
case scanner.Ident:
b = append(b, strconv.Quote(s.TokenText())...)
default:
b = append(b, s.TokenText()...)
}
}
var m map[string]interface{}
err := json.Unmarshal(b, &m)
if err != nil {
// handle error
}
fmt.Printf("%#v\n",m)
package main
import (
"fmt"
psc "github.com/Dwarfartisan/goparsec"
)
type kv struct {
key string
value interface{}
}
var tchar = psc.NoneOf("|{}= ")
func escaped(st psc.ParseState) (interface{}, error) {
_, err := psc.Try(psc.Rune('\\'))(st)
if err == nil {
r, err := psc.AnyRune(st)
if err == nil {
switch r.(rune) {
case 't':
return '\t', nil
case '"':
return '"', nil
case 'n':
return '\n', nil
case '\\':
return '\\', nil
default:
return nil, st.Trap("Unknown escape \\%r", r)
}
} else {
return nil, err
}
} else {
return psc.NoneOf("\"")(st)
}
}
var token = psc.Either(
psc.Between(psc.Rune('"'), psc.Rune('"'),
psc.Try(psc.Bind(psc.Many1(escaped), psc.ReturnString))),
psc.Bind(psc.Many1(tchar), psc.ReturnString))
// rune with skip spaces
func syms(r rune) psc.Parser {
return func(st psc.ParseState) (interface{}, error) {
_, err := psc.Bind_(psc.Bind_(psc.Many(psc.Space), psc.Rune(r)), psc.Many(psc.Space))(st)
if err == nil {
return r, nil
} else {
return nil, err
}
}
}
var lbracket = syms('{')
var rbracket = syms('}')
var eql = syms('=')
var vbar = syms('|')
func pair(st psc.ParseState) (interface{}, error) {
left, err := token(st)
if err != nil {
return nil, err
}
right, err := psc.Bind_(eql, psc.Either(psc.Try(token), mapExpr))(st)
if err != nil {
return nil, err
}
return kv{left.(string), right}, nil
}
func pairs(st psc.ParseState) (interface{}, error) {
return psc.SepBy1(pair, vbar)(st)
}
func mapExpr(st psc.ParseState) (interface{}, error) {
p, err := psc.Try(psc.Between(lbracket, rbracket, pair))(st)
if err == nil {
return p, nil
}
ps, err := psc.Between(lbracket, rbracket, pairs)(st)
if err == nil {
return ps, nil
} else {
return nil, err
}
}
func makeMap(data interface{}) interface{} {
ret := make(map[string]interface{})
switch val := data.(type) {
case kv:
ret[val.key] = makeMap(val.value)
case string:
return data
case []interface{}:
for _, item := range val {
it := item.(kv)
ret[it.key] = makeMap(it.value)
}
}
return ret
}
func main() {
input := `{key1 = "\"value1\"\n" | key2 = { key3 = 10 } | key4 = {key5 = { key6 = value6}}}`
st := psc.MemoryParseState(input)
ret, err := mapExpr(makeMap(st))
if err == nil {
fmt.Println(ret)
} else {
fmt.Println(err)
}
}
go run parser.go
map[key1:"value1"
key2:map[key3:10] key4:map[key5:map[key6:value6]]]
{
"key1": "value1",
"key2" : {
"key3": "value3"
},
"key4": {
"key5": {
"key6": "value6"
}
}
}
package main
import (
"fmt"
"log"
"github.com/l3x/jsoncfgo"
)
func main() {
configPath := "/Users/lex/dev/go/data/jsoncfgo/fritjof.json"
cfg, err := jsoncfgo.ReadFile(configPath)
if err != nil {
log.Fatal(err.Error()) // Handle error here
}
key1 := cfg.RequiredString("key1")
fmt.Printf("key1: %v\n\n", key1)
key2 := cfg.OptionalObject("key2")
fmt.Printf("key2: %v\n\n", key2)
key4 := cfg.OptionalObject("key4")
fmt.Printf("key4: %v\n\n", key4)
if err := cfg.Validate(); err != nil {
defer log.Fatalf("ERROR - Invalid config file...\n%v", err)
return
}
}
key1: value1
key2: map[key3:value3]
key4: map[key5:map[key6:value6]]