Linear array storage for AST
This commit is contained in:
+100
-177
@@ -2,182 +2,103 @@ package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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))
|
||||
// Iterator starts uninitialized, you need to call Next() first.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// it := n.Children()
|
||||
// for it.Next() {
|
||||
// it.Node()
|
||||
// }
|
||||
type Iterator interface {
|
||||
// Next moves the iterator forward and returns true if points to a node, false
|
||||
// otherwise.
|
||||
Next() bool
|
||||
// Node returns a copy of the node pointed at by the iterator.
|
||||
Node() Node
|
||||
}
|
||||
|
||||
type Root []Node
|
||||
|
||||
// Dot returns a dot representation of the AST for debugging.
|
||||
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()
|
||||
type chainIterator struct {
|
||||
started bool
|
||||
node Node
|
||||
}
|
||||
|
||||
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 {
|
||||
Kind Kind
|
||||
Data []byte // Raw bytes from the input
|
||||
|
||||
// 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 last 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
|
||||
// next idx (in the root array). 0 if last of the collection.
|
||||
next int
|
||||
// child idx (in the root array). 0 if no child.
|
||||
child int
|
||||
// pointer to the root array
|
||||
root *Root
|
||||
}
|
||||
|
||||
// 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{}
|
||||
@@ -186,15 +107,16 @@ var NoNode = Node{}
|
||||
// otherwise.
|
||||
// They are guaranteed to be all be of the Kind Key. A simple key would return
|
||||
// just one element.
|
||||
func (n *Node) Key() []Node {
|
||||
func (n *Node) Key() Iterator {
|
||||
switch n.Kind {
|
||||
case KeyValue:
|
||||
if len(n.Children) < 2 {
|
||||
panic(fmt.Errorf("KeyValue should have at least two children, not %d", len(n.Children)))
|
||||
value := n.Child()
|
||||
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:
|
||||
return n.Children
|
||||
return &chainIterator{node: n.Child()}
|
||||
default:
|
||||
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.
|
||||
// Guaranteed to be non-nil.
|
||||
// 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)
|
||||
if len(n.Children) < 2 {
|
||||
panic(fmt.Errorf("KeyValue should have at least two children, not %d", len(n.Children)))
|
||||
}
|
||||
return &n.Children[len(n.Children)-1]
|
||||
return n.Child()
|
||||
}
|
||||
|
||||
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 {
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user