diff --git a/ast.go b/ast.go index cfcc6a7..081a9af 100644 --- a/ast.go +++ b/ast.go @@ -2,6 +2,7 @@ package toml import ( "fmt" + "sort" "strings" "time" ) @@ -74,13 +75,18 @@ func (parseItem item) String() string { } return fmt.Sprintf("[%s]", strings.Join(items, ", ")) case pTable: - items := make([]string, len(parseItem.Data)) pairs := parseItem.Data[0].(table) + keys := make([]string, len(pairs)) i := 0 - for k, v := range pairs { - items[i] = fmt.Sprintf("%q: %s", k, v.String()) + for k := range pairs { + keys[i] = k i++ } + sort.Strings(keys) + items := make([]string, len(pairs)) + for i, k := range keys { + items[i] = fmt.Sprintf("%q: %s", k, pairs[k].String()) + } return fmt.Sprintf("{%s}", strings.Join(items, ", ")) case pComment: return fmt.Sprintf("comment(%q)", parseItem.Data[0]) @@ -154,15 +160,15 @@ func (t *parser) setValue(key item, value item) error { return nil } -func (t *parser) newTable(key item) (table, error) { +func (t *parser) openTable(key item) (table, error) { node := t.Root // Go over all requested levels of the key. For all levels, except the last // one, it is okay if a table already exists. For at least the last level, // no table or value must exist, because that would mean we are overwriting // an existing key/value pair, which is not allowed. - for i, e := range key.Data { - name := e.(string) - if subItem, ok := node[name]; ok { + for i, value := range key.Data { + keyName := value.(string) + if subItem, ok := node[keyName]; ok { // You cannot overwrite an already defined key, regardless its value. if subItem.Type != pTable { path := formatKeyPath(key, i) @@ -178,7 +184,7 @@ func (t *parser) newTable(key item) (table, error) { } else { // Create the subtable. subTable := make(table) - node[name] = newItem(pTable, subTable) + node[keyName] = newItem(pTable, subTable) node = subTable } } diff --git a/ast_test.go b/ast_test.go index d3405bc..245a0d7 100644 --- a/ast_test.go +++ b/ast_test.go @@ -1,72 +1,69 @@ package toml import ( - "fmt" "testing" ) func TestAST_ConstructStructure(t *testing.T) { - p := newParser() - p.Root["ding"] = newItem(pInteger, 10) - p.Root["dong"] = newItem(pString, "not a song") - subTable1, _ := p.newTable(newItem(pKey, "key1", "key2 a")) - subTable1["dooh"] = newItem(pBoolean, true) - subTable1["dah"] = newItem(pBoolean, false) - subTable2, _ := p.newTable(newItem(pKey, "key1", "key2 b")) - subTable2["dieh"] = newItem(pFloat, 1.111) - subTable2["duhh"] = newItem(pFloat, 1.18e-12) + testAST(t, func() (error, *parser) { + p := newParser() + p.setValue(newItem(pKey, "ding"), newItem(pInteger, 10)) + p.setValue(newItem(pKey, "dong"), newItem(pString, "not a song")) + p.openTable(newItem(pKey, "key1", "key2 a")) + p.setValue(newItem(pKey, "dooh"), newItem(pBoolean, true)) + p.setValue(newItem(pKey, "dah"), newItem(pBoolean, false)) + p.openTable(newItem(pKey, "key1", "key2 b")) + p.setValue(newItem(pKey, "dieh"), newItem(pFloat, 1.111)) + p.setValue(newItem(pKey, "duh"), newItem(pFloat, 1.18e-12)) + return nil, p + }, "", `{"ding": 10, "dong": "not a song", "key1": {"key2 a": {"dah": false, "dooh": true}, "key2 b": {"dieh": 1.111, "duh": 1.18e-12}}}`) } func TestAST_StoreValueInRootTable(t *testing.T) { - testError(t, func() error { + testAST(t, func() (error, *parser) { p := newParser() p.setValue(newItem(pKey, "key1"), newItem(pString, "value1")) - return p.setValue(newItem(pKey, "key2"), newItem(pString, "value2")) - }, "") + return p.setValue(newItem(pKey, "key2"), newItem(pString, "value2")), p + }, "", `{"key1": "value1", "key2": "value2"}`) } func TestAST_StoreValueWithMultipartKey_CreatesTableHierarchy(t *testing.T) { - testError(t, func() error { + testAST(t, func() (error, *parser) { p := newParser() - return p.setValue(newItem(pKey, "key1", "key2", "key3"), newItem(pString, "value")) - }, "") - // TODO an actual test assertion + return p.setValue(newItem(pKey, "key1", "key2", "key3"), newItem(pString, "value")), p + }, "", `{"key1": {"key2": {"key3": "value"}}}`) } func TestAST_StoreValueWithMultipartKey_UnderSubtable_CreatesTableHierarchy(t *testing.T) { - testError(t, func() error { + testAST(t, func() (error, *parser) { p := newParser() - p.newTable(newItem(pKey, "subkey1", "subkey2")) - err := p.setValue(newItem(pKey, "key1", "key2", "key3"), newItem(pString, "value")) - fmt.Printf("%s", p.Root) - return err - }, "") - t.Fail() - // TODO an actual test assertion + p.openTable(newItem(pKey, "tablekey1", "tablekey2")) + return p.setValue(newItem(pKey, "valuekey1", "valuekey2", "valuekey3"), newItem(pString, "value")), p + }, "", `{"tablekey1": {"tablekey2": {"valuekey1": {"valuekey2": {"valuekey3": "value"}}}}}`) } func TestAST_StoreDuplicateKeyInRootTable_ReturnsError(t *testing.T) { - testError(t, func() error { + testAST(t, func() (error, *parser) { p := newParser() p.setValue(newItem(pKey, "key"), newItem(pString, "value")) - return p.setValue(newItem(pKey, "key"), newItem(pInteger, 321)) - }, `Cannot store value: string item already exists at key "key"`) + return p.setValue(newItem(pKey, "key"), newItem(pInteger, 321)), p + }, `Cannot store value: string item already exists at key "key"`, "") } func TestAST_GivenExistingTableAtKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) { - testError(t, func() error { + testAST(t, func() (error, *parser) { p := newParser() - p.newTable(newItem(pKey, "key1", "key2")) - _, err := p.newTable(newItem(pKey, "key1", "key2")) - return err - }, `Cannot create table: table for key "key1"."key2" already exists`) + p.openTable(newItem(pKey, "key1", "key2")) + _, err := p.openTable(newItem(pKey, "key1", "key2")) + return err, p + }, `Cannot create table: table for key "key1"."key2" already exists`, "") } func TestAST_GivenExistingItemAtKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) { - testError(t, func() error { + testAST(t, func() (error, *parser) { p := newParser() p.Root["key"] = newItem(pString, "value") - _, err := p.newTable(newItem(pKey, "key")) - return err - }, `Cannot create table: string item already exists at key "key"`) + _, err := p.openTable(newItem(pKey, "key")) + return err, p + }, `Cannot create table: string item already exists at key "key"`, "") } diff --git a/helpers_test.go b/helpers_test.go index 9f76c0e..eaa5094 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -54,17 +54,23 @@ func testParseHandler(t *testing.T, p *parser, handler parse.Handler, test parse err = parse.New(handler)(test.input) } -func testError(t *testing.T, code func() error, expected string) { - err := code() - if expected == "" { +func testAST(t *testing.T, code func() (error, *parser), expectedError string, expectedData string) { + err, p := code() + if expectedError == "" { if err != nil { t.Fatalf("Unexpected error: %s", err) } } else { if err == nil { t.Fatalf("An error was expected, but no error was returned") - } else if err.Error() != expected { - t.Fatalf("Unexpected error:\nexpected: %s\nactual: %s\n", expected, err.Error()) + } else if err.Error() != expectedError { + t.Fatalf("Unexpected error:\nexpected: %s\nactual: %s\n", expectedError, err.Error()) } } + if expectedData == "" { + return + } + if expectedData != p.Root.String() { + t.Fatalf("Unexpected data after parsing:\nexpected: %s\nactual: %s\n", expectedData, p.Root.String()) + } } diff --git a/value_table.go b/value_table.go index b66800f..4e17265 100644 --- a/value_table.go +++ b/value_table.go @@ -107,5 +107,5 @@ func (t *parser) startPlainTable(p *parse.API) { } key := t.Items[0] t.Items = t.Items[1:] - t.newTable(key) + t.openTable(key) }