@bablr/language-en-json 0.11.0 → 0.13.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/lib/grammar.js CHANGED
@@ -1,225 +1,229 @@
1
- /* @macrome
2
- * @generatedby @bablr/macrome-generator-bablr
3
- * @generatedfrom ./grammar.macro.js#dd06bed2bba53b9dde1ae7cf33f8696edb684bd6
4
- * This file is autogenerated. Please do not edit it directly.
5
- * When editing run `npx macrome watch` then change the file this is generated from.
6
- */
7
- import _applyDecs from "@babel/runtime/helpers/applyDecs2305";
8
- let _initProto, _ExpressionDecs, _ArrayDecs, _ObjectDecs, _StringDecs, _NumberDecs, _InfinityDecs, _BooleanDecs, _NullDecs, _KeywordDecs, _PunctuatorDecs, _ListDecs, _AnyDecs, _AllDecs;
9
1
  import { re, spam as m } from '@bablr/boot';
10
- import { basicTriviaEnhancer } from '@bablr/helpers/trivia';
11
2
  import * as productions from '@bablr/helpers/productions';
12
- import { o, eat, eatMatch, match, fail, defineAttribute } from '@bablr/helpers/grammar';
3
+ import {
4
+ o,
5
+ eat,
6
+ eatMatch,
7
+ match,
8
+ fail,
9
+ defineAttribute,
10
+ startSpan,
11
+ endSpan,
12
+ } from '@bablr/helpers/grammar';
13
13
  import { buildString } from '@bablr/helpers/builders';
14
- import { Node, CoveredBy, AllowEmpty, InjectFrom, Literal } from '@bablr/helpers/decorators';
15
- import * as Space from '@bablr/language-en-blank-space';
16
- export const dependencies = {
17
- Space
18
- };
14
+ import Space from '@bablr/language-en-blank-space';
15
+ import { get, printSource } from '@bablr/agast-helpers/tree';
16
+
17
+ export const dependencies = { Space };
18
+
19
19
  export const canonicalURL = 'https://bablr.org/languages/core/en/json';
20
- const escapables = new Map(Object.entries({
21
- b: '\b',
22
- // these two escapes are antiquated
23
- f: '\f',
24
- // but giving their meaning away could be confusing
25
- n: '\n',
26
- r: '\r',
27
- t: '\t',
28
- 0: '\0'
29
- }));
30
- function first(iter) {
31
- for (let value of iter) return value;
20
+
21
+ export const defaultMatcher = m`<_Expression />`;
22
+
23
+ const escapables = new Map(
24
+ Object.entries({
25
+ b: '\b', // these two escapes are antiquated
26
+ f: '\f', // but giving their meaning away could be confusing
27
+ n: '\n',
28
+ r: '\r',
29
+ t: '\t',
30
+ 0: '\0',
31
+ }),
32
+ );
33
+
34
+ export function* eatMatchTrivia() {
35
+ let trivia = null;
36
+ while (yield match(re`/[ \t\r\n]/`)) {
37
+ trivia = yield eat(m`#: :Space: <_Blank />`);
38
+ }
39
+ return trivia;
32
40
  }
33
- export const grammar = basicTriviaEnhancer({
34
- triviaIsAllowed: s => s.span === 'Bare',
35
- triviaMatcher: m`#: :Space: <*Space /[ \n\r\t]/ />`
36
- }, class JSONGrammar {
37
- static {
38
- [_initProto] = _applyDecs(this, [[_ExpressionDecs, 2, "Expression"], [_ArrayDecs, 2, "Array"], [_ObjectDecs, 2, "Object"], [Node, 2, "Property"], [_StringDecs, 2, "String"], [[AllowEmpty, Node], 2, "StringContent"], [Node, 2, "EscapeSequence"], [Node, 2, "EscapeCode"], [_NumberDecs, 2, "Number"], [Node, 2, "Integer"], [Node, 2, "UnsignedInteger"], [Node, 2, "UnsignedHexInteger"], [_InfinityDecs, 2, "Infinity"], [_BooleanDecs, 2, "Boolean"], [_NullDecs, 2, "Null"], [_KeywordDecs, 2, "Keyword"], [_PunctuatorDecs, 2, "Punctuator"], [_ListDecs, 2, "List"], [_AnyDecs, 2, "Any"], [_AllDecs, 2, "All"]], []).e;
39
- }
41
+
42
+ export const fragmentProduction = 'Fragment';
43
+
44
+ export const grammar = class JSONGrammar {
40
45
  constructor() {
41
- _initProto(this);
46
+ this.emptyables = new Set(['StringContent', 'List']);
47
+ this.literals = new Set(['Keyword']);
42
48
  }
43
- *[(_ExpressionDecs = CoveredBy('Element'), _ArrayDecs = [CoveredBy('Expression'), Node], _ObjectDecs = [CoveredBy('Expression'), Node], _StringDecs = [CoveredBy('Expression'), Node], _NumberDecs = [CoveredBy('Expression'), Node], _InfinityDecs = [CoveredBy('Expression'), Node], _BooleanDecs = [CoveredBy('Expression'), Node], _NullDecs = [CoveredBy('Expression'), Node], _KeywordDecs = [Literal, Node, InjectFrom(productions)], _PunctuatorDecs = [Literal, Node, InjectFrom(productions)], _ListDecs = [AllowEmpty, InjectFrom(productions)], _AnyDecs = InjectFrom(productions), _AllDecs = InjectFrom(productions), Symbol.for('@bablr/fragment'))]({
44
- props: {
45
- rootMatcher
46
- }
47
- }) {
48
- // needed for the trivia plugin
49
+
50
+ *Fragment({ props: { rootMatcher } }) {
51
+ yield* eatMatchTrivia();
49
52
  yield eat(rootMatcher);
53
+ yield* eatMatchTrivia();
50
54
  }
55
+
51
56
  *Expression() {
52
- yield eat(m`<_Any />`, [m`<Array '[' />`, m`<Object '{' />`, m`<String /['"]/ />`, m`<Number /\d|-[\d\g]/ {span: 'Number'} />`, m`<Infinity /-?Infinity/ />`, m`<Null 'null' />`, m`<Boolean /true|false/ />`]);
57
+ if (yield eatMatch(m`<Array '[' />`)) {
58
+ } else if (yield eatMatch(m`<Object '{' />`)) {
59
+ } else if (yield eatMatch(m`<String /['"]/ />`)) {
60
+ } else if (yield eatMatch(m`<Number /\d|-[\d\g]/ />`)) {
61
+ } else if (yield eatMatch(m`<Null 'null' />`)) {
62
+ } else {
63
+ yield eatMatch(m`<Boolean /true|false/ />`);
64
+ }
53
65
  }
66
+
54
67
  *Array() {
55
- yield eat(m`openToken: <*Punctuator '[' { balanced: ']' } />`);
56
- yield eat(m`<_List />`, o({
57
- element: m`elements[]+$: <__Expression />`,
58
- separator: m`separatorTokens[]: <*Punctuator ',' />`,
59
- allowTrailingSeparator: false
60
- }));
61
- yield eat(m`closeToken: <*Punctuator ']' { balancer: true } />`);
68
+ yield eat(m`openToken*: <* '[' />`);
69
+ yield* eatMatchTrivia();
70
+ let sep = true;
71
+
72
+ while (sep && (yield match(re`/[^\]]/s`))) {
73
+ yield eat(m`elements[]$: <_Expression />`);
74
+ yield* eatMatchTrivia();
75
+ sep = yield eatMatch(m`#separatorTokens: <* ',' />`);
76
+ if (sep) {
77
+ yield* eatMatchTrivia();
78
+ }
79
+ }
80
+ yield eat(m`closeToken*: <* ']' />`);
62
81
  }
82
+
63
83
  *Object() {
64
- yield eat(m`openToken: <*Punctuator '{' { balanced: '}' } />`);
84
+ yield eat(m`openToken*: <* '{' />`);
85
+ yield* eatMatchTrivia();
65
86
  let sep = true;
66
- yield eatMatch(m`separatorTokens[]: []`);
67
- yield eatMatch(m`properties[]$: []`);
68
- while (sep && (yield match(re`/.|\g/s`))) {
69
- let suppressGap = !!(yield match(m`<_All />`, [m`key: <//>`, m`sigilToken: <*Punctuator ':' />`]));
70
- yield eat(m`properties[]$: <Property />`, null, o({
71
- suppressGap
72
- }));
73
- sep = yield eatMatch(m`separatorTokens[]: <*Punctuator ',' />`);
87
+
88
+ while (sep && (yield match(re`/[^}]/s`))) {
89
+ yield match(m`<__All />`, [m`key$: <//>`, m`sigilToken*: <* ':' />`]);
90
+ yield eat(m`properties[]$: <Property />`);
91
+ yield* eatMatchTrivia();
92
+ sep = yield eatMatch(m`#separatorTokens: <* ',' />`);
93
+ if (sep) {
94
+ yield* eatMatchTrivia();
95
+ }
74
96
  }
75
- yield eat(m`closeToken: <*Punctuator '}' { balancer: true } />`);
97
+ yield eat(m`closeToken*: <* '}' />`);
76
98
  }
99
+
77
100
  *Property() {
78
101
  yield eat(m`key$: <String />`);
79
- yield eat(m`sigilToken: <*Punctuator ':' />`);
80
- yield eat(m`value+$: <__Expression />`);
102
+ yield* eatMatchTrivia();
103
+ yield eat(m`sigilToken*: <* ':' />`);
104
+ yield* eatMatchTrivia();
105
+ yield eat(m`value+$: <_Expression />`);
81
106
  }
107
+
82
108
  *String() {
83
- yield eat(m`openToken: <*Punctuator '"' { balanced: '"', balancedSpan: 'String:Double' } />`);
109
+ yield eat(m`openToken*: <* '"' />`);
110
+ yield startSpan('String:Double', '"');
84
111
  yield eat(m`content$: <*StringContent />`);
85
- yield eat(m`closeToken: <*Punctuator '"' { balancer: true } />`);
112
+ yield endSpan();
113
+ yield eat(m`closeToken*: <* '"' />`);
86
114
  }
87
- *StringContent({
88
- state: {
89
- span
90
- }
91
- }) {
115
+
116
+ *StringContent() {
92
117
  let esc, lit;
93
118
  do {
94
- esc = (yield match('\\')) && (yield eat(m`@: <EscapeSequence />`));
95
- lit = span === 'String:Single' ? yield eatMatch(re`/[^\r\n\\'\g]+/`) : yield eatMatch(re`/[^\r\n\\"\g]+/`);
119
+ lit = yield eatMatch(re`/[^\r\n\\\g]+/`);
120
+ esc = yield eatMatch(m`@: <EscapeSequence '\\' />`);
96
121
  } while (esc || lit);
97
122
  }
98
- *EscapeSequence({
99
- state: {
100
- span
101
- },
102
- ctx
103
- }) {
104
- if (!span.startsWith('String')) {
105
- yield fail();
106
- }
107
- yield eat(m`sigilToken: <*Punctuator '\\' { openSpan: 'Escape' } />`);
123
+
124
+ *EscapeSequence() {
125
+ yield startSpan('Escape');
126
+ yield eat(m`sigilToken*: <* '\\' />`);
127
+
108
128
  let match_;
109
129
  let cooked;
110
- if (match_ = span === 'String:Single' ? yield match(re`/[\\/nrt0']/`) : yield match(re`/[\\/nrt0"]/`)) {
111
- const matchText = ctx.sourceTextFor(match_);
112
- yield eat(m`code: <*Keyword ${buildString(matchText)} { closeSpan: 'Escape' } />`);
130
+
131
+ if ((match_ = yield match(re`/[\\/bfnrt0"]/`))) {
132
+ const matchText = printSource(match_);
133
+ yield eat(m`code*: <*Keyword ${buildString(matchText)} />`);
134
+
113
135
  cooked = escapables.get(matchText) || matchText;
114
136
  } else if (yield match('u')) {
115
- let codeNode = yield eat(m`code: <EscapeCode { closeSpan: 'Escape' } />`);
116
- if (codeNode && first(codeNode.children)?.value.flags.token) {
117
- const match_ = ctx.sourceTextFor(codeNode);
118
- } else {
119
- const type = ctx.sourceTextFor(codeNode.get('typeToken'));
120
- if (type) {
121
- const value = ctx.sourceTextFor(codeNode.get('value'));
122
- if (!span.startsWith('String')) {
123
- throw new Error('not implemented');
124
- }
125
- if (type === 'u') {
126
- cooked = String.fromCharCode(parseInt(value, 16));
127
- } else {
128
- throw new Error();
129
- }
137
+ let codeNode = yield eat(m`code*: <EscapeCode />`);
138
+
139
+ const type = printSource(get('typeToken', codeNode.node));
140
+
141
+ if (type) {
142
+ const value = printSource(get('value', codeNode.node));
143
+
144
+ if (type === 'u') {
145
+ cooked = String.fromCharCode(parseInt(value, 16));
130
146
  } else {
131
- let value = ctx.sourceTextFor(codeNode);
132
- cooked = escapables.get(value) || value;
147
+ throw new Error();
133
148
  }
149
+ } else {
150
+ let value = printSource(codeNode.node);
151
+ cooked = escapables.get(value) || value;
134
152
  }
135
153
  } else {
136
154
  yield fail();
137
155
  }
156
+
157
+ // TODO error if we don't see the span ended
158
+ yield endSpan();
159
+
138
160
  yield defineAttribute('cooked', cooked);
139
161
  }
162
+
140
163
  *EscapeCode() {
141
- if (yield eatMatch(m`typeToken: <*Keyword 'u' />`)) {
142
- if (yield eatMatch(m`openToken: <*Punctuator '{' { balanced: '}' } />`, null, o({
143
- bind: true
144
- }))) {
145
- yield eat(m`value$: <*UnsignedHexInteger />`);
146
- yield eat(m`closeToken: <*Punctuator '}' { balancer: true } />`);
164
+ if (yield eatMatch(m`typeToken*: <*Keyword 'u' />`)) {
165
+ if (yield eatMatch(m`openToken*: <* '{' />`)) {
166
+ yield* eatMatchTrivia();
167
+ yield eat(m`value: <*UnsignedHexInteger />`);
168
+ yield* eatMatchTrivia();
169
+ yield eat(m`closeToken*: <* '}' />`);
147
170
  } else {
148
- yield eat(m`value$: <*UnsignedHexInteger /[\da-fA-F]{4}/ />`);
149
- yield eat(m`closeToken: null`);
171
+ yield eat(m`value: <*UnsignedHexInteger /[\da-fA-F]{4}/ />`);
150
172
  }
151
173
  }
152
174
  }
175
+
153
176
  *Number() {
154
- yield eat(m`wholePart$: <Integer />`, o({
155
- noDoubleZero: true,
156
- matchSign: '-'
157
- }));
158
- let fs = yield eatMatch(m`fractionalSeparatorToken: <*Punctuator '.' />`, null, o({
159
- bind: true
160
- }));
177
+ yield eat(m`wholePart$: <Integer />`, o({ noDoubleZero: true, matchSign: '-' }));
178
+
179
+ let fs = yield eatMatch(m`fractionalSeparatorToken*: <* '.' />`);
180
+
161
181
  if (fs) {
162
182
  yield eat(m`fractionalPart$: <*UnsignedInteger />`);
163
183
  } else {
164
184
  yield eat(m`fractionalPart$: null`);
165
185
  }
166
- let es = yield eatMatch(m`exponentSeparatorToken: <*Punctuator /[eE]/ />`, null, o({
167
- bind: true
168
- }));
186
+
187
+ let es = yield eatMatch(m`exponentSeparatorToken*: <* /[eE]/ />`);
188
+
169
189
  if (es) {
170
- yield eat(m`exponentPart$: <Integer />`, {
171
- matchSign: /[+-]/
172
- });
190
+ yield eat(m`exponentPart$: <Integer />`, { matchSign: /[+-]/ });
173
191
  } else {
174
192
  yield eat(m`exponentPart$: null`);
175
193
  }
176
194
  }
177
- *Integer({
178
- props: {
179
- matchSign = null,
180
- noDoubleZero = false
181
- }
182
- }) {
195
+
196
+ *Integer({ props: { matchSign = null, noDoubleZero = false } }) {
183
197
  if (matchSign) {
184
- yield eatMatch(m`signToken: <*Punctuator ${buildString(matchSign)} />`, null, o({
185
- bind: true
186
- }));
187
- } else {
188
- yield eat(m`signToken: null`);
198
+ yield eatMatch(m`signToken*: <* ${buildString(matchSign)} />`);
189
199
  }
190
- yield eat(m`value$: <*UnsignedInteger />`, o({
191
- noDoubleZero
192
- }));
193
- }
194
- *UnsignedInteger({
195
- props: {
196
- noDoubleZero = false
197
- },
198
- ctx
199
- }) {
200
- let firstDigit = ctx.sourceTextFor(yield eat(re`/\d/`));
200
+
201
+ yield eat(m`value$: <*UnsignedInteger />`, o({ noDoubleZero }));
202
+ }
203
+
204
+ *UnsignedInteger({ props: { noDoubleZero = false } }) {
205
+ let firstDigit = printSource(yield eat(re`/\d/`));
206
+
201
207
  if (!noDoubleZero || firstDigit.value !== '0') {
202
208
  yield eatMatch(re`/\d+/`);
203
209
  }
204
210
  }
211
+
205
212
  *UnsignedHexInteger() {
206
213
  yield eatMatch(re`/[\da-fA-F]+/`);
207
214
  }
208
- *Infinity() {
209
- yield eatMatch(m`signToken: <*Punctuator '-' />`, null, o({
210
- bind: true
211
- }));
212
- yield eat(m`sigilToken: <*Keyword 'Infinity' />`);
213
- }
215
+
214
216
  *Boolean() {
215
- yield eat(m`sigilToken: <*Keyword /true|false/ />`);
217
+ yield eat(m`sigilToken*: <*Keyword /true|false/ />`);
216
218
  }
219
+
217
220
  *Null() {
218
- yield eat(m`sigilToken: <*Keyword 'null' />`);
219
- }
220
- *Keyword() {}
221
- *Punctuator() {}
222
- *List() {}
223
- *Any() {}
224
- *All() {}
225
- });
221
+ yield eat(m`sigilToken*: <*Keyword 'null' />`);
222
+ }
223
+
224
+ All(args) {
225
+ return productions.All(args);
226
+ }
227
+ };
228
+
229
+ export default { canonicalURL, dependencies, grammar, defaultMatcher, fragmentProduction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bablr/language-en-json",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "A BABLR language for JSON",
5
5
  "engines": {
6
6
  "node": ">=12.0.0"
@@ -14,26 +14,20 @@
14
14
  "lib/**/*.js"
15
15
  ],
16
16
  "scripts": {
17
- "build": "macrome build",
18
- "watch": "macrome watch",
19
- "clean": "macrome clean",
20
17
  "test": "mocha"
21
18
  },
22
19
  "sideEffects": false,
23
20
  "dependencies": {
24
- "@bablr/agast-helpers": "0.8.0",
25
- "@bablr/agast-vm-helpers": "0.8.0",
26
- "@bablr/boot": "0.9.0",
27
- "@bablr/helpers": "0.23.0",
28
- "@bablr/language-en-blank-space": "0.8.0",
29
- "@babel/runtime": "^7.22.15"
21
+ "@bablr/agast-helpers": "0.10.0",
22
+ "@bablr/agast-vm-helpers": "0.10.0",
23
+ "@bablr/boot": "0.11.0",
24
+ "@bablr/helpers": "0.25.0",
25
+ "@bablr/language-en-blank-space": "0.10.0"
30
26
  },
31
27
  "devDependencies": {
32
28
  "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#c97bfa4b3663f8378e9b3e42bb5a41e685406cf9",
33
- "@bablr/macrome": "^0.1.3",
34
- "@bablr/macrome-generator-bablr": "^0.3.2",
35
29
  "@qnighy/dedent": "0.1.1",
36
- "bablr": "^0.7.0",
30
+ "bablr": "^0.11.0",
37
31
  "enhanced-resolve": "^5.12.0",
38
32
  "eslint": "^7.32.0",
39
33
  "eslint-import-resolver-enhanced-resolve": "^1.0.5",
@@ -48,7 +42,10 @@
48
42
  "grammar",
49
43
  "english"
50
44
  ],
51
- "repository": "git@github.com:bablr-lang/language-en-json.git",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+ssh://git@github.com/bablr-lang/language-en-json.git"
48
+ },
52
49
  "homepage": "https://github.com/bablr-lang/language-en-json",
53
50
  "author": "Conrad Buck <conartist6@gmail.com>",
54
51
  "license": "MIT"
@@ -1,285 +0,0 @@
1
- import { re, spam as m } from '@bablr/boot';
2
- import { basicTriviaEnhancer } from '@bablr/helpers/trivia';
3
- import * as productions from '@bablr/helpers/productions';
4
- import { o, eat, eatMatch, match, fail, defineAttribute } from '@bablr/helpers/grammar';
5
- import { buildString } from '@bablr/helpers/builders';
6
- import { Node, CoveredBy, AllowEmpty, InjectFrom, Literal } from '@bablr/helpers/decorators';
7
- import * as Space from '@bablr/language-en-blank-space';
8
-
9
- export const dependencies = { Space };
10
-
11
- export const canonicalURL = 'https://bablr.org/languages/core/en/json';
12
-
13
- const escapables = new Map(
14
- Object.entries({
15
- b: '\b', // these two escapes are antiquated
16
- f: '\f', // but giving their meaning away could be confusing
17
- n: '\n',
18
- r: '\r',
19
- t: '\t',
20
- 0: '\0',
21
- }),
22
- );
23
-
24
- function first(iter) {
25
- for (let value of iter) return value;
26
- }
27
-
28
- export const grammar = basicTriviaEnhancer(
29
- {
30
- triviaIsAllowed: (s) => s.span === 'Bare',
31
- triviaMatcher: m`#: :Space: <*Space /[ \n\r\t]/ />`,
32
- },
33
- class JSONGrammar {
34
- *[Symbol.for('@bablr/fragment')]({ props: { rootMatcher } }) {
35
- // needed for the trivia plugin
36
- yield eat(rootMatcher);
37
- }
38
-
39
- @CoveredBy('Element')
40
- *Expression() {
41
- yield eat(m`<_Any />`, [
42
- m`<Array '[' />`,
43
- m`<Object '{' />`,
44
- m`<String /['"]/ />`,
45
- m`<Number /\d|-[\d\g]/ {span: 'Number'} />`,
46
- m`<Infinity /-?Infinity/ />`,
47
- m`<Null 'null' />`,
48
- m`<Boolean /true|false/ />`,
49
- ]);
50
- }
51
-
52
- @CoveredBy('Expression')
53
- @Node
54
- *Array() {
55
- yield eat(m`openToken: <*Punctuator '[' { balanced: ']' } />`);
56
- yield eat(
57
- m`<_List />`,
58
- o({
59
- element: m`elements[]+$: <__Expression />`,
60
- separator: m`separatorTokens[]: <*Punctuator ',' />`,
61
- allowTrailingSeparator: false,
62
- }),
63
- );
64
- yield eat(m`closeToken: <*Punctuator ']' { balancer: true } />`);
65
- }
66
-
67
- @CoveredBy('Expression')
68
- @Node
69
- *Object() {
70
- yield eat(m`openToken: <*Punctuator '{' { balanced: '}' } />`);
71
- let sep = true;
72
-
73
- yield eatMatch(m`separatorTokens[]: []`);
74
- yield eatMatch(m`properties[]$: []`);
75
-
76
- while (sep && (yield match(re`/.|\g/s`))) {
77
- let suppressGap = !!(yield match(m`<_All />`, [
78
- m`key: <//>`,
79
- m`sigilToken: <*Punctuator ':' />`,
80
- ]));
81
- yield eat(m`properties[]$: <Property />`, null, o({ suppressGap }));
82
- sep = yield eatMatch(m`separatorTokens[]: <*Punctuator ',' />`);
83
- }
84
- yield eat(m`closeToken: <*Punctuator '}' { balancer: true } />`);
85
- }
86
-
87
- @Node
88
- *Property() {
89
- yield eat(m`key$: <String />`);
90
- yield eat(m`sigilToken: <*Punctuator ':' />`);
91
- yield eat(m`value+$: <__Expression />`);
92
- }
93
-
94
- @CoveredBy('Expression')
95
- @Node
96
- *String() {
97
- yield eat(m`openToken: <*Punctuator '"' { balanced: '"', balancedSpan: 'String:Double' } />`);
98
-
99
- yield eat(m`content$: <*StringContent />`);
100
-
101
- yield eat(m`closeToken: <*Punctuator '"' { balancer: true } />`);
102
- }
103
-
104
- @AllowEmpty
105
- @Node
106
- *StringContent({ state: { span } }) {
107
- let esc, lit;
108
- do {
109
- esc = (yield match('\\')) && (yield eat(m`@: <EscapeSequence />`));
110
- lit =
111
- span === 'String:Single'
112
- ? yield eatMatch(re`/[^\r\n\\'\g]+/`)
113
- : yield eatMatch(re`/[^\r\n\\"\g]+/`);
114
- } while (esc || lit);
115
- }
116
-
117
- @Node
118
- *EscapeSequence({ state: { span }, ctx }) {
119
- if (!span.startsWith('String')) {
120
- yield fail();
121
- }
122
-
123
- yield eat(m`sigilToken: <*Punctuator '\\' { openSpan: 'Escape' } />`);
124
-
125
- let match_;
126
- let cooked;
127
-
128
- if (
129
- (match_ =
130
- span === 'String:Single' ? yield match(re`/[\\/nrt0']/`) : yield match(re`/[\\/nrt0"]/`))
131
- ) {
132
- const matchText = ctx.sourceTextFor(match_);
133
- yield eat(m`code: <*Keyword ${buildString(matchText)} { closeSpan: 'Escape' } />`);
134
-
135
- cooked = escapables.get(matchText) || matchText;
136
- } else if (yield match('u')) {
137
- let codeNode = yield eat(m`code: <EscapeCode { closeSpan: 'Escape' } />`);
138
-
139
- if (codeNode && first(codeNode.children)?.value.flags.token) {
140
- const match_ = ctx.sourceTextFor(codeNode);
141
- } else {
142
- const type = ctx.sourceTextFor(codeNode.get('typeToken'));
143
-
144
- if (type) {
145
- const value = ctx.sourceTextFor(codeNode.get('value'));
146
-
147
- if (!span.startsWith('String')) {
148
- throw new Error('not implemented');
149
- }
150
-
151
- if (type === 'u') {
152
- cooked = String.fromCharCode(parseInt(value, 16));
153
- } else {
154
- throw new Error();
155
- }
156
- } else {
157
- let value = ctx.sourceTextFor(codeNode);
158
- cooked = escapables.get(value) || value;
159
- }
160
- }
161
- } else {
162
- yield fail();
163
- }
164
-
165
- yield defineAttribute('cooked', cooked);
166
- }
167
-
168
- @Node
169
- *EscapeCode() {
170
- if (yield eatMatch(m`typeToken: <*Keyword 'u' />`)) {
171
- if (
172
- yield eatMatch(
173
- m`openToken: <*Punctuator '{' { balanced: '}' } />`,
174
- null,
175
- o({ bind: true }),
176
- )
177
- ) {
178
- yield eat(m`value$: <*UnsignedHexInteger />`);
179
- yield eat(m`closeToken: <*Punctuator '}' { balancer: true } />`);
180
- } else {
181
- yield eat(m`value$: <*UnsignedHexInteger /[\da-fA-F]{4}/ />`);
182
- yield eat(m`closeToken: null`);
183
- }
184
- }
185
- }
186
-
187
- @CoveredBy('Expression')
188
- @Node
189
- *Number() {
190
- yield eat(m`wholePart$: <Integer />`, o({ noDoubleZero: true, matchSign: '-' }));
191
-
192
- let fs = yield eatMatch(
193
- m`fractionalSeparatorToken: <*Punctuator '.' />`,
194
- null,
195
- o({ bind: true }),
196
- );
197
-
198
- if (fs) {
199
- yield eat(m`fractionalPart$: <*UnsignedInteger />`);
200
- } else {
201
- yield eat(m`fractionalPart$: null`);
202
- }
203
-
204
- let es = yield eatMatch(
205
- m`exponentSeparatorToken: <*Punctuator /[eE]/ />`,
206
- null,
207
- o({ bind: true }),
208
- );
209
-
210
- if (es) {
211
- yield eat(m`exponentPart$: <Integer />`, { matchSign: /[+-]/ });
212
- } else {
213
- yield eat(m`exponentPart$: null`);
214
- }
215
- }
216
-
217
- @Node
218
- *Integer({ props: { matchSign = null, noDoubleZero = false } }) {
219
- if (matchSign) {
220
- yield eatMatch(
221
- m`signToken: <*Punctuator ${buildString(matchSign)} />`,
222
- null,
223
- o({ bind: true }),
224
- );
225
- } else {
226
- yield eat(m`signToken: null`);
227
- }
228
-
229
- yield eat(m`value$: <*UnsignedInteger />`, o({ noDoubleZero }));
230
- }
231
-
232
- @Node
233
- *UnsignedInteger({ props: { noDoubleZero = false }, ctx }) {
234
- let firstDigit = ctx.sourceTextFor(yield eat(re`/\d/`));
235
-
236
- if (!noDoubleZero || firstDigit.value !== '0') {
237
- yield eatMatch(re`/\d+/`);
238
- }
239
- }
240
-
241
- @Node
242
- *UnsignedHexInteger() {
243
- yield eatMatch(re`/[\da-fA-F]+/`);
244
- }
245
-
246
- @CoveredBy('Expression')
247
- @Node
248
- *Infinity() {
249
- yield eatMatch(m`signToken: <*Punctuator '-' />`, null, o({ bind: true }));
250
- yield eat(m`sigilToken: <*Keyword 'Infinity' />`);
251
- }
252
-
253
- @CoveredBy('Expression')
254
- @Node
255
- *Boolean() {
256
- yield eat(m`sigilToken: <*Keyword /true|false/ />`);
257
- }
258
-
259
- @CoveredBy('Expression')
260
- @Node
261
- *Null() {
262
- yield eat(m`sigilToken: <*Keyword 'null' />`);
263
- }
264
-
265
- @Literal
266
- @Node
267
- @InjectFrom(productions)
268
- *Keyword() {}
269
-
270
- @Literal
271
- @Node
272
- @InjectFrom(productions)
273
- *Punctuator() {}
274
-
275
- @AllowEmpty
276
- @InjectFrom(productions)
277
- *List() {}
278
-
279
- @InjectFrom(productions)
280
- *Any() {}
281
-
282
- @InjectFrom(productions)
283
- *All() {}
284
- },
285
- );