Added a good string formatter for the AST, to making testing more straight forward.
This commit is contained in:
parent
8838dc9c44
commit
15560b29b0
22
ast.go
22
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
|
||||
}
|
||||
}
|
||||
|
|
69
ast_test.go
69
ast_test.go
|
@ -1,72 +1,69 @@
|
|||
package toml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAST_ConstructStructure(t *testing.T) {
|
||||
testAST(t, func() (error, *parser) {
|
||||
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)
|
||||
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"`, "")
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue