Cleaning up test code.

This commit is contained in:
Maurice Makaay 2019-06-26 06:39:41 +00:00
parent c536dd1243
commit 608e68c207
9 changed files with 101 additions and 149 deletions

64
ast.go
View File

@ -10,33 +10,33 @@ import (
// item represents a TOML item.
type item struct {
Type itemType
Type valueType
Data []interface{}
}
// table represents a TOML table.
type table map[string]*item
// itemType identifies the semantic role of a TOML item.
type itemType string
// valueType identifies the type of a TOML value.
type valueType string
const (
pString itemType = "string" // "various", 'types', """of""", '''strings'''
pInteger itemType = "integer" // 12345, 0xffee12a0, 0o0755, 0b101101011
pFloat itemType = "float" // 10.1234, 143E-12, 43.28377e+4, +inf, -inf, nan
pBoolean itemType = "boolean" // true or false
pOffsetDateTime itemType = "offset datetime" // 2019-06-18 10:32:15.173645362+0200
pLocalDateTime itemType = "datetime" // 2018-12-25 12:12:18.876772533
pLocalDate itemType = "date" // 2017-05-17
pLocalTime itemType = "time" // 23:01:22
pArrayOfTables itemType = "array" // defined using an [[array.of.tables]]
pArray itemType = "static array" // defined using ["an", "inline", "array"]
pTable itemType = "table" // defined using { "inline" = "table" } or [standard.table]
tString valueType = "string" // "various", 'types', """of""", '''strings'''
tInteger valueType = "integer" // 12345, 0xffee12a0, 0o0755, 0b101101011
tFloat valueType = "float" // 10.1234, 143E-12, 43.28377e+4, +inf, -inf, nan
tBoolean valueType = "boolean" // true or false
tOffsetDateTime valueType = "offset datetime" // 2019-06-18 10:32:15.173645362+0200
tLocalDateTime valueType = "datetime" // 2018-12-25 12:12:18.876772533
tLocalDate valueType = "date" // 2017-05-17
tLocalTime valueType = "time" // 23:01:22
tArrayOfTables valueType = "array" // defined using an [[array.of.tables]]
tArray valueType = "static array" // defined using ["an", "inline", "array"]
tTable valueType = "table" // defined using { "inline" = "table" } or [standard.table]
)
// newItem instantiates a new item struct.
func newItem(itemType itemType, data ...interface{}) *item {
return &item{Type: itemType, Data: data}
func newItem(valueType valueType, data ...interface{}) *item {
return &item{Type: valueType, Data: data}
}
// 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
// methods of this 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)
Current table // 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.
subTable := make(table)
node[lastKeyPart] = newItem(pTable, subTable)
node[lastKeyPart] = newItem(tTable, subTable)
node = subTable
// 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
// existing value must be a table array. Other values are invalid.
if existing, ok := node[lastKeyPart]; ok {
if existing.Type != pArrayOfTables {
if existing.Type != tArrayOfTables {
path := t.formatKeyPath(key, len(key)-1)
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]
subTable := make(table)
tables := array.Data
tables = append(tables, newItem(pTable, subTable))
tables = append(tables, newItem(tTable, subTable))
array.Data = tables
node = subTable
} else {
// No value exists at the defined key path. Create a new table array.
subTable := make(table)
node[lastKeyPart] = newItem(pArrayOfTables, newItem(pTable, subTable))
node[lastKeyPart] = newItem(tArrayOfTables, newItem(tTable, subTable))
node = subTable
}
@ -154,7 +153,7 @@ func (t *parser) makeTablePath(key []string) (table, string, error) {
if subItem, 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.
if subItem.Type != pTable {
if subItem.Type != tTable {
path := t.formatKeyPath(key, i)
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 {
// The subtable does not exist yet. Create the subtable.
subTable := make(table)
node[keyPart] = newItem(pTable, subTable)
node[keyPart] = newItem(tTable, subTable)
node = subTable
}
}
@ -202,30 +201,31 @@ func formatKeyName(key 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 {
switch parseItem.Type {
case pString:
case tString:
return fmt.Sprintf("%q", parseItem.Data[0])
case pOffsetDateTime:
case tOffsetDateTime:
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")
case pLocalDate:
case tLocalDate:
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")
case pArrayOfTables:
case tArrayOfTables:
fallthrough
case pArray:
case tArray:
items := make([]string, len(parseItem.Data))
for i, value := range parseItem.Data {
items[i] = value.(*item).String()
}
return fmt.Sprintf("[%s]", strings.Join(items, ", "))
case pTable:
case tTable:
pairs := parseItem.Data[0].(table)
keys := make([]string, len(pairs))
i := 0

View File

@ -7,20 +7,20 @@ import (
func TestAST_ConstructStructure(t *testing.T) {
testAST(t, func() (error, *parser) {
p := newParser()
p.setKeyValuePair(newKey("ding"), newItem(pInteger, 10))
p.setKeyValuePair(newKey("dong"), newItem(pString, "not a song"))
p.setKeyValuePair(newKey("ding"), newItem(tInteger, 10))
p.setKeyValuePair(newKey("dong"), newItem(tString, "not a song"))
p.openTable(newKey("key1", "key2 a"))
p.setKeyValuePair(newKey("dooh"), newItem(pBoolean, true))
p.setKeyValuePair(newKey("dah"), newItem(pBoolean, false))
p.setKeyValuePair(newKey("dooh"), newItem(tBoolean, true))
p.setKeyValuePair(newKey("dah"), newItem(tBoolean, false))
p.openTable(newKey("key1", "key2 b"))
p.setKeyValuePair(newKey("dieh"), newItem(pFloat, 1.111))
p.setKeyValuePair(newKey("duh"), newItem(pFloat, 1.18e-12))
p.setKeyValuePair(newKey("foo", "bar"), newItem(pArrayOfTables, newItem(pInteger, 1), newItem(pInteger, 2)))
p.setKeyValuePair(newKey("dieh"), newItem(tFloat, 1.111))
p.setKeyValuePair(newKey("duh"), newItem(tFloat, 1.18e-12))
p.setKeyValuePair(newKey("foo", "bar"), newItem(tArrayOfTables, newItem(tInteger, 1), newItem(tInteger, 2)))
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.setKeyValuePair(newKey("b"), newItem(pFloat, 2.345))
p.setKeyValuePair(newKey("c"), newItem(pString, "bingo!"))
p.setKeyValuePair(newKey("b"), newItem(tFloat, 2.345))
p.setKeyValuePair(newKey("c"), newItem(tString, "bingo!"))
p.openArrayOfTables(newKey("aaah", "table array"))
return nil, p
},
@ -44,8 +44,8 @@ func TestAST_EmptyKeyForCreatingTablePath_Panics(t *testing.T) {
func TestAST_StoreValueInRootTable(t *testing.T) {
testAST(t, func() (error, *parser) {
p := newParser()
p.setKeyValuePair(newKey("key1"), newItem(pString, "value1"))
return p.setKeyValuePair(newKey("key2"), newItem(pString, "value2")), p
p.setKeyValuePair(newKey("key1"), newItem(tString, "value1"))
return p.setKeyValuePair(newKey("key2"), newItem(tString, "value2")), p
},
"",
`{"key1": "value1", "key2": "value2"}`)
@ -54,7 +54,7 @@ func TestAST_StoreValueInRootTable(t *testing.T) {
func TestAST_StoreValueWithMultipartKey_CreatesTableHierarchy(t *testing.T) {
testAST(t, func() (error, *parser) {
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"}}}`)
@ -63,8 +63,8 @@ func TestAST_StoreValueWithMultipartKey_CreatesTableHierarchy(t *testing.T) {
func TestAST_StoreDuplicateKeyInRootTable_ReturnsError(t *testing.T) {
testAST(t, func() (error, *parser) {
p := newParser()
p.setKeyValuePair(newKey("key"), newItem(pString, "value"))
return p.setKeyValuePair(newKey("key"), newItem(pInteger, 321)), p
p.setKeyValuePair(newKey("key"), newItem(tString, "value"))
return p.setKeyValuePair(newKey("key"), newItem(tInteger, 321)), p
},
`invalid key/value pair: string item already exists at key [key]`,
`{"key": "value"}`)
@ -74,7 +74,7 @@ func TestAST_StoreValueWithMultipartKey_UnderSubtable_CreatesTableHierarchy(t *t
testAST(t, func() (error, *parser) {
p := newParser()
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"}}}}}`)
@ -84,8 +84,8 @@ func TestAST_StoreKeyPathWherePathContainsNonTableAlready_ReturnsError(t *testin
testAST(t, func() (error, *parser) {
p := newParser()
p.openTable(newKey("key1"))
p.setKeyValuePair(newKey("key2"), newItem(pInteger, 0))
return p.setKeyValuePair(newKey("key2", "key3"), newItem(pString, "value")), p
p.setKeyValuePair(newKey("key2"), newItem(tInteger, 0))
return p.setKeyValuePair(newKey("key2", "key3"), newItem(tString, "value")), p
},
`invalid key/value pair: integer item already exists at key [key1->key2]`,
`{"key1": {"key2": 0}}`)
@ -104,7 +104,7 @@ func TestAST_GivenExistingTableAtKey_CreatingTableAtSameKey_ReturnsError(t *test
func TestAST_GivenExistingItemAtKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) {
testAST(t, func() (error, *parser) {
p := newParser()
p.setKeyValuePair(newKey("key"), newItem(pString, "value"))
p.setKeyValuePair(newKey("key"), newItem(tString, "value"))
return p.openTable(newKey("key")), p
},
`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) {
testAST(t, func() (error, *parser) {
p := newParser()
p.setKeyValuePair(newKey("key"), newItem(pString, "value"))
p.setKeyValuePair(newKey("key"), newItem(tString, "value"))
return p.openTable(newKey("key", "subkey")), p
},
`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) {
p := newParser()
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
},
`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) {
p := newParser()
p.openTable(newKey("deep", "table"))
p.setKeyValuePair(newKey("key1", "key2"), newItem(pInteger, 0))
return p.setKeyValuePair(newKey("key1", "key2"), newItem(pBoolean, true)), p
p.setKeyValuePair(newKey("key1", "key2"), newItem(tInteger, 0))
return p.setKeyValuePair(newKey("key1", "key2"), newItem(tBoolean, true)), p
},
// 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().
@ -149,8 +149,8 @@ func TestAST_FormattingOfQuotedPathPartInError(t *testing.T) {
testAST(t, func() (error, *parser) {
p := newParser()
p.openTable(newKey("must be quoted"))
p.setKeyValuePair(newKey("this one too"), newItem(pInteger, 0))
return p.setKeyValuePair(newKey("this one too"), newItem(pInteger, 0)), p
p.setKeyValuePair(newKey("this one too"), newItem(tInteger, 0))
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"]`,
`{"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) {
testAST(t, func() (error, *parser) {
p := newParser()
p.Root["key"] = newItem(pString, "value")
p.Root["key"] = newItem(tString, "value")
return p.openArrayOfTables(newKey("key")), p
},
`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) {
testAST(t, func() (error, *parser) {
p := newParser()
p.Root["key"] = newItem(pString, "value")
p.Root["key"] = newItem(tString, "value")
return p.openArrayOfTables(newKey("key", "subkey")), p
},
`invalid table array: string item already exists at key [key]`,

View File

@ -7,53 +7,6 @@ import (
"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) {
err, p := code()
if expectedError == "" {

View File

@ -45,7 +45,7 @@ func (t *parser) parseArray(p *parse.API) (*item, bool) {
// Check for an empty array.
if p.Accept(arrayClose) {
return newItem(pArray), true
return newItem(tArray), true
}
// 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.
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.

View File

@ -4,8 +4,8 @@ import (
"git.makaay.nl/mauricem/go-parsekit/parse"
)
var falseItem = newItem(pBoolean, false)
var trueItem = newItem(pBoolean, true)
var falseItem = newItem(tBoolean, false)
var trueItem = newItem(tBoolean, true)
// Booleans are just the tokens you're used to. Always lowercase.
func (t *parser) parseBoolean(p *parse.API) (*item, bool) {

View File

@ -66,10 +66,10 @@ var (
// The full date/time parse format, based on the above definitions.
// The first token denotes the type of date/time value.
// The rest of the tokens contain layout fragments for time.Parse().
offsetDateTime = tok.Str(pOffsetDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok, tzTok))
localDateTime = tok.Str(pLocalDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok))
localDate = tok.Str(pLocalDate, dateTok)
localTime = tok.Str(pLocalTime, c.Seq(timeTok, microTok))
offsetDateTime = tok.Str(tOffsetDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok, tzTok))
localDateTime = tok.Str(tLocalDateTime, c.Seq(dateTok, tdelimTok, timeTok, microTok))
localDate = tok.Str(tLocalDate, dateTok)
localTime = tok.Str(tLocalTime, c.Seq(timeTok, microTok))
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.
// Its type denotes the type of date/time value that it wraps.
func getDateTimeValueType(tokens *[]*tokenize.Token) itemType {
return (*tokens)[0].Type.(itemType)
func getDateTimeValueType(tokens *[]*tokenize.Token) valueType {
return (*tokens)[0].Type.(valueType)
}
// The rest of the tokens contain fragments that can be used with

View File

@ -68,18 +68,18 @@ var (
func (t *parser) parseNumber(p *parse.API) (*item, bool) {
switch {
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):
return newItem(pFloat, math.NaN()), true
return newItem(tFloat, math.NaN()), true
case p.Accept(inf):
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):
return t.parseIntegerStartingWithZero(p)
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:
p.Expected("a number")
return nil, false
@ -97,10 +97,10 @@ func (t *parser) parseIntegerStartingWithZero(p *parse.API) (*item, bool) {
case p.Accept(binary):
value, err = strconv.ParseInt(p.Result().Value(0).(string), 2, 64)
default:
return newItem(pInteger, int64(0)), true
return newItem(tInteger, int64(0)), true
}
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)
return nil, false

View File

@ -59,7 +59,7 @@ func (t *parser) parseString(p *parse.API) (*item, bool) {
p.Expected("a string value")
}
if ok {
return newItem(pString, value), ok
return newItem(tString, value), ok
}
return nil, false
}

View File

@ -5,21 +5,20 @@ import (
)
func TestTableKey(t *testing.T) {
for _, test := range []parseTest{
{"", []string{`Error: unexpected end of file (expected a table) at start of file`}},
{"[", []string{`Error: unexpected end of file (expected a key name) at line 1, column 2`}},
{" \t [", []string{`Error: unexpected end of file (expected a key name) at line 1, column 5`}},
{" \t [key", []string{`Error: unexpected end of file (expected closing ']' for table name) at line 1, column 8`}},
{" \t [key.", []string{`Error: unexpected end of file (expected a key name) at line 1, column 9`}},
{" \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]", []string{}},
{" \t [key.'sub key' \t] \t ", []string{}},
{" \t [key.'sub key' \t] \t \n", []string{}},
{" \t [key.'sub key' \t]# with comment\n", []string{}},
{" \t [key.'sub key' \t] \t # with comment\n", []string{}},
for _, test := range []parseToASTTest{
{"[", `{}`, `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 [key", `{}`, `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.'sub key'", `{}`, `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] \t ", `{"key": {"sub key": {}}}`, ``},
{" \t [key.'sub key' \t] \t \n", `{"key": {"sub key": {}}}`, ``},
{" \t [key.'sub key' \t]# with comment\n", `{"key": {"sub key": {}}}`, ``},
{" \t [key.'sub key' \t] \t # with comment\n", `{"key": {"sub key": {}}}`, ``},
} {
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) {
for _, test := range []parseTest{
{"[[", []string{`Error: 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 [[key", []string{`Error: 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.'sub key'", []string{`Error: 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]] \t ", []string{}},
{" \t [[key.'sub key' \t]] \t \n", []string{}},
{" \t [[key.'sub key' \t]]# with comment\n", []string{}},
{" \t [[key.'sub key' \t]] \t # with comment\n", []string{}},
for _, test := range []parseToASTTest{
{"[[", `{}`, `unexpected end of file (expected a key name) at line 1, column 3`},
{" \t [[", `{}`, `unexpected end of file (expected a key name) at line 1, column 6`},
{" \t [[key", `{}`, `unexpected end of file (expected closing ']]' for array of tables name) at line 1, column 9`},
{" \t [[key.", `{}`, `unexpected end of file (expected a key name) at line 1, column 10`},
{" \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]]", `{"key": {"sub key": [{}]}}`, ``},
{" \t [[key.'sub key' \t]] \t ", `{"key": {"sub key": [{}]}}`, ``},
{" \t [[key.'sub key' \t]] \t \n", `{"key": {"sub key": [{}]}}`, ``},
{" \t [[key.'sub key' \t]]# with comment\n", `{"key": {"sub key": [{}]}}`, ``},
{" \t [[key.'sub key' \t]] \t # with comment\n", `{"key": {"sub key": [{}]}}`, ``},
} {
p := newParser()
testParseHandler(t, p, p.startTable, test)
testParseToAST(t, p, p.startTable, test)
}
}