From fa7ee6461a69c0231cf7f7cb3db1354235cc2ba0 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Sat, 13 Mar 2021 23:06:16 -0500 Subject: [PATCH] Inline tables --- internal/unmarshaler/parser.go | 58 ++++++++++++------------ internal/unmarshaler/parser_test.go | 34 ++++++++++++++ internal/unmarshaler/unmarshaler.go | 15 +++++- internal/unmarshaler/unmarshaler_test.go | 56 +++++++++++++++++++++++ 4 files changed, 134 insertions(+), 29 deletions(-) diff --git a/internal/unmarshaler/parser.go b/internal/unmarshaler/parser.go index 4bdb2d7..9d127f1 100644 --- a/internal/unmarshaler/parser.go +++ b/internal/unmarshaler/parser.go @@ -222,8 +222,9 @@ func (p *parser) parseVal(b []byte) (ast.Node, []byte, error) { b, err := p.parseValArray(&node, b) return node, b, err case '{': - // TODO - //return p.parseInlineTable(b) + node.Kind = ast.InlineTable + b, err := p.parseInlineTable(&node, b) + return node, b, err default: // TODO //return p.parseIntOrFloatOrDateTime(b) @@ -240,38 +241,39 @@ func (p *parser) parseLiteralString(b []byte) ([]byte, []byte, error) { return v[1 : len(v)-1], rest, nil } -func (p *parser) parseInlineTable(b []byte) ([]byte, error) { +func (p *parser) parseInlineTable(node *ast.Node, b []byte) ([]byte, error) { //inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close //inline-table-open = %x7B ws ; { //inline-table-close = ws %x7D ; } //inline-table-sep = ws %x2C ws ; , Comma //inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] - // TODO - //b = b[1:] - // - //first := true - //var err error - //for len(b) > 0 { - // b = p.parseWhitespace(b) - // if b[0] == '}' { - // break - // } - // - // if !first { - // b, err = expect(',', b) - // if err != nil { - // return nil, err - // } - // b = p.parseWhitespace(b) - // } - // b, err = p.parseKeyval(b) - // if err != nil { - // return nil, err - // } - // - // first = false - //} + b = b[1:] + + first := true + var err error + for len(b) > 0 { + b = p.parseWhitespace(b) + if b[0] == '}' { + break + } + + if !first { + b, err = expect(',', b) + if err != nil { + return nil, err + } + b = p.parseWhitespace(b) + } + var kv ast.Node + kv, b, err = p.parseKeyval(b) + if err != nil { + return nil, err + } + node.Children = append(node.Children, kv) + + first = false + } return expect('}', b) } diff --git a/internal/unmarshaler/parser_test.go b/internal/unmarshaler/parser_test.go index e3e22db..0f2a71c 100644 --- a/internal/unmarshaler/parser_test.go +++ b/internal/unmarshaler/parser_test.go @@ -98,6 +98,40 @@ func TestParser_AST(t *testing.T) { }, }, }, + { + desc: "inline table", + input: `name = { first = "Tom", last = "Preston-Werner" }`, + ast: ast.Root{ + ast.Node{ + Kind: ast.KeyValue, + Children: []ast.Node{ + { + Kind: ast.Key, + Data: []byte(`name`), + }, + { + Kind: ast.InlineTable, + Children: []ast.Node{ + { + Kind: ast.KeyValue, + Children: []ast.Node{ + {Kind: ast.Key, Data: []byte(`first`)}, + {Kind: ast.String, Data: []byte(`Tom`)}, + }, + }, + { + Kind: ast.KeyValue, + Children: []ast.Node{ + {Kind: ast.Key, Data: []byte(`last`)}, + {Kind: ast.String, Data: []byte(`Preston-Werner`)}, + }, + }, + }, + }, + }, + }, + }, + }, } for _, e := range examples { diff --git a/internal/unmarshaler/unmarshaler.go b/internal/unmarshaler/unmarshaler.go index 4e79a4e..2f8c2ab 100644 --- a/internal/unmarshaler/unmarshaler.go +++ b/internal/unmarshaler/unmarshaler.go @@ -80,6 +80,8 @@ func unmarshalValue(x target, node *ast.Node) error { return unmarshalString(x, node) case ast.Array: return unmarshalArray(x, node) + case ast.InlineTable: + return unmarshalInlineTable(x, node) default: panic(fmt.Errorf("unhandled unmarshalValue kind %s", node.Kind)) } @@ -87,10 +89,21 @@ func unmarshalValue(x target, node *ast.Node) error { func unmarshalString(x target, node *ast.Node) error { assertNode(ast.String, node) - return x.setString(string(node.Data)) } +func unmarshalInlineTable(x target, node *ast.Node) error { + assertNode(ast.InlineTable, node) + + for _, kv := range node.Children { + err := unmarshalKeyValue(x, &kv) + if err != nil { + return err + } + } + return nil +} + func unmarshalArray(x target, node *ast.Node) error { assertNode(ast.Array, node) diff --git a/internal/unmarshaler/unmarshaler_test.go b/internal/unmarshaler/unmarshaler_test.go index 33b331d..1759299 100644 --- a/internal/unmarshaler/unmarshaler_test.go +++ b/internal/unmarshaler/unmarshaler_test.go @@ -137,6 +137,62 @@ func TestFromAst_Table(t *testing.T) { }) } +func TestFromAst_InlineTable(t *testing.T) { + t.Run("one level of strings", func(t *testing.T) { + // name = { first = "Tom", last = "Preston-Werner" } + + root := ast.Root{ + ast.Node{ + Kind: ast.KeyValue, + Children: []ast.Node{ + { + Kind: ast.Key, + Data: []byte(`Name`)}, + { + Kind: ast.InlineTable, + Children: []ast.Node{ + { + Kind: ast.KeyValue, + Children: []ast.Node{ + {Kind: ast.Key, Data: []byte(`First`)}, + {Kind: ast.String, Data: []byte(`Tom`)}, + }, + }, + { + Kind: ast.KeyValue, + Children: []ast.Node{ + {Kind: ast.Key, Data: []byte(`Last`)}, + {Kind: ast.String, Data: []byte(`Preston-Werner`)}, + }, + }, + }, + }, + }, + }, + } + + type Name struct { + First string + Last string + } + + type Doc struct { + Name Name + } + + x := Doc{} + err := fromAst(root, &x) + require.NoError(t, err) + assert.Equal(t, Doc{ + Name: Name{ + First: "Tom", + Last: "Preston-Werner", + }, + }, x) + + }) +} + func TestFromAst_Slice(t *testing.T) { t.Run("slice of string", func(t *testing.T) { root := ast.Root{