Linear array storage for AST
This commit is contained in:
+100
-177
@@ -2,182 +2,103 @@ package ast
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Kind int
|
// Iterator starts uninitialized, you need to call Next() first.
|
||||||
|
//
|
||||||
const (
|
// For example:
|
||||||
// meta
|
//
|
||||||
Invalid Kind = iota
|
// it := n.Children()
|
||||||
Comment
|
// for it.Next() {
|
||||||
Key
|
// it.Node()
|
||||||
|
// }
|
||||||
// top level structures
|
type Iterator interface {
|
||||||
Table
|
// Next moves the iterator forward and returns true if points to a node, false
|
||||||
ArrayTable
|
// otherwise.
|
||||||
KeyValue
|
Next() bool
|
||||||
|
// Node returns a copy of the node pointed at by the iterator.
|
||||||
// containers values
|
Node() Node
|
||||||
Array
|
|
||||||
InlineTable
|
|
||||||
|
|
||||||
// values
|
|
||||||
String
|
|
||||||
Bool
|
|
||||||
Float
|
|
||||||
Integer
|
|
||||||
LocalDate
|
|
||||||
LocalDateTime
|
|
||||||
DateTime
|
|
||||||
Time
|
|
||||||
)
|
|
||||||
|
|
||||||
func (k Kind) String() string {
|
|
||||||
switch k {
|
|
||||||
case Invalid:
|
|
||||||
return "Invalid"
|
|
||||||
case Comment:
|
|
||||||
return "Comment"
|
|
||||||
case Key:
|
|
||||||
return "Key"
|
|
||||||
case Table:
|
|
||||||
return "Table"
|
|
||||||
case ArrayTable:
|
|
||||||
return "ArrayTable"
|
|
||||||
case KeyValue:
|
|
||||||
return "KeyValue"
|
|
||||||
case Array:
|
|
||||||
return "Array"
|
|
||||||
case InlineTable:
|
|
||||||
return "InlineTable"
|
|
||||||
case String:
|
|
||||||
return "String"
|
|
||||||
case Bool:
|
|
||||||
return "Bool"
|
|
||||||
case Float:
|
|
||||||
return "Float"
|
|
||||||
case Integer:
|
|
||||||
return "Integer"
|
|
||||||
case LocalDate:
|
|
||||||
return "LocalDate"
|
|
||||||
case LocalDateTime:
|
|
||||||
return "LocalDateTime"
|
|
||||||
case DateTime:
|
|
||||||
return "DateTime"
|
|
||||||
case Time:
|
|
||||||
return "Time"
|
|
||||||
}
|
|
||||||
panic(fmt.Errorf("Kind.String() not implemented for '%d'", k))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Root []Node
|
type chainIterator struct {
|
||||||
|
started bool
|
||||||
// Dot returns a dot representation of the AST for debugging.
|
node Node
|
||||||
func (r Root) Sdot() string {
|
|
||||||
type edge struct {
|
|
||||||
from int
|
|
||||||
childIdx int
|
|
||||||
to int
|
|
||||||
}
|
|
||||||
|
|
||||||
var nodes []Node
|
|
||||||
var edges []edge // indexes into nodes
|
|
||||||
|
|
||||||
nodes = append(nodes, Node{
|
|
||||||
Kind: Invalid,
|
|
||||||
Data: []byte(`ROOT`),
|
|
||||||
Children: r,
|
|
||||||
})
|
|
||||||
|
|
||||||
var processNode func(int, int, *Node)
|
|
||||||
processNode = func(parentIdx int, childIdx int, node *Node) {
|
|
||||||
idx := len(nodes)
|
|
||||||
nodes = append(nodes, *node)
|
|
||||||
edges = append(edges, edge{
|
|
||||||
from: parentIdx,
|
|
||||||
childIdx: childIdx,
|
|
||||||
to: idx,
|
|
||||||
})
|
|
||||||
|
|
||||||
for i, c := range node.Children {
|
|
||||||
processNode(idx, i, &c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, n := range r {
|
|
||||||
processNode(0, i, &n)
|
|
||||||
}
|
|
||||||
|
|
||||||
var b strings.Builder
|
|
||||||
|
|
||||||
b.WriteString("digraph tree {\n")
|
|
||||||
b.WriteString("\tnode [shape=record];\n")
|
|
||||||
|
|
||||||
for i, node := range nodes {
|
|
||||||
label := ""
|
|
||||||
attrs := map[string]string{}
|
|
||||||
|
|
||||||
if i == 0 {
|
|
||||||
var ports []string
|
|
||||||
for i := 0; i < len(node.Children); i++ {
|
|
||||||
ports = append(ports, fmt.Sprintf("<f%d> %d", i, i))
|
|
||||||
}
|
|
||||||
joinedPorts := strings.Join(ports, "|")
|
|
||||||
label = fmt.Sprintf("{ROOT|{%s}}", joinedPorts)
|
|
||||||
} else {
|
|
||||||
fields := []string{node.Kind.String()}
|
|
||||||
if len(node.Data) > 0 {
|
|
||||||
fields = append(fields, string(node.Data))
|
|
||||||
}
|
|
||||||
|
|
||||||
var ports []string
|
|
||||||
for i := 0; i < len(node.Children); i++ {
|
|
||||||
ports = append(ports, fmt.Sprintf("<f%d> %d", i, i))
|
|
||||||
}
|
|
||||||
joinedPorts := strings.Join(ports, "|")
|
|
||||||
|
|
||||||
joinedFields := strings.Join(fields, "|")
|
|
||||||
label = fmt.Sprintf("{{%s}", joinedFields)
|
|
||||||
if len(ports) > 0 {
|
|
||||||
label += fmt.Sprintf("|{%s}", joinedPorts)
|
|
||||||
}
|
|
||||||
label += "}"
|
|
||||||
if node.Kind == Invalid {
|
|
||||||
attrs["style"] = "filled"
|
|
||||||
attrs["fillcolor"] = "red"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(&b, "\tnode%d [label=\"%s\"", i, label)
|
|
||||||
for k, v := range attrs {
|
|
||||||
_, _ = fmt.Fprintf(&b, ", %s=\"%s\"", k, v)
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(&b, "];\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteString("\n")
|
|
||||||
|
|
||||||
for _, e := range edges {
|
|
||||||
_, _ = fmt.Fprintf(&b, "\tnode%d:f%d -> node%d;\n", e.from, e.childIdx, e.to)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteString("}")
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *chainIterator) Next() bool {
|
||||||
|
if !c.started {
|
||||||
|
c.started = true
|
||||||
|
} else if c.node.Valid() {
|
||||||
|
c.node = c.node.Next()
|
||||||
|
}
|
||||||
|
return c.node.Valid()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chainIterator) Node() Node {
|
||||||
|
return c.node
|
||||||
|
}
|
||||||
|
|
||||||
|
type Root struct {
|
||||||
|
nodes []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) Iterator() Iterator {
|
||||||
|
it := &chainIterator{}
|
||||||
|
if len(r.nodes) > 0 {
|
||||||
|
it.node = r.nodes[0]
|
||||||
|
}
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) Reset() {
|
||||||
|
r.nodes = r.nodes[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) at(idx int) Node {
|
||||||
|
// TODO: unsafe to point to the node directly
|
||||||
|
return r.nodes[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrays have one child per element in the array.
|
||||||
|
// InlineTables have one child per key-value pair in the table.
|
||||||
|
// KeyValues have at least two children. The first one is the value. The
|
||||||
|
// rest make a potentially dotted key.
|
||||||
|
// Table and Array table have one child per element of the key they
|
||||||
|
// represent (same as KeyValue, but without the last node being the value).
|
||||||
|
// children []Node
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Kind Kind
|
Kind Kind
|
||||||
Data []byte // Raw bytes from the input
|
Data []byte // Raw bytes from the input
|
||||||
|
|
||||||
// Arrays have one child per element in the array.
|
// next idx (in the root array). 0 if last of the collection.
|
||||||
// InlineTables have one child per key-value pair in the table.
|
next int
|
||||||
// KeyValues have at least two children. The last one is the value. The
|
// child idx (in the root array). 0 if no child.
|
||||||
// rest make a potentially dotted key.
|
child int
|
||||||
// Table and Array table have one child per element of the key they
|
// pointer to the root array
|
||||||
// represent (same as KeyValue, but without the last node being the value).
|
root *Root
|
||||||
Children []Node
|
}
|
||||||
|
|
||||||
|
// Next returns a copy of the next node, or an invalid Node if there is no
|
||||||
|
// next node.
|
||||||
|
func (n Node) Next() Node {
|
||||||
|
if n.next <= 0 {
|
||||||
|
return NoNode
|
||||||
|
}
|
||||||
|
return n.root.at(n.next)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Child returns a copy of the first child node of this node. Other children
|
||||||
|
// can be accessed calling Next on the first child.
|
||||||
|
// Returns an invalid Node if there is none.
|
||||||
|
func (n Node) Child() Node {
|
||||||
|
if n.child <= 0 {
|
||||||
|
return NoNode
|
||||||
|
}
|
||||||
|
return n.root.at(n.child)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n Node) Valid() bool {
|
||||||
|
return n.Kind != Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
var NoNode = Node{}
|
var NoNode = Node{}
|
||||||
@@ -186,15 +107,16 @@ var NoNode = Node{}
|
|||||||
// otherwise.
|
// otherwise.
|
||||||
// They are guaranteed to be all be of the Kind Key. A simple key would return
|
// They are guaranteed to be all be of the Kind Key. A simple key would return
|
||||||
// just one element.
|
// just one element.
|
||||||
func (n *Node) Key() []Node {
|
func (n *Node) Key() Iterator {
|
||||||
switch n.Kind {
|
switch n.Kind {
|
||||||
case KeyValue:
|
case KeyValue:
|
||||||
if len(n.Children) < 2 {
|
value := n.Child()
|
||||||
panic(fmt.Errorf("KeyValue should have at least two children, not %d", len(n.Children)))
|
if !value.Valid() {
|
||||||
|
panic(fmt.Errorf("KeyValue should have at least two children"))
|
||||||
}
|
}
|
||||||
return n.Children[:len(n.Children)-1]
|
return &chainIterator{node: value.Next()}
|
||||||
case Table, ArrayTable:
|
case Table, ArrayTable:
|
||||||
return n.Children
|
return &chainIterator{node: n.Child()}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("Key() is not supported on a %s", n.Kind))
|
panic(fmt.Errorf("Key() is not supported on a %s", n.Kind))
|
||||||
}
|
}
|
||||||
@@ -203,15 +125,16 @@ func (n *Node) Key() []Node {
|
|||||||
// Value returns a pointer to the value node of a KeyValue.
|
// Value returns a pointer to the value node of a KeyValue.
|
||||||
// Guaranteed to be non-nil.
|
// Guaranteed to be non-nil.
|
||||||
// Panics if not called on a KeyValue node, or if the Children are malformed.
|
// Panics if not called on a KeyValue node, or if the Children are malformed.
|
||||||
func (n *Node) Value() *Node {
|
func (n Node) Value() Node {
|
||||||
assertKind(KeyValue, n)
|
assertKind(KeyValue, n)
|
||||||
if len(n.Children) < 2 {
|
return n.Child()
|
||||||
panic(fmt.Errorf("KeyValue should have at least two children, not %d", len(n.Children)))
|
|
||||||
}
|
|
||||||
return &n.Children[len(n.Children)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertKind(k Kind, n *Node) {
|
func (n Node) Children() Iterator {
|
||||||
|
return &chainIterator{node: n.Child()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertKind(k Kind, n Node) {
|
||||||
if n.Kind != k {
|
if n.Kind != k {
|
||||||
panic(fmt.Errorf("method was expecting a %s, not a %s", k, n.Kind))
|
panic(fmt.Errorf("method was expecting a %s, not a %s", k, n.Kind))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
nodes []Node
|
||||||
|
lastIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Reference struct {
|
||||||
|
idx int
|
||||||
|
set bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Reference) Valid() bool {
|
||||||
|
return r.set
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Finish() *Root {
|
||||||
|
r := &Root{
|
||||||
|
nodes: b.nodes,
|
||||||
|
}
|
||||||
|
b.nodes = nil
|
||||||
|
|
||||||
|
for i := range r.nodes {
|
||||||
|
r.nodes[i].root = r
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Push(n Node) Reference {
|
||||||
|
b.lastIdx = len(b.nodes)
|
||||||
|
b.nodes = append(b.nodes, n)
|
||||||
|
return Reference{
|
||||||
|
idx: b.lastIdx,
|
||||||
|
set: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) PushAndChain(n Node) Reference {
|
||||||
|
newIdx := len(b.nodes)
|
||||||
|
b.nodes = append(b.nodes, n)
|
||||||
|
if b.lastIdx >= 0 {
|
||||||
|
b.nodes[b.lastIdx].next = newIdx
|
||||||
|
}
|
||||||
|
b.lastIdx = newIdx
|
||||||
|
return Reference{
|
||||||
|
idx: b.lastIdx,
|
||||||
|
set: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) AttachChild(parent Reference, child Reference) {
|
||||||
|
b.nodes[parent.idx].child = child.idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Chain(from Reference, to Reference) {
|
||||||
|
b.nodes[from.idx].next = to.idx
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Kind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// meta
|
||||||
|
Invalid Kind = iota
|
||||||
|
Comment
|
||||||
|
Key
|
||||||
|
|
||||||
|
// top level structures
|
||||||
|
Table
|
||||||
|
ArrayTable
|
||||||
|
KeyValue
|
||||||
|
|
||||||
|
// containers values
|
||||||
|
Array
|
||||||
|
InlineTable
|
||||||
|
|
||||||
|
// values
|
||||||
|
String
|
||||||
|
Bool
|
||||||
|
Float
|
||||||
|
Integer
|
||||||
|
LocalDate
|
||||||
|
LocalDateTime
|
||||||
|
DateTime
|
||||||
|
Time
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k Kind) String() string {
|
||||||
|
switch k {
|
||||||
|
case Invalid:
|
||||||
|
return "Invalid"
|
||||||
|
case Comment:
|
||||||
|
return "Comment"
|
||||||
|
case Key:
|
||||||
|
return "Key"
|
||||||
|
case Table:
|
||||||
|
return "Table"
|
||||||
|
case ArrayTable:
|
||||||
|
return "ArrayTable"
|
||||||
|
case KeyValue:
|
||||||
|
return "KeyValue"
|
||||||
|
case Array:
|
||||||
|
return "Array"
|
||||||
|
case InlineTable:
|
||||||
|
return "InlineTable"
|
||||||
|
case String:
|
||||||
|
return "String"
|
||||||
|
case Bool:
|
||||||
|
return "Bool"
|
||||||
|
case Float:
|
||||||
|
return "Float"
|
||||||
|
case Integer:
|
||||||
|
return "Integer"
|
||||||
|
case LocalDate:
|
||||||
|
return "LocalDate"
|
||||||
|
case LocalDateTime:
|
||||||
|
return "LocalDateTime"
|
||||||
|
case DateTime:
|
||||||
|
return "DateTime"
|
||||||
|
case Time:
|
||||||
|
return "Time"
|
||||||
|
}
|
||||||
|
panic(fmt.Errorf("Kind.String() not implemented for '%d'", k))
|
||||||
|
}
|
||||||
@@ -10,11 +10,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
tree ast.Root
|
builder ast.Builder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parse(b []byte) error {
|
func (p *parser) parse(b []byte) error {
|
||||||
b, err := p.parseExpression(b)
|
last, b, err := p.parseExpression(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -24,10 +24,15 @@ func (p *parser) parse(b []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err = p.parseExpression(b)
|
var next ast.Reference
|
||||||
|
next, b, err = p.parseExpression(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if next.Valid() {
|
||||||
|
p.builder.Chain(last, next)
|
||||||
|
last = next
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -43,49 +48,48 @@ func (p *parser) parseNewline(b []byte) ([]byte, error) {
|
|||||||
return nil, fmt.Errorf("expected newline but got %#U", b[0])
|
return nil, fmt.Errorf("expected newline but got %#U", b[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseExpression(b []byte) ([]byte, error) {
|
func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) {
|
||||||
//expression = ws [ comment ]
|
//expression = ws [ comment ]
|
||||||
//expression =/ ws keyval ws [ comment ]
|
//expression =/ ws keyval ws [ comment ]
|
||||||
//expression =/ ws table ws [ comment ]
|
//expression =/ ws table ws [ comment ]
|
||||||
|
|
||||||
|
var ref ast.Reference
|
||||||
|
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return b, nil
|
return ref, b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if b[0] == '#' {
|
if b[0] == '#' {
|
||||||
_, rest, err := scanComment(b)
|
_, rest, err := scanComment(b)
|
||||||
return rest, err
|
return ref, rest, err
|
||||||
}
|
}
|
||||||
if b[0] == '\n' || b[0] == '\r' {
|
if b[0] == '\n' || b[0] == '\r' {
|
||||||
return b, nil
|
return ref, b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var node ast.Node
|
|
||||||
if b[0] == '[' {
|
if b[0] == '[' {
|
||||||
node, b, err = p.parseTable(b)
|
ref, b, err = p.parseTable(b)
|
||||||
} else {
|
} else {
|
||||||
node, b, err = p.parseKeyval(b)
|
ref, b, err = p.parseKeyval(b)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ref, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.tree = append(p.tree, node)
|
|
||||||
|
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
|
|
||||||
if len(b) > 0 && b[0] == '#' {
|
if len(b) > 0 && b[0] == '#' {
|
||||||
_, rest, err := scanComment(b)
|
_, rest, err := scanComment(b)
|
||||||
return rest, err
|
return ref, rest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b, nil
|
return ref, b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseTable(b []byte) (ast.Node, []byte, error) {
|
func (p *parser) parseTable(b []byte) (ast.Reference, []byte, error) {
|
||||||
//table = std-table / array-table
|
//table = std-table / array-table
|
||||||
if len(b) > 1 && b[1] == '[' {
|
if len(b) > 1 && b[1] == '[' {
|
||||||
return p.parseArrayTable(b)
|
return p.parseArrayTable(b)
|
||||||
@@ -93,90 +97,95 @@ func (p *parser) parseTable(b []byte) (ast.Node, []byte, error) {
|
|||||||
return p.parseStdTable(b)
|
return p.parseStdTable(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseArrayTable(b []byte) (ast.Node, []byte, error) {
|
func (p *parser) parseArrayTable(b []byte) (ast.Reference, []byte, error) {
|
||||||
//array-table = array-table-open key array-table-close
|
//array-table = array-table-open key array-table-close
|
||||||
//array-table-open = %x5B.5B ws ; [[ Double left square bracket
|
//array-table-open = %x5B.5B ws ; [[ Double left square bracket
|
||||||
//array-table-close = ws %x5D.5D ; ]] Double right square bracket
|
//array-table-close = ws %x5D.5D ; ]] Double right square bracket
|
||||||
|
|
||||||
node := ast.Node{
|
ref := p.builder.Push(ast.Node{
|
||||||
Kind: ast.ArrayTable,
|
Kind: ast.ArrayTable,
|
||||||
}
|
})
|
||||||
|
|
||||||
b = b[2:]
|
b = b[2:]
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
k, b, err := p.parseKey(b)
|
k, b, err := p.parseKey(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return node, nil, err
|
return ref, nil, err
|
||||||
}
|
}
|
||||||
node.Children = k
|
p.builder.AttachChild(ref, k)
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
b, err = expect(']', b)
|
b, err = expect(']', b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return node, nil, err
|
return ref, nil, err
|
||||||
}
|
}
|
||||||
b, err = expect(']', b)
|
b, err = expect(']', b)
|
||||||
return node, b, err
|
return ref, b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseStdTable(b []byte) (ast.Node, []byte, error) {
|
func (p *parser) parseStdTable(b []byte) (ast.Reference, []byte, error) {
|
||||||
//std-table = std-table-open key std-table-close
|
//std-table = std-table-open key std-table-close
|
||||||
//std-table-open = %x5B ws ; [ Left square bracket
|
//std-table-open = %x5B ws ; [ Left square bracket
|
||||||
//std-table-close = ws %x5D ; ] Right square bracket
|
//std-table-close = ws %x5D ; ] Right square bracket
|
||||||
|
|
||||||
node := ast.Node{
|
ref := p.builder.Push(ast.Node{
|
||||||
Kind: ast.Table,
|
Kind: ast.Table,
|
||||||
}
|
})
|
||||||
|
|
||||||
b = b[1:]
|
b = b[1:]
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
key, b, err := p.parseKey(b)
|
key, b, err := p.parseKey(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ast.NoNode, nil, err
|
return ref, nil, err
|
||||||
}
|
}
|
||||||
node.Children = key
|
|
||||||
|
p.builder.AttachChild(ref, key)
|
||||||
|
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
|
|
||||||
b, err = expect(']', b)
|
b, err = expect(']', b)
|
||||||
|
|
||||||
return node, b, err
|
return ref, b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseKeyval(b []byte) (ast.Node, []byte, error) {
|
func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) {
|
||||||
//keyval = key keyval-sep val
|
//keyval = key keyval-sep val
|
||||||
|
|
||||||
node := ast.Node{
|
ref := p.builder.Push(ast.Node{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
}
|
})
|
||||||
|
|
||||||
key, b, err := p.parseKey(b)
|
key, b, err := p.parseKey(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ast.NoNode, nil, err
|
return ast.Reference{}, nil, err
|
||||||
}
|
}
|
||||||
node.Children = append(node.Children, key...)
|
|
||||||
|
|
||||||
//keyval-sep = ws %x3D ws ; =
|
//keyval-sep = ws %x3D ws ; =
|
||||||
|
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
b, err = expect('=', b)
|
b, err = expect('=', b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ast.NoNode, nil, err
|
return ast.Reference{}, nil, err
|
||||||
}
|
}
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
|
|
||||||
valNode, b, err := p.parseVal(b)
|
valRef, b, err := p.parseVal(b)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
node.Children = append(node.Children, valNode)
|
return ref, b, err
|
||||||
}
|
}
|
||||||
return node, b, err
|
p.builder.Chain(valRef, key)
|
||||||
|
p.builder.AttachChild(ref, valRef)
|
||||||
|
|
||||||
|
return ref, b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseVal(b []byte) (ast.Node, []byte, error) {
|
func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) {
|
||||||
// val = string / boolean / array / inline-table / date-time / float / integer
|
// val = string / boolean / array / inline-table / date-time / float / integer
|
||||||
|
var ref ast.Reference
|
||||||
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return ast.NoNode, nil, fmt.Errorf("expected value, not eof")
|
return ref, nil, fmt.Errorf("expected value, not eof")
|
||||||
}
|
}
|
||||||
|
|
||||||
node := ast.Node{}
|
|
||||||
var err error
|
var err error
|
||||||
c := b[0]
|
c := b[0]
|
||||||
|
|
||||||
@@ -189,10 +198,12 @@ func (p *parser) parseVal(b []byte) (ast.Node, []byte, error) {
|
|||||||
v, b, err = p.parseBasicString(b)
|
v, b, err = p.parseBasicString(b)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
node.Kind = ast.String
|
ref = p.builder.Push(ast.Node{
|
||||||
node.Data = v
|
Kind: ast.String,
|
||||||
|
Data: v,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return node, b, err
|
return ref, b, err
|
||||||
case '\'':
|
case '\'':
|
||||||
var v []byte
|
var v []byte
|
||||||
if scanFollowsMultilineLiteralStringDelimiter(b) {
|
if scanFollowsMultilineLiteralStringDelimiter(b) {
|
||||||
@@ -201,35 +212,36 @@ func (p *parser) parseVal(b []byte) (ast.Node, []byte, error) {
|
|||||||
v, b, err = p.parseLiteralString(b)
|
v, b, err = p.parseLiteralString(b)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
node.Kind = ast.String
|
ref = p.builder.Push(ast.Node{
|
||||||
node.Data = v
|
Kind: ast.String,
|
||||||
|
Data: v,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return node, b, err
|
return ref, b, err
|
||||||
case 't':
|
case 't':
|
||||||
if !scanFollowsTrue(b) {
|
if !scanFollowsTrue(b) {
|
||||||
return node, nil, fmt.Errorf("expected 'true'")
|
return ref, nil, fmt.Errorf("expected 'true'")
|
||||||
}
|
}
|
||||||
node.Kind = ast.Bool
|
ref = p.builder.Push(ast.Node{
|
||||||
node.Data = b[:4]
|
Kind: ast.Bool,
|
||||||
return node, b[4:], nil
|
Data: b[:4],
|
||||||
|
})
|
||||||
|
return ref, b[4:], nil
|
||||||
case 'f':
|
case 'f':
|
||||||
if !scanFollowsFalse(b) {
|
if !scanFollowsFalse(b) {
|
||||||
return node, nil, fmt.Errorf("expected 'false'")
|
return ast.Reference{}, nil, fmt.Errorf("expected 'false'")
|
||||||
}
|
}
|
||||||
node.Kind = ast.Bool
|
ref = p.builder.Push(ast.Node{
|
||||||
node.Data = b[:5]
|
Kind: ast.Bool,
|
||||||
return node, b[5:], nil
|
Data: b[:5],
|
||||||
|
})
|
||||||
|
return ref, b[5:], nil
|
||||||
case '[':
|
case '[':
|
||||||
node.Kind = ast.Array
|
return p.parseValArray(b)
|
||||||
b, err := p.parseValArray(&node, b)
|
|
||||||
return node, b, err
|
|
||||||
case '{':
|
case '{':
|
||||||
node.Kind = ast.InlineTable
|
return p.parseInlineTable(b)
|
||||||
b, err := p.parseInlineTable(&node, b)
|
|
||||||
return node, b, err
|
|
||||||
default:
|
default:
|
||||||
b, err = p.parseIntOrFloatOrDateTime(&node, b)
|
return p.parseIntOrFloatOrDateTime(b)
|
||||||
return node, b, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,16 +253,22 @@ func (p *parser) parseLiteralString(b []byte) ([]byte, []byte, error) {
|
|||||||
return v[1 : len(v)-1], rest, nil
|
return v[1 : len(v)-1], rest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseInlineTable(node *ast.Node, b []byte) ([]byte, error) {
|
func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) {
|
||||||
//inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close
|
//inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close
|
||||||
//inline-table-open = %x7B ws ; {
|
//inline-table-open = %x7B ws ; {
|
||||||
//inline-table-close = ws %x7D ; }
|
//inline-table-close = ws %x7D ; }
|
||||||
//inline-table-sep = ws %x2C ws ; , Comma
|
//inline-table-sep = ws %x2C ws ; , Comma
|
||||||
//inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ]
|
//inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ]
|
||||||
|
|
||||||
b = b[1:]
|
parent := p.builder.Push(ast.Node{
|
||||||
|
Kind: ast.InlineTable,
|
||||||
|
})
|
||||||
|
|
||||||
first := true
|
first := true
|
||||||
|
var child ast.Reference
|
||||||
|
|
||||||
|
b = b[1:]
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
@@ -261,24 +279,32 @@ func (p *parser) parseInlineTable(node *ast.Node, b []byte) ([]byte, error) {
|
|||||||
if !first {
|
if !first {
|
||||||
b, err = expect(',', b)
|
b, err = expect(',', b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return parent, nil, err
|
||||||
}
|
}
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
}
|
}
|
||||||
var kv ast.Node
|
var kv ast.Reference
|
||||||
kv, b, err = p.parseKeyval(b)
|
kv, b, err = p.parseKeyval(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return parent, nil, err
|
||||||
}
|
}
|
||||||
node.Children = append(node.Children, kv)
|
|
||||||
|
if first {
|
||||||
|
p.builder.AttachChild(parent, kv)
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
p.builder.Chain(child, kv)
|
||||||
|
}
|
||||||
|
child = kv
|
||||||
|
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return expect('}', b)
|
rest, err := expect('}', b)
|
||||||
|
return parent, rest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseValArray(node *ast.Node, b []byte) ([]byte, error) {
|
func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) {
|
||||||
//array = array-open [ array-values ] ws-comment-newline array-close
|
//array = array-open [ array-values ] ws-comment-newline array-close
|
||||||
//array-open = %x5B ; [
|
//array-open = %x5B ; [
|
||||||
//array-close = %x5D ; ]
|
//array-close = %x5D ; ]
|
||||||
@@ -289,16 +315,22 @@ func (p *parser) parseValArray(node *ast.Node, b []byte) ([]byte, error) {
|
|||||||
|
|
||||||
b = b[1:]
|
b = b[1:]
|
||||||
|
|
||||||
|
parent := p.builder.Push(ast.Node{
|
||||||
|
Kind: ast.Array,
|
||||||
|
})
|
||||||
|
|
||||||
first := true
|
first := true
|
||||||
|
var lastChild ast.Reference
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
b, err = p.parseOptionalWhitespaceCommentNewline(b)
|
b, err = p.parseOptionalWhitespaceCommentNewline(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return parent, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return nil, unexpectedCharacter{b: b}
|
return parent, nil, unexpectedCharacter{b: b}
|
||||||
}
|
}
|
||||||
|
|
||||||
if b[0] == ']' {
|
if b[0] == ']' {
|
||||||
@@ -306,29 +338,38 @@ func (p *parser) parseValArray(node *ast.Node, b []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
if b[0] == ',' {
|
if b[0] == ',' {
|
||||||
if first {
|
if first {
|
||||||
return nil, fmt.Errorf("array cannot start with comma")
|
return parent, nil, fmt.Errorf("array cannot start with comma")
|
||||||
}
|
}
|
||||||
b = b[1:]
|
b = b[1:]
|
||||||
b, err = p.parseOptionalWhitespaceCommentNewline(b)
|
b, err = p.parseOptionalWhitespaceCommentNewline(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return parent, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var valueNode ast.Node
|
var valueRef ast.Reference
|
||||||
valueNode, b, err = p.parseVal(b)
|
valueRef, b, err = p.parseVal(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return parent, nil, err
|
||||||
}
|
}
|
||||||
node.Children = append(node.Children, valueNode)
|
|
||||||
|
if first {
|
||||||
|
p.builder.AttachChild(parent, valueRef)
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
p.builder.Chain(lastChild, valueRef)
|
||||||
|
}
|
||||||
|
lastChild = valueRef
|
||||||
|
|
||||||
b, err = p.parseOptionalWhitespaceCommentNewline(b)
|
b, err = p.parseOptionalWhitespaceCommentNewline(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return parent, nil, err
|
||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return expect(']', b)
|
rest, err := expect(']', b)
|
||||||
|
return parent, rest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) {
|
func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) {
|
||||||
@@ -454,7 +495,7 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, error) {
|
|||||||
return builder.Bytes(), rest, nil
|
return builder.Bytes(), rest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseKey(b []byte) ([]ast.Node, []byte, error) {
|
func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) {
|
||||||
//key = simple-key / dotted-key
|
//key = simple-key / dotted-key
|
||||||
//simple-key = quoted-key / unquoted-key
|
//simple-key = quoted-key / unquoted-key
|
||||||
//
|
//
|
||||||
@@ -464,14 +505,12 @@ func (p *parser) parseKey(b []byte) ([]ast.Node, []byte, error) {
|
|||||||
//
|
//
|
||||||
//dot-sep = ws %x2E ws ; . Period
|
//dot-sep = ws %x2E ws ; . Period
|
||||||
|
|
||||||
var nodes []ast.Node
|
|
||||||
|
|
||||||
key, b, err := p.parseSimpleKey(b)
|
key, b, err := p.parseSimpleKey(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nodes, nil, err
|
return ast.Reference{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes = append(nodes, ast.Node{
|
ref := p.builder.Push(ast.Node{
|
||||||
Kind: ast.Key,
|
Kind: ast.Key,
|
||||||
Data: key,
|
Data: key,
|
||||||
})
|
})
|
||||||
@@ -481,14 +520,14 @@ func (p *parser) parseKey(b []byte) ([]ast.Node, []byte, error) {
|
|||||||
if len(b) > 0 && b[0] == '.' {
|
if len(b) > 0 && b[0] == '.' {
|
||||||
b, err = expect('.', b)
|
b, err = expect('.', b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nodes, nil, err
|
return ref, nil, err
|
||||||
}
|
}
|
||||||
b = p.parseWhitespace(b)
|
b = p.parseWhitespace(b)
|
||||||
key, b, err = p.parseSimpleKey(b)
|
key, b, err = p.parseSimpleKey(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nodes, nil, err
|
return ref, nil, err
|
||||||
}
|
}
|
||||||
nodes = append(nodes, ast.Node{
|
p.builder.PushAndChain(ast.Node{
|
||||||
Kind: ast.Key,
|
Kind: ast.Key,
|
||||||
Data: key,
|
Data: key,
|
||||||
})
|
})
|
||||||
@@ -497,7 +536,7 @@ func (p *parser) parseKey(b []byte) ([]ast.Node, []byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes, b, nil
|
return ref, b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseSimpleKey(b []byte) (key, rest []byte, err error) {
|
func (p *parser) parseSimpleKey(b []byte) (key, rest []byte, err error) {
|
||||||
@@ -609,28 +648,30 @@ func (p *parser) parseWhitespace(b []byte) []byte {
|
|||||||
return rest
|
return rest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseIntOrFloatOrDateTime(node *ast.Node, b []byte) ([]byte, error) {
|
func (p *parser) parseIntOrFloatOrDateTime(b []byte) (ast.Reference, []byte, error) {
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case 'i':
|
case 'i':
|
||||||
if !scanFollowsInf(b) {
|
if !scanFollowsInf(b) {
|
||||||
return nil, fmt.Errorf("expected 'inf'")
|
return ast.Reference{}, nil, fmt.Errorf("expected 'inf'")
|
||||||
}
|
}
|
||||||
node.Kind = ast.Float
|
return p.builder.Push(ast.Node{
|
||||||
node.Data = b[:3]
|
Kind: ast.Float,
|
||||||
return b[3:], nil
|
Data: b[:3],
|
||||||
|
}), b[3:], nil
|
||||||
case 'n':
|
case 'n':
|
||||||
if !scanFollowsNan(b) {
|
if !scanFollowsNan(b) {
|
||||||
return nil, fmt.Errorf("expected 'nan'")
|
return ast.Reference{}, nil, fmt.Errorf("expected 'nan'")
|
||||||
}
|
}
|
||||||
node.Kind = ast.Float
|
return p.builder.Push(ast.Node{
|
||||||
node.Data = b[:3]
|
Kind: ast.Float,
|
||||||
return b[3:], nil
|
Data: b[:3],
|
||||||
|
}), b[3:], nil
|
||||||
case '+', '-':
|
case '+', '-':
|
||||||
return p.scanIntOrFloat(node, b)
|
return p.scanIntOrFloat(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) < 3 {
|
if len(b) < 3 {
|
||||||
return p.scanIntOrFloat(node, b)
|
return p.scanIntOrFloat(b)
|
||||||
}
|
}
|
||||||
s := 5
|
s := 5
|
||||||
if len(b) < s {
|
if len(b) < s {
|
||||||
@@ -641,10 +682,10 @@ func (p *parser) parseIntOrFloatOrDateTime(node *ast.Node, b []byte) ([]byte, er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if idx == 2 && c == ':' || (idx == 4 && c == '-') {
|
if idx == 2 && c == ':' || (idx == 4 && c == '-') {
|
||||||
return p.scanDateTime(node, b)
|
return p.scanDateTime(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.scanIntOrFloat(node, b)
|
return p.scanIntOrFloat(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func digitsToInt(b []byte) int {
|
func digitsToInt(b []byte) int {
|
||||||
@@ -656,7 +697,7 @@ func digitsToInt(b []byte) int {
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) scanDateTime(node *ast.Node, b []byte) ([]byte, error) {
|
func (p *parser) scanDateTime(b []byte) (ast.Reference, []byte, error) {
|
||||||
// scans for contiguous characters in [0-9T:Z.+-], and up to one space if
|
// scans for contiguous characters in [0-9T:Z.+-], and up to one space if
|
||||||
// followed by a digit.
|
// followed by a digit.
|
||||||
|
|
||||||
@@ -686,22 +727,25 @@ func (p *parser) scanDateTime(node *ast.Node, b []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var kind ast.Kind
|
||||||
|
|
||||||
if hasTime {
|
if hasTime {
|
||||||
if hasTz {
|
if hasTz {
|
||||||
node.Kind = ast.DateTime
|
kind = ast.DateTime
|
||||||
} else {
|
} else {
|
||||||
node.Kind = ast.LocalDateTime
|
kind = ast.LocalDateTime
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if hasTz {
|
if hasTz {
|
||||||
return nil, fmt.Errorf("possible DateTime cannot have a timezone but no time component")
|
return ast.Reference{}, nil, fmt.Errorf("possible DateTime cannot have a timezone but no time component")
|
||||||
}
|
}
|
||||||
node.Kind = ast.LocalDate
|
kind = ast.LocalDate
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Data = b[:i]
|
return p.builder.Push(ast.Node{
|
||||||
|
Kind: kind,
|
||||||
return b[i:], nil
|
Data: b[:i],
|
||||||
|
}), b[i:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseDateTime(b []byte) ([]byte, error) {
|
func (p *parser) parseDateTime(b []byte) ([]byte, error) {
|
||||||
@@ -964,7 +1008,7 @@ func (p *parser) parseTime(b []byte) ([]byte, error) {
|
|||||||
return b[idx:], nil
|
return b[idx:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) scanIntOrFloat(node *ast.Node, b []byte) ([]byte, error) {
|
func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) {
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
if len(b) > 2 && b[0] == '0' {
|
if len(b) > 2 && b[0] == '0' {
|
||||||
@@ -989,9 +1033,10 @@ func (p *parser) scanIntOrFloat(node *ast.Node, b []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Kind = ast.Integer
|
return p.builder.Push(ast.Node{
|
||||||
node.Data = b[:i]
|
Kind: ast.Integer,
|
||||||
return b[i:], nil
|
Data: b[:i],
|
||||||
|
}), b[i:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
isFloat := false
|
isFloat := false
|
||||||
@@ -1010,31 +1055,36 @@ func (p *parser) scanIntOrFloat(node *ast.Node, b []byte) ([]byte, error) {
|
|||||||
|
|
||||||
if c == 'i' {
|
if c == 'i' {
|
||||||
if scanFollowsInf(b[i:]) {
|
if scanFollowsInf(b[i:]) {
|
||||||
node.Kind = ast.Float
|
return p.builder.Push(ast.Node{
|
||||||
node.Data = b[:i+3]
|
Kind: ast.Float,
|
||||||
return b[i+3:], nil
|
Data: b[:i+3],
|
||||||
|
}), b[i+3:], nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unexpected character i while scanning for a number")
|
return ast.Reference{}, nil, fmt.Errorf("unexpected character i while scanning for a number")
|
||||||
}
|
}
|
||||||
if c == 'n' {
|
if c == 'n' {
|
||||||
if scanFollowsNan(b[i:]) {
|
if scanFollowsNan(b[i:]) {
|
||||||
node.Kind = ast.Float
|
return p.builder.Push(ast.Node{
|
||||||
node.Data = b[:i+3]
|
Kind: ast.Float,
|
||||||
return b[i+3:], nil
|
Data: b[:i+3],
|
||||||
|
}), b[i+3:], nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unexpected character n while scanning for a number")
|
return ast.Reference{}, nil, fmt.Errorf("unexpected character n while scanning for a number")
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kind := ast.Integer
|
||||||
|
|
||||||
if isFloat {
|
if isFloat {
|
||||||
node.Kind = ast.Float
|
kind = ast.Float
|
||||||
} else {
|
|
||||||
node.Kind = ast.Integer
|
|
||||||
}
|
}
|
||||||
node.Data = b[:i]
|
|
||||||
return b[i:], nil
|
return p.builder.Push(ast.Node{
|
||||||
|
Kind: kind,
|
||||||
|
Data: b[:i],
|
||||||
|
}), b[i:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDigit(r byte) bool {
|
func isDigit(r byte) bool {
|
||||||
|
|||||||
+134
-50
@@ -125,44 +125,128 @@ func TestParser_AST_Numbers(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := ast.Root{
|
expected := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{Kind: ast.Key, Data: []byte(`A`)},
|
|
||||||
{Kind: e.kind, Data: []byte(e.input)},
|
{Kind: e.kind, Data: []byte(e.input)},
|
||||||
|
{Kind: ast.Key, Data: []byte(`A`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Equal(t, expected, p.tree)
|
compareAST(t, expected, p.builder.Finish())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type astRoot []astNode
|
||||||
|
type astNode struct {
|
||||||
|
Kind ast.Kind
|
||||||
|
Data []byte
|
||||||
|
Children []astNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareAST(t *testing.T, expected astRoot, actual *ast.Root) {
|
||||||
|
it := actual.Iterator()
|
||||||
|
compareIterator(t, expected, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareIterator(t *testing.T, expected []astNode, actual ast.Iterator) {
|
||||||
|
idx := 0
|
||||||
|
|
||||||
|
for actual.Next() {
|
||||||
|
n := actual.Node()
|
||||||
|
|
||||||
|
if idx >= len(expected) {
|
||||||
|
t.Fatal("extra child in actual tree")
|
||||||
|
}
|
||||||
|
e := expected[idx]
|
||||||
|
|
||||||
|
require.Equal(t, e.Kind, n.Kind)
|
||||||
|
require.Equal(t, e.Data, n.Data)
|
||||||
|
|
||||||
|
compareIterator(t, e.Children, n.Children())
|
||||||
|
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx < len(expected) {
|
||||||
|
t.Fatal("missing children in actual", "idx =", idx, "expected =", len(expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r astRoot) toOrig() *ast.Root {
|
||||||
|
builder := &ast.Builder{}
|
||||||
|
|
||||||
|
var last ast.Reference
|
||||||
|
|
||||||
|
for i, n := range r {
|
||||||
|
ref := builder.Push(ast.Node{
|
||||||
|
Kind: n.Kind,
|
||||||
|
Data: n.Data,
|
||||||
|
})
|
||||||
|
|
||||||
|
if i > 0 {
|
||||||
|
builder.Chain(last, ref)
|
||||||
|
}
|
||||||
|
last = ref
|
||||||
|
|
||||||
|
if len(n.Children) > 0 {
|
||||||
|
c := childrenToOrig(builder, n.Children)
|
||||||
|
builder.AttachChild(ref, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
func childrenToOrig(b *ast.Builder, nodes []astNode) ast.Reference {
|
||||||
|
var first ast.Reference
|
||||||
|
var last ast.Reference
|
||||||
|
for i, n := range nodes {
|
||||||
|
ref := b.Push(ast.Node{
|
||||||
|
Kind: n.Kind,
|
||||||
|
Data: n.Data,
|
||||||
|
})
|
||||||
|
if i == 0 {
|
||||||
|
first = ref
|
||||||
|
} else {
|
||||||
|
b.Chain(last, ref)
|
||||||
|
}
|
||||||
|
last = ref
|
||||||
|
|
||||||
|
if len(n.Children) > 0 {
|
||||||
|
c := childrenToOrig(b, n.Children)
|
||||||
|
b.AttachChild(ref, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
|
||||||
func TestParser_AST(t *testing.T) {
|
func TestParser_AST(t *testing.T) {
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
ast ast.Root
|
ast astRoot
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "simple string assignment",
|
desc: "simple string assignment",
|
||||||
input: `A = "hello"`,
|
input: `A = "hello"`,
|
||||||
ast: ast.Root{
|
ast: astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`A`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`A`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -170,18 +254,18 @@ func TestParser_AST(t *testing.T) {
|
|||||||
{
|
{
|
||||||
desc: "simple bool assignment",
|
desc: "simple bool assignment",
|
||||||
input: `A = true`,
|
input: `A = true`,
|
||||||
ast: ast.Root{
|
ast: astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`A`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.Bool,
|
Kind: ast.Bool,
|
||||||
Data: []byte(`true`),
|
Data: []byte(`true`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`A`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -189,24 +273,20 @@ func TestParser_AST(t *testing.T) {
|
|||||||
{
|
{
|
||||||
desc: "array of strings",
|
desc: "array of strings",
|
||||||
input: `A = ["hello", ["world", "again"]]`,
|
input: `A = ["hello", ["world", "again"]]`,
|
||||||
ast: ast.Root{
|
ast: astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`A`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.Array,
|
Kind: ast.Array,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Kind: ast.Array,
|
Kind: ast.Array,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`world`),
|
Data: []byte(`world`),
|
||||||
@@ -219,6 +299,10 @@ func TestParser_AST(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`A`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -226,17 +310,13 @@ func TestParser_AST(t *testing.T) {
|
|||||||
{
|
{
|
||||||
desc: "array of arrays of strings",
|
desc: "array of arrays of strings",
|
||||||
input: `A = ["hello", "world"]`,
|
input: `A = ["hello", "world"]`,
|
||||||
ast: ast.Root{
|
ast: astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`A`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.Array,
|
Kind: ast.Array,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
@@ -247,6 +327,10 @@ func TestParser_AST(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`A`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -254,33 +338,33 @@ func TestParser_AST(t *testing.T) {
|
|||||||
{
|
{
|
||||||
desc: "inline table",
|
desc: "inline table",
|
||||||
input: `name = { first = "Tom", last = "Preston-Werner" }`,
|
input: `name = { first = "Tom", last = "Preston-Werner" }`,
|
||||||
ast: ast.Root{
|
ast: astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`name`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.InlineTable,
|
Kind: ast.InlineTable,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{Kind: ast.Key, Data: []byte(`first`)},
|
|
||||||
{Kind: ast.String, Data: []byte(`Tom`)},
|
{Kind: ast.String, Data: []byte(`Tom`)},
|
||||||
|
{Kind: ast.Key, Data: []byte(`first`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{Kind: ast.Key, Data: []byte(`last`)},
|
|
||||||
{Kind: ast.String, Data: []byte(`Preston-Werner`)},
|
{Kind: ast.String, Data: []byte(`Preston-Werner`)},
|
||||||
|
{Kind: ast.Key, Data: []byte(`last`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`name`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -295,7 +379,7 @@ func TestParser_AST(t *testing.T) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, e.ast, p.tree)
|
compareAST(t, e.ast, p.builder.Finish())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-30
@@ -17,7 +17,7 @@ func Unmarshal(data []byte, v interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
return d.fromAst(p.tree, v)
|
return d.fromAst(p.builder.Finish(), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type decoder struct {
|
type decoder struct {
|
||||||
@@ -41,7 +41,7 @@ func (d *decoder) arrayIndex(append bool, v reflect.Value) int {
|
|||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) fromAst(tree ast.Root, v interface{}) error {
|
func (d *decoder) fromAst(tree *ast.Root, v interface{}) error {
|
||||||
r := reflect.ValueOf(v)
|
r := reflect.ValueOf(v)
|
||||||
if r.Kind() != reflect.Ptr {
|
if r.Kind() != reflect.Ptr {
|
||||||
return fmt.Errorf("need to target a pointer, not %s", r.Kind())
|
return fmt.Errorf("need to target a pointer, not %s", r.Kind())
|
||||||
@@ -54,14 +54,17 @@ func (d *decoder) fromAst(tree ast.Root, v interface{}) error {
|
|||||||
var skipUntilTable bool
|
var skipUntilTable bool
|
||||||
var root target = valueTarget(r.Elem())
|
var root target = valueTarget(r.Elem())
|
||||||
current := root
|
current := root
|
||||||
for _, node := range tree {
|
|
||||||
|
it := tree.Iterator()
|
||||||
|
for it.Next() {
|
||||||
|
node := it.Node()
|
||||||
var found bool
|
var found bool
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
case ast.KeyValue:
|
case ast.KeyValue:
|
||||||
if skipUntilTable {
|
if skipUntilTable {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = d.unmarshalKeyValue(current, &node)
|
err = d.unmarshalKeyValue(current, node)
|
||||||
found = true
|
found = true
|
||||||
case ast.Table:
|
case ast.Table:
|
||||||
current, found, err = d.scopeWithKey(root, node.Key())
|
current, found, err = d.scopeWithKey(root, node.Key())
|
||||||
@@ -91,10 +94,12 @@ func (d *decoder) fromAst(tree ast.Root, v interface{}) error {
|
|||||||
//
|
//
|
||||||
// When encountering slices, it should always use its last element, and error
|
// When encountering slices, it should always use its last element, and error
|
||||||
// if the slice does not have any.
|
// if the slice does not have any.
|
||||||
func (d *decoder) scopeWithKey(x target, key []ast.Node) (target, bool, error) {
|
func (d *decoder) scopeWithKey(x target, key ast.Iterator) (target, bool, error) {
|
||||||
var err error
|
var err error
|
||||||
found := true
|
found := true
|
||||||
for _, n := range key {
|
|
||||||
|
for key.Next() {
|
||||||
|
n := key.Node()
|
||||||
x, found, err = d.scopeTableTarget(false, x, string(n.Data))
|
x, found, err = d.scopeTableTarget(false, x, string(n.Data))
|
||||||
if err != nil || !found {
|
if err != nil || !found {
|
||||||
return nil, found, err
|
return nil, found, err
|
||||||
@@ -108,18 +113,21 @@ func (d *decoder) scopeWithKey(x target, key []ast.Node) (target, bool, error) {
|
|||||||
//
|
//
|
||||||
// It is the same as scopeWithKey, but when scoping the last part of the key
|
// It is the same as scopeWithKey, but when scoping the last part of the key
|
||||||
// it creates a new element in the array instead of using the last one.
|
// it creates a new element in the array instead of using the last one.
|
||||||
func (d *decoder) scopeWithArrayTable(x target, key []ast.Node) (target, bool, error) {
|
func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool, error) {
|
||||||
var err error
|
var err error
|
||||||
found := true
|
found := true
|
||||||
if len(key) > 1 {
|
for key.Next() {
|
||||||
for _, n := range key[:len(key)-1] {
|
n := key.Node()
|
||||||
x, found, err = d.scopeTableTarget(false, x, string(n.Data))
|
if !n.Next().Valid() { // want to stop at one before last
|
||||||
if err != nil || !found {
|
break
|
||||||
return nil, found, err
|
}
|
||||||
}
|
x, found, err = d.scopeTableTarget(false, x, string(n.Data))
|
||||||
|
if err != nil || !found {
|
||||||
|
return nil, found, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x, found, err = d.scopeTableTarget(false, x, string(key[len(key)-1].Data))
|
n := key.Node()
|
||||||
|
x, found, err = d.scopeTableTarget(false, x, string(n.Data))
|
||||||
if err != nil || !found {
|
if err != nil || !found {
|
||||||
return x, found, err
|
return x, found, err
|
||||||
}
|
}
|
||||||
@@ -152,7 +160,7 @@ func (d *decoder) scopeWithArrayTable(x target, key []ast.Node) (target, bool, e
|
|||||||
return x, found, err
|
return x, found, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) unmarshalKeyValue(x target, node *ast.Node) error {
|
func (d *decoder) unmarshalKeyValue(x target, node ast.Node) error {
|
||||||
assertNode(ast.KeyValue, node)
|
assertNode(ast.KeyValue, node)
|
||||||
|
|
||||||
x, found, err := d.scopeWithKey(x, node.Key())
|
x, found, err := d.scopeWithKey(x, node.Key())
|
||||||
@@ -170,7 +178,7 @@ func (d *decoder) unmarshalKeyValue(x target, node *ast.Node) error {
|
|||||||
|
|
||||||
var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
|
var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
|
||||||
|
|
||||||
func tryTextUnmarshaler(x target, node *ast.Node) (bool, error) {
|
func tryTextUnmarshaler(x target, node ast.Node) (bool, error) {
|
||||||
v := x.get()
|
v := x.get()
|
||||||
|
|
||||||
if v.Kind() != reflect.Struct {
|
if v.Kind() != reflect.Struct {
|
||||||
@@ -185,7 +193,7 @@ func tryTextUnmarshaler(x target, node *ast.Node) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) unmarshalValue(x target, node *ast.Node) error {
|
func (d *decoder) unmarshalValue(x target, node ast.Node) error {
|
||||||
v := x.get()
|
v := x.get()
|
||||||
if v.Kind() == reflect.Ptr {
|
if v.Kind() == reflect.Ptr {
|
||||||
if !v.Elem().IsValid() {
|
if !v.Elem().IsValid() {
|
||||||
@@ -225,7 +233,7 @@ func (d *decoder) unmarshalValue(x target, node *ast.Node) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalLocalDateTime(x target, node *ast.Node) error {
|
func unmarshalLocalDateTime(x target, node ast.Node) error {
|
||||||
assertNode(ast.LocalDateTime, node)
|
assertNode(ast.LocalDateTime, node)
|
||||||
v, rest, err := parseLocalDateTime(node.Data)
|
v, rest, err := parseLocalDateTime(node.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -237,7 +245,7 @@ func unmarshalLocalDateTime(x target, node *ast.Node) error {
|
|||||||
return setLocalDateTime(x, v)
|
return setLocalDateTime(x, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalDateTime(x target, node *ast.Node) error {
|
func unmarshalDateTime(x target, node ast.Node) error {
|
||||||
assertNode(ast.DateTime, node)
|
assertNode(ast.DateTime, node)
|
||||||
v, err := parseDateTime(node.Data)
|
v, err := parseDateTime(node.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -254,18 +262,18 @@ func setDateTime(x target, v time.Time) error {
|
|||||||
return x.set(reflect.ValueOf(v))
|
return x.set(reflect.ValueOf(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalString(x target, node *ast.Node) error {
|
func unmarshalString(x target, node ast.Node) error {
|
||||||
assertNode(ast.String, node)
|
assertNode(ast.String, node)
|
||||||
return setString(x, string(node.Data))
|
return setString(x, string(node.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalBool(x target, node *ast.Node) error {
|
func unmarshalBool(x target, node ast.Node) error {
|
||||||
assertNode(ast.Bool, node)
|
assertNode(ast.Bool, node)
|
||||||
v := node.Data[0] == 't'
|
v := node.Data[0] == 't'
|
||||||
return setBool(x, v)
|
return setBool(x, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalInteger(x target, node *ast.Node) error {
|
func unmarshalInteger(x target, node ast.Node) error {
|
||||||
assertNode(ast.Integer, node)
|
assertNode(ast.Integer, node)
|
||||||
v, err := parseInteger(node.Data)
|
v, err := parseInteger(node.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -274,7 +282,7 @@ func unmarshalInteger(x target, node *ast.Node) error {
|
|||||||
return setInt64(x, v)
|
return setInt64(x, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalFloat(x target, node *ast.Node) error {
|
func unmarshalFloat(x target, node ast.Node) error {
|
||||||
assertNode(ast.Float, node)
|
assertNode(ast.Float, node)
|
||||||
v, err := parseFloat(node.Data)
|
v, err := parseFloat(node.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -283,11 +291,13 @@ func unmarshalFloat(x target, node *ast.Node) error {
|
|||||||
return setFloat64(x, v)
|
return setFloat64(x, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) unmarshalInlineTable(x target, node *ast.Node) error {
|
func (d *decoder) unmarshalInlineTable(x target, node ast.Node) error {
|
||||||
assertNode(ast.InlineTable, node)
|
assertNode(ast.InlineTable, node)
|
||||||
|
|
||||||
for _, kv := range node.Children {
|
it := node.Children()
|
||||||
err := d.unmarshalKeyValue(x, &kv)
|
for it.Next() {
|
||||||
|
n := it.Node()
|
||||||
|
err := d.unmarshalKeyValue(x, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -295,7 +305,7 @@ func (d *decoder) unmarshalInlineTable(x target, node *ast.Node) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) unmarshalArray(x target, node *ast.Node) error {
|
func (d *decoder) unmarshalArray(x target, node ast.Node) error {
|
||||||
assertNode(ast.Array, node)
|
assertNode(ast.Array, node)
|
||||||
|
|
||||||
err := ensureValueIndexable(x)
|
err := ensureValueIndexable(x)
|
||||||
@@ -303,7 +313,10 @@ func (d *decoder) unmarshalArray(x target, node *ast.Node) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, n := range node.Children {
|
it := node.Children()
|
||||||
|
idx := 0
|
||||||
|
for it.Next() {
|
||||||
|
n := it.Node()
|
||||||
v, err := elementAt(x, idx)
|
v, err := elementAt(x, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -313,15 +326,16 @@ func (d *decoder) unmarshalArray(x target, node *ast.Node) error {
|
|||||||
// mimic encoding/json
|
// mimic encoding/json
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
err = d.unmarshalValue(v, &n)
|
err = d.unmarshalValue(v, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
idx++
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertNode(expected ast.Kind, node *ast.Node) {
|
func assertNode(expected ast.Kind, node ast.Node) {
|
||||||
if node.Kind != expected {
|
if node.Kind != expected {
|
||||||
panic(fmt.Errorf("expected node of kind %s, not %s", expected, node.Kind))
|
panic(fmt.Errorf("expected node of kind %s, not %s", expected, node.Kind))
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-74
@@ -660,18 +660,18 @@ B = "data"`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFromAst_KV(t *testing.T) {
|
func TestFromAst_KV(t *testing.T) {
|
||||||
root := ast.Root{
|
root := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`Foo`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`Foo`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -682,44 +682,44 @@ func TestFromAst_KV(t *testing.T) {
|
|||||||
|
|
||||||
x := Doc{}
|
x := Doc{}
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
err := d.fromAst(root, &x)
|
err := d.fromAst(root.toOrig(), &x)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, Doc{Foo: "hello"}, x)
|
assert.Equal(t, Doc{Foo: "hello"}, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromAst_Table(t *testing.T) {
|
func TestFromAst_Table(t *testing.T) {
|
||||||
t.Run("one level table on struct", func(t *testing.T) {
|
t.Run("one level table on struct", func(t *testing.T) {
|
||||||
root := ast.Root{
|
root := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.Table,
|
Kind: ast.Table,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{Kind: ast.Key, Data: []byte(`Level1`)},
|
{Kind: ast.Key, Data: []byte(`Level1`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`A`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
},
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
ast.Node{
|
|
||||||
Kind: ast.KeyValue,
|
|
||||||
Children: []ast.Node{
|
|
||||||
{
|
{
|
||||||
Kind: ast.Key,
|
Kind: ast.Key,
|
||||||
Data: []byte(`B`),
|
Data: []byte(`A`),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
astNode{
|
||||||
|
Kind: ast.KeyValue,
|
||||||
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`world`),
|
Data: []byte(`world`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`B`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -735,7 +735,7 @@ func TestFromAst_Table(t *testing.T) {
|
|||||||
|
|
||||||
x := Doc{}
|
x := Doc{}
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
err := d.fromAst(root, &x)
|
err := d.fromAst(root.toOrig(), &x)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, Doc{
|
assert.Equal(t, Doc{
|
||||||
Level1: Level1{
|
Level1: Level1{
|
||||||
@@ -745,25 +745,25 @@ func TestFromAst_Table(t *testing.T) {
|
|||||||
}, x)
|
}, x)
|
||||||
})
|
})
|
||||||
t.Run("one level table on struct", func(t *testing.T) {
|
t.Run("one level table on struct", func(t *testing.T) {
|
||||||
root := ast.Root{
|
root := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.Table,
|
Kind: ast.Table,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{Kind: ast.Key, Data: []byte(`A`)},
|
{Kind: ast.Key, Data: []byte(`A`)},
|
||||||
{Kind: ast.Key, Data: []byte(`B`)},
|
{Kind: ast.Key, Data: []byte(`B`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`C`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`value`),
|
Data: []byte(`value`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`C`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -782,7 +782,7 @@ func TestFromAst_Table(t *testing.T) {
|
|||||||
|
|
||||||
x := Doc{}
|
x := Doc{}
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
err := d.fromAst(root, &x)
|
err := d.fromAst(root.toOrig(), &x)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, Doc{
|
assert.Equal(t, Doc{
|
||||||
A: A{B: B{C: "value"}},
|
A: A{B: B{C: "value"}},
|
||||||
@@ -792,32 +792,33 @@ func TestFromAst_Table(t *testing.T) {
|
|||||||
|
|
||||||
func TestFromAst_InlineTable(t *testing.T) {
|
func TestFromAst_InlineTable(t *testing.T) {
|
||||||
t.Run("one level of strings", func(t *testing.T) {
|
t.Run("one level of strings", func(t *testing.T) {
|
||||||
root := ast.Root{
|
root := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`Name`)},
|
|
||||||
{
|
{
|
||||||
Kind: ast.InlineTable,
|
Kind: ast.InlineTable,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{Kind: ast.Key, Data: []byte(`First`)},
|
|
||||||
{Kind: ast.String, Data: []byte(`Tom`)},
|
{Kind: ast.String, Data: []byte(`Tom`)},
|
||||||
|
{Kind: ast.Key, Data: []byte(`First`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{Kind: ast.Key, Data: []byte(`Last`)},
|
|
||||||
{Kind: ast.String, Data: []byte(`Preston-Werner`)},
|
{Kind: ast.String, Data: []byte(`Preston-Werner`)},
|
||||||
|
{Kind: ast.Key, Data: []byte(`Last`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`Name`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -833,7 +834,7 @@ func TestFromAst_InlineTable(t *testing.T) {
|
|||||||
|
|
||||||
x := Doc{}
|
x := Doc{}
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
err := d.fromAst(root, &x)
|
err := d.fromAst(root.toOrig(), &x)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, Doc{
|
assert.Equal(t, Doc{
|
||||||
Name: Name{
|
Name: Name{
|
||||||
@@ -847,17 +848,13 @@ func TestFromAst_InlineTable(t *testing.T) {
|
|||||||
|
|
||||||
func TestFromAst_Slice(t *testing.T) {
|
func TestFromAst_Slice(t *testing.T) {
|
||||||
t.Run("slice of string", func(t *testing.T) {
|
t.Run("slice of string", func(t *testing.T) {
|
||||||
root := ast.Root{
|
root := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`Foo`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.Array,
|
Kind: ast.Array,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
@@ -868,6 +865,10 @@ func TestFromAst_Slice(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`Foo`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -878,23 +879,19 @@ func TestFromAst_Slice(t *testing.T) {
|
|||||||
|
|
||||||
x := Doc{}
|
x := Doc{}
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
err := d.fromAst(root, &x)
|
err := d.fromAst(root.toOrig(), &x)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, Doc{Foo: []string{"hello", "world"}}, x)
|
assert.Equal(t, Doc{Foo: []string{"hello", "world"}}, x)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("slice of interfaces for strings", func(t *testing.T) {
|
t.Run("slice of interfaces for strings", func(t *testing.T) {
|
||||||
root := ast.Root{
|
root := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`Foo`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.Array,
|
Kind: ast.Array,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
@@ -905,6 +902,10 @@ func TestFromAst_Slice(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`Foo`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -915,30 +916,26 @@ func TestFromAst_Slice(t *testing.T) {
|
|||||||
|
|
||||||
x := Doc{}
|
x := Doc{}
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
err := d.fromAst(root, &x)
|
err := d.fromAst(root.toOrig(), &x)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, Doc{Foo: []interface{}{"hello", "world"}}, x)
|
assert.Equal(t, Doc{Foo: []interface{}{"hello", "world"}}, x)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("slice of interfaces with slices", func(t *testing.T) {
|
t.Run("slice of interfaces with slices", func(t *testing.T) {
|
||||||
root := ast.Root{
|
root := astRoot{
|
||||||
ast.Node{
|
astNode{
|
||||||
Kind: ast.KeyValue,
|
Kind: ast.KeyValue,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
|
||||||
Kind: ast.Key,
|
|
||||||
Data: []byte(`Foo`),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Kind: ast.Array,
|
Kind: ast.Array,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`hello`),
|
Data: []byte(`hello`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Kind: ast.Array,
|
Kind: ast.Array,
|
||||||
Children: []ast.Node{
|
Children: []astNode{
|
||||||
{
|
{
|
||||||
Kind: ast.String,
|
Kind: ast.String,
|
||||||
Data: []byte(`inner1`),
|
Data: []byte(`inner1`),
|
||||||
@@ -951,6 +948,10 @@ func TestFromAst_Slice(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: ast.Key,
|
||||||
|
Data: []byte(`Foo`),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -961,7 +962,7 @@ func TestFromAst_Slice(t *testing.T) {
|
|||||||
|
|
||||||
x := Doc{}
|
x := Doc{}
|
||||||
d := decoder{}
|
d := decoder{}
|
||||||
err := d.fromAst(root, &x)
|
err := d.fromAst(root.toOrig(), &x)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, Doc{Foo: []interface{}{"hello", []interface{}{"inner1", "inner2"}}}, x)
|
assert.Equal(t, Doc{Foo: []interface{}{"hello", []interface{}{"inner1", "inner2"}}}, x)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user