yini-parser 1.5.0 → 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 (87) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +183 -37
  3. package/dist/YINI.d.ts +22 -7
  4. package/dist/YINI.js +101 -0
  5. package/dist/core/astBuilder.d.ts +94 -15
  6. package/dist/core/astBuilder.js +394 -362
  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.js +21 -6
  11. package/dist/core/options/defaultParserOptions.d.ts +3 -2
  12. package/dist/core/options/defaultParserOptions.js +2 -1
  13. package/dist/core/options/optionsFunctions.js +5 -1
  14. package/dist/core/pipeline/pipeline.js +31 -10
  15. package/dist/core/runtime.js +28 -19
  16. package/dist/grammar/generated/YiniLexer.d.ts +28 -35
  17. package/dist/grammar/generated/YiniLexer.js +323 -310
  18. package/dist/grammar/generated/YiniParser.d.ts +158 -80
  19. package/dist/grammar/generated/YiniParser.js +1141 -620
  20. package/dist/grammar/generated/YiniParserVisitor.d.ts +77 -14
  21. package/dist/grammar/generated/YiniParserVisitor.js +1 -1
  22. package/dist/index.d.ts +2 -1
  23. package/dist/index.js +4 -3
  24. package/dist/parsers/extractHeaderParts.d.ts +12 -19
  25. package/dist/parsers/extractHeaderParts.js +57 -46
  26. package/dist/parsers/parseNumber.d.ts +24 -6
  27. package/dist/parsers/parseNumber.js +114 -49
  28. package/dist/parsers/parseSectionHeader.d.ts +11 -3
  29. package/dist/parsers/parseSectionHeader.js +55 -43
  30. package/dist/parsers/parseString.js +39 -20
  31. package/dist/parsers/validateShebangPlacement.d.ts +3 -0
  32. package/dist/parsers/validateShebangPlacement.js +52 -0
  33. package/dist/types/index.d.ts +19 -2
  34. package/dist/utils/print.d.ts +1 -0
  35. package/dist/utils/print.js +5 -1
  36. package/dist/utils/string.d.ts +1 -0
  37. package/dist/utils/string.js +17 -1
  38. package/dist/utils/system.d.ts +1 -0
  39. package/dist/utils/system.js +6 -1
  40. package/dist/utils/yiniHelpers.d.ts +44 -2
  41. package/dist/utils/yiniHelpers.js +134 -46
  42. package/examples/compare-formats.md +1 -1
  43. package/examples/nested.yini +1 -1
  44. package/package.json +11 -3
  45. package/dist/YINI.js.map +0 -1
  46. package/dist/config/env.js.map +0 -1
  47. package/dist/core/astBuilder.js.map +0 -1
  48. package/dist/core/errorDataHandler.js.map +0 -1
  49. package/dist/core/internalTypes.js.map +0 -1
  50. package/dist/core/objectBuilder.js.map +0 -1
  51. package/dist/core/options/defaultParserOptions.js.map +0 -1
  52. package/dist/core/options/failLevel.js.map +0 -1
  53. package/dist/core/options/optionsFunctions.js.map +0 -1
  54. package/dist/core/parsingRules/modeFromRulesMatcher.js.map +0 -1
  55. package/dist/core/parsingRules/rulesConstAndGuards.js.map +0 -1
  56. package/dist/core/pipeline/errorListeners.js.map +0 -1
  57. package/dist/core/pipeline/pipeline.js.map +0 -1
  58. package/dist/core/resultMetadataBuilder.js.map +0 -1
  59. package/dist/core/runtime.js.map +0 -1
  60. package/dist/dev/main.d.ts +0 -1
  61. package/dist/dev/main.js +0 -139
  62. package/dist/dev/main.js.map +0 -1
  63. package/dist/dev/quick-test-samples/defect-inputs.d.ts +0 -37
  64. package/dist/dev/quick-test-samples/defect-inputs.js +0 -106
  65. package/dist/dev/quick-test-samples/defect-inputs.js.map +0 -1
  66. package/dist/dev/quick-test-samples/valid-inputs.d.ts +0 -21
  67. package/dist/dev/quick-test-samples/valid-inputs.js +0 -422
  68. package/dist/dev/quick-test-samples/valid-inputs.js.map +0 -1
  69. package/dist/grammar/generated/YiniLexer.js.map +0 -1
  70. package/dist/grammar/generated/YiniParser.js.map +0 -1
  71. package/dist/grammar/generated/YiniParserVisitor.js.map +0 -1
  72. package/dist/index.js.map +0 -1
  73. package/dist/parsers/extractHeaderParts.js.map +0 -1
  74. package/dist/parsers/extractSignificantYiniLine.js.map +0 -1
  75. package/dist/parsers/parseBoolean.js.map +0 -1
  76. package/dist/parsers/parseNull.js.map +0 -1
  77. package/dist/parsers/parseNumber.js.map +0 -1
  78. package/dist/parsers/parseSectionHeader.js.map +0 -1
  79. package/dist/parsers/parseString.js.map +0 -1
  80. package/dist/types/index.js.map +0 -1
  81. package/dist/utils/number.js.map +0 -1
  82. package/dist/utils/object.js.map +0 -1
  83. package/dist/utils/pathAndFileName.js.map +0 -1
  84. package/dist/utils/print.js.map +0 -1
  85. package/dist/utils/string.js.map +0 -1
  86. package/dist/utils/system.js.map +0 -1
  87. package/dist/utils/yiniHelpers.js.map +0 -1
@@ -2,24 +2,33 @@ import { ParseTreeVisitor } from 'antlr4';
2
2
  import { YiniContext } from "./YiniParser.js";
3
3
  import { PrologContext } from "./YiniParser.js";
4
4
  import { Terminal_stmtContext } from "./YiniParser.js";
5
+ import { Terminal_triviaContext } from "./YiniParser.js";
5
6
  import { StmtContext } from "./YiniParser.js";
7
+ import { Full_line_comment_stmtContext } from "./YiniParser.js";
8
+ import { Disabled_line_stmtContext } from "./YiniParser.js";
6
9
  import { Invalid_section_stmtContext } from "./YiniParser.js";
7
10
  import { Meta_stmtContext } from "./YiniParser.js";
8
11
  import { DirectiveContext } from "./YiniParser.js";
12
+ import { Yini_directiveContext } from "./YiniParser.js";
13
+ import { Yini_mode_declarationContext } from "./YiniParser.js";
9
14
  import { AnnotationContext } from "./YiniParser.js";
10
15
  import { EolContext } from "./YiniParser.js";
11
16
  import { AssignmentContext } from "./YiniParser.js";
12
17
  import { MemberContext } from "./YiniParser.js";
13
18
  import { ValueContext } from "./YiniParser.js";
19
+ import { Scalar_valueContext } from "./YiniParser.js";
20
+ import { Concat_expressionContext } from "./YiniParser.js";
21
+ import { Concat_tailContext } from "./YiniParser.js";
22
+ import { Concat_operandContext } from "./YiniParser.js";
23
+ import { String_literalContext } from "./YiniParser.js";
14
24
  import { Object_literalContext } from "./YiniParser.js";
15
25
  import { Object_membersContext } from "./YiniParser.js";
16
26
  import { Object_memberContext } from "./YiniParser.js";
27
+ import { Object_member_separatorContext } from "./YiniParser.js";
17
28
  import { List_literalContext } from "./YiniParser.js";
18
29
  import { ElementsContext } from "./YiniParser.js";
19
30
  import { Number_literalContext } from "./YiniParser.js";
20
31
  import { Null_literalContext } from "./YiniParser.js";
21
- import { String_literalContext } from "./YiniParser.js";
22
- import { String_concatContext } from "./YiniParser.js";
23
32
  import { Boolean_literalContext } from "./YiniParser.js";
24
33
  import { Bad_meta_textContext } from "./YiniParser.js";
25
34
  import { Bad_memberContext } from "./YiniParser.js";
@@ -49,12 +58,30 @@ export default class YiniParserVisitor<Result> extends ParseTreeVisitor<Result>
49
58
  * @return the visitor result
50
59
  */
51
60
  visitTerminal_stmt?: (ctx: Terminal_stmtContext) => Result;
61
+ /**
62
+ * Visit a parse tree produced by `YiniParser.terminal_trivia`.
63
+ * @param ctx the parse tree
64
+ * @return the visitor result
65
+ */
66
+ visitTerminal_trivia?: (ctx: Terminal_triviaContext) => Result;
52
67
  /**
53
68
  * Visit a parse tree produced by `YiniParser.stmt`.
54
69
  * @param ctx the parse tree
55
70
  * @return the visitor result
56
71
  */
57
72
  visitStmt?: (ctx: StmtContext) => Result;
73
+ /**
74
+ * Visit a parse tree produced by `YiniParser.full_line_comment_stmt`.
75
+ * @param ctx the parse tree
76
+ * @return the visitor result
77
+ */
78
+ visitFull_line_comment_stmt?: (ctx: Full_line_comment_stmtContext) => Result;
79
+ /**
80
+ * Visit a parse tree produced by `YiniParser.disabled_line_stmt`.
81
+ * @param ctx the parse tree
82
+ * @return the visitor result
83
+ */
84
+ visitDisabled_line_stmt?: (ctx: Disabled_line_stmtContext) => Result;
58
85
  /**
59
86
  * Visit a parse tree produced by `YiniParser.invalid_section_stmt`.
60
87
  * @param ctx the parse tree
@@ -73,6 +100,18 @@ export default class YiniParserVisitor<Result> extends ParseTreeVisitor<Result>
73
100
  * @return the visitor result
74
101
  */
75
102
  visitDirective?: (ctx: DirectiveContext) => Result;
103
+ /**
104
+ * Visit a parse tree produced by `YiniParser.yini_directive`.
105
+ * @param ctx the parse tree
106
+ * @return the visitor result
107
+ */
108
+ visitYini_directive?: (ctx: Yini_directiveContext) => Result;
109
+ /**
110
+ * Visit a parse tree produced by `YiniParser.yini_mode_declaration`.
111
+ * @param ctx the parse tree
112
+ * @return the visitor result
113
+ */
114
+ visitYini_mode_declaration?: (ctx: Yini_mode_declarationContext) => Result;
76
115
  /**
77
116
  * Visit a parse tree produced by `YiniParser.annotation`.
78
117
  * @param ctx the parse tree
@@ -103,6 +142,36 @@ export default class YiniParserVisitor<Result> extends ParseTreeVisitor<Result>
103
142
  * @return the visitor result
104
143
  */
105
144
  visitValue?: (ctx: ValueContext) => Result;
145
+ /**
146
+ * Visit a parse tree produced by `YiniParser.scalar_value`.
147
+ * @param ctx the parse tree
148
+ * @return the visitor result
149
+ */
150
+ visitScalar_value?: (ctx: Scalar_valueContext) => Result;
151
+ /**
152
+ * Visit a parse tree produced by `YiniParser.concat_expression`.
153
+ * @param ctx the parse tree
154
+ * @return the visitor result
155
+ */
156
+ visitConcat_expression?: (ctx: Concat_expressionContext) => Result;
157
+ /**
158
+ * Visit a parse tree produced by `YiniParser.concat_tail`.
159
+ * @param ctx the parse tree
160
+ * @return the visitor result
161
+ */
162
+ visitConcat_tail?: (ctx: Concat_tailContext) => Result;
163
+ /**
164
+ * Visit a parse tree produced by `YiniParser.concat_operand`.
165
+ * @param ctx the parse tree
166
+ * @return the visitor result
167
+ */
168
+ visitConcat_operand?: (ctx: Concat_operandContext) => Result;
169
+ /**
170
+ * Visit a parse tree produced by `YiniParser.string_literal`.
171
+ * @param ctx the parse tree
172
+ * @return the visitor result
173
+ */
174
+ visitString_literal?: (ctx: String_literalContext) => Result;
106
175
  /**
107
176
  * Visit a parse tree produced by `YiniParser.object_literal`.
108
177
  * @param ctx the parse tree
@@ -121,6 +190,12 @@ export default class YiniParserVisitor<Result> extends ParseTreeVisitor<Result>
121
190
  * @return the visitor result
122
191
  */
123
192
  visitObject_member?: (ctx: Object_memberContext) => Result;
193
+ /**
194
+ * Visit a parse tree produced by `YiniParser.object_member_separator`.
195
+ * @param ctx the parse tree
196
+ * @return the visitor result
197
+ */
198
+ visitObject_member_separator?: (ctx: Object_member_separatorContext) => Result;
124
199
  /**
125
200
  * Visit a parse tree produced by `YiniParser.list_literal`.
126
201
  * @param ctx the parse tree
@@ -145,18 +220,6 @@ export default class YiniParserVisitor<Result> extends ParseTreeVisitor<Result>
145
220
  * @return the visitor result
146
221
  */
147
222
  visitNull_literal?: (ctx: Null_literalContext) => Result;
148
- /**
149
- * Visit a parse tree produced by `YiniParser.string_literal`.
150
- * @param ctx the parse tree
151
- * @return the visitor result
152
- */
153
- visitString_literal?: (ctx: String_literalContext) => Result;
154
- /**
155
- * Visit a parse tree produced by `YiniParser.string_concat`.
156
- * @param ctx the parse tree
157
- * @return the visitor result
158
- */
159
- visitString_concat?: (ctx: String_concatContext) => Result;
160
223
  /**
161
224
  * Visit a parse tree produced by `YiniParser.boolean_literal`.
162
225
  * @param ctx the parse tree
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // Generated from ./grammar/v1.0.0-rc.5/YiniParser.g4 by ANTLR 4.13.2
2
+ // Generated from ./grammar/v1.0.0-rc.6/YiniParser.g4 by ANTLR 4.13.2
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const antlr4_1 = require("antlr4");
5
5
  /**
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ import YINI from './YINI';
25
25
  export default YINI;
26
26
  export declare const parse: typeof YINI.parse;
27
27
  export declare const parseFile: typeof YINI.parseFile;
28
+ export declare const parseForTooling: typeof YINI.parseForTooling;
28
29
  export declare const getTabSize: typeof YINI.getTabSize;
29
30
  export declare const setTabSize: typeof YINI.setTabSize;
30
31
  /**
@@ -42,4 +43,4 @@ export declare const setTabSize: typeof YINI.setTabSize;
42
43
  *
43
44
  * @packageDocumentation
44
45
  */
45
- export type { ParsedObject, AllUserOptions, YiniParseResult, OnDuplicateKey, DuplicateKeyPolicy, DocumentTerminatorRule, EmptyValueRule, FailLevelKey, PreferredFailLevel, BasicOptions, PrimaryUserParams, ParseOptions, IssuePayload, MetaSchemaVersion, OrderGuarantee, ResultMetadata, } from './types';
46
+ export type { ParsedObject, AllUserOptions, YiniParseResult, YiniToolingParseResult, ToolingDiagnostic, ToolingDiagnosticSeverity, OnDuplicateKey, DuplicateKeyPolicy, DocumentTerminatorRule, EmptyValueRule, FailLevelKey, PreferredFailLevel, BasicOptions, PrimaryUserParams, ParseOptions, ParseForToolingOptions, IssuePayload, MetaSchemaVersion, OrderGuarantee, ResultMetadata, } from './types';
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.setTabSize = exports.getTabSize = exports.parseFile = exports.parse = void 0;
7
+ exports.setTabSize = exports.getTabSize = exports.parseForTooling = exports.parseFile = exports.parse = void 0;
8
8
  /*
9
9
  This file is a pure barrel file.
10
10
 
@@ -13,9 +13,9 @@ exports.setTabSize = exports.getTabSize = exports.parseFile = exports.parse = vo
13
13
  Run the code with the following command:
14
14
  npm start
15
15
  or
16
- npm run start:dev
16
+ npm run start:main
17
17
  or
18
- npm run start:dev:debug
18
+ npm run start:main:dev:debug
19
19
 
20
20
  /END
21
21
  */
@@ -47,6 +47,7 @@ const YINI_1 = __importDefault(require("./YINI"));
47
47
  exports.default = YINI_1.default; // Public package API.
48
48
  exports.parse = YINI_1.default.parse;
49
49
  exports.parseFile = YINI_1.default.parseFile;
50
+ exports.parseForTooling = YINI_1.default.parseForTooling;
50
51
  exports.getTabSize = YINI_1.default.getTabSize;
51
52
  exports.setTabSize = YINI_1.default.setTabSize;
52
53
  //# sourceMappingURL=index.js.map
@@ -1,27 +1,20 @@
1
1
  import { ErrorDataHandler } from '../core/errorDataHandler';
2
2
  import { StmtContext } from '../grammar/generated/YiniParser';
3
3
  /**
4
- * Check and identify the section header parts via tokenizing the parts and return them as strings.
5
- * @param rawHeaderLine Raw line with the section header where the header
6
- * marker will be identified. E.g. does the header start with '^^^' or '^3'
7
- * and then some identifier.
4
+ * Extracts the marker sequence, optional numeric shorthand depth,
5
+ * section name, and backticked-name flag from a YINI section header line.
8
6
  *
9
- * Below, copied from YINI Specification v1.0.0 Beta 7.
10
- * Form 1: Identifier of Simple Form:
11
- * - Keys must be non-empty.
12
- * - Keys are case-sensitive (`Title` and `title` are different).
13
- * - Can only contain letters (a-z or A-Z), digits (0-9) and underscores `_`.
14
- * - Must begin with a letter or an underscore `_`.
15
- * - Note: Cannot contain hyphens (`-`) or periods (`.`).
7
+ * Examples:
8
+ * - ^ Section
9
+ * - ^^ Section
10
+ * - ^^^_^^^_^ Section
11
+ * - ^10 Section
12
+ * - §2 Section
13
+ * - < `Backticked Section`
16
14
  *
17
- * Form 2: Backticked Identifier:
18
- * - A phrase is a name wrapped in backticks ``` ` ```.
19
- * - Backticked identifiers must be on a single line and must not contain tabs or new lines unless using escaping codes, except for ordinary spaces.
20
- * - Special control characters (U+0000–U+001F) must be escaped.
21
- *
22
- * @note Implemented without regexp to keep it less cryptic, etc.
23
- * @note Returns the parts as strings; each part needs to be analyzed separately against the contraints in the specifications.
24
- * @returns An object with the identified header parts: marker characters, parsed name, and parsed level string.
15
+ * @note This function only splits the header into parts.
16
+ * Validation of marker depth, marker separator placement, mixed markers,
17
+ * and section name rules is handled by parseSectionHeader.ts.
25
18
  */
26
19
  declare const extractHeaderParts: (rawLine: string, errorHandler?: ErrorDataHandler | null, ctx?: StmtContext | null) => {
27
20
  strMarkerChars: string;
@@ -1,31 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ // src/parsers/extractHeaderParts.ts
3
4
  const errorDataHandler_1 = require("../core/errorDataHandler");
4
5
  const print_1 = require("../utils/print");
5
6
  const yiniHelpers_1 = require("../utils/yiniHelpers");
6
7
  const extractSignificantYiniLine_1 = require("./extractSignificantYiniLine");
7
8
  /**
8
- * Check and identify the section header parts via tokenizing the parts and return them as strings.
9
- * @param rawHeaderLine Raw line with the section header where the header
10
- * marker will be identified. E.g. does the header start with '^^^' or '^3'
11
- * and then some identifier.
9
+ * Extracts the marker sequence, optional numeric shorthand depth,
10
+ * section name, and backticked-name flag from a YINI section header line.
12
11
  *
13
- * Below, copied from YINI Specification v1.0.0 Beta 7.
14
- * Form 1: Identifier of Simple Form:
15
- * - Keys must be non-empty.
16
- * - Keys are case-sensitive (`Title` and `title` are different).
17
- * - Can only contain letters (a-z or A-Z), digits (0-9) and underscores `_`.
18
- * - Must begin with a letter or an underscore `_`.
19
- * - Note: Cannot contain hyphens (`-`) or periods (`.`).
12
+ * Examples:
13
+ * - ^ Section
14
+ * - ^^ Section
15
+ * - ^^^_^^^_^ Section
16
+ * - ^10 Section
17
+ * - §2 Section
18
+ * - < `Backticked Section`
20
19
  *
21
- * Form 2: Backticked Identifier:
22
- * - A phrase is a name wrapped in backticks ``` ` ```.
23
- * - Backticked identifiers must be on a single line and must not contain tabs or new lines unless using escaping codes, except for ordinary spaces.
24
- * - Special control characters (U+0000–U+001F) must be escaped.
25
- *
26
- * @note Implemented without regexp to keep it less cryptic, etc.
27
- * @note Returns the parts as strings; each part needs to be analyzed separately against the contraints in the specifications.
28
- * @returns An object with the identified header parts: marker characters, parsed name, and parsed level string.
20
+ * @note This function only splits the header into parts.
21
+ * Validation of marker depth, marker separator placement, mixed markers,
22
+ * and section name rules is handled by parseSectionHeader.ts.
29
23
  */
30
24
  const extractHeaderParts = (rawLine, errorHandler = null, ctx = null) => {
31
25
  (0, print_1.debugPrint)('-> Entered extractHeaderParts(..)');
@@ -33,9 +27,13 @@ const extractHeaderParts = (rawLine, errorHandler = null, ctx = null) => {
33
27
  const str = (0, extractSignificantYiniLine_1.extractYiniLine)(rawLine);
34
28
  (0, print_1.debugPrint)('rawLine: >>>' + rawLine + '<<<');
35
29
  (0, print_1.debugPrint)(' str: >>>' + str + '<<<');
36
- // Edge case: empty line.
37
30
  if (!str) {
38
- errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Internal-Error', 'Received blank argument in extractHeaderParts(..)', 'Sorry, an unintended internal error happened.');
31
+ if (errorHandler) {
32
+ errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Internal-Error', 'Received blank argument in extractHeaderParts(..)', 'Sorry, an unintended internal error happened.');
33
+ }
34
+ else {
35
+ throw new Error('Internal error: extractHeaderParts(..) received a blank argument.');
36
+ }
39
37
  }
40
38
  let pos = 0;
41
39
  const len = str.length;
@@ -43,48 +41,61 @@ const extractHeaderParts = (rawLine, errorHandler = null, ctx = null) => {
43
41
  let numberPart = '';
44
42
  let sectionNamePart = '';
45
43
  let isBacktickedName = false;
46
- // 1. Skip leading whitespace.
47
- while (pos < len && (str[pos] === ' ' || str[pos] === '\t'))
44
+ // 1. Skip leading horizontal whitespace.
45
+ while (pos < len && (str[pos] === ' ' || str[pos] === '\t')) {
48
46
  pos++;
49
- // 2. Collect marker(s): ^, <, §, €.
50
- while (pos < len && (0, yiniHelpers_1.isMarkerCharacter)(str[pos])) {
47
+ }
48
+ // 2. Collect the first section marker character.
49
+ if (pos < len && (0, yiniHelpers_1.isMarkerCharacter)(str[pos])) {
51
50
  markerCharsPart += str[pos];
52
51
  pos++;
53
52
  }
54
- // 3. Numeric part (for numeric shorthand; only if single marker found).
55
- if (markerCharsPart.length === 1 &&
56
- pos < len &&
57
- str[pos] >= '1' &&
58
- str[pos] <= '9') {
59
- // Start collecting number part.
53
+ // 3. Numeric shorthand: only allowed after a single marker character,
54
+ // for example ^10 Section or §2 Section.
55
+ if (pos < len && str[pos] >= '1' && str[pos] <= '9') {
60
56
  while (pos < len && str[pos] >= '0' && str[pos] <= '9') {
61
57
  numberPart += str[pos];
62
58
  pos++;
63
59
  }
64
- markerCharsPart += numberPart; // E.g., "^7".
60
+ const hasWhitespaceAfterNumber = pos >= len || str[pos] === ' ' || str[pos] === '\t';
61
+ if (!hasWhitespaceAfterNumber && errorHandler) {
62
+ errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Missing whitespace after numeric shorthand section marker.', `Numeric shorthand section headers require whitespace after the number. Got '${str}'.`, "Use a form such as '^10 Section', not '^10Section'.");
63
+ }
64
+ }
65
+ else {
66
+ // 4. Repeated marker sequence. Separators '_' are collected here too,
67
+ // so parseSectionHeader.ts can validate their placement.
68
+ while (pos < len && (str[pos] === '_' || (0, yiniHelpers_1.isMarkerCharacter)(str[pos]))) {
69
+ markerCharsPart += str[pos];
70
+ pos++;
71
+ }
65
72
  }
66
- // 4. Skip whitespace between marker and section name.
67
- while (pos < len && (str[pos] === ' ' || str[pos] === '\t'))
73
+ // 5. Skip horizontal whitespace between marker and section name.
74
+ while (pos < len && (str[pos] === ' ' || str[pos] === '\t')) {
68
75
  pos++;
69
- // 5. Collect section name (identifier or backticked).
76
+ }
77
+ // 6. Collect section name.
70
78
  if (pos < len && str[pos] === '`') {
71
- // Backticked identifier.
72
- let start = pos;
73
- pos++; // Skip initial backtick.
74
- while (pos < len && str[pos] !== '`')
79
+ const start = pos;
80
+ pos++; // Skip opening backtick.
81
+ while (pos < len) {
82
+ const ch = str[pos];
83
+ // Skip escaped character inside backticked identifier.
84
+ if (ch === '\\') {
85
+ pos += 2;
86
+ continue;
87
+ }
88
+ if (ch === '`') {
89
+ pos++; // Include closing backtick.
90
+ break;
91
+ }
75
92
  pos++;
76
- pos++; // Include the closing backtick (if found).
93
+ }
77
94
  sectionNamePart = str.slice(start, pos).trim();
78
95
  isBacktickedName = true;
79
96
  }
80
97
  else {
81
- // Non-backticked: take the rest of the line, trim off any trailing comments, etc.
82
98
  sectionNamePart = str.slice(pos).trim();
83
- // Optionally, strip trailing comments or newlines here if needed.
84
- }
85
- if (isBacktickedName) {
86
- (0, print_1.debugPrint)('Backticed sectionNamePart: ' + sectionNamePart);
87
- // sectionNamePart = trimBackticks(sectionNamePart)
88
99
  }
89
100
  (0, print_1.debugPrint)();
90
101
  (0, print_1.debugPrint)('------');
@@ -1,11 +1,29 @@
1
1
  /**
2
+ * Parses a YINI number literal into a JavaScript number.
3
+ *
2
4
  * @property {string | undefined} [tag]
3
- * This parameter is for debugging only. Its
4
- * contents may change at any time and should not
5
- * be relied upon for any significant purpose.
5
+ * This property is for debugging only. Its contents may change
6
+ * at any time and should not be relied upon for any significant
7
+ * purpose.
8
+ * @examples
9
+ * scientific = 1.23e4
10
+ * binary_alt = %1010
11
+ * duodecimal = 0z2EX9
12
+ * positive = +123
13
+ *
14
+ * a = 1_000
15
+ * b = +1_000
16
+ * c = 1.23_45e4
17
+ * d = 1.23e+4_5
18
+ * e = 0x_FF_AA
19
+ * f = hex:_FF_AA
20
+ * g = 0b_1010_1100
21
+ * h = %_1010_1100
22
+ * i = 0o_755_644
23
+ * j = 0z_2E_X9
6
24
  */
7
- declare const parseNumberLiteral: (txt: string) => {
8
- tag: string | undefined;
9
- value: number | undefined;
25
+ export declare function parseNumberLiteral(rawText: string): {
26
+ value: number;
27
+ tag?: string;
10
28
  };
11
29
  export default parseNumberLiteral;
@@ -1,84 +1,149 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const number_1 = require("../utils/number");
3
+ exports.parseNumberLiteral = parseNumberLiteral;
4
+ // src/parsers/parseNumber.ts
4
5
  const print_1 = require("../utils/print");
5
6
  /**
7
+ * Parses a YINI number literal into a JavaScript number.
8
+ *
6
9
  * @property {string | undefined} [tag]
7
- * This parameter is for debugging only. Its
8
- * contents may change at any time and should not
9
- * be relied upon for any significant purpose.
10
+ * This property is for debugging only. Its contents may change
11
+ * at any time and should not be relied upon for any significant
12
+ * purpose.
13
+ * @examples
14
+ * scientific = 1.23e4
15
+ * binary_alt = %1010
16
+ * duodecimal = 0z2EX9
17
+ * positive = +123
18
+ *
19
+ * a = 1_000
20
+ * b = +1_000
21
+ * c = 1.23_45e4
22
+ * d = 1.23e+4_5
23
+ * e = 0x_FF_AA
24
+ * f = hex:_FF_AA
25
+ * g = 0b_1010_1100
26
+ * h = %_1010_1100
27
+ * i = 0o_755_644
28
+ * j = 0z_2E_X9
10
29
  */
11
- const parseNumberLiteral = (txt) => {
12
- (0, print_1.debugPrint)('-> Entered parseNumberLiteral(..), txt: ' + txt);
13
- if (/^[+-]?(?:\d+\.\d*|\d*\.?\d+)e[+-]?\d+$/i.test(txt)) {
14
- // Exp. numbers
30
+ function parseNumberLiteral(rawText) {
31
+ const raw = rawText.trim();
32
+ const signChar = raw.startsWith('-') || raw.startsWith('+') ? raw[0] : '';
33
+ const signFactor = signChar === '-' ? -1 : 1;
34
+ const unsigned = signChar ? raw.slice(1) : raw;
35
+ const decDigits = String.raw `\d(?:_?\d)*`;
36
+ const hexDigits = String.raw `[0-9a-fA-F](?:_?[0-9a-fA-F])*`;
37
+ const binDigits = String.raw `[01](?:_?[01])*`;
38
+ const octDigits = String.raw `[0-7](?:_?[0-7])*`;
39
+ const duoDigits = String.raw `[0-9aAbBxXeE](?:_?[0-9aAbBxXeE])*`;
40
+ // --- Exponent notation ---------
41
+ // Examples: 1e3, 1.23e4, 1_000e2, 1.23e-4, .5e2.
42
+ // Underscores may appear between digits, but not adjacent or at the end.
43
+ const expPattern = new RegExp(`^(?:${decDigits}(?:\\.${decDigits})?|\\.${decDigits})[eE][+-]?${decDigits}$`);
44
+ if (expPattern.test(unsigned)) {
15
45
  (0, print_1.debugPrint)('* Identified as an exp number');
46
+ const normalized = `${signChar}${unsigned.replace(/_/g, '')}`;
16
47
  return {
48
+ value: Number(normalized),
17
49
  tag: 'From exp number, Number-Float',
18
- // value: parseInt(txt.replace('#', '0x'), 16),
19
- value: parseFloat(txt),
20
50
  };
21
51
  }
22
- // --- Hexadecimal ---------
23
- if (/^[+-]?(0[xX]|#)/.test(txt)) {
24
- // Prefix: 0x, 0X, #
52
+ // --- Hexadecimal with prefix 0x / 0X ---------
53
+ // Prefix: 0x or 0X.
54
+ // Allows underscores immediately after the prefix or between hex digits.
55
+ // Examples: 0xFF, 0x_FF, 0xFF_AA.
56
+ const hexPattern = new RegExp(`^0[xX]_?${hexDigits}$`);
57
+ if (hexPattern.test(unsigned)) {
25
58
  (0, print_1.debugPrint)('* Identified as a hex number');
26
- (0, print_1.debugPrint)('parsed out HEX: ' + txt.replace(/0[xX]|#/, ''));
59
+ const normalized = unsigned.replace(/^0[xX]_?/, '').replace(/_/g, '');
27
60
  return {
61
+ value: signFactor * Number.parseInt(normalized, 16),
28
62
  tag: 'From hex number, Number-Integer',
29
- // value: parseInt(txt.replace('#', '0x'), 16),
30
- value: parseInt(txt.replace(/0[xX]|#/, ''), 16),
31
63
  };
32
64
  }
33
- // --- Binary ---------
34
- if (/^[+-]?(0[bB]|%)/.test(txt)) {
35
- // Prefix: 0b, 0B, %
65
+ // --- Hexadecimal with explicit prefix hex: ---------
66
+ // Prefix: hex: or HEX:.
67
+ // Whitespace after hex: is not allowed.
68
+ // Allows underscores immediately after the prefix or between hex digits.
69
+ // Examples: hex:FF, hex:_FF, hex:FF_AA.
70
+ const explicitHexPattern = new RegExp(`^hex:_?${hexDigits}$`, 'i');
71
+ if (explicitHexPattern.test(unsigned)) {
72
+ (0, print_1.debugPrint)('* Identified as an explicit hex number');
73
+ const normalized = unsigned.replace(/^hex:_?/i, '').replace(/_/g, '');
74
+ return {
75
+ value: signFactor * Number.parseInt(normalized, 16),
76
+ tag: 'From explicit hex number, Number-Integer',
77
+ };
78
+ }
79
+ // --- Binary with prefix 0b / 0B or % ---------
80
+ // Prefix: 0b, 0B, or %.
81
+ // Allows underscores immediately after the prefix or between binary digits.
82
+ // Examples: 0b1010, 0b_1010, 0b1010_1100, %1010, %_1010.
83
+ const binPattern = new RegExp(`^(?:0[bB]|%)_?${binDigits}$`);
84
+ if (binPattern.test(unsigned)) {
36
85
  (0, print_1.debugPrint)('* Identified as a bin number');
37
- (0, print_1.debugPrint)('parsed out BIN: ' + txt.replace(/0[bB]|%/, ''));
86
+ const normalized = unsigned
87
+ .replace(/^(?:0[bB]|%)_?/, '')
88
+ .replace(/_/g, '');
38
89
  return {
90
+ value: signFactor * Number.parseInt(normalized, 2),
39
91
  tag: 'From bin number, Number-Integer',
40
- value: parseInt(txt.replace(/0[bB]|%/, ''), 2),
41
92
  };
42
93
  }
43
- // --- Octal ---------
44
- if (/^[+-]?0[oO]/.test(txt)) {
45
- // Prefix: 0o, 0O
46
- (0, print_1.debugPrint)('* Identified as a oct number');
47
- (0, print_1.debugPrint)('parsed out OCT: ' + txt.replace(/0[oO]/, ''));
94
+ // --- Octal with prefix 0o / 0O ---------
95
+ // Prefix: 0o or 0O.
96
+ // Allows underscores immediately after the prefix or between octal digits.
97
+ // Examples: 0o755, 0o_755, 0o755_644.
98
+ const octPattern = new RegExp(`^0[oO]_?${octDigits}$`);
99
+ if (octPattern.test(unsigned)) {
100
+ (0, print_1.debugPrint)('* Identified as an oct number');
101
+ const normalized = unsigned.replace(/^0[oO]_?/, '').replace(/_/g, '');
48
102
  return {
103
+ value: signFactor * Number.parseInt(normalized, 8),
49
104
  tag: 'From oct number, Number-Integer',
50
- value: parseInt(txt.replace(/0[oO]/, ''), 8),
51
105
  };
52
106
  }
53
- // --- Duodecimal ---------
54
- if (/^[+-]?0[zZ]/.test(txt)) {
55
- // Prefix: 0z, 0Z, x = A = 10, e = B = 11.
107
+ // --- Duodecimal / dozenal with prefix 0z / 0Z ---------
108
+ // Prefix: 0z or 0Z.
109
+ // x/X = A/a = 10, e/E = B/b = 11.
110
+ // Allows underscores immediately after the prefix or between duodecimal digits.
111
+ // Examples: 0z2EX9, 0z_2EX9, 0z2E_X9.
112
+ const duoPattern = new RegExp(`^0[zZ]_?${duoDigits}$`);
113
+ if (duoPattern.test(unsigned)) {
56
114
  (0, print_1.debugPrint)('* Identified as a duodecimal number');
57
- (0, print_1.debugPrint)('parsed out DOZ: ' + txt.replace(/0[zZ]/, ''));
58
- txt = txt.replace(/[xX]/g, 'A');
59
- txt = txt.replace(/[eE]/g, 'B');
60
- (0, print_1.debugPrint)('Converter to AB form: ' + txt.replace(/0[zZ]/, ''));
115
+ const normalized = unsigned
116
+ .replace(/^0[zZ]_?/, '')
117
+ .replace(/_/g, '')
118
+ .replace(/[xX]/g, 'A')
119
+ .replace(/[eE]/g, 'B');
61
120
  return {
121
+ value: signFactor * Number.parseInt(normalized, 12),
62
122
  tag: 'From doz (duodecimal) number, Number-Integer',
63
- value: parseInt(txt.replace(/0[zZ]/, ''), 12),
64
123
  };
65
124
  }
66
- // In a regex literal the dot must be escaped (\.) to match a literal '.'
67
- if (/\./.test(txt)) {
68
- (0, print_1.debugPrint)('* Identified as a float number');
125
+ // --- Decimal integer / float ---------
126
+ // Allows underscores between decimal digits.
127
+ // Examples: 1000, +1000, -1000, 1_000, 3.14, 3.141_592, .5.
128
+ const decimalPattern = new RegExp(`^(?:${decDigits}(?:\\.${decDigits})?|\\.${decDigits})$`);
129
+ if (decimalPattern.test(unsigned)) {
130
+ (0, print_1.debugPrint)(/\./.test(unsigned)
131
+ ? '* Identified as a float number'
132
+ : '* Identified as an int number');
133
+ const normalized = `${signChar}${unsigned.replace(/_/g, '')}`;
69
134
  return {
70
- tag: 'From float number, Number-Float',
71
- value: parseFloat(txt),
135
+ value: Number(normalized),
136
+ tag: /\./.test(unsigned)
137
+ ? 'From float number, Number-Float'
138
+ : 'From int number, Number-Integer',
72
139
  };
73
140
  }
74
- // TODO: Depending, on mode, below continue or break on error
75
- //console.error('Error: Failed to parse number value: ' + txt)
76
- if (!(0, number_1.isValidJSNumber)(txt)) {
77
- (0, print_1.debugPrint)('* Identified as invalid number');
78
- return { tag: 'From invalid number/value', value: undefined };
79
- }
80
- (0, print_1.debugPrint)('* Identified as a int number');
81
- return { tag: 'From int number, Number-Integer', value: parseInt(txt) };
82
- };
141
+ // --- Invalid number literal ---------
142
+ (0, print_1.debugPrint)('* Identified as invalid number');
143
+ return {
144
+ value: Number.NaN,
145
+ tag: 'From invalid number/value',
146
+ };
147
+ }
83
148
  exports.default = parseNumberLiteral;
84
149
  //# sourceMappingURL=parseNumber.js.map