yini-parser 1.4.2-beta → 1.4.3
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 +9 -0
- package/README.md +59 -141
- package/dist/core/astBuilder.js +172 -58
- package/dist/core/astBuilder.js.map +1 -1
- package/dist/core/errorDataHandler.js +19 -14
- package/dist/core/errorDataHandler.js.map +1 -1
- package/dist/dev/main.js +85 -273
- package/dist/dev/main.js.map +1 -1
- package/dist/parsers/parseSectionHeader.js +2 -6
- package/dist/parsers/parseSectionHeader.js.map +1 -1
- package/dist/parsers/parseString.js +1 -1
- package/dist/parsers/parseString.js.map +1 -1
- package/dist/utils/string.d.ts +1 -0
- package/dist/utils/string.js +5 -1
- package/dist/utils/string.js.map +1 -1
- package/package.json +3 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.4.3 - 2026 Apr
|
|
4
|
+
- **Fixed:** Rebuilt the project and reduced reported vulnerabilities from 4 to 0.
|
|
5
|
+
|
|
6
|
+
## 1.4.3-beta - 2026 Mar
|
|
7
|
+
- **Fixed:** Error messages and thrown parse errors now include correct line and column information again.
|
|
8
|
+
- **Improved:** Syntax and string-related parse errors are now clearer and more consistent.
|
|
9
|
+
- **Improved:** Reduced some duplicate follow-up errors during recovery after invalid input.
|
|
10
|
+
- **Added:** Regression tests for diagnostics, thrown errors, and line/column reporting.
|
|
11
|
+
|
|
3
12
|
## 1.4.2-beta - 2026 Mar
|
|
4
13
|
- **Fixed:** Error messages now include line and column information again. This was unintentionally missing after the previous update.
|
|
5
14
|
- **Added:** Test cases to help ensure line and column information is preserved in future updates.
|
package/README.md
CHANGED
|
@@ -1,145 +1,68 @@
|
|
|
1
|
-
#
|
|
2
|
-
> **Readable configuration without YAML foot-guns or JSON noise.**
|
|
1
|
+
# yini-parser
|
|
2
|
+
> **Readable configuration for Node.js and TypeScript — without YAML foot-guns or JSON noise.**
|
|
3
3
|
|
|
4
|
-
The official TypeScript / Node.js parser for **YINI** (by the YINI-lang project) —
|
|
4
|
+
The official TypeScript / Node.js parser for **YINI** (by the YINI-lang project) — a human-friendly configuration format with real structure, nested sections, comments, and predictable parsing.
|
|
5
|
+
|
|
6
|
+
YINI is designed for applications, tools, and services that need configuration that stays readable for humans without becoming vague, fragile, or hard to maintain.
|
|
5
7
|
|
|
6
8
|
[](https://www.npmjs.com/package/yini-parser) [](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-all-tests.yml) [](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-regression-tests.yml) [](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-grammar-drift-check.yml) [](https://www.npmjs.com/package/yini-parser)
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
## Quick Start
|
|
9
11
|
|
|
10
12
|
```sh
|
|
11
13
|
npm install yini-parser
|
|
12
14
|
```
|
|
13
15
|
|
|
14
|
-
```ts
|
|
15
|
-
import YINI from 'yini-parser'
|
|
16
|
-
const config = YINI.parseFile('./config.yini')
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
npm run test
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
➡️ See full [documentation or YINI format specification](https://github.com/YINI-lang/YINI-spec)
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Example of YINI code
|
|
28
|
-
> A basic YINI configuration example, showing a section, nested section, comments:
|
|
29
|
-

|
|
30
|
-
Source: [basic.yini](./samples/basic.yini)
|
|
31
|
-
|
|
32
|
-
## YINI Parser – (source code in TypeScript)
|
|
33
|
-
|
|
34
|
-
A runtime parser for the official [YINI configuration file format](https://github.com/YINI-lang/YINI-spec).
|
|
35
|
-
|
|
36
|
-
The parser follows the official YINI specification and is implemented in TypeScript.
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Quick Start
|
|
41
|
-
|
|
42
|
-
A small example using YINI in TypeScript/JavaScript:
|
|
43
16
|
```ts
|
|
44
17
|
import YINI from 'yini-parser'
|
|
45
18
|
|
|
46
19
|
const config = YINI.parse(`
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Indentation is only for readability.
|
|
20
|
+
^ App
|
|
21
|
+
name = 'My App'
|
|
22
|
+
darkMode = true
|
|
51
23
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
items = 25
|
|
55
|
-
darkMode = true // "ON" and "YES" works too
|
|
56
|
-
|
|
57
|
-
// Sub-section of the "App" section
|
|
58
|
-
^^ Special
|
|
59
|
-
primaryColor = #336699 // Hex number format
|
|
60
|
-
isCaching = false // "OFF" and "NO" works too
|
|
24
|
+
^^ Features
|
|
25
|
+
caching = on
|
|
61
26
|
`)
|
|
62
27
|
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
console.log(config.App.name) // My Title
|
|
67
|
-
console.log(config.App.Special.isCaching) // false
|
|
68
|
-
console.log()
|
|
69
|
-
console.log(config)
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**Output:**
|
|
73
|
-
```js
|
|
74
|
-
My Title
|
|
75
|
-
false
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
App: {
|
|
79
|
-
name: 'My Title',
|
|
80
|
-
items: 25,
|
|
81
|
-
darkMode: true,
|
|
82
|
-
Special: {
|
|
83
|
-
primaryColor: 3368601,
|
|
84
|
-
isCaching: false
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
28
|
+
console.log(config.App.name) // My App
|
|
29
|
+
console.log(config.App.Features.caching) // true
|
|
88
30
|
```
|
|
89
31
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
- ▶️ Link to [Demo Apps](https://github.com/YINI-lang/yini-demo-apps/tree/main) with complete basic usage.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## Example 2
|
|
97
|
-
> A real-world YINI configuration example, showing sections, nesting, comments, and multiple data types:
|
|
98
|
-

|
|
99
|
-
Source: [config.yini](./samples/config.yini)
|
|
100
|
-
|
|
101
|
-
## 📂 More Examples
|
|
102
|
-
|
|
103
|
-
- ▶️ Explore more [YINI examples](https://yini-lang.org/learn-yini/examples/?utm_source=yini-parser-ts&utm_medium=github&utm_campaign=repo-link&utm_content=readme).
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
### Who is this for?
|
|
108
|
-
|
|
109
|
-
YINI is designed as an ideal option for application developers, platform teams, DevOps engineers, and anyone maintaining complex configuration at scale.
|
|
32
|
+
➡️ Learn more in the [YINI specification and documentation](https://yini-lang.org/refs/specification).
|
|
110
33
|
|
|
111
34
|
---
|
|
112
35
|
|
|
113
|
-
## 🙋♀️ Why YINI?
|
|
114
|
-
- **Indentation-independent structure:** The YINI config format is indentation-independent, meaning spaces and tabs never affect meaning.
|
|
115
|
-
- **Explicit nesting for easy refactoring & large configs:** It uses clear header markers (`^`, `^^`, `^^^`) to define hierarchy (like in Markdown), without long dotted keys.
|
|
116
|
-
- **Multiple data types:** Supports boolean literals (`true` / `false`, `Yes` / `No`, etc), numbers, arrays (lists), and JS-style objects natively, with explicit string syntax.
|
|
117
|
-
- **Comments support:** YINI supports multiple comment styles (`#`, `//`, `/* ... */`, and `;`). Allows you to document configuration directly in the file.
|
|
118
|
-
- **Predictable parsing rules:** Fewer production surprises, well-defined rules with optional strict and lenient modes, for different use cases.
|
|
36
|
+
## 🙋♀️ Why try YINI?
|
|
119
37
|
|
|
38
|
+
- **Readable by humans** — Less noisy than JSON, less fragile than indentation-driven formats.
|
|
39
|
+
- **Structured enough for real configuration** — Sections, nested sections, lists, objects, booleans, and null.
|
|
40
|
+
- **Predictable parsing** — Explicit syntax with clear rules.
|
|
41
|
+
- **Easy to use from TypeScript/Node.js** — Parse from strings or files in a few lines.
|
|
42
|
+
|
|
120
43
|
---
|
|
121
44
|
|
|
122
|
-
##
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- Only the `YINI` class is exported; all internal details are private.
|
|
127
|
-
- Arrays/Lists (bracketed): `list = [10, 20, 30]`
|
|
128
|
-
- JavaScript-style objects (inline maps).
|
|
45
|
+
## What YINI looks like in practice
|
|
46
|
+
> A basic YINI configuration example, showing a section, nested section, comments:
|
|
47
|
+

|
|
48
|
+
Source: [basic.yini](./samples/basic.yini)
|
|
129
49
|
|
|
130
|
-
|
|
50
|
+
- ▶️ Link to [Demo Apps](https://github.com/YINI-lang/yini-demo-apps/tree/main) with complete basic usage.
|
|
131
51
|
|
|
132
52
|
---
|
|
133
53
|
|
|
134
|
-
##
|
|
135
|
-
|
|
136
|
-
|
|
54
|
+
## Why YINI works well for configuration
|
|
55
|
+
- **Indentation-independent structure:** Spaces and tabs never change meaning, so files can be reformatted without changing structure.
|
|
56
|
+
- **Explicit nesting:** Hierarchy is defined with section markers like `^`, `^^`, and `^^^`, making large configurations easier to scan and refactor.
|
|
57
|
+
- **Multiple data types:** Supports booleans (`true` / `false`, `yes` / `no`, etc.), numbers, lists, and inline objects, with explicit string syntax.
|
|
58
|
+
- **Comment support:** YINI supports multiple comment styles (`#`, `//`, `/* ... */`, and `;`), making it easier to document configuration directly in the file.
|
|
59
|
+
- **Predictable parsing:** Clear rules with optional strict and lenient modes for different use cases.
|
|
137
60
|
|
|
138
61
|
---
|
|
139
62
|
|
|
140
63
|
## Usage
|
|
141
64
|
|
|
142
|
-
###
|
|
65
|
+
### Install with your package manager
|
|
143
66
|
|
|
144
67
|
With **npm**:
|
|
145
68
|
```sh
|
|
@@ -160,8 +83,7 @@ pnpm add yini-parser
|
|
|
160
83
|
**Note:** Only a default export (YINI) is provided. Named imports are not supported.
|
|
161
84
|
```js
|
|
162
85
|
const YINI = require('yini-parser').default;
|
|
163
|
-
//
|
|
164
|
-
// (Some Node.js setups require the .default property, others don't, due to ESM/CommonJS interop quirks.)
|
|
86
|
+
// If your setup handles default interop differently, try:
|
|
165
87
|
// const YINI = require('yini-parser');
|
|
166
88
|
|
|
167
89
|
// Parse from string.
|
|
@@ -206,54 +128,50 @@ const configFromFile = YINI.parseFile('./config.yini');
|
|
|
206
128
|
|
|
207
129
|
---
|
|
208
130
|
|
|
209
|
-
##
|
|
210
|
-
|
|
211
|
-
1. **Improve existing functionality** — Ongoing improvements to core parsing, richer diagnostics, and expanded QA for areas not yet fully covered.
|
|
212
|
-
2. **Full spec compliance** — Complete support for all features in the YINI Specification v1.0.0 (and the latest RCs).
|
|
213
|
-
- Progress and current status are tracked in [FEATURE-CHECKLIST.md](https://github.com/YINI-lang/yini-parser-typescript/blob/main/FEATURE-CHECKLIST.md).
|
|
214
|
-
3. **Schema validation (future)** — Possible future expansion to support schema/contract validation for stricter type-safe configs.
|
|
215
|
-
4. **Ecosystem integration** - Broader and additional support for tooling, and other ecosystem projects.
|
|
131
|
+
## 📂 More Examples
|
|
216
132
|
|
|
217
|
-
|
|
218
|
-
Some advanced YINI features are still evolving and are tracked transparently.
|
|
133
|
+
- ▶️ Explore more [YINI examples](https://yini-lang.org/learn-yini/examples/?utm_source=yini-parser-ts&utm_medium=github&utm_campaign=repo-link&utm_content=readme).
|
|
219
134
|
|
|
220
|
-
|
|
135
|
+
### Example 2
|
|
136
|
+
> A real-world YINI configuration example, showing sections, nesting, comments, and multiple data types:
|
|
137
|
+

|
|
138
|
+
Source: [config.yini](./samples/config.yini)
|
|
221
139
|
|
|
222
140
|
---
|
|
223
141
|
|
|
224
|
-
##
|
|
225
|
-
We welcome feedback, bug reports, feature requests, and code contributions!
|
|
226
|
-
- [Open an Issue](https://github.com/YINI-lang/yini-parser-typescript/issues)
|
|
227
|
-
- [Start a Discussion](https://github.com/YINI-lang/yini-parser-typescript/discussions)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
If this library is useful to you, a GitHub star helps guide future development.
|
|
231
|
-
|
|
232
|
-
---
|
|
142
|
+
## 🧪 Testing and Stability
|
|
233
143
|
|
|
234
|
-
|
|
235
|
-
- [Project Setup](https://github.com/YINI-lang/yini-parser-typescript/blob/main/docs/Project-Setup.md) — How to run, test, and build the project, etc.
|
|
236
|
-
- [Conventions](https://github.com/YINI-lang/yini-parser-typescript/blob/main/docs/Conventions.md) — Project conventions, naming patterns, etc.
|
|
144
|
+
This parser is continuously validated through comprehensive regression and smoke tests, ensuring deterministic parsing behavior across default, strict, and metadata-enabled modes.
|
|
237
145
|
|
|
238
146
|
---
|
|
239
147
|
|
|
240
148
|
## Links
|
|
241
|
-
- ➡️ [
|
|
242
|
-
*
|
|
149
|
+
- ➡️ [YINI Homepage](https://yini-lang.org)
|
|
150
|
+
*Tutorials, guides, and examples.*
|
|
243
151
|
|
|
244
152
|
- ➡️ [YINI CLI on GitHub](https://github.com/YINI-lang/yini-cli)
|
|
245
|
-
*
|
|
153
|
+
*CLI tooling for working with YINI files.*
|
|
246
154
|
|
|
247
155
|
- ➡️ [YINI Project](https://github.com/YINI-lang)
|
|
248
|
-
*
|
|
156
|
+
*Repositories and related ecosystem projects.*
|
|
249
157
|
|
|
250
|
-
|
|
251
|
-
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 🤝 Contributing
|
|
161
|
+
We welcome feedback, bug reports, feature requests, and code contributions!
|
|
162
|
+
- [Open an Issue](https://github.com/YINI-lang/yini-parser-typescript/issues)
|
|
163
|
+
- [Start a Discussion](https://github.com/YINI-lang/yini-parser-typescript/discussions)
|
|
164
|
+
|
|
165
|
+
If this library is useful to you, a GitHub star helps more people discover the project and supports future development.
|
|
166
|
+
|
|
167
|
+
### Documentation
|
|
168
|
+
- [Project Setup](https://github.com/YINI-lang/yini-parser-typescript/blob/main/docs/Project-Setup.md) — How to run, test, and build the project, etc.
|
|
169
|
+
- [Conventions](https://github.com/YINI-lang/yini-parser-typescript/blob/main/docs/Conventions.md) — Project conventions, naming patterns, etc.
|
|
252
170
|
|
|
253
171
|
---
|
|
254
172
|
|
|
255
173
|
## License
|
|
256
|
-
This project is licensed under the Apache-2.0 license
|
|
174
|
+
This project is licensed under the Apache-2.0 license — see the [LICENSE](./LICENSE) file for details.
|
|
257
175
|
|
|
258
176
|
In this project on GitHub, the `libs` directory contains third party software and each is licensed under its own license which is described in its own license file under the respective directory under `libs`.
|
|
259
177
|
|
|
@@ -264,4 +182,4 @@ In this project on GitHub, the `libs` directory contains third party software an
|
|
|
264
182
|
>
|
|
265
183
|
> Predictable configuration with clear rules.
|
|
266
184
|
|
|
267
|
-
[yini-lang.org](https://yini-lang.org/?utm_source=github&utm_medium=referral&utm_campaign=yini_parser_ts&utm_content=readme_footer) · [YINI on GitHub](https://github.com/YINI-lang)
|
|
185
|
+
[yini-lang.org](https://yini-lang.org/?utm_source=github&utm_medium=referral&utm_campaign=yini_parser_ts&utm_content=readme_footer) · [YINI-lang on GitHub](https://github.com/YINI-lang)
|
package/dist/core/astBuilder.js
CHANGED
|
@@ -386,16 +386,39 @@ class ASTBuilder extends YiniParserVisitor_1.default {
|
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
const resultKey = (0, string_1.trimBackticks)(rawKey);
|
|
389
|
+
const rawMemberText = ctx.getText();
|
|
389
390
|
const rawValue = ctx.value?.()?.getText();
|
|
390
391
|
(0, print_1.debugPrint)(`visitMember(..): rawValue = ` + ctx.value?.()?.getText());
|
|
392
|
+
(0, print_1.debugPrint)(`visitMember(..): rawMemberText = ` + rawMemberText);
|
|
393
|
+
/* NOTE:
|
|
394
|
+
ctx.value() can be missing after parser recovery, even when the user did type something after ""=".
|
|
395
|
+
|
|
396
|
+
Example:
|
|
397
|
+
port = 54_32
|
|
398
|
+
|
|
399
|
+
ANTLR may reject 54_32, and then ctx.value() may not exist in the AST as you expect.
|
|
400
|
+
So rawValue alone is not enough to know whether this was:
|
|
401
|
+
* truly empty: port =
|
|
402
|
+
* malformed: port = 54_32
|
|
403
|
+
* rawMemberText lets one inspect the whole member text.
|
|
404
|
+
*/
|
|
391
405
|
let valueContext = ctx.value?.();
|
|
392
406
|
let valueNode;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
407
|
+
const hasEquals = rawMemberText.includes('=');
|
|
408
|
+
// const hasTextAfterEquals = hasEquals
|
|
409
|
+
// ? rawMemberText.split('=').slice(1).join('=').trim().length > 0
|
|
410
|
+
// : false
|
|
411
|
+
const eqIndex = rawMemberText.indexOf('=');
|
|
412
|
+
const hasTextAfterEquals = eqIndex >= 0 && rawMemberText.slice(eqIndex + 1).trim().length > 0;
|
|
413
|
+
if (!valueContext || !rawValue) {
|
|
414
|
+
// Case 1: there is text after '=' but parser could not form a valid value.
|
|
415
|
+
// Parser-level syntax error has likely already been reported.
|
|
416
|
+
if (hasTextAfterEquals) {
|
|
417
|
+
return makeScalarValue('Undefined', undefined, 'Parser syntax error already reported');
|
|
418
|
+
}
|
|
419
|
+
// Case 2: truly empty value.
|
|
396
420
|
switch (this.options.rules.treatEmptyValueAsNull) {
|
|
397
421
|
case 'allow':
|
|
398
|
-
// Lenient mode: implicit null, no warning (treatEmptyValueAsNull = 'allow').
|
|
399
422
|
valueNode = makeScalarValue('Null', null, 'Implicit null (empty value)');
|
|
400
423
|
break;
|
|
401
424
|
case 'allow-with-warning':
|
|
@@ -403,41 +426,56 @@ class ASTBuilder extends YiniParserVisitor_1.default {
|
|
|
403
426
|
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Warning', `Empty value treated as null for key '${resultKey}'.`, `An empty value after '=' was encountered. Per 'treatEmptyValueAsNull = allow-with-warning', interpreted as null.`, `If you intended null, write it explicitly: ${resultKey} = null. Otherwise provide a non-empty value or set 'treatEmptyValueAsNull' to 'disallow'.`);
|
|
404
427
|
break;
|
|
405
428
|
case 'disallow':
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', `Missing value for key '${resultKey}'.`, `Expected a value after '=' but found none. Implicit nulls are disallowed by 'treatEmptyValueAsNull = disallow'.`, `Write 'null' explicitly (${resultKey} = null) if that is intended, or provide a concrete value.`);
|
|
409
|
-
break;
|
|
429
|
+
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', `Missing value for key '${resultKey}'`, `Expected a value after '=' but found none. Implicit nulls are disallowed by 'treatEmptyValueAsNull = disallow'.`, `Write 'null' explicitly (${resultKey} = null) if that is intended, or provide a concrete value.`);
|
|
430
|
+
return makeScalarValue('Undefined', undefined, 'Missing value already reported');
|
|
410
431
|
}
|
|
411
432
|
}
|
|
412
433
|
else {
|
|
413
|
-
valueNode = this.visitValue
|
|
434
|
+
valueNode = this.visitValue(valueContext);
|
|
414
435
|
}
|
|
415
436
|
(0, print_1.debugPrint)('visitMember(..): valueNode:');
|
|
416
437
|
if ((0, env_1.isDebug)()) {
|
|
417
438
|
(0, print_1.printObject)(valueNode);
|
|
418
439
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
440
|
+
/*
|
|
441
|
+
if (!valueNode) {
|
|
442
|
+
this.errorHandler!.pushOrBail(
|
|
443
|
+
toErrorLocation(ctx),
|
|
444
|
+
'Syntax-Error',
|
|
445
|
+
'Invalid value',
|
|
446
|
+
`Invalid value for key '${resultKey}' in member (<key> = <value> pair).`,
|
|
447
|
+
`Got '${rawValue}', but expected a valid value/literal (string, number, boolean, null, list, or object). Optionally with a single leading minus sign '-'.`,
|
|
448
|
+
)
|
|
449
|
+
} else if (
|
|
450
|
+
valueNode.type === 'Undefined' &&
|
|
451
|
+
valueNode.tag !== 'Invalid string literal already reported'
|
|
452
|
+
) {
|
|
453
|
+
this.errorHandler!.pushOrBail(
|
|
454
|
+
toErrorLocation(ctx),
|
|
455
|
+
'Syntax-Error',
|
|
456
|
+
'Invalid value',
|
|
457
|
+
`Invalid value for key '${resultKey}' in member (<key> = <value> pair).`,
|
|
458
|
+
`Got '${rawValue}', but expected a valid value/literal (string, number, boolean, null, list, or object). Optionally with a single leading minus sign '-'.`,
|
|
459
|
+
)
|
|
460
|
+
}*/
|
|
429
461
|
if (!valueNode) {
|
|
430
462
|
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid value', `Invalid value for key '${resultKey}' in member (<key> = <value> pair).`, `Got '${rawValue}', but expected a valid value/literal (string, number, boolean, null, list, or object). Optionally with a single leading minus sign '-'.`);
|
|
431
463
|
}
|
|
432
464
|
else if (valueNode.type === 'Undefined' &&
|
|
433
|
-
valueNode.tag !== 'Invalid string literal already reported'
|
|
465
|
+
valueNode.tag !== 'Invalid string literal already reported' &&
|
|
466
|
+
valueNode.tag !== 'Missing value already reported' &&
|
|
467
|
+
valueNode.tag !== 'Parser syntax error already reported') {
|
|
434
468
|
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Invalid value', `Invalid value for key '${resultKey}' in member (<key> = <value> pair).`, `Got '${rawValue}', but expected a valid value/literal (string, number, boolean, null, list, or object). Optionally with a single leading minus sign '-'.`);
|
|
435
469
|
}
|
|
470
|
+
// It keeps the sentinel tags useful for control flow inside visitMember(..),
|
|
471
|
+
// but prevents them from leaking into the final parsed structure.
|
|
436
472
|
const current = this.sectionStack[this.sectionStack.length - 1];
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
473
|
+
const shouldSkipMemberInsertion = valueNode?.type === 'Undefined' &&
|
|
474
|
+
(valueNode.tag === 'Invalid string literal already reported' ||
|
|
475
|
+
valueNode.tag === 'Missing value already reported' ||
|
|
476
|
+
valueNode.tag === 'Parser syntax error already reported');
|
|
477
|
+
if (!shouldSkipMemberInsertion && valueNode !== undefined) {
|
|
478
|
+
this.putMember(this.errorHandler, ctx, current, resultKey, valueNode, this.onDuplicateKey);
|
|
441
479
|
}
|
|
442
480
|
return valueNode;
|
|
443
481
|
};
|
|
@@ -491,48 +529,104 @@ class ASTBuilder extends YiniParserVisitor_1.default {
|
|
|
491
529
|
* @param ctx the parse tree
|
|
492
530
|
* @return the visitor result
|
|
493
531
|
*/
|
|
494
|
-
|
|
495
|
-
|
|
532
|
+
/*
|
|
533
|
+
visitString_literal = (ctx: String_literalContext): any => {
|
|
534
|
+
let text = ''
|
|
535
|
+
|
|
496
536
|
const pieces = [
|
|
497
537
|
ctx.STRING(),
|
|
498
538
|
...(ctx.string_concat_list()?.map((c) => c.STRING()) ?? []),
|
|
499
|
-
]
|
|
539
|
+
]
|
|
540
|
+
|
|
500
541
|
try {
|
|
501
542
|
for (const token of pieces) {
|
|
502
|
-
const tokenText = token.getText()
|
|
503
|
-
const parsed = this.extractStringKindAndValue(tokenText)
|
|
504
|
-
|
|
505
|
-
let txt = '';
|
|
543
|
+
const tokenText = token.getText()
|
|
544
|
+
const parsed = this.extractStringKindAndValue(tokenText)
|
|
545
|
+
|
|
506
546
|
try {
|
|
507
|
-
text += (
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
547
|
+
text += parseStringLiteral(parsed)
|
|
548
|
+
} catch (err: unknown) {
|
|
549
|
+
const msg = '' + (<any>err)?.message
|
|
550
|
+
this.errorHandler!.pushOrBail(
|
|
551
|
+
toErrorLocation(ctx),
|
|
552
|
+
'Syntax-Error',
|
|
553
|
+
'Parse error in string',
|
|
554
|
+
`${msg}`,
|
|
555
|
+
)
|
|
512
556
|
}
|
|
513
557
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
catch (err) {
|
|
517
|
-
const msg = err instanceof Error ? err.message : String(err)
|
|
518
|
-
|
|
519
|
-
let
|
|
520
|
-
let
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
558
|
+
|
|
559
|
+
return makeScalarValue('String', text)
|
|
560
|
+
} catch (err: unknown) {
|
|
561
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
562
|
+
|
|
563
|
+
let msgWhat = 'Invalid string literal'
|
|
564
|
+
let msgWhy = msg
|
|
565
|
+
let msgHint = ''
|
|
566
|
+
|
|
567
|
+
if (err instanceof CYiniStringParseError) {
|
|
568
|
+
msgWhat = 'Invalid escape sequence in string'
|
|
569
|
+
msgWhy = msg
|
|
570
|
+
|
|
524
571
|
if (/Invalid escape sequence \\\\/.test(msg)) {
|
|
525
572
|
msgHint =
|
|
526
|
-
'Use double backslashes (\\\\) in C-strings, or use a raw string for file paths.'
|
|
527
|
-
}
|
|
528
|
-
else if (/end of string/i.test(msg)) {
|
|
573
|
+
'Use double backslashes (\\\\) in C-strings, or use a raw string for file paths.'
|
|
574
|
+
} else if (/end of string/i.test(msg)) {
|
|
529
575
|
msgHint =
|
|
530
|
-
'Check that all escape sequences in the C-string are complete and valid.'
|
|
576
|
+
'Check that all escape sequences in the C-string are complete and valid.'
|
|
531
577
|
}
|
|
532
578
|
}
|
|
533
|
-
|
|
534
|
-
|
|
579
|
+
|
|
580
|
+
this.errorHandler!.pushOrBail(
|
|
581
|
+
toErrorLocation(ctx),
|
|
582
|
+
'Syntax-Error',
|
|
583
|
+
msgWhat,
|
|
584
|
+
msgWhy,
|
|
585
|
+
msgHint,
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
return makeScalarValue(
|
|
589
|
+
'Undefined',
|
|
590
|
+
undefined,
|
|
591
|
+
'Invalid string literal',
|
|
592
|
+
)
|
|
535
593
|
}
|
|
594
|
+
}
|
|
595
|
+
*/
|
|
596
|
+
this.visitString_literal = (ctx) => {
|
|
597
|
+
let text = '';
|
|
598
|
+
const pieces = [
|
|
599
|
+
ctx.STRING(),
|
|
600
|
+
...(ctx.string_concat_list()?.map((c) => c.STRING()) ?? []),
|
|
601
|
+
];
|
|
602
|
+
for (const token of pieces) {
|
|
603
|
+
const tokenText = token.getText();
|
|
604
|
+
const parsed = this.extractStringKindAndValue(tokenText);
|
|
605
|
+
try {
|
|
606
|
+
text += (0, parseString_1.default)(parsed);
|
|
607
|
+
}
|
|
608
|
+
catch (err) {
|
|
609
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
610
|
+
let msgWhat = 'Parse error in string';
|
|
611
|
+
let msgWhy = msg;
|
|
612
|
+
let msgHint = '';
|
|
613
|
+
if (err instanceof parseString_1.CYiniStringParseError) {
|
|
614
|
+
if (/Invalid escape sequence/i.test(msg)) {
|
|
615
|
+
msgWhat = 'Invalid escape sequence in string';
|
|
616
|
+
msgHint =
|
|
617
|
+
'Use double backslashes (\\\\) in C-strings, or use a raw string if escapes are not needed.';
|
|
618
|
+
}
|
|
619
|
+
else if (/end of string/i.test(msg)) {
|
|
620
|
+
msgWhat = 'Incomplete escape sequence in string';
|
|
621
|
+
msgHint =
|
|
622
|
+
'Check that all escape sequences in the C-string are complete and valid.';
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', msgWhat, msgWhy, msgHint);
|
|
626
|
+
return makeScalarValue('Undefined', undefined, 'Invalid string literal already reported');
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return makeScalarValue('String', text);
|
|
536
630
|
};
|
|
537
631
|
/**
|
|
538
632
|
* Visit a parse tree produced by `YiniParser.number_literal`.
|
|
@@ -741,17 +835,37 @@ class ASTBuilder extends YiniParserVisitor_1.default {
|
|
|
741
835
|
* @param ctx the parse tree
|
|
742
836
|
* @return the visitor result
|
|
743
837
|
*/
|
|
838
|
+
/*
|
|
839
|
+
visitString_concat = (ctx: String_concatContext): any => {
|
|
840
|
+
const rawText = ctx.STRING().getText() // The token text.
|
|
841
|
+
const parsedInput = this.extractStringKindAndValue(rawText)
|
|
842
|
+
// return parseStringLiteral(parsedInput)
|
|
843
|
+
|
|
844
|
+
let txt = ''
|
|
845
|
+
try {
|
|
846
|
+
txt = parseStringLiteral(parsedInput)
|
|
847
|
+
} catch (err) {
|
|
848
|
+
const msg = '' + (<any>err)?.message
|
|
849
|
+
this.errorHandler!.pushOrBail(
|
|
850
|
+
toErrorLocation(ctx),
|
|
851
|
+
'Syntax-Error',
|
|
852
|
+
'Parse error in string',
|
|
853
|
+
`${msg}`,
|
|
854
|
+
)
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
*/
|
|
858
|
+
//@ todo (?) Check that this function actually works, not sure this function is finished.
|
|
744
859
|
this.visitString_concat = (ctx) => {
|
|
745
|
-
const rawText = ctx.STRING().getText();
|
|
860
|
+
const rawText = ctx.STRING().getText();
|
|
746
861
|
const parsedInput = this.extractStringKindAndValue(rawText);
|
|
747
|
-
// return parseStringLiteral(parsedInput)
|
|
748
|
-
let txt = '';
|
|
749
862
|
try {
|
|
750
|
-
|
|
863
|
+
return (0, parseString_1.default)(parsedInput);
|
|
751
864
|
}
|
|
752
865
|
catch (err) {
|
|
753
|
-
const msg =
|
|
754
|
-
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Parse error in string',
|
|
866
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
867
|
+
this.errorHandler.pushOrBail((0, errorDataHandler_1.toErrorLocation)(ctx), 'Syntax-Error', 'Parse error in string', msg);
|
|
868
|
+
return undefined;
|
|
755
869
|
}
|
|
756
870
|
};
|
|
757
871
|
/**
|