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]]