smol-toml 0.1.0 → 1.0.1
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/README.md +66 -19
- package/dist/date.d.ts +41 -0
- package/dist/date.js +123 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +97 -38
- package/dist/parse.d.ts +2 -2
- package/dist/parse.js +20 -65
- package/dist/primitive.d.ts +2 -1
- package/dist/primitive.js +38 -30
- package/dist/struct.d.ts +4 -3
- package/dist/struct.js +73 -26
- package/dist/util.d.ts +7 -2
- package/dist/util.js +55 -46
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# smol-toml
|
|
2
|
+
[](https://toml.io/en/v1.0.0)
|
|
2
3
|
[](https://github.com/squirrelchat/smol-toml/blob/mistress/LICENSE)
|
|
3
4
|
[](https://npm.im/smol-toml)
|
|
4
5
|
|
|
5
|
-
A small, fast, and correct TOML parser. smol-toml is (
|
|
6
|
+
A small, fast, and correct TOML parser. smol-toml is fully(ish) spec-compliant with TOML v1.0.0.
|
|
6
7
|
|
|
7
8
|
Why yet another TOML parser? Well, the ecosystem of TOML parsers in JavaScript is quite underwhelming, most likely due
|
|
8
9
|
to a lack of interest. With most parsers being outdated, unmaintained, non-compliant, or a combination of these, a new
|
|
@@ -10,12 +11,31 @@ parser didn't feel too out of place.
|
|
|
10
11
|
|
|
11
12
|
*[insert xkcd 927]*
|
|
12
13
|
|
|
13
|
-
smol-toml
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
14
|
+
smol-toml passes most of the tests from [BurntSushi's `toml-test` suite](https://github.com/BurntSushi/toml-test).
|
|
15
|
+
However, due to the nature of JavaScript and the limits of the language, it doesn't pass certain tests, namely:
|
|
16
|
+
- Invalid UTF-8 strings are not rejected
|
|
17
|
+
- Certain invalid UTF-8 codepoints are not rejected
|
|
18
|
+
- smol-toml doesn't preserve type information between integers and floats (in JS, everything is a float)
|
|
19
|
+
- smol-toml doesn't support the whole 64-bit range for integers (but does throw an appropriate error)
|
|
20
|
+
- As all numbers are floats in JS, the safe range is `2**53 - 1` <=> `-(2**53 - 1)`.
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
smol-toml also passes all of the tests in https://github.com/iarna/toml-spec-tests.
|
|
23
|
+
|
|
24
|
+
<details>
|
|
25
|
+
<summary>List of failed `toml-test` cases</summary>
|
|
26
|
+
These tests were done by modifying `primitive.ts` and make the implementation return bigints for integers. This allows
|
|
27
|
+
verifying the parser correctly intents a number to be an integer or a float.
|
|
28
|
+
|
|
29
|
+
*Ideally, this becomes an option of the library, but for now...*
|
|
30
|
+
|
|
31
|
+
The following tests are failing:
|
|
32
|
+
- invalid/encoding/bad-utf8-in-comment
|
|
33
|
+
- invalid/encoding/bad-utf8-in-multiline-literal
|
|
34
|
+
- invalid/encoding/bad-utf8-in-multiline
|
|
35
|
+
- invalid/encoding/bad-utf8-in-string-literal
|
|
36
|
+
- invalid/encoding/bad-utf8-in-string
|
|
37
|
+
- invalid/string/bad-codepoint
|
|
38
|
+
</details>
|
|
19
39
|
|
|
20
40
|
## Installation
|
|
21
41
|
```
|
|
@@ -43,28 +63,55 @@ The large TOML generator can be found [here](https://gist.github.com/cyyynthia/e
|
|
|
43
63
|
|
|
44
64
|
| | smol-toml | @iarna/toml@3.0.0 | @ltd/j-toml | fast-toml |
|
|
45
65
|
|----------------|---------------------|-------------------|----------------|----------------|
|
|
46
|
-
| Spec example | **
|
|
47
|
-
| ~5MB test file | **
|
|
66
|
+
| Spec example | **71,356.51 op/s** | 33,629.31 op/s | 16,433.86 op/s | 29,421.60 op/s |
|
|
67
|
+
| ~5MB test file | **3.8091 op/s** | *DNF* | 2.4369 op/s | 2.6078 op/s |
|
|
48
68
|
|
|
49
69
|
<details>
|
|
50
70
|
<summary>Detailed benchmark data</summary>
|
|
51
71
|
|
|
52
|
-
Tests ran using Vitest v0.31.0 on commit
|
|
72
|
+
Tests ran using Vitest v0.31.0 on commit 04d233e351f9ae719222154ee2217aea8b95dbab
|
|
53
73
|
|
|
54
74
|
CPU: Intel Core i7 7700K (4.2GHz)
|
|
55
75
|
|
|
56
76
|
```
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
RUN v0.31.0
|
|
78
|
+
|
|
79
|
+
✓ bench/parseSpecExample.bench.ts (4) 2462ms
|
|
59
80
|
name hz min max mean p75 p99 p995 p999 rme samples
|
|
60
|
-
· smol-toml
|
|
61
|
-
· @iarna/toml
|
|
62
|
-
· @ltd/j-toml 16,
|
|
63
|
-
· fast-toml
|
|
64
|
-
|
|
81
|
+
· smol-toml 71,356.51 0.0132 0.2633 0.0140 0.0137 0.0219 0.0266 0.1135 ±0.37% 35679 fastest
|
|
82
|
+
· @iarna/toml 33,629.31 0.0272 0.2629 0.0297 0.0287 0.0571 0.0650 0.1593 ±0.45% 16815
|
|
83
|
+
· @ltd/j-toml 16,433.86 0.0523 1.3088 0.0608 0.0550 0.1140 0.1525 0.7348 ±1.47% 8217 slowest
|
|
84
|
+
· fast-toml 29,421.60 0.0305 0.2995 0.0340 0.0312 0.0618 0.0640 0.1553 ±0.47% 14711
|
|
85
|
+
✓ bench/parseLargeMixed.bench.ts (3) 16062ms
|
|
65
86
|
name hz min max mean p75 p99 p995 p999 rme samples
|
|
66
|
-
· smol-toml
|
|
67
|
-
· @ltd/j-toml 2.
|
|
68
|
-
· fast-toml 2.
|
|
87
|
+
· smol-toml 3.8091 239.60 287.30 262.53 274.17 287.30 287.30 287.30 ±3.66% 10 fastest
|
|
88
|
+
· @ltd/j-toml 2.4369 376.73 493.49 410.35 442.58 493.49 493.49 493.49 ±7.08% 10 slowest
|
|
89
|
+
· fast-toml 2.6078 373.88 412.79 383.47 388.62 412.79 412.79 412.79 ±2.72% 10
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
BENCH Summary
|
|
93
|
+
|
|
94
|
+
smol-toml - bench/parseLargeMixed.bench.ts >
|
|
95
|
+
1.46x faster than fast-toml
|
|
96
|
+
1.56x faster than @ltd/j-toml
|
|
97
|
+
|
|
98
|
+
smol-toml - bench/parseSpecExample.bench.ts >
|
|
99
|
+
2.12x faster than @iarna/toml
|
|
100
|
+
2.43x faster than fast-toml
|
|
101
|
+
4.34x faster than @ltd/j-toml
|
|
69
102
|
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
Additional notes:
|
|
106
|
+
|
|
107
|
+
I initially tried to benchmark `toml-nodejs`, but the 0.3.0 package is broken.
|
|
108
|
+
I initially reported this to the library author, but the author decided to
|
|
109
|
+
- a) advise to use a custom loader (via *experimental* flag) to circumvent the invalid imports.
|
|
110
|
+
- Said flag, `--experimental-specifier-resolution`, has been removed in Node v20.
|
|
111
|
+
- b) [delete the issue](https://github.com/huan231/toml-nodejs/issues/12) when pointed out links to the NodeJS
|
|
112
|
+
documentation about the flag removal and standard resolution algorithm.
|
|
113
|
+
|
|
114
|
+
For the reference anyways, `toml-nodejs` (with proper imports) is ~8x slower on both benchmark with:
|
|
115
|
+
- spec example: 7,543.47 op/s
|
|
116
|
+
- 5mb mixed: 0.7006 op/s
|
|
70
117
|
</details>
|
package/dist/date.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
*
|
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7
|
+
*
|
|
8
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
* list of conditions and the following disclaimer.
|
|
10
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
* this list of conditions and the following disclaimer in the
|
|
12
|
+
* documentation and/or other materials provided with the distribution.
|
|
13
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
14
|
+
* may be used to endorse or promote products derived from this software without
|
|
15
|
+
* specific prior written permission.
|
|
16
|
+
*
|
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
18
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
19
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
20
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
21
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
22
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
23
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
24
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
25
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
export default class TomlDate extends Date {
|
|
29
|
+
#private;
|
|
30
|
+
constructor(date: string | Date);
|
|
31
|
+
isDateTime(): boolean;
|
|
32
|
+
isLocal(): boolean;
|
|
33
|
+
isDate(): boolean;
|
|
34
|
+
isTime(): boolean;
|
|
35
|
+
isValid(): boolean;
|
|
36
|
+
toISOString(): string;
|
|
37
|
+
static wrapAsOffsetDateTime(jsDate: Date, offset?: string): TomlDate;
|
|
38
|
+
static wrapAsLocalDateTime(jsDate: Date): TomlDate;
|
|
39
|
+
static wrapAsLocalDate(jsDate: Date): TomlDate;
|
|
40
|
+
static wrapAsLocalTime(jsDate: Date): TomlDate;
|
|
41
|
+
}
|
package/dist/date.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
*
|
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7
|
+
*
|
|
8
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
* list of conditions and the following disclaimer.
|
|
10
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
* this list of conditions and the following disclaimer in the
|
|
12
|
+
* documentation and/or other materials provided with the distribution.
|
|
13
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
14
|
+
* may be used to endorse or promote products derived from this software without
|
|
15
|
+
* specific prior written permission.
|
|
16
|
+
*
|
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
18
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
19
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
20
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
21
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
22
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
23
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
24
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
25
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
let DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}:\d{2}(?:\.\d+)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
29
|
+
export default class TomlDate extends Date {
|
|
30
|
+
#hasDate = false;
|
|
31
|
+
#hasTime = false;
|
|
32
|
+
#offset = null;
|
|
33
|
+
constructor(date) {
|
|
34
|
+
let hasDate = true;
|
|
35
|
+
let hasTime = true;
|
|
36
|
+
let offset = 'Z';
|
|
37
|
+
if (typeof date === 'string') {
|
|
38
|
+
let match = date.match(DATE_TIME_RE);
|
|
39
|
+
if (match) {
|
|
40
|
+
if (!match[1]) {
|
|
41
|
+
hasDate = false;
|
|
42
|
+
date = `0000-01-01T${date}`;
|
|
43
|
+
}
|
|
44
|
+
hasTime = !!match[2];
|
|
45
|
+
// Do not allow rollover hours
|
|
46
|
+
if (match[2] && +match[2] > 23) {
|
|
47
|
+
date = '';
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
offset = match[3] || null;
|
|
51
|
+
date = date.toUpperCase();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
date = '';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
super(date);
|
|
59
|
+
if (!isNaN(this.getTime())) {
|
|
60
|
+
this.#hasDate = hasDate;
|
|
61
|
+
this.#hasTime = hasTime;
|
|
62
|
+
this.#offset = offset;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
isDateTime() {
|
|
66
|
+
return this.#hasDate && this.#hasTime;
|
|
67
|
+
}
|
|
68
|
+
isLocal() {
|
|
69
|
+
return !this.#hasDate || !this.#hasTime || !this.#offset;
|
|
70
|
+
}
|
|
71
|
+
isDate() {
|
|
72
|
+
return this.#hasDate && !this.#hasTime;
|
|
73
|
+
}
|
|
74
|
+
isTime() {
|
|
75
|
+
return this.#hasTime && !this.#hasDate;
|
|
76
|
+
}
|
|
77
|
+
isValid() {
|
|
78
|
+
return this.#hasDate || this.#hasTime;
|
|
79
|
+
}
|
|
80
|
+
toISOString() {
|
|
81
|
+
let iso = super.toISOString();
|
|
82
|
+
// Local Date
|
|
83
|
+
if (this.isDate())
|
|
84
|
+
return iso.slice(0, 10);
|
|
85
|
+
// Local Time
|
|
86
|
+
if (this.isTime())
|
|
87
|
+
return iso.slice(11, 23);
|
|
88
|
+
// Local DateTime
|
|
89
|
+
if (this.#offset === null)
|
|
90
|
+
return iso.slice(0, -1);
|
|
91
|
+
// Offset DateTime
|
|
92
|
+
if (this.#offset === 'Z')
|
|
93
|
+
return iso;
|
|
94
|
+
// This part is quite annoying: JS strips the original timezone from the ISO string representation
|
|
95
|
+
// Instead of using a "modified" date and "Z", we restore the representation "as authored"
|
|
96
|
+
let offset = (+(this.#offset.slice(1, 3)) * 60) + +(this.#offset.slice(4, 6));
|
|
97
|
+
offset = this.#offset[0] === '-' ? offset : -offset;
|
|
98
|
+
let offsetDate = new Date(this.getTime() - (offset * 60e3));
|
|
99
|
+
return offsetDate.toISOString().slice(0, -1) + this.#offset;
|
|
100
|
+
}
|
|
101
|
+
static wrapAsOffsetDateTime(jsDate, offset = 'Z') {
|
|
102
|
+
let date = new TomlDate(jsDate);
|
|
103
|
+
date.#offset = offset;
|
|
104
|
+
return date;
|
|
105
|
+
}
|
|
106
|
+
static wrapAsLocalDateTime(jsDate) {
|
|
107
|
+
let date = new TomlDate(jsDate);
|
|
108
|
+
date.#offset = null;
|
|
109
|
+
return date;
|
|
110
|
+
}
|
|
111
|
+
static wrapAsLocalDate(jsDate) {
|
|
112
|
+
let date = new TomlDate(jsDate);
|
|
113
|
+
date.#hasTime = false;
|
|
114
|
+
date.#offset = null;
|
|
115
|
+
return date;
|
|
116
|
+
}
|
|
117
|
+
static wrapAsLocalTime(jsDate) {
|
|
118
|
+
let date = new TomlDate(jsDate);
|
|
119
|
+
date.#hasDate = false;
|
|
120
|
+
date.#offset = null;
|
|
121
|
+
return date;
|
|
122
|
+
}
|
|
123
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -25,4 +25,6 @@
|
|
|
25
25
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
import { type TomlPrimitive } from './util.js';
|
|
29
|
+
export { default as TomlDate } from './date.js';
|
|
30
|
+
export declare function parse(toml: string): Record<string, TomlPrimitive>;
|
package/dist/index.js
CHANGED
|
@@ -26,56 +26,115 @@
|
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
import { parseKey } from './struct.js';
|
|
29
|
-
import {
|
|
30
|
-
import { skipVoid
|
|
29
|
+
import { extractValue } from './parse.js';
|
|
30
|
+
import { skipVoid } from './util.js';
|
|
31
31
|
import TomlError from './error.js';
|
|
32
|
+
export { default as TomlDate } from './date.js';
|
|
33
|
+
function peekTable(key, table, meta, type) {
|
|
34
|
+
let t = table;
|
|
35
|
+
let m = meta;
|
|
36
|
+
let k;
|
|
37
|
+
let hasOwn = false;
|
|
38
|
+
let state;
|
|
39
|
+
for (let i = 0; i < key.length; i++) {
|
|
40
|
+
if (i) {
|
|
41
|
+
t = hasOwn ? t[k] : (t[k] = {});
|
|
42
|
+
m = (state = m[k]).c;
|
|
43
|
+
if (type === 0 /* Type.DOTTED */ && state.t === 1 /* Type.EXPLICIT */) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (state.t === 2 /* Type.ARRAY */) {
|
|
47
|
+
let l = t.length - 1;
|
|
48
|
+
t = t[l];
|
|
49
|
+
m = m[l].c;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
k = key[i];
|
|
53
|
+
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 /* Type.DOTTED */ && m[k]?.d) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (!hasOwn) {
|
|
57
|
+
if (k === '__proto__') {
|
|
58
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
59
|
+
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
|
|
60
|
+
}
|
|
61
|
+
m[k] = {
|
|
62
|
+
t: i < key.length - 1 && type === 2 /* Type.ARRAY */
|
|
63
|
+
? 0 /* Type.DOTTED */
|
|
64
|
+
: type,
|
|
65
|
+
d: false,
|
|
66
|
+
i: 0,
|
|
67
|
+
c: {},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
state = m[k];
|
|
72
|
+
if (state.t !== type) {
|
|
73
|
+
// Bad key type!
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
if (type === 2 /* Type.ARRAY */) {
|
|
77
|
+
if (!state.d) {
|
|
78
|
+
state.d = true;
|
|
79
|
+
t[k] = [];
|
|
80
|
+
}
|
|
81
|
+
t[k].push(t = {});
|
|
82
|
+
state.c[state.i++] = (state = { t: 1 /* Type.EXPLICIT */, d: false, i: 0, c: {} });
|
|
83
|
+
}
|
|
84
|
+
if (state.d) {
|
|
85
|
+
// Redefining a table!
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
state.d = true;
|
|
89
|
+
if (type === 1 /* Type.EXPLICIT */) {
|
|
90
|
+
t = hasOwn ? t[k] : (t[k] = {});
|
|
91
|
+
}
|
|
92
|
+
else if (type === 0 /* Type.DOTTED */ && hasOwn) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return [k, t, state.c];
|
|
96
|
+
}
|
|
32
97
|
export function parse(toml) {
|
|
33
98
|
let res = {};
|
|
99
|
+
let meta = {};
|
|
34
100
|
let tbl = res;
|
|
35
|
-
let
|
|
36
|
-
let seenValues = new Set();
|
|
37
|
-
toml = toml.trim();
|
|
101
|
+
let m = meta;
|
|
38
102
|
for (let ptr = skipVoid(toml, 0); ptr < toml.length;) {
|
|
39
103
|
if (toml[ptr] === '[') {
|
|
40
|
-
let isTableArray = toml[ptr
|
|
41
|
-
let
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
throw new TomlError('trying to redefine an already defined table', {
|
|
51
|
-
toml: toml,
|
|
52
|
-
ptr: ptr - 1
|
|
53
|
-
});
|
|
54
|
-
seenTables.add(strKey);
|
|
55
|
-
let r = peekTable(res, k, seenValues, true);
|
|
56
|
-
if (!r) {
|
|
57
|
-
throw new TomlError('trying to redefine an already defined value', {
|
|
58
|
-
toml: toml,
|
|
59
|
-
ptr: ptr - 1
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
let v = r[1][r[0]];
|
|
63
|
-
if (!v) {
|
|
64
|
-
r[1][r[0]] = (v = isTableArray ? [] : {});
|
|
104
|
+
let isTableArray = toml[++ptr] === '[';
|
|
105
|
+
let k = parseKey(toml, ptr += +isTableArray, ']');
|
|
106
|
+
if (isTableArray) {
|
|
107
|
+
if (toml[k[1] - 1] !== ']') {
|
|
108
|
+
throw new TomlError('expected end of table declaration', {
|
|
109
|
+
toml: toml,
|
|
110
|
+
ptr: k[1] - 1,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
k[1]++;
|
|
65
114
|
}
|
|
66
|
-
|
|
67
|
-
|
|
115
|
+
let p = peekTable(k[0], res, meta, isTableArray ? 2 /* Type.ARRAY */ : 1 /* Type.EXPLICIT */);
|
|
116
|
+
if (!p) {
|
|
117
|
+
throw new TomlError('trying to redefine an already defined table or value', {
|
|
68
118
|
toml: toml,
|
|
69
|
-
ptr: ptr
|
|
119
|
+
ptr: ptr,
|
|
70
120
|
});
|
|
71
121
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
ptr = end + +isTableArray;
|
|
122
|
+
m = p[2];
|
|
123
|
+
tbl = p[1];
|
|
124
|
+
ptr = k[1];
|
|
76
125
|
}
|
|
77
126
|
else {
|
|
78
|
-
|
|
127
|
+
let k = parseKey(toml, ptr);
|
|
128
|
+
let p = peekTable(k[0], tbl, m, 0 /* Type.DOTTED */);
|
|
129
|
+
if (!p) {
|
|
130
|
+
throw new TomlError('trying to redefine an already defined table or value', {
|
|
131
|
+
toml: toml,
|
|
132
|
+
ptr: ptr,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
let v = extractValue(toml, k[1]);
|
|
136
|
+
p[1][p[0]] = v[0];
|
|
137
|
+
ptr = v[1];
|
|
79
138
|
}
|
|
80
139
|
ptr = skipVoid(toml, ptr, true);
|
|
81
140
|
if (toml[ptr] && toml[ptr] !== '\n' && toml[ptr] !== '\r') {
|
package/dist/parse.d.ts
CHANGED
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
export declare function
|
|
28
|
+
import { type TomlPrimitive } from './util.js';
|
|
29
|
+
export declare function extractValue(str: string, ptr: number, end?: string): [TomlPrimitive, number];
|
package/dist/parse.js
CHANGED
|
@@ -26,56 +26,37 @@
|
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
import { parseString, parseValue } from './primitive.js';
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
29
|
+
import { parseArray, parseInlineTable } from './struct.js';
|
|
30
|
+
import { indexOfNewline, skipVoid, skipUntil, skipComment, getStringEnd } from './util.js';
|
|
31
31
|
import TomlError from './error.js';
|
|
32
|
-
function getStringEnd(str, seek) {
|
|
33
|
-
let first = str[seek];
|
|
34
|
-
let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2]
|
|
35
|
-
? str.slice(seek, seek + 3)
|
|
36
|
-
: first;
|
|
37
|
-
seek += target.length - 1;
|
|
38
|
-
do
|
|
39
|
-
seek = str.indexOf(target, ++seek);
|
|
40
|
-
while (seek > -1 && first !== "'" && str[seek - 1] === '\\' && str[seek - 2] !== '\\');
|
|
41
|
-
seek += target.length;
|
|
42
|
-
if (target.length > 1) {
|
|
43
|
-
if (str[seek] === first)
|
|
44
|
-
seek++;
|
|
45
|
-
if (str[seek] === first)
|
|
46
|
-
seek++;
|
|
47
|
-
}
|
|
48
|
-
return seek;
|
|
49
|
-
}
|
|
50
32
|
function sliceAndTrimEndOf(str, startPtr, endPtr, allowNewLines) {
|
|
51
33
|
let value = str.slice(startPtr, endPtr);
|
|
52
|
-
let newlineIdx;
|
|
53
34
|
let commentIdx = value.indexOf('#');
|
|
54
35
|
if (commentIdx > -1) {
|
|
36
|
+
// The call to skipComment allows to "validate" the comment
|
|
37
|
+
// (absence of control characters)
|
|
38
|
+
skipComment(str, commentIdx);
|
|
55
39
|
value = value.slice(0, commentIdx);
|
|
56
40
|
}
|
|
57
41
|
let trimmed = value.trimEnd();
|
|
58
42
|
if (!allowNewLines) {
|
|
59
|
-
let
|
|
60
|
-
newlineIdx
|
|
61
|
-
if (newlineIdx < 0)
|
|
62
|
-
newlineIdx = value.lastIndexOf(s = '\r');
|
|
63
|
-
if (trimmed.lastIndexOf(s) !== newlineIdx) {
|
|
43
|
+
let newlineIdx = value.indexOf('\n', trimmed.length);
|
|
44
|
+
if (newlineIdx > -1) {
|
|
64
45
|
throw new TomlError('newlines are not allowed in inline tables', {
|
|
65
46
|
toml: str,
|
|
66
47
|
ptr: startPtr + newlineIdx
|
|
67
48
|
});
|
|
68
49
|
}
|
|
69
50
|
}
|
|
70
|
-
return trimmed;
|
|
51
|
+
return [trimmed, commentIdx];
|
|
71
52
|
}
|
|
72
53
|
export function extractValue(str, ptr, end) {
|
|
73
|
-
let c = str[ptr]
|
|
54
|
+
let c = str[ptr];
|
|
74
55
|
if (c === '[' || c === '{') {
|
|
75
56
|
let [value, endPtr] = c === '['
|
|
76
57
|
? parseArray(str, ptr)
|
|
77
58
|
: parseInlineTable(str, ptr);
|
|
78
|
-
let newPtr = skipUntil(str, endPtr, end);
|
|
59
|
+
let newPtr = skipUntil(str, endPtr, ',', end);
|
|
79
60
|
if (end === '}') {
|
|
80
61
|
let nextNewLine = indexOfNewline(str, endPtr, newPtr);
|
|
81
62
|
if (nextNewLine > -1) {
|
|
@@ -90,48 +71,22 @@ export function extractValue(str, ptr, end) {
|
|
|
90
71
|
let endPtr;
|
|
91
72
|
if (c === '"' || c === "'") {
|
|
92
73
|
endPtr = getStringEnd(str, ptr);
|
|
93
|
-
|
|
94
|
-
return [parseString(str, ptr, endPtr), endPtr + +offset];
|
|
74
|
+
return [parseString(str, ptr, endPtr), endPtr + +(!!end && str[endPtr] === ',')];
|
|
95
75
|
}
|
|
96
|
-
endPtr = skipUntil(str, ptr, end);
|
|
97
|
-
let
|
|
98
|
-
if (!
|
|
76
|
+
endPtr = skipUntil(str, ptr, ',', end);
|
|
77
|
+
let slice = sliceAndTrimEndOf(str, ptr, endPtr - (+(str[endPtr - 1] === ',')), end === ']');
|
|
78
|
+
if (!slice[0]) {
|
|
99
79
|
throw new TomlError('incomplete key-value declaration: no value specified', {
|
|
100
80
|
toml: str,
|
|
101
81
|
ptr: ptr
|
|
102
82
|
});
|
|
103
83
|
}
|
|
84
|
+
if (end && slice[1] > -1) {
|
|
85
|
+
endPtr = skipVoid(str, ptr + slice[1]);
|
|
86
|
+
endPtr += +(str[endPtr] === ',');
|
|
87
|
+
}
|
|
104
88
|
return [
|
|
105
|
-
parseValue(
|
|
106
|
-
endPtr
|
|
89
|
+
parseValue(slice[0], str, ptr),
|
|
90
|
+
endPtr,
|
|
107
91
|
];
|
|
108
92
|
}
|
|
109
|
-
export function extractKeyValue(str, ptr, table, seen, isInline) {
|
|
110
|
-
let equalIdx = str.indexOf('=', ptr);
|
|
111
|
-
if (equalIdx < 0) {
|
|
112
|
-
throw new TomlError('incomplete key-value declaration: no equals sign after the key', {
|
|
113
|
-
toml: str,
|
|
114
|
-
ptr: ptr
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
// KEY
|
|
118
|
-
let t = peekTable(table, parseKey(str, ptr, equalIdx), seen);
|
|
119
|
-
if (!t) {
|
|
120
|
-
throw new TomlError('trying to redefine an already defined value', {
|
|
121
|
-
toml: str,
|
|
122
|
-
ptr: ptr
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
// VALUE
|
|
126
|
-
ptr = skipVoid(str, equalIdx + 1, true, true);
|
|
127
|
-
if (str[ptr] === '\n' || str[ptr] === '\r') {
|
|
128
|
-
throw new TomlError('newlines are not allowed in key-value declarations', {
|
|
129
|
-
toml: str,
|
|
130
|
-
ptr: ptr
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
let e = extractValue(str, ptr, isInline ? '}' : void 0);
|
|
134
|
-
t[1][t[0]] = e[0];
|
|
135
|
-
seen.add(e[0]);
|
|
136
|
-
return e[1];
|
|
137
|
-
}
|
package/dist/primitive.d.ts
CHANGED
|
@@ -25,5 +25,6 @@
|
|
|
25
25
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
|
+
import TomlDate from './date.js';
|
|
28
29
|
export declare function parseString(str: string, ptr?: number, endPtr?: number): string;
|
|
29
|
-
export declare function parseValue(value: string, toml: string, ptr: number): boolean | number |
|
|
30
|
+
export declare function parseValue(value: string, toml: string, ptr: number): boolean | number | TomlDate;
|
package/dist/primitive.js
CHANGED
|
@@ -26,10 +26,11 @@
|
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
import { skipVoid } from './util.js';
|
|
29
|
+
import TomlDate from './date.js';
|
|
29
30
|
import TomlError from './error.js';
|
|
30
|
-
let
|
|
31
|
-
let
|
|
32
|
-
let LEADING_ZERO = /^[+-]?0
|
|
31
|
+
let INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
32
|
+
let FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
33
|
+
let LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
33
34
|
let ESCAPE_REGEX = /^[0-9a-f]{4,8}$/i;
|
|
34
35
|
let ESC_MAP = {
|
|
35
36
|
b: '\b',
|
|
@@ -40,17 +41,6 @@ let ESC_MAP = {
|
|
|
40
41
|
'"': '"',
|
|
41
42
|
'\\': '\\',
|
|
42
43
|
};
|
|
43
|
-
let VALUES_MAP = {
|
|
44
|
-
true: true,
|
|
45
|
-
false: false,
|
|
46
|
-
inf: Infinity,
|
|
47
|
-
'+inf': Infinity,
|
|
48
|
-
'-inf': -Infinity,
|
|
49
|
-
nan: NaN,
|
|
50
|
-
'+nan': NaN,
|
|
51
|
-
'-nan': NaN,
|
|
52
|
-
'-0': 0,
|
|
53
|
-
};
|
|
54
44
|
export function parseString(str, ptr = 0, endPtr = str.length) {
|
|
55
45
|
let isLiteral = str[ptr] === "'";
|
|
56
46
|
let isMultiline = str[ptr++] === str[ptr] && str[ptr] === str[ptr + 1];
|
|
@@ -67,13 +57,15 @@ export function parseString(str, ptr = 0, endPtr = str.length) {
|
|
|
67
57
|
let sliceStart = ptr;
|
|
68
58
|
while (ptr < endPtr - 1) {
|
|
69
59
|
let c = str[ptr++];
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
if (c === '\n' || (c === '\r' && str[ptr] === '\n')) {
|
|
61
|
+
if (!isMultiline) {
|
|
62
|
+
throw new TomlError('newlines are not allowed in strings', {
|
|
63
|
+
toml: str,
|
|
64
|
+
ptr: ptr - 1
|
|
65
|
+
});
|
|
66
|
+
}
|
|
75
67
|
}
|
|
76
|
-
if (c < '\
|
|
68
|
+
else if ((c < '\x20' && c !== '\t') || c === '\x7f') {
|
|
77
69
|
throw new TomlError('control characters are not allowed in strings', {
|
|
78
70
|
toml: str,
|
|
79
71
|
ptr: ptr - 1
|
|
@@ -91,7 +83,7 @@ export function parseString(str, ptr = 0, endPtr = str.length) {
|
|
|
91
83
|
});
|
|
92
84
|
}
|
|
93
85
|
try {
|
|
94
|
-
parsed += String.fromCodePoint(
|
|
86
|
+
parsed += String.fromCodePoint(parseInt(code, 16));
|
|
95
87
|
}
|
|
96
88
|
catch {
|
|
97
89
|
throw new TomlError('invalid unicode escape', {
|
|
@@ -132,19 +124,36 @@ export function parseString(str, ptr = 0, endPtr = str.length) {
|
|
|
132
124
|
return parsed + str.slice(sliceStart, endPtr - 1);
|
|
133
125
|
}
|
|
134
126
|
export function parseValue(value, toml, ptr) {
|
|
135
|
-
|
|
136
|
-
|
|
127
|
+
// Constant values
|
|
128
|
+
if (value === 'true')
|
|
129
|
+
return true;
|
|
130
|
+
if (value === 'false')
|
|
131
|
+
return false;
|
|
132
|
+
if (value === '-inf')
|
|
133
|
+
return -Infinity;
|
|
134
|
+
if (value === 'inf' || value === '+inf')
|
|
135
|
+
return Infinity;
|
|
136
|
+
if (value === 'nan' || value === '+nan' || value === '-nan')
|
|
137
|
+
return NaN;
|
|
137
138
|
// Numbers
|
|
138
|
-
|
|
139
|
+
let isInt;
|
|
140
|
+
if (value === '-0')
|
|
141
|
+
return 0; // Avoid FP representation of -0
|
|
142
|
+
if ((isInt = INT_REGEX.test(value)) || FLOAT_REGEX.test(value)) {
|
|
139
143
|
if (LEADING_ZERO.test(value)) {
|
|
140
144
|
throw new TomlError('leading zeroes are not allowed', {
|
|
141
145
|
toml: toml,
|
|
142
146
|
ptr: ptr
|
|
143
147
|
});
|
|
144
148
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
let numeric = +(value.replace(/_/g, ''));
|
|
150
|
+
if (isNaN(numeric)) {
|
|
151
|
+
throw new TomlError('invalid number', {
|
|
152
|
+
toml: toml,
|
|
153
|
+
ptr: ptr
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (isInt && !Number.isSafeInteger(numeric)) {
|
|
148
157
|
throw new TomlError('integer value cannot be represented losslessly', {
|
|
149
158
|
toml: toml,
|
|
150
159
|
ptr: ptr
|
|
@@ -152,9 +161,8 @@ export function parseValue(value, toml, ptr) {
|
|
|
152
161
|
}
|
|
153
162
|
return numeric;
|
|
154
163
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (isNaN(date.getTime())) {
|
|
164
|
+
let date = new TomlDate(value);
|
|
165
|
+
if (!date.isValid()) {
|
|
158
166
|
throw new TomlError('invalid value', {
|
|
159
167
|
toml: toml,
|
|
160
168
|
ptr: ptr
|
package/dist/struct.d.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
export declare function
|
|
30
|
-
export declare function
|
|
28
|
+
import { type TomlPrimitive } from './util.js';
|
|
29
|
+
export declare function parseKey(str: string, ptr: number, end?: string): [string[], number];
|
|
30
|
+
export declare function parseInlineTable(str: string, ptr: number): [Record<string, TomlPrimitive>, number];
|
|
31
|
+
export declare function parseArray(str: string, ptr: number): [TomlPrimitive[], number];
|
package/dist/struct.js
CHANGED
|
@@ -26,61 +26,81 @@
|
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
import { parseString } from './primitive.js';
|
|
29
|
-
import { extractValue
|
|
30
|
-
import { indexOfNewline } from './util.js';
|
|
29
|
+
import { extractValue } from './parse.js';
|
|
30
|
+
import { skipComment, indexOfNewline, getStringEnd, skipVoid } from './util.js';
|
|
31
31
|
import TomlError from './error.js';
|
|
32
|
-
let KEY_PART_RE = /^[a-zA-Z0-9-_ \t]
|
|
33
|
-
export function parseKey(str,
|
|
34
|
-
let ptr;
|
|
35
|
-
let dot = -1;
|
|
32
|
+
let KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
|
|
33
|
+
export function parseKey(str, ptr, end = '=') {
|
|
34
|
+
let dot = ptr - 1;
|
|
36
35
|
let parsed = [];
|
|
37
|
-
let
|
|
36
|
+
let endPtr = str.indexOf(end, ptr);
|
|
37
|
+
if (endPtr < 0) {
|
|
38
|
+
throw new TomlError('incomplete key-value: cannot find end of key', {
|
|
39
|
+
toml: str,
|
|
40
|
+
ptr: ptr
|
|
41
|
+
});
|
|
42
|
+
}
|
|
38
43
|
do {
|
|
39
|
-
let c =
|
|
44
|
+
let c = str[ptr = ++dot];
|
|
40
45
|
// If it's whitespace, ignore
|
|
41
46
|
if (c !== ' ' && c !== '\t') {
|
|
42
47
|
// If it's a string
|
|
43
48
|
if (c === '"' || c === "'") {
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
if (c === str[ptr + 1] && c === str[ptr + 2]) {
|
|
50
|
+
throw new TomlError('multiline strings are not allowed in keys', {
|
|
51
|
+
toml: str,
|
|
52
|
+
ptr: ptr,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
let eos = getStringEnd(str, ptr);
|
|
56
|
+
if (eos < 0) {
|
|
46
57
|
throw new TomlError('unfinished string encountered', {
|
|
47
58
|
toml: str,
|
|
48
|
-
ptr: ptr
|
|
59
|
+
ptr: ptr,
|
|
49
60
|
});
|
|
50
61
|
}
|
|
51
|
-
dot =
|
|
52
|
-
let
|
|
53
|
-
let newLine = indexOfNewline(
|
|
62
|
+
dot = str.indexOf('.', eos);
|
|
63
|
+
let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
64
|
+
let newLine = indexOfNewline(strEnd);
|
|
54
65
|
if (newLine > -1) {
|
|
55
66
|
throw new TomlError('newlines are not allowed in keys', {
|
|
56
67
|
toml: str,
|
|
57
|
-
ptr: ptr + dot + newLine
|
|
68
|
+
ptr: ptr + dot + newLine,
|
|
58
69
|
});
|
|
59
70
|
}
|
|
60
|
-
if (
|
|
71
|
+
if (strEnd.trimStart()) {
|
|
61
72
|
throw new TomlError('found extra tokens after the string part', {
|
|
62
73
|
toml: str,
|
|
63
|
-
ptr: eos
|
|
74
|
+
ptr: eos,
|
|
64
75
|
});
|
|
65
76
|
}
|
|
66
|
-
|
|
77
|
+
if (endPtr < eos) {
|
|
78
|
+
endPtr = str.indexOf(end, eos);
|
|
79
|
+
if (endPtr < 0) {
|
|
80
|
+
throw new TomlError('incomplete key-value: cannot find end of key', {
|
|
81
|
+
toml: str,
|
|
82
|
+
ptr: ptr,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
parsed.push(parseString(str, ptr, eos));
|
|
67
87
|
}
|
|
68
88
|
else {
|
|
69
89
|
// Normal raw key part consumption and validation
|
|
70
|
-
dot =
|
|
71
|
-
let part =
|
|
90
|
+
dot = str.indexOf('.', ptr);
|
|
91
|
+
let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
72
92
|
if (!KEY_PART_RE.test(part)) {
|
|
73
93
|
throw new TomlError('only letter, numbers, dashes and underscores are allowed in keys', {
|
|
74
94
|
toml: str,
|
|
75
|
-
ptr: ptr
|
|
95
|
+
ptr: ptr,
|
|
76
96
|
});
|
|
77
97
|
}
|
|
78
98
|
parsed.push(part.trimEnd());
|
|
79
99
|
}
|
|
80
100
|
}
|
|
81
101
|
// Until there's no more dot
|
|
82
|
-
} while (dot + 1);
|
|
83
|
-
return parsed;
|
|
102
|
+
} while (dot + 1 && dot < endPtr);
|
|
103
|
+
return [parsed, skipVoid(str, endPtr + 1, true, true)];
|
|
84
104
|
}
|
|
85
105
|
export function parseInlineTable(str, ptr) {
|
|
86
106
|
let res = {};
|
|
@@ -89,7 +109,7 @@ export function parseInlineTable(str, ptr) {
|
|
|
89
109
|
let comma = 0;
|
|
90
110
|
ptr++;
|
|
91
111
|
while ((c = str[ptr++]) !== '}' && c) {
|
|
92
|
-
if (c === '\n'
|
|
112
|
+
if (c === '\n') {
|
|
93
113
|
throw new TomlError('newlines are not allowed in inline tables', {
|
|
94
114
|
toml: str,
|
|
95
115
|
ptr: ptr - 1
|
|
@@ -108,7 +128,34 @@ export function parseInlineTable(str, ptr) {
|
|
|
108
128
|
});
|
|
109
129
|
}
|
|
110
130
|
else if (c !== ' ' && c !== '\t') {
|
|
111
|
-
|
|
131
|
+
let k;
|
|
132
|
+
let t = res;
|
|
133
|
+
let hasOwn = false;
|
|
134
|
+
let [key, keyEndPtr] = parseKey(str, ptr - 1);
|
|
135
|
+
for (let i = 0; i < key.length; i++) {
|
|
136
|
+
if (i)
|
|
137
|
+
t = hasOwn ? t[k] : (t[k] = {});
|
|
138
|
+
k = key[i];
|
|
139
|
+
if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== 'object' || seen.has(t[k]))) {
|
|
140
|
+
throw new TomlError('trying to redefine an already defined value', {
|
|
141
|
+
toml: str,
|
|
142
|
+
ptr: ptr
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (!hasOwn && k === '__proto__') {
|
|
146
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (hasOwn) {
|
|
150
|
+
throw new TomlError('trying to redefine an already defined value', {
|
|
151
|
+
toml: str,
|
|
152
|
+
ptr: ptr
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
let [value, valueEndPtr] = extractValue(str, keyEndPtr, '}');
|
|
156
|
+
seen.add(value);
|
|
157
|
+
t[k] = value;
|
|
158
|
+
ptr = valueEndPtr;
|
|
112
159
|
comma = str[ptr - 1] === ',' ? ptr - 1 : 0;
|
|
113
160
|
}
|
|
114
161
|
}
|
|
@@ -138,7 +185,7 @@ export function parseArray(str, ptr) {
|
|
|
138
185
|
});
|
|
139
186
|
}
|
|
140
187
|
else if (c === '#')
|
|
141
|
-
ptr =
|
|
188
|
+
ptr = skipComment(str, ptr);
|
|
142
189
|
else if (c !== ' ' && c !== '\t' && c !== '\n' && c !== '\r') {
|
|
143
190
|
let e = extractValue(str, ptr - 1, ']');
|
|
144
191
|
res.push(e[0]);
|
package/dist/util.d.ts
CHANGED
|
@@ -25,7 +25,12 @@
|
|
|
25
25
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
26
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
|
+
import TomlDate from './date.js';
|
|
29
|
+
export type TomlPrimitive = string | number | boolean | TomlDate | {
|
|
30
|
+
[key: string]: TomlPrimitive;
|
|
31
|
+
} | TomlPrimitive[];
|
|
28
32
|
export declare function indexOfNewline(str: string, start?: number, end?: number): number;
|
|
33
|
+
export declare function skipComment(str: string, ptr: number): number;
|
|
29
34
|
export declare function skipVoid(str: string, ptr: number, banNewLines?: boolean, banComments?: boolean): number;
|
|
30
|
-
export declare function skipUntil(str: string, ptr: number, end?: string): number;
|
|
31
|
-
export declare function
|
|
35
|
+
export declare function skipUntil(str: string, ptr: number, sep: string, end?: string): number;
|
|
36
|
+
export declare function getStringEnd(str: string, seek: number): number;
|
package/dist/util.js
CHANGED
|
@@ -27,65 +27,74 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import TomlError from './error.js';
|
|
29
29
|
export function indexOfNewline(str, start = 0, end = str.length) {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
let idx = str.indexOf('\n', start);
|
|
31
|
+
if (str[idx - 1] === '\r')
|
|
32
|
+
idx--;
|
|
33
|
+
return idx <= end ? idx : -1;
|
|
34
|
+
}
|
|
35
|
+
export function skipComment(str, ptr) {
|
|
36
|
+
for (let i = ptr; i < str.length; i++) {
|
|
37
|
+
let c = str[i];
|
|
38
|
+
if (c === '\n')
|
|
32
39
|
return i;
|
|
40
|
+
if (c === '\r' && str[i + 1] === '\n')
|
|
41
|
+
return i + 1;
|
|
42
|
+
if ((c < '\x20' && c !== '\t') || c === '\x7f') {
|
|
43
|
+
throw new TomlError('control characters are not allowed in comments', {
|
|
44
|
+
toml: str,
|
|
45
|
+
ptr: ptr,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
33
48
|
}
|
|
34
|
-
return
|
|
49
|
+
return str.length;
|
|
35
50
|
}
|
|
36
51
|
export function skipVoid(str, ptr, banNewLines, banComments) {
|
|
37
52
|
let c;
|
|
38
|
-
while ((c = str[ptr]) === ' ' || c === '\t' || (!banNewLines && (c === '\n' || c === '\r')))
|
|
53
|
+
while ((c = str[ptr]) === ' ' || c === '\t' || (!banNewLines && (c === '\n' || c === '\r' && str[ptr + 1] === '\n')))
|
|
39
54
|
ptr++;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return ptr < 0 ? str.length : skipVoid(str, ptr, banNewLines);
|
|
55
|
+
return banComments || c !== '#'
|
|
56
|
+
? ptr
|
|
57
|
+
: skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
44
58
|
}
|
|
45
|
-
export function skipUntil(str, ptr, end) {
|
|
59
|
+
export function skipUntil(str, ptr, sep, end) {
|
|
46
60
|
if (!end) {
|
|
47
61
|
ptr = indexOfNewline(str, ptr);
|
|
48
62
|
return ptr < 0 ? str.length : ptr;
|
|
49
63
|
}
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
toml: str,
|
|
55
|
-
ptr: ptr
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
let nextSep = str.indexOf(',', ptr) + 1;
|
|
59
|
-
return !nextSep || nextEnd < nextSep ? nextEnd : nextSep;
|
|
60
|
-
}
|
|
61
|
-
let DESCRIPTOR = { enumerable: true, configurable: true, writable: true };
|
|
62
|
-
export function peekTable(table, key, seen, allowSuper) {
|
|
63
|
-
let k = '';
|
|
64
|
-
let v;
|
|
65
|
-
let hasOwn;
|
|
66
|
-
let hadOwn;
|
|
67
|
-
for (let i = 0; i < key.length; i++) {
|
|
68
|
-
if (i) {
|
|
69
|
-
if (!(hadOwn = hasOwn)) {
|
|
70
|
-
if (k === '__proto__')
|
|
71
|
-
Object.defineProperty(table, k, DESCRIPTOR);
|
|
72
|
-
table[k] = {};
|
|
73
|
-
}
|
|
74
|
-
table = table[k];
|
|
75
|
-
if (Array.isArray(table))
|
|
76
|
-
table = table[table.length - 1];
|
|
64
|
+
for (let i = ptr; i < str.length; i++) {
|
|
65
|
+
let c = str[i];
|
|
66
|
+
if (c === '#') {
|
|
67
|
+
i = indexOfNewline(str, i);
|
|
77
68
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
return
|
|
69
|
+
else if (c === sep) {
|
|
70
|
+
return i + 1;
|
|
71
|
+
}
|
|
72
|
+
else if (c === end) {
|
|
73
|
+
return i;
|
|
83
74
|
}
|
|
84
75
|
}
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
throw new TomlError('cannot find end of structure', {
|
|
77
|
+
toml: str,
|
|
78
|
+
ptr: ptr
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function getStringEnd(str, seek) {
|
|
82
|
+
let first = str[seek];
|
|
83
|
+
let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2]
|
|
84
|
+
? str.slice(seek, seek + 3)
|
|
85
|
+
: first;
|
|
86
|
+
seek += target.length - 1;
|
|
87
|
+
do
|
|
88
|
+
seek = str.indexOf(target, ++seek);
|
|
89
|
+
while (seek > -1 && first !== "'" && str[seek - 1] === '\\' && str[seek - 2] !== '\\');
|
|
90
|
+
if (seek > -1) {
|
|
91
|
+
seek += target.length;
|
|
92
|
+
if (target.length > 1) {
|
|
93
|
+
if (str[seek] === first)
|
|
94
|
+
seek++;
|
|
95
|
+
if (str[seek] === first)
|
|
96
|
+
seek++;
|
|
97
|
+
}
|
|
87
98
|
}
|
|
88
|
-
|
|
89
|
-
Object.defineProperty(table, k, DESCRIPTOR);
|
|
90
|
-
return [k, table];
|
|
99
|
+
return seek;
|
|
91
100
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smol-toml",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"keywords": [
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"toml",
|
|
6
|
+
"parser"
|
|
7
|
+
],
|
|
5
8
|
"description": "A small, fast, and correct TOML parser",
|
|
6
9
|
"repository": "git@github.com:squirrelchat/smol-toml.git",
|
|
7
10
|
"author": "Cynthia <cyyynthia@borkenware.com>",
|
|
@@ -9,7 +12,7 @@
|
|
|
9
12
|
"type": "module",
|
|
10
13
|
"engines": {
|
|
11
14
|
"node": ">= 18",
|
|
12
|
-
"pnpm": ">=
|
|
15
|
+
"pnpm": ">= 8"
|
|
13
16
|
},
|
|
14
17
|
"scripts": {
|
|
15
18
|
"build": "tsc",
|