Basic array table implementation

This commit is contained in:
Thomas Pelletier
2021-03-16 10:24:19 -04:00
parent c6892fcf5a
commit f9f9ccb777
5 changed files with 156 additions and 9 deletions
+1 -1
View File
@@ -147,7 +147,7 @@ func (n *Node) Key() []Node {
panic(fmt.Errorf("KeyValue should have at least two children, not %d", len(n.Children)))
}
return n.Children[:len(n.Children)-1]
case Table:
case Table, ArrayTable:
return n.Children
default:
panic(fmt.Errorf("Key() is not supported on a %s", n.Kind))
+2 -2
View File
@@ -73,6 +73,8 @@ func (p *parser) parseExpression(b []byte) ([]byte, error) {
return nil, err
}
p.tree = append(p.tree, node)
b = p.parseWhitespace(b)
if len(b) > 0 && b[0] == '#' {
@@ -80,8 +82,6 @@ func (p *parser) parseExpression(b []byte) ([]byte, error) {
return rest, err
}
p.tree = append(p.tree, node)
return b, nil
}
+27
View File
@@ -201,6 +201,19 @@ func scopeTarget(t target, name string) (target, error) {
return scope(x, name)
}
func scopeTableTarget(append bool, t target, name string) (target, error) {
x := t.get()
t, err := scope(x, name)
if err != nil {
return t, err
}
x = t.get()
if x.Kind() == reflect.Slice {
return scopeSlice(t, append)
}
return t, nil
}
func scope(v reflect.Value, name string) (target, error) {
switch v.Kind() {
case reflect.Struct:
@@ -218,6 +231,20 @@ func scope(v reflect.Value, name string) (target, error) {
}
}
func scopeSlice(t target, append bool) (target, error) {
v := t.get()
if append {
newElem := reflect.New(v.Type().Elem())
newSlice := reflect.Append(v, newElem.Elem())
err := t.set(newSlice)
if err != nil {
return t, err
}
v = t.get()
}
return valueTarget(v.Index(v.Len() - 1)), nil
}
func scopeMap(v reflect.Value, name string) (target, error) {
if v.IsNil() {
v.Set(reflect.MakeMap(v.Type()))
+30 -5
View File
@@ -25,10 +25,11 @@ func fromAst(tree ast.Root, v interface{}) error {
return fmt.Errorf("target pointer must be non-nil")
}
var x target = valueTarget(r.Elem())
var err error
var root target = valueTarget(r.Elem())
current := root
for _, node := range tree {
x, err = unmarshalTopLevelNode(x, &node)
current, err = unmarshalTopLevelNode(root, current, &node)
if err != nil {
return err
}
@@ -39,12 +40,12 @@ func fromAst(tree ast.Root, v interface{}) error {
// The target return value is the target for the next top-level node. Mostly
// unchanged, except by table and array table.
func unmarshalTopLevelNode(x target, node *ast.Node) (target, error) {
func unmarshalTopLevelNode(root target, x target, node *ast.Node) (target, error) {
switch node.Kind {
case ast.Table:
return scopeWithKey(x, node.Key())
return scopeWithTable(root, node.Key())
case ast.ArrayTable:
panic("TODO")
return scopeWithArrayTable(root, node.Key())
case ast.KeyValue:
return x, unmarshalKeyValue(x, node)
default:
@@ -52,6 +53,30 @@ func unmarshalTopLevelNode(x target, node *ast.Node) (target, error) {
}
}
func scopeWithTable(x target, key []ast.Node) (target, error) {
var err error
for _, n := range key {
x, err = scopeTableTarget(false, x, string(n.Data))
if err != nil {
return nil, err
}
}
return x, nil
}
func scopeWithArrayTable(x target, key []ast.Node) (target, error) {
var err error
if len(key) > 1 {
for _, n := range key[:len(key)-1] {
x, err = scopeTableTarget(false, x, string(n.Data))
if err != nil {
return nil, err
}
}
}
return scopeTableTarget(true, x, string(key[len(key)-1].Data))
}
func scopeWithKey(x target, key []ast.Node) (target, error) {
var err error
for _, n := range key {
+96 -1
View File
@@ -175,6 +175,7 @@ func TestUnmarshal(t *testing.T) {
err bool
}
examples := []struct {
skip bool
desc string
input string
gen func() test
@@ -312,7 +313,7 @@ B = "data"`,
{
desc: "multi keys of different types into map[string]interface{}",
input: `A = "foo"
B = 42`,
B = 42`,
gen: func() test {
doc := map[string]interface{}{}
return test{
@@ -361,10 +362,104 @@ B = 42`,
}
},
},
{
desc: "one-level one-element array table",
input: `[[First]]
Second = "hello"`,
gen: func() test {
type First struct {
Second string
}
type Doc struct {
First []First
}
return test{
target: &Doc{},
expected: &Doc{
First: []First{
{
Second: "hello",
},
},
},
}
},
},
{
desc: "one-level multi-element array table",
input: `[[Products]]
Name = "Hammer"
Sku = 738594937
[[Products]] # empty table within the array
[[Products]]
Name = "Nail"
Sku = 284758393
Color = "gray"`,
gen: func() test {
type Product struct {
Name string
Sku int64
Color string
}
type Doc struct {
Products []Product
}
return test{
target: &Doc{},
expected: &Doc{
Products: []Product{
{Name: "Hammer", Sku: 738594937},
{},
{Name: "Nail", Sku: 284758393, Color: "gray"},
},
},
}
},
},
{
skip: true, // TODO
desc: "one-level multi-element array table to map",
input: `[[Products]]
Name = "Hammer"
Sku = 738594937
[[Products]] # empty table within the array
[[Products]]
Name = "Nail"
Sku = 284758393
Color = "gray"`,
gen: func() test {
return test{
target: &map[string]interface{}{},
expected: &map[string]interface{}{
"Products": []interface{}{
map[string]interface{}{
"Name": "Hammer",
"Sku": 738594937,
},
nil,
map[string]interface{}{
"Name": "Nail",
"Sku": 284758393,
"Color": "gray",
},
},
},
}
},
},
}
for _, e := range examples {
t.Run(e.desc, func(t *testing.T) {
if e.skip {
t.Skip()
}
test := e.gen()
if test.err && test.expected != nil {
panic("invalid test: cannot expect both an error and a value")