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