Cleaning up test code.
This commit is contained in:
parent
c536dd1243
commit
608e68c207
64
ast.go
64
ast.go
|
@ -10,33 +10,33 @@ import (
|
||||||
|
|
||||||
// item represents a TOML item.
|
// item represents a TOML item.
|
||||||
type item struct {
|
type item struct {
|
||||||
Type itemType
|
Type valueType
|
||||||
Data []interface{}
|
Data []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// table represents a TOML table.
|
// table represents a TOML table.
|
||||||
type table map[string]*item
|
type table map[string]*item
|
||||||
|
|
||||||
// itemType identifies the semantic role of a TOML item.
|
// valueType identifies the type of a TOML value.
|
||||||
type itemType string
|
type valueType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pString itemType = "string" // "various", 'types', """of""", '''strings'''
|
tString valueType = "string" // "various", 'types', """of""", '''strings'''
|
||||||
pInteger itemType = "integer" // 12345, 0xffee12a0, 0o0755, 0b101101011
|
tInteger valueType = "integer" // 12345, 0xffee12a0, 0o0755, 0b101101011
|
||||||
pFloat itemType = "float" // 10.1234, 143E-12, 43.28377e+4, +inf, -inf, nan
|
tFloat valueType = "float" // 10.1234, 143E-12, 43.28377e+4, +inf, -inf, nan
|
||||||
pBoolean itemType = "boolean" // true or false
|
tBoolean valueType = "boolean" // true or false
|
||||||
pOffsetDateTime itemType = "offset datetime" // 2019-06-18 10:32:15.173645362+0200
|
tOffsetDateTime valueType = "offset datetime" // 2019-06-18 10:32:15.173645362+0200
|
||||||
pLocalDateTime itemType = "datetime" // 2018-12-25 12:12:18.876772533
|
tLocalDateTime valueType = "datetime" // 2018-12-25 12:12:18.876772533
|
||||||
pLocalDate itemType = "date" // 2017-05-17
|
tLocalDate valueType = "date" // 2017-05-17
|
||||||
pLocalTime itemType = "time" // 23:01:22
|
tLocalTime valueType = "time" // 23:01:22
|
||||||
pArrayOfTables itemType = "array" // defined using an [[array.of.tables]]
|
tArrayOfTables valueType = "array" // defined using an [[array.of.tables]]
|
||||||
pArray itemType = "static array" // defined using ["an", "inline", "array"]
|
tArray valueType = "static array" // defined using ["an", "inline", "array"]
|
||||||
pTable itemType = "table" // defined using { "inline" = "table" } or [standard.table]
|
tTable valueType = "table" // defined using { "inline" = "table" } or [standard.table]
|
||||||
)
|
)
|
||||||
|
|
||||||
// newItem instantiates a new item struct.
|
// newItem instantiates a new item struct.
|
||||||
func newItem(itemType itemType, data ...interface{}) *item {
|
func newItem(valueType valueType, data ...interface{}) *item {
|
||||||
return &item{Type: itemType, Data: data}
|
return &item{Type: valueType, Data: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newKey instantiates a new key.
|
// newKey instantiates a new key.
|
||||||
|
@ -47,7 +47,6 @@ func newKey(key ...string) []string {
|
||||||
// parser holds the state for the TOML parser. All parsing functions are
|
// parser holds the state for the TOML parser. All parsing functions are
|
||||||
// methods of this struct.
|
// methods of this struct.
|
||||||
type parser struct {
|
type parser struct {
|
||||||
Items []*item // a buffer for holding parsed items
|
|
||||||
Root table // the root-level TOML table (each TOML doc is implicitly a table)
|
Root table // the root-level TOML table (each TOML doc is implicitly a table)
|
||||||
Current table // the currently active TOML table
|
Current table // the currently active TOML table
|
||||||
CurrentKey []string // the key for the currently active TOML table
|
CurrentKey []string // the key for the currently active TOML table
|
||||||
|
@ -93,7 +92,7 @@ func (t *parser) openTable(key []string) error {
|
||||||
}
|
}
|
||||||
// The subtable does not exist yet. Create the subtable.
|
// The subtable does not exist yet. Create the subtable.
|
||||||
subTable := make(table)
|
subTable := make(table)
|
||||||
node[lastKeyPart] = newItem(pTable, subTable)
|
node[lastKeyPart] = newItem(tTable, subTable)
|
||||||
node = subTable
|
node = subTable
|
||||||
|
|
||||||
// From here on, key/value pairs are added to the newly defined table.
|
// From here on, key/value pairs are added to the newly defined table.
|
||||||
|
@ -118,7 +117,7 @@ func (t *parser) openArrayOfTables(key []string) error {
|
||||||
// At the last key position, there must be either no value yet, or the
|
// At the last key position, there must be either no value yet, or the
|
||||||
// existing value must be a table array. Other values are invalid.
|
// existing value must be a table array. Other values are invalid.
|
||||||
if existing, ok := node[lastKeyPart]; ok {
|
if existing, ok := node[lastKeyPart]; ok {
|
||||||
if existing.Type != pArrayOfTables {
|
if existing.Type != tArrayOfTables {
|
||||||
path := t.formatKeyPath(key, len(key)-1)
|
path := t.formatKeyPath(key, len(key)-1)
|
||||||
return fmt.Errorf("invalid table array: %s item already exists at key %s", existing.Type, path)
|
return fmt.Errorf("invalid table array: %s item already exists at key %s", existing.Type, path)
|
||||||
}
|
}
|
||||||
|
@ -126,13 +125,13 @@ func (t *parser) openArrayOfTables(key []string) error {
|
||||||
array := node[lastKeyPart]
|
array := node[lastKeyPart]
|
||||||
subTable := make(table)
|
subTable := make(table)
|
||||||
tables := array.Data
|
tables := array.Data
|
||||||
tables = append(tables, newItem(pTable, subTable))
|
tables = append(tables, newItem(tTable, subTable))
|
||||||
array.Data = tables
|
array.Data = tables
|
||||||
node = subTable
|
node = subTable
|
||||||
} else {
|
} else {
|
||||||
// No value exists at the defined key path. Create a new table array.
|
// No value exists at the defined key path. Create a new table array.
|
||||||
subTable := make(table)
|
subTable := make(table)
|
||||||
node[lastKeyPart] = newItem(pArrayOfTables, newItem(pTable, subTable))
|
node[lastKeyPart] = newItem(tArrayOfTables, newItem(tTable, subTable))
|
||||||
node = subTable
|
node = subTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +153,7 @@ func (t *parser) makeTablePath(key []string) (table, string, error) {
|
||||||
if subItem, ok := node[keyPart]; ok {
|
if subItem, ok := node[keyPart]; ok {
|
||||||
// You cannot overwrite an already defined key, regardless its value.
|
// 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.
|
// When a value already exists at the current key, this can only be a table.
|
||||||
if subItem.Type != pTable {
|
if subItem.Type != tTable {
|
||||||
path := t.formatKeyPath(key, i)
|
path := t.formatKeyPath(key, i)
|
||||||
return nil, "", fmt.Errorf("%s item already exists at key %s", subItem.Type, path)
|
return nil, "", fmt.Errorf("%s item already exists at key %s", subItem.Type, path)
|
||||||
}
|
}
|
||||||
|
@ -163,7 +162,7 @@ func (t *parser) makeTablePath(key []string) (table, string, error) {
|
||||||
} else {
|
} else {
|
||||||
// The subtable does not exist yet. Create the subtable.
|
// The subtable does not exist yet. Create the subtable.
|
||||||
subTable := make(table)
|
subTable := make(table)
|
||||||
node[keyPart] = newItem(pTable, subTable)
|
node[keyPart] = newItem(tTable, subTable)
|
||||||
node = subTable
|
node = subTable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,30 +201,31 @@ func formatKeyName(key string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t table) String() string {
|
func (t table) String() string {
|
||||||
return newItem(pTable, t).String()
|
return newItem(tTable, t).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String() produces a JSON-like (but not JSON) string representation of the value.
|
||||||
func (parseItem item) String() string {
|
func (parseItem item) String() string {
|
||||||
switch parseItem.Type {
|
switch parseItem.Type {
|
||||||
case pString:
|
case tString:
|
||||||
return fmt.Sprintf("%q", parseItem.Data[0])
|
return fmt.Sprintf("%q", parseItem.Data[0])
|
||||||
case pOffsetDateTime:
|
case tOffsetDateTime:
|
||||||
return parseItem.Data[0].(time.Time).Format(time.RFC3339Nano)
|
return parseItem.Data[0].(time.Time).Format(time.RFC3339Nano)
|
||||||
case pLocalDateTime:
|
case tLocalDateTime:
|
||||||
return parseItem.Data[0].(time.Time).Format("2006-01-02 15:04:05.999999999")
|
return parseItem.Data[0].(time.Time).Format("2006-01-02 15:04:05.999999999")
|
||||||
case pLocalDate:
|
case tLocalDate:
|
||||||
return parseItem.Data[0].(time.Time).Format("2006-01-02")
|
return parseItem.Data[0].(time.Time).Format("2006-01-02")
|
||||||
case pLocalTime:
|
case tLocalTime:
|
||||||
return parseItem.Data[0].(time.Time).Format("15:04:05.999999999")
|
return parseItem.Data[0].(time.Time).Format("15:04:05.999999999")
|
||||||
case pArrayOfTables:
|
case tArrayOfTables:
|
||||||
fallthrough
|
fallthrough
|
||||||
case pArray:
|
case tArray:
|
||||||
items := make([]string, len(parseItem.Data))
|
items := make([]string, len(parseItem.Data))
|
||||||
for i, value := range parseItem.Data {
|
for i, value := range parseItem.Data {
|
||||||
items[i] = value.(*item).String()
|
items[i] = value.(*item).String()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("[%s]", strings.Join(items, ", "))
|
return fmt.Sprintf("[%s]", strings.Join(items, ", "))
|
||||||
case pTable:
|
case tTable:
|
||||||
pairs := parseItem.Data[0].(table)
|
pairs := parseItem.Data[0].(table)
|
||||||
keys := make([]string, len(pairs))
|
keys := make([]string, len(pairs))
|
||||||
i := 0
|
i := 0
|
||||||
|
|
54
ast_test.go
54
ast_test.go
|
@ -7,20 +7,20 @@ import (
|
||||||
func TestAST_ConstructStructure(t *testing.T) {
|
func TestAST_ConstructStructure(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.setKeyValuePair(newKey("ding"), newItem(pInteger, 10))
|
p.setKeyValuePair(newKey("ding"), newItem(tInteger, 10))
|
||||||
p.setKeyValuePair(newKey("dong"), newItem(pString, "not a song"))
|
p.setKeyValuePair(newKey("dong"), newItem(tString, "not a song"))
|
||||||
p.openTable(newKey("key1", "key2 a"))
|
p.openTable(newKey("key1", "key2 a"))
|
||||||
p.setKeyValuePair(newKey("dooh"), newItem(pBoolean, true))
|
p.setKeyValuePair(newKey("dooh"), newItem(tBoolean, true))
|
||||||
p.setKeyValuePair(newKey("dah"), newItem(pBoolean, false))
|
p.setKeyValuePair(newKey("dah"), newItem(tBoolean, false))
|
||||||
p.openTable(newKey("key1", "key2 b"))
|
p.openTable(newKey("key1", "key2 b"))
|
||||||
p.setKeyValuePair(newKey("dieh"), newItem(pFloat, 1.111))
|
p.setKeyValuePair(newKey("dieh"), newItem(tFloat, 1.111))
|
||||||
p.setKeyValuePair(newKey("duh"), newItem(pFloat, 1.18e-12))
|
p.setKeyValuePair(newKey("duh"), newItem(tFloat, 1.18e-12))
|
||||||
p.setKeyValuePair(newKey("foo", "bar"), newItem(pArrayOfTables, newItem(pInteger, 1), newItem(pInteger, 2)))
|
p.setKeyValuePair(newKey("foo", "bar"), newItem(tArrayOfTables, newItem(tInteger, 1), newItem(tInteger, 2)))
|
||||||
p.openArrayOfTables(newKey("aaah", "table array"))
|
p.openArrayOfTables(newKey("aaah", "table array"))
|
||||||
p.setKeyValuePair(newKey("a"), newItem(pFloat, 1.234))
|
p.setKeyValuePair(newKey("a"), newItem(tFloat, 1.234))
|
||||||
p.openArrayOfTables(newKey("aaah", "table array"))
|
p.openArrayOfTables(newKey("aaah", "table array"))
|
||||||
p.setKeyValuePair(newKey("b"), newItem(pFloat, 2.345))
|
p.setKeyValuePair(newKey("b"), newItem(tFloat, 2.345))
|
||||||
p.setKeyValuePair(newKey("c"), newItem(pString, "bingo!"))
|
p.setKeyValuePair(newKey("c"), newItem(tString, "bingo!"))
|
||||||
p.openArrayOfTables(newKey("aaah", "table array"))
|
p.openArrayOfTables(newKey("aaah", "table array"))
|
||||||
return nil, p
|
return nil, p
|
||||||
},
|
},
|
||||||
|
@ -44,8 +44,8 @@ func TestAST_EmptyKeyForCreatingTablePath_Panics(t *testing.T) {
|
||||||
func TestAST_StoreValueInRootTable(t *testing.T) {
|
func TestAST_StoreValueInRootTable(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.setKeyValuePair(newKey("key1"), newItem(pString, "value1"))
|
p.setKeyValuePair(newKey("key1"), newItem(tString, "value1"))
|
||||||
return p.setKeyValuePair(newKey("key2"), newItem(pString, "value2")), p
|
return p.setKeyValuePair(newKey("key2"), newItem(tString, "value2")), p
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
`{"key1": "value1", "key2": "value2"}`)
|
`{"key1": "value1", "key2": "value2"}`)
|
||||||
|
@ -54,7 +54,7 @@ func TestAST_StoreValueInRootTable(t *testing.T) {
|
||||||
func TestAST_StoreValueWithMultipartKey_CreatesTableHierarchy(t *testing.T) {
|
func TestAST_StoreValueWithMultipartKey_CreatesTableHierarchy(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
return p.setKeyValuePair(newKey("key1", "key2", "key3"), newItem(pString, "value")), p
|
return p.setKeyValuePair(newKey("key1", "key2", "key3"), newItem(tString, "value")), p
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
`{"key1": {"key2": {"key3": "value"}}}`)
|
`{"key1": {"key2": {"key3": "value"}}}`)
|
||||||
|
@ -63,8 +63,8 @@ func TestAST_StoreValueWithMultipartKey_CreatesTableHierarchy(t *testing.T) {
|
||||||
func TestAST_StoreDuplicateKeyInRootTable_ReturnsError(t *testing.T) {
|
func TestAST_StoreDuplicateKeyInRootTable_ReturnsError(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.setKeyValuePair(newKey("key"), newItem(pString, "value"))
|
p.setKeyValuePair(newKey("key"), newItem(tString, "value"))
|
||||||
return p.setKeyValuePair(newKey("key"), newItem(pInteger, 321)), p
|
return p.setKeyValuePair(newKey("key"), newItem(tInteger, 321)), p
|
||||||
},
|
},
|
||||||
`invalid key/value pair: string item already exists at key [key]`,
|
`invalid key/value pair: string item already exists at key [key]`,
|
||||||
`{"key": "value"}`)
|
`{"key": "value"}`)
|
||||||
|
@ -74,7 +74,7 @@ func TestAST_StoreValueWithMultipartKey_UnderSubtable_CreatesTableHierarchy(t *t
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.openTable(newKey("tablekey1", "tablekey2"))
|
p.openTable(newKey("tablekey1", "tablekey2"))
|
||||||
return p.setKeyValuePair(newKey("valuekey1", "valuekey2", "valuekey3"), newItem(pString, "value")), p
|
return p.setKeyValuePair(newKey("valuekey1", "valuekey2", "valuekey3"), newItem(tString, "value")), p
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
`{"tablekey1": {"tablekey2": {"valuekey1": {"valuekey2": {"valuekey3": "value"}}}}}`)
|
`{"tablekey1": {"tablekey2": {"valuekey1": {"valuekey2": {"valuekey3": "value"}}}}}`)
|
||||||
|
@ -84,8 +84,8 @@ func TestAST_StoreKeyPathWherePathContainsNonTableAlready_ReturnsError(t *testin
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.openTable(newKey("key1"))
|
p.openTable(newKey("key1"))
|
||||||
p.setKeyValuePair(newKey("key2"), newItem(pInteger, 0))
|
p.setKeyValuePair(newKey("key2"), newItem(tInteger, 0))
|
||||||
return p.setKeyValuePair(newKey("key2", "key3"), newItem(pString, "value")), p
|
return p.setKeyValuePair(newKey("key2", "key3"), newItem(tString, "value")), p
|
||||||
},
|
},
|
||||||
`invalid key/value pair: integer item already exists at key [key1->key2]`,
|
`invalid key/value pair: integer item already exists at key [key1->key2]`,
|
||||||
`{"key1": {"key2": 0}}`)
|
`{"key1": {"key2": 0}}`)
|
||||||
|
@ -104,7 +104,7 @@ func TestAST_GivenExistingTableAtKey_CreatingTableAtSameKey_ReturnsError(t *test
|
||||||
func TestAST_GivenExistingItemAtKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) {
|
func TestAST_GivenExistingItemAtKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.setKeyValuePair(newKey("key"), newItem(pString, "value"))
|
p.setKeyValuePair(newKey("key"), newItem(tString, "value"))
|
||||||
return p.openTable(newKey("key")), p
|
return p.openTable(newKey("key")), p
|
||||||
},
|
},
|
||||||
`invalid table: string item already exists at key [key]`,
|
`invalid table: string item already exists at key [key]`,
|
||||||
|
@ -114,7 +114,7 @@ func TestAST_GivenExistingItemAtKey_CreatingTableAtSameKey_ReturnsError(t *testi
|
||||||
func TestAST_GivenExistingItemInKeyPath_CreatingTable_ReturnsError(t *testing.T) {
|
func TestAST_GivenExistingItemInKeyPath_CreatingTable_ReturnsError(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.setKeyValuePair(newKey("key"), newItem(pString, "value"))
|
p.setKeyValuePair(newKey("key"), newItem(tString, "value"))
|
||||||
return p.openTable(newKey("key", "subkey")), p
|
return p.openTable(newKey("key", "subkey")), p
|
||||||
},
|
},
|
||||||
`invalid table: string item already exists at key [key]`,
|
`invalid table: string item already exists at key [key]`,
|
||||||
|
@ -125,7 +125,7 @@ func TestAST_GivenExistingItemAtDeepKey_CreatingTableAtSameKey_ReturnsError(t *t
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.openTable(newKey("deep", "table"))
|
p.openTable(newKey("deep", "table"))
|
||||||
p.setKeyValuePair(newKey("key"), newItem(pInteger, 0))
|
p.setKeyValuePair(newKey("key"), newItem(tInteger, 0))
|
||||||
return p.openTable(newKey("deep", "table", "key")), p
|
return p.openTable(newKey("deep", "table", "key")), p
|
||||||
},
|
},
|
||||||
`invalid table: integer item already exists at key [deep->table->key]`,
|
`invalid table: integer item already exists at key [deep->table->key]`,
|
||||||
|
@ -136,8 +136,8 @@ func TestAST_GivenExistingItemAtDeepKeyFromSubTable_CreatingTableAtSameKey_Retur
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.openTable(newKey("deep", "table"))
|
p.openTable(newKey("deep", "table"))
|
||||||
p.setKeyValuePair(newKey("key1", "key2"), newItem(pInteger, 0))
|
p.setKeyValuePair(newKey("key1", "key2"), newItem(tInteger, 0))
|
||||||
return p.setKeyValuePair(newKey("key1", "key2"), newItem(pBoolean, true)), p
|
return p.setKeyValuePair(newKey("key1", "key2"), newItem(tBoolean, true)), p
|
||||||
},
|
},
|
||||||
// This test mainly tests the formatting of [deep->table->key1->key2], being a concatenation
|
// This test mainly tests the formatting of [deep->table->key1->key2], being a concatenation
|
||||||
// of the currently active table plus the multipart key for setKeyValuePair().
|
// of the currently active table plus the multipart key for setKeyValuePair().
|
||||||
|
@ -149,8 +149,8 @@ func TestAST_FormattingOfQuotedPathPartInError(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.openTable(newKey("must be quoted"))
|
p.openTable(newKey("must be quoted"))
|
||||||
p.setKeyValuePair(newKey("this one too"), newItem(pInteger, 0))
|
p.setKeyValuePair(newKey("this one too"), newItem(tInteger, 0))
|
||||||
return p.setKeyValuePair(newKey("this one too"), newItem(pInteger, 0)), p
|
return p.setKeyValuePair(newKey("this one too"), newItem(tInteger, 0)), p
|
||||||
},
|
},
|
||||||
`invalid key/value pair: integer item already exists at key ["must be quoted"->"this one too"]`,
|
`invalid key/value pair: integer item already exists at key ["must be quoted"->"this one too"]`,
|
||||||
`{"must be quoted": {"this one too": 0}}`)
|
`{"must be quoted": {"this one too": 0}}`)
|
||||||
|
@ -159,7 +159,7 @@ func TestAST_FormattingOfQuotedPathPartInError(t *testing.T) {
|
||||||
func TestAST_GivenExistingItemAtKey_CreatingArrayOfTablesAtSameKey_ReturnsError(t *testing.T) {
|
func TestAST_GivenExistingItemAtKey_CreatingArrayOfTablesAtSameKey_ReturnsError(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.Root["key"] = newItem(pString, "value")
|
p.Root["key"] = newItem(tString, "value")
|
||||||
return p.openArrayOfTables(newKey("key")), p
|
return p.openArrayOfTables(newKey("key")), p
|
||||||
},
|
},
|
||||||
`invalid table array: string item already exists at key [key]`,
|
`invalid table array: string item already exists at key [key]`,
|
||||||
|
@ -169,7 +169,7 @@ func TestAST_GivenExistingItemAtKey_CreatingArrayOfTablesAtSameKey_ReturnsError(
|
||||||
func TestAST_GivenExistingItemInKeyPath_CreatingArrayOfTables_ReturnsError(t *testing.T) {
|
func TestAST_GivenExistingItemInKeyPath_CreatingArrayOfTables_ReturnsError(t *testing.T) {
|
||||||
testAST(t, func() (error, *parser) {
|
testAST(t, func() (error, *parser) {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
p.Root["key"] = newItem(pString, "value")
|
p.Root["key"] = newItem(tString, "value")
|
||||||
return p.openArrayOfTables(newKey("key", "subkey")), p
|
return p.openArrayOfTables(newKey("key", "subkey")), p
|
||||||
},
|
},
|
||||||
`invalid table array: string item already exists at key [key]`,
|
`invalid table array: string item already exists at key [key]`,
|
||||||
|
|
|
@ -7,53 +7,6 @@ import (
|
||||||
"git.makaay.nl/mauricem/go-parsekit/parse"
|
"git.makaay.nl/mauricem/go-parsekit/parse"
|
||||||
)
|
)
|
||||||
|
|
||||||
type statesT struct {
|
|
||||||
name string
|
|
||||||
in string
|
|
||||||
out interface{}
|
|
||||||
err string
|
|
||||||
}
|
|
||||||
|
|
||||||
type parseTest struct {
|
|
||||||
input interface{}
|
|
||||||
expected []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func testParseHandler(t *testing.T, p *parser, handler parse.Handler, test parseTest) {
|
|
||||||
var err error
|
|
||||||
defer func() {
|
|
||||||
recovered := recover()
|
|
||||||
results := []string{}
|
|
||||||
for _, item := range p.Items {
|
|
||||||
results = append(results, item.String())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
results = append(results, fmt.Sprintf("Error: %s", err))
|
|
||||||
}
|
|
||||||
if recovered != nil {
|
|
||||||
results = append(results, fmt.Sprintf("Panic: %s", recovered.(string)))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, e := range test.expected {
|
|
||||||
if i > len(results)-1 {
|
|
||||||
t.Errorf("No result at index %d for input %q, expected: %s", i, test.input, e)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r := results[i]
|
|
||||||
if e != r {
|
|
||||||
t.Errorf("Unexpected result at index %d for input %q:\nexpected: %s\nactual: %s\n", i, test.input, e, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(results) > len(test.expected) {
|
|
||||||
t.Errorf("Got more results than expected for input %q, surplus result(s):\n", test.input)
|
|
||||||
for i := len(test.expected); i < len(results); i++ {
|
|
||||||
t.Errorf("[%d] %s", i, results[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
err = parse.New(handler)(test.input)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAST(t *testing.T, code func() (error, *parser), expectedError string, expectedData string) {
|
func testAST(t *testing.T, code func() (error, *parser), expectedError string, expectedData string) {
|
||||||
err, p := code()
|
err, p := code()
|
||||||
if expectedError == "" {
|
if expectedError == "" {
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (t *parser) parseArray(p *parse.API) (*item, bool) {
|
||||||
|
|
||||||
// Check for an empty array.
|
// Check for an empty array.
|
||||||
if p.Accept(arrayClose) {
|
if p.Accept(arrayClose) {
|
||||||
return newItem(pArray), true
|
return newItem(tArray), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not an empty array, parse the array items.
|
// Not an empty array, parse the array items.
|
||||||
|
@ -68,7 +68,7 @@ func (t *parser) parseArray(p *parse.API) (*item, bool) {
|
||||||
|
|
||||||
// Check for the end of the array.
|
// Check for the end of the array.
|
||||||
if p.Accept(arrayClose) {
|
if p.Accept(arrayClose) {
|
||||||
return newItem(pArray, items...), true
|
return newItem(tArray, items...), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not the end of the array? Then we should find an array separator.
|
// Not the end of the array? Then we should find an array separator.
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"git.makaay.nl/mauricem/go-parsekit/parse"
|
"git.makaay.nl/mauricem/go-parsekit/parse"
|
||||||
)
|
)
|
||||||
|
|
||||||
var falseItem = newItem(pBoolean, false)
|
var falseItem = newItem(tBoolean, false)
|
||||||
var trueItem = newItem(pBoolean, true)
|
var trueItem = newItem(tBoolean, true)
|
||||||
|
|
||||||
// Booleans are just the tokens you're used to. Always lowercase.
|
// Booleans are just the tokens you're used to. Always lowercase.
|
||||||
func (t *parser) parseBoolean(p *parse.API) (*item, bool) {
|
func (t *parser) parseBoolean(p *parse.API) (*item, bool) {
|
||||||
|
|
|
@ -66,10 +66,10 @@ var (
|
||||||
// The full date/time parse format, based on the above definitions.
|
// The full date/time parse format, based on the above definitions.
|
||||||
// The first token denotes the type of date/time value.
|
// The first token denotes the type of date/time value.
|
||||||
// The rest of the tokens contain layout fragments for time.Parse().
|
// The rest of the tokens contain layout fragments for time.Parse().
|
||||||
offsetDateTime = tok.Str(pOffsetDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok, tzTok))
|
offsetDateTime = tok.Str(tOffsetDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok, tzTok))
|
||||||
localDateTime = tok.Str(pLocalDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok))
|
localDateTime = tok.Str(tLocalDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok))
|
||||||
localDate = tok.Str(pLocalDate, dateTok)
|
localDate = tok.Str(tLocalDate, dateTok)
|
||||||
localTime = tok.Str(pLocalTime, c.Seq(timeTok, microTok))
|
localTime = tok.Str(tLocalTime, c.Seq(timeTok, microTok))
|
||||||
datetime = c.Any(offsetDateTime, localDateTime, localDate, localTime)
|
datetime = c.Any(offsetDateTime, localDateTime, localDate, localTime)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,8 +91,8 @@ func (t *parser) parseDateTime(p *parse.API) (*item, bool) {
|
||||||
|
|
||||||
// The first token is a token that wraps the complete date/time input.
|
// The first token is a token that wraps the complete date/time input.
|
||||||
// Its type denotes the type of date/time value that it wraps.
|
// Its type denotes the type of date/time value that it wraps.
|
||||||
func getDateTimeValueType(tokens *[]*tokenize.Token) itemType {
|
func getDateTimeValueType(tokens *[]*tokenize.Token) valueType {
|
||||||
return (*tokens)[0].Type.(itemType)
|
return (*tokens)[0].Type.(valueType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rest of the tokens contain fragments that can be used with
|
// The rest of the tokens contain fragments that can be used with
|
||||||
|
|
|
@ -68,18 +68,18 @@ var (
|
||||||
func (t *parser) parseNumber(p *parse.API) (*item, bool) {
|
func (t *parser) parseNumber(p *parse.API) (*item, bool) {
|
||||||
switch {
|
switch {
|
||||||
case p.Accept(tok.Float64(nil, float)):
|
case p.Accept(tok.Float64(nil, float)):
|
||||||
return newItem(pFloat, p.Result().Value(0).(float64)), true
|
return newItem(tFloat, p.Result().Value(0).(float64)), true
|
||||||
case p.Accept(nan):
|
case p.Accept(nan):
|
||||||
return newItem(pFloat, math.NaN()), true
|
return newItem(tFloat, math.NaN()), true
|
||||||
case p.Accept(inf):
|
case p.Accept(inf):
|
||||||
if p.Result().Rune(0) == '-' {
|
if p.Result().Rune(0) == '-' {
|
||||||
return newItem(pFloat, math.Inf(-1)), true
|
return newItem(tFloat, math.Inf(-1)), true
|
||||||
}
|
}
|
||||||
return newItem(pFloat, math.Inf(+1)), true
|
return newItem(tFloat, math.Inf(+1)), true
|
||||||
case p.Accept(a.Zero):
|
case p.Accept(a.Zero):
|
||||||
return t.parseIntegerStartingWithZero(p)
|
return t.parseIntegerStartingWithZero(p)
|
||||||
case p.Accept(tok.Int64(nil, integer)):
|
case p.Accept(tok.Int64(nil, integer)):
|
||||||
return newItem(pInteger, p.Result().Value(0).(int64)), true
|
return newItem(tInteger, p.Result().Value(0).(int64)), true
|
||||||
default:
|
default:
|
||||||
p.Expected("a number")
|
p.Expected("a number")
|
||||||
return nil, false
|
return nil, false
|
||||||
|
@ -97,10 +97,10 @@ func (t *parser) parseIntegerStartingWithZero(p *parse.API) (*item, bool) {
|
||||||
case p.Accept(binary):
|
case p.Accept(binary):
|
||||||
value, err = strconv.ParseInt(p.Result().Value(0).(string), 2, 64)
|
value, err = strconv.ParseInt(p.Result().Value(0).(string), 2, 64)
|
||||||
default:
|
default:
|
||||||
return newItem(pInteger, int64(0)), true
|
return newItem(tInteger, int64(0)), true
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return newItem(pInteger, value), true
|
return newItem(tInteger, value), true
|
||||||
}
|
}
|
||||||
p.Error("invalid integer value 0%s: %s", p.Result().String(), err)
|
p.Error("invalid integer value 0%s: %s", p.Result().String(), err)
|
||||||
return nil, false
|
return nil, false
|
||||||
|
|
|
@ -59,7 +59,7 @@ func (t *parser) parseString(p *parse.API) (*item, bool) {
|
||||||
p.Expected("a string value")
|
p.Expected("a string value")
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
return newItem(pString, value), ok
|
return newItem(tString, value), ok
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTableKey(t *testing.T) {
|
func TestTableKey(t *testing.T) {
|
||||||
for _, test := range []parseTest{
|
for _, test := range []parseToASTTest{
|
||||||
{"", []string{`Error: unexpected end of file (expected a table) at start of file`}},
|
{"[", `{}`, `unexpected end of file (expected a key name) at line 1, column 2`},
|
||||||
{"[", []string{`Error: unexpected end of file (expected a key name) at line 1, column 2`}},
|
{" \t [", `{}`, `unexpected end of file (expected a key name) at line 1, column 5`},
|
||||||
{" \t [", []string{`Error: unexpected end of file (expected a key name) at line 1, column 5`}},
|
{" \t [key", `{}`, `unexpected end of file (expected closing ']' for table name) at line 1, column 8`},
|
||||||
{" \t [key", []string{`Error: unexpected end of file (expected closing ']' for table name) at line 1, column 8`}},
|
{" \t [key.", `{}`, `unexpected end of file (expected a key name) at line 1, column 9`},
|
||||||
{" \t [key.", []string{`Error: unexpected end of file (expected a key name) at line 1, column 9`}},
|
{" \t [key.'sub key'", `{}`, `unexpected end of file (expected closing ']' for table name) at line 1, column 18`},
|
||||||
{" \t [key.'sub key'", []string{`Error: unexpected end of file (expected closing ']' for table name) at line 1, column 18`}},
|
{" \t [key.'sub key' \t]", `{"key": {"sub key": {}}}`, ``},
|
||||||
{" \t [key.'sub key' \t]", []string{}},
|
{" \t [key.'sub key' \t] \t ", `{"key": {"sub key": {}}}`, ``},
|
||||||
{" \t [key.'sub key' \t] \t ", []string{}},
|
{" \t [key.'sub key' \t] \t \n", `{"key": {"sub key": {}}}`, ``},
|
||||||
{" \t [key.'sub key' \t] \t \n", []string{}},
|
{" \t [key.'sub key' \t]# with comment\n", `{"key": {"sub key": {}}}`, ``},
|
||||||
{" \t [key.'sub key' \t]# with comment\n", []string{}},
|
{" \t [key.'sub key' \t] \t # with comment\n", `{"key": {"sub key": {}}}`, ``},
|
||||||
{" \t [key.'sub key' \t] \t # with comment\n", []string{}},
|
|
||||||
} {
|
} {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
testParseHandler(t, p, p.startTable, test)
|
testParseToAST(t, p, p.startTable, test)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,20 +42,20 @@ func TestTable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayOfTableKey(t *testing.T) {
|
func TestArrayOfTableKey(t *testing.T) {
|
||||||
for _, test := range []parseTest{
|
for _, test := range []parseToASTTest{
|
||||||
{"[[", []string{`Error: unexpected end of file (expected a key name) at line 1, column 3`}},
|
{"[[", `{}`, `unexpected end of file (expected a key name) at line 1, column 3`},
|
||||||
{" \t [[", []string{`Error: unexpected end of file (expected a key name) at line 1, column 6`}},
|
{" \t [[", `{}`, `unexpected end of file (expected a key name) at line 1, column 6`},
|
||||||
{" \t [[key", []string{`Error: unexpected end of file (expected closing ']]' for array of tables name) at line 1, column 9`}},
|
{" \t [[key", `{}`, `unexpected end of file (expected closing ']]' for array of tables name) at line 1, column 9`},
|
||||||
{" \t [[key.", []string{`Error: unexpected end of file (expected a key name) at line 1, column 10`}},
|
{" \t [[key.", `{}`, `unexpected end of file (expected a key name) at line 1, column 10`},
|
||||||
{" \t [[key.'sub key'", []string{`Error: unexpected end of file (expected closing ']]' for array of tables name) at line 1, column 19`}},
|
{" \t [[key.'sub key'", `{}`, `unexpected end of file (expected closing ']]' for array of tables name) at line 1, column 19`},
|
||||||
{" \t [[key.'sub key' \t]]", []string{}},
|
{" \t [[key.'sub key' \t]]", `{"key": {"sub key": [{}]}}`, ``},
|
||||||
{" \t [[key.'sub key' \t]] \t ", []string{}},
|
{" \t [[key.'sub key' \t]] \t ", `{"key": {"sub key": [{}]}}`, ``},
|
||||||
{" \t [[key.'sub key' \t]] \t \n", []string{}},
|
{" \t [[key.'sub key' \t]] \t \n", `{"key": {"sub key": [{}]}}`, ``},
|
||||||
{" \t [[key.'sub key' \t]]# with comment\n", []string{}},
|
{" \t [[key.'sub key' \t]]# with comment\n", `{"key": {"sub key": [{}]}}`, ``},
|
||||||
{" \t [[key.'sub key' \t]] \t # with comment\n", []string{}},
|
{" \t [[key.'sub key' \t]] \t # with comment\n", `{"key": {"sub key": [{}]}}`, ``},
|
||||||
} {
|
} {
|
||||||
p := newParser()
|
p := newParser()
|
||||||
testParseHandler(t, p, p.startTable, test)
|
testParseToAST(t, p, p.startTable, test)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue