Go - How to create a parser

后端 未结 6 916
醉话见心
醉话见心 2020-12-22 23:16

I want to build a parser but have some problems understanding how to do this.

Sample string I would like to parse

{key1 = value1 | key2 = {key3 = val         


        
6条回答
  •  -上瘾入骨i
    2020-12-22 23:54

    Would you like try to parsec for golang edition? I write a rune(for unicode) fork of goparsec(https://github.com/sanyaade-buildtools/goparsec) what is https://github.com/Dwarfartisan/goparsec .

    Haskell parsec is a power tools for make parser. The first perl6 parser named pugs was written by it. My golang Edition is not simple than yacc, but it is easier than yacc.

    For this example, I wrote code as this:

    parser.go

    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)
        }
    }
    

    RUN

    go run parser.go
    

    OUTPUT

    map[key1:"value1"
      key2:map[key3:10] key4:map[key5:map[key6:value6]]]
    

    This demo include escape, token, string and key/value map. You can create a parser as package or application.

提交回复
热议问题