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