mirror of
https://github.com/lightbend/config.git
synced 2025-03-25 08:40:40 +08:00
Allow unquoted strings consisting entirely of number chars
Previously, a string which was an invalid number (such as "1.0.0" or "1e3e3") but consisted entirely of chars allowed in numbers would be an error and require quoting. Now, we allow such strings to be unquoted.
This commit is contained in:
parent
41f3de8261
commit
7ac81ecb5e
1
HOCON.md
1
HOCON.md
@ -441,6 +441,7 @@ number-to-string library function).
|
||||
be a two-element path with `foo10` and `0` as the elements.
|
||||
- `foo"10.0"` is an unquoted then a quoted string which are
|
||||
concatenated, so this is a single-element path.
|
||||
- `1.2.3` is the three-element path with `1`,`2`,`3`
|
||||
|
||||
Unlike value concatenations, path expressions are _always_
|
||||
converted to a string, even if they are just a single value.
|
||||
|
@ -355,7 +355,15 @@ final class Tokenizer {
|
||||
return Tokens.newLong(lineOrigin, Long.parseLong(s), s);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw problem(s, "Invalid number: '" + s + "'", true /* suggestQuotes */, e);
|
||||
// not a number after all, see if it's an unquoted string.
|
||||
for (char u : s.toCharArray()) {
|
||||
if (notInUnquotedText.indexOf(u) >= 0)
|
||||
throw problem(asString(u), "Reserved character '" + asString(u)
|
||||
+ "' is not allowed outside quotes", true /* suggestQuotes */);
|
||||
}
|
||||
// no evil chars so we just decide this was a string and
|
||||
// not a number.
|
||||
return Tokens.newUnquotedText(lineOrigin, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ class ConfParserTest extends TestUtils {
|
||||
assertEquals(path("a b"), parsePath(" a b"))
|
||||
assertEquals(path("a", "b.c", "d"), parsePath("a.\"b.c\".d"))
|
||||
assertEquals(path("3", "14"), parsePath("3.14"))
|
||||
assertEquals(path("3", "14", "159"), parsePath("3.14.159"))
|
||||
assertEquals(path("a3", "14"), parsePath("a3.14"))
|
||||
assertEquals(path(""), parsePath("\"\""))
|
||||
assertEquals(path("a", "", "b"), parsePath("a.\"\".b"))
|
||||
@ -133,6 +134,9 @@ class ConfParserTest extends TestUtils {
|
||||
assertEquals(path("a-c"), parsePath("a-c"))
|
||||
assertEquals(path("a_c"), parsePath("a_c"))
|
||||
assertEquals(path("-"), parsePath("\"-\""))
|
||||
assertEquals(path("-"), parsePath("-"))
|
||||
assertEquals(path("-foo"), parsePath("-foo"))
|
||||
assertEquals(path("-10"), parsePath("-10"))
|
||||
|
||||
// here 10.0 is part of an unquoted string
|
||||
assertEquals(path("foo10", "0"), parsePath("foo10.0"))
|
||||
@ -140,6 +144,8 @@ class ConfParserTest extends TestUtils {
|
||||
assertEquals(path("10", "0foo"), parsePath("10.0foo"))
|
||||
// just a number
|
||||
assertEquals(path("10", "0"), parsePath("10.0"))
|
||||
// multiple-decimal number
|
||||
assertEquals(path("1", "2", "3", "4"), parsePath("1.2.3.4"))
|
||||
|
||||
for (invalid <- Seq("", " ", " \n \n ", "a.", ".b", "a..b", "a${b}c", "\"\".", ".\"\"")) {
|
||||
try {
|
||||
@ -152,11 +158,6 @@ class ConfParserTest extends TestUtils {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
intercept[ConfigException.Parse] {
|
||||
// this gets parsed as a number since it starts with '-'
|
||||
parsePath("-")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -339,11 +340,6 @@ class ConfParserTest extends TestUtils {
|
||||
lineNumberTest(2, "\n\"foo\"")
|
||||
lineNumberTest(3, "\n\n\"foo\"")
|
||||
|
||||
// newline in middle of number uses the line the number was on
|
||||
lineNumberTest(1, "1e\n")
|
||||
lineNumberTest(2, "\n1e\n")
|
||||
lineNumberTest(3, "\n\n1e\n")
|
||||
|
||||
// newlines in triple-quoted string should not hose up the numbering
|
||||
lineNumberTest(1, "a : \"\"\"foo\"\"\"}")
|
||||
lineNumberTest(2, "a : \"\"\"foo\n\"\"\"}")
|
||||
@ -813,4 +809,14 @@ class ConfParserTest extends TestUtils {
|
||||
val conf = ConfigFactory.parseString("foo= \uFEFFbar\uFEFF")
|
||||
assertEquals("bar", conf.getString("foo"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def acceptMultiPeriodNumericPath() {
|
||||
val conf1 = ConfigFactory.parseString("0.1.2.3=foobar1")
|
||||
assertEquals("foobar1", conf1.getString("0.1.2.3"))
|
||||
val conf2 = ConfigFactory.parseString("0.1.2.3.ABC=foobar2")
|
||||
assertEquals("foobar2", conf2.getString("0.1.2.3.ABC"))
|
||||
val conf3 = ConfigFactory.parseString("ABC.0.1.2.3=foobar3")
|
||||
assertEquals("foobar3", conf3.getString("ABC.0.1.2.3"))
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,9 @@ class PathTest extends TestUtils {
|
||||
RenderTest("\" foo \"", path(" foo ")),
|
||||
// trailing space only
|
||||
RenderTest("\"foo \"", path("foo ")))
|
||||
// numbers with decimal points
|
||||
RenderTest("1.2", path("1", "2"))
|
||||
RenderTest("1.2.3.4", path("1", "2", "3", "4"))
|
||||
|
||||
for (t <- tests) {
|
||||
assertEquals(t.expected, t.path.render())
|
||||
|
@ -342,8 +342,6 @@ abstract trait TestUtils {
|
||||
// these two problems are ignored by the lift tokenizer
|
||||
"[:\"foo\", \"bar\"]", // colon in an array; lift doesn't throw (tokenizer erases it)
|
||||
"[\"foo\" : \"bar\"]", // colon in an array another way, lift ignores (tokenizer erases it)
|
||||
"[ 10e3e3 ]", // two exponents. ideally this might parse to a number plus string "e3" but it's hard to implement.
|
||||
"[ 1-e3 ]", // malformed number but all chars can appear in a number
|
||||
"[ \"hello ]", // unterminated string
|
||||
ParseTest(true, "{ \"foo\" , true }"), // comma instead of colon, lift is fine with this
|
||||
ParseTest(true, "{ \"foo\" : true \"bar\" : false }"), // missing comma between fields, lift fine with this
|
||||
@ -380,6 +378,7 @@ abstract trait TestUtils {
|
||||
"[ += ]",
|
||||
"+= 10",
|
||||
"10 +=",
|
||||
"[ 10e+3e ]", // "+" not allowed in unquoted strings, and not a valid number
|
||||
ParseTest(true, "[ \"foo\nbar\" ]"), // unescaped newline in quoted string, lift doesn't care
|
||||
"[ # comment ]",
|
||||
"${ #comment }",
|
||||
@ -412,7 +411,8 @@ abstract trait TestUtils {
|
||||
"[ \"//comment\" ]", // quoted // comment
|
||||
// this long one is mostly to test rendering
|
||||
"""{ "foo" : { "bar" : "baz", "woo" : "w00t" }, "baz" : { "bar" : "baz", "woo" : [1,2,3,4], "w00t" : true, "a" : false, "b" : 3.14, "c" : null } }""",
|
||||
"{}")
|
||||
"{}",
|
||||
ParseTest(true, "[ 10e+3 ]")) // "+" in a number (lift doesn't handle)
|
||||
|
||||
private val validConfInvalidJson = List[ParseTest]("", // empty document
|
||||
" ", // empty document single space
|
||||
@ -504,7 +504,11 @@ abstract trait TestUtils {
|
||||
"a = [], a += b", // += operator with previous init
|
||||
"{ a = [], a += 10 }", // += in braces object with previous init
|
||||
"a += b", // += operator without previous init
|
||||
"{ a += 10 }") // += in braces object without previous init
|
||||
"{ a += 10 }", // += in braces object without previous init
|
||||
"[ 10e3e3 ]", // two exponents. this should parse to a number plus string "e3"
|
||||
"[ 1-e3 ]", // malformed number should end up as a string instead
|
||||
"[ 1.0.0 ]", // two decimals, should end up as a string
|
||||
"[ 1.0. ]") // trailing decimal should end up as a string
|
||||
|
||||
protected val invalidJson = validConfInvalidJson ++ invalidJsonInvalidConf;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user