Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Return []any for hash arrays too
Browse files Browse the repository at this point in the history
veigaribo committed Aug 1, 2024

Verified

This commit was signed with the committer’s verified signature.
veigaribo Gabriel Lopes Veiga
1 parent eb72747 commit e4bc5d9
Showing 4 changed files with 30 additions and 42 deletions.
4 changes: 2 additions & 2 deletions decode_test.go
Original file line number Diff line number Diff line change
@@ -642,9 +642,9 @@ func (d *dish) UnmarshalTOML(p any) error {
data, _ := p.(map[string]any)
d.Name, _ = data["name"].(string)
d.Price, _ = data["price"].(float32)
ingredients, _ := data["ingredients"].([]map[string]any)
ingredients, _ := data["ingredients"].([]any)
for _, e := range ingredients {
n, _ := any(e).(map[string]any)
n, _ := e.(map[string]any)
name, _ := n["name"].(string)
i := ingredient{name}
d.Ingredients = append(d.Ingredients, i)
6 changes: 0 additions & 6 deletions internal/tag/add.go
Original file line number Diff line number Diff line change
@@ -27,12 +27,6 @@ func Add(key string, tomlData any) any {

// An array: we don't need to add any tags, just recurse for every table
// entry.
case []map[string]any:
typed := make([]map[string]any, len(orig))
for i, v := range orig {
typed[i] = Add("", v).(map[string]any)
}
return typed
case []any:
typed := make([]any, len(orig))
for i, v := range orig {
23 changes: 3 additions & 20 deletions internal/toml-test/toml.go
Original file line number Diff line number Diff line change
@@ -38,12 +38,6 @@ func (r Test) CompareTOML(want, have any) Test {
switch w := want.(type) {
case map[string]any:
return r.cmpTOMLMap(w, have)
case []map[string]any:
ww := make([]any, 0, len(w))
for _, v := range w {
ww = append(ww, v)
}
return r.cmpTOMLArrays(ww, have)
case []any:
return r.cmpTOMLArrays(w, have)
default:
@@ -83,21 +77,10 @@ func (r Test) cmpTOMLMap(want map[string]any, have any) Test {
}

func (r Test) cmpTOMLArrays(want []any, have any) Test {
// Slice can be decoded to []any for an array of primitives, or
// []map[string]any for an array of tables.
//
// TODO: it would be nicer if it could always decode to []any?
haveSlice, ok := have.([]any)
if !ok {
tblArray, ok := have.([]map[string]any)
if !ok {
return r.mismatch("array", want, have)
}

haveSlice = make([]any, len(tblArray))
for i := range tblArray {
haveSlice[i] = tblArray[i]
}
if !ok {
return r.mismatch("array", want, have)
}

if len(want) != len(haveSlice) {
@@ -146,7 +129,7 @@ func deepEqual(want, have any) bool {

func isTomlValue(v any) bool {
switch v.(type) {
case map[string]any, []map[string]any, []any:
case map[string]any, []any:
return false
}
return true
39 changes: 25 additions & 14 deletions parse.go
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ type parser struct {
type keyInfo struct {
pos Position
tomlType tomlType
locked bool // Writing to inline arrays after definition is forbidden.
}

func parse(data string) (p *parser, err error) {
@@ -173,7 +174,7 @@ func (p *parser) topLevel(item item) {
p.assertEqual(itemTableEnd, name.typ)

p.addContext(key, false)
p.setType("", tomlHash, item.pos)
p.setType("", tomlHash, item.pos, false)
p.ordered = append(p.ordered, key)
case itemArrayTableStart: // [[ .. ]]
name := p.nextPos()
@@ -185,7 +186,7 @@ func (p *parser) topLevel(item item) {
p.assertEqual(itemArrayTableEnd, name.typ)

p.addContext(key, true)
p.setType("", tomlArrayHash, item.pos)
p.setType("", tomlArrayHash, item.pos, false)
p.ordered = append(p.ordered, key)
case itemKeyStart: // key = ..
outerContext := p.context
@@ -212,7 +213,7 @@ func (p *parser) topLevel(item item) {
vItem := p.next()
val, typ := p.value(vItem, false)
p.setValue(p.currentKey, val)
p.setType(p.currentKey, typ, vItem.pos)
p.setType(p.currentKey, typ, vItem.pos, true)

/// Remove the context we added (preserving any context from [tbl] lines).
p.context = outerContext
@@ -408,7 +409,7 @@ func missingLeadingZero(d, l string) bool {
}

func (p *parser) valueArray(it item) (any, tomlType) {
p.setType(p.currentKey, tomlArray, it.pos)
p.setType(p.currentKey, tomlArray, it.pos, false)

var (
// Initialize to a non-nil slice to make it consistent with how S = []
@@ -478,7 +479,7 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (any, tomlType) {
/// Set the value.
val, typ := p.value(p.next(), false)
p.setValue(p.currentKey, val)
p.setType(p.currentKey, typ, it.pos)
p.setType(p.currentKey, typ, it.pos, true)

hash := topHash
for _, c := range context {
@@ -575,8 +576,12 @@ func (p *parser) addContext(key Key, array bool) {
// Otherwise, it better be a table, since this MUST be a key group (by
// virtue of it not being the last element in a key).
switch t := hashContext[k].(type) {
case []map[string]any:
hashContext = t[len(t)-1]
case []any:
if !p.isLocked(keyContext) {
hashContext = t[len(t)-1].(map[string]any)
} else {
p.panicf("Key '%s' was already created as a hash.", keyContext)
}
case map[string]any:
hashContext = t
default:
@@ -590,13 +595,17 @@ func (p *parser) addContext(key Key, array bool) {
// list of tables for it.
k := key.last()
if _, ok := hashContext[k]; !ok {
hashContext[k] = make([]map[string]any, 0, 4)
hashContext[k] = make([]any, 0, 4)
}

// Add a new table. But make sure the key hasn't already been used
// for something else.
if hash, ok := hashContext[k].([]map[string]any); ok {
hashContext[k] = append(hash, make(map[string]any))
if hash, ok := hashContext[k].([]any); ok {
if !p.isLocked(append(keyContext, k)) {
hashContext[k] = append(hash, make(map[string]any))
} else {
p.panicf("Key '%s' was already created and cannot be used as an array.", key)
}
} else {
p.panicf("Key '%s' was already created and cannot be used as an array.", key)
}
@@ -622,10 +631,10 @@ func (p *parser) setValue(key string, value any) {
p.bug("Context for key '%s' has not been established.", keyContext)
}
switch t := tmpHash.(type) {
case []map[string]any:
case []any:
// The context is a table of hashes. Pick the most recent table
// defined as the current hash.
hash = t[len(t)-1]
hash = t[len(t)-1].(map[string]any)
case map[string]any:
hash = t
default:
@@ -666,7 +675,7 @@ func (p *parser) setValue(key string, value any) {
//
// Note that if `key` is empty, then the type given will be applied to the
// current context (which is either a table or an array of tables).
func (p *parser) setType(key string, typ tomlType, pos Position) {
func (p *parser) setType(key string, typ tomlType, pos Position, locked bool) {
keyContext := make(Key, 0, len(p.context)+1)
keyContext = append(keyContext, p.context...)
if len(key) > 0 { // allow type setting for hashes
@@ -678,7 +687,8 @@ func (p *parser) setType(key string, typ tomlType, pos Position) {
if len(keyContext) == 0 {
keyContext = Key{""}
}
p.keyInfo[keyContext.String()] = keyInfo{tomlType: typ, pos: pos}

p.keyInfo[keyContext.String()] = keyInfo{tomlType: typ, pos: pos, locked: locked}
}

// Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and
@@ -687,6 +697,7 @@ func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struc
func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) }
func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok }
func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray }
func (p *parser) isLocked(key Key) bool { return p.keyInfo[key.String()].locked }
func (p *parser) addImplicitContext(key Key) { p.addImplicit(key); p.addContext(key, false) }

// current returns the full key name of the current context.

0 comments on commit e4bc5d9

Please sign in to comment.