@bablr/boot 0.9.0 → 0.11.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,11 +1,10 @@
1
- import * as sym from '@bablr/agast-vm-helpers/symbols';
2
- import * as Regex from './regex.js';
3
- import * as CSTML from './cstml.js';
4
- import * as JSON from './json.js';
1
+ import Regex from './regex.js';
2
+ import CSTML from './cstml.js';
3
+ import JSON from './json.js';
4
+ import { get } from '@bablr/agast-helpers/path';
5
5
 
6
6
  const _ = /\s+/y;
7
- const PN = 'Punctuator';
8
- const ID = 'Identifier';
7
+ const PN = null;
9
8
 
10
9
  export const name = 'Spamex';
11
10
 
@@ -15,138 +14,126 @@ export const dependencies = { CSTML, JSON, Regex };
15
14
 
16
15
  export const covers = new Map([
17
16
  [
18
- sym.node,
17
+ Symbol.for('@bablr/node'),
19
18
  new Set([
20
19
  'CSTML:Identifier',
21
20
  'PropertyMatcher',
22
21
  'JSON:String',
23
22
  'Regex:Pattern',
24
- 'BasicNodeMatcher',
23
+ 'TreeNodeMatcher',
25
24
  'GapNodeMatcher',
26
- 'ArrayNodeMatcher',
27
25
  'NullNodeMatcher',
28
26
  'ReferenceMatcher',
27
+ 'BoundNodeMatcher',
29
28
  'BindingMatcher',
30
- 'OpenNodeMatcher',
31
- 'CloseNodeMatcher',
29
+ 'TreeNodeMatcherOpen',
30
+ 'TreeNodeMatcherClose',
32
31
  'Literal',
33
32
  'CSTML:NodeFlags',
34
33
  ]),
35
34
  ],
36
35
  ['AttributeValue', new Set(['JSON:String', 'CSTML:Number'])],
37
- ['Matcher', new Set(['PropertyMatcher', 'JSON:String', 'Regex:Pattern'])],
38
- [
39
- 'NodeMatcher',
40
- new Set(['BasicNodeMatcher', 'GapNodeMatcher', 'ArrayNodeMatcher', 'NullNodeMatcher']),
41
- ],
36
+ ['Matcher', new Set(['BoundNodeMatcher'])],
37
+ ['NodeMatcher', new Set(['TreeNodeMatcher', 'GapNodeMatcher', 'NullNodeMatcher'])],
42
38
  ['StringMatcher', new Set(['JSON:String', 'Regex:Pattern'])],
43
39
  ]);
44
40
 
45
41
  export const grammar = class SpamexMiniparserGrammar {
46
- // @Cover
47
42
  Matcher(p) {
48
- if (p.match(/[a-zA-Z.#@<:]/y)) {
49
- p.eatProduction('PropertyMatcher');
50
- } else if (p.match(/['"/]/y)) {
51
- p.eatProduction('StringMatcher');
52
- } else {
53
- throw new Error(`Unexpected character ${p.chr}`);
54
- }
43
+ p.eatProduction('BoundNodeMatcher');
55
44
  }
56
45
 
57
- // @Node
58
- // @CoveredBy('NodeMatcher')
59
46
  GapNodeMatcher(p) {
60
47
  p.eat('<//>', PN, { path: 'sigilToken' });
61
48
  }
62
49
 
63
- // @Node
64
- // @CoveredBy('NodeMatcher')
65
- ArrayNodeMatcher(p) {
66
- p.eat('[]', PN, { path: 'sigilToken' });
67
- }
68
-
69
- // @Node
70
- // @CoveredBy('NodeMatcher')
71
50
  NullNodeMatcher(p) {
72
51
  p.eat('null', PN, { path: 'sigilToken' });
73
52
  }
74
53
 
75
- // @Node
76
- // @CoveredBy('Matcher')
77
54
  PropertyMatcher(p) {
78
- if (p.match(/[a-zA-Z.#@]/y)) {
55
+ if (p.match(/[a-zA-Z.#@_]/y) || p.atExpression || null) {
79
56
  p.eatProduction('ReferenceMatcher', { path: 'refMatcher' });
80
57
  }
81
58
 
82
59
  p.eatMatchTrivia(_);
83
60
 
84
- if (p.match(':')) {
85
- p.eatProduction('BindingMatcher', { path: 'bindingMatcher' });
86
- }
87
-
88
- p.eatMatchTrivia(_);
89
-
90
- p.eatProduction('NodeMatcher', { path: 'nodeMatcher' });
61
+ p.eatProduction('BoundNodeMatcher', { path: 'valueMatcher' });
91
62
  }
92
63
 
93
- // @Node
94
64
  ReferenceMatcher(p) {
95
- let name;
96
- if ((name = p.match(/[.#@]/y))) {
97
- name = p.eat(name, PN, { path: 'type' });
98
- } else if (p.match(/[A-Za-z]/y)) {
99
- name = p.eatProduction('CSTML:Identifier', { path: 'name' });
65
+ let name, type;
66
+ if ((type = p.match(/\.\.|[.#@_]/y))) {
67
+ p.eat(type, PN, { path: 'type' });
100
68
  }
101
69
 
102
- let open =
103
- name && p.eatMatch('[', PN, { path: 'openIndexToken', startSpan: 'Index', balanced: ']' });
104
-
105
- if (open) {
106
- p.eatMatchTrivia(_);
107
- p.eat(']', PN, { path: 'closeIndexToken', endSpan: 'Index', balancer: true });
70
+ if ((!type || type === '#') && p.match(/[A-Za-z]/y)) {
71
+ name = p.eatProduction('CSTML:Identifier', { path: 'name' });
108
72
  }
109
73
 
110
74
  p.eatMatchTrivia(_);
111
- if (p.match(/[+$]/y)) {
75
+ if (!['#', '@'].includes(type)) {
112
76
  p.eatProduction('CSTML:ReferenceFlags', { path: 'flags' });
113
77
  p.eatMatchTrivia(_);
114
78
  }
115
79
  p.eat(':', PN, { path: 'mapToken' });
116
80
  }
117
81
 
118
- // @Node
82
+ BoundNodeMatcher(p) {
83
+ while (p.match(':')) {
84
+ p.eatProduction('BindingMatcher', { path: 'bindingMatchers[]' });
85
+ p.eatMatchTrivia(_);
86
+ }
87
+
88
+ p.eatProduction('NodeMatcher', { path: 'nodeMatcher', noInterpolate: true });
89
+ }
90
+
119
91
  BindingMatcher(p) {
120
92
  p.eat(':', PN, { path: 'openToken' });
93
+
121
94
  p.eatMatchTrivia(_);
122
- p.eatProduction('CSTML:IdentifierPath', { path: 'languagePath' });
123
- p.eatMatchTrivia(_);
95
+ let first = true;
96
+ while (!p.match(':') && (first || p.match('/'))) {
97
+ if (!first) {
98
+ p.eat('/', PN, { path: '#separatorTokens' });
99
+ }
100
+ p.eatProduction('CSTML:BindingSegment', { path: 'segments[]' });
101
+ p.eatMatchTrivia(_);
102
+ first = false;
103
+ }
124
104
  p.eat(':', PN, { path: 'closeToken' });
125
105
  }
126
106
 
127
107
  NodeMatcher(p) {
128
- if (p.match('<//>')) {
129
- p.eatProduction('GapNodeMatcher');
130
- } else if (p.match('<')) {
131
- p.eatProduction('BasicNodeMatcher');
132
- } else if (p.match('[')) {
133
- p.eatProduction('ArrayNodeMatcher');
134
- } else if (p.match('null')) {
135
- p.eatProduction('NullNodeMatcher');
136
- } else {
137
- p.fail();
108
+ let chrs = p.match(/<\/\/?>|<|null/y);
109
+ switch (chrs) {
110
+ case '<//>':
111
+ p.eatProduction('GapNodeMatcher');
112
+ break;
113
+ case '</>':
114
+ p.fail();
115
+ break;
116
+ case 'null':
117
+ p.eatProduction('NullNodeMatcher');
118
+ break;
119
+ default:
120
+ p.eatProduction('TreeNodeMatcher', { noInterpolate: true });
121
+ break;
138
122
  }
139
123
  }
140
124
 
141
- // @Node
142
- // @CoveredBy('NodeMatcher')
143
- BasicNodeMatcher(p) {
144
- let open = p.eatProduction('OpenNodeMatcher', { path: 'open' });
125
+ TreeNodeMatcher(p) {
126
+ if (p.match(/[a-zA-Z.#@]/y) || p.atExpression) {
127
+ p.eatProduction('PropertyMatcher', { path: 'children[]', noInterpolate: true });
128
+ return;
129
+ }
145
130
 
146
- if (!open.properties.selfClosingTagToken) {
131
+ let open = p.eatProduction('TreeNodeMatcherOpen', { path: 'open' });
132
+
133
+ if (!get('selfClosingToken', open)) {
147
134
  p.eatMatchTrivia(_);
148
135
 
149
- if (open.properties.flags?.token) {
136
+ if (get('flags', open)?.token) {
150
137
  // p.eatProduction('NodeChild', { path: 'children[]' }, { token: true });
151
138
  // p.eatMatchTrivia(_);
152
139
  } else {
@@ -156,30 +143,37 @@ export const grammar = class SpamexMiniparserGrammar {
156
143
  // }
157
144
  }
158
145
 
159
- p.eatProduction('CloseNodeMatcher', { path: 'close' });
146
+ p.eatProduction('TreeNodeMatcherClose', { path: 'close' });
160
147
  }
161
148
  }
162
149
 
163
- // @Node
164
- OpenNodeMatcher(p) {
165
- p.eat('<', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
150
+ TreeNodeMatcherOpen(p) {
151
+ if (p.match(/['"/]/y)) {
152
+ p.eatProduction('CSTML:NodeFlags', { path: 'flags', token: true });
153
+ p.eatProduction('StringMatcher', { path: 'literalValue' });
154
+
155
+ return { attrs: { selfClosing: true } };
156
+ }
157
+
158
+ p.eat('<', PN, { path: 'openToken' });
166
159
 
167
160
  if (!p.atExpression) {
168
161
  p.eatProduction('CSTML:NodeFlags', { path: 'flags' });
169
162
  }
170
163
 
164
+ let type;
165
+ if ((type = p.match(/[?]|__?/y))) {
166
+ p.eat(type, PN, { path: 'type' });
167
+ }
168
+
171
169
  if (p.match(/[a-zA-Z]/y) || p.atExpression) {
172
- p.eatProduction('CSTML:Identifier', { path: 'type' });
173
- } else if (p.match('?')) {
174
- p.eat('?', PN, { path: 'type' });
175
- } else if (p.match('_')) {
176
- p.eat('_', PN, { path: 'type' });
170
+ p.eatProduction('CSTML:Identifier', { path: 'name' });
177
171
  }
178
172
 
179
173
  let sp = p.eatMatchTrivia(_);
180
174
 
181
175
  if (sp && ((p.match(/['"/]/y) && !p.match('/>')) || p.atExpression)) {
182
- p.eatProduction('StringMatcher', { path: 'intrinsicValue' });
176
+ p.eatProduction('StringMatcher', { path: 'literalValue' });
183
177
 
184
178
  sp = p.eatMatchTrivia(_);
185
179
  }
@@ -190,16 +184,17 @@ export const grammar = class SpamexMiniparserGrammar {
190
184
  }
191
185
 
192
186
  p.eatMatchTrivia(_);
193
- p.eatMatch('/', PN, { path: 'selfClosingTagToken' });
194
- p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
187
+ let sc = p.eatMatch('/', PN, { path: 'selfClosingToken' });
188
+ p.eat('>', PN, { path: 'closeToken' });
189
+
190
+ return { attrs: { selfClosing: !!sc } };
195
191
  }
196
192
 
197
- CloseNodeMatcher(p) {
198
- p.eat('</', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
199
- p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
193
+ TreeNodeMatcherClose(p) {
194
+ p.eat('</', PN, { path: 'openToken' });
195
+ p.eat('>', PN, { path: 'closeToken' });
200
196
  }
201
197
 
202
- // @Cover
203
198
  StringMatcher(p) {
204
199
  if (p.match(/['"]/y)) {
205
200
  p.eatProduction('JSON:String');
@@ -208,3 +203,5 @@ export const grammar = class SpamexMiniparserGrammar {
208
203
  }
209
204
  }
210
205
  };
206
+
207
+ export default { name, canonicalURL, dependencies, covers, grammar };
package/lib/match.js CHANGED
@@ -1,7 +1,8 @@
1
- import { createNode } from '@bablr/agast-helpers/tree';
2
1
  import { Path } from './path.js';
3
2
  import { resolveDependentLanguage } from './utils.js';
4
3
  import * as sym from '@bablr/agast-vm-helpers/symbols';
4
+ import { buildOpenCoverTag, buildOpenNodeTag, nodeFlags } from '@bablr/agast-helpers/builders';
5
+ import { buildNode } from '@bablr/agast-helpers/path';
5
6
 
6
7
  export class Match {
7
8
  constructor(parent, resolvedLanguage, id, attributes, path) {
@@ -18,10 +19,10 @@ export class Match {
18
19
  }
19
20
 
20
21
  get isNode() {
21
- const { type, resolvedLanguage } = this;
22
+ const { name, resolvedLanguage } = this;
22
23
  const { covers } = resolvedLanguage;
23
24
 
24
- return covers.get(sym.node).has(type) && !covers.has(type);
25
+ return covers.get(Symbol.for('@bablr/node')).has(name) && !covers.has(name);
25
26
  }
26
27
 
27
28
  get language() {
@@ -32,6 +33,10 @@ export class Match {
32
33
  return this.id.type;
33
34
  }
34
35
 
36
+ get name() {
37
+ return this.id.name;
38
+ }
39
+
35
40
  get attrs() {
36
41
  return this.attributes;
37
42
  }
@@ -39,21 +44,19 @@ export class Match {
39
44
  static from(language, id, attrs = {}) {
40
45
  const resolvedLanguage = resolveDependentLanguage(language, id.language);
41
46
  const { covers } = resolvedLanguage;
42
- const { type } = id;
43
- const isCover = covers.has(type);
44
- const isNode = covers.get(sym.node).has(type);
47
+ const { name } = id;
48
+ const isCover = covers.has(name);
49
+ const isNode = covers.get(Symbol.for('@bablr/node')).has(name);
45
50
 
46
51
  if (!isNode && !isCover) {
47
- throw new Error(`Top {type: ${type}} must be a node or fragment`);
52
+ throw new Error(`Top {type: ${name}} must be a node or fragment`);
48
53
  }
49
54
 
50
55
  const path = Path.from(id, attrs);
51
56
 
52
- if (isNode && !isCover) {
53
- path.node = createNode();
54
- path.node.type = type;
55
- path.node.language = resolvedLanguage.canonicalURL;
56
- }
57
+ path.node = buildNode(
58
+ isCover ? buildOpenCoverTag(nodeFlags, name) : buildOpenNodeTag(nodeFlags, name),
59
+ );
57
60
 
58
61
  return new Match(null, resolvedLanguage, id, attrs, path);
59
62
  }
@@ -61,9 +64,9 @@ export class Match {
61
64
  generate(id, attrs) {
62
65
  const resolvedLanguage = resolveDependentLanguage(this.resolvedLanguage, id.language);
63
66
  const { covers } = resolvedLanguage;
64
- const { type } = id;
65
- const isCover = covers.has(type);
66
- const isNode = covers.get(sym.node).has(type) && !isCover;
67
+ const { name } = id;
68
+ const isCover = covers.has(name);
69
+ const isNode = covers.get(Symbol.for('@bablr/node')).has(name) && !isCover;
67
70
 
68
71
  const baseAttrs = this.isNode ? {} : this.attrs;
69
72
 
@@ -76,9 +79,7 @@ export class Match {
76
79
  } else {
77
80
  path = path.generate(id, attrs);
78
81
  }
79
- path.node = createNode();
80
- path.node.type = Symbol.for(type);
81
- path.node.language = resolvedLanguage.canonicalURL;
82
+ path.node = buildNode(buildOpenNodeTag(nodeFlags, name));
82
83
  }
83
84
 
84
85
  return new Match(this, resolvedLanguage, id, { ...baseAttrs, ...attrs }, path);