yini-parser 1.0.0-alpha.6 → 1.0.0-alpha.7x
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -1
- package/README.md +309 -156
- package/dist/YINI.d.ts +27 -5
- package/dist/YINI.js +27 -5
- package/dist/core/ErrorDataHandler.js +28 -32
- package/dist/core/YINIVisitor.d.ts +2 -2
- package/dist/core/YINIVisitor.js +99 -5
- package/dist/grammar/YiniLexer.js +4 -1
- package/dist/grammar/YiniParser.d.ts +27 -3
- package/dist/grammar/YiniParser.js +544 -294
- package/dist/grammar/YiniParserVisitor.d.ts +14 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +34 -15
- package/dist/parsers/extractSignificantYiniLine.js +2 -0
- package/examples/basic-with-comments.yini +15 -0
- package/examples/basic.yini +11 -0
- package/examples/compare-formats.md +89 -0
- package/examples/nested.yini +26 -0
- package/examples/parse-example.ts +22 -0
- package/package.json +3 -2
package/dist/YINI.js
CHANGED
|
@@ -17,9 +17,20 @@ class YINI {
|
|
|
17
17
|
}
|
|
18
18
|
YINI.filePath = ''; // Used in error reporting.
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @
|
|
20
|
+
* Parse YINI content into a JavaScript object.
|
|
21
|
+
*
|
|
22
|
+
* @param yiniContent YINI code as a string (multi‑line content supported).
|
|
23
|
+
* @param strictMode If `true`, enforce strict parsing rules (e.g. require `/END`, disallow trailing commas).
|
|
24
|
+
* @param bailSensitivity Controls how errors and warnings are handled:
|
|
25
|
+
* - `'auto'` : Auto‑select level (strict→1, lenient→0)
|
|
26
|
+
* - `0` / `'Ignore-Errors'` : Continue parsing despite errors; log them and attempt recovery.
|
|
27
|
+
* - `1` / `'Abort-on-Errors'` : Stop parsing on the first error.
|
|
28
|
+
* - `2` / `'Abort-Even-on-Warnings'`: Stop parsing on the first warning **or** error.
|
|
29
|
+
* @param includeMetaData If `true`, return additional metadata (e.g. warnings, statistics) alongside the parsed object.
|
|
30
|
+
*
|
|
31
|
+
* @note The order of properties in each output object may differ from their order in the YINI source.
|
|
32
|
+
*
|
|
33
|
+
* @returns A JavaScript object representing the parsed YINI content.
|
|
23
34
|
*/
|
|
24
35
|
YINI.parse = (yiniContent, strictMode = false, bailSensitivity = 'auto', includeMetaData = false) => {
|
|
25
36
|
(0, system_1.debugPrint)('-> Entered static parse(..) in class YINI\n');
|
|
@@ -66,9 +77,20 @@ YINI.parse = (yiniContent, strictMode = false, bailSensitivity = 'auto', include
|
|
|
66
77
|
return result;
|
|
67
78
|
};
|
|
68
79
|
/**
|
|
80
|
+
* Parse a YINI file into a JavaScript object.
|
|
81
|
+
*
|
|
69
82
|
* @param yiniFile Path to the YINI file.
|
|
70
|
-
* @
|
|
71
|
-
* @
|
|
83
|
+
* @param strictMode If `true`, enforce strict parsing rules (e.g. require `/END`, disallow trailing commas).
|
|
84
|
+
* @param bailSensitivity Controls how errors and warnings are handled:
|
|
85
|
+
* - `'auto'` : Auto‑select level (strict→1, lenient→0)
|
|
86
|
+
* - `0` / `'Ignore-Errors'` : Continue parsing despite errors; log them and attempt recovery.
|
|
87
|
+
* - `1` / `'Abort-on-Errors'` : Stop parsing on the first error.
|
|
88
|
+
* - `2` / `'Abort-Even-on-Warnings'`: Stop parsing on the first warning **or** error.
|
|
89
|
+
* @param includeMetaData If `true`, return additional metadata (e.g. warnings, statistics) alongside the parsed object.
|
|
90
|
+
*
|
|
91
|
+
* @note The order of properties in each output object may differ from their order in the YINI source.
|
|
92
|
+
*
|
|
93
|
+
* @returns A JavaScript object representing the parsed YINI content.
|
|
72
94
|
*/
|
|
73
95
|
YINI.parseFile = (filePath, strictMode = false, bailSensitivity = 'auto', includeMetaData = false) => {
|
|
74
96
|
(0, system_1.debugPrint)('Current directory = ' + process.cwd());
|
|
@@ -95,13 +95,12 @@ class ErrorDataHandler {
|
|
|
95
95
|
this.emitInternalError(msgWhat, msgWhy, msgHint);
|
|
96
96
|
if (this.persistThreshold === '1-Abort-on-Errors' ||
|
|
97
97
|
this.persistThreshold === '2-Abort-Even-on-Warnings') {
|
|
98
|
-
if (process.env.NODE_ENV === 'test') {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
98
|
+
// if (process.env.NODE_ENV === 'test') {
|
|
99
|
+
// In test, throw an error instead of exiting.
|
|
100
|
+
throw new Error(`Internal-Error: ${msgWhat}`);
|
|
101
|
+
// } else {
|
|
102
|
+
// process.exit(2)
|
|
103
|
+
// }
|
|
105
104
|
}
|
|
106
105
|
break;
|
|
107
106
|
case 'Syntax-Error':
|
|
@@ -109,26 +108,24 @@ class ErrorDataHandler {
|
|
|
109
108
|
this.emitSyntaxError(msgWhat, msgWhy, msgHint);
|
|
110
109
|
if (this.persistThreshold === '1-Abort-on-Errors' ||
|
|
111
110
|
this.persistThreshold === '2-Abort-Even-on-Warnings') {
|
|
112
|
-
if (process.env.NODE_ENV === 'test') {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
111
|
+
// if (process.env.NODE_ENV === 'test') {
|
|
112
|
+
// In test, throw an error instead of exiting.
|
|
113
|
+
throw new Error(`Syntax-Error: ${'' + msgWhat}`);
|
|
114
|
+
// } else {
|
|
115
|
+
// process.exit(3)
|
|
116
|
+
// }
|
|
119
117
|
}
|
|
120
118
|
break;
|
|
121
119
|
case 'Syntax-Warning':
|
|
122
120
|
this.numSyntaxWarnings++;
|
|
123
121
|
this.emitSyntaxWarning(msgWhat, msgWhy, msgHint);
|
|
124
122
|
if (this.persistThreshold === '2-Abort-Even-on-Warnings') {
|
|
125
|
-
if (process.env.NODE_ENV === 'test') {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
123
|
+
// if (process.env.NODE_ENV === 'test') {
|
|
124
|
+
// In test, throw an error instead of exiting.
|
|
125
|
+
throw new Error(`Syntax-Warning: ${msgWhat}`);
|
|
126
|
+
// } else {
|
|
127
|
+
// process.exit(4)
|
|
128
|
+
// }
|
|
132
129
|
}
|
|
133
130
|
break;
|
|
134
131
|
case 'Notice':
|
|
@@ -143,17 +140,16 @@ class ErrorDataHandler {
|
|
|
143
140
|
this.numFatalErrors++;
|
|
144
141
|
this.emitFatalError(msgWhat, msgWhy, msgHint);
|
|
145
142
|
// CANNOT recover fatal errors, will lead to an exit!
|
|
146
|
-
if (process.env.NODE_ENV === 'test') {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
143
|
+
// if (process.env.NODE_ENV === 'test') {
|
|
144
|
+
// In test, throw an error instead of exiting.
|
|
145
|
+
throw new Error(`Internal-Error: ${msgWhat}`);
|
|
146
|
+
// } else {
|
|
147
|
+
// process.exit(1)
|
|
148
|
+
// (!) Not sure about the below yet, if it's preferable in this case...
|
|
149
|
+
// Use this instead of process.exit(1), this will
|
|
150
|
+
// lead to that the current thread(s) will exit as well.
|
|
151
|
+
// process.exitCode = 1
|
|
152
|
+
// }
|
|
157
153
|
}
|
|
158
154
|
};
|
|
159
155
|
this.emitFatalError = (msgWhat = 'Something went wrong!', msgWhy = '', msgHint = '') => {
|
|
@@ -136,13 +136,13 @@ export default class YINIVisitor<IResult> extends YiniParserVisitor<IResult> {
|
|
|
136
136
|
* @param ctx the parse tree
|
|
137
137
|
* @return the visitor result
|
|
138
138
|
*/
|
|
139
|
-
visitElements
|
|
139
|
+
visitElements: (ctx: ElementsContext) => IResult;
|
|
140
140
|
/**
|
|
141
141
|
* Visit a parse tree produced by `YiniParser.element`.
|
|
142
142
|
* @param ctx the parse tree
|
|
143
143
|
* @return the visitor result
|
|
144
144
|
*/
|
|
145
|
-
visitElement: (ctx: ElementContext) =>
|
|
145
|
+
visitElement: (ctx: ElementContext) => any;
|
|
146
146
|
/**
|
|
147
147
|
* Visit a parse tree produced by `YiniParser.string_concat`.
|
|
148
148
|
* @param ctx the parse tree
|
package/dist/core/YINIVisitor.js
CHANGED
|
@@ -779,7 +779,8 @@ class YINIVisitor extends YiniParserVisitor_1.default {
|
|
|
779
779
|
return this.visit(ctx.null_literal());
|
|
780
780
|
if (ctx.object_literal())
|
|
781
781
|
return this.visit(ctx.object_literal());
|
|
782
|
-
|
|
782
|
+
if (ctx.list_in_brackets())
|
|
783
|
+
return this.visit(ctx.list_in_brackets());
|
|
783
784
|
// if (ctx.string_concat()) return this.visit(ctx.string_concat())
|
|
784
785
|
return null;
|
|
785
786
|
};
|
|
@@ -883,8 +884,80 @@ class YINIVisitor extends YiniParserVisitor_1.default {
|
|
|
883
884
|
*/
|
|
884
885
|
// visitList_in_brackets?: (ctx: List_in_bracketsContext) => IResult
|
|
885
886
|
this.visitList_in_brackets = (ctx) => {
|
|
886
|
-
|
|
887
|
-
|
|
887
|
+
(0, system_1.debugPrint)('-> Entered visitList_in_brackets(..)');
|
|
888
|
+
let elements = [];
|
|
889
|
+
if (!ctx.elements()) {
|
|
890
|
+
(0, system_1.debugPrint)('Detected elements() is [], in list brackets');
|
|
891
|
+
// elements = []
|
|
892
|
+
return { type: 'List', value: [] };
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
(0, system_1.debugPrint)('Detected elements() has items, in list brackets');
|
|
896
|
+
elements = this.visit(ctx.elements());
|
|
897
|
+
}
|
|
898
|
+
(0, system_1.debugPrint)('<- Leaving visitList_in_brackets(..)');
|
|
899
|
+
if ((0, env_1.isDebug)()) {
|
|
900
|
+
console.log('returning:');
|
|
901
|
+
console.log({ type: 'List', value: elements.value });
|
|
902
|
+
console.log();
|
|
903
|
+
}
|
|
904
|
+
return { type: 'List', value: elements.value };
|
|
905
|
+
};
|
|
906
|
+
/**
|
|
907
|
+
* Visit a parse tree produced by `YiniParser.elements`.
|
|
908
|
+
* @param ctx the parse tree
|
|
909
|
+
* @return the visitor result
|
|
910
|
+
*/
|
|
911
|
+
this.visitElements = (ctx) => {
|
|
912
|
+
(0, system_1.debugPrint)('-> Entered visitElements(..)');
|
|
913
|
+
const firstElem = ctx.element();
|
|
914
|
+
let elements = [];
|
|
915
|
+
(0, system_1.debugPrint)(' element = ' + firstElem);
|
|
916
|
+
(0, system_1.debugPrint)(' element.getText() = ' + firstElem.getText());
|
|
917
|
+
(0, system_1.debugPrint)(' elements = ' + !!ctx.elements());
|
|
918
|
+
const resultElem = ctx.element()
|
|
919
|
+
? this.visitElement(ctx.element())
|
|
920
|
+
: null;
|
|
921
|
+
const resultTypeElem = resultElem === null || resultElem === void 0 ? void 0 : resultElem.type;
|
|
922
|
+
const resultValueElem = resultElem === null || resultElem === void 0 ? void 0 : resultElem.value;
|
|
923
|
+
(0, system_1.debugPrint)(' elem type = ' + resultTypeElem + ' @visitElements(..)');
|
|
924
|
+
(0, system_1.debugPrint)(' elem value = ' + resultValueElem + ' @visitElements(..)');
|
|
925
|
+
const resultElems = ctx.elements()
|
|
926
|
+
? this.visitElements(ctx.elements())
|
|
927
|
+
: null;
|
|
928
|
+
const resultTypeElems = resultElems === null || resultElems === void 0 ? void 0 : resultElems.type;
|
|
929
|
+
const resultValueElems = resultElems === null || resultElems === void 0 ? void 0 : resultElems.value;
|
|
930
|
+
(0, system_1.debugPrint)(' elems type = ' +
|
|
931
|
+
resultTypeElems +
|
|
932
|
+
' @visitElements(..)');
|
|
933
|
+
(0, system_1.debugPrint)(' elems value = ' +
|
|
934
|
+
resultValueElems +
|
|
935
|
+
' @visitElements(..)');
|
|
936
|
+
if (!ctx.elements()) {
|
|
937
|
+
(0, system_1.debugPrint)('In visitElements(..) detected that elements() has no elements');
|
|
938
|
+
elements = undefined;
|
|
939
|
+
}
|
|
940
|
+
else {
|
|
941
|
+
(0, system_1.debugPrint)('In visitElements(..) detected elements in elements()');
|
|
942
|
+
elements = this.visit(ctx.elements());
|
|
943
|
+
if ((0, env_1.isDebug)()) {
|
|
944
|
+
console.log('result of visited elements:');
|
|
945
|
+
(0, system_1.printObject)(elements);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
const returnValues = elements
|
|
949
|
+
? [resultElem].concat(elements.value)
|
|
950
|
+
: [resultElem];
|
|
951
|
+
(0, system_1.debugPrint)('<- Leaving visitElements(..)');
|
|
952
|
+
if ((0, env_1.isDebug)()) {
|
|
953
|
+
console.log('returnValues:');
|
|
954
|
+
(0, system_1.printObject)(returnValues);
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
type: 'List',
|
|
958
|
+
// value: [resultElem].concat(elements.value),
|
|
959
|
+
value: returnValues,
|
|
960
|
+
};
|
|
888
961
|
};
|
|
889
962
|
/**
|
|
890
963
|
* Visit a parse tree produced by `YiniParser.element`.
|
|
@@ -892,12 +965,33 @@ class YINIVisitor extends YiniParserVisitor_1.default {
|
|
|
892
965
|
* @return the visitor result
|
|
893
966
|
*/
|
|
894
967
|
// visitElement?: (ctx: ElementContext) => IResult
|
|
968
|
+
// visitElement = (ctx: ElementContext): IResult => {
|
|
895
969
|
this.visitElement = (ctx) => {
|
|
970
|
+
(0, system_1.debugPrint)('-> Entered visitElement(..)');
|
|
971
|
+
// if (ctx.value()) {
|
|
972
|
+
// return this.visit(ctx.value())
|
|
973
|
+
// } else {
|
|
974
|
+
// return { type: 'Null', value: null } as IResult
|
|
975
|
+
// }
|
|
976
|
+
let result;
|
|
896
977
|
if (ctx.value()) {
|
|
897
|
-
|
|
978
|
+
result = this.visit(ctx.value());
|
|
898
979
|
}
|
|
899
980
|
else {
|
|
900
|
-
|
|
981
|
+
result = { type: 'Null', value: null };
|
|
982
|
+
}
|
|
983
|
+
(0, system_1.debugPrint)('<- Leaving visitElement(..)');
|
|
984
|
+
if ((0, env_1.isDebug)()) {
|
|
985
|
+
console.log('returning:');
|
|
986
|
+
(0, system_1.printObject)(result);
|
|
987
|
+
console.log();
|
|
988
|
+
}
|
|
989
|
+
//return 'value'
|
|
990
|
+
switch (result.type) {
|
|
991
|
+
case 'String':
|
|
992
|
+
return `${result.value}`;
|
|
993
|
+
default:
|
|
994
|
+
return result.value;
|
|
901
995
|
}
|
|
902
996
|
};
|
|
903
997
|
this.errorHandler = errorHandler;
|
|
@@ -81,7 +81,10 @@ YiniLexer.literalNames = [null, null,
|
|
|
81
81
|
"']'", "'{'",
|
|
82
82
|
"'}'", "'+'",
|
|
83
83
|
"'$'", "'%'",
|
|
84
|
-
"'@'", "';'"
|
|
84
|
+
"'@'", "';'",
|
|
85
|
+
null, null,
|
|
86
|
+
null, "'{}'",
|
|
87
|
+
"'[]'"];
|
|
85
88
|
YiniLexer.symbolicNames = [null, "YINI_MARKER",
|
|
86
89
|
"SECTION_HEAD",
|
|
87
90
|
"TERMINAL_TOKEN",
|
|
@@ -67,6 +67,8 @@ export default class YiniParser extends Parser {
|
|
|
67
67
|
static readonly RULE_string_literal = 16;
|
|
68
68
|
static readonly RULE_string_concat = 17;
|
|
69
69
|
static readonly RULE_boolean_literal = 18;
|
|
70
|
+
static readonly RULE_empty_object = 19;
|
|
71
|
+
static readonly RULE_empty_list = 20;
|
|
70
72
|
static readonly literalNames: (string | null)[];
|
|
71
73
|
static readonly symbolicNames: (string | null)[];
|
|
72
74
|
static readonly ruleNames: string[];
|
|
@@ -96,6 +98,8 @@ export default class YiniParser extends Parser {
|
|
|
96
98
|
string_literal(): String_literalContext;
|
|
97
99
|
string_concat(): String_concatContext;
|
|
98
100
|
boolean_literal(): Boolean_literalContext;
|
|
101
|
+
empty_object(): Empty_objectContext;
|
|
102
|
+
empty_list(): Empty_listContext;
|
|
99
103
|
static readonly _serializedATN: number[];
|
|
100
104
|
private static __ATN;
|
|
101
105
|
static get _ATN(): ATN;
|
|
@@ -183,7 +187,7 @@ export declare class Object_literalContext extends ParserRuleContext {
|
|
|
183
187
|
CC(): TerminalNode;
|
|
184
188
|
NL_list(): TerminalNode[];
|
|
185
189
|
NL(i: number): TerminalNode;
|
|
186
|
-
|
|
190
|
+
empty_object(): Empty_objectContext;
|
|
187
191
|
get ruleIndex(): number;
|
|
188
192
|
accept<Result>(visitor: YiniParserVisitor<Result>): Result;
|
|
189
193
|
}
|
|
@@ -195,7 +199,7 @@ export declare class ObjectMemberListContext extends ParserRuleContext {
|
|
|
195
199
|
COMMA(i: number): TerminalNode;
|
|
196
200
|
NL_list(): TerminalNode[];
|
|
197
201
|
NL(i: number): TerminalNode;
|
|
198
|
-
|
|
202
|
+
empty_object(): Empty_objectContext;
|
|
199
203
|
get ruleIndex(): number;
|
|
200
204
|
accept<Result>(visitor: YiniParserVisitor<Result>): Result;
|
|
201
205
|
}
|
|
@@ -224,7 +228,7 @@ export declare class List_in_bracketsContext extends ParserRuleContext {
|
|
|
224
228
|
CB(): TerminalNode;
|
|
225
229
|
NL_list(): TerminalNode[];
|
|
226
230
|
NL(i: number): TerminalNode;
|
|
227
|
-
|
|
231
|
+
empty_list(): Empty_listContext;
|
|
228
232
|
get ruleIndex(): number;
|
|
229
233
|
accept<Result>(visitor: YiniParserVisitor<Result>): Result;
|
|
230
234
|
}
|
|
@@ -281,3 +285,23 @@ export declare class Boolean_literalContext extends ParserRuleContext {
|
|
|
281
285
|
get ruleIndex(): number;
|
|
282
286
|
accept<Result>(visitor: YiniParserVisitor<Result>): Result;
|
|
283
287
|
}
|
|
288
|
+
export declare class Empty_objectContext extends ParserRuleContext {
|
|
289
|
+
constructor(parser?: YiniParser, parent?: ParserRuleContext, invokingState?: number);
|
|
290
|
+
EMPTY_OBJECT(): TerminalNode;
|
|
291
|
+
OC(): TerminalNode;
|
|
292
|
+
CC(): TerminalNode;
|
|
293
|
+
NL_list(): TerminalNode[];
|
|
294
|
+
NL(i: number): TerminalNode;
|
|
295
|
+
get ruleIndex(): number;
|
|
296
|
+
accept<Result>(visitor: YiniParserVisitor<Result>): Result;
|
|
297
|
+
}
|
|
298
|
+
export declare class Empty_listContext extends ParserRuleContext {
|
|
299
|
+
constructor(parser?: YiniParser, parent?: ParserRuleContext, invokingState?: number);
|
|
300
|
+
EMPTY_LIST(): TerminalNode;
|
|
301
|
+
OB(): TerminalNode;
|
|
302
|
+
CB(): TerminalNode;
|
|
303
|
+
NL_list(): TerminalNode[];
|
|
304
|
+
NL(i: number): TerminalNode;
|
|
305
|
+
get ruleIndex(): number;
|
|
306
|
+
accept<Result>(visitor: YiniParserVisitor<Result>): Result;
|
|
307
|
+
}
|