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