@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.
- package/lib/index.js +45 -15
- package/lib/languages/cstml.js +196 -117
- package/lib/languages/instruction.js +32 -22
- package/lib/languages/json.js +22 -30
- package/lib/languages/regex.js +6 -32
- package/lib/languages/spamex.js +89 -92
- package/lib/match.js +19 -18
- package/lib/miniparser.js +143 -114
- package/lib/path.js +5 -36
- package/package.json +8 -5
package/lib/languages/spamex.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
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 =
|
|
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
|
-
|
|
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
|
-
'
|
|
23
|
+
'TreeNodeMatcher',
|
|
25
24
|
'GapNodeMatcher',
|
|
26
|
-
'ArrayNodeMatcher',
|
|
27
25
|
'NullNodeMatcher',
|
|
28
26
|
'ReferenceMatcher',
|
|
27
|
+
'BoundNodeMatcher',
|
|
29
28
|
'BindingMatcher',
|
|
30
|
-
'
|
|
31
|
-
'
|
|
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(['
|
|
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
|
-
|
|
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
|
-
|
|
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 ((
|
|
97
|
-
|
|
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
|
-
|
|
103
|
-
name
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
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
|
+
}
|
|
124
104
|
p.eat(':', PN, { path: 'closeToken' });
|
|
125
105
|
}
|
|
126
106
|
|
|
127
107
|
NodeMatcher(p) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
131
|
+
let open = p.eatProduction('TreeNodeMatcherOpen', { path: 'open' });
|
|
132
|
+
|
|
133
|
+
if (!get('selfClosingToken', open)) {
|
|
147
134
|
p.eatMatchTrivia(_);
|
|
148
135
|
|
|
149
|
-
if (open
|
|
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('
|
|
146
|
+
p.eatProduction('TreeNodeMatcherClose', { path: 'close' });
|
|
160
147
|
}
|
|
161
148
|
}
|
|
162
149
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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: '
|
|
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: '
|
|
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: '
|
|
194
|
-
p.eat('>', PN, { path: 'closeToken'
|
|
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
|
-
|
|
198
|
-
p.eat('</', PN, { path: 'openToken'
|
|
199
|
-
p.eat('>', PN, { path: 'closeToken'
|
|
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 {
|
|
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,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 {
|
|
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
|
-
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 {
|
|
65
|
-
const isCover = covers.has(
|
|
66
|
-
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;
|
|
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 =
|
|
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);
|