@bablr/boot 0.4.0 → 0.6.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,13 +1,12 @@
1
1
  const objectEntries = require('iter-tools/methods/object-entries');
2
2
 
3
3
  const { buildCovers } = require('../utils.js');
4
- const sym = require('../symbols.js');
4
+ const sym = require('@bablr/boot-helpers/symbols');
5
5
 
6
6
  const _ = /\s+/y;
7
7
  const PN = 'Punctuator';
8
8
  const ID = 'Identifier';
9
9
  const KW = 'Keyword';
10
- const LIT = 'Literal';
11
10
 
12
11
  const name = 'CSTML';
13
12
 
@@ -62,15 +61,15 @@ const covers = buildCovers({
62
61
  'DoctypeTag',
63
62
  'Attribute',
64
63
  'Property',
65
- 'Reference',
64
+ 'ReferenceTag',
66
65
  'TagType',
67
- 'Null',
68
- 'Gap',
66
+ 'NullTag',
67
+ 'GapTag',
69
68
  'Node',
70
69
  'IdentifierPath',
71
70
  'OpenNodeTag',
72
71
  'CloseNodeTag',
73
- 'Terminal',
72
+ 'Tag',
74
73
  'Number',
75
74
  'Digit',
76
75
  'String',
@@ -82,9 +81,9 @@ const covers = buildCovers({
82
81
  Attribute: ['MappingAttribute', 'BooleanAttribute'],
83
82
  AttributeValue: ['String', 'Number'],
84
83
  TagType: ['Identifier', 'GlobalIdentifier'],
85
- Terminal: ['Literal', 'Trivia'],
86
- PropertyValue: ['Gap', 'Node', 'Null'],
87
- EmbeddedTerminal: ['Literal'],
84
+ Tag: ['LiteralTag', 'Trivia'],
85
+ PropertyValue: ['GapTag', 'Node', 'NullTag'],
86
+ EmbeddedTag: ['LiteralTag'],
88
87
  Number: ['Integer', 'Infinity'],
89
88
  });
90
89
 
@@ -92,6 +91,7 @@ const grammar = class CSTMLMiniparserGrammar {
92
91
  // @Node
93
92
  Document(p) {
94
93
  p.eatProduction('DoctypeTag', { path: 'doctype' });
94
+ p.eatMatchTrivia(_);
95
95
  p.eatProduction('Node', { path: 'tree' });
96
96
  }
97
97
 
@@ -104,7 +104,7 @@ const grammar = class CSTMLMiniparserGrammar {
104
104
 
105
105
  let sp = p.eatMatchTrivia(_);
106
106
 
107
- if ((sp && p.match(/[a-zA-Z]+/y)) || p.atExpression) {
107
+ if ((sp && p.match(/!?[a-zA-Z]/y)) || p.atExpression) {
108
108
  p.eatProduction('Attributes');
109
109
  sp = p.eatMatchTrivia(_);
110
110
  }
@@ -113,12 +113,12 @@ const grammar = class CSTMLMiniparserGrammar {
113
113
  }
114
114
 
115
115
  // @Node
116
- Null(p) {
116
+ NullTag(p) {
117
117
  p.eat('null', KW, { path: 'sigilToken' });
118
118
  }
119
119
 
120
120
  // @Node
121
- Gap(p) {
121
+ GapTag(p) {
122
122
  p.eat('<//>', PN, { path: 'sigilToken' });
123
123
  }
124
124
 
@@ -131,14 +131,16 @@ const grammar = class CSTMLMiniparserGrammar {
131
131
  if (open.properties.flags?.token) {
132
132
  p.eatProduction('NodeChild', { path: 'children[]' }, { token: true });
133
133
  p.eatMatchTrivia(_);
134
- } else {
135
- while (!p.match('</')) {
134
+ } else if (!open.properties.selfClosingTagToken) {
135
+ while (!(p.match('</') || p.done)) {
136
136
  p.eatProduction('NodeChild', { path: 'children[]' });
137
137
  p.eatMatchTrivia(_);
138
138
  }
139
139
  }
140
140
 
141
- p.eatProduction('CloseNodeTag', { path: 'close' });
141
+ if (!open.properties.selfClosingTagToken) {
142
+ p.eatProduction('CloseNodeTag', { path: 'close' });
143
+ }
142
144
  }
143
145
 
144
146
  NodeChild(p, _, props) {
@@ -148,31 +150,31 @@ const grammar = class CSTMLMiniparserGrammar {
148
150
  if (p.match(/<\*?@/y)) {
149
151
  p.eatProduction('Node');
150
152
  } else {
151
- p.eatProduction('Literal');
153
+ p.eatProduction('LiteralTag');
152
154
  }
153
155
  } else {
154
156
  if (p.match(/<\*?#/y)) {
155
157
  p.eatProduction('Node');
156
- } else if (p.match(/[a-zA-Z]/y)) {
158
+ } else if (p.match(/[a-zA-Z]|\./y)) {
157
159
  p.eatProduction('Property');
158
160
  } else if (p.match(/['"]/y)) {
159
- p.eatProduction('Literal');
161
+ p.eatProduction('LiteralTag');
160
162
  }
161
163
  }
162
164
  }
163
165
 
164
166
  // @Node
165
167
  Property(p) {
166
- p.eatProduction('Reference', { path: 'reference' });
168
+ p.eatProduction('ReferenceTag', { path: 'reference' });
167
169
  p.eatMatchTrivia(_);
168
170
  p.eatProduction('PropertyValue', { path: 'value' });
169
171
  }
170
172
 
171
173
  PropertyValue(p) {
172
174
  if (p.match('null')) {
173
- p.eatProduction('Null');
175
+ p.eatProduction('NullTag');
174
176
  } else if (p.match('<//>')) {
175
- p.eatProduction('Gap');
177
+ p.eatProduction('GapTag');
176
178
  } else {
177
179
  p.eatProduction('Node');
178
180
  }
@@ -181,10 +183,10 @@ const grammar = class CSTMLMiniparserGrammar {
181
183
  // @Node
182
184
  Flags(p) {
183
185
  let tr = p.eatMatch('#', PN, { path: 'triviaToken' });
184
- p.eatMatch('~', PN, { path: 'intrinsicToken' });
185
186
  p.eatMatch('*', PN, { path: 'tokenToken' });
186
187
  let esc = p.eatMatch('@', PN, { path: 'escapeToken' });
187
188
  let exp = p.eatMatch('+', PN, { path: 'expressionToken' });
189
+ p.eatMatch('$', PN, { path: 'hasGapToken' });
188
190
 
189
191
  if ((tr && esc) || (exp && (tr || esc))) throw new Error();
190
192
  }
@@ -198,26 +200,34 @@ const grammar = class CSTMLMiniparserGrammar {
198
200
  flags = p.eatProduction('Flags', { path: 'flags' });
199
201
  }
200
202
 
203
+ if (p.done) {
204
+ p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
205
+ return;
206
+ }
207
+
201
208
  p.eatProduction('TagType', { path: 'type' });
202
209
 
203
210
  let sp = p.eatMatchTrivia(_);
204
211
 
205
212
  let iv;
206
- if (flags.properties.intrinsic && sp && (p.match(/['"/]/y) || p.atExpression)) {
213
+
214
+ if (sp && (p.match(/['"]/y) || p.atExpression)) {
207
215
  iv = p.eatProduction('String', { path: 'intrinsicValue' });
208
216
 
209
217
  sp = p.eatMatchTrivia(_);
210
218
  }
211
219
 
220
+ if (!flags.properties.tokenToken && iv) {
221
+ throw new Error();
222
+ }
223
+
212
224
  if ((sp && p.match(/[a-zA-Z]+/y)) || p.atExpression) {
213
225
  p.eatProduction('Attributes');
214
226
  sp = p.eatMatchTrivia(_);
215
227
  }
216
228
 
217
229
  p.eatMatchTrivia(_);
218
- if (iv) {
219
- p.eat('/', PN, { path: 'selfClosingTagToken' });
220
- }
230
+ p.eatMatch('/', PN, { path: 'selfClosingTagToken' });
221
231
  p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
222
232
  }
223
233
 
@@ -230,13 +240,13 @@ const grammar = class CSTMLMiniparserGrammar {
230
240
  // @Fragment
231
241
  Attributes(p) {
232
242
  let sp = true;
233
- while (sp && (p.match(/[a-zA-Z]+/y) || p.atExpression)) {
243
+ while (sp && (p.match(/!?[a-zA-Z]/y) || p.atExpression)) {
234
244
  if (p.atExpression) {
235
245
  p.eatProduction('Attributes'); // ??
236
246
  } else {
237
247
  p.eatProduction('Attribute', { path: 'attributes[]' });
238
248
  }
239
- if (p.match(/\s+[a-zA-Z]/y) || (p.match(/\s+$/y) && !p.quasisDone)) {
249
+ if (p.match(/\s+!?[a-zA-Z]/y) || (p.match(/\s+$/y) && !p.quasisDone)) {
240
250
  sp = p.eatMatchTrivia(_);
241
251
  } else {
242
252
  sp = false;
@@ -246,7 +256,7 @@ const grammar = class CSTMLMiniparserGrammar {
246
256
 
247
257
  // @Cover
248
258
  Attribute(p) {
249
- if (p.match(/[a-zA-Z][[a-zA-Z]-_]*\s*=/y)) {
259
+ if (p.match(/[a-zA-Z][a-zA-Z-_]*\s*=/y)) {
250
260
  p.eatProduction('MappingAttribute');
251
261
  } else {
252
262
  p.eatProduction('BooleanAttribute');
@@ -255,13 +265,13 @@ const grammar = class CSTMLMiniparserGrammar {
255
265
 
256
266
  // @Node
257
267
  BooleanAttribute(p) {
258
- p.eat('!', KW, { path: 'negateToken' });
259
- p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
268
+ p.eatMatch('!', KW, { path: 'negateToken' });
269
+ p.eat(/[a-zA-Z][a-zA-Z-_]*/y, ID, { path: 'key' });
260
270
  }
261
271
 
262
272
  // @Node
263
273
  MappingAttribute(p) {
264
- p.eat(/[a-zA-Z]+/y, LIT, { path: 'key' });
274
+ p.eat(/[a-zA-Z][a-zA-Z-_]*/y, ID, { path: 'key' });
265
275
  p.eatMatchTrivia(_);
266
276
  p.eat('=', PN, { path: 'mapToken' });
267
277
  p.eatMatchTrivia(_);
@@ -278,7 +288,7 @@ const grammar = class CSTMLMiniparserGrammar {
278
288
  }
279
289
 
280
290
  TagType(p) {
281
- if (p.match(/[[a-zA-Z].]+:/y)) {
291
+ if (p.match(/[a-zA-Z\.]+:/y)) {
282
292
  p.eatProduction('LanguageReference', { path: 'language' });
283
293
  p.eat(':', PN, { path: 'namespaceSeparatorToken' });
284
294
  p.eatProduction('Identifier', { path: 'type' });
@@ -305,21 +315,25 @@ const grammar = class CSTMLMiniparserGrammar {
305
315
 
306
316
  // @Node
307
317
  Identifier(p) {
308
- p.eatLiteral(/[a-zA-Z][[a-zA-Z]-_]*/y);
318
+ p.eatLiteral(/[a-zA-Z][a-zA-Z-_]*/y);
309
319
  }
310
320
 
311
321
  // @Cover
312
- Terminal(p) {
322
+ Tag(p) {
313
323
  if (p.match(/['"]/y)) {
314
- p.eatProduction('Literal');
324
+ p.eatProduction('LiteralTag');
315
325
  } else {
316
326
  throw new Error();
317
327
  }
318
328
  }
319
329
 
320
330
  // @Node
321
- Reference(p) {
322
- p.eatProduction('Identifier', { path: 'name' });
331
+ ReferenceTag(p) {
332
+ if (p.match('.')) {
333
+ p.eat('.', PN, { path: 'name' });
334
+ } else {
335
+ p.eatProduction('Identifier', { path: 'name' });
336
+ }
323
337
  p.eatMatchTrivia(_);
324
338
  p.eatMatch('[]', PN, { path: 'arrayToken' });
325
339
  p.eatMatchTrivia(_);
@@ -327,11 +341,11 @@ const grammar = class CSTMLMiniparserGrammar {
327
341
  }
328
342
 
329
343
  // @Cover
330
- EmbeddedTerminal(p) {
344
+ EmbeddedTag(p) {
331
345
  if (p.match(/!['"]/y)) {
332
346
  p.eatProduction('Escape');
333
347
  } else if (p.match(/['"]/y)) {
334
- p.eatProduction('Literal');
348
+ p.eatProduction('LiteralTag');
335
349
  } else {
336
350
  throw new Error();
337
351
  }
@@ -1,7 +1,7 @@
1
1
  const Spamex = require('./spamex.js');
2
2
  const CSTML = require('./cstml.js');
3
3
  const Regex = require('./regex.js');
4
- const { node } = require('../symbols.js');
4
+ const { node } = require('@bablr/boot-helpers/symbols');
5
5
  const { buildCovers } = require('../utils.js');
6
6
 
7
7
  const _ = /\s+/y;
@@ -1,7 +1,7 @@
1
1
  const when = require('iter-tools/methods/when');
2
2
  const { escapables } = require('./cstml.js');
3
3
  const { buildCovers } = require('../utils.js');
4
- const { node } = require('../symbols.js');
4
+ const { node } = require('@bablr/boot-helpers/symbols');
5
5
 
6
6
  const name = 'Regex';
7
7
 
@@ -65,7 +65,7 @@ const unique = (flags) => flags.length === new Set(flags).size;
65
65
  const getSpecialPattern = (span) => {
66
66
  const { type } = span;
67
67
  if (type === 'Bare') {
68
- return /[*+{}\[\]()\.^$|\\\n\/]/y;
68
+ return /[*+{}\[\]()\.^$|\\\n\/><]/y;
69
69
  } else if (type === 'CharacterClass') {
70
70
  return /[\]\\]/y;
71
71
  } else if (type === 'CharacterClass:First') {
@@ -154,7 +154,7 @@ const grammar = class RegexMiniparserGrammar {
154
154
  }
155
155
 
156
156
  Elements(p) {
157
- while (p.match(/[^|]/y || p.atExpression)) {
157
+ while (p.match(/[^|]/y) || p.atExpression) {
158
158
  p.eatProduction('Element');
159
159
  }
160
160
  }
@@ -1,12 +1,11 @@
1
1
  const Regex = require('./regex.js');
2
2
  const CSTML = require('./cstml.js');
3
3
  const { buildCovers } = require('../utils.js');
4
- const sym = require('../symbols.js');
4
+ const sym = require('@bablr/boot-helpers/symbols');
5
5
 
6
6
  const _ = /\s+/y;
7
7
  const PN = 'Punctuator';
8
8
  const ID = 'Identifier';
9
- const LIT = 'Literal';
10
9
 
11
10
  const name = 'Spamex';
12
11
 
@@ -15,14 +14,18 @@ const canonicalURL = 'https://bablr.org/languages/core/en/spamex';
15
14
  const dependencies = { CSTML, Regex };
16
15
 
17
16
  const covers = buildCovers({
18
- [sym.node]: ['Attribute', 'Identifier', 'Matcher', 'Literal', 'CSTML:Flags'],
17
+ [sym.node]: ['Attribute', 'Identifier', 'Pattern', 'Matcher', 'Literal', 'CSTML:Flags'],
19
18
  Attribute: ['MappingAttribute', 'BooleanAttribute'],
20
19
  AttributeValue: ['CSTML:String', 'CSTML:Number'],
21
- Matcher: ['NodeMatcher', 'StringMatcher'],
20
+ Matcher: ['NodeMatcher', 'OpenNodeMatcher', 'CloseNodeMatcher', 'StringMatcher'],
22
21
  StringMatcher: ['CSTML:String', 'Regex:Pattern'],
23
22
  });
24
23
 
25
24
  const grammar = class SpamexMiniparserGrammar {
25
+ Pattern(p) {
26
+ p.eatProduction('Matcher', { path: 'matcher' });
27
+ }
28
+
26
29
  // @Cover
27
30
  Matcher(p) {
28
31
  if (p.match(/<[^!/]/y)) {
@@ -36,8 +39,28 @@ const grammar = class SpamexMiniparserGrammar {
36
39
  }
37
40
  }
38
41
 
39
- // @Node
40
42
  NodeMatcher(p) {
43
+ let open = p.eatProduction('OpenNodeMatcher', { path: 'open' });
44
+
45
+ if (!open.properties.selfClosingTagToken) {
46
+ p.eatMatchTrivia(_);
47
+
48
+ if (open.properties.flags?.token) {
49
+ // p.eatProduction('NodeChild', { path: 'children[]' }, { token: true });
50
+ // p.eatMatchTrivia(_);
51
+ } else {
52
+ // while (!(p.match('</') || p.done)) {
53
+ // p.eatProduction('NodeChild', { path: 'children[]' });
54
+ // p.eatMatchTrivia(_);
55
+ // }
56
+ }
57
+
58
+ p.eatProduction('CloseNodeMatcher', { path: 'close' });
59
+ }
60
+ }
61
+
62
+ // @Node
63
+ OpenNodeMatcher(p) {
41
64
  p.eat('<', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
42
65
 
43
66
  if (!p.atExpression) {
@@ -48,6 +71,10 @@ const grammar = class SpamexMiniparserGrammar {
48
71
  p.eat(/[a-zA-Z]+/y, ID, { path: 'language' });
49
72
  p.eat(':', PN, { path: 'namespaceSeparatorToken' });
50
73
  p.eat(/[a-zA-Z]+/y, ID, { path: 'type' });
74
+ } else if (p.match('?')) {
75
+ p.eat('?', PN, { path: 'type' });
76
+ } else if (p.match(' ')) {
77
+ p.eatMatchTrivia(_);
51
78
  } else {
52
79
  if (p.atExpression) {
53
80
  p.eatProduction('Identifier', { path: 'type' });
@@ -58,7 +85,7 @@ const grammar = class SpamexMiniparserGrammar {
58
85
 
59
86
  let sp = p.eatMatchTrivia(_);
60
87
 
61
- if (sp && (p.match(/['"/]/y) || p.atExpression)) {
88
+ if (sp && ((p.match(/['"/]/y) && !p.match('/>')) || p.atExpression)) {
62
89
  p.eatProduction('StringMatcher', { path: 'intrinsicValue' });
63
90
 
64
91
  sp = p.eatMatchTrivia(_);
@@ -70,6 +97,12 @@ const grammar = class SpamexMiniparserGrammar {
70
97
  }
71
98
 
72
99
  p.eatMatchTrivia(_);
100
+ p.eatMatch('/', PN, { path: 'selfClosingTagToken' });
101
+ p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
102
+ }
103
+
104
+ CloseNodeMatcher(p) {
105
+ p.eat('</', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
73
106
  p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
74
107
  }
75
108
 
@@ -94,12 +127,12 @@ const grammar = class SpamexMiniparserGrammar {
94
127
 
95
128
  // @Node
96
129
  BooleanAttribute(p) {
97
- p.eat(/[a-zA-Z]+/y, LIT, { path: 'key' });
130
+ p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
98
131
  }
99
132
 
100
133
  // @Node
101
134
  MappingAttribute(p) {
102
- p.eat(/[a-zA-Z]+/y, LIT, { path: 'key' });
135
+ p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
103
136
  p.eatMatchTrivia(_);
104
137
  p.eat('=', PN, { path: 'mapToken' });
105
138
  p.eatMatchTrivia(_);
package/lib/match.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const { Path, buildNode } = require('./path.js');
2
2
  const { resolveDependentLanguage } = require('./utils.js');
3
- const sym = require('./symbols.js');
3
+ const sym = require('@bablr/boot-helpers/symbols');
4
4
 
5
5
  class Match {
6
6
  constructor(parent, resolvedLanguage, id, attributes, path) {
package/lib/miniparser.js CHANGED
@@ -3,10 +3,11 @@ const escapeRegex = require('escape-string-regexp');
3
3
  const arrayLast = require('iter-tools/methods/array-last');
4
4
  const isString = require('iter-tools/methods/is-string');
5
5
  const isObject = require('iter-tools/methods/is-object');
6
- const sym = require('./symbols.js');
6
+ const sym = require('@bablr/boot-helpers/symbols');
7
7
  const { Match } = require('./match.js');
8
8
  const { parsePath } = require('./path.js');
9
9
  const { set, isRegex, isArray, getPrototypeOf, buildNode } = require('./utils.js');
10
+ const { ReferenceTag, LiteralTag, Escape } = require('@bablr/boot-helpers/symbols');
10
11
 
11
12
  class TemplateParser {
12
13
  constructor(rootLanguage, quasis, expressions) {
@@ -177,9 +178,19 @@ class TemplateParser {
177
178
  const { properties, children } = parentPath.node;
178
179
  const path = parsePath(this.m.attrs.path);
179
180
 
180
- children.push({ ...ref(path), id });
181
+ if (isArray(result)) {
182
+ for (const value of result) {
183
+ children.push(ref(path));
181
184
 
182
- set(properties, path, result);
185
+ // TODO interpolate separators!
186
+
187
+ set(properties, path, value);
188
+ }
189
+ } else {
190
+ children.push(ref(path));
191
+
192
+ set(properties, path, result);
193
+ }
183
194
  }
184
195
  } else {
185
196
  if (isEmbedded) {
@@ -197,6 +208,7 @@ class TemplateParser {
197
208
  if (result?.attrs) {
198
209
  node.attributes = result.attrs;
199
210
  }
211
+
200
212
  if (parentPath?.node && !covers.has(type)) {
201
213
  const path = parsePath(this.m.attrs.path);
202
214
 
@@ -210,11 +222,11 @@ class TemplateParser {
210
222
  this.m = this.m.parent;
211
223
 
212
224
  if (this.path?.node) {
213
- const isTerminal = (child) => ['Literal', 'Escape'].includes(child.type);
225
+ const isTag = (child) => [LiteralTag, Escape].includes(child.type);
214
226
 
215
227
  const { children } = this.path.node;
216
228
 
217
- if (children.find(isTerminal) && !children.every(isTerminal)) {
229
+ if (children.find(isTag) && !children.every(isTag)) {
218
230
  throw new Error('strings must be wrapped in nodes');
219
231
  }
220
232
  }
@@ -280,7 +292,7 @@ class TemplateParser {
280
292
 
281
293
  const lastChild = arrayLast(children);
282
294
 
283
- if (lastChild.type !== 'Reference') {
295
+ if (lastChild.type !== ReferenceTag) {
284
296
  throw new Error();
285
297
  }
286
298