114 lines
2.2 KiB
Go
114 lines
2.2 KiB
Go
|
package tree
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"io"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Node represent a single data structure.
|
||
|
type Node struct {
|
||
|
Parent *Node
|
||
|
Children []*Node
|
||
|
Value string
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
// nodeBreakSymbol delimits nodes (lines).
|
||
|
nodeBreakSymbol rune = '\n'
|
||
|
|
||
|
// wordBreakSymbol delimits words (cells).
|
||
|
wordBreakSymbol rune = ' '
|
||
|
|
||
|
// edgeSymbol is used to indicate the parent/child relationship between
|
||
|
// nodes.
|
||
|
//
|
||
|
// TODO(toby3d): allow clients to change this to '\t' for example.
|
||
|
edgeSymbol rune = wordBreakSymbol
|
||
|
)
|
||
|
|
||
|
// Parse parses the data into a Tree Notation nodes.
|
||
|
//
|
||
|
// Note what all documents are valid Tree Notation documents. Like binary
|
||
|
// notation, there are no syntax errors in Tree Notation.
|
||
|
func Parse(data io.Reader) (root *Node) {
|
||
|
stack := make([]*Node, 0)
|
||
|
|
||
|
scanner := bufio.NewScanner(data)
|
||
|
for scanner.Scan() {
|
||
|
node := &Node{
|
||
|
Parent: nil,
|
||
|
Children: nil,
|
||
|
Value: scanner.Text(),
|
||
|
}
|
||
|
|
||
|
if root == nil {
|
||
|
root = node
|
||
|
stack = append(stack, root)
|
||
|
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if lenIndent(node.Value) <= lenIndent(root.Value) && len(stack) > 0 {
|
||
|
root = stack[len(stack)-1]
|
||
|
stack = stack[:len(stack)-1]
|
||
|
}
|
||
|
|
||
|
if root.Children == nil {
|
||
|
root.Children = make([]*Node, 0)
|
||
|
}
|
||
|
|
||
|
node.Parent = root
|
||
|
root.Children = append(root.Children, node)
|
||
|
stack = append(stack, node)
|
||
|
root = stack[len(stack)-1]
|
||
|
}
|
||
|
|
||
|
if root == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
for root.Parent != nil {
|
||
|
root = root.Parent
|
||
|
}
|
||
|
|
||
|
return root
|
||
|
}
|
||
|
|
||
|
// Satisfies the fmt.Stringer interface to print node line passed in as an
|
||
|
// operand to any format that accepts a string, or to an unformatted printer
|
||
|
// such as fmt.Print.
|
||
|
func (n Node) String() string {
|
||
|
return strings.TrimLeft(n.Value, string(edgeSymbol))
|
||
|
}
|
||
|
|
||
|
// GoString satisfies the fmt.GoStringer interface to print values passed as an
|
||
|
// operand to a %#v format.
|
||
|
func (n Node) GoString() (result string) {
|
||
|
result += n.Value
|
||
|
|
||
|
for i := range n.Children {
|
||
|
result += string(nodeBreakSymbol)
|
||
|
result += n.Children[i].GoString()
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// lenIndent count egdeSymbol prefixes in line.
|
||
|
//
|
||
|
// Returns 0 if line starts from any word character.
|
||
|
func lenIndent(v string) int {
|
||
|
count := 0
|
||
|
|
||
|
for i := range v {
|
||
|
if rune(v[i]) != edgeSymbol {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
count++
|
||
|
}
|
||
|
|
||
|
return count
|
||
|
}
|