123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- package toml
- import (
- "errors"
- "fmt"
- "strconv"
- "strings"
- "github.com/naoina/toml/ast"
- )
- // The parser is generated by github.com/pointlander/peg. To regenerate it, do:
- //
- // go get -u github.com/pointlander/peg
- // go generate .
- //go:generate peg -switch -inline parse.peg
- var errParse = errors.New("invalid TOML syntax")
- // Parse returns an AST representation of TOML.
- // The toplevel is represented by a table.
- func Parse(data []byte) (*ast.Table, error) {
- d := &parseState{p: &tomlParser{Buffer: string(data)}}
- d.init()
- if err := d.parse(); err != nil {
- return nil, err
- }
- return d.p.toml.table, nil
- }
- type parseState struct {
- p *tomlParser
- }
- func (d *parseState) init() {
- d.p.Init()
- d.p.toml.init(d.p.buffer)
- }
- func (d *parseState) parse() error {
- if err := d.p.Parse(); err != nil {
- if err, ok := err.(*parseError); ok {
- return lineError(err.Line(), errParse)
- }
- return err
- }
- return d.execute()
- }
- func (d *parseState) execute() (err error) {
- defer func() {
- if e := recover(); e != nil {
- lerr, ok := e.(*LineError)
- if !ok {
- panic(e)
- }
- err = lerr
- }
- }()
- d.p.Execute()
- return nil
- }
- func (e *parseError) Line() int {
- tokens := []token32{e.max}
- positions, p := make([]int, 2*len(tokens)), 0
- for _, token := range tokens {
- positions[p], p = int(token.begin), p+1
- positions[p], p = int(token.end), p+1
- }
- for _, t := range translatePositions(e.p.buffer, positions) {
- if e.p.line < t.line {
- e.p.line = t.line
- }
- }
- return e.p.line
- }
- type stack struct {
- key string
- table *ast.Table
- }
- type array struct {
- parent *array
- child *array
- current *ast.Array
- line int
- }
- type toml struct {
- table *ast.Table
- line int
- currentTable *ast.Table
- s string
- key string
- val ast.Value
- arr *array
- stack []*stack
- skip bool
- }
- func (p *toml) init(data []rune) {
- p.line = 1
- p.table = p.newTable(ast.TableTypeNormal, "")
- p.table.Position.End = len(data) - 1
- p.table.Data = data[:len(data)-1] // truncate the end_symbol added by PEG parse generator.
- p.currentTable = p.table
- }
- func (p *toml) Error(err error) {
- panic(lineError(p.line, err))
- }
- func (p *tomlParser) SetTime(begin, end int) {
- p.val = &ast.Datetime{
- Position: ast.Position{Begin: begin, End: end},
- Data: p.buffer[begin:end],
- Value: string(p.buffer[begin:end]),
- }
- }
- func (p *tomlParser) SetFloat64(begin, end int) {
- p.val = &ast.Float{
- Position: ast.Position{Begin: begin, End: end},
- Data: p.buffer[begin:end],
- Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
- }
- }
- func (p *tomlParser) SetInt64(begin, end int) {
- p.val = &ast.Integer{
- Position: ast.Position{Begin: begin, End: end},
- Data: p.buffer[begin:end],
- Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
- }
- }
- func (p *tomlParser) SetString(begin, end int) {
- p.val = &ast.String{
- Position: ast.Position{Begin: begin, End: end},
- Data: p.buffer[begin:end],
- Value: p.s,
- }
- p.s = ""
- }
- func (p *tomlParser) SetBool(begin, end int) {
- p.val = &ast.Boolean{
- Position: ast.Position{Begin: begin, End: end},
- Data: p.buffer[begin:end],
- Value: string(p.buffer[begin:end]),
- }
- }
- func (p *tomlParser) StartArray() {
- if p.arr == nil {
- p.arr = &array{line: p.line, current: &ast.Array{}}
- return
- }
- p.arr.child = &array{parent: p.arr, line: p.line, current: &ast.Array{}}
- p.arr = p.arr.child
- }
- func (p *tomlParser) AddArrayVal() {
- if p.arr.current == nil {
- p.arr.current = &ast.Array{}
- }
- p.arr.current.Value = append(p.arr.current.Value, p.val)
- }
- func (p *tomlParser) SetArray(begin, end int) {
- p.arr.current.Position = ast.Position{Begin: begin, End: end}
- p.arr.current.Data = p.buffer[begin:end]
- p.val = p.arr.current
- p.arr = p.arr.parent
- }
- func (p *toml) SetTable(buf []rune, begin, end int) {
- p.setTable(p.table, buf, begin, end)
- }
- func (p *toml) setTable(parent *ast.Table, buf []rune, begin, end int) {
- name := string(buf[begin:end])
- names := splitTableKey(name)
- parent, err := p.lookupTable(parent, names[:len(names)-1])
- if err != nil {
- p.Error(err)
- }
- last := names[len(names)-1]
- tbl := p.newTable(ast.TableTypeNormal, last)
- switch v := parent.Fields[last].(type) {
- case nil:
- parent.Fields[last] = tbl
- case []*ast.Table:
- p.Error(fmt.Errorf("table `%s' is in conflict with array table in line %d", name, v[0].Line))
- case *ast.Table:
- if (v.Position == ast.Position{}) {
- // This table was created as an implicit parent.
- // Replace it with the real defined table.
- tbl.Fields = v.Fields
- parent.Fields[last] = tbl
- } else {
- p.Error(fmt.Errorf("table `%s' is in conflict with table in line %d", name, v.Line))
- }
- case *ast.KeyValue:
- p.Error(fmt.Errorf("table `%s' is in conflict with line %d", name, v.Line))
- default:
- p.Error(fmt.Errorf("BUG: table `%s' is in conflict but it's unknown type `%T'", last, v))
- }
- p.currentTable = tbl
- }
- func (p *toml) newTable(typ ast.TableType, name string) *ast.Table {
- return &ast.Table{
- Line: p.line,
- Name: name,
- Type: typ,
- Fields: make(map[string]interface{}),
- }
- }
- func (p *tomlParser) SetTableString(begin, end int) {
- p.currentTable.Data = p.buffer[begin:end]
- p.currentTable.Position.Begin = begin
- p.currentTable.Position.End = end
- }
- func (p *toml) SetArrayTable(buf []rune, begin, end int) {
- p.setArrayTable(p.table, buf, begin, end)
- }
- func (p *toml) setArrayTable(parent *ast.Table, buf []rune, begin, end int) {
- name := string(buf[begin:end])
- names := splitTableKey(name)
- parent, err := p.lookupTable(parent, names[:len(names)-1])
- if err != nil {
- p.Error(err)
- }
- last := names[len(names)-1]
- tbl := p.newTable(ast.TableTypeArray, last)
- switch v := parent.Fields[last].(type) {
- case nil:
- parent.Fields[last] = []*ast.Table{tbl}
- case []*ast.Table:
- parent.Fields[last] = append(v, tbl)
- case *ast.Table:
- p.Error(fmt.Errorf("array table `%s' is in conflict with table in line %d", name, v.Line))
- case *ast.KeyValue:
- p.Error(fmt.Errorf("array table `%s' is in conflict with line %d", name, v.Line))
- default:
- p.Error(fmt.Errorf("BUG: array table `%s' is in conflict but it's unknown type `%T'", name, v))
- }
- p.currentTable = tbl
- }
- func (p *toml) StartInlineTable() {
- p.skip = false
- p.stack = append(p.stack, &stack{p.key, p.currentTable})
- buf := []rune(p.key)
- if p.arr == nil {
- p.setTable(p.currentTable, buf, 0, len(buf))
- } else {
- p.setArrayTable(p.currentTable, buf, 0, len(buf))
- }
- }
- func (p *toml) EndInlineTable() {
- st := p.stack[len(p.stack)-1]
- p.key, p.currentTable = st.key, st.table
- p.stack[len(p.stack)-1] = nil
- p.stack = p.stack[:len(p.stack)-1]
- p.skip = true
- }
- func (p *toml) AddLineCount(i int) {
- p.line += i
- }
- func (p *toml) SetKey(buf []rune, begin, end int) {
- p.key = string(buf[begin:end])
- }
- func (p *toml) AddKeyValue() {
- if p.skip {
- p.skip = false
- return
- }
- if val, exists := p.currentTable.Fields[p.key]; exists {
- switch v := val.(type) {
- case *ast.Table:
- p.Error(fmt.Errorf("key `%s' is in conflict with table in line %d", p.key, v.Line))
- case *ast.KeyValue:
- p.Error(fmt.Errorf("key `%s' is in conflict with line %xd", p.key, v.Line))
- default:
- p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", p.key, v))
- }
- }
- p.currentTable.Fields[p.key] = &ast.KeyValue{Key: p.key, Value: p.val, Line: p.line}
- }
- func (p *toml) SetBasicString(buf []rune, begin, end int) {
- p.s = p.unquote(string(buf[begin:end]))
- }
- func (p *toml) SetMultilineString() {
- p.s = p.unquote(`"` + escapeReplacer.Replace(strings.TrimLeft(p.s, "\r\n")) + `"`)
- }
- func (p *toml) AddMultilineBasicBody(buf []rune, begin, end int) {
- p.s += string(buf[begin:end])
- }
- func (p *toml) SetLiteralString(buf []rune, begin, end int) {
- p.s = string(buf[begin:end])
- }
- func (p *toml) SetMultilineLiteralString(buf []rune, begin, end int) {
- p.s = strings.TrimLeft(string(buf[begin:end]), "\r\n")
- }
- func (p *toml) unquote(s string) string {
- s, err := strconv.Unquote(s)
- if err != nil {
- p.Error(err)
- }
- return s
- }
- func (p *toml) lookupTable(t *ast.Table, keys []string) (*ast.Table, error) {
- for _, s := range keys {
- val, exists := t.Fields[s]
- if !exists {
- tbl := p.newTable(ast.TableTypeNormal, s)
- t.Fields[s] = tbl
- t = tbl
- continue
- }
- switch v := val.(type) {
- case *ast.Table:
- t = v
- case []*ast.Table:
- t = v[len(v)-1]
- case *ast.KeyValue:
- return nil, fmt.Errorf("key `%s' is in conflict with line %d", s, v.Line)
- default:
- return nil, fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", s, v)
- }
- }
- return t, nil
- }
- func splitTableKey(tk string) []string {
- key := make([]byte, 0, 1)
- keys := make([]string, 0, 1)
- inQuote := false
- for i := 0; i < len(tk); i++ {
- k := tk[i]
- switch {
- case k == tableSeparator && !inQuote:
- keys = append(keys, string(key))
- key = key[:0] // reuse buffer.
- case k == '"':
- inQuote = !inQuote
- case (k == ' ' || k == '\t') && !inQuote:
- // skip.
- default:
- key = append(key, k)
- }
- }
- keys = append(keys, string(key))
- return keys
- }
|