@effected/toml 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.
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/Toml.js +165 -0
- package/TomlDateTime.js +147 -0
- package/TomlDiagnostic.js +106 -0
- package/TomlDocument.js +133 -0
- package/TomlEdit.js +52 -0
- package/TomlFormat.js +611 -0
- package/TomlNode.js +212 -0
- package/TomlVisitor.js +152 -0
- package/index.d.ts +897 -0
- package/index.js +10 -0
- package/internal/diagnostics.js +53 -0
- package/internal/limits.js +24 -0
- package/internal/parser.js +408 -0
- package/internal/scanner.js +493 -0
- package/internal/semantic.js +253 -0
- package/internal/stringifyValue.js +191 -0
- package/package.json +45 -0
- package/tsdoc-metadata.json +11 -0
package/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime, TomlOffsetDateTime } from "./TomlDateTime.js";
|
|
2
|
+
import { TomlArray, TomlArrayTableHeader, TomlBoolean, TomlDateTimeLiteral, TomlExpression, TomlFloat, TomlInlineEntry, TomlInlineTable, TomlInteger, TomlKey, TomlKeyKind, TomlKeyValue, TomlString, TomlStringStyle, TomlTableHeader, TomlTrivia, TomlValueNode } from "./TomlNode.js";
|
|
3
|
+
import { TomlDiagnostic, TomlErrorCode, TomlLexErrorCode, TomlParseErrorCode, TomlSemanticErrorCode, TomlStringifyErrorCode } from "./TomlDiagnostic.js";
|
|
4
|
+
import { Toml, TomlParseError, TomlStringifyError, TomlStringifyOptions } from "./Toml.js";
|
|
5
|
+
import { TomlDocument } from "./TomlDocument.js";
|
|
6
|
+
import { TomlEdit, TomlRange } from "./TomlEdit.js";
|
|
7
|
+
import { TomlFormat, TomlFormattingOptions, TomlModificationError } from "./TomlFormat.js";
|
|
8
|
+
import { TomlVisitor, TomlVisitorEvent } from "./TomlVisitor.js";
|
|
9
|
+
|
|
10
|
+
export { Toml, TomlArray, TomlArrayTableHeader, TomlBoolean, TomlDateTimeLiteral, TomlDiagnostic, TomlDocument, TomlEdit, TomlErrorCode, TomlExpression, TomlFloat, TomlFormat, TomlFormattingOptions, TomlInlineEntry, TomlInlineTable, TomlInteger, TomlKey, TomlKeyKind, TomlKeyValue, TomlLexErrorCode, TomlLocalDate, TomlLocalDateTime, TomlLocalTime, TomlModificationError, TomlOffsetDateTime, TomlParseError, TomlParseErrorCode, TomlRange, TomlSemanticErrorCode, TomlString, TomlStringStyle, TomlStringifyError, TomlStringifyErrorCode, TomlStringifyOptions, TomlTableHeader, TomlTrivia, TomlValueNode, TomlVisitor, TomlVisitorEvent };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/internal/diagnostics.ts
|
|
2
|
+
const TOML_LEX_ERROR_CODES = [
|
|
3
|
+
"InvalidUtf8",
|
|
4
|
+
"UnterminatedString",
|
|
5
|
+
"InvalidEscape",
|
|
6
|
+
"InvalidUnicodeEscape",
|
|
7
|
+
"ControlCharacterInString",
|
|
8
|
+
"ControlCharacterInComment",
|
|
9
|
+
"InvalidCharacter",
|
|
10
|
+
"BareCarriageReturn"
|
|
11
|
+
];
|
|
12
|
+
const TOML_PARSE_ERROR_CODES = [
|
|
13
|
+
"ExpectedKey",
|
|
14
|
+
"ExpectedEquals",
|
|
15
|
+
"ExpectedValue",
|
|
16
|
+
"ExpectedNewline",
|
|
17
|
+
"ExpectedTableHeaderClose",
|
|
18
|
+
"UnterminatedArray",
|
|
19
|
+
"UnterminatedInlineTable",
|
|
20
|
+
"TrailingCommaInInlineTable",
|
|
21
|
+
"NewlineInInlineTable",
|
|
22
|
+
"InvalidValue",
|
|
23
|
+
"InvalidNumber",
|
|
24
|
+
"IntegerOutOfRange",
|
|
25
|
+
"InvalidDateTime",
|
|
26
|
+
"NestingDepthExceeded"
|
|
27
|
+
];
|
|
28
|
+
const TOML_SEMANTIC_ERROR_CODES = [
|
|
29
|
+
"DuplicateKey",
|
|
30
|
+
"TableRedefined",
|
|
31
|
+
"ArrayOfTablesConflict",
|
|
32
|
+
"DottedKeyConflict",
|
|
33
|
+
"InlineTableExtended"
|
|
34
|
+
];
|
|
35
|
+
const TOML_STRINGIFY_ERROR_CODES = [
|
|
36
|
+
"CircularReference",
|
|
37
|
+
"UnsupportedValue",
|
|
38
|
+
"IntegerOutOfRange",
|
|
39
|
+
"NestingDepthExceeded"
|
|
40
|
+
];
|
|
41
|
+
/** The engine's only throw carrier besides GuardExceeded. */
|
|
42
|
+
var RawTomlError = class extends Error {
|
|
43
|
+
diagnostic;
|
|
44
|
+
_tag = "RawTomlError";
|
|
45
|
+
constructor(diagnostic) {
|
|
46
|
+
super(diagnostic.message);
|
|
47
|
+
this.diagnostic = diagnostic;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const isRawTomlError = (u) => u instanceof RawTomlError;
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
export { RawTomlError, TOML_LEX_ERROR_CODES, TOML_PARSE_ERROR_CODES, TOML_SEMANTIC_ERROR_CODES, TOML_STRINGIFY_ERROR_CODES, isRawTomlError };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/internal/limits.ts
|
|
2
|
+
/**
|
|
3
|
+
* Raw guard-trip signal. The engine throws it; ONLY the public modules catch
|
|
4
|
+
* it and materialize the typed error. It must never escape a public entry
|
|
5
|
+
* point as a defect.
|
|
6
|
+
*/
|
|
7
|
+
var GuardExceeded = class extends Error {
|
|
8
|
+
reason;
|
|
9
|
+
limit;
|
|
10
|
+
actual;
|
|
11
|
+
offset;
|
|
12
|
+
_tag = "GuardExceeded";
|
|
13
|
+
constructor(reason, limit, actual, offset) {
|
|
14
|
+
super(`${reason}: limit ${limit}, actual ${actual}`);
|
|
15
|
+
this.reason = reason;
|
|
16
|
+
this.limit = limit;
|
|
17
|
+
this.actual = actual;
|
|
18
|
+
this.offset = offset;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const isGuardExceeded = (u) => u instanceof GuardExceeded;
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { GuardExceeded, isGuardExceeded };
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import { RawTomlError } from "./diagnostics.js";
|
|
2
|
+
import { GuardExceeded } from "./limits.js";
|
|
3
|
+
import { TomlLocalDate, TomlLocalDateTime, TomlLocalTime, TomlOffsetDateTime } from "../TomlDateTime.js";
|
|
4
|
+
import { TomlArray, TomlArrayTableHeader, TomlBoolean, TomlDateTimeLiteral, TomlFloat, TomlInlineEntry, TomlInlineTable, TomlInteger, TomlKey, TomlKeyValue, TomlString, TomlTableHeader, TomlTrivia } from "../TomlNode.js";
|
|
5
|
+
import { assertValidUnicode, classifyValueToken, scanBareKey, scanBasicString, scanComment, scanLiteralString, scanMultilineBasicString, scanMultilineLiteralString, scanNewline, scanValueToken, scanWhitespace, skipBom } from "./scanner.js";
|
|
6
|
+
|
|
7
|
+
//#region src/internal/parser.ts
|
|
8
|
+
const LF = 10;
|
|
9
|
+
const CR = 13;
|
|
10
|
+
const QUOTE = 34;
|
|
11
|
+
const HASH = 35;
|
|
12
|
+
const APOSTROPHE = 39;
|
|
13
|
+
const COMMA = 44;
|
|
14
|
+
const DOT = 46;
|
|
15
|
+
const EQUALS = 61;
|
|
16
|
+
const LEFT_BRACKET = 91;
|
|
17
|
+
const RIGHT_BRACKET = 93;
|
|
18
|
+
const LEFT_BRACE = 123;
|
|
19
|
+
const RIGHT_BRACE = 125;
|
|
20
|
+
const raise = (code, message, offset, length) => {
|
|
21
|
+
throw new RawTomlError({
|
|
22
|
+
code,
|
|
23
|
+
message,
|
|
24
|
+
offset,
|
|
25
|
+
length
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
/** Decoded trailing-comment text: the raw text after `#` with one leading space stripped. */
|
|
29
|
+
const decodeComment = (raw) => raw.startsWith(" ") ? raw.slice(1) : raw;
|
|
30
|
+
/** One simple key: bare, basic-quoted or literal-quoted. */
|
|
31
|
+
const parseSimpleKey = (source, pos) => {
|
|
32
|
+
const code = source.charCodeAt(pos);
|
|
33
|
+
if (code === QUOTE) {
|
|
34
|
+
const scanned = scanBasicString(source, pos);
|
|
35
|
+
return {
|
|
36
|
+
node: new TomlKey({
|
|
37
|
+
value: scanned.value,
|
|
38
|
+
kind: "basic",
|
|
39
|
+
offset: pos,
|
|
40
|
+
length: scanned.end - pos
|
|
41
|
+
}),
|
|
42
|
+
end: scanned.end
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (code === APOSTROPHE) {
|
|
46
|
+
const scanned = scanLiteralString(source, pos);
|
|
47
|
+
return {
|
|
48
|
+
node: new TomlKey({
|
|
49
|
+
value: scanned.value,
|
|
50
|
+
kind: "literal",
|
|
51
|
+
offset: pos,
|
|
52
|
+
length: scanned.end - pos
|
|
53
|
+
}),
|
|
54
|
+
end: scanned.end
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const bare = scanBareKey(source, pos);
|
|
58
|
+
if (bare.end === pos) return raise("ExpectedKey", "expected a key", pos, 1);
|
|
59
|
+
return {
|
|
60
|
+
node: new TomlKey({
|
|
61
|
+
value: bare.value,
|
|
62
|
+
kind: "bare",
|
|
63
|
+
offset: pos,
|
|
64
|
+
length: bare.end - pos
|
|
65
|
+
}),
|
|
66
|
+
end: bare.end
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/** A dotted key path: `simple-key ( ws "." ws simple-key )*`. */
|
|
70
|
+
const parseKeyPath = (source, pos) => {
|
|
71
|
+
const keys = [];
|
|
72
|
+
const first = parseSimpleKey(source, pos);
|
|
73
|
+
keys.push(first.node);
|
|
74
|
+
let i = first.end;
|
|
75
|
+
for (;;) {
|
|
76
|
+
const afterWs = scanWhitespace(source, i);
|
|
77
|
+
if (source.charCodeAt(afterWs) !== DOT) break;
|
|
78
|
+
const keyStart = scanWhitespace(source, afterWs + 1);
|
|
79
|
+
const next = parseSimpleKey(source, keyStart);
|
|
80
|
+
keys.push(next.node);
|
|
81
|
+
i = next.end;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
node: keys,
|
|
85
|
+
end: i
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
/** The end of an expression line: ws + optional comment + newline (or EOF), else ExpectedNewline. */
|
|
89
|
+
const parseLineEnd = (source, pos) => {
|
|
90
|
+
let i = scanWhitespace(source, pos);
|
|
91
|
+
let comment;
|
|
92
|
+
if (source.charCodeAt(i) === HASH) {
|
|
93
|
+
const scanned = scanComment(source, i);
|
|
94
|
+
comment = decodeComment(scanned.value);
|
|
95
|
+
i = scanned.end;
|
|
96
|
+
}
|
|
97
|
+
if (i >= source.length) return {
|
|
98
|
+
...comment !== void 0 ? { comment } : {},
|
|
99
|
+
end: i
|
|
100
|
+
};
|
|
101
|
+
const after = scanNewline(source, i);
|
|
102
|
+
if (after === i) return raise("ExpectedNewline", "expected a newline after the expression", i, 1);
|
|
103
|
+
return {
|
|
104
|
+
...comment !== void 0 ? { comment } : {},
|
|
105
|
+
end: after
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
/** Skip whitespace, newlines and comments inside array brackets. */
|
|
109
|
+
const skipArrayGap = (source, pos) => {
|
|
110
|
+
let i = pos;
|
|
111
|
+
for (;;) {
|
|
112
|
+
i = scanWhitespace(source, i);
|
|
113
|
+
if (source.charCodeAt(i) === HASH) {
|
|
114
|
+
i = scanComment(source, i).end;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const after = scanNewline(source, i);
|
|
118
|
+
if (after !== i) {
|
|
119
|
+
i = after;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
return i;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
/** Guard the inline-table single-line rule and unterminated end-of-input. */
|
|
126
|
+
const checkInsideInlineTable = (source, pos, openPos) => {
|
|
127
|
+
if (pos >= source.length) raise("UnterminatedInlineTable", "inline table not closed before end of input", openPos, pos - openPos);
|
|
128
|
+
const code = source.charCodeAt(pos);
|
|
129
|
+
if (code === LF || code === CR) raise("NewlineInInlineTable", "inline tables must fit on a single line", pos, 1);
|
|
130
|
+
};
|
|
131
|
+
/** Whether a token that classified as `number` spells a float (never called on hex/oct/bin). */
|
|
132
|
+
const isFloatToken = (token) => !/^0[xob]/.test(token) && (/[.eE]/.test(token) || token.includes("inf") || token.includes("nan"));
|
|
133
|
+
/** An array value starting at `[`; `depth` is this array's own nesting count. */
|
|
134
|
+
const parseArray = (source, openPos, depth) => {
|
|
135
|
+
if (depth > 256) throw new GuardExceeded("NestingDepthExceeded", 256, depth, openPos);
|
|
136
|
+
const items = [];
|
|
137
|
+
let i = openPos + 1;
|
|
138
|
+
for (;;) {
|
|
139
|
+
i = skipArrayGap(source, i);
|
|
140
|
+
if (i >= source.length) raise("UnterminatedArray", "array not closed before end of input", openPos, i - openPos);
|
|
141
|
+
if (source.charCodeAt(i) === RIGHT_BRACKET) {
|
|
142
|
+
i += 1;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
const item = parseValue(source, i, depth);
|
|
146
|
+
items.push(item.node);
|
|
147
|
+
i = skipArrayGap(source, item.end);
|
|
148
|
+
const code = source.charCodeAt(i);
|
|
149
|
+
if (code === COMMA) {
|
|
150
|
+
i += 1;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (code === RIGHT_BRACKET) {
|
|
154
|
+
i += 1;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (i >= source.length) raise("UnterminatedArray", "array not closed before end of input", openPos, i - openPos);
|
|
158
|
+
raise("UnterminatedArray", "expected , or ] in array", i, 1);
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
node: new TomlArray({
|
|
162
|
+
items,
|
|
163
|
+
offset: openPos,
|
|
164
|
+
length: i - openPos
|
|
165
|
+
}),
|
|
166
|
+
end: i
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
/** An inline table starting at `{`; `depth` is this table's own nesting count. */
|
|
170
|
+
const parseInlineTable = (source, openPos, depth) => {
|
|
171
|
+
if (depth > 256) throw new GuardExceeded("NestingDepthExceeded", 256, depth, openPos);
|
|
172
|
+
const entries = [];
|
|
173
|
+
let i = scanWhitespace(source, openPos + 1);
|
|
174
|
+
checkInsideInlineTable(source, i, openPos);
|
|
175
|
+
if (source.charCodeAt(i) === RIGHT_BRACE) return {
|
|
176
|
+
node: new TomlInlineTable({
|
|
177
|
+
entries,
|
|
178
|
+
offset: openPos,
|
|
179
|
+
length: i + 1 - openPos
|
|
180
|
+
}),
|
|
181
|
+
end: i + 1
|
|
182
|
+
};
|
|
183
|
+
for (;;) {
|
|
184
|
+
const entryStart = i;
|
|
185
|
+
const keyPath = parseKeyPath(source, i);
|
|
186
|
+
i = scanWhitespace(source, keyPath.end);
|
|
187
|
+
checkInsideInlineTable(source, i, openPos);
|
|
188
|
+
if (source.charCodeAt(i) !== EQUALS) raise("ExpectedEquals", "expected = after key", i, 1);
|
|
189
|
+
i = scanWhitespace(source, i + 1);
|
|
190
|
+
checkInsideInlineTable(source, i, openPos);
|
|
191
|
+
const value = parseValue(source, i, depth);
|
|
192
|
+
entries.push(new TomlInlineEntry({
|
|
193
|
+
keyPath: keyPath.node,
|
|
194
|
+
value: value.node,
|
|
195
|
+
offset: entryStart,
|
|
196
|
+
length: value.end - entryStart
|
|
197
|
+
}));
|
|
198
|
+
i = scanWhitespace(source, value.end);
|
|
199
|
+
checkInsideInlineTable(source, i, openPos);
|
|
200
|
+
const code = source.charCodeAt(i);
|
|
201
|
+
if (code === RIGHT_BRACE) {
|
|
202
|
+
i += 1;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
if (code === COMMA) {
|
|
206
|
+
const commaPos = i;
|
|
207
|
+
i = scanWhitespace(source, i + 1);
|
|
208
|
+
checkInsideInlineTable(source, i, openPos);
|
|
209
|
+
if (source.charCodeAt(i) === RIGHT_BRACE) raise("TrailingCommaInInlineTable", "inline tables may not end with a trailing comma", commaPos, 1);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
raise("UnterminatedInlineTable", "expected , or } in inline table", i, 1);
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
node: new TomlInlineTable({
|
|
216
|
+
entries,
|
|
217
|
+
offset: openPos,
|
|
218
|
+
length: i - openPos
|
|
219
|
+
}),
|
|
220
|
+
end: i
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
/** Wrap a classified scalar token into its value node. */
|
|
224
|
+
const scalarNode = (source, pos) => {
|
|
225
|
+
const token = scanValueToken(source, pos);
|
|
226
|
+
if (token.value === "") return raise("ExpectedValue", "expected a value", pos, 1);
|
|
227
|
+
const scalar = classifyValueToken(token.value, pos);
|
|
228
|
+
const length = token.end - pos;
|
|
229
|
+
if (typeof scalar === "boolean") return {
|
|
230
|
+
node: new TomlBoolean({
|
|
231
|
+
value: scalar,
|
|
232
|
+
offset: pos,
|
|
233
|
+
length
|
|
234
|
+
}),
|
|
235
|
+
end: token.end
|
|
236
|
+
};
|
|
237
|
+
if (typeof scalar === "bigint") return {
|
|
238
|
+
node: new TomlInteger({
|
|
239
|
+
value: scalar,
|
|
240
|
+
offset: pos,
|
|
241
|
+
length
|
|
242
|
+
}),
|
|
243
|
+
end: token.end
|
|
244
|
+
};
|
|
245
|
+
if (typeof scalar === "number") return {
|
|
246
|
+
node: isFloatToken(token.value) ? new TomlFloat({
|
|
247
|
+
value: scalar,
|
|
248
|
+
offset: pos,
|
|
249
|
+
length
|
|
250
|
+
}) : new TomlInteger({
|
|
251
|
+
value: scalar,
|
|
252
|
+
offset: pos,
|
|
253
|
+
length
|
|
254
|
+
}),
|
|
255
|
+
end: token.end
|
|
256
|
+
};
|
|
257
|
+
if (scalar instanceof TomlOffsetDateTime || scalar instanceof TomlLocalDateTime || scalar instanceof TomlLocalDate || scalar instanceof TomlLocalTime) return {
|
|
258
|
+
node: new TomlDateTimeLiteral({
|
|
259
|
+
value: scalar,
|
|
260
|
+
offset: pos,
|
|
261
|
+
length
|
|
262
|
+
}),
|
|
263
|
+
end: token.end
|
|
264
|
+
};
|
|
265
|
+
return raise("InvalidValue", `${String(scalar)} is not a valid TOML value`, pos, length);
|
|
266
|
+
};
|
|
267
|
+
/** A value at `pos`; `depth` is the number of containers already enclosing it. */
|
|
268
|
+
const parseValue = (source, pos, depth) => {
|
|
269
|
+
const code = source.charCodeAt(pos);
|
|
270
|
+
if (pos >= source.length || code === LF || code === CR || code === HASH || code === COMMA || code === EQUALS || code === RIGHT_BRACKET || code === RIGHT_BRACE) return raise("ExpectedValue", "expected a value", pos, 1);
|
|
271
|
+
if (code === QUOTE) {
|
|
272
|
+
if (source.charCodeAt(pos + 1) === QUOTE && source.charCodeAt(pos + 2) === QUOTE) {
|
|
273
|
+
const scanned = scanMultilineBasicString(source, pos);
|
|
274
|
+
return {
|
|
275
|
+
node: new TomlString({
|
|
276
|
+
value: scanned.value,
|
|
277
|
+
style: "multiline-basic",
|
|
278
|
+
offset: pos,
|
|
279
|
+
length: scanned.end - pos
|
|
280
|
+
}),
|
|
281
|
+
end: scanned.end
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
const scanned = scanBasicString(source, pos);
|
|
285
|
+
return {
|
|
286
|
+
node: new TomlString({
|
|
287
|
+
value: scanned.value,
|
|
288
|
+
style: "basic",
|
|
289
|
+
offset: pos,
|
|
290
|
+
length: scanned.end - pos
|
|
291
|
+
}),
|
|
292
|
+
end: scanned.end
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (code === APOSTROPHE) {
|
|
296
|
+
if (source.charCodeAt(pos + 1) === APOSTROPHE && source.charCodeAt(pos + 2) === APOSTROPHE) {
|
|
297
|
+
const scanned = scanMultilineLiteralString(source, pos);
|
|
298
|
+
return {
|
|
299
|
+
node: new TomlString({
|
|
300
|
+
value: scanned.value,
|
|
301
|
+
style: "multiline-literal",
|
|
302
|
+
offset: pos,
|
|
303
|
+
length: scanned.end - pos
|
|
304
|
+
}),
|
|
305
|
+
end: scanned.end
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const scanned = scanLiteralString(source, pos);
|
|
309
|
+
return {
|
|
310
|
+
node: new TomlString({
|
|
311
|
+
value: scanned.value,
|
|
312
|
+
style: "literal",
|
|
313
|
+
offset: pos,
|
|
314
|
+
length: scanned.end - pos
|
|
315
|
+
}),
|
|
316
|
+
end: scanned.end
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (code === LEFT_BRACKET) return parseArray(source, pos, depth + 1);
|
|
320
|
+
if (code === LEFT_BRACE) return parseInlineTable(source, pos, depth + 1);
|
|
321
|
+
return scalarNode(source, pos);
|
|
322
|
+
};
|
|
323
|
+
/** One `key = value` expression line; returns the position after its newline. */
|
|
324
|
+
const parseKeyValueExpression = (source, lineStart, keyStart) => {
|
|
325
|
+
const keyPath = parseKeyPath(source, keyStart);
|
|
326
|
+
const equalsPos = scanWhitespace(source, keyPath.end);
|
|
327
|
+
if (source.charCodeAt(equalsPos) !== EQUALS) raise("ExpectedEquals", "expected = after key", equalsPos, 1);
|
|
328
|
+
const valueStart = scanWhitespace(source, equalsPos + 1);
|
|
329
|
+
const value = parseValue(source, valueStart, 0);
|
|
330
|
+
const lineEnd = parseLineEnd(source, value.end);
|
|
331
|
+
return {
|
|
332
|
+
node: new TomlKeyValue({
|
|
333
|
+
keyPath: keyPath.node,
|
|
334
|
+
value: value.node,
|
|
335
|
+
...lineEnd.comment !== void 0 ? { comment: lineEnd.comment } : {},
|
|
336
|
+
offset: lineStart,
|
|
337
|
+
length: lineEnd.end - lineStart
|
|
338
|
+
}),
|
|
339
|
+
end: lineEnd.end
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
/** One `[table]` or `[[array-of-tables]]` header line. */
|
|
343
|
+
const parseHeaderExpression = (source, lineStart, bracketPos) => {
|
|
344
|
+
const isArrayTable = source.charCodeAt(bracketPos + 1) === LEFT_BRACKET;
|
|
345
|
+
const keyStart = scanWhitespace(source, bracketPos + (isArrayTable ? 2 : 1));
|
|
346
|
+
const keyPath = parseKeyPath(source, keyStart);
|
|
347
|
+
let i = scanWhitespace(source, keyPath.end);
|
|
348
|
+
if (isArrayTable) {
|
|
349
|
+
if (source.charCodeAt(i) !== RIGHT_BRACKET || source.charCodeAt(i + 1) !== RIGHT_BRACKET) raise("ExpectedTableHeaderClose", "expected ]] to close the array-of-tables header", i, 1);
|
|
350
|
+
i += 2;
|
|
351
|
+
} else {
|
|
352
|
+
if (source.charCodeAt(i) !== RIGHT_BRACKET) raise("ExpectedTableHeaderClose", "expected ] to close the table header", i, 1);
|
|
353
|
+
i += 1;
|
|
354
|
+
}
|
|
355
|
+
const lineEnd = parseLineEnd(source, i);
|
|
356
|
+
const fields = {
|
|
357
|
+
keyPath: keyPath.node,
|
|
358
|
+
...lineEnd.comment !== void 0 ? { comment: lineEnd.comment } : {},
|
|
359
|
+
offset: lineStart,
|
|
360
|
+
length: lineEnd.end - lineStart
|
|
361
|
+
};
|
|
362
|
+
return {
|
|
363
|
+
node: isArrayTable ? new TomlArrayTableHeader(fields) : new TomlTableHeader(fields),
|
|
364
|
+
end: lineEnd.end
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Parse a TOML document into its linear CST: the flat, source-tiling list of
|
|
369
|
+
* expressions. Throws RawTomlError on malformed input and GuardExceeded when
|
|
370
|
+
* value nesting exceeds MAX_NESTING_DEPTH; the facade (Task 7) materializes
|
|
371
|
+
* both into typed errors.
|
|
372
|
+
*/
|
|
373
|
+
const parseExpressions = (source) => {
|
|
374
|
+
assertValidUnicode(source);
|
|
375
|
+
const expressions = [];
|
|
376
|
+
let triviaStart = -1;
|
|
377
|
+
const flushTrivia = (end) => {
|
|
378
|
+
if (triviaStart !== -1) {
|
|
379
|
+
expressions.push(new TomlTrivia({
|
|
380
|
+
text: source.slice(triviaStart, end),
|
|
381
|
+
offset: triviaStart,
|
|
382
|
+
length: end - triviaStart
|
|
383
|
+
}));
|
|
384
|
+
triviaStart = -1;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
let pos = 0;
|
|
388
|
+
while (pos < source.length) {
|
|
389
|
+
const lineStart = pos;
|
|
390
|
+
let i = scanWhitespace(source, lineStart === 0 ? skipBom(source) : lineStart);
|
|
391
|
+
const code = source.charCodeAt(i);
|
|
392
|
+
if (i >= source.length || code === LF || code === CR || code === HASH) {
|
|
393
|
+
if (code === HASH) i = scanComment(source, i).end;
|
|
394
|
+
pos = i < source.length ? scanNewline(source, i) : i;
|
|
395
|
+
if (triviaStart === -1) triviaStart = lineStart;
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
flushTrivia(lineStart);
|
|
399
|
+
const parsed = code === LEFT_BRACKET ? parseHeaderExpression(source, lineStart, i) : parseKeyValueExpression(source, lineStart, i);
|
|
400
|
+
expressions.push(parsed.node);
|
|
401
|
+
pos = parsed.end;
|
|
402
|
+
}
|
|
403
|
+
flushTrivia(source.length);
|
|
404
|
+
return expressions;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
//#endregion
|
|
408
|
+
export { parseExpressions };
|