Fixed formatting; added name to license file
This commit is contained in:
@@ -67,7 +67,7 @@ You can run both of them using `./test.sh`.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (c) 2013 Thomas Pelletier
|
Copyright (c) 2013, 2014 Thomas Pelletier, Eric Anderton
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const (
|
|||||||
tokenDoubleRightBracket
|
tokenDoubleRightBracket
|
||||||
tokenDate
|
tokenDate
|
||||||
tokenKeyGroup
|
tokenKeyGroup
|
||||||
tokenKeyGroupArray
|
tokenKeyGroupArray
|
||||||
tokenComma
|
tokenComma
|
||||||
tokenEOL
|
tokenEOL
|
||||||
)
|
)
|
||||||
@@ -389,19 +389,18 @@ func lexString(l *lexer) stateFn {
|
|||||||
|
|
||||||
func lexKeyGroup(l *lexer) stateFn {
|
func lexKeyGroup(l *lexer) stateFn {
|
||||||
l.ignore()
|
l.ignore()
|
||||||
l.pos += 1
|
l.pos += 1
|
||||||
|
|
||||||
if l.peek() == '[' {
|
if l.peek() == '[' {
|
||||||
// token '[[' signifies an array of anonymous key groups
|
// token '[[' signifies an array of anonymous key groups
|
||||||
l.ignore()
|
l.pos += 1
|
||||||
l.pos += 1
|
l.emit(tokenDoubleLeftBracket)
|
||||||
l.emit(tokenDoubleLeftBracket)
|
return lexInsideKeyGroupArray
|
||||||
return lexInsideKeyGroupArray
|
} else {
|
||||||
} else {
|
// vanilla key group
|
||||||
// vanilla key group
|
l.emit(tokenLeftBracket)
|
||||||
l.emit(tokenLeftBracket)
|
return lexInsideKeyGroup
|
||||||
return lexInsideKeyGroup
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexInsideKeyGroupArray(l *lexer) stateFn {
|
func lexInsideKeyGroupArray(l *lexer) stateFn {
|
||||||
@@ -410,13 +409,12 @@ func lexInsideKeyGroupArray(l *lexer) stateFn {
|
|||||||
if l.pos > l.start {
|
if l.pos > l.start {
|
||||||
l.emit(tokenKeyGroupArray)
|
l.emit(tokenKeyGroupArray)
|
||||||
}
|
}
|
||||||
l.ignore()
|
|
||||||
l.pos += 1
|
|
||||||
if l.peek() != ']' {
|
|
||||||
break // error
|
|
||||||
}
|
|
||||||
l.ignore()
|
l.ignore()
|
||||||
l.pos += 1
|
l.pos += 1
|
||||||
|
if l.peek() != ']' {
|
||||||
|
break // error
|
||||||
|
}
|
||||||
|
l.pos += 1
|
||||||
l.emit(tokenDoubleRightBracket)
|
l.emit(tokenDoubleRightBracket)
|
||||||
return lexVoid
|
return lexVoid
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,3 +404,12 @@ func TestUnicodeString(t *testing.T) {
|
|||||||
token{tokenEOF, ""},
|
token{tokenEOF, ""},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKeyGroupArray(t *testing.T) {
|
||||||
|
testFlow(t, "[[foo]]", []token{
|
||||||
|
token{tokenDoubleLeftBracket, "[["},
|
||||||
|
token{tokenKeyGroupArray, "foo"},
|
||||||
|
token{tokenDoubleRightBracket, "]]"},
|
||||||
|
token{tokenEOF, ""},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ func parseStart(p *parser) parserStateFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenDoubleLeftBracket:
|
case tokenDoubleLeftBracket:
|
||||||
return parseGroupArray
|
return parseGroupArray
|
||||||
case tokenLeftBracket:
|
case tokenLeftBracket:
|
||||||
return parseGroup
|
return parseGroup
|
||||||
case tokenKey:
|
case tokenKey:
|
||||||
@@ -86,33 +86,33 @@ func parseStart(p *parser) parserStateFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseGroupArray(p *parser) parserStateFn {
|
func parseGroupArray(p *parser) parserStateFn {
|
||||||
p.getToken() // discard the [[
|
p.getToken() // discard the [[
|
||||||
key := p.getToken()
|
key := p.getToken()
|
||||||
if key.typ != tokenKeyGroupArray {
|
if key.typ != tokenKeyGroupArray {
|
||||||
panic(fmt.Sprintf("unexpected token %s, was expecting a key group array", key))
|
panic(fmt.Sprintf("unexpected token %s, was expecting a key group array", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// get or create group array element at the indicated part in the path
|
// get or create group array element at the indicated part in the path
|
||||||
p.currentGroup = strings.Split(key.val, ".")
|
p.currentGroup = strings.Split(key.val, ".")
|
||||||
dest_tree := p.tree.GetPath(p.currentGroup)
|
dest_tree := p.tree.GetPath(p.currentGroup)
|
||||||
var array []*TomlTree
|
var array []*TomlTree
|
||||||
if dest_tree == nil {
|
if dest_tree == nil {
|
||||||
array = make([]*TomlTree, 0)
|
array = make([]*TomlTree, 0)
|
||||||
} else if dest_tree.([]*TomlTree) != nil {
|
} else if dest_tree.([]*TomlTree) != nil {
|
||||||
array = dest_tree.([]*TomlTree)
|
array = dest_tree.([]*TomlTree)
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintf("key %s is already assigned and not of type group array", key))
|
panic(fmt.Sprintf("key %s is already assigned and not of type group array", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a new tree to the end of the group array
|
// add a new tree to the end of the group array
|
||||||
new_tree := make(TomlTree)
|
new_tree := make(TomlTree)
|
||||||
array = append(array, &new_tree)
|
array = append(array, &new_tree)
|
||||||
p.tree.SetPath(p.currentGroup, array)
|
p.tree.SetPath(p.currentGroup, array)
|
||||||
|
|
||||||
// keep this key name from use by other kinds of assignments
|
// keep this key name from use by other kinds of assignments
|
||||||
p.seenGroupKeys = append(p.seenGroupKeys, key.val)
|
p.seenGroupKeys = append(p.seenGroupKeys, key.val)
|
||||||
|
|
||||||
// move to next parser state
|
// move to next parser state
|
||||||
p.assume(tokenDoubleRightBracket)
|
p.assume(tokenDoubleRightBracket)
|
||||||
return parseStart(p)
|
return parseStart(p)
|
||||||
}
|
}
|
||||||
@@ -146,24 +146,24 @@ func parseAssign(p *parser) parserStateFn {
|
|||||||
group_key = make([]string, 0)
|
group_key = make([]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the group to assign, looking out for arrays of groups
|
// find the group to assign, looking out for arrays of groups
|
||||||
var target_node *TomlTree
|
var target_node *TomlTree
|
||||||
switch node := p.tree.GetPath(group_key).(type) {
|
switch node := p.tree.GetPath(group_key).(type) {
|
||||||
case []*TomlTree:
|
case []*TomlTree:
|
||||||
target_node = node[len(node)-1]
|
target_node = node[len(node)-1]
|
||||||
case *TomlTree:
|
case *TomlTree:
|
||||||
target_node = node
|
target_node = node
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unknown group type for path %v", group_key))
|
panic(fmt.Sprintf("Unknown group type for path %v", group_key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign value to the found group
|
// assign value to the found group
|
||||||
local_key := []string{ key.val }
|
local_key := []string{key.val}
|
||||||
final_key := append(group_key, key.val)
|
final_key := append(group_key, key.val)
|
||||||
if target_node.GetPath(local_key) != nil {
|
if target_node.GetPath(local_key) != nil {
|
||||||
panic(fmt.Sprintf("the following key was defined twice: %s", strings.Join(final_key, ".")))
|
panic(fmt.Sprintf("the following key was defined twice: %s", strings.Join(final_key, ".")))
|
||||||
}
|
}
|
||||||
target_node.SetPath(local_key, value)
|
target_node.SetPath(local_key, value)
|
||||||
return parseStart(p)
|
return parseStart(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ package toml
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"strconv"
|
"time"
|
||||||
"time"
|
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Definition of a TomlTree.
|
// Definition of a TomlTree.
|
||||||
@@ -46,14 +46,18 @@ func (t *TomlTree) Keys() []string {
|
|||||||
// Returns nil if the path does not exist in the tree.
|
// Returns nil if the path does not exist in the tree.
|
||||||
// If keys is of length zero, the current tree is returned.
|
// If keys is of length zero, the current tree is returned.
|
||||||
func (t *TomlTree) Get(key string) interface{} {
|
func (t *TomlTree) Get(key string) interface{} {
|
||||||
if key == "" { return t }
|
if key == "" {
|
||||||
|
return t
|
||||||
|
}
|
||||||
return t.GetPath(strings.Split(key, "."))
|
return t.GetPath(strings.Split(key, "."))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the element in the tree indicated by 'keys'.
|
// Returns the element in the tree indicated by 'keys'.
|
||||||
// If keys is of length zero, the current tree is returned.
|
// If keys is of length zero, the current tree is returned.
|
||||||
func (t *TomlTree) GetPath(keys []string) interface{} {
|
func (t *TomlTree) GetPath(keys []string) interface{} {
|
||||||
if len(keys) == 0 { return t }
|
if len(keys) == 0 {
|
||||||
|
return t
|
||||||
|
}
|
||||||
subtree := t
|
subtree := t
|
||||||
for _, intermediate_key := range keys[:len(keys)-1] {
|
for _, intermediate_key := range keys[:len(keys)-1] {
|
||||||
_, exists := (*subtree)[intermediate_key]
|
_, exists := (*subtree)[intermediate_key]
|
||||||
@@ -67,11 +71,11 @@ func (t *TomlTree) GetPath(keys []string) interface{} {
|
|||||||
|
|
||||||
// Same as Get but with a default value
|
// Same as Get but with a default value
|
||||||
func (t *TomlTree) GetDefault(key string, def interface{}) interface{} {
|
func (t *TomlTree) GetDefault(key string, def interface{}) interface{} {
|
||||||
val := t.Get(key)
|
val := t.Get(key)
|
||||||
if val == nil {
|
if val == nil {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
return val;
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set an element in the tree.
|
// Set an element in the tree.
|
||||||
@@ -116,89 +120,100 @@ func (t *TomlTree) createSubTree(key string) {
|
|||||||
|
|
||||||
// encodes a string to a TOML-compliant string value
|
// encodes a string to a TOML-compliant string value
|
||||||
func encodeTomlString(value string) string {
|
func encodeTomlString(value string) string {
|
||||||
result := ""
|
result := ""
|
||||||
for _, rr := range value {
|
for _, rr := range value {
|
||||||
int_rr := uint16(rr)
|
int_rr := uint16(rr)
|
||||||
switch rr {
|
switch rr {
|
||||||
case '\b': result += "\\b"
|
case '\b':
|
||||||
case '\t': result += "\\t"
|
result += "\\b"
|
||||||
case '\n': result += "\\n"
|
case '\t':
|
||||||
case '\f': result += "\\f"
|
result += "\\t"
|
||||||
case '\r': result += "\\r"
|
case '\n':
|
||||||
case '"': result += "\\\""
|
result += "\\n"
|
||||||
case '\\': result += "\\\\"
|
case '\f':
|
||||||
default:
|
result += "\\f"
|
||||||
if int_rr < 0x001F {
|
case '\r':
|
||||||
result += fmt.Sprintf("\\u%0.4X", int_rr)
|
result += "\\r"
|
||||||
} else {
|
case '"':
|
||||||
result += string(rr)
|
result += "\\\""
|
||||||
}
|
case '\\':
|
||||||
}
|
result += "\\\\"
|
||||||
}
|
default:
|
||||||
return result
|
if int_rr < 0x001F {
|
||||||
|
result += fmt.Sprintf("\\u%0.4X", int_rr)
|
||||||
|
} else {
|
||||||
|
result += string(rr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value print support function for ToString()
|
// Value print support function for ToString()
|
||||||
// Outputs the TOML compliant string representation of a value
|
// Outputs the TOML compliant string representation of a value
|
||||||
func toTomlValue(item interface{}, indent int) string {
|
func toTomlValue(item interface{}, indent int) string {
|
||||||
tab := strings.Repeat(" ", indent)
|
tab := strings.Repeat(" ", indent)
|
||||||
switch value := item.(type) {
|
switch value := item.(type) {
|
||||||
case int64:
|
case int64:
|
||||||
return tab + strconv.FormatInt(value, 10)
|
return tab + strconv.FormatInt(value, 10)
|
||||||
case float64:
|
case float64:
|
||||||
return tab + strconv.FormatFloat(value, 'f', -1, 64)
|
return tab + strconv.FormatFloat(value, 'f', -1, 64)
|
||||||
case string:
|
case string:
|
||||||
return tab + "\"" + encodeTomlString(value) + "\""
|
return tab + "\"" + encodeTomlString(value) + "\""
|
||||||
case bool:
|
case bool:
|
||||||
if value { return "true" } else { return "false" }
|
if value {
|
||||||
case time.Time:
|
return "true"
|
||||||
return tab + value.Format(time.RFC3339)
|
} else {
|
||||||
case []interface{}:
|
return "false"
|
||||||
result := tab + "[\n"
|
}
|
||||||
for _, item := range value {
|
case time.Time:
|
||||||
result += toTomlValue(item, indent + 2) + ",\n"
|
return tab + value.Format(time.RFC3339)
|
||||||
}
|
case []interface{}:
|
||||||
return result + tab + "]"
|
result := tab + "[\n"
|
||||||
default:
|
for _, item := range value {
|
||||||
panic(fmt.Sprintf("unsupported value type: %v", value))
|
result += toTomlValue(item, indent+2) + ",\n"
|
||||||
}
|
}
|
||||||
|
return result + tab + "]"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported value type: %v", value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursive support function for ToString()
|
// Recursive support function for ToString()
|
||||||
// Outputs a tree, using the provided keyspace to prefix group names
|
// Outputs a tree, using the provided keyspace to prefix group names
|
||||||
func (t *TomlTree) toToml(keyspace string) string {
|
func (t *TomlTree) toToml(keyspace string) string {
|
||||||
result := ""
|
result := ""
|
||||||
for k, v := range (map[string]interface{})(*t) {
|
for k, v := range (map[string]interface{})(*t) {
|
||||||
// figure out the keyspace
|
// figure out the keyspace
|
||||||
combined_key := k
|
combined_key := k
|
||||||
if keyspace != "" {
|
if keyspace != "" {
|
||||||
combined_key = keyspace + "." + combined_key
|
combined_key = keyspace + "." + combined_key
|
||||||
}
|
}
|
||||||
// output based on type
|
// output based on type
|
||||||
switch node := v.(type) {
|
switch node := v.(type) {
|
||||||
case []*TomlTree:
|
case []*TomlTree:
|
||||||
for _, item := range node {
|
for _, item := range node {
|
||||||
if len(item.Keys()) > 0 {
|
if len(item.Keys()) > 0 {
|
||||||
result += fmt.Sprintf("\n[[%s]]\n", combined_key)
|
result += fmt.Sprintf("\n[[%s]]\n", combined_key)
|
||||||
}
|
}
|
||||||
result += item.toToml(combined_key)
|
result += item.toToml(combined_key)
|
||||||
}
|
}
|
||||||
case *TomlTree:
|
case *TomlTree:
|
||||||
if len(node.Keys()) > 0 {
|
if len(node.Keys()) > 0 {
|
||||||
result += fmt.Sprintf("\n[%s]\n", combined_key)
|
result += fmt.Sprintf("\n[%s]\n", combined_key)
|
||||||
}
|
}
|
||||||
result += node.toToml(combined_key)
|
result += node.toToml(combined_key)
|
||||||
default:
|
default:
|
||||||
result += fmt.Sprintf("%s = %s\n", k, toTomlValue(node,0))
|
result += fmt.Sprintf("%s = %s\n", k, toTomlValue(node, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a human-readable representation of the current tree.
|
// Generates a human-readable representation of the current tree.
|
||||||
// Output spans multiple lines, and is suitable for ingest by a TOML parser
|
// Output spans multiple lines, and is suitable for ingest by a TOML parser
|
||||||
func (t *TomlTree) ToString() string {
|
func (t *TomlTree) ToString() string {
|
||||||
return t.toToml("")
|
return t.toToml("")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a TomlTree from a string.
|
// Create a TomlTree from a string.
|
||||||
|
|||||||
+17
-17
@@ -1,25 +1,25 @@
|
|||||||
package toml
|
package toml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTomlGetPath(t *testing.T) {
|
func TestTomlGetPath(t *testing.T) {
|
||||||
node := make(TomlTree)
|
node := make(TomlTree)
|
||||||
//TODO: set other node data
|
//TODO: set other node data
|
||||||
|
|
||||||
for idx, item := range []struct {
|
for idx, item := range []struct {
|
||||||
Path []string
|
Path []string
|
||||||
Expected interface{}
|
Expected interface{}
|
||||||
} {
|
}{
|
||||||
{ // empty path test
|
{ // empty path test
|
||||||
[]string{},
|
[]string{},
|
||||||
&node,
|
&node,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
result := node.GetPath(item.Path)
|
result := node.GetPath(item.Path)
|
||||||
if result != item.Expected {
|
if result != item.Expected {
|
||||||
t.Errorf("GetPath[%d] %v - expected %v, got %v instead.", idx, item.Path, item.Expected, result)
|
t.Errorf("GetPath[%d] %v - expected %v, got %v instead.", idx, item.Path, item.Expected, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user