Fixed formatting; added name to license file

This commit is contained in:
eanderton
2014-07-07 21:06:42 -04:00
parent c8b5633273
commit 7b208738bc
6 changed files with 173 additions and 151 deletions
+1 -1
View File
@@ -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
+16 -18
View File
@@ -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
} }
+9
View File
@@ -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, ""},
})
}
+37 -37
View File
@@ -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)
} }
+93 -78
View File
@@ -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
View File
@@ -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)
} }
} }
} }