100% compatibility with the BurntSushi tests established.

This commit is contained in:
Maurice Makaay 2019-06-27 22:43:17 +00:00
parent 0d4cb356e9
commit 5782971bde
6 changed files with 45 additions and 26 deletions

View File

@ -1,9 +1,11 @@
all:
all: build
build:
@(cd cmd/toml-test-decoder; go build)
test:
@(cd ast; go test)
@(cd parse; go test)
@(cd ast; go test | grep -v ^PASS)
@(cd parse; go test | grep -v ^PASS)
sushi-test:
sushi-test: build
@(cd cmd/toml-test-decoder; go build; ${GOPATH}/bin/toml-test ./toml-test-decoder)

View File

@ -69,10 +69,10 @@ const (
TypeBoolean ValueType = "boolean"
// TypeOffsetDateTime identifies a date/time value, including timezone info (2019-06-18 10:32:15.173645362+0200).
TypeOffsetDateTime ValueType = "offset datetime"
TypeOffsetDateTime ValueType = "datetime"
// TypeLocalDateTime identifies a date/time value, without timezone info (2018-12-25 12:12:18.876772533).
TypeLocalDateTime ValueType = "datetime"
TypeLocalDateTime ValueType = "datetime-local"
// TypeLocalDate identifies a date value (2017-05-17).
TypeLocalDate ValueType = "date"
@ -81,13 +81,18 @@ const (
TypeLocalTime ValueType = "time"
// TypeArrayOfTables identifies an [[array.of.tables]].
TypeArrayOfTables ValueType = "arrayOfTables"
TypeArrayOfTables ValueType = "array-of-tables"
// TypeArray identifies ["an", "inline", "static", "array"].
TypeArray ValueType = "array"
// TypeTable identifies an { "inline" = "table" } or [standard.table].
TypeTable ValueType = "table"
// TypeImplicitTable identifies an intermediate table that was created
// using a table definition that looks like [a.b.c.d] (where a, b and c
// would be implicit tables when they hadn't been created explicitly before).
TypeImplicitTable ValueType = "implicit-table"
)
// SetKeyValuePair is used to set a key and an accompanying value in the
@ -115,22 +120,30 @@ func (doc *Document) OpenTable(key Key) error {
doc.CurrentKey = nil
doc.Current = doc.Root
// Go over all requested levels of the key. For all levels, except the last
// one, it is okay if a Table or TableArray 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.
// one, it is okay if a Table, implicit Table or TableArray already exists.
// For at least the last level, no table or value must exist, or an implicit
// table must exist. Otherwise we are overwriting an existing value, which
// is not allowed.
node, lastKeyPart, err := doc.makeTablePath(key)
if err != nil {
return fmt.Errorf("invalid table: %s", err)
}
// Check if the key is still free for use.
// Check if the key is already in use.
if existing, ok := node[lastKeyPart]; ok {
path := doc.formatKeyPath(key, len(key)-1)
return fmt.Errorf("invalid table: %s value already exists at key %s", existing.Type, path)
// It is, but it is an implicit table. Upgrade it to an explicit and use it.
if existing.Type == TypeImplicitTable {
existing.Type = TypeTable
node = existing.Data[0].(Table)
} else {
path := doc.formatKeyPath(key, len(key)-1)
return fmt.Errorf("invalid table: %s value already exists at key %s", existing.Type, path)
}
} else {
// The subtable does not exist yet. Create the subtable.
subTable := make(Table)
node[lastKeyPart] = NewValue(TypeTable, subTable)
node = subTable
}
// The subtable does not exist yet. Create the subtable.
subTable := make(Table)
node[lastKeyPart] = NewValue(TypeTable, subTable)
node = subTable
// From here on, key/value pairs are added to the newly defined table.
doc.Current = node
@ -193,10 +206,10 @@ func (doc *Document) makeTablePath(key Key) (Table, string, error) {
}
if subValue, ok := node[keyPart]; ok {
// You cannot overwrite an already defined key, regardless its value.
// When a value already exists at the current key, this can only be a table
// or an array of tables. In case of an array of tables, the last created
// table will be used.
if subValue.Type == TypeTable {
// When a value already exists at the current key, this can only be a table,
// an implicit table or an array of tables. In case of an array of tables,
// the table that was added last to the array will be used.
if subValue.Type == TypeTable || subValue.Type == TypeImplicitTable {
// A table was found, traverse to that table.
node = subValue.Data[0].(Table)
} else if subValue.Type == TypeArrayOfTables {
@ -209,9 +222,9 @@ func (doc *Document) makeTablePath(key Key) (Table, string, error) {
return nil, "", fmt.Errorf("%s value already exists at key %s", subValue.Type, path)
}
} else {
// The subtable does not exist yet. Create the subtable.
// The subtable does not exist yet. Create the subtable as an implicit table.
subTable := make(Table)
node[keyPart] = NewValue(TypeTable, subTable)
node[keyPart] = NewValue(TypeImplicitTable, subTable)
node = subTable
}
}

View File

@ -73,9 +73,9 @@ func Test_ConstructExplicitTableAfterImplicitSubtable(t *testing.T) {
p := ast.NewDocument()
p.OpenTable(ast.NewKey("a", "b", "c"))
p.SetKeyValuePair(ast.NewKey("answer"), ast.NewValue(ast.TypeString, "42"))
p.OpenTable(ast.NewKey("a"))
err := p.OpenTable(ast.NewKey("a"))
p.SetKeyValuePair(ast.NewKey("better"), ast.NewValue(ast.TypeString, "43"))
return nil, p
return err, p
},
"",
`{"a": {"b": {"c": {"answer": "42"}}, "better": "43"}}`)

View File

@ -35,6 +35,8 @@ func (value Value) String() string {
values[i] = value.(*Value).String()
}
return fmt.Sprintf("[%s]", strings.Join(values, ", "))
case TypeImplicitTable:
fallthrough
case TypeTable:
pairs := value.Data[0].(Table)
keys := make([]string, len(pairs))

View File

@ -77,6 +77,8 @@ func makeSushi(value *ast.Value) string {
} else {
return fmt.Sprintf(`{"type": "array", "value": [%s]}`, strings.Join(values, ", "))
}
case ast.TypeImplicitTable:
fallthrough
case ast.TypeTable:
pairs := value.Data[0].(ast.Table)
keys := make([]string, len(pairs))

View File

@ -39,7 +39,7 @@ func TestArray(t *testing.T) {
{`x=[[1],['a']]`, `{"x": [[1], ["a"]]}`, ``},
{`x=[[[],[]],[]]`, `{"x": [[[], []], []]}`, ``},
{"x=[\r\n\r\n \t\n [\r\n\r\n\t [],[\t]],\t\n[]\t \t \n ]", `{"x": [[[], []], []]}`, ``},
{`x=[[1],'a']`, `{}`, `type mismatch in array of static arrays: found an item of type string at line 1, column 11`},
{`x=[[1],'a']`, `{}`, `type mismatch in array of arrays: found an item of type string at line 1, column 11`},
} {
p := newParser()
testParse(t, p, p.startDocument, test)