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.
Files changed (90) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +187 -35
  3. package/dist/YINI.d.ts +29 -18
  4. package/dist/YINI.js +104 -3
  5. package/dist/core/astBuilder.d.ts +94 -18
  6. package/dist/core/astBuilder.js +439 -376
  7. package/dist/core/errorDataHandler.d.ts +6 -1
  8. package/dist/core/errorDataHandler.js +30 -43
  9. package/dist/core/internalTypes.d.ts +10 -1
  10. package/dist/core/objectBuilder.d.ts +8 -4
  11. package/dist/core/objectBuilder.js +47 -62
  12. package/dist/core/options/defaultParserOptions.d.ts +3 -2
  13. package/dist/core/options/defaultParserOptions.js +11 -2
  14. package/dist/core/options/optionsFunctions.js +6 -4
  15. package/dist/core/parsingRules/modeFromRulesMatcher.d.ts +1 -1
  16. package/dist/core/parsingRules/modeFromRulesMatcher.js +22 -13
  17. package/dist/core/pipeline/pipeline.js +35 -10
  18. package/dist/core/runtime.js +28 -19
  19. package/dist/grammar/generated/YiniLexer.d.ts +40 -53
  20. package/dist/grammar/generated/YiniLexer.js +357 -356
  21. package/dist/grammar/generated/YiniParser.d.ts +174 -118
  22. package/dist/grammar/generated/YiniParser.js +1185 -929
  23. package/dist/grammar/generated/YiniParserVisitor.d.ts +82 -19
  24. package/dist/grammar/generated/YiniParserVisitor.js +1 -1
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.js +4 -3
  27. package/dist/parsers/extractHeaderParts.d.ts +12 -19
  28. package/dist/parsers/extractHeaderParts.js +57 -46
  29. package/dist/parsers/parseNumber.d.ts +24 -6
  30. package/dist/parsers/parseNumber.js +114 -49
  31. package/dist/parsers/parseSectionHeader.d.ts +11 -3
  32. package/dist/parsers/parseSectionHeader.js +55 -43
  33. package/dist/parsers/parseString.js +39 -20
  34. package/dist/parsers/validateShebangPlacement.d.ts +3 -0
  35. package/dist/parsers/validateShebangPlacement.js +52 -0
  36. package/dist/types/index.d.ts +20 -3
  37. package/dist/utils/print.d.ts +1 -0
  38. package/dist/utils/print.js +5 -1
  39. package/dist/utils/string.d.ts +1 -0
  40. package/dist/utils/string.js +17 -1
  41. package/dist/utils/system.d.ts +1 -0
  42. package/dist/utils/system.js +6 -1
  43. package/dist/utils/yiniHelpers.d.ts +44 -2
  44. package/dist/utils/yiniHelpers.js +134 -46
  45. package/examples/compare-formats.md +1 -1
  46. package/examples/nested.yini +1 -1
  47. package/package.json +11 -3
  48. package/dist/YINI.js.map +0 -1
  49. package/dist/config/env.js.map +0 -1
  50. package/dist/core/astBuilder.js.map +0 -1
  51. package/dist/core/errorDataHandler.js.map +0 -1
  52. package/dist/core/internalTypes.js.map +0 -1
  53. package/dist/core/objectBuilder.js.map +0 -1
  54. package/dist/core/options/defaultParserOptions.js.map +0 -1
  55. package/dist/core/options/failLevel.js.map +0 -1
  56. package/dist/core/options/optionsFunctions.js.map +0 -1
  57. package/dist/core/parsingRules/modeFromRulesMatcher.js.map +0 -1
  58. package/dist/core/parsingRules/rulesConstAndGuards.js.map +0 -1
  59. package/dist/core/pipeline/errorListeners.js.map +0 -1
  60. package/dist/core/pipeline/pipeline.js.map +0 -1
  61. package/dist/core/resultMetadataBuilder.js.map +0 -1
  62. package/dist/core/runtime.js.map +0 -1
  63. package/dist/dev/main.d.ts +0 -1
  64. package/dist/dev/main.js +0 -168
  65. package/dist/dev/main.js.map +0 -1
  66. package/dist/dev/quick-test-samples/defect-inputs.d.ts +0 -37
  67. package/dist/dev/quick-test-samples/defect-inputs.js +0 -106
  68. package/dist/dev/quick-test-samples/defect-inputs.js.map +0 -1
  69. package/dist/dev/quick-test-samples/valid-inputs.d.ts +0 -21
  70. package/dist/dev/quick-test-samples/valid-inputs.js +0 -422
  71. package/dist/dev/quick-test-samples/valid-inputs.js.map +0 -1
  72. package/dist/grammar/generated/YiniLexer.js.map +0 -1
  73. package/dist/grammar/generated/YiniParser.js.map +0 -1
  74. package/dist/grammar/generated/YiniParserVisitor.js.map +0 -1
  75. package/dist/index.js.map +0 -1
  76. package/dist/parsers/extractHeaderParts.js.map +0 -1
  77. package/dist/parsers/extractSignificantYiniLine.js.map +0 -1
  78. package/dist/parsers/parseBoolean.js.map +0 -1
  79. package/dist/parsers/parseNull.js.map +0 -1
  80. package/dist/parsers/parseNumber.js.map +0 -1
  81. package/dist/parsers/parseSectionHeader.js.map +0 -1
  82. package/dist/parsers/parseString.js.map +0 -1
  83. package/dist/types/index.js.map +0 -1
  84. package/dist/utils/number.js.map +0 -1
  85. package/dist/utils/object.js.map +0 -1
  86. package/dist/utils/pathAndFileName.js.map +0 -1
  87. package/dist/utils/print.js.map +0 -1
  88. package/dist/utils/string.js.map +0 -1
  89. package/dist/utils/system.js.map +0 -1
  90. 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
- * Extract ...
6
- * @param rawLine Raw line with the section header.
7
- * @note Implemented without regexp to keep it less cryptic, etc.
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
- * Extract ...
14
- * @param rawLine Raw line with the section header.
15
- * @note Implemented without regexp to keep it less cryptic, etc.
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, header text: ' +
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
- // --- Determing level and headerMarkerType ------------------------
43
+ // ---------------------------------------------------------------------
44
+ // Determine section marker type and depth.
45
+ // ---------------------------------------------------------------------
36
46
  if (strNumberPart === '') {
37
47
  headerMarkerType = 'Classic-Header-Marker';
38
- level = strMarkerChars.length;
39
- if (strNumberPart !== '') {
40
- errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid extra input in section header of a repeating marker characters: ' +
41
- strNumberPart, 'Classic section header markers may not include any numbers after, correct header markers looks like ^ Title, ^^ Title, ^^^ Title, etc.');
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
- level = Number.parseInt(strNumberPart);
55
+ strMarkerChars =
56
+ (0, yiniHelpers_1.normalizeRepeatedSectionMarkerSequence)(strMarkerChars);
48
57
  }
49
58
  catch (err) {
50
- errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'No number in this shorthand section header marker found', 'This shorthand section header marker could not be parse correctly, section header text: ' +
51
- line +
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
- if (level < 1) {
66
- errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid number in numeric shorthand section marker: ' +
67
- strMarkerChars, 'The number in a numeric shorthand section marker must be 1 or higher, e.g. ^1, ^2, ^3, etc.');
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
- // --- Check naming contraints based on isBacktickedName ----------
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', 'Invalid section header name "' + strSectionName + '"', 'Section name should be backticked like e.g. `My section name`.');
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 contraints: Is not a BacktickedName');
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', 'Invalid section name in repeating marker characters header, section name: "' +
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', 'Invalid section header name "' + strSectionName + '"', 'Section name must start with: A-Z, a-z, or _, unless enclosed in backticks e.g. `' +
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
- function normalizeHyperWhitespace(input) {
14
- return input.replace(/[\s\r\n]+/g, ' ').trim();
15
- }
16
- const parseClassicEscapes = (input) => {
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 && ch !== '\t') {
24
- throw new CYiniStringParseError(`Unescaped control character U+${code
25
- .toString(16)
26
- .padStart(4, '0')}`);
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(`Invalid \\x escape`);
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(`Invalid \\u escape`);
97
+ throw new CYiniStringParseError('Invalid \\u escape');
88
98
  }
89
- result += String.fromCharCode(parseInt(hex, 16));
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(`Invalid \\U escape`);
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(`Invalid \\o escape`);
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(`Octal escape out of range`);
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(`Invalid \\o escape`);
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,3 @@
1
+ import { IPreflightIssue } from '../core/internalTypes';
2
+ export declare const getShebangPlacementIssue: (input: string, strictMode: boolean) => IPreflightIssue | undefined;
3
+ export declare const stripBomAndValidShebang: (input: string) => string;
@@ -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
@@ -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.quiet - Print **errors only** to the console; warnings and info are not printed.
77
- * Diagnostics in the returned metadata are unaffected. Silent overrides quiet if both are enabled.
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 - Will throw on first parse error encountered.
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;
@@ -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;
@@ -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
@@ -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;
@@ -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
@@ -13,3 +13,4 @@
13
13
  * }
14
14
  */
15
15
  export declare const isError: (e: unknown) => e is Error;
16
+ export declare const forceExit: () => void;
@@ -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 in a string.
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