From bef0f5796797a12cc4f107faa0ba993ec0ddee3d Mon Sep 17 00:00:00 2001 From: Jonathan Lloyd Date: Mon, 21 Oct 2019 01:36:14 +0100 Subject: [PATCH] Fix key parsing in line tables (#311) A bug was reported that indicated that inline tables did not fully support bare keys: $ echo 'foo = { -bar => "buz"}' | ./tomljson (1, 9): unexpected token type in inline table: Error $ echo 'foo = { "whatever" = "buz"}' | ./tomljson (1, 10): unexpected token type in inline table: String echo 'foo = { _no = "buz"}' | ./tomljson (1, 9): unexpected token type in inline table: Error This change makes a couple of tweaks to to allow for all key variants in inline tables Fixes: #282 --- lexer.go | 4 ++-- lexer_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ parser.go | 13 ++++++++----- parser_test.go | 36 +++++++++++++++++++++++++++++++++- 4 files changed, 97 insertions(+), 8 deletions(-) diff --git a/lexer.go b/lexer.go index 1060980..e075e09 100644 --- a/lexer.go +++ b/lexer.go @@ -247,13 +247,13 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn { func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn { l.next() l.emit(tokenLeftCurlyBrace) - return l.lexRvalue + return l.lexVoid } func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn { l.next() l.emit(tokenRightCurlyBrace) - return l.lexRvalue + return l.lexVoid } func (l *tomlLexer) lexDate() tomlLexStateFn { diff --git a/lexer_test.go b/lexer_test.go index de91d92..e979f8a 100644 --- a/lexer_test.go +++ b/lexer_test.go @@ -735,6 +735,58 @@ func TestLexUnknownRvalue(t *testing.T) { }) } +func TestLexInlineTableBareKey(t *testing.T) { + testFlow(t, `foo = { bar = "baz" }`, []token{ + {Position{1, 1}, tokenKey, "foo"}, + {Position{1, 5}, tokenEqual, "="}, + {Position{1, 7}, tokenLeftCurlyBrace, "{"}, + {Position{1, 9}, tokenKey, "bar"}, + {Position{1, 13}, tokenEqual, "="}, + {Position{1, 16}, tokenString, "baz"}, + {Position{1, 21}, tokenRightCurlyBrace, "}"}, + {Position{1, 22}, tokenEOF, ""}, + }) +} + +func TestLexInlineTableBareKeyDash(t *testing.T) { + testFlow(t, `foo = { -bar = "baz" }`, []token{ + {Position{1, 1}, tokenKey, "foo"}, + {Position{1, 5}, tokenEqual, "="}, + {Position{1, 7}, tokenLeftCurlyBrace, "{"}, + {Position{1, 9}, tokenKey, "-bar"}, + {Position{1, 14}, tokenEqual, "="}, + {Position{1, 17}, tokenString, "baz"}, + {Position{1, 22}, tokenRightCurlyBrace, "}"}, + {Position{1, 23}, tokenEOF, ""}, + }) +} + +func TestLexInlineTableBareKeyUnderscore(t *testing.T) { + testFlow(t, `foo = { _bar = "baz" }`, []token{ + {Position{1, 1}, tokenKey, "foo"}, + {Position{1, 5}, tokenEqual, "="}, + {Position{1, 7}, tokenLeftCurlyBrace, "{"}, + {Position{1, 9}, tokenKey, "_bar"}, + {Position{1, 14}, tokenEqual, "="}, + {Position{1, 17}, tokenString, "baz"}, + {Position{1, 22}, tokenRightCurlyBrace, "}"}, + {Position{1, 23}, tokenEOF, ""}, + }) +} + +func TestLexInlineTableQuotedKey(t *testing.T) { + testFlow(t, `foo = { "bar" = "baz" }`, []token{ + {Position{1, 1}, tokenKey, "foo"}, + {Position{1, 5}, tokenEqual, "="}, + {Position{1, 7}, tokenLeftCurlyBrace, "{"}, + {Position{1, 9}, tokenKey, "\"bar\""}, + {Position{1, 15}, tokenEqual, "="}, + {Position{1, 18}, tokenString, "baz"}, + {Position{1, 23}, tokenRightCurlyBrace, "}"}, + {Position{1, 24}, tokenEOF, ""}, + }) +} + func BenchmarkLexer(b *testing.B) { sample := `title = "Hugo: A Fast and Flexible Website Generator" baseurl = "http://gohugo.io/" diff --git a/parser.go b/parser.go index 09fec25..983e161 100644 --- a/parser.go +++ b/parser.go @@ -360,12 +360,15 @@ Loop: } key := p.getToken() p.assume(tokenEqual) - value := p.parseRvalue() - tree.Set(key.val, value) - case tokenComma: - if previous == nil { - p.raiseError(follow, "inline table cannot start with a comma") + + parsedKey, err := parseKey(key.val) + if err != nil { + p.raiseError(key, "invalid key: %s", err) } + + value := p.parseRvalue() + tree.SetPath(parsedKey, value) + case tokenComma: if tokenIsComma(previous) { p.raiseError(follow, "need field between two commas in inline table") } diff --git a/parser_test.go b/parser_test.go index a03bb7b..97b2f22 100644 --- a/parser_test.go +++ b/parser_test.go @@ -532,6 +532,33 @@ point = { x = 1, y = 2 }`) }) } +func TestInlineGroupBareKeysUnderscore(t *testing.T) { + tree, err := Load(`foo = { _bar = "buz" }`) + assertTree(t, tree, err, map[string]interface{}{ + "foo": map[string]interface{}{ + "_bar": "buz", + }, + }) +} + +func TestInlineGroupBareKeysDash(t *testing.T) { + tree, err := Load(`foo = { -bar = "buz" }`) + assertTree(t, tree, err, map[string]interface{}{ + "foo": map[string]interface{}{ + "-bar": "buz", + }, + }) +} + +func TestInlineGroupKeyQuoted(t *testing.T) { + tree, err := Load(`foo = { "bar" = "buz" }`) + assertTree(t, tree, err, map[string]interface{}{ + "foo": map[string]interface{}{ + "bar": "buz", + }, + }) +} + func TestExampleInlineGroupInArray(t *testing.T) { tree, err := Load(`points = [{ x = 1, y = 2 }]`) assertTree(t, tree, err, map[string]interface{}{ @@ -560,7 +587,7 @@ func TestInlineTableCommaExpected(t *testing.T) { func TestInlineTableCommaStart(t *testing.T) { _, err := Load("foo = {, hello = 53}") - if err.Error() != "(1, 8): inline table cannot start with a comma" { + if err.Error() != "(1, 8): unexpected token type in inline table: keys cannot contain , character" { t.Error("Bad error message:", err.Error()) } } @@ -916,6 +943,13 @@ func TestMapKeyIsNum(t *testing.T) { } } +func TestInvalidKeyInlineTable(t *testing.T) { + _, err := Load("table={invalid..key = 1}") + if err.Error() != "(1, 8): invalid key: expecting key part after dot" { + t.Error("Bad error message:", err.Error()) + } +} + func TestDottedKeys(t *testing.T) { tree, err := Load(` name = "Orange"