@o3co/ts.hocon 1.12.0 → 1.14.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.
- package/README.ja.md +256 -32
- package/README.md +166 -32
- package/dist/adapters/env.cjs +26 -1678
- package/dist/adapters/env.d.cts +1 -1
- package/dist/adapters/env.d.ts +1 -1
- package/dist/adapters/env.js +11 -1625
- package/dist/adapters/json5.cjs +496 -0
- package/dist/adapters/json5.d.cts +47 -0
- package/dist/adapters/json5.d.ts +47 -0
- package/dist/adapters/json5.js +496 -0
- package/dist/adapters/jsonc.cjs +17 -1632
- package/dist/adapters/jsonc.d.cts +1 -1
- package/dist/adapters/jsonc.d.ts +1 -1
- package/dist/adapters/jsonc.js +9 -1586
- package/dist/adapters/properties.cjs +7 -1773
- package/dist/adapters/properties.d.cts +1 -1
- package/dist/adapters/properties.d.ts +1 -1
- package/dist/adapters/properties.js +6 -1735
- package/dist/adapters/toml.cjs +12 -1626
- package/dist/adapters/toml.d.cts +1 -1
- package/dist/adapters/toml.d.ts +1 -1
- package/dist/adapters/toml.js +10 -1589
- package/dist/adapters/yaml.cjs +20 -1635
- package/dist/adapters/yaml.d.cts +1 -1
- package/dist/adapters/yaml.d.ts +1 -1
- package/dist/adapters/yaml.js +10 -1589
- package/dist/chunk-GBNQADHT.cjs +83 -0
- package/dist/chunk-IMJ7QHIK.cjs +158 -0
- package/dist/chunk-QY7ECGS6.js +83 -0
- package/dist/chunk-RO4D6BH7.cjs +3792 -0
- package/dist/chunk-WTXULUZS.js +3792 -0
- package/dist/chunk-XWKO4JM3.js +158 -0
- package/dist/{config-Dlq6rF3w.d.cts → config-OccaKqXu.d.cts} +148 -4
- package/dist/{config-Dlq6rF3w.d.ts → config-OccaKqXu.d.ts} +148 -4
- package/dist/index.cjs +35 -3449
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +25 -3383
- package/dist/zod.cjs +107 -3291
- package/dist/zod.d.cts +83 -3
- package/dist/zod.d.ts +83 -3
- package/dist/zod.js +95 -3245
- package/package.json +12 -2
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fromMap
|
|
3
|
+
} from "../chunk-QY7ECGS6.js";
|
|
4
|
+
import {
|
|
5
|
+
ConfigError,
|
|
6
|
+
depthError,
|
|
7
|
+
guardStackDepth,
|
|
8
|
+
stripBom
|
|
9
|
+
} from "../chunk-WTXULUZS.js";
|
|
10
|
+
|
|
11
|
+
// src/adapters/json5.ts
|
|
12
|
+
function parseJson5(input, originDescription) {
|
|
13
|
+
const p = new Parser(stripBom(input), originDescription);
|
|
14
|
+
const doc = guardStackDepth(
|
|
15
|
+
() => p.parseDocument(),
|
|
16
|
+
(msg) => depthError(`json5: ${describe(originDescription)}: ${msg}`)
|
|
17
|
+
);
|
|
18
|
+
if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
|
|
19
|
+
throw new ConfigError(
|
|
20
|
+
`json5: ${describe(originDescription)}: document root is ${rootKind(doc)}, but a config root must be an object (spec F0.3)`,
|
|
21
|
+
""
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return fromMap(doc, originDescription);
|
|
25
|
+
}
|
|
26
|
+
function describe(origin) {
|
|
27
|
+
return origin === void 0 || origin === "" ? "document" : origin;
|
|
28
|
+
}
|
|
29
|
+
function rootKind(doc) {
|
|
30
|
+
if (doc === null) return "null";
|
|
31
|
+
if (Array.isArray(doc)) return "an array";
|
|
32
|
+
return typeof doc;
|
|
33
|
+
}
|
|
34
|
+
var INT64_MAX = 9223372036854775807n;
|
|
35
|
+
var INT64_MIN = -9223372036854775808n;
|
|
36
|
+
function isLineTerminator(cp) {
|
|
37
|
+
return cp === 10 || cp === 13 || cp === 8232 || cp === 8233;
|
|
38
|
+
}
|
|
39
|
+
var ZS_RE = /^\p{Zs}$/u;
|
|
40
|
+
function isJson5Space(cp) {
|
|
41
|
+
switch (cp) {
|
|
42
|
+
case 9:
|
|
43
|
+
case 11:
|
|
44
|
+
case 12:
|
|
45
|
+
case 32:
|
|
46
|
+
case 160:
|
|
47
|
+
case 65279:
|
|
48
|
+
return true;
|
|
49
|
+
default:
|
|
50
|
+
return ZS_RE.test(String.fromCodePoint(cp));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
var IDENT_START_RE = /^[\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}]$/u;
|
|
54
|
+
var IDENT_PART_EXTRA_RE = /^[\p{Mn}\p{Mc}\p{Nd}\p{Pc}]$/u;
|
|
55
|
+
function isIdentStart(cp) {
|
|
56
|
+
return cp === 36 || cp === 95 || IDENT_START_RE.test(String.fromCodePoint(cp));
|
|
57
|
+
}
|
|
58
|
+
function isIdentPart(cp) {
|
|
59
|
+
return isIdentStart(cp) || cp === 8204 || cp === 8205 || IDENT_PART_EXTRA_RE.test(String.fromCodePoint(cp));
|
|
60
|
+
}
|
|
61
|
+
function continuesIdentifier(s, i) {
|
|
62
|
+
if (i >= s.length) return false;
|
|
63
|
+
return isIdentPart(s.codePointAt(i));
|
|
64
|
+
}
|
|
65
|
+
function isHexDigit(c) {
|
|
66
|
+
return c >= "0" && c <= "9" || c >= "a" && c <= "f" || c >= "A" && c <= "F";
|
|
67
|
+
}
|
|
68
|
+
var hex4 = (cp) => cp.toString(16).toUpperCase().padStart(4, "0");
|
|
69
|
+
function isPlainObject(v) {
|
|
70
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
71
|
+
}
|
|
72
|
+
function mergeObjects(dst, src) {
|
|
73
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
74
|
+
for (const [k, v] of Object.entries(dst)) out[k] = v;
|
|
75
|
+
for (const [k, v] of Object.entries(src)) {
|
|
76
|
+
const prev = k in out ? out[k] : void 0;
|
|
77
|
+
if (prev !== void 0 && isPlainObject(prev) && isPlainObject(v)) {
|
|
78
|
+
out[k] = mergeObjects(prev, v);
|
|
79
|
+
} else {
|
|
80
|
+
out[k] = v;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
var Parser = class {
|
|
86
|
+
// code unit offset where the current line starts
|
|
87
|
+
constructor(src, origin) {
|
|
88
|
+
this.src = src;
|
|
89
|
+
this.origin = origin;
|
|
90
|
+
this.pos = 0;
|
|
91
|
+
// UTF-16 code unit offset
|
|
92
|
+
this.line = 0;
|
|
93
|
+
// 0-based; reported 1-based
|
|
94
|
+
this.lineAt = 0;
|
|
95
|
+
}
|
|
96
|
+
errf(msg) {
|
|
97
|
+
const col = [...this.src.slice(this.lineAt, this.pos)].length + 1;
|
|
98
|
+
return new ConfigError(
|
|
99
|
+
`json5: ${describe(this.origin)}: line ${this.line + 1} col ${col}: ${msg}`,
|
|
100
|
+
""
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
/** The code point at `pos` (caller guarantees `pos < src.length`). */
|
|
104
|
+
rune() {
|
|
105
|
+
const cp = this.src.codePointAt(this.pos);
|
|
106
|
+
return { cp, size: cp > 65535 ? 2 : 1 };
|
|
107
|
+
}
|
|
108
|
+
advance(cp, size) {
|
|
109
|
+
this.pos += size;
|
|
110
|
+
if (isLineTerminator(cp)) {
|
|
111
|
+
if (cp === 13 && this.src[this.pos] === "\n") this.pos++;
|
|
112
|
+
this.line++;
|
|
113
|
+
this.lineAt = this.pos;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Parse exactly one JSON5 value, allowing only whitespace and comments after
|
|
118
|
+
* it (the F3.2 strictness rule).
|
|
119
|
+
*/
|
|
120
|
+
parseDocument() {
|
|
121
|
+
this.skipSpace();
|
|
122
|
+
const v = this.parseValue();
|
|
123
|
+
this.skipSpace();
|
|
124
|
+
if (this.pos < this.src.length) {
|
|
125
|
+
throw this.errf("unexpected content after top-level value");
|
|
126
|
+
}
|
|
127
|
+
return v;
|
|
128
|
+
}
|
|
129
|
+
/** Consume whitespace, line terminators, and both comment forms. */
|
|
130
|
+
skipSpace() {
|
|
131
|
+
while (this.pos < this.src.length) {
|
|
132
|
+
const { cp, size } = this.rune();
|
|
133
|
+
if (isJson5Space(cp) || isLineTerminator(cp)) {
|
|
134
|
+
this.advance(cp, size);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (cp === 47 && this.src[this.pos + 1] === "/") {
|
|
138
|
+
this.pos += 2;
|
|
139
|
+
while (this.pos < this.src.length) {
|
|
140
|
+
const cp2 = this.src.codePointAt(this.pos);
|
|
141
|
+
if (isLineTerminator(cp2)) break;
|
|
142
|
+
this.pos += cp2 > 65535 ? 2 : 1;
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (cp === 47 && this.src[this.pos + 1] === "*") {
|
|
147
|
+
this.pos += 2;
|
|
148
|
+
let closed = false;
|
|
149
|
+
while (this.pos < this.src.length) {
|
|
150
|
+
const r2 = this.rune();
|
|
151
|
+
if (r2.cp === 42 && this.src[this.pos + 1] === "/") {
|
|
152
|
+
this.pos += 2;
|
|
153
|
+
closed = true;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
this.advance(r2.cp, r2.size);
|
|
157
|
+
}
|
|
158
|
+
if (!closed) throw this.errf("unterminated /* comment");
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
parseValue() {
|
|
165
|
+
if (this.pos >= this.src.length) {
|
|
166
|
+
throw this.errf("unexpected end of input, expected a value");
|
|
167
|
+
}
|
|
168
|
+
const c = this.src[this.pos];
|
|
169
|
+
if (c === "{") return this.parseObject();
|
|
170
|
+
if (c === "[") return this.parseArray();
|
|
171
|
+
if (c === '"' || c === "'") return this.parseString(c);
|
|
172
|
+
if (c === "+" || c === "-" || c === "." || c >= "0" && c <= "9") return this.parseNumber();
|
|
173
|
+
return this.parseKeyword();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Handle true/false/null, and reject Infinity/NaN by name so the error
|
|
177
|
+
* explains itself (spec F0.6).
|
|
178
|
+
*/
|
|
179
|
+
parseKeyword() {
|
|
180
|
+
const rest = this.src.slice(this.pos);
|
|
181
|
+
for (const [kw, v] of [
|
|
182
|
+
["true", true],
|
|
183
|
+
["false", false],
|
|
184
|
+
["null", null]
|
|
185
|
+
]) {
|
|
186
|
+
if (rest.startsWith(kw) && !continuesIdentifier(rest, kw.length)) {
|
|
187
|
+
this.pos += kw.length;
|
|
188
|
+
return v;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
for (const kw of ["Infinity", "NaN"]) {
|
|
192
|
+
if (rest.startsWith(kw) && !continuesIdentifier(rest, kw.length)) {
|
|
193
|
+
throw this.errf(`${kw} is not representable in the HOCON number model (spec F0.6)`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const first = String.fromCodePoint(rest.codePointAt(0));
|
|
197
|
+
throw this.errf(`unexpected character ${JSON.stringify(first)}`);
|
|
198
|
+
}
|
|
199
|
+
// ── Objects and arrays ──────────────────────────────────────────────────────
|
|
200
|
+
parseObject() {
|
|
201
|
+
this.pos++;
|
|
202
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
203
|
+
for (; ; ) {
|
|
204
|
+
this.skipSpace();
|
|
205
|
+
if (this.pos >= this.src.length) throw this.errf("unterminated object, expected '}'");
|
|
206
|
+
if (this.src[this.pos] === "}") {
|
|
207
|
+
this.pos++;
|
|
208
|
+
return obj;
|
|
209
|
+
}
|
|
210
|
+
const key = this.parseMemberName();
|
|
211
|
+
this.skipSpace();
|
|
212
|
+
if (this.pos >= this.src.length || this.src[this.pos] !== ":") {
|
|
213
|
+
throw this.errf(`expected ':' after object key ${JSON.stringify(key)}`);
|
|
214
|
+
}
|
|
215
|
+
this.pos++;
|
|
216
|
+
this.skipSpace();
|
|
217
|
+
let val = this.parseValue();
|
|
218
|
+
if (key in obj) {
|
|
219
|
+
const prev = obj[key];
|
|
220
|
+
if (isPlainObject(prev) && isPlainObject(val)) val = mergeObjects(prev, val);
|
|
221
|
+
}
|
|
222
|
+
obj[key] = val;
|
|
223
|
+
this.skipSpace();
|
|
224
|
+
if (this.pos >= this.src.length) throw this.errf("unterminated object, expected ',' or '}'");
|
|
225
|
+
const c = this.src[this.pos];
|
|
226
|
+
if (c === ",") {
|
|
227
|
+
this.pos++;
|
|
228
|
+
} else if (c === "}") {
|
|
229
|
+
this.pos++;
|
|
230
|
+
return obj;
|
|
231
|
+
} else {
|
|
232
|
+
throw this.errf("expected ',' or '}' in object");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
parseArray() {
|
|
237
|
+
this.pos++;
|
|
238
|
+
const arr = [];
|
|
239
|
+
for (; ; ) {
|
|
240
|
+
this.skipSpace();
|
|
241
|
+
if (this.pos >= this.src.length) throw this.errf("unterminated array, expected ']'");
|
|
242
|
+
if (this.src[this.pos] === "]") {
|
|
243
|
+
this.pos++;
|
|
244
|
+
return arr;
|
|
245
|
+
}
|
|
246
|
+
arr.push(this.parseValue());
|
|
247
|
+
this.skipSpace();
|
|
248
|
+
if (this.pos >= this.src.length) throw this.errf("unterminated array, expected ',' or ']'");
|
|
249
|
+
const c = this.src[this.pos];
|
|
250
|
+
if (c === ",") {
|
|
251
|
+
this.pos++;
|
|
252
|
+
} else if (c === "]") {
|
|
253
|
+
this.pos++;
|
|
254
|
+
return arr;
|
|
255
|
+
} else {
|
|
256
|
+
throw this.errf("expected ',' or ']' in array");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// ── Member names (quoted or ES5 IdentifierName) ─────────────────────────────
|
|
261
|
+
parseMemberName() {
|
|
262
|
+
const c = this.src[this.pos];
|
|
263
|
+
if (c === '"' || c === "'") return this.parseString(c);
|
|
264
|
+
return this.parseIdentifier();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Scan an ES5 IdentifierName, honouring `\uXXXX` escapes in the name (the
|
|
268
|
+
* escaped codepoint must itself be a legal identifier character for its
|
|
269
|
+
* position, per ES5 — `1` cannot start a key).
|
|
270
|
+
*/
|
|
271
|
+
parseIdentifier() {
|
|
272
|
+
let name = "";
|
|
273
|
+
let first = true;
|
|
274
|
+
while (this.pos < this.src.length) {
|
|
275
|
+
const r = this.rune();
|
|
276
|
+
let cp = r.cp;
|
|
277
|
+
let escaped = false;
|
|
278
|
+
if (cp === 92) {
|
|
279
|
+
if (this.src[this.pos + 1] !== "u") {
|
|
280
|
+
throw this.errf("only \\uXXXX escapes are allowed in identifiers");
|
|
281
|
+
}
|
|
282
|
+
this.pos += 2;
|
|
283
|
+
cp = this.readHex4();
|
|
284
|
+
escaped = true;
|
|
285
|
+
}
|
|
286
|
+
const legal = first ? isIdentStart(cp) : isIdentPart(cp);
|
|
287
|
+
if (!legal) {
|
|
288
|
+
if (escaped) {
|
|
289
|
+
throw this.errf(`escape \\u${hex4(cp)} is not a valid identifier character here`);
|
|
290
|
+
}
|
|
291
|
+
if (first) {
|
|
292
|
+
throw this.errf(`expected an object key, got ${JSON.stringify(String.fromCodePoint(cp))}`);
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
name += String.fromCodePoint(cp);
|
|
297
|
+
if (!escaped) this.pos += r.size;
|
|
298
|
+
first = false;
|
|
299
|
+
}
|
|
300
|
+
if (name.length === 0) throw this.errf("expected an object key");
|
|
301
|
+
return name;
|
|
302
|
+
}
|
|
303
|
+
/** Read exactly four hex digits at `pos` and return the code point. */
|
|
304
|
+
readHex4() {
|
|
305
|
+
if (this.pos + 4 > this.src.length) throw this.errf("truncated \\u escape");
|
|
306
|
+
const quad = this.src.slice(this.pos, this.pos + 4);
|
|
307
|
+
if (!/^[0-9a-fA-F]{4}$/.test(quad)) {
|
|
308
|
+
throw this.errf(`invalid \\u escape ${JSON.stringify("\\u" + quad)}`);
|
|
309
|
+
}
|
|
310
|
+
this.pos += 4;
|
|
311
|
+
return parseInt(quad, 16);
|
|
312
|
+
}
|
|
313
|
+
// ── Strings ─────────────────────────────────────────────────────────────────
|
|
314
|
+
/**
|
|
315
|
+
* Scan a single- or double-quoted JSON5 string. `quote` is the opening quote
|
|
316
|
+
* character. JSON5 differences from JSON: either quote character, `\xHH`
|
|
317
|
+
* escapes, `\v`, `\0`, line continuations (backslash before a line
|
|
318
|
+
* terminator, including CRLF as one), any other non-digit character escaping
|
|
319
|
+
* to itself, and unescaped LS/PS allowed inside the string.
|
|
320
|
+
*/
|
|
321
|
+
parseString(quote) {
|
|
322
|
+
this.pos++;
|
|
323
|
+
let out = "";
|
|
324
|
+
for (; ; ) {
|
|
325
|
+
if (this.pos >= this.src.length) throw this.errf("unterminated string");
|
|
326
|
+
const { cp, size } = this.rune();
|
|
327
|
+
if (this.src[this.pos] === quote) {
|
|
328
|
+
this.pos++;
|
|
329
|
+
return out;
|
|
330
|
+
}
|
|
331
|
+
if (cp === 10 || cp === 13) {
|
|
332
|
+
throw this.errf("unescaped line terminator in string");
|
|
333
|
+
}
|
|
334
|
+
if (cp === 92) {
|
|
335
|
+
this.pos++;
|
|
336
|
+
out += this.readEscape();
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
out += this.src.slice(this.pos, this.pos + size);
|
|
340
|
+
this.advance(cp, size);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Consume one escape sequence (the backslash is already consumed) and return
|
|
345
|
+
* its value.
|
|
346
|
+
*/
|
|
347
|
+
readEscape() {
|
|
348
|
+
if (this.pos >= this.src.length) throw this.errf("unterminated escape sequence");
|
|
349
|
+
const { cp, size } = this.rune();
|
|
350
|
+
if (isLineTerminator(cp)) {
|
|
351
|
+
this.advance(cp, size);
|
|
352
|
+
return "";
|
|
353
|
+
}
|
|
354
|
+
const ch = String.fromCodePoint(cp);
|
|
355
|
+
switch (ch) {
|
|
356
|
+
case "n":
|
|
357
|
+
this.pos++;
|
|
358
|
+
return "\n";
|
|
359
|
+
case "t":
|
|
360
|
+
this.pos++;
|
|
361
|
+
return " ";
|
|
362
|
+
case "r":
|
|
363
|
+
this.pos++;
|
|
364
|
+
return "\r";
|
|
365
|
+
case "b":
|
|
366
|
+
this.pos++;
|
|
367
|
+
return "\b";
|
|
368
|
+
case "f":
|
|
369
|
+
this.pos++;
|
|
370
|
+
return "\f";
|
|
371
|
+
case "v":
|
|
372
|
+
this.pos++;
|
|
373
|
+
return "\v";
|
|
374
|
+
case "0": {
|
|
375
|
+
const next = this.src[this.pos + 1];
|
|
376
|
+
if (next !== void 0 && next >= "0" && next <= "9") {
|
|
377
|
+
throw this.errf("octal escape sequences are not allowed");
|
|
378
|
+
}
|
|
379
|
+
this.pos++;
|
|
380
|
+
return "\0";
|
|
381
|
+
}
|
|
382
|
+
case "1":
|
|
383
|
+
case "2":
|
|
384
|
+
case "3":
|
|
385
|
+
case "4":
|
|
386
|
+
case "5":
|
|
387
|
+
case "6":
|
|
388
|
+
case "7":
|
|
389
|
+
case "8":
|
|
390
|
+
case "9":
|
|
391
|
+
throw this.errf(`escape \\${ch} is not allowed (digits cannot be escaped)`);
|
|
392
|
+
case "x": {
|
|
393
|
+
this.pos++;
|
|
394
|
+
if (this.pos + 2 > this.src.length) throw this.errf("truncated \\x escape");
|
|
395
|
+
const hh = this.src.slice(this.pos, this.pos + 2);
|
|
396
|
+
if (!/^[0-9a-fA-F]{2}$/.test(hh)) throw this.errf("invalid \\x escape");
|
|
397
|
+
this.pos += 2;
|
|
398
|
+
return String.fromCharCode(parseInt(hh, 16));
|
|
399
|
+
}
|
|
400
|
+
case "u": {
|
|
401
|
+
this.pos++;
|
|
402
|
+
const hi = this.readHex4();
|
|
403
|
+
if (hi >= 55296 && hi <= 57343) {
|
|
404
|
+
if (this.pos + 6 <= this.src.length && this.src[this.pos] === "\\" && this.src[this.pos + 1] === "u") {
|
|
405
|
+
this.pos += 2;
|
|
406
|
+
const lo = this.readHex4();
|
|
407
|
+
if (hi <= 56319 && lo >= 56320 && lo <= 57343) {
|
|
408
|
+
return String.fromCodePoint(65536 + (hi - 55296 << 10) + (lo - 56320));
|
|
409
|
+
}
|
|
410
|
+
throw this.errf(`unpaired \\u${hex4(hi)} surrogate (spec F3.5)`);
|
|
411
|
+
}
|
|
412
|
+
throw this.errf(`unpaired \\u${hex4(hi)} surrogate (spec F3.5)`);
|
|
413
|
+
}
|
|
414
|
+
return String.fromCharCode(hi);
|
|
415
|
+
}
|
|
416
|
+
default:
|
|
417
|
+
this.advance(cp, size);
|
|
418
|
+
return ch;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// ── Numbers ─────────────────────────────────────────────────────────────────
|
|
422
|
+
/**
|
|
423
|
+
* Scan a JSON5 numeric literal: optional sign, then a hex integer or a
|
|
424
|
+
* decimal with optional leading/trailing point and exponent. Signed
|
|
425
|
+
* Infinity/NaN are routed to the F0.6 error here.
|
|
426
|
+
*/
|
|
427
|
+
parseNumber() {
|
|
428
|
+
const start = this.pos;
|
|
429
|
+
let neg = false;
|
|
430
|
+
const sign = this.src[this.pos];
|
|
431
|
+
if (sign === "+" || sign === "-") {
|
|
432
|
+
neg = sign === "-";
|
|
433
|
+
this.pos++;
|
|
434
|
+
}
|
|
435
|
+
const rest = this.src.slice(this.pos);
|
|
436
|
+
for (const kw of ["Infinity", "NaN"]) {
|
|
437
|
+
if (rest.startsWith(kw) && !continuesIdentifier(rest, kw.length)) {
|
|
438
|
+
throw this.errf(`${kw} is not representable in the HOCON number model (spec F0.6)`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (rest.startsWith("0x") || rest.startsWith("0X")) {
|
|
442
|
+
this.pos += 2;
|
|
443
|
+
const ds = this.pos;
|
|
444
|
+
while (this.pos < this.src.length && isHexDigit(this.src[this.pos])) this.pos++;
|
|
445
|
+
if (this.pos === ds) throw this.errf("hex literal needs at least one digit");
|
|
446
|
+
const mag = BigInt("0x" + this.src.slice(ds, this.pos));
|
|
447
|
+
const limit = neg ? 1n << 63n : (1n << 63n) - 1n;
|
|
448
|
+
if (mag > limit) {
|
|
449
|
+
throw this.errf(`integer ${this.src.slice(start, this.pos)} does not fit in int64 (spec F0.5)`);
|
|
450
|
+
}
|
|
451
|
+
if (neg && mag === 0n) return -0;
|
|
452
|
+
return integerValue(neg ? -mag : mag);
|
|
453
|
+
}
|
|
454
|
+
let sawDigit = false;
|
|
455
|
+
let sawDot = false;
|
|
456
|
+
let sawExp = false;
|
|
457
|
+
while (this.pos < this.src.length) {
|
|
458
|
+
const c = this.src[this.pos];
|
|
459
|
+
if (c >= "0" && c <= "9") {
|
|
460
|
+
sawDigit = true;
|
|
461
|
+
} else if (c === "." && !sawDot && !sawExp) {
|
|
462
|
+
sawDot = true;
|
|
463
|
+
} else if ((c === "e" || c === "E") && sawDigit && !sawExp) {
|
|
464
|
+
sawExp = true;
|
|
465
|
+
const n = this.src[this.pos + 1];
|
|
466
|
+
if (n === "+" || n === "-") this.pos++;
|
|
467
|
+
} else {
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
this.pos++;
|
|
471
|
+
}
|
|
472
|
+
const text = this.src.slice(start, this.pos);
|
|
473
|
+
if (!sawDigit) throw this.errf(`malformed number ${JSON.stringify(text)}`);
|
|
474
|
+
if (sawDot || sawExp) {
|
|
475
|
+
const f = Number(text);
|
|
476
|
+
if (Number.isNaN(f) || !Number.isFinite(f)) {
|
|
477
|
+
throw this.errf(`malformed number ${JSON.stringify(text)}`);
|
|
478
|
+
}
|
|
479
|
+
return f;
|
|
480
|
+
}
|
|
481
|
+
const digits = text.startsWith("+") ? text.slice(1) : text;
|
|
482
|
+
const i = BigInt(digits);
|
|
483
|
+
if (i > INT64_MAX || i < INT64_MIN) {
|
|
484
|
+
throw this.errf(`integer ${text} does not fit in int64 (spec F0.5)`);
|
|
485
|
+
}
|
|
486
|
+
if (i >= -9007199254740991n && i <= 9007199254740991n) return Number(digits);
|
|
487
|
+
return i;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
function integerValue(v) {
|
|
491
|
+
if (v >= -9007199254740991n && v <= 9007199254740991n) return Number(v);
|
|
492
|
+
return v;
|
|
493
|
+
}
|
|
494
|
+
export {
|
|
495
|
+
parseJson5
|
|
496
|
+
};
|