package toml

import (
	"errors"

	"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("parse error")

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