我想构建一个解析器,但在理解如何执行此操作时遇到一些问题。
我要解析的示例字符串
{key1 = value1 | key2 = {key3 = value3} | key4 = {key5 = { key6 = value6 }}}
最好我想获得类似于嵌套地图的输出
map[key1] = value1
map[key2] = (map[key3] = value3)
map[key4] = (map[key5] = (map[key6] = value6))
怎么可以这样做?我的目标是错误的吗?
答案 0 :(得分:34)
编写解析器是一个复杂的主题,太大而无法覆盖在单个答案中。
Rob Pike发表了一篇精彩的演讲,在Go中写了一个词法分析器(这是解析器的一半):http://www.youtube.com/watch?v=HxaD_trXwRE
你也应该看看,例如Go标准库中的解析器代码,以获取有关如何执行此操作的示例:http://golang.org/src/pkg/go/parser/parser.go
在互联网上解析也有很多资源。他们可能在其他语言中有示例,但这只是将语法转换为Go。
我建议您阅读递归下降解析(例如http://www.cs.binghamton.edu/~zdu/parsdemo/recintro.html)或自上而下解析(例如http://javascript.crockford.com/tdop/tdop.html,http://effbot.org/zone/simple-top-down-parsing.htm)。
答案 1 :(得分:31)
使用标准goyacc工具怎么样?这是一个骨架:
%{
package main
import (
"fmt"
"log"
)
%}
%union{
tok int
val interface{}
pair struct{key, val interface{}}
pairs map[interface{}]interface{}
}
%token KEY
%token VAL
%type <val> KEY VAL
%type <pair> pair
%type <pairs> pairs
%%
goal:
'{' pairs '}'
{
yylex.(*lex).m = $2
}
pairs:
pair
{
$$ = map[interface{}]interface{}{$1.key: $1.val}
}
| pairs '|' pair
{
$$[$3.key] = $3.val
}
pair:
KEY '=' VAL
{
$$.key, $$.val = $1, $3
}
| KEY '=' '{' pairs '}'
{
$$.key, $$.val = $1, $4
}
%%
type token struct {
tok int
val interface{}
}
type lex struct {
tokens []token
m map[interface{}]interface{}
}
func (l *lex) Lex(lval *yySymType) int {
if len(l.tokens) == 0 {
return 0
}
v := l.tokens[0]
l.tokens = l.tokens[1:]
lval.val = v.val
return v.tok
}
func (l *lex) Error(e string) {
log.Fatal(e)
}
func main() {
l := &lex{
// {key1 = value1 | key2 = {key3 = value3} | key4 = {key5 = { key6 = value6 }}}
[]token{
{'{', ""},
{KEY, "key1"},
{'=', ""},
{VAL, "value1"},
{'|', ""},
{KEY, "key2"},
{'=', ""},
{'{', ""},
{KEY, "key3"},
{'=', ""},
{VAL, "value3"},
{'}', ""},
{'|', ""},
{KEY, "key4"},
{'=', ""},
{'{', ""},
{KEY, "key5"},
{'=', ""},
{'{', ""},
{KEY, "key6"},
{'=', ""},
{VAL, "value6"},
{'}', ""},
{'}', ""},
{'}', ""},
},
map[interface{}]interface{}{},
}
yyParse(l)
fmt.Println(l.m)
}
$ 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]]
$
答案 2 :(得分:2)
这种特殊格式与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)
答案 3 :(得分:1)
你想尝试使用golang版的parsec吗?我写了一个符文(对于unicode)goparsec(https://github.com/sanyaade-buildtools/goparsec)的分支是什么https://github.com/Dwarfartisan/goparsec。
Haskell parsec是make解析器的强大工具。名为pugs的第一个perl6解析器是由它编写的。我的golang版并不比yacc简单,但它比yacc更容易。
对于这个例子,我编写了代码:
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]]]
此演示包括转义,令牌,字符串和键/值映射。您可以将解析器创建为包或应用程序。
答案 4 :(得分:1)
如果您愿意将输入转换为标准JSON格式,为什么在Go库为您做繁重的工作时创建解析器呢?
给出以下输入文件(/Users/lex/dev/go/data/jsoncfgo/fritjof.json):
输入文件
{
"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]]
备注强>
jsoncfgo可以处理任何级别的嵌套JSON对象。
详见: