@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.
@@ -1,50 +1,52 @@
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 } 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://github.com/bablr-lang/language-en-json';
12
+ export const canonicalURL = 'https://bablr.org/languages/core/en/json';
17
13
 
18
- export const escapables = new Map(
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 (!type) {
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
- throw new Error();
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 i`match(/[ \n\r\t]/)`) {
58
- yield i`eat(<#*Space:Space />)`;
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 i`eat(< />)`;
67
+ yield eat(m`<${buildIdentifier(value.productionName)} />`);
66
68
  }
67
69
 
68
70
  @CoveredBy('Element')
69
71
  *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
- ])`;
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 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')`;
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 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')`;
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 i`eat(<String /> 'key$')`;
107
- yield i`eat(<*Punctuator ':' /> 'sigilToken')`;
108
- yield i`eat(<Expression /> 'value$')`;
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
- yield i`eat(<*Punctuator '"' balanced='"' balancedSpan='String' /> 'openToken')`;
115
- yield i`eat(<*StringContent /> 'content')`;
116
- yield i`eat(<*Punctuator '"' balancer /> 'closeToken')`;
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 i`match('\\')`) && (yield i`eat(<@EscapeSequence />)`);
125
- lit = yield i`eatMatch(/[^\r\n\\"\g]+/)`;
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 i`fail()`;
165
+ yield fail();
133
166
  }
134
167
 
135
- yield i`eat(<*Punctuator '\\' openSpan='Escape' /> 'sigilToken')`;
168
+ yield eat(m`sigilToken: <*Punctuator '\\' { openSpan: 'Escape' } />`);
136
169
 
137
- let match;
170
+ let match_;
138
171
 
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')`;
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 i`fail()`;
181
+ yield fail();
146
182
  }
147
183
  }
148
184
 
149
185
  @Node
150
186
  *EscapeCode() {
151
- yield i`eat(<*Keyword 'u' /> 'typeToken')`;
152
- yield i`eat(<*UnsignedInteger /> 'value$')`;
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 i`eat(<Integer /> 'wholePart' { noDoubleZero: true matchSign: '-' })`;
201
+ yield eat(m`wholePart$: <Integer />`, o({ noDoubleZero: true, matchSign: '-' }));
159
202
 
160
- let fs = yield i`eatMatch(<*Punctuator '.' /> 'fractionalSeparatorToken')`;
203
+ let fs = yield eatMatch(m`fractionalSeparatorToken: <*Punctuator '.' />`);
161
204
 
162
205
  if (fs) {
163
- yield i`eat(<Integer /> 'fractionalPart')`;
206
+ yield eat(m`fractionalPart$: <Integer />`);
164
207
  } else {
165
- yield i`eat(null 'fractionalPart')`;
208
+ yield eat(m`fractionalPart$: null`);
166
209
  }
167
210
 
168
- let es = yield i`eatMatch(<*Punctuator /[eE]/ /> 'exponentSeparatorToken')`;
211
+ let es = yield eatMatch(m`exponentSeparatorToken: <*Punctuator /[eE]/ />`);
169
212
 
170
213
  if (es) {
171
- yield i`eat(<Integer /> 'exponentPart' { matchSign: /[+-]/ })`;
214
+ yield eat(m`exponentPart$: <Integer />`, { matchSign: /[+-]/ });
172
215
  } else {
173
- yield i`eat(null 'exponentPart')`;
216
+ yield eat(m`exponentPart$: null`);
174
217
  }
175
218
  }
176
219
 
177
220
  @Node
178
- *Integer({ value: props, ctx }) {
179
- const { matchSign = null, noDoubleZero = false } = (props && ctx.unbox(props)) || {};
221
+ *Integer({ value: props }) {
222
+ const { matchSign = null, noDoubleZero = false } = props ? getEmbeddedObject(props) : {};
180
223
 
181
224
  if (matchSign) {
182
- yield i`eatMatch(<*Punctuator ${matchSign} /> 'signToken')`;
225
+ yield eatMatch(m`signToken: <*Punctuator ${buildString(matchSign)} />`);
183
226
  } else {
184
- yield i`eat(null 'signToken')`;
227
+ yield eat(m`signToken: null`);
185
228
  }
186
229
 
187
- yield i`eat(<*UnsignedInteger noDoubleZero=${buildBoolean(noDoubleZero)} /> 'value')`;
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 } = (props && ctx.unbox(props)) || {};
235
+ const { noDoubleZero = false } = props || {};
193
236
 
194
- let [firstDigit] = ctx.allTagsFor(yield i`eat(/\d/)`);
237
+ let firstDigit = ctx.sourceTextFor(yield eat(re`/\d/`));
195
238
 
196
239
  if (!noDoubleZero || firstDigit.value !== '0') {
197
- yield i`eatMatch(/\d+/)`;
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 i`eat(<*Keyword /true|false/ /> 'sigilToken')`;
254
+ yield eat(m`sigilToken: <*Keyword /true|false/ />`);
205
255
  }
206
256
 
207
257
  @CoveredBy('Expression')
208
258
  @Node
209
259
  *Null() {
210
- yield i`eat(<*Keyword 'null' /> 'sigilToken')`;
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.8.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": ["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.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": ["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>",