@bablr/language-en-json 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/grammar.js +134 -1289
- package/lib/grammar.macro.js +155 -90
- package/package.json +14 -8
package/lib/grammar.macro.js
CHANGED
|
@@ -1,50 +1,52 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { re, spam as m } from '@bablr/boot';
|
|
2
2
|
import { triviaEnhancer } from '@bablr/helpers/trivia';
|
|
3
3
|
import * as productions from '@bablr/helpers/productions';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
CoveredBy,
|
|
8
|
-
AllowEmpty,
|
|
9
|
-
InjectFrom,
|
|
10
|
-
UnboundAttributes,
|
|
11
|
-
} from '@bablr/helpers/decorators';
|
|
4
|
+
import { o, eat, eatMatch, match, fail } from '@bablr/helpers/grammar';
|
|
5
|
+
import { buildString, buildIdentifier } from '@bablr/helpers/builders';
|
|
6
|
+
import { Node, CoveredBy, AllowEmpty, InjectFrom } from '@bablr/helpers/decorators';
|
|
12
7
|
import * as Space from '@bablr/language-en-blank-space';
|
|
8
|
+
import { getEmbeddedObject } from '@bablr/agast-vm-helpers/deembed';
|
|
13
9
|
|
|
14
10
|
export const dependencies = { Space };
|
|
15
11
|
|
|
16
|
-
export const canonicalURL = 'https://
|
|
12
|
+
export const canonicalURL = 'https://bablr.org/languages/core/en/json';
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
const escapables = new Map(
|
|
19
15
|
Object.entries({
|
|
20
|
-
b: '\b',
|
|
21
|
-
f: '\f',
|
|
16
|
+
b: '\b', // these two escapes are antiquated
|
|
17
|
+
f: '\f', // but giving their meaning away could be confusing
|
|
22
18
|
n: '\n',
|
|
23
19
|
r: '\r',
|
|
24
20
|
t: '\t',
|
|
25
|
-
|
|
26
|
-
'/': '/',
|
|
21
|
+
0: '\0',
|
|
27
22
|
}),
|
|
28
23
|
);
|
|
29
24
|
|
|
25
|
+
function first(iter) {
|
|
26
|
+
for (let value of iter) return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
30
29
|
export const getCooked = (escapeNode, span, ctx) => {
|
|
31
30
|
let cooked;
|
|
32
31
|
const codeNode = escapeNode.get('code');
|
|
33
|
-
const type = ctx.sourceTextFor(codeNode.get('typeToken'));
|
|
34
|
-
const value = ctx.sourceTextFor(codeNode.get('value'));
|
|
35
|
-
|
|
36
|
-
if (!span.startsWith('String')) {
|
|
37
|
-
throw new Error('not implemented');
|
|
38
|
-
}
|
|
39
32
|
|
|
40
|
-
if (
|
|
33
|
+
if (first(codeNode.children)?.value.flags.token) {
|
|
41
34
|
const match_ = ctx.sourceTextFor(codeNode);
|
|
42
35
|
|
|
43
36
|
cooked = escapables.get(match_) || match_;
|
|
44
|
-
} else if (type === 'u') {
|
|
45
|
-
cooked = parseInt(value, 16);
|
|
46
37
|
} else {
|
|
47
|
-
|
|
38
|
+
const type = ctx.sourceTextFor(codeNode.get('typeToken'));
|
|
39
|
+
const value = ctx.sourceTextFor(codeNode.get('value'));
|
|
40
|
+
|
|
41
|
+
if (!span.startsWith('String')) {
|
|
42
|
+
throw new Error('not implemented');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (type === 'u') {
|
|
46
|
+
cooked = parseInt(value, 16);
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error();
|
|
49
|
+
}
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
return cooked.toString(10);
|
|
@@ -54,160 +56,208 @@ export const grammar = triviaEnhancer(
|
|
|
54
56
|
{
|
|
55
57
|
triviaIsAllowed: (s) => s.span === 'Bare',
|
|
56
58
|
*eatMatchTrivia() {
|
|
57
|
-
if (yield
|
|
58
|
-
yield
|
|
59
|
+
if (yield match(re`/[ \n\r\t]/`)) {
|
|
60
|
+
yield eat(m`#: <*Space:Space />`);
|
|
59
61
|
}
|
|
60
62
|
},
|
|
61
63
|
},
|
|
62
64
|
class JSONGrammar {
|
|
63
|
-
*[Symbol.for('@bablr/fragment')]() {
|
|
65
|
+
*[Symbol.for('@bablr/fragment')]({ value }) {
|
|
64
66
|
// needed for the trivia plugin
|
|
65
|
-
yield
|
|
67
|
+
yield eat(m`<${buildIdentifier(value.productionName)} />`);
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
@CoveredBy('Element')
|
|
69
71
|
*Expression() {
|
|
70
|
-
yield
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
yield eat(m`<Any />`, [
|
|
73
|
+
m`<Array '[' />`,
|
|
74
|
+
m`<Object '{' />`,
|
|
75
|
+
m`<String /['"]/ />`,
|
|
76
|
+
m`<Number /-?\d/ {span: 'Number'} />`,
|
|
77
|
+
m`<Infinity 'Infinity' />`,
|
|
78
|
+
m`<Null 'null' />`,
|
|
79
|
+
m`<Boolean /true|false/ />`,
|
|
80
|
+
]);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
@CoveredBy('Expression')
|
|
81
84
|
@Node
|
|
82
85
|
*Array() {
|
|
83
|
-
yield
|
|
84
|
-
yield
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
yield eat(m`openToken: <*Punctuator '[' { balanced: ']' } />`);
|
|
87
|
+
yield eat(
|
|
88
|
+
m`<List />`,
|
|
89
|
+
o({
|
|
90
|
+
element: m`elements[]$: <Expression />`,
|
|
91
|
+
separator: m`separatorTokens[]: <*Punctuator ',' />`,
|
|
92
|
+
allowTrailingSeparator: false,
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
yield eat(m`closeToken: <*Punctuator ']' { balancer: true } />`);
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
@CoveredBy('Expression')
|
|
93
99
|
@Node
|
|
94
100
|
*Object() {
|
|
95
|
-
yield
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
yield eat(m`openToken: <*Punctuator '{' { balanced: '}' } />`);
|
|
102
|
+
let sep = true;
|
|
103
|
+
|
|
104
|
+
yield eatMatch(m`separatorTokens[]: []`);
|
|
105
|
+
yield eatMatch(m`properties[]$: []`);
|
|
106
|
+
|
|
107
|
+
while (sep && (yield match(re`/.|\g/s`))) {
|
|
108
|
+
let suppressGap = !!(yield match(m`<All />`, [
|
|
109
|
+
m`key: <//>`,
|
|
110
|
+
m`sigilToken: <*Punctuator ':' />`,
|
|
111
|
+
]));
|
|
112
|
+
yield eat(m`properties[]$: <Property />`, null, o({ suppressGap }));
|
|
113
|
+
sep = yield eatMatch(m`separatorTokens[]: <*Punctuator ',' />`);
|
|
114
|
+
}
|
|
115
|
+
yield eat(m`closeToken: <*Punctuator '}' { balancer: true } />`);
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
@Node
|
|
105
119
|
*Property() {
|
|
106
|
-
yield
|
|
107
|
-
yield
|
|
108
|
-
yield
|
|
120
|
+
yield eat(m`key$: <*Identifier />`);
|
|
121
|
+
yield eat(m`sigilToken: <*Punctuator ':' />`);
|
|
122
|
+
yield eat(m`value$: <Expression />`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@Node
|
|
126
|
+
*Identifier() {
|
|
127
|
+
yield eat(re`/[a-zA-Z][a-zA-Z_-]*/`);
|
|
109
128
|
}
|
|
110
129
|
|
|
111
|
-
@CoveredBy('Language')
|
|
112
130
|
@Node
|
|
113
|
-
*String() {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
*String({ ctx }) {
|
|
132
|
+
let q = yield match(re`/['"]/`);
|
|
133
|
+
|
|
134
|
+
if (!q) yield fail();
|
|
135
|
+
|
|
136
|
+
const q_ = ctx.sourceTextFor(q);
|
|
137
|
+
|
|
138
|
+
yield q_ === "'"
|
|
139
|
+
? eat(m`openToken: <*Punctuator "'" { balanced: "'", balancedSpan: 'String:Single' } />`)
|
|
140
|
+
: eat(m`openToken: <*Punctuator '"' { balanced: '"', balancedSpan: 'String:Double' } />`);
|
|
141
|
+
|
|
142
|
+
yield eat(m`content$: <*StringContent />`);
|
|
143
|
+
|
|
144
|
+
yield q_ === "'"
|
|
145
|
+
? eat(m`closeToken: <*Punctuator "'" { balancer: true } />`)
|
|
146
|
+
: eat(m`closeToken: <*Punctuator '"' { balancer: true } />`);
|
|
117
147
|
}
|
|
118
148
|
|
|
119
149
|
@AllowEmpty
|
|
120
150
|
@Node
|
|
121
|
-
*StringContent() {
|
|
151
|
+
*StringContent({ state: { span } }) {
|
|
122
152
|
let esc, lit;
|
|
123
153
|
do {
|
|
124
|
-
esc = (yield
|
|
125
|
-
lit =
|
|
154
|
+
esc = (yield match('\\')) && (yield eat(m`@: <EscapeSequence />`));
|
|
155
|
+
lit =
|
|
156
|
+
span === 'String:Single'
|
|
157
|
+
? yield eatMatch(re`/[^\r\n\\'\g]+/`)
|
|
158
|
+
: yield eatMatch(re`/[^\r\n\\"\g]+/`);
|
|
126
159
|
} while (esc || lit);
|
|
127
160
|
}
|
|
128
161
|
|
|
129
162
|
@Node
|
|
130
163
|
*EscapeSequence({ state: { span }, ctx }) {
|
|
131
164
|
if (!span.startsWith('String')) {
|
|
132
|
-
yield
|
|
165
|
+
yield fail();
|
|
133
166
|
}
|
|
134
167
|
|
|
135
|
-
yield
|
|
168
|
+
yield eat(m`sigilToken: <*Punctuator '\\' { openSpan: 'Escape' } />`);
|
|
136
169
|
|
|
137
|
-
let
|
|
170
|
+
let match_;
|
|
138
171
|
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
172
|
+
if (
|
|
173
|
+
(match_ =
|
|
174
|
+
span === 'String:Single' ? yield match(re`/[\\/nrt0']/`) : yield match(re`/[\\/nrt0"]/`))
|
|
175
|
+
) {
|
|
176
|
+
const matchText = ctx.sourceTextFor(match_);
|
|
177
|
+
yield eat(m`code: <*Keyword ${buildString(matchText)} { closeSpan: 'Escape' } />`);
|
|
178
|
+
} else if (yield match('u')) {
|
|
179
|
+
yield eat(m`code: <EscapeCode { closeSpan: 'Escape' } />`);
|
|
144
180
|
} else {
|
|
145
|
-
yield
|
|
181
|
+
yield fail();
|
|
146
182
|
}
|
|
147
183
|
}
|
|
148
184
|
|
|
149
185
|
@Node
|
|
150
186
|
*EscapeCode() {
|
|
151
|
-
yield
|
|
152
|
-
|
|
187
|
+
if (yield eatMatch(m`typeToken: <*Keyword 'u' />`)) {
|
|
188
|
+
if (yield eatMatch(m`openToken: <*Punctuator '{' { balanced: '}' } />`)) {
|
|
189
|
+
yield eat(m`value$: <*UnsignedInteger />`);
|
|
190
|
+
yield eat(m`closeToken: <*Punctuator '}' { balancer: true } />`);
|
|
191
|
+
} else {
|
|
192
|
+
yield eat(m`value$: <*UnsignedInteger /\d{4}/ />`);
|
|
193
|
+
yield eat(m`closeToken: null`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
153
196
|
}
|
|
154
197
|
|
|
155
198
|
@CoveredBy('Expression')
|
|
156
199
|
@Node
|
|
157
200
|
*Number() {
|
|
158
|
-
yield
|
|
201
|
+
yield eat(m`wholePart$: <Integer />`, o({ noDoubleZero: true, matchSign: '-' }));
|
|
159
202
|
|
|
160
|
-
let fs = yield
|
|
203
|
+
let fs = yield eatMatch(m`fractionalSeparatorToken: <*Punctuator '.' />`);
|
|
161
204
|
|
|
162
205
|
if (fs) {
|
|
163
|
-
yield
|
|
206
|
+
yield eat(m`fractionalPart$: <Integer />`);
|
|
164
207
|
} else {
|
|
165
|
-
yield
|
|
208
|
+
yield eat(m`fractionalPart$: null`);
|
|
166
209
|
}
|
|
167
210
|
|
|
168
|
-
let es = yield
|
|
211
|
+
let es = yield eatMatch(m`exponentSeparatorToken: <*Punctuator /[eE]/ />`);
|
|
169
212
|
|
|
170
213
|
if (es) {
|
|
171
|
-
yield
|
|
214
|
+
yield eat(m`exponentPart$: <Integer />`, { matchSign: /[+-]/ });
|
|
172
215
|
} else {
|
|
173
|
-
yield
|
|
216
|
+
yield eat(m`exponentPart$: null`);
|
|
174
217
|
}
|
|
175
218
|
}
|
|
176
219
|
|
|
177
220
|
@Node
|
|
178
|
-
*Integer({ value: props
|
|
179
|
-
const { matchSign = null, noDoubleZero = false } =
|
|
221
|
+
*Integer({ value: props }) {
|
|
222
|
+
const { matchSign = null, noDoubleZero = false } = props ? getEmbeddedObject(props) : {};
|
|
180
223
|
|
|
181
224
|
if (matchSign) {
|
|
182
|
-
yield
|
|
225
|
+
yield eatMatch(m`signToken: <*Punctuator ${buildString(matchSign)} />`);
|
|
183
226
|
} else {
|
|
184
|
-
yield
|
|
227
|
+
yield eat(m`signToken: null`);
|
|
185
228
|
}
|
|
186
229
|
|
|
187
|
-
yield
|
|
230
|
+
yield eat(m`value$: <*UnsignedInteger />`, o({ noDoubleZero }));
|
|
188
231
|
}
|
|
189
232
|
|
|
190
233
|
@Node
|
|
191
234
|
*UnsignedInteger({ value: props, ctx }) {
|
|
192
|
-
const { noDoubleZero = false } =
|
|
235
|
+
const { noDoubleZero = false } = props || {};
|
|
193
236
|
|
|
194
|
-
let
|
|
237
|
+
let firstDigit = ctx.sourceTextFor(yield eat(re`/\d/`));
|
|
195
238
|
|
|
196
239
|
if (!noDoubleZero || firstDigit.value !== '0') {
|
|
197
|
-
yield
|
|
240
|
+
yield eatMatch(re`/\d+/`);
|
|
198
241
|
}
|
|
199
242
|
}
|
|
200
243
|
|
|
244
|
+
@CoveredBy('Expression')
|
|
245
|
+
@Node
|
|
246
|
+
*Infinity() {
|
|
247
|
+
yield eatMatch(m`signToken: <*Punctuator '-' />`);
|
|
248
|
+
yield eat(m`sigilToken: <*Keyword 'Infinity' />`);
|
|
249
|
+
}
|
|
250
|
+
|
|
201
251
|
@CoveredBy('Expression')
|
|
202
252
|
@Node
|
|
203
253
|
*Boolean() {
|
|
204
|
-
yield
|
|
254
|
+
yield eat(m`sigilToken: <*Keyword /true|false/ />`);
|
|
205
255
|
}
|
|
206
256
|
|
|
207
257
|
@CoveredBy('Expression')
|
|
208
258
|
@Node
|
|
209
259
|
*Null() {
|
|
210
|
-
yield
|
|
260
|
+
yield eat(m`sigilToken: <*Keyword 'null' />`);
|
|
211
261
|
}
|
|
212
262
|
|
|
213
263
|
@Node
|
|
@@ -224,5 +274,20 @@ export const grammar = triviaEnhancer(
|
|
|
224
274
|
|
|
225
275
|
@InjectFrom(productions)
|
|
226
276
|
*Any() {}
|
|
277
|
+
|
|
278
|
+
@InjectFrom(productions)
|
|
279
|
+
*All() {}
|
|
227
280
|
},
|
|
228
281
|
);
|
|
282
|
+
|
|
283
|
+
export const YJSON = (self) => {
|
|
284
|
+
return {
|
|
285
|
+
dependencies: {
|
|
286
|
+
...dependencies,
|
|
287
|
+
Y: self,
|
|
288
|
+
},
|
|
289
|
+
canonicalURL,
|
|
290
|
+
getCooked,
|
|
291
|
+
grammar,
|
|
292
|
+
};
|
|
293
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bablr/language-en-json",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A BABLR language for JSON",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=12.0.0"
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
".": "./lib/grammar.js",
|
|
11
11
|
"./package.json": "./package.json"
|
|
12
12
|
},
|
|
13
|
-
"files": [
|
|
13
|
+
"files": [
|
|
14
|
+
"lib/**/*.js"
|
|
15
|
+
],
|
|
14
16
|
"scripts": {
|
|
15
17
|
"build": "macrome build",
|
|
16
18
|
"watch": "macrome watch",
|
|
@@ -19,11 +21,11 @@
|
|
|
19
21
|
},
|
|
20
22
|
"sideEffects": false,
|
|
21
23
|
"dependencies": {
|
|
22
|
-
"@bablr/agast-helpers": "
|
|
23
|
-
"@bablr/agast-vm-helpers": "
|
|
24
|
-
"@bablr/boot": "
|
|
25
|
-
"@bablr/helpers": "
|
|
26
|
-
"@bablr/language-en-blank-space": "
|
|
24
|
+
"@bablr/agast-helpers": "0.6.0",
|
|
25
|
+
"@bablr/agast-vm-helpers": "0.6.0",
|
|
26
|
+
"@bablr/boot": "0.7.0",
|
|
27
|
+
"@bablr/helpers": "0.21.1",
|
|
28
|
+
"@bablr/language-en-blank-space": "0.6.0",
|
|
27
29
|
"@babel/runtime": "^7.22.15"
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
@@ -41,7 +43,11 @@
|
|
|
41
43
|
"mocha": "^10.4.0",
|
|
42
44
|
"prettier": "^2.0.5"
|
|
43
45
|
},
|
|
44
|
-
"keywords": [
|
|
46
|
+
"keywords": [
|
|
47
|
+
"bablr-language",
|
|
48
|
+
"grammar",
|
|
49
|
+
"english"
|
|
50
|
+
],
|
|
45
51
|
"repository": "git@github.com:bablr-lang/language-en-json.git",
|
|
46
52
|
"homepage": "https://github.com/bablr-lang/language-en-json",
|
|
47
53
|
"author": "Conrad Buck <conartist6@gmail.com>",
|