@o3co/ts.hocon 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  3. package/.github/pull_request_template.md +17 -0
  4. package/.github/workflows/lint.yml +33 -0
  5. package/.github/workflows/release.yml +41 -0
  6. package/.github/workflows/test.yml +53 -0
  7. package/CHANGELOG.md +22 -0
  8. package/CONTRIBUTING.md +62 -0
  9. package/LICENSE +202 -0
  10. package/README.ja.md +195 -0
  11. package/README.md +195 -0
  12. package/SECURITY.md +20 -0
  13. package/docs/superpowers/plans/2026-03-20-ts-hocon.md +2412 -0
  14. package/docs/superpowers/specs/2026-03-20-ts-hocon-design.md +368 -0
  15. package/package.json +44 -0
  16. package/src/config.ts +108 -0
  17. package/src/errors.ts +34 -0
  18. package/src/index.ts +5 -0
  19. package/src/internal/lexer/lexer.ts +168 -0
  20. package/src/internal/lexer/token.ts +20 -0
  21. package/src/internal/parser/ast.ts +17 -0
  22. package/src/internal/parser/parser.ts +229 -0
  23. package/src/internal/resolver/resolver.ts +428 -0
  24. package/src/parse.ts +79 -0
  25. package/src/value.ts +5 -0
  26. package/src/zod.ts +12 -0
  27. package/tests/config.test.ts +100 -0
  28. package/tests/errors.test.ts +43 -0
  29. package/tests/lexer.test.ts +141 -0
  30. package/tests/lightbend/lightbend.test.ts +39 -0
  31. package/tests/lightbend/testdata/a_1.conf +1 -0
  32. package/tests/lightbend/testdata/b_2.conf +1 -0
  33. package/tests/lightbend/testdata/beanconfig/beanconfig01.conf +131 -0
  34. package/tests/lightbend/testdata/bom.conf +2 -0
  35. package/tests/lightbend/testdata/cycle.conf +1 -0
  36. package/tests/lightbend/testdata/env-variables.conf +16 -0
  37. package/tests/lightbend/testdata/equiv01/comments.conf +45 -0
  38. package/tests/lightbend/testdata/equiv01/equals.conf +41 -0
  39. package/tests/lightbend/testdata/equiv01/no-commas.conf +55 -0
  40. package/tests/lightbend/testdata/equiv01/no-root-braces.conf +40 -0
  41. package/tests/lightbend/testdata/equiv01/no-whitespace.json +1 -0
  42. package/tests/lightbend/testdata/equiv01/omit-colons.conf +44 -0
  43. package/tests/lightbend/testdata/equiv01/original.json +41 -0
  44. package/tests/lightbend/testdata/equiv01/path-keys.conf +29 -0
  45. package/tests/lightbend/testdata/equiv01/properties-style.conf +37 -0
  46. package/tests/lightbend/testdata/equiv01/substitutions.conf +41 -0
  47. package/tests/lightbend/testdata/equiv01/unquoted.conf +41 -0
  48. package/tests/lightbend/testdata/equiv02/original.json +17 -0
  49. package/tests/lightbend/testdata/equiv02/path-keys-weird-whitespace.conf +32 -0
  50. package/tests/lightbend/testdata/equiv02/path-keys.conf +9 -0
  51. package/tests/lightbend/testdata/equiv03/includes.conf +7 -0
  52. package/tests/lightbend/testdata/equiv03/letters/a.conf +6 -0
  53. package/tests/lightbend/testdata/equiv03/letters/b.json +3 -0
  54. package/tests/lightbend/testdata/equiv03/letters/c.conf +2 -0
  55. package/tests/lightbend/testdata/equiv03/letters/c.properties +1 -0
  56. package/tests/lightbend/testdata/equiv03/letters/numbers/1.conf +1 -0
  57. package/tests/lightbend/testdata/equiv03/letters/numbers/2.properties +1 -0
  58. package/tests/lightbend/testdata/equiv03/original.json +15 -0
  59. package/tests/lightbend/testdata/equiv03/root/foo.conf +1 -0
  60. package/tests/lightbend/testdata/equiv04/missing-substitutions.conf +4 -0
  61. package/tests/lightbend/testdata/equiv04/original.json +3 -0
  62. package/tests/lightbend/testdata/equiv05/original.json +16 -0
  63. package/tests/lightbend/testdata/equiv05/triple-quotes.conf +20 -0
  64. package/tests/lightbend/testdata/file-include.conf +5 -0
  65. package/tests/lightbend/testdata/include-from-list.conf +5 -0
  66. package/tests/lightbend/testdata/onclasspath.conf +2 -0
  67. package/tests/lightbend/testdata/subdir/bar-file.conf +1 -0
  68. package/tests/lightbend/testdata/subdir/bar.conf +1 -0
  69. package/tests/lightbend/testdata/subdir/baz.conf +1 -0
  70. package/tests/lightbend/testdata/subdir/foo.conf +5 -0
  71. package/tests/lightbend/testdata/test01.conf +96 -0
  72. package/tests/lightbend/testdata/test01.json +4 -0
  73. package/tests/lightbend/testdata/test01.properties +5 -0
  74. package/tests/lightbend/testdata/test02.conf +13 -0
  75. package/tests/lightbend/testdata/test03-included.conf +9 -0
  76. package/tests/lightbend/testdata/test03.conf +36 -0
  77. package/tests/lightbend/testdata/test04.conf +305 -0
  78. package/tests/lightbend/testdata/test05.conf +154 -0
  79. package/tests/lightbend/testdata/test06.conf +14 -0
  80. package/tests/lightbend/testdata/test07.conf +4 -0
  81. package/tests/lightbend/testdata/test08.conf +6 -0
  82. package/tests/lightbend/testdata/test09.conf +17 -0
  83. package/tests/lightbend/testdata/test10.conf +11 -0
  84. package/tests/lightbend/testdata/test11.conf +7 -0
  85. package/tests/lightbend/testdata/test12.conf +43 -0
  86. package/tests/lightbend/testdata/test13-application-override-substitutions.conf +1 -0
  87. package/tests/lightbend/testdata/test13-reference-bad-substitutions.conf +1 -0
  88. package/tests/lightbend/testdata/test13-reference-with-substitutions.conf +2 -0
  89. package/tests/lightbend/testdata/validate-invalid.conf +30 -0
  90. package/tests/lightbend/testdata/validate-reference.conf +32 -0
  91. package/tests/parse.test.ts +44 -0
  92. package/tests/parser.test.ts +154 -0
  93. package/tests/resolver.test.ts +159 -0
  94. package/tests/zod.test.ts +57 -0
  95. package/tsconfig.json +16 -0
  96. package/tsup.config.ts +12 -0
  97. package/vitest.config.ts +7 -0
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { tokenize } from '../src/internal/lexer/lexer.js'
3
+ import { ParseError } from '../src/errors.js'
4
+
5
+ describe('tokenize', () => {
6
+ it('tokenizes empty string', () => {
7
+ const tokens = tokenize('')
8
+ expect(tokens).toHaveLength(1)
9
+ expect(tokens[0].kind).toBe('eof')
10
+ })
11
+
12
+ it('tokenizes braces and brackets', () => {
13
+ const tokens = tokenize('{}[]')
14
+ expect(tokens.map(t => t.kind)).toEqual(['lbrace', 'rbrace', 'lbracket', 'rbracket', 'eof'])
15
+ })
16
+
17
+ it('tokenizes = and +=', () => {
18
+ const tokens = tokenize('=+=')
19
+ expect(tokens[0].kind).toBe('equals')
20
+ expect(tokens[1].kind).toBe('plus_equals')
21
+ })
22
+
23
+ it('tokenizes : and ,', () => {
24
+ const tokens = tokenize(':,')
25
+ expect(tokens.map(t => t.kind)).toEqual(['colon', 'comma', 'eof'])
26
+ })
27
+
28
+ it('skips // comments, keeps newline', () => {
29
+ const tokens = tokenize('// comment\nfoo')
30
+ expect(tokens[0].kind).toBe('newline')
31
+ expect(tokens[1].kind).toBe('unquoted')
32
+ expect(tokens[1].value).toBe('foo')
33
+ })
34
+
35
+ it('skips # comments', () => {
36
+ const tokens = tokenize('# comment\nfoo')
37
+ expect(tokens[0].kind).toBe('newline')
38
+ expect(tokens[1].value).toBe('foo')
39
+ })
40
+
41
+ it('tokenizes quoted strings', () => {
42
+ const [t] = tokenize('"hello world"')
43
+ expect(t.kind).toBe('string')
44
+ expect(t.value).toBe('hello world')
45
+ expect(t.isQuoted).toBe(true)
46
+ })
47
+
48
+ it('handles escape sequences', () => {
49
+ const [t] = tokenize('"a\\nb\\tc"')
50
+ expect(t.value).toBe('a\nb\tc')
51
+ })
52
+
53
+ it('handles \\u unicode escapes', () => {
54
+ const [t] = tokenize('"\\u0041"')
55
+ expect(t.value).toBe('A')
56
+ })
57
+
58
+ it('tokenizes triple-quoted strings', () => {
59
+ const [t] = tokenize('"""hello\nworld"""')
60
+ expect(t.kind).toBe('triple_string')
61
+ expect(t.value).toBe('hello\nworld')
62
+ expect(t.isQuoted).toBe(true)
63
+ })
64
+
65
+ it('strips leading newline from triple-quoted strings', () => {
66
+ const [t] = tokenize('"""\nhello"""')
67
+ expect(t.value).toBe('hello')
68
+ })
69
+
70
+ it('tokenizes unquoted strings', () => {
71
+ const [t] = tokenize('localhost')
72
+ expect(t.kind).toBe('unquoted')
73
+ expect(t.value).toBe('localhost')
74
+ expect(t.isQuoted).toBe(false)
75
+ })
76
+
77
+ it('tokenizes numbers as unquoted', () => {
78
+ const [t] = tokenize('8080')
79
+ expect(t.kind).toBe('unquoted')
80
+ expect(t.value).toBe('8080')
81
+ })
82
+
83
+ it('tokenizes substitutions', () => {
84
+ const [t] = tokenize('${server.host}')
85
+ expect(t.kind).toBe('subst')
86
+ expect(t.value).toBe('server.host')
87
+ })
88
+
89
+ it('tokenizes optional substitutions', () => {
90
+ const [t] = tokenize('${?foo}')
91
+ expect(t.kind).toBe('opt_subst')
92
+ expect(t.value).toBe('foo')
93
+ })
94
+
95
+ it('tokenizes newlines', () => {
96
+ const tokens = tokenize('a\nb')
97
+ expect(tokens[1].kind).toBe('newline')
98
+ })
99
+
100
+ it('deduplicates consecutive newlines', () => {
101
+ const tokens = tokenize('a\n\n\nb')
102
+ const newlines = tokens.filter(t => t.kind === 'newline')
103
+ expect(newlines).toHaveLength(1)
104
+ })
105
+
106
+ it('tracks line and col', () => {
107
+ const tokens = tokenize('a\nb')
108
+ expect(tokens[0].line).toBe(1)
109
+ expect(tokens[0].col).toBe(1)
110
+ expect(tokens[2].line).toBe(2)
111
+ expect(tokens[2].col).toBe(1)
112
+ })
113
+
114
+ it('sets precedingSpace on token after whitespace', () => {
115
+ const tokens = tokenize('a b')
116
+ expect(tokens[1].precedingSpace).toBe(true)
117
+ expect(tokens[0].precedingSpace).toBe(false)
118
+ })
119
+
120
+ it('strips UTF-8 BOM', () => {
121
+ const tokens = tokenize('\uFEFFfoo')
122
+ expect(tokens[0].value).toBe('foo')
123
+ })
124
+
125
+ it('stops unquoted scan at $ for concat', () => {
126
+ const tokens = tokenize('foo${bar}')
127
+ expect(tokens[0].kind).toBe('unquoted')
128
+ expect(tokens[0].value).toBe('foo')
129
+ expect(tokens[1].kind).toBe('subst')
130
+ expect(tokens[1].value).toBe('bar')
131
+ expect(tokens[1].precedingSpace).toBe(false)
132
+ })
133
+
134
+ it('throws ParseError on unterminated string', () => {
135
+ expect(() => tokenize('"unterminated')).toThrow(ParseError)
136
+ })
137
+
138
+ it('throws ParseError on unterminated substitution', () => {
139
+ expect(() => tokenize('${foo')).toThrow(ParseError)
140
+ })
141
+ })
@@ -0,0 +1,39 @@
1
+ // tests/lightbend/lightbend.test.ts
2
+ //
3
+ // Lightbend HOCON spec compliance tests, mirroring go.hocon's lightbend_test.go.
4
+ // Tests equiv01-05 directories (each .conf compared against original.json).
5
+ import { describe, it, expect } from 'vitest'
6
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
7
+ import { join } from 'node:path'
8
+ import { parse } from '../../src/parse.js'
9
+
10
+ const dataDir = new URL('./testdata', import.meta.url).pathname
11
+
12
+ // Known skip reasons for specific test files
13
+ const equivSkip = new Set([
14
+ 'equiv03/includes.conf', // requires .properties file support
15
+ ])
16
+
17
+ describe('Lightbend HOCON equiv tests', () => {
18
+ for (let i = 1; i <= 5; i++) {
19
+ const dir = join(dataDir, `equiv${String(i).padStart(2, '0')}`)
20
+ const jsonPath = join(dir, 'original.json')
21
+ if (!existsSync(jsonPath)) continue
22
+
23
+ const expected = JSON.parse(readFileSync(jsonPath, 'utf-8'))
24
+ const entries = readdirSync(dir)
25
+
26
+ for (const entry of entries) {
27
+ if (!entry.endsWith('.conf')) continue
28
+ const rel = `equiv${String(i).padStart(2, '0')}/${entry}`
29
+ if (equivSkip.has(rel)) continue
30
+
31
+ it(rel, () => {
32
+ const confPath = join(dir, entry)
33
+ const confContent = readFileSync(confPath, 'utf-8')
34
+ const config = parse(confContent, { baseDir: dir })
35
+ expect(config.toObject()).toEqual(expected)
36
+ })
37
+ }
38
+ }
39
+ })
@@ -0,0 +1 @@
1
+ a=1
@@ -0,0 +1 @@
1
+ b=2
@@ -0,0 +1,131 @@
1
+ {
2
+ "booleans" : {
3
+ "trueVal" : true,
4
+ "trueValAgain" : ${booleans.trueVal},
5
+ "falseVal" : false,
6
+ "falseValAgain" : ${booleans.falseVal},
7
+ "on" : "on",
8
+ "off" : "off"
9
+ }
10
+
11
+ "numbers" : {
12
+ "byteVal" : "1",
13
+ "byteObj" : ${numbers.byteVal},
14
+ "shortVal" : "2",
15
+ "shortObj" : ${numbers.shortVal},
16
+ "intVal" : "3",
17
+ "intObj" : ${numbers.intVal},
18
+ "longVal" : "4",
19
+ "longObj" : ${numbers.longVal},
20
+ "doubleVal" : "1.0",
21
+ "doubleObj" : ${numbers.doubleVal}
22
+ }
23
+ //
24
+ "strings" : {
25
+ "abcd" : "abcd",
26
+ "abcd-again" : ${strings.a}${strings.b}${strings.c}${strings.d},
27
+ "a" : "a",
28
+ "b" : "b",
29
+ "c" : "c",
30
+ "d" : "d",
31
+ "concatenated" : null bar 42 baz true 3.14 hi,
32
+ "double" : "3.14",
33
+ "number" : "57",
34
+ "null" : "null",
35
+ "true" : "true",
36
+ "yes" : "yes",
37
+ "false" : "false",
38
+ "no" : "no"
39
+ },
40
+
41
+ "arrays" : {
42
+ "empty" : [],
43
+ "ofInt" : [1, 2, 3],
44
+ "ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ],
45
+ "of-double" : [3.14, 4.14, 5.14],
46
+ "of-long" : { "1" : 32, "2" : 42, "3" : 52 }, // object-to-list conversion
47
+ "ofNull" : [null, null, null],
48
+ "ofBoolean" : [true, false],
49
+ "ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}],
50
+ "ofObject" : [${numbers}, ${booleans}, ${strings}],
51
+ "ofConfig" : [${numbers}, ${booleans}, ${strings}],
52
+ "ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
53
+ "ofConfigValue" : [1, 2, "a"],
54
+ "ofDuration" : [1, 2h, 3 days],
55
+ "ofMemorySize" : [1024, 1M, 1G],
56
+ "ofStringBean" : [
57
+ {
58
+ abcd : "testAbcdOne"
59
+ yes : "testYesOne"
60
+ },
61
+ {
62
+ abcd : "testAbcdTwo"
63
+ yes : "testYesTwo"
64
+ }
65
+ ]
66
+ },
67
+ "bytes" : {
68
+ "kilobyte" : "1kB",
69
+ "kibibyte" : "1K",
70
+ "thousandBytes" : "1000B"
71
+ },
72
+ "durations" : {
73
+ "second" : 1s,
74
+ "secondAsNumber" : 1000,
75
+ "halfSecond" : 0.5s
76
+ },
77
+ "validation" : {
78
+ "shouldBeInt" : true,
79
+ "should-be-boolean" : 42,
80
+ "should-be-list" : "hello"
81
+ },
82
+ "preferCamelNames" : {
83
+ "foo-bar" : "no",
84
+ "fooBar" : "yes",
85
+ "baz-bar" : "no",
86
+ "bazBar" : "yes"
87
+ },
88
+ "values" : {
89
+ "obj" : 42,
90
+ "config" : ${strings},
91
+ "configObj" : ${numbers},
92
+ "configValue" : "hello world",
93
+ "list" : [1,2,3],
94
+ "unwrappedMap" : ${validation}
95
+ },
96
+ "enums" : {
97
+ "problem" : "P1",
98
+ "solutions" : ["S1", "S3"]
99
+ },
100
+ "objects" : {
101
+ "valueObject": {
102
+ "mandatoryValue": "notNull"
103
+ }
104
+ },
105
+ "sets" : {
106
+ "empty" : [],
107
+ "ofInt" : [1, 2, 3, 2, 3],
108
+ "ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ],
109
+ "of-double" : [3.14, 4.14, 4.14, 5.14],
110
+ "of-long" : { "1" : 32, "2" : 42, "3" : 52 }, // object-to-list conversion
111
+ "ofNull" : [null, null, null],
112
+ "ofBoolean" : [true, false, false],
113
+ "ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}],
114
+ "ofObject" : [${numbers}, ${booleans}, ${strings}],
115
+ "ofConfig" : [${numbers}, ${booleans}, ${strings}],
116
+ "ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
117
+ "ofConfigValue" : [1, 2, "a"],
118
+ "ofDuration" : [1, 2h, 3 days],
119
+ "ofMemorySize" : [1024, 1M, 1G],
120
+ "ofStringBean" : [
121
+ {
122
+ abcd : "testAbcdOne"
123
+ yes : "testYesOne"
124
+ },
125
+ {
126
+ abcd : "testAbcdTwo"
127
+ yes : "testYesTwo"
128
+ }
129
+ ]
130
+ }
131
+ }
@@ -0,0 +1,2 @@
1
+ #
2
+ foo = bar
@@ -0,0 +1 @@
1
+ include "cycle.conf"
@@ -0,0 +1,16 @@
1
+ secret = a
2
+ secret = ${?SECRET_A}
3
+ secrets = ["b", "c"]
4
+ secrets = [${?SECRET_B}, ${?SECRET_C}]
5
+
6
+ myList = ${MY_LIST[]}
7
+ myOptionalList = ${?MY_LIST[]}
8
+ myOptionalUndefinedList = ${?UNDEFINED_LIST[]}
9
+ numList = ${NUM_LIST[]}
10
+
11
+ # list concatenation with env var list expansion
12
+ prependedList = ["x", "y"] ${?MY_LIST[]}
13
+ appendedList = ${?MY_LIST[]} ["x", "y"]
14
+ selfAppendedList = ["x"]
15
+ selfAppendedList = ${?selfAppendedList} ${?MY_LIST[]}
16
+ appendedUndefined = ["x", "y"] ${?UNDEFINED_LIST[]}
@@ -0,0 +1,45 @@
1
+ { // These are some integers
2
+ "ints" : { # we can comment them with # too
3
+ "fortyTwo" : 42, ## double-# !
4
+ "fortyTwoAgain" : 42 # { }
5
+ },
6
+ ######## COMMENT COMMENT COMMENT
7
+ /// I COMMENT YOU
8
+ "floats" : {
9
+ "fortyTwoPointOne" : 42.1,
10
+ "fortyTwoPointOneAgain" : 42.1
11
+ // I CAN HAS COMMENT
12
+ },
13
+
14
+ "strings" : {
15
+ "abcd" : // I got yr comment right here
16
+ "abcd",
17
+ "abcdAgain" : "abcd",
18
+ "a" : "a",
19
+ "b" : "b",
20
+ "c" : "c",
21
+ "d" : "d",
22
+ "concatenated" # more to say?
23
+ : "null bar 42 baz true 3.14 hi"
24
+ },
25
+
26
+ "arrays" : {
27
+ "empty" : [],
28
+ "1" : [ 1 ],
29
+ "12" : [1, 2],
30
+ "123" : [1, 2, 3],
31
+ "ofString" : [ "a", "b", "c" ]
32
+ },
33
+
34
+ "booleans" : {
35
+ "true" : true,
36
+ "trueAgain" : true,
37
+ "false" : false,
38
+ "falseAgain" : false
39
+ },
40
+
41
+ "nulls" : {
42
+ "null" : null,
43
+ "nullAgain" : null
44
+ }
45
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "ints" = {
3
+ "fortyTwo" = 42,
4
+ "fortyTwoAgain" = 42
5
+ },
6
+
7
+ "floats" = {
8
+ "fortyTwoPointOne" = 42.1,
9
+ "fortyTwoPointOneAgain" = 42.1
10
+ },
11
+
12
+ "strings" = {
13
+ "abcd" = "abcd",
14
+ "abcdAgain" = "abcd",
15
+ "a" = "a",
16
+ "b" = "b",
17
+ "c" = "c",
18
+ "d" = "d",
19
+ "concatenated" = "null bar 42 baz true 3.14 hi"
20
+ },
21
+
22
+ "arrays" = {
23
+ "empty" = [],
24
+ "1" = [ 1 ],
25
+ "12" = [1, 2],
26
+ "123" = [1, 2, 3],
27
+ "ofString" = [ "a", "b", "c" ]
28
+ },
29
+
30
+ "booleans" = {
31
+ "true" = true,
32
+ "trueAgain" = true,
33
+ "false" = false,
34
+ "falseAgain" = false
35
+ },
36
+
37
+ "nulls" = {
38
+ "null" = null,
39
+ "nullAgain" = null
40
+ }
41
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "ints" : {
3
+ "fortyTwo" : 42
4
+ "fortyTwoAgain" : 42
5
+ }
6
+
7
+ "floats" : {
8
+ "fortyTwoPointOne" : 42.1
9
+ "fortyTwoPointOneAgain" : 42.1
10
+ }
11
+
12
+ "strings" : {
13
+ "abcd" : "abcd"
14
+ "abcdAgain" : "abcd"
15
+ "a" : "a"
16
+ "b" : "b"
17
+ "c" : "c"
18
+ "d" : "d"
19
+ "concatenated" : "null bar 42 baz true 3.14 hi"
20
+ }
21
+
22
+ "arrays" : {
23
+ "empty" : []
24
+ "1" : [ 1 ]
25
+ "12" : [1
26
+ 2]
27
+ "123" : [1
28
+ 2
29
+ 3 ]
30
+ "ofString" : [
31
+
32
+ "a"
33
+ "b"
34
+ "c"
35
+
36
+ ]
37
+ }
38
+
39
+ "booleans" : {
40
+
41
+ "true" : true
42
+
43
+ "trueAgain" : true
44
+
45
+ "false" : false
46
+
47
+ "falseAgain" : false
48
+
49
+ }
50
+
51
+ "nulls" : {
52
+ "null" : null
53
+ "nullAgain" : null
54
+ }
55
+ }
@@ -0,0 +1,40 @@
1
+ "ints" : {
2
+ "fortyTwo" : 42,
3
+ "fortyTwoAgain" : 42
4
+ },
5
+
6
+ "floats" : {
7
+ "fortyTwoPointOne" : 42.1,
8
+ "fortyTwoPointOneAgain" : 42.1
9
+ },
10
+
11
+ "strings" : {
12
+ "abcd" : "abcd",
13
+ "abcdAgain" : "abcd",
14
+ "a" : "a",
15
+ "b" : "b",
16
+ "c" : "c",
17
+ "d" : "d",
18
+ "concatenated" : "null bar 42 baz true 3.14 hi"
19
+ },
20
+
21
+ "arrays" : {
22
+ "empty" : [],
23
+ "1" : [ 1 ],
24
+ "12" : [1, 2],
25
+ "123" : [1, 2, 3],
26
+ "ofString" : [ "a", "b", "c" ]
27
+ },
28
+
29
+ "booleans" : {
30
+ "true" : true,
31
+ "trueAgain" : true,
32
+ "false" : false,
33
+ "falseAgain" : false
34
+ },
35
+
36
+ "nulls" : {
37
+ "null" : null,
38
+ "nullAgain" : null
39
+ }
40
+
@@ -0,0 +1 @@
1
+ {"ints":{"fortyTwo":42,"fortyTwoAgain":42},"floats":{"fortyTwoPointOne":42.1,"fortyTwoPointOneAgain":42.1},"strings":{"abcd":"abcd","abcdAgain":"abcd","a":"a","b":"b","c":"c","d":"d","concatenated":"null bar 42 baz true 3.14 hi"},"arrays":{"empty":[],"1":[1],"12":[1,2],"123":[1,2,3],"ofString":["a","b","c"]},"booleans":{"true":true,"trueAgain":true,"false":false,"falseAgain":false},"nulls":{"null":null,"nullAgain":null}}
@@ -0,0 +1,44 @@
1
+ {
2
+ "ints" {
3
+ "fortyTwo" : 42,
4
+ "fortyTwoAgain" : 42
5
+ },
6
+
7
+ "floats"
8
+ {
9
+ "fortyTwoPointOne" : 42.1,
10
+ "fortyTwoPointOneAgain" : 42.1
11
+ },
12
+
13
+ "strings"
14
+
15
+ {
16
+ "abcd" : "abcd",
17
+ "abcdAgain" : "abcd",
18
+ "a" : "a",
19
+ "b" : "b",
20
+ "c" : "c",
21
+ "d" : "d",
22
+ "concatenated" : "null bar 42 baz true 3.14 hi"
23
+ },
24
+
25
+ "arrays" {
26
+ "empty" : [],
27
+ "1" : [ 1 ],
28
+ "12" : [1, 2],
29
+ "123" : [1, 2, 3],
30
+ "ofString" : [ "a", "b", "c" ]
31
+ },
32
+
33
+ "booleans" {
34
+ "true" : true,
35
+ "trueAgain" : true,
36
+ "false" : false,
37
+ "falseAgain" : false
38
+ },
39
+
40
+ "nulls" {
41
+ "null" : null,
42
+ "nullAgain" : null
43
+ }
44
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "ints" : {
3
+ "fortyTwo" : 42,
4
+ "fortyTwoAgain" : 42
5
+ },
6
+
7
+ "floats" : {
8
+ "fortyTwoPointOne" : 42.1,
9
+ "fortyTwoPointOneAgain" : 42.1
10
+ },
11
+
12
+ "strings" : {
13
+ "abcd" : "abcd",
14
+ "abcdAgain" : "abcd",
15
+ "a" : "a",
16
+ "b" : "b",
17
+ "c" : "c",
18
+ "d" : "d",
19
+ "concatenated" : "null bar 42 baz true 3.14 hi"
20
+ },
21
+
22
+ "arrays" : {
23
+ "empty" : [],
24
+ "1" : [ 1 ],
25
+ "12" : [1, 2],
26
+ "123" : [1, 2, 3],
27
+ "ofString" : [ "a", "b", "c" ]
28
+ },
29
+
30
+ "booleans" : {
31
+ "true" : true,
32
+ "trueAgain" : true,
33
+ "false" : false,
34
+ "falseAgain" : false
35
+ },
36
+
37
+ "nulls" : {
38
+ "null" : null,
39
+ "nullAgain" : null
40
+ }
41
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ ints.fortyTwo : 42,
3
+ ints.fortyTwoAgain : 42,
4
+
5
+ floats.fortyTwoPointOne : 42.1,
6
+ floats.fortyTwoPointOneAgain : 42.1,
7
+
8
+ strings.abcd : "abcd",
9
+ strings.abcdAgain : "abcd",
10
+ strings.a : "a",
11
+ strings.b : "b",
12
+ strings.c : "c",
13
+ strings.d : "d",
14
+ strings.concatenated : "null bar 42 baz true 3.14 hi",
15
+
16
+ arrays."empty" : [],
17
+ arrays."1" : [ 1 ],
18
+ arrays.12 : [1, 2],
19
+ arrays.123 : [1, 2, 3],
20
+ arrays.ofString : [ "a", "b", "c" ],
21
+
22
+ booleans.true : true,
23
+ booleans.trueAgain : true,
24
+ booleans.false : false,
25
+ booleans.falseAgain : false,
26
+
27
+ nulls.null : null,
28
+ nulls.nullAgain : null
29
+ }
@@ -0,0 +1,37 @@
1
+ # this file is in a no-braces java properties style,
2
+ # but it's a .conf file with json-style escaping,
3
+ # not a Java properties file.
4
+ # lots of playing with whitespace and commas
5
+ # in here to test various cases; don't copy
6
+ # this as exemplary, it has gratuitously
7
+ # stupid formatting.
8
+ ints.fortyTwo=42
9
+ ints.fortyTwoAgain = 42 // whitespace ignored around the =
10
+ floats.fortyTwoPointOne=42.1
11
+ floats.fortyTwoPointOneAgain:42.1 // colon is fine too
12
+ strings.abcd=abcd
13
+ strings.abcdAgain : "abcd", // colon and quotes and comma!
14
+ strings.a=a
15
+ strings.b=b
16
+ strings.c="c",
17
+ strings.d:"d"
18
+ strings.concatenated=null bar 42 baz true 3.14 hi # this gets squished to a string
19
+ arrays.empty=[]
20
+ arrays.1 : [ 1 ]
21
+ # it's allowed to omit commas in arrays if you have newlines
22
+ arrays.12 = [
23
+ 1
24
+ 2
25
+ ]
26
+ arrays.123 = [1,
27
+ 2
28
+ 3]
29
+ arrays.ofString = [ "a", "b", "c" ]
30
+ booleans.true=true
31
+ booleans.trueAgain=${booleans.true}
32
+ booleans.false=false
33
+ # you could quote keys, though here it's pointless
34
+ # since the key contains no special characters
35
+ "booleans"."falseAgain" : ${booleans.false},
36
+ nulls.null=${nulls.nullAgain}
37
+ nulls.nullAgain=null