yini-parser 1.4.3 → 1.6.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/CHANGELOG.md +52 -0
- package/README.md +187 -35
- package/dist/YINI.d.ts +29 -18
- package/dist/YINI.js +104 -3
- package/dist/core/astBuilder.d.ts +94 -18
- package/dist/core/astBuilder.js +439 -376
- package/dist/core/errorDataHandler.d.ts +6 -1
- package/dist/core/errorDataHandler.js +30 -43
- package/dist/core/internalTypes.d.ts +10 -1
- package/dist/core/objectBuilder.d.ts +8 -4
- package/dist/core/objectBuilder.js +47 -62
- package/dist/core/options/defaultParserOptions.d.ts +3 -2
- package/dist/core/options/defaultParserOptions.js +11 -2
- package/dist/core/options/optionsFunctions.js +6 -4
- package/dist/core/parsingRules/modeFromRulesMatcher.d.ts +1 -1
- package/dist/core/parsingRules/modeFromRulesMatcher.js +22 -13
- package/dist/core/pipeline/pipeline.js +35 -10
- package/dist/core/runtime.js +28 -19
- package/dist/grammar/generated/YiniLexer.d.ts +40 -53
- package/dist/grammar/generated/YiniLexer.js +357 -356
- package/dist/grammar/generated/YiniParser.d.ts +174 -118
- package/dist/grammar/generated/YiniParser.js +1185 -929
- package/dist/grammar/generated/YiniParserVisitor.d.ts +82 -19
- package/dist/grammar/generated/YiniParserVisitor.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -3
- package/dist/parsers/extractHeaderParts.d.ts +12 -19
- package/dist/parsers/extractHeaderParts.js +57 -46
- package/dist/parsers/parseNumber.d.ts +24 -6
- package/dist/parsers/parseNumber.js +114 -49
- package/dist/parsers/parseSectionHeader.d.ts +11 -3
- package/dist/parsers/parseSectionHeader.js +55 -43
- package/dist/parsers/parseString.js +39 -20
- package/dist/parsers/validateShebangPlacement.d.ts +3 -0
- package/dist/parsers/validateShebangPlacement.js +52 -0
- package/dist/types/index.d.ts +20 -3
- package/dist/utils/print.d.ts +1 -0
- package/dist/utils/print.js +5 -1
- package/dist/utils/string.d.ts +1 -0
- package/dist/utils/string.js +17 -1
- package/dist/utils/system.d.ts +1 -0
- package/dist/utils/system.js +6 -1
- package/dist/utils/yiniHelpers.d.ts +44 -2
- package/dist/utils/yiniHelpers.js +134 -46
- package/examples/compare-formats.md +1 -1
- package/examples/nested.yini +1 -1
- package/package.json +11 -3
- package/dist/YINI.js.map +0 -1
- package/dist/config/env.js.map +0 -1
- package/dist/core/astBuilder.js.map +0 -1
- package/dist/core/errorDataHandler.js.map +0 -1
- package/dist/core/internalTypes.js.map +0 -1
- package/dist/core/objectBuilder.js.map +0 -1
- package/dist/core/options/defaultParserOptions.js.map +0 -1
- package/dist/core/options/failLevel.js.map +0 -1
- package/dist/core/options/optionsFunctions.js.map +0 -1
- package/dist/core/parsingRules/modeFromRulesMatcher.js.map +0 -1
- package/dist/core/parsingRules/rulesConstAndGuards.js.map +0 -1
- package/dist/core/pipeline/errorListeners.js.map +0 -1
- package/dist/core/pipeline/pipeline.js.map +0 -1
- package/dist/core/resultMetadataBuilder.js.map +0 -1
- package/dist/core/runtime.js.map +0 -1
- package/dist/dev/main.d.ts +0 -1
- package/dist/dev/main.js +0 -168
- package/dist/dev/main.js.map +0 -1
- package/dist/dev/quick-test-samples/defect-inputs.d.ts +0 -37
- package/dist/dev/quick-test-samples/defect-inputs.js +0 -106
- package/dist/dev/quick-test-samples/defect-inputs.js.map +0 -1
- package/dist/dev/quick-test-samples/valid-inputs.d.ts +0 -21
- package/dist/dev/quick-test-samples/valid-inputs.js +0 -422
- package/dist/dev/quick-test-samples/valid-inputs.js.map +0 -1
- package/dist/grammar/generated/YiniLexer.js.map +0 -1
- package/dist/grammar/generated/YiniParser.js.map +0 -1
- package/dist/grammar/generated/YiniParserVisitor.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/parsers/extractHeaderParts.js.map +0 -1
- package/dist/parsers/extractSignificantYiniLine.js.map +0 -1
- package/dist/parsers/parseBoolean.js.map +0 -1
- package/dist/parsers/parseNull.js.map +0 -1
- package/dist/parsers/parseNumber.js.map +0 -1
- package/dist/parsers/parseSectionHeader.js.map +0 -1
- package/dist/parsers/parseString.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/number.js.map +0 -1
- package/dist/utils/object.js.map +0 -1
- package/dist/utils/pathAndFileName.js.map +0 -1
- package/dist/utils/print.js.map +0 -1
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/system.js.map +0 -1
- package/dist/utils/yiniHelpers.js.map +0 -1
|
@@ -2,9 +2,17 @@ import { ErrorDataHandler } from '../core/errorDataHandler';
|
|
|
2
2
|
import { TSectionHeaderType } from '../core/internalTypes';
|
|
3
3
|
import { StmtContext } from '../grammar/generated/YiniParser';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Parses a YINI section header line and returns the marker type,
|
|
6
|
+
* section name, and resolved section depth.
|
|
7
|
+
*
|
|
8
|
+
* Supports:
|
|
9
|
+
* - Repeated markers: ^ Section, ^^ Section, ^^^_^^^_^ Section.
|
|
10
|
+
* - Numeric shorthand markers: ^10 Section.
|
|
11
|
+
* - Alternative section marker characters handled by extractHeaderParts(..).
|
|
12
|
+
*
|
|
13
|
+
* @param rawLine Raw line containing the section header.
|
|
14
|
+
* @param errorHandler Error handler used for diagnostics.
|
|
15
|
+
* @param ctx Parser context used for error location reporting.
|
|
8
16
|
*/
|
|
9
17
|
declare const parseSectionHeader: (rawLine: string, errorHandler: ErrorDataHandler, ctx: null | StmtContext) => {
|
|
10
18
|
markerType: TSectionHeaderType;
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// src/parsers/parseSectionHeader.ts
|
|
6
7
|
const errorDataHandler_1 = require("../core/errorDataHandler");
|
|
7
8
|
const extractHeaderParts_1 = __importDefault(require("../parsers/extractHeaderParts"));
|
|
8
9
|
const extractSignificantYiniLine_1 = require("../parsers/extractSignificantYiniLine");
|
|
@@ -10,9 +11,17 @@ const print_1 = require("../utils/print");
|
|
|
10
11
|
const string_1 = require("../utils/string");
|
|
11
12
|
const yiniHelpers_1 = require("../utils/yiniHelpers");
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Parses a YINI section header line and returns the marker type,
|
|
15
|
+
* section name, and resolved section depth.
|
|
16
|
+
*
|
|
17
|
+
* Supports:
|
|
18
|
+
* - Repeated markers: ^ Section, ^^ Section, ^^^_^^^_^ Section.
|
|
19
|
+
* - Numeric shorthand markers: ^10 Section.
|
|
20
|
+
* - Alternative section marker characters handled by extractHeaderParts(..).
|
|
21
|
+
*
|
|
22
|
+
* @param rawLine Raw line containing the section header.
|
|
23
|
+
* @param errorHandler Error handler used for diagnostics.
|
|
24
|
+
* @param ctx Parser context used for error location reporting.
|
|
16
25
|
*/
|
|
17
26
|
const parseSectionHeader = (rawLine, errorHandler, ctx) => {
|
|
18
27
|
(0, print_1.debugPrint)('-> Entered parseSectionHeader(..)');
|
|
@@ -29,68 +38,71 @@ const parseSectionHeader = (rawLine, errorHandler, ctx) => {
|
|
|
29
38
|
let headerMarkerType;
|
|
30
39
|
let level = 0;
|
|
31
40
|
if (strMarkerChars === '') {
|
|
32
|
-
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Unknown section header marker type', 'Section header marker type could not be identified,
|
|
33
|
-
rawLine);
|
|
41
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Unknown section header marker type.', 'Section header marker type could not be identified.', `Header text: ${rawLine}`);
|
|
34
42
|
}
|
|
35
|
-
//
|
|
43
|
+
// ---------------------------------------------------------------------
|
|
44
|
+
// Determine section marker type and depth.
|
|
45
|
+
// ---------------------------------------------------------------------
|
|
36
46
|
if (strNumberPart === '') {
|
|
37
47
|
headerMarkerType = 'Classic-Header-Marker';
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
if ((0, yiniHelpers_1.hasMixedSectionMarkers)(strMarkerChars)) {
|
|
49
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Mixed section marker characters are not allowed.', `Got '${strMarkerChars}'. A repeated section marker sequence must use only one marker kind.`);
|
|
50
|
+
}
|
|
51
|
+
if ((0, yiniHelpers_1.hasInvalidSectionMarkerSeparatorPlacement)(strMarkerChars)) {
|
|
52
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid section marker separator placement.', `Got '${strMarkerChars}'. Section marker separators '_' may appear only between repeated occurrences of the same marker character.`, 'Examples of valid forms: ^^, ^^^_^^^, ^^^_^^^_^.');
|
|
42
53
|
}
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
headerMarkerType = 'Numeric-Header-Marker';
|
|
46
54
|
try {
|
|
47
|
-
|
|
55
|
+
strMarkerChars =
|
|
56
|
+
(0, yiniHelpers_1.normalizeRepeatedSectionMarkerSequence)(strMarkerChars);
|
|
48
57
|
}
|
|
49
58
|
catch (err) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
', raw: ' +
|
|
53
|
-
rawLine);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// ---------------------------------------------------------------
|
|
57
|
-
// --- Check level contraints based on headerMarkerType ----------
|
|
58
|
-
if (headerMarkerType === 'Classic-Header-Marker') {
|
|
59
|
-
if (level > 6) {
|
|
60
|
-
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid number of repeating marker characters: ' +
|
|
61
|
-
strMarkerChars, 'It is invalid to use seven or more section marker characters in succession (e.g. ^^^^^^^). However, to represent nesting levels deeper than 6, you may switch to the numeric shorthand section header syntax, e.g. ^7.');
|
|
59
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
60
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid section marker separator placement.', message, `Got '${strMarkerChars}'.`);
|
|
62
61
|
}
|
|
62
|
+
level = (0, yiniHelpers_1.countRepeatedSectionMarkers)(strMarkerChars);
|
|
63
63
|
}
|
|
64
64
|
else {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
headerMarkerType = 'Numeric-Header-Marker';
|
|
66
|
+
if (strMarkerChars.includes('_')) {
|
|
67
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid separator in numeric shorthand section header.', `Numeric shorthand section headers must not contain '_'. Got '${strMarkerChars}${strNumberPart}'.`, "Use numeric shorthand like '^10 Section', not '^_10 Section'.");
|
|
68
68
|
}
|
|
69
|
+
level = Number.parseInt(strNumberPart, 10);
|
|
70
|
+
if (Number.isNaN(level)) {
|
|
71
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid numeric shorthand section depth.', `Could not parse '${strNumberPart}' as a section depth.`, 'Use a positive integer, for example: ^1 Section, ^2 Section, or ^10 Section.');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------
|
|
75
|
+
// Validate resolved section depth.
|
|
76
|
+
// ---------------------------------------------------------------------
|
|
77
|
+
if (level < 1) {
|
|
78
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid section depth.', `Section depth must be 1 or higher. Got ${level}.`);
|
|
79
|
+
}
|
|
80
|
+
if (level > yiniHelpers_1.MAX_SECTION_DEPTH) {
|
|
81
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Section depth exceeds the maximum supported depth.', `Got section depth ${level}. The maximum supported section depth is ${yiniHelpers_1.MAX_SECTION_DEPTH}.`);
|
|
69
82
|
}
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
if (headerMarkerType === 'Classic-Header-Marker' &&
|
|
84
|
+
level > yiniHelpers_1.MAX_REPEATED_SECTION_MARKER_DEPTH) {
|
|
85
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Too many repeated section marker characters.', `Repeated/basic section markers may express levels 1–${yiniHelpers_1.MAX_REPEATED_SECTION_MARKER_DEPTH}. Got level ${level}.`, `For levels ${yiniHelpers_1.MAX_REPEATED_SECTION_MARKER_DEPTH + 1} and deeper, use numeric shorthand, for example '^10 Section'.`);
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------
|
|
88
|
+
// Validate section name.
|
|
89
|
+
// ---------------------------------------------------------------------
|
|
72
90
|
const lenOfName = strSectionName.length;
|
|
73
91
|
if (isBacktickedName) {
|
|
74
92
|
if (!(0, yiniHelpers_1.isValidBacktickedIdent)(strSectionName)) {
|
|
75
|
-
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error',
|
|
93
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', `Invalid section header name "${strSectionName}".`, 'Backticked section names must be enclosed in backticks, for example: `My section name`.');
|
|
76
94
|
}
|
|
95
|
+
strSectionName = (0, string_1.trimBackticks)(strSectionName);
|
|
77
96
|
}
|
|
78
97
|
else {
|
|
79
|
-
(0, print_1.debugPrint)('Naming
|
|
98
|
+
(0, print_1.debugPrint)('Naming constraints: Is not a BacktickedName.');
|
|
80
99
|
if (lenOfName <= 0) {
|
|
81
|
-
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', '
|
|
82
|
-
strSectionName +
|
|
83
|
-
'"');
|
|
100
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Missing section name.', `Invalid section header: '${rawLine}'.`, 'A section header must contain a marker and a section name, for example: ^ App.');
|
|
84
101
|
}
|
|
85
|
-
if (!(0, yiniHelpers_1.isValidSimpleIdent)(strSectionName)) {
|
|
86
|
-
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error',
|
|
87
|
-
strSectionName +
|
|
88
|
-
'`, `My section name`.');
|
|
102
|
+
else if (!(0, yiniHelpers_1.isValidSimpleIdent)(strSectionName)) {
|
|
103
|
+
errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', `Invalid section header name "${strSectionName}".`, 'Section names must start with A-Z, a-z, or _, unless enclosed in backticks.', `Use ${strSectionName} only if it is a valid simple identifier, or write it as \`${strSectionName}\`.`);
|
|
89
104
|
}
|
|
90
|
-
strSectionName = (0, string_1.trimBackticks)(strSectionName);
|
|
91
105
|
}
|
|
92
|
-
// ---------------------------------------------------------------
|
|
93
|
-
// strSectionName = trimBackticks(strSectionName)
|
|
94
106
|
(0, print_1.debugPrint)(' --------------');
|
|
95
107
|
(0, print_1.debugPrint)('<- About to leave parseSectionHeader(..)');
|
|
96
108
|
(0, print_1.debugPrint)(` rawLine = >>>${rawLine}<<<`);
|
|
@@ -10,28 +10,38 @@ class CYiniStringParseError extends Error {
|
|
|
10
10
|
exports.CYiniStringParseError = CYiniStringParseError;
|
|
11
11
|
const isHex = (c) => /^[0-9a-fA-F]$/.test(c);
|
|
12
12
|
const isOctal = (c) => /^[0-7]$/.test(c);
|
|
13
|
-
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const isUnicodeScalarValue = (codePoint) => {
|
|
14
|
+
return (codePoint >= 0x0000 &&
|
|
15
|
+
codePoint <= 0x10ffff &&
|
|
16
|
+
!(codePoint >= 0xd800 && codePoint <= 0xdfff));
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param isAllowRealLineBreaks True if this is a Multi-line string (a triple-quoted string in YINI).
|
|
21
|
+
*/
|
|
22
|
+
const parseClassicEscapes = (input, isAllowRealLineBreaks = false) => {
|
|
17
23
|
let result = '';
|
|
18
24
|
for (let i = 0; i < input.length; i++) {
|
|
19
25
|
const ch = input[i];
|
|
20
26
|
if (ch !== '\\') {
|
|
21
|
-
// Control character enforcement (U+0000–U+001F except tab).
|
|
22
27
|
const code = ch.charCodeAt(0);
|
|
23
|
-
if (code < 0x20
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
if (code < 0x20) {
|
|
29
|
+
const isAllowedLineBreak = isAllowRealLineBreaks && (ch === '\n' || ch === '\r');
|
|
30
|
+
const isAllowedTab = ch === '\t';
|
|
31
|
+
if (!isAllowedLineBreak && !isAllowedTab) {
|
|
32
|
+
throw new CYiniStringParseError(`Unescaped control character U+${code
|
|
33
|
+
.toString(16)
|
|
34
|
+
.padStart(4, '0')}`);
|
|
35
|
+
}
|
|
27
36
|
}
|
|
28
37
|
result += ch;
|
|
29
38
|
continue;
|
|
30
39
|
}
|
|
31
40
|
// Escape sequence.
|
|
32
41
|
const next = input[++i];
|
|
33
|
-
if (!next)
|
|
42
|
+
if (!next) {
|
|
34
43
|
throw new CYiniStringParseError('Invalid escape sequence at end of string');
|
|
44
|
+
}
|
|
35
45
|
switch (next) {
|
|
36
46
|
case '\\':
|
|
37
47
|
result += '\\';
|
|
@@ -75,7 +85,7 @@ const parseClassicEscapes = (input) => {
|
|
|
75
85
|
case 'x': {
|
|
76
86
|
const hex = input.slice(i + 1, i + 3);
|
|
77
87
|
if (hex.length !== 2 || ![...hex].every(isHex)) {
|
|
78
|
-
throw new CYiniStringParseError(
|
|
88
|
+
throw new CYiniStringParseError('Invalid \\x escape');
|
|
79
89
|
}
|
|
80
90
|
result += String.fromCharCode(parseInt(hex, 16));
|
|
81
91
|
i += 2;
|
|
@@ -84,18 +94,26 @@ const parseClassicEscapes = (input) => {
|
|
|
84
94
|
case 'u': {
|
|
85
95
|
const hex = input.slice(i + 1, i + 5);
|
|
86
96
|
if (hex.length !== 4 || ![...hex].every(isHex)) {
|
|
87
|
-
throw new CYiniStringParseError(
|
|
97
|
+
throw new CYiniStringParseError('Invalid \\u escape');
|
|
88
98
|
}
|
|
89
|
-
|
|
99
|
+
const codePoint = parseInt(hex, 16);
|
|
100
|
+
if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
|
|
101
|
+
throw new CYiniStringParseError('Unicode escape must not be a surrogate code point');
|
|
102
|
+
}
|
|
103
|
+
result += String.fromCharCode(codePoint);
|
|
90
104
|
i += 4;
|
|
91
105
|
break;
|
|
92
106
|
}
|
|
93
107
|
case 'U': {
|
|
94
108
|
const hex = input.slice(i + 1, i + 9);
|
|
95
109
|
if (hex.length !== 8 || ![...hex].every(isHex)) {
|
|
96
|
-
throw new CYiniStringParseError(
|
|
110
|
+
throw new CYiniStringParseError('Invalid \\U escape');
|
|
97
111
|
}
|
|
98
112
|
const codePoint = parseInt(hex, 16);
|
|
113
|
+
if (codePoint > 0x10ffff ||
|
|
114
|
+
(codePoint >= 0xd800 && codePoint <= 0xdfff)) {
|
|
115
|
+
throw new CYiniStringParseError('Unicode escape must represent a valid Unicode scalar value');
|
|
116
|
+
}
|
|
99
117
|
result += String.fromCodePoint(codePoint);
|
|
100
118
|
i += 8;
|
|
101
119
|
break;
|
|
@@ -110,16 +128,16 @@ const parseClassicEscapes = (input) => {
|
|
|
110
128
|
j++;
|
|
111
129
|
}
|
|
112
130
|
if (oct.length === 0) {
|
|
113
|
-
throw new CYiniStringParseError(
|
|
131
|
+
throw new CYiniStringParseError('Invalid \\o escape');
|
|
114
132
|
}
|
|
115
133
|
const value = parseInt(oct, 8);
|
|
116
134
|
if (value > 0o377) {
|
|
117
|
-
throw new CYiniStringParseError(
|
|
135
|
+
throw new CYiniStringParseError('Octal escape out of range');
|
|
118
136
|
}
|
|
119
137
|
result += String.fromCharCode(value);
|
|
120
138
|
i += oct.length;
|
|
121
139
|
if (j < input.length && /[0-9]/.test(input[j])) {
|
|
122
|
-
throw new CYiniStringParseError(
|
|
140
|
+
throw new CYiniStringParseError('Invalid \\o escape');
|
|
123
141
|
}
|
|
124
142
|
break;
|
|
125
143
|
}
|
|
@@ -133,12 +151,13 @@ const parseStringLiteral = ({ strKind, value }) => {
|
|
|
133
151
|
switch (strKind) {
|
|
134
152
|
case 'raw':
|
|
135
153
|
case 'triple-raw':
|
|
154
|
+
// Raw strings preserve content exactly as provided by the lexer.
|
|
155
|
+
// Single-line raw string constraints are enforced by the lexer.
|
|
136
156
|
return value;
|
|
137
157
|
case 'classic':
|
|
158
|
+
return parseClassicEscapes(value, false);
|
|
138
159
|
case 'triple-classic':
|
|
139
|
-
return parseClassicEscapes(value);
|
|
140
|
-
case 'hyper':
|
|
141
|
-
return normalizeHyperWhitespace(value);
|
|
160
|
+
return parseClassicEscapes(value, true);
|
|
142
161
|
default:
|
|
143
162
|
throw new CYiniStringParseError(`Unknown string kind: ${strKind}`);
|
|
144
163
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/parsers/validateShebangPlacement.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.stripBomAndValidShebang = exports.getShebangPlacementIssue = void 0;
|
|
5
|
+
const getShebangPlacementIssue = (input, strictMode) => {
|
|
6
|
+
const text = input.startsWith('\uFEFF') ? input.slice(1) : input;
|
|
7
|
+
const lines = text.split(/\r?\n/);
|
|
8
|
+
for (let index = 0; index < lines.length; index++) {
|
|
9
|
+
const line = lines[index];
|
|
10
|
+
const trimmedStart = line.trimStart();
|
|
11
|
+
if (!trimmedStart.startsWith('#!')) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const isFirstLine = index === 0;
|
|
15
|
+
const startsAtFirstColumn = line.startsWith('#!');
|
|
16
|
+
if (isFirstLine && startsAtFirstColumn) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const message = 'Misplaced shebang-like sequence. A shebang is only recognized when #! is the first two non-BOM characters of the document.';
|
|
20
|
+
const lineNumber = index + 1;
|
|
21
|
+
const columnNumber = line.length - trimmedStart.length + 1;
|
|
22
|
+
return {
|
|
23
|
+
locInput: {
|
|
24
|
+
line: lineNumber,
|
|
25
|
+
column: columnNumber,
|
|
26
|
+
endColumn: columnNumber,
|
|
27
|
+
},
|
|
28
|
+
type: strictMode ? 'Syntax-Error' : 'Syntax-Warning',
|
|
29
|
+
msgWhat: 'Misplaced shebang-like sequence.',
|
|
30
|
+
msgWhy: message,
|
|
31
|
+
msgHint: 'Move the shebang to the first line and first column, or remove the leading whitespace.',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
};
|
|
36
|
+
exports.getShebangPlacementIssue = getShebangPlacementIssue;
|
|
37
|
+
const stripBomAndValidShebang = (input) => {
|
|
38
|
+
let text = input;
|
|
39
|
+
if (text.startsWith('\uFEFF')) {
|
|
40
|
+
text = text.slice(1);
|
|
41
|
+
}
|
|
42
|
+
if (!text.startsWith('#!')) {
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
const newlineIndex = text.indexOf('\n');
|
|
46
|
+
if (newlineIndex < 0) {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
return text.slice(newlineIndex + 1);
|
|
50
|
+
};
|
|
51
|
+
exports.stripBomAndValidShebang = stripBomAndValidShebang;
|
|
52
|
+
//# sourceMappingURL=validateShebangPlacement.js.map
|
package/dist/types/index.d.ts
CHANGED
|
@@ -20,6 +20,19 @@ export interface YiniParseResult {
|
|
|
20
20
|
result: ParsedObject;
|
|
21
21
|
meta: ResultMetadata;
|
|
22
22
|
}
|
|
23
|
+
export type ToolingDiagnosticSeverity = 'error' | 'warning' | 'notice' | 'info';
|
|
24
|
+
export interface ToolingDiagnostic {
|
|
25
|
+
severity: ToolingDiagnosticSeverity;
|
|
26
|
+
code: string;
|
|
27
|
+
message: string;
|
|
28
|
+
line?: number;
|
|
29
|
+
column?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface YiniToolingParseResult {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
result: ParsedObject;
|
|
34
|
+
diagnostics: ToolingDiagnostic[];
|
|
35
|
+
}
|
|
23
36
|
export type FailLevelKey = 'ignore-errors' | 'errors' | 'warnings-and-errors';
|
|
24
37
|
export type PreferredFailLevel = 'auto' | FailLevelKey;
|
|
25
38
|
/** Version tag for the public metadata schema. */
|
|
@@ -73,8 +86,10 @@ export interface PrimaryUserParams extends BasicOptions {
|
|
|
73
86
|
* Allowed values: `'warn-and-keep-first'` | `'warn-and-overwrite'` | `'keep-first'` (silent, first wins) | `'overwrite'` (silent, last wins) | `'error'`.
|
|
74
87
|
* @param options.preserveUndefinedInMeta - Keep properties with value `undefined` inside
|
|
75
88
|
* the returned metadata. Requires: `includeMetadata = true`. Ignored otherwise.
|
|
76
|
-
* @param options.
|
|
77
|
-
*
|
|
89
|
+
* @param options.logDiagnostics - Opt in to writing diagnostics to stderr.
|
|
90
|
+
* Library calls do not write diagnostics by default.
|
|
91
|
+
* @param options.quiet - When `logDiagnostics = true`, print **errors only** to stderr;
|
|
92
|
+
* warnings and info are not printed. Structured diagnostics are unaffected.
|
|
78
93
|
* @param options.requireDocTerminator - Whether a document terminator is required.
|
|
79
94
|
* One of: 'optional' | 'warn-if-missing' | 'required'.
|
|
80
95
|
* @param options.silent - **No console output** at all (including errors).
|
|
@@ -86,7 +101,7 @@ export interface PrimaryUserParams extends BasicOptions {
|
|
|
86
101
|
* the effective mode becomes **custom** rather than purely strict/lenient.
|
|
87
102
|
* @param options.treatEmptyValueAsNull - How to treat an explicitly empty value on the
|
|
88
103
|
* right-hand side of '='. Allowed values: `'allow'` | `'allow-with-warning'` | `'disallow'`.
|
|
89
|
-
* @param options.throwOnError -
|
|
104
|
+
* @param options.throwOnError - Throw when a parse issue reaches the active bail threshold (for example, on errors if `failLevel = 'errors'`).
|
|
90
105
|
*/
|
|
91
106
|
export interface ParseOptions extends BasicOptions {
|
|
92
107
|
includeDiagnostics?: boolean;
|
|
@@ -95,6 +110,7 @@ export interface ParseOptions extends BasicOptions {
|
|
|
95
110
|
onDuplicateKey?: OnDuplicateKey;
|
|
96
111
|
requireDocTerminator?: DocumentTerminatorRule;
|
|
97
112
|
treatEmptyValueAsNull?: EmptyValueRule;
|
|
113
|
+
logDiagnostics?: boolean;
|
|
98
114
|
quiet?: boolean;
|
|
99
115
|
silent?: boolean;
|
|
100
116
|
throwOnError?: boolean;
|
|
@@ -103,6 +119,7 @@ export interface ParseOptions extends BasicOptions {
|
|
|
103
119
|
export interface AllUserOptions extends ParseOptions {
|
|
104
120
|
dummy: null;
|
|
105
121
|
}
|
|
122
|
+
export type ParseForToolingOptions = Omit<ParseOptions, 'failLevel' | 'includeMetadata' | 'includeDiagnostics' | 'logDiagnostics' | 'silent' | 'throwOnError'>;
|
|
106
123
|
export interface IssuePayload {
|
|
107
124
|
/** 1-based; use undefined when not applicable. */
|
|
108
125
|
line: number | undefined;
|
package/dist/utils/print.d.ts
CHANGED
|
@@ -11,3 +11,4 @@ export declare const printJSON: (obj: any, isForce?: boolean) => void;
|
|
|
11
11
|
* @param isColors If true, the output is styled with ANSI color codes.
|
|
12
12
|
*/
|
|
13
13
|
export declare const printObject: (obj: any, isForce?: boolean, isColors?: boolean) => void;
|
|
14
|
+
export declare const normalizeHyperWhitespace: (input: string) => string;
|
package/dist/utils/print.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.printObject = exports.printJSON = exports.toPrettyJSON = exports.devPrint = exports.debugPrint = void 0;
|
|
6
|
+
exports.normalizeHyperWhitespace = exports.printObject = exports.printJSON = exports.toPrettyJSON = exports.devPrint = exports.debugPrint = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* This file contains general system helper functions (utils).
|
|
9
9
|
* @note More specific YINI helper functions should go into yiniHelpers.ts-file.
|
|
@@ -48,4 +48,8 @@ const printObject = (obj, isForce = false, isColors = true) => {
|
|
|
48
48
|
console.log(util_1.default.inspect(obj, { depth: null, colors: isColors }));
|
|
49
49
|
};
|
|
50
50
|
exports.printObject = printObject;
|
|
51
|
+
const normalizeHyperWhitespace = (input) => {
|
|
52
|
+
return input.replace(/[\s\r\n]+/g, ' ').trim();
|
|
53
|
+
};
|
|
54
|
+
exports.normalizeHyperWhitespace = normalizeHyperWhitespace;
|
|
51
55
|
//# sourceMappingURL=print.js.map
|
package/dist/utils/string.d.ts
CHANGED
|
@@ -68,3 +68,4 @@ export declare const toLowerSnakeCase: (txt: string) => string;
|
|
|
68
68
|
export declare const toLowerKebabCase: (txt: string) => string;
|
|
69
69
|
export declare const removeSuffix: (str: string, suffix: string) => string;
|
|
70
70
|
export declare const ensurePeriod: (text: string) => string;
|
|
71
|
+
export declare const trimQuotes: (text: string) => string;
|
package/dist/utils/string.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @note More specific YINI helper functions should go into yiniHelpers.ts-file.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ensurePeriod = exports.removeSuffix = exports.toLowerKebabCase = exports.toLowerSnakeCase = exports.stripNLAndAfter = exports.isDigit = exports.isAlpha = exports.isEnclosedInBackticks = exports.trimBackticks = exports.trimTrailingNonLetters = exports.splitLines = exports.computeSha256 = exports.capitalizeFirst = exports.toColRow = void 0;
|
|
7
|
+
exports.trimQuotes = exports.ensurePeriod = exports.removeSuffix = exports.toLowerKebabCase = exports.toLowerSnakeCase = exports.stripNLAndAfter = exports.isDigit = exports.isAlpha = exports.isEnclosedInBackticks = exports.trimBackticks = exports.trimTrailingNonLetters = exports.splitLines = exports.computeSha256 = exports.capitalizeFirst = exports.toColRow = void 0;
|
|
8
8
|
const crypto_1 = require("crypto");
|
|
9
9
|
const print_1 = require("./print");
|
|
10
10
|
const toColRow = (size, label, value) => {
|
|
@@ -157,4 +157,20 @@ const ensurePeriod = (text) => {
|
|
|
157
157
|
return text.endsWith('.') ? text : text + '.';
|
|
158
158
|
};
|
|
159
159
|
exports.ensurePeriod = ensurePeriod;
|
|
160
|
+
const trimQuotes = (text) => {
|
|
161
|
+
// STRING token already excludes quotes; the rule returns the literal with quotes present.
|
|
162
|
+
// We’ll reliably strip the outer quote(s) and leave contents as-is (concat pieces handled below).
|
|
163
|
+
const q = text[0];
|
|
164
|
+
if ((q === '"' || q === "'") &&
|
|
165
|
+
text.length >= 2 &&
|
|
166
|
+
text[text.length - 1] === q) {
|
|
167
|
+
return text.slice(1, -1);
|
|
168
|
+
}
|
|
169
|
+
// Triple-quoted cases are handled by the lexer too; same stripping works since token text begins with quotes.
|
|
170
|
+
if (text.startsWith('"""') && text.endsWith('"""') && text.length >= 6) {
|
|
171
|
+
return text.slice(3, -3);
|
|
172
|
+
}
|
|
173
|
+
return text;
|
|
174
|
+
};
|
|
175
|
+
exports.trimQuotes = trimQuotes;
|
|
160
176
|
//# sourceMappingURL=string.js.map
|
package/dist/utils/system.d.ts
CHANGED
package/dist/utils/system.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isError = void 0;
|
|
3
|
+
exports.forceExit = exports.isError = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Error instance helper.
|
|
6
6
|
* @example
|
|
@@ -19,4 +19,9 @@ const isError = (e) => {
|
|
|
19
19
|
return e instanceof Error;
|
|
20
20
|
};
|
|
21
21
|
exports.isError = isError;
|
|
22
|
+
const forceExit = () => {
|
|
23
|
+
console.log('\n*** Forced program termination via forceExit() ***');
|
|
24
|
+
process.exit();
|
|
25
|
+
};
|
|
26
|
+
exports.forceExit = forceExit;
|
|
22
27
|
//# sourceMappingURL=system.js.map
|
|
@@ -3,18 +3,60 @@
|
|
|
3
3
|
* @note More general helper functions should go into the dir "src/utils/".
|
|
4
4
|
*/
|
|
5
5
|
import { TScalarValue, TValueLiteral } from '../core/internalTypes';
|
|
6
|
+
export declare const SECTION_MARKER_CARET = "^";
|
|
7
|
+
export declare const SECTION_MARKER_SECTION_SIGN = "\u00A7";
|
|
8
|
+
export declare const SECTION_MARKER_GT = ">";
|
|
9
|
+
export declare const SECTION_MARKER_LT = "<";
|
|
10
|
+
export declare const SECTION_MARKERS: Set<string>;
|
|
11
|
+
export declare const MAX_REPEATED_SECTION_MARKER_DEPTH = 9;
|
|
12
|
+
export declare const MAX_SECTION_DEPTH = 255;
|
|
6
13
|
/**
|
|
7
14
|
* Check if the character is a section marker character.
|
|
8
|
-
* @param character A character
|
|
9
|
-
* @note The string must be of length 1.
|
|
15
|
+
* @param character A single character.
|
|
10
16
|
* @throws Will throw if not exactly of length 1.
|
|
11
17
|
*/
|
|
12
18
|
export declare const isMarkerCharacter: (character: string) => boolean;
|
|
19
|
+
export declare const countRepeatedSectionMarkers: (markerText: string) => number;
|
|
20
|
+
/**
|
|
21
|
+
* Strips/removes separator characters from a repeated section marker sequence.
|
|
22
|
+
*
|
|
23
|
+
* Examples:
|
|
24
|
+
* - ^^^_^^^_^ -> ^^^^^^^
|
|
25
|
+
* - ^^^_^^^_^^ -> ^^^^^^^^
|
|
26
|
+
*
|
|
27
|
+
* Invalid:
|
|
28
|
+
* - _^^
|
|
29
|
+
* - ^^_
|
|
30
|
+
* - ^^__^^
|
|
31
|
+
*/
|
|
32
|
+
export declare const normalizeRepeatedSectionMarkerSequence: (marker: string) => string;
|
|
33
|
+
export declare const hasMixedSectionMarkers: (markerText: string) => boolean;
|
|
34
|
+
export declare const hasInvalidSectionMarkerSeparatorPlacement: (markerText: string) => boolean;
|
|
13
35
|
/**
|
|
14
36
|
* @returns Returns the beginning up to (but not including) any comments
|
|
15
37
|
* starting with //, #, ; or --.
|
|
16
38
|
* @throws Will throw if consisting more than 1 lines.
|
|
17
39
|
*/
|
|
40
|
+
/**
|
|
41
|
+
* Returns the significant beginning of a single logical line.
|
|
42
|
+
*
|
|
43
|
+
* Recognized inline comments:
|
|
44
|
+
* - `//`
|
|
45
|
+
* - `#`
|
|
46
|
+
*
|
|
47
|
+
* Recognized full-line trivia:
|
|
48
|
+
* - `;`
|
|
49
|
+
* - `--`
|
|
50
|
+
*
|
|
51
|
+
* Notes:
|
|
52
|
+
* - `;` is not an inline comment marker in YINI.
|
|
53
|
+
* - `--` is not an inline comment marker; it only disables a line when it is
|
|
54
|
+
* the first non-whitespace content.
|
|
55
|
+
*
|
|
56
|
+
* This helper is intentionally simple and should only be used after the lexer
|
|
57
|
+
* has already separated strings/comments, or in places where the input is known
|
|
58
|
+
* not to contain string literals with comment-like text.
|
|
59
|
+
*/
|
|
18
60
|
export declare const stripCommentsAndAfter: (line: string) => string;
|
|
19
61
|
/**
|
|
20
62
|
* Checks if a string conforms to the identifier rules for section headers and
|