yini-parser 1.0.1-beta → 1.1.0-beta

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 (43) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +131 -328
  3. package/dist/YINI.d.ts +34 -11
  4. package/dist/YINI.js +206 -121
  5. package/dist/core/ASTBuilder.d.ts +191 -0
  6. package/dist/core/ASTBuilder.js +827 -0
  7. package/dist/core/ErrorDataHandler.d.ts +19 -19
  8. package/dist/core/ErrorDataHandler.js +258 -150
  9. package/dist/core/objectBuilder.d.ts +9 -3
  10. package/dist/core/objectBuilder.js +126 -163
  11. package/dist/core/types.d.ts +234 -44
  12. package/dist/core/types.js +7 -33
  13. package/dist/grammar/YiniLexer.d.ts +54 -48
  14. package/dist/grammar/YiniLexer.js +330 -293
  15. package/dist/grammar/YiniParser.d.ts +167 -150
  16. package/dist/grammar/YiniParser.js +1241 -1202
  17. package/dist/grammar/YiniParserVisitor.d.ts +59 -45
  18. package/dist/grammar/YiniParserVisitor.js +1 -1
  19. package/dist/index.d.ts +4 -2
  20. package/dist/index.js +298 -120
  21. package/dist/parseEntry.d.ts +3 -2
  22. package/dist/parseEntry.js +352 -81
  23. package/dist/parsers/extractHeaderParts.d.ts +3 -2
  24. package/dist/parsers/extractHeaderParts.js +1 -0
  25. package/dist/parsers/parseBoolean.d.ts +1 -1
  26. package/dist/parsers/parseBoolean.js +2 -1
  27. package/dist/parsers/parseNumber.d.ts +8 -3
  28. package/dist/parsers/parseNumber.js +50 -16
  29. package/dist/parsers/parseSectionHeader.d.ts +3 -2
  30. package/dist/parsers/parseSectionHeader.js +1 -0
  31. package/dist/utils/number.d.ts +3 -0
  32. package/dist/utils/number.js +18 -0
  33. package/dist/utils/object.d.ts +55 -0
  34. package/dist/utils/object.js +85 -0
  35. package/dist/utils/string.d.ts +21 -1
  36. package/dist/utils/string.js +39 -4
  37. package/dist/utils/system.d.ts +15 -0
  38. package/dist/utils/system.js +21 -0
  39. package/dist/yiniHelpers.d.ts +3 -0
  40. package/dist/yiniHelpers.js +43 -7
  41. package/package.json +3 -3
  42. package/dist/core/YINIVisitor.d.ts +0 -158
  43. package/dist/core/YINIVisitor.js +0 -1010
@@ -1,17 +1,92 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.parseMain = void 0;
39
+ exports._parseMain = void 0;
40
+ const perf_hooks_1 = require("perf_hooks");
7
41
  const antlr4_1 = require("antlr4");
8
42
  const env_1 = require("./config/env");
43
+ const ASTBuilder_1 = __importDefault(require("./core/ASTBuilder"));
9
44
  const ErrorDataHandler_1 = require("./core/ErrorDataHandler");
10
45
  const objectBuilder_1 = require("./core/objectBuilder");
11
- const YINIVisitor_1 = __importDefault(require("./core/YINIVisitor"));
12
46
  const YiniLexer_1 = __importDefault(require("./grammar/YiniLexer"));
13
- const YiniParser_1 = __importDefault(require("./grammar/YiniParser"));
47
+ const YiniParser_1 = __importStar(require("./grammar/YiniParser"));
48
+ const object_1 = require("./utils/object");
14
49
  const print_1 = require("./utils/print");
50
+ const string_1 = require("./utils/string");
51
+ const system_1 = require("./utils/system");
52
+ const pkg = require('../package.json');
53
+ /**
54
+ * @param line Line number as 1-based.
55
+ * @param col Column number as 0-based.
56
+ */
57
+ const createGeneralCtx = (line, endColumn, startColumn = undefined) => {
58
+ const startToken = new antlr4_1.Token();
59
+ const stopToken = new antlr4_1.Token();
60
+ const ctx = new YiniParser_1.YiniContext();
61
+ ctx.start = startToken;
62
+ ctx.stop = stopToken;
63
+ ctx.start.line = line; // Note: Line num is 1-based.
64
+ if (startColumn && startColumn >= 0) {
65
+ ctx.start.column = startColumn; // Note: Column num is 0-based.
66
+ }
67
+ ctx.stop.column = endColumn; // Note: Column num is 0-based.
68
+ return ctx;
69
+ };
70
+ const parsePossibleStartCol = (errorHandler, recognizer) => {
71
+ var _a, _b;
72
+ let possibleStartCol = undefined;
73
+ try {
74
+ possibleStartCol = ((_a = recognizer === null || recognizer === void 0 ? void 0 : recognizer._ctx.start) === null || _a === void 0 ? void 0 : _a.column)
75
+ ? ((_b = recognizer === null || recognizer === void 0 ? void 0 : recognizer._ctx.start) === null || _b === void 0 ? void 0 : _b.column) + 1
76
+ : undefined;
77
+ }
78
+ catch (err) {
79
+ let msgHint = '';
80
+ if ((0, system_1.isError)(err)) {
81
+ msgHint = 'Error: ' + err.message;
82
+ }
83
+ else {
84
+ msgHint = 'Thrown value:' + JSON.stringify(err);
85
+ }
86
+ errorHandler.pushOrBail(null, 'Internal-Error', 'Catched error of possibleStartCol in parser grammar listener.', msgHint);
87
+ }
88
+ return possibleStartCol;
89
+ };
15
90
  class MyParserErrorListener {
16
91
  constructor(errorHandler) {
17
92
  this.errors = [];
@@ -19,10 +94,17 @@ class MyParserErrorListener {
19
94
  }
20
95
  syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e) {
21
96
  (0, print_1.debugPrint)('ANTLR parser cached an error');
22
- this.errors.push(`Line ${line}:${charPositionInLine} ${msg}`);
23
- const msgWhat = `Syntax error, at line: ${line}`;
24
- const msgWhy = `At about column ${1 + charPositionInLine} ${msg}`;
25
- this.errorHandler.pushOrBail(null, 'Syntax-Error', msgWhat, msgWhy);
97
+ const col = charPositionInLine + 1;
98
+ const possibleStartCol = parsePossibleStartCol(this.errorHandler, recognizer);
99
+ const msgWhat = `Syntax error`;
100
+ // Try to map message:
101
+ // From: "mismatched input '/END' expecting <EOF>"
102
+ // To: "Found '/END', but expected the end of the document."
103
+ // const msgWhy = `${capitalizeFirst(msg)}`
104
+ const msgWhy = `Details: ${msg}`;
105
+ const ctx = createGeneralCtx(line, charPositionInLine, possibleStartCol); // So we can show line/col in error message.
106
+ // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold.
107
+ this.errorHandler.pushOrBail(ctx, 'Syntax-Error', msgWhat, msgWhy);
26
108
  }
27
109
  // The following are required for the interface, but can be left empty.
28
110
  reportAmbiguity(...args) { }
@@ -36,26 +118,46 @@ class MyLexerErrorListener {
36
118
  }
37
119
  syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e) {
38
120
  // Handle the error as you want:
39
- (0, print_1.debugPrint)('ANTLR parser cached an error');
40
- this.errors.push(`Line ${line}:${charPositionInLine} ${msg}`);
41
- const msgWhat = `Syntax error, at line: ${line}`;
42
- const msgWhy = `At about column ${1 + charPositionInLine} ${msg}`;
43
- this.errorHandler.pushOrBail(null, 'Syntax-Error', msgWhat, msgWhy);
121
+ (0, print_1.debugPrint)('ANTLR lexer cached an error');
122
+ const col = charPositionInLine + 1;
123
+ const possibleStartCol = parsePossibleStartCol(this.errorHandler, recognizer);
124
+ // const msgWhat = `Syntax error at line ${line}, column ${col}:`
125
+ const msgWhat = `Syntax error`;
126
+ // Try to map message:
127
+ // From: "mismatched input '/END' expecting <EOF>"
128
+ // To: "Found '/END', but expected the end of the document."
129
+ // const msgWhy = `${capitalizeFirst(msg)}`
130
+ const msgWhy = `Details: ${msg}`;
131
+ // const msgHint = ``
132
+ const ctx = createGeneralCtx(line, charPositionInLine, possibleStartCol); // So we can show line/col in error message.
133
+ // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold.
134
+ this.errorHandler.pushOrBail(ctx, 'Syntax-Error', msgWhat, msgWhy);
44
135
  }
45
136
  }
46
- const parseMain = (yiniContent, options = {
137
+ /** Single source of truth. */
138
+ const _parseMain = (yiniContent,
139
+ // options: IParseMainOptions = {
140
+ options = {
47
141
  isStrict: false,
48
- bailSensitivityLevel: 0,
142
+ bailSensitivity: 0,
49
143
  isIncludeMeta: false,
50
144
  isWithDiagnostics: false,
51
145
  isWithTiming: false,
52
- }) => {
146
+ isKeepUndefinedInMeta: false,
147
+ isRequireDocTerminator: false,
148
+ }, runtimeInfo) => {
53
149
  (0, print_1.debugPrint)();
54
150
  (0, print_1.debugPrint)('-> Entered parseMain(..) in parseEntry');
55
- (0, print_1.debugPrint)(' isStrict mode = ' + options.isStrict);
56
- (0, print_1.debugPrint)('bailSensitivityLevel = ' + options.bailSensitivityLevel);
151
+ (0, print_1.debugPrint)(' isStrict mode = ' + options.isStrict);
152
+ (0, print_1.debugPrint)(' bailSensitivity = ' + options.bailSensitivity);
153
+ (0, print_1.debugPrint)(' isIncludeMeta = ' + options.isIncludeMeta);
154
+ (0, print_1.debugPrint)(' isWithDiagnostics = ' + options.isWithDiagnostics);
155
+ (0, print_1.debugPrint)(' isWithTiming = ' + options.isWithTiming);
156
+ (0, print_1.debugPrint)(' requireDocTerminator = ' + options.isRequireDocTerminator);
157
+ (0, print_1.debugPrint)('runtimeInfo.sourceType = ' + runtimeInfo.sourceType);
158
+ (0, print_1.debugPrint)(' runtimeInfo.fileName = ' + runtimeInfo.fileName);
57
159
  let persistThreshold;
58
- switch (options.bailSensitivityLevel) {
160
+ switch (options.bailSensitivity) {
59
161
  case 0:
60
162
  persistThreshold = '0-Ignore-Errors';
61
163
  break;
@@ -65,23 +167,57 @@ const parseMain = (yiniContent, options = {
65
167
  default:
66
168
  persistThreshold = '2-Abort-Even-on-Warnings';
67
169
  }
170
+ const errorHandler = new ErrorDataHandler_1.ErrorDataHandler(runtimeInfo.sourceType, persistThreshold, runtimeInfo.fileName);
171
+ if (yiniContent.trim() === '') {
172
+ const isFileSourceType = (runtimeInfo === null || runtimeInfo === void 0 ? void 0 : runtimeInfo.sourceType) === 'File';
173
+ // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold.
174
+ errorHandler.pushOrBail(null, 'Syntax-Error', 'Empty YINI document.', `The input is blank or contains only whitespace in the ${isFileSourceType ? 'YINI file' : 'YINI inline content'}.`, `Tip: Add at least one section '^ SectionName' or a key-value pair 'key = value' to make it a valid YINI file.`);
175
+ }
176
+ //---------------------------------------------
177
+ // Note: Only computed when isWithTiming.
178
+ let timeStartMs = 0;
179
+ let timeEnd1Ms = 0;
180
+ let timeEnd2Ms = 0;
181
+ let timeEnd3Ms = 0;
182
+ let timeEnd4Ms = 0;
183
+ //---------------------------------------------
184
+ //---------------------------------------------
185
+ // Note: Should ALWAYS be computed.
186
+ let runStartedAt = '';
187
+ let runFinishedAt = '';
188
+ let durationMs = 0;
189
+ //---------------------------------------------
68
190
  (0, env_1.isDebug)() && console.log();
69
- (0, print_1.debugPrint)('=== Phase 1 ===================================================');
191
+ (0, print_1.debugPrint)('=== Phase 1 - Lexing ===================================================');
192
+ // -----------------------------
193
+ // Below block should always be done despite isWithTiming to compute
194
+ // total time and runStartedAt that should always be computed.
195
+ {
196
+ timeStartMs = perf_hooks_1.performance.now();
197
+ runStartedAt = new Date().toISOString();
198
+ }
199
+ // -----------------------------
70
200
  const inputStream = antlr4_1.CharStreams.fromString(yiniContent);
71
201
  const lexer = new YiniLexer_1.default(inputStream);
72
- const tokenStream = new antlr4_1.CommonTokenStream(lexer);
73
- const parser = new YiniParser_1.default(tokenStream);
74
- const errorHandler = new ErrorDataHandler_1.ErrorDataHandler(persistThreshold);
75
202
  // Remove the default ConsoleErrorListener
76
203
  lexer.removeErrorListeners(); // Removes the default lexer console error output.
77
204
  const lexerErrorListener = new MyLexerErrorListener(errorHandler);
78
205
  lexer.addErrorListener(lexerErrorListener);
206
+ const tokenStream = new antlr4_1.CommonTokenStream(lexer);
207
+ // Important: force tokenization here so lexing is measured separately.
208
+ tokenStream.fill();
209
+ (0, print_1.debugPrint)('--- Parsing done. ---');
210
+ (0, print_1.debugPrint)('=== Ended phase 1 =============================================');
211
+ (0, env_1.isDebug)() && console.log();
212
+ (0, print_1.debugPrint)('=== Phase 2 - Parsing ===================================================');
213
+ if (options.isWithTiming) {
214
+ timeEnd1Ms = perf_hooks_1.performance.now();
215
+ }
216
+ const parser = new YiniParser_1.default(tokenStream);
79
217
  // const errorListener = new MyParserErrorListener(errorHandler)
80
218
  parser.removeErrorListeners(); // Removes the default parser console error output.
81
219
  const parserErrorListener = new MyParserErrorListener(errorHandler);
82
220
  parser.addErrorListener(parserErrorListener);
83
- (0, print_1.debugPrint)();
84
- (0, print_1.debugPrint)('--- Starting parsing... ---');
85
221
  const parseTree = parser.yini(); // The function yini() is the start rule.
86
222
  if (parserErrorListener.errors.length > 0 ||
87
223
  lexerErrorListener.errors.length > 0) {
@@ -89,85 +225,218 @@ const parseMain = (yiniContent, options = {
89
225
  if ((0, env_1.isDebug)()) {
90
226
  // Handle or display syntax errors
91
227
  console.error('Syntax errors detected:', parserErrorListener.errors, lexerErrorListener.errors);
92
- //process.exit(1)
93
228
  }
94
229
  }
95
- (0, print_1.debugPrint)('--- Parsing done. ---');
96
- (0, print_1.debugPrint)('=== Ended phase 1 =============================================');
230
+ (0, print_1.debugPrint)('=== Ended phase 2 =============================================');
97
231
  (0, env_1.isDebug)() && console.log();
98
- (0, print_1.debugPrint)('=== Phase 2 ===================================================');
99
- // const errorHandler = new ErrorDataHandler(persistThreshold)
100
- const visitor = new YINIVisitor_1.default(errorHandler, options.isStrict);
101
- const syntaxTreeC = visitor.visit(parseTree);
232
+ (0, print_1.debugPrint)('=== Phase 3 - AST Model build & validation ===================================================');
233
+ if (options.isWithTiming) {
234
+ timeEnd2Ms = perf_hooks_1.performance.now();
235
+ }
236
+ const builder = new ASTBuilder_1.default(errorHandler, options, runtimeInfo.sourceType, runtimeInfo.fileName || null);
237
+ const ast = builder.buildAST(parseTree);
238
+ if (ast.numOfMembers === 0 && ast.numOfSections === 0) {
239
+ // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold.
240
+ errorHandler.pushOrBail(null, 'Syntax-Error', 'No meaningful content.', `No sections or members found in the ${ast.sourceType === 'File' ? 'YINI file' : 'YINI inline content'}.`, `${ast.sourceType === 'File' ? 'A valid YINI file' : 'Any valid YINI inline content'} must contain at least one section '^ SectionName' or a key–value pair 'key = value' to make it a valid YINI file.`);
241
+ }
102
242
  if ((0, env_1.isDebug)()) {
103
243
  console.log();
104
244
  console.log('**************************************************************************');
105
- console.log('*** syntaxTreeContainer: *************************************************');
106
- (0, print_1.printObject)(syntaxTreeC);
245
+ console.log('*** AST *************************************************');
246
+ (0, print_1.printObject)(ast);
107
247
  console.log('**************************************************************************');
108
248
  console.log('**************************************************************************');
109
249
  console.log();
110
250
  }
111
- (0, print_1.debugPrint)('=== Ended phase 2 =============================================');
251
+ (0, print_1.debugPrint)('=== Ended phase 3 =============================================');
112
252
  (0, env_1.isDebug)() && console.log();
113
- (0, print_1.debugPrint)('=== Phase 3 ===================================================');
253
+ (0, print_1.debugPrint)('=== Phase 4 - Object Building Construction / Binding / Evaluation) ===================================================');
254
+ if (options.isWithTiming) {
255
+ timeEnd3Ms = perf_hooks_1.performance.now();
256
+ }
114
257
  // Construct.
115
- const finalJSResult = (0, objectBuilder_1.constructFinalObject)(syntaxTreeC, errorHandler);
116
- (0, print_1.debugPrint)('=== Ended phase 3 =============================================');
258
+ // const finalJSResult = constructFinalObject(syntaxTreeC, errorHandler)
259
+ // const finalJSResult = builder.build(parseTree)
260
+ // const finalJSResult = ast //NOTE: ONLY TEMP so code runs
261
+ const finalJSResult = (0, objectBuilder_1.astToObject)(ast, errorHandler);
262
+ (0, print_1.debugPrint)('=== Ended phase 4 =============================================');
263
+ // -----------------------------
264
+ // Below block should always be done despite isWithTiming to compute
265
+ // total time and runStartedAt that should always be computed.
266
+ {
267
+ timeEnd4Ms = perf_hooks_1.performance.now();
268
+ durationMs = timeEnd4Ms - timeStartMs;
269
+ runFinishedAt = new Date().toISOString();
270
+ }
271
+ // -----------------------------
117
272
  (0, print_1.debugPrint)('visitor.visit(..): finalJSResult:');
118
273
  (0, env_1.isDebug)() && console.debug(finalJSResult);
119
274
  (0, print_1.debugPrint)();
120
275
  if (options.isStrict) {
121
- //throw Error('ERROR: Strict-mode not yet implemented')
122
- errorHandler.pushOrBail(null, 'Syntax-Warning', 'WARNING: Strict-mode not yet fully implemented', '', '');
276
+ // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold.
277
+ errorHandler.pushOrBail(null, 'Syntax-Warning', 'Warning: Strict mode is not yet fully implemented.', 'Some validation rules may still be missing or incomplete.');
123
278
  }
124
279
  else {
125
280
  (0, print_1.debugPrint)('visitor.visit(..): finalJSResult:');
126
281
  (0, env_1.isDebug)() && console.debug(finalJSResult);
127
282
  }
128
- // Construct meta data.
129
- const metaData = {
130
- strictMode: options.isStrict,
131
- hasTerminal: syntaxTreeC._hasTerminal,
132
- sections: syntaxTreeC._meta_numOfSections,
133
- members: syntaxTreeC._meta_numOfMembers,
134
- sectionChains: syntaxTreeC._meta_numOfChains,
135
- keysParsed: null,
136
- timing: {
137
- totalMs: null,
138
- phase1Ms: null,
139
- phase2Ms: null,
140
- phase3Ms: null,
141
- },
142
- };
143
- if (options.isWithDiagnostics) {
144
- // Attach optional diagnostics.
145
- metaData.diagnostics = {
146
- bailSensitivityLevel: options.bailSensitivityLevel,
147
- errors: errorHandler.getNumOfErrors(),
148
- warnings: errorHandler.getNumOfWarnings(),
149
- infoAndNotices: errorHandler.getNumOfInfoAndNotices(),
150
- envs: {
151
- NODE_ENV: process.env.NODE_ENV,
152
- APP_ENV: process.env.APP_ENV,
153
- libNodeEnv: env_1.localNodeEnv,
154
- libAppEnv: env_1.localAppEnv,
283
+ const constructResultMetaData = () => {
284
+ // --- Construct meta information -------------------------------------
285
+ const to3 = (n) => Number.parseFloat(n.toFixed(3));
286
+ // Construct meta data.
287
+ const metaData = {
288
+ parserVersion: pkg.version,
289
+ mode: options.isStrict ? 'strict' : 'lenient',
290
+ orderPreserved: true,
291
+ totalErrors: errorHandler.getNumOfErrors(),
292
+ totalWarnings: errorHandler.getNumOfWarnings(),
293
+ totalMessages: errorHandler.getNumOfAllMessages(),
294
+ runStartedAt,
295
+ runFinishedAt,
296
+ durationMs: to3(durationMs),
297
+ source: {
298
+ sourceType: (0, string_1.toLowerSnakeCase)(ast.sourceType),
299
+ fileName: ast.fileName,
300
+ hasDocumentTerminator: ast.terminatorSeen,
301
+ hasYiniMarker: ast.yiniMarkerSeen,
302
+ lineCount: runtimeInfo.lineCount,
303
+ byteSize: runtimeInfo.fileByteSize,
304
+ sha256: runtimeInfo.sha256,
155
305
  },
156
- libFlags: {
157
- isDev: (0, env_1.isDev)(),
158
- isDebug: (0, env_1.isDebug)(),
306
+ structure: {
307
+ maxDepth: ast.maxDepth,
308
+ sectionCount: ast.numOfSections,
309
+ memberCount: ast.numOfMembers,
310
+ keysParsedCount: null,
311
+ // objectCount: null,
312
+ // listCount: null,
313
+ sectionNamePaths: ast.sectionNamePaths,
159
314
  },
315
+ metaSchemaVersion: '1.0.0',
160
316
  };
161
- }
162
- if (options.isWithTiming) {
163
- // Attach optional timing data.
164
- metaData.timing = {
165
- totalMs: null,
166
- phase1Ms: null,
167
- phase2Ms: null,
168
- phase3Ms: null,
169
- };
170
- }
317
+ // Attach optional diagnostics.
318
+ if (options.isWithDiagnostics) {
319
+ const mapLevelKey = (level) => {
320
+ switch (level) {
321
+ case 0:
322
+ return 'ignore_errors';
323
+ case 1:
324
+ return 'abort_on_errors';
325
+ case 2:
326
+ return 'abort_on_warnings';
327
+ }
328
+ };
329
+ const mapLevelLabel = (level) => {
330
+ switch (level) {
331
+ case 0:
332
+ return '0-Ignore-Errors';
333
+ case 1:
334
+ return '1-Abort-on-Errors';
335
+ case 2:
336
+ return '2-Abort-Even-on-Warnings';
337
+ }
338
+ };
339
+ const mapLevelDescription = (level) => {
340
+ switch (level) {
341
+ case 0:
342
+ return 'Continue despite errors.';
343
+ case 1:
344
+ return 'Abort when errors occur.';
345
+ case 2:
346
+ return 'Abort when errors or warnings occur.';
347
+ }
348
+ return null;
349
+ };
350
+ metaData.diagnostics = {
351
+ failLevel: {
352
+ preferredLevel: runtimeInfo.preferredBailSensitivity,
353
+ levelUsed: options.bailSensitivity,
354
+ levelKey: mapLevelKey(options.bailSensitivity),
355
+ levelLabel: mapLevelLabel(options.bailSensitivity),
356
+ levelDescription: (mapLevelDescription(options.bailSensitivity)),
357
+ },
358
+ errors: {
359
+ errorCount: errorHandler.getNumOfErrors(),
360
+ payload: errorHandler.getErrors(),
361
+ },
362
+ warnings: {
363
+ warningCount: errorHandler.getNumOfWarnings(),
364
+ payload: errorHandler.getWarnings(),
365
+ },
366
+ notices: {
367
+ noticeCount: errorHandler.getNumOfNotices(),
368
+ payload: errorHandler.getNotices(),
369
+ },
370
+ infos: {
371
+ infoCount: errorHandler.getNumOfInfos(),
372
+ payload: errorHandler.getInfos(),
373
+ },
374
+ environment: {
375
+ NODE_ENV: process.env.NODE_ENV,
376
+ APP_ENV: process.env.APP_ENV,
377
+ lib: {
378
+ nodeEnv: env_1.localNodeEnv,
379
+ appEnv: env_1.localAppEnv,
380
+ flags: { isDev: (0, env_1.isDev)(), isDebug: (0, env_1.isDebug)() },
381
+ },
382
+ },
383
+ optionsUsed: {
384
+ // NOTE: (!) These MUST user options.
385
+ strictMode: options.isStrict,
386
+ failLevel: options.bailSensitivity,
387
+ includeMetaData: options.isIncludeMeta,
388
+ includeDiagnostics: options.isWithDiagnostics,
389
+ includeTiming: options.isWithTiming,
390
+ preserveUndefinedInMeta: options.isKeepUndefinedInMeta,
391
+ requireDocTerminator: options.isRequireDocTerminator,
392
+ },
393
+ };
394
+ }
395
+ // Attach optional durations timing data.
396
+ if (options.isWithTiming) {
397
+ metaData.timing = {
398
+ total: !options.isWithTiming
399
+ ? null
400
+ : {
401
+ timeMs: to3(durationMs), // durationMs = timeEnd4Ms - timeStartMs
402
+ name: runtimeInfo.sourceType === 'Inline'
403
+ ? 'Total'
404
+ : 'Total (excluding phase0 (I/O))',
405
+ },
406
+ phase0: !options.isWithTiming || runtimeInfo.sourceType === 'Inline'
407
+ ? undefined
408
+ : {
409
+ timeMs: to3(runtimeInfo.timeIoMs),
410
+ name: 'I/O',
411
+ },
412
+ phase1: !options.isWithTiming
413
+ ? null
414
+ : {
415
+ timeMs: to3(timeEnd1Ms - timeStartMs),
416
+ name: 'Lexing',
417
+ },
418
+ phase2: !options.isWithTiming
419
+ ? null
420
+ : {
421
+ timeMs: to3(timeEnd2Ms - timeEnd1Ms),
422
+ name: 'Parsing',
423
+ },
424
+ phase3: !options.isWithTiming
425
+ ? null
426
+ : {
427
+ timeMs: to3(timeEnd3Ms - timeEnd2Ms),
428
+ name: 'AST Building',
429
+ },
430
+ phase4: !options.isWithTiming
431
+ ? null
432
+ : {
433
+ timeMs: to3(timeEnd4Ms - timeEnd3Ms),
434
+ name: 'Object Building',
435
+ },
436
+ };
437
+ }
438
+ return metaData;
439
+ };
171
440
  (0, print_1.debugPrint)('getNumOfErrors(): ' + errorHandler.getNumOfErrors());
172
441
  if (errorHandler.getNumOfErrors()) {
173
442
  console.log();
@@ -177,9 +446,11 @@ const parseMain = (yiniContent, options = {
177
446
  if (options.isIncludeMeta) {
178
447
  return {
179
448
  result: finalJSResult,
180
- meta: metaData,
449
+ meta: !options.isKeepUndefinedInMeta
450
+ ? (0, object_1.removeUndefinedDeep)(constructResultMetaData())
451
+ : constructResultMetaData(),
181
452
  };
182
453
  }
183
454
  return finalJSResult;
184
455
  };
185
- exports.parseMain = parseMain;
456
+ exports._parseMain = _parseMain;
@@ -1,5 +1,5 @@
1
1
  import { ErrorDataHandler } from '../core/ErrorDataHandler';
2
- import { SectionContext } from '../grammar/YiniParser';
2
+ import { StmtContext } from '../grammar/YiniParser';
3
3
  /**
4
4
  * Check and identify the section header parts via tokenizing the parts and return them as strings.
5
5
  * @param rawHeaderLine Raw line with the section header where the header
@@ -19,10 +19,11 @@ import { SectionContext } from '../grammar/YiniParser';
19
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
20
  * - Special control characters (U+0000–U+001F) must be escaped.
21
21
  *
22
+ * @note Implemented without regexp to keep it less cryptic, etc.
22
23
  * @note Returns the parts as strings; each part needs to be analyzed separately against the contraints in the specifications.
23
24
  * @returns An object with the identified header parts: marker characters, parsed name, and parsed level string.
24
25
  */
25
- declare const extractHeaderParts: (rawLine: string, errorHandler?: ErrorDataHandler | null, ctx?: SectionContext | null) => {
26
+ declare const extractHeaderParts: (rawLine: string, errorHandler?: ErrorDataHandler | null, ctx?: StmtContext | null) => {
26
27
  strMarkerChars: string;
27
28
  strSectionName: string;
28
29
  strNumberPart: string;
@@ -22,6 +22,7 @@ const extractSignificantYiniLine_1 = require("./extractSignificantYiniLine");
22
22
  * - Backticked identifiers must be on a single line and must not contain tabs or new lines unless using escaping codes, except for ordinary spaces.
23
23
  * - Special control characters (U+0000–U+001F) must be escaped.
24
24
  *
25
+ * @note Implemented without regexp to keep it less cryptic, etc.
25
26
  * @note Returns the parts as strings; each part needs to be analyzed separately against the contraints in the specifications.
26
27
  * @returns An object with the identified header parts: marker characters, parsed name, and parsed level string.
27
28
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Extract boolean literal.
3
3
  */
4
- declare const parseBooleanLiteral: (txt: string) => boolean;
4
+ declare const parseBooleanLiteral: (text: string) => boolean;
5
5
  export default parseBooleanLiteral;
@@ -4,8 +4,9 @@ const print_1 = require("../utils/print");
4
4
  /**
5
5
  * Extract boolean literal.
6
6
  */
7
- const parseBooleanLiteral = (txt) => {
7
+ const parseBooleanLiteral = (text) => {
8
8
  (0, print_1.debugPrint)('-> Entered parseBooleanLiteral(..)');
9
+ const txt = text.trim().toLowerCase();
9
10
  const value = !!(txt === 'true' || txt === 'yes' || txt === 'on');
10
11
  return value;
11
12
  };
@@ -1,6 +1,11 @@
1
- import { TDataType } from '../core/YINIVisitor';
1
+ /**
2
+ * @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.
6
+ */
2
7
  declare const parseNumberLiteral: (txt: string) => {
3
- type: TDataType;
4
- value: number;
8
+ tag: string | undefined;
9
+ value: number | undefined;
5
10
  };
6
11
  export default parseNumberLiteral;