Added Position Support to TomlTree

TomlDocument provides an optional TOML processing path where position
informaiton is stored alongside a TomlTree.
* Added Position struct
* Revised TomlTree to contain position data
* Added tomlValue to bind positions to values
* Revised parser to emit position data
* Revised token to use new Position struct
* Added tests for new functionality
* Bugfixed table array duplicate key handling
* Applied gofmt to all code
This commit is contained in:
eanderton
2014-08-14 22:04:25 -04:00
parent bcbaee1079
commit 7c63fff960
9 changed files with 582 additions and 314 deletions
+46 -19
View File
@@ -22,7 +22,7 @@ type parserStateFn func(*parser) parserStateFn
// Formats and panics an error message based on a token
func (p *parser) raiseError(tok *token, msg string, args ...interface{}) {
panic(tok.Pos() + ": " + fmt.Sprintf(msg, args...))
panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
}
func (p *parser) run() {
@@ -47,10 +47,10 @@ func (p *parser) peek() *token {
func (p *parser) assume(typ tokenType) {
tok := p.getToken()
if tok == nil {
p.raiseError(tok, "was expecting token %s, but token stream is empty", tok.typ)
p.raiseError(tok, "was expecting token %s, but token stream is empty", tok)
}
if tok.typ != typ {
p.raiseError(tok, "was expecting token %s, but got %s", typ, tok.typ)
p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok)
}
}
@@ -70,6 +70,9 @@ func (p *parser) getToken() *token {
func parseStart(p *parser) parserStateFn {
tok := p.peek()
// prime position data with root tree instance
p.tree.position = tok.Position
// end of stream, parsing is finished
if tok == nil {
return nil
@@ -91,15 +94,16 @@ func parseStart(p *parser) parserStateFn {
}
func parseGroupArray(p *parser) parserStateFn {
p.getToken() // discard the [[
start_token := p.getToken() // discard the [[
key := p.getToken()
if key.typ != tokenKeyGroupArray {
p.raiseError(key, "unexpected token %s, was expecting a key group array", key)
}
// get or create group array element at the indicated part in the path
p.currentGroup = strings.Split(key.val, ".")
dest_tree := p.tree.GetPath(p.currentGroup)
keys := strings.Split(key.val, ".")
p.tree.createSubTree(keys[:len(keys)-1]) // create parent entries
dest_tree := p.tree.GetPath(keys)
var array []*TomlTree
if dest_tree == nil {
array = make([]*TomlTree, 0)
@@ -108,14 +112,31 @@ func parseGroupArray(p *parser) parserStateFn {
} else {
p.raiseError(key, "key %s is already assigned and not of type group array", key)
}
p.currentGroup = keys
// add a new tree to the end of the group array
new_tree := make(TomlTree)
array = append(array, &new_tree)
new_tree := newTomlTree()
new_tree.position = start_token.Position
array = append(array, new_tree)
p.tree.SetPath(p.currentGroup, array)
// remove all keys that were children of this group array
prefix := key.val + "."
found := false
for ii := 0; ii < len(p.seenGroupKeys); {
groupKey := p.seenGroupKeys[ii]
if strings.HasPrefix(groupKey, prefix) {
p.seenGroupKeys = append(p.seenGroupKeys[:ii], p.seenGroupKeys[ii+1:]...)
} else {
found = (groupKey == key.val)
ii++
}
}
// keep this key name from use by other kinds of assignments
p.seenGroupKeys = append(p.seenGroupKeys, key.val)
if !found {
p.seenGroupKeys = append(p.seenGroupKeys, key.val)
}
// move to next parser state
p.assume(tokenDoubleRightBracket)
@@ -123,7 +144,7 @@ func parseGroupArray(p *parser) parserStateFn {
}
func parseGroup(p *parser) parserStateFn {
p.getToken() // discard the [
start_token := p.getToken() // discard the [
key := p.getToken()
if key.typ != tokenKeyGroup {
p.raiseError(key, "unexpected token %s, was expecting a key group", key)
@@ -133,12 +154,16 @@ func parseGroup(p *parser) parserStateFn {
p.raiseError(key, "duplicated tables")
}
}
p.seenGroupKeys = append(p.seenGroupKeys, key.val)
if err := p.tree.createSubTree(key.val); err != nil {
keys := strings.Split(key.val, ".")
if err := p.tree.createSubTree(keys); err != nil {
p.raiseError(key, "%s", err)
}
p.assume(tokenRightBracket)
p.currentGroup = strings.Split(key.val, ".")
p.currentGroup = keys
target_tree := p.tree.GetPath(p.currentGroup).(*TomlTree)
target_tree.position = start_token.Position
return parseStart(p)
}
@@ -150,7 +175,7 @@ func parseAssign(p *parser) parserStateFn {
if len(p.currentGroup) > 0 {
group_key = p.currentGroup
} else {
group_key = make([]string, 0)
group_key = []string{}
}
// find the group to assign, looking out for arrays of groups
@@ -161,16 +186,18 @@ func parseAssign(p *parser) parserStateFn {
case *TomlTree:
target_node = node
default:
p.raiseError(key, "Unknown group type for path %s", group_key)
p.raiseError(key, "Unknown group type for path: %s",
strings.Join(group_key, "."))
}
// assign value to the found group
local_key := []string{key.val}
final_key := append(group_key, key.val)
if target_node.GetPath(local_key) != nil {
p.raiseError(key, "the following key was defined twice: %s", strings.Join(final_key, "."))
p.raiseError(key, "The following key was defined twice: %s",
strings.Join(final_key, "."))
}
target_node.SetPath(local_key, value)
target_node.values[key.val] = &tomlValue{value, key.Position}
return parseStart(p)
}
@@ -251,14 +278,14 @@ func parseArray(p *parser) []interface{} {
}
func parse(flow chan token) *TomlTree {
result := make(TomlTree)
result := newTomlTree()
parser := &parser{
flow: flow,
tree: &result,
tree: result,
tokensBuffer: make([]token, 0),
currentGroup: make([]string, 0),
seenGroupKeys: make([]string, 0),
}
parser.run()
return parser.tree
return result
}