From 47055eddef14f9a2af495f55723c334d5e5c1039 Mon Sep 17 00:00:00 2001 From: Waldemar Quevedo Date: Thu, 31 Jan 2019 16:40:06 -0800 Subject: [PATCH] Add support for unquoted config strings that start with number Signed-off-by: Waldemar Quevedo --- conf/lex.go | 28 +++++++++++------ conf/lex_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/conf/lex.go b/conf/lex.go index f3050331..7015290d 100644 --- a/conf/lex.go +++ b/conf/lex.go @@ -547,7 +547,7 @@ func lexValue(lx *lexer) stateFn { return lexBlock case unicode.IsDigit(r): lx.backup() // avoid an extra state and use the same as above - return lexNumberOrDateOrIPStart + return lexNumberOrDateOrStringOrIPStart case r == '.': // special error case, be kind to users return lx.errorf("Floats must start with a digit") case isNL(r): @@ -948,9 +948,11 @@ func lexStringBinary(lx *lexer) stateFn { return lx.stringStateFn } -// lexNumberOrDateStart consumes either a (positive) integer, a float, a datetime, or IP. -// It assumes that NO negative sign has been consumed, that is triggered above. -func lexNumberOrDateOrIPStart(lx *lexer) stateFn { +// lexNumberOrDateOrStringOrIPStart consumes either a (positive) +// integer, a float, a datetime, or IP, or String that started with a +// number. It assumes that NO negative sign has been consumed, that +// is triggered above. +func lexNumberOrDateOrStringOrIPStart(lx *lexer) stateFn { r := lx.next() if !unicode.IsDigit(r) { if r == '.' { @@ -958,11 +960,13 @@ func lexNumberOrDateOrIPStart(lx *lexer) stateFn { } return lx.errorf("Expected a digit but got '%v'.", r) } - return lexNumberOrDateOrIP + return lexNumberOrDateOrStringOrIP } -// lexNumberOrDateOrIP consumes either a (positive) integer, float, datetime or IP. -func lexNumberOrDateOrIP(lx *lexer) stateFn { +// lexNumberOrDateOrStringOrIP consumes either a (positive) integer, +// float, datetime, IP or string without quotes that starts with a +// number. +func lexNumberOrDateOrStringOrIP(lx *lexer) stateFn { r := lx.next() switch { case r == '-': @@ -971,13 +975,17 @@ func lexNumberOrDateOrIP(lx *lexer) stateFn { } return lexDateAfterYear case unicode.IsDigit(r): - return lexNumberOrDateOrIP + return lexNumberOrDateOrStringOrIP case r == '.': - return lexFloatStart // Assume float at first, but could be IP + // Assume float at first, but could be IP + return lexFloatStart case isNumberSuffix(r): return lexConvenientNumber + case !(isNL(r) || r == eof || r == mapEnd || r == optValTerm || r == mapValTerm || isWhitespace(r) || unicode.IsDigit(r)): + // Treat it as a string value once we get a rune that + // is not a number. + return lexString } - lx.backup() lx.emit(itemInteger) return lx.pop() diff --git a/conf/lex_test.go b/conf/lex_test.go index 20dab248..d15383c9 100644 --- a/conf/lex_test.go +++ b/conf/lex_test.go @@ -87,6 +87,84 @@ func TestComplexStringValues(t *testing.T) { expect(t, lx, expectedItems) } +func TestStringStartingWithNumber(t *testing.T) { + expectedItems := []item{ + {itemKey, "foo", 1, 0}, + {itemString, "3xyz", 1, 6}, + {itemEOF, "", 2, 0}, + } + + lx := lex(`foo = 3xyz`) + expect(t, lx, expectedItems) + + lx = lex(`foo = 3xyz,`) + expect(t, lx, expectedItems) + + lx = lex(`foo = 3xyz;`) + expect(t, lx, expectedItems) + + expectedItems = []item{ + {itemKey, "foo", 2, 9}, + {itemString, "3xyz", 2, 15}, + {itemEOF, "", 2, 0}, + } + content := ` + foo = 3xyz + ` + lx = lex(content) + expect(t, lx, expectedItems) + + expectedItems = []item{ + {itemKey, "map", 2, 9}, + {itemMapStart, "", 2, 14}, + {itemKey, "foo", 3, 11}, + {itemString, "3xyz", 3, 17}, + {itemMapEnd, "", 3, 22}, + {itemEOF, "", 2, 0}, + } + content = ` + map { + foo = 3xyz} + ` + lx = lex(content) + expect(t, lx, expectedItems) + + expectedItems = []item{ + {itemKey, "map", 2, 9}, + {itemMapStart, "", 2, 14}, + {itemKey, "foo", 3, 11}, + {itemString, "3xyz", 3, 17}, + {itemMapEnd, "", 4, 10}, + {itemEOF, "", 2, 0}, + } + content = ` + map { + foo = 3xyz; + } + ` + lx = lex(content) + expect(t, lx, expectedItems) + + expectedItems = []item{ + {itemKey, "map", 2, 9}, + {itemMapStart, "", 2, 14}, + {itemKey, "foo", 3, 11}, + {itemString, "3xyz", 3, 17}, + {itemKey, "bar", 4, 11}, + {itemString, "4wqs", 4, 17}, + {itemMapEnd, "", 5, 10}, + {itemEOF, "", 2, 0}, + } + content = ` + map { + foo = 3xyz, + bar = 4wqs + } + ` + lx = lex(content) + expect(t, lx, expectedItems) +} + func TestBinaryString(t *testing.T) { expectedItems := []item{ {itemKey, "foo", 1, 0}, @@ -380,7 +458,7 @@ func TestRawString(t *testing.T) { } lx := lex("foo = bar") expect(t, lx, expectedItems) - lx = lex(`foo = bar' `) //'single-quote for emacs TODO: Remove me + lx = lex(`foo = bar' `) expect(t, lx, expectedItems) }