@bablr/boot 0.6.3 → 0.7.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 +37 -218
- package/lib/languages/cstml.js +86 -375
- package/lib/languages/instruction.js +84 -119
- package/lib/languages/json.js +241 -0
- package/lib/languages/regex.js +76 -52
- package/lib/languages/spamex.js +119 -82
- package/lib/match.js +13 -11
- package/lib/miniparser.js +101 -80
- package/lib/path.js +42 -18
- package/lib/utils.js +14 -100
- package/package.json +7 -24
- package/shorthand.macro.js +5 -5
- package/dist/cjs.bundle.cjs +0 -5141
- package/dist/esm.bundle.mjs +0 -5139
- package/lib/builders.js +0 -475
- package/lib/esm-entry.mjs +0 -3
- package/lib/print.js +0 -401
package/lib/languages/spamex.js
CHANGED
|
@@ -1,49 +1,130 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import * as sym from '@bablr/agast-helpers/symbols';
|
|
2
|
+
import * as Regex from './regex.js';
|
|
3
|
+
import * as CSTML from './cstml.js';
|
|
4
|
+
import * as JSON from './json.js';
|
|
5
5
|
|
|
6
6
|
const _ = /\s+/y;
|
|
7
7
|
const PN = 'Punctuator';
|
|
8
8
|
const ID = 'Identifier';
|
|
9
9
|
|
|
10
|
-
const name = 'Spamex';
|
|
11
|
-
|
|
12
|
-
const canonicalURL = 'https://bablr.org/languages/core/en/spamex';
|
|
13
|
-
|
|
14
|
-
const dependencies = { CSTML, Regex };
|
|
15
|
-
|
|
16
|
-
const covers =
|
|
17
|
-
[
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
export const name = 'Spamex';
|
|
11
|
+
|
|
12
|
+
export const canonicalURL = 'https://bablr.org/languages/core/en/spamex';
|
|
13
|
+
|
|
14
|
+
export const dependencies = { CSTML, JSON, Regex };
|
|
15
|
+
|
|
16
|
+
export const covers = new Map([
|
|
17
|
+
[
|
|
18
|
+
sym.node,
|
|
19
|
+
new Set([
|
|
20
|
+
'Identifier',
|
|
21
|
+
'PropertyMatcher',
|
|
22
|
+
'JSON:String',
|
|
23
|
+
'Regex:Pattern',
|
|
24
|
+
'BasicNodeMatcher',
|
|
25
|
+
'GapNodeMatcher',
|
|
26
|
+
'ArrayNodeMatcher',
|
|
27
|
+
'NullNodeMatcher',
|
|
28
|
+
'ReferenceMatcher',
|
|
29
|
+
'OpenNodeMatcher',
|
|
30
|
+
'CloseNodeMatcher',
|
|
31
|
+
'Literal',
|
|
32
|
+
'CSTML:NodeFlags',
|
|
33
|
+
]),
|
|
34
|
+
],
|
|
35
|
+
['AttributeValue', new Set(['JSON:String', 'CSTML:Number'])],
|
|
36
|
+
['Matcher', new Set(['PropertyMatcher', 'JSON:String', 'Regex:Pattern'])],
|
|
37
|
+
[
|
|
38
|
+
'NodeMatcher',
|
|
39
|
+
new Set(['BasicNodeMatcher', 'GapNodeMatcher', 'ArrayNodeMatcher', 'NullNodeMatcher']),
|
|
25
40
|
],
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Matcher: ['NodeMatcher', 'StringMatcher'],
|
|
29
|
-
StringMatcher: ['CSTML:String', 'Regex:Pattern'],
|
|
30
|
-
});
|
|
41
|
+
['StringMatcher', new Set(['JSON:String', 'Regex:Pattern'])],
|
|
42
|
+
]);
|
|
31
43
|
|
|
32
|
-
const grammar = class SpamexMiniparserGrammar {
|
|
44
|
+
export const grammar = class SpamexMiniparserGrammar {
|
|
33
45
|
// @Cover
|
|
34
46
|
Matcher(p) {
|
|
35
|
-
if (p.match(
|
|
36
|
-
p.eatProduction('
|
|
37
|
-
} else if (p.match(/['"]/y)) {
|
|
38
|
-
p.eatProduction('
|
|
39
|
-
} else if (p.match('/')) {
|
|
40
|
-
p.eatProduction('Regex:Pattern');
|
|
47
|
+
if (p.match(/[a-zA-Z.#@<]/y)) {
|
|
48
|
+
p.eatProduction('PropertyMatcher');
|
|
49
|
+
} else if (p.match(/['"/]/y)) {
|
|
50
|
+
p.eatProduction('StringMatcher');
|
|
41
51
|
} else {
|
|
42
52
|
throw new Error(`Unexpected character ${p.chr}`);
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
|
|
56
|
+
// @Node
|
|
57
|
+
// @CoveredBy('NodeMatcher')
|
|
58
|
+
GapNodeMatcher(p) {
|
|
59
|
+
p.eat('<//>', PN, { path: 'sigilToken' });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// @Node
|
|
63
|
+
// @CoveredBy('NodeMatcher')
|
|
64
|
+
ArrayNodeMatcher(p) {
|
|
65
|
+
p.eat('[]', PN, { path: 'sigilToken' });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// @Node
|
|
69
|
+
// @CoveredBy('NodeMatcher')
|
|
70
|
+
NullNodeMatcher(p) {
|
|
71
|
+
p.eat('null', PN, { path: 'sigilToken' });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// @Node
|
|
75
|
+
// @CoveredBy('Matcher')
|
|
76
|
+
PropertyMatcher(p) {
|
|
77
|
+
if (p.match(/[a-zA-Z.#@]/y)) {
|
|
78
|
+
p.eatProduction('ReferenceMatcher', { path: 'refMatcher' });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
p.eatMatchTrivia(_);
|
|
82
|
+
|
|
83
|
+
p.eatProduction('NodeMatcher', { path: 'nodeMatcher' });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// @Node
|
|
87
|
+
ReferenceMatcher(p) {
|
|
88
|
+
let name;
|
|
89
|
+
if ((name = p.match(/[.#@]/y))) {
|
|
90
|
+
name = p.eat(name, PN, { path: 'name' });
|
|
91
|
+
} else if (p.match(/[A-Za-z]/y)) {
|
|
92
|
+
name = p.eatProduction('CSTML:Identifier', { path: 'name' });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let open =
|
|
96
|
+
name && p.eatMatch('[', PN, { path: 'openIndexToken', startSpan: 'Index', balanced: ']' });
|
|
97
|
+
|
|
98
|
+
if (open) {
|
|
99
|
+
p.eatMatchTrivia(_);
|
|
100
|
+
p.eat(']', PN, { path: 'closeIndexToken', endSpan: 'Index', balancer: true });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
p.eatMatchTrivia(_);
|
|
104
|
+
if (p.match(/[+$]/y)) {
|
|
105
|
+
p.eatProduction('CSTML:ReferenceFlags', { path: 'flags' });
|
|
106
|
+
p.eatMatchTrivia(_);
|
|
107
|
+
}
|
|
108
|
+
p.eat(':', PN, { path: 'mapToken' });
|
|
109
|
+
}
|
|
110
|
+
|
|
46
111
|
NodeMatcher(p) {
|
|
112
|
+
if (p.match('<//>')) {
|
|
113
|
+
p.eatProduction('GapNodeMatcher');
|
|
114
|
+
} else if (p.match('<')) {
|
|
115
|
+
p.eatProduction('BasicNodeMatcher');
|
|
116
|
+
} else if (p.match('[')) {
|
|
117
|
+
p.eatProduction('ArrayNodeMatcher');
|
|
118
|
+
} else if (p.match('null')) {
|
|
119
|
+
p.eatProduction('NullNodeMatcher');
|
|
120
|
+
} else {
|
|
121
|
+
p.fail();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// @Node
|
|
126
|
+
// @CoveredBy('NodeMatcher')
|
|
127
|
+
BasicNodeMatcher(p) {
|
|
47
128
|
let open = p.eatProduction('OpenNodeMatcher', { path: 'open' });
|
|
48
129
|
|
|
49
130
|
if (!open.properties.selfClosingTagToken) {
|
|
@@ -68,11 +149,11 @@ const grammar = class SpamexMiniparserGrammar {
|
|
|
68
149
|
p.eat('<', PN, { path: 'openToken', startSpan: 'Tag', balanced: '>' });
|
|
69
150
|
|
|
70
151
|
if (!p.atExpression) {
|
|
71
|
-
p.eatProduction('CSTML:
|
|
152
|
+
p.eatProduction('CSTML:NodeFlags', { path: 'flags' });
|
|
72
153
|
}
|
|
73
154
|
|
|
74
|
-
if (p.match(/['"]|[a-zA-Z]+:/y)) {
|
|
75
|
-
p.eatProduction('CSTML:TagType', { path: 'type' });
|
|
155
|
+
if (p.match(/['"]|[a-zA-Z]+:/y) || p.atExpression) {
|
|
156
|
+
p.eatProduction('CSTML:TagType', { path: 'type', noInterpolate: true });
|
|
76
157
|
} else if (p.match('?')) {
|
|
77
158
|
p.eat('?', PN, { path: 'type' });
|
|
78
159
|
} else if (p.match(' ')) {
|
|
@@ -93,9 +174,9 @@ const grammar = class SpamexMiniparserGrammar {
|
|
|
93
174
|
sp = p.eatMatchTrivia(_);
|
|
94
175
|
}
|
|
95
176
|
|
|
96
|
-
if (
|
|
97
|
-
p.eatProduction('
|
|
98
|
-
|
|
177
|
+
if (p.match('{') || p.atExpression) {
|
|
178
|
+
p.eatProduction('JSON:Object', { path: 'attributes' });
|
|
179
|
+
p.eatMatchTrivia(_);
|
|
99
180
|
}
|
|
100
181
|
|
|
101
182
|
p.eatMatchTrivia(_);
|
|
@@ -108,52 +189,10 @@ const grammar = class SpamexMiniparserGrammar {
|
|
|
108
189
|
p.eat('>', PN, { path: 'closeToken', endSpan: 'Tag', balancer: true });
|
|
109
190
|
}
|
|
110
191
|
|
|
111
|
-
Attributes(p) {
|
|
112
|
-
let sp = true;
|
|
113
|
-
while (sp && (p.match(/[a-zA-Z]+/y) || p.atExpression)) {
|
|
114
|
-
p.eatProduction('Attribute');
|
|
115
|
-
if (p.match(/\s+[a-zA-Z]/y)) {
|
|
116
|
-
sp = p.eatMatchTrivia(_);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// @Cover
|
|
122
|
-
Attribute(p) {
|
|
123
|
-
if (p.match(/[a-zA-Z]+\s*=/y)) {
|
|
124
|
-
p.eatProduction('MappingAttribute');
|
|
125
|
-
} else {
|
|
126
|
-
p.eatProduction('BooleanAttribute');
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// @Node
|
|
131
|
-
BooleanAttribute(p) {
|
|
132
|
-
p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// @Node
|
|
136
|
-
MappingAttribute(p) {
|
|
137
|
-
p.eat(/[a-zA-Z]+/y, ID, { path: 'key' });
|
|
138
|
-
p.eatMatchTrivia(_);
|
|
139
|
-
p.eat('=', PN, { path: 'mapToken' });
|
|
140
|
-
p.eatMatchTrivia(_);
|
|
141
|
-
p.eatProduction('AttributeValue', { path: 'value' });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// @Cover
|
|
145
|
-
AttributeValue(p) {
|
|
146
|
-
if (p.match(/['"]/y)) {
|
|
147
|
-
p.eatProduction('CSTML:String');
|
|
148
|
-
} else if (p.match(/-|\d/y)) {
|
|
149
|
-
p.eatProduction('CSTML:Number');
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
192
|
// @Cover
|
|
154
193
|
StringMatcher(p) {
|
|
155
194
|
if (p.match(/['"]/y)) {
|
|
156
|
-
p.eatProduction('
|
|
195
|
+
p.eatProduction('JSON:String');
|
|
157
196
|
} else {
|
|
158
197
|
p.eatProduction('Regex:Pattern');
|
|
159
198
|
}
|
|
@@ -164,5 +203,3 @@ const grammar = class SpamexMiniparserGrammar {
|
|
|
164
203
|
p.eatLiteral(/[a-zA-Z]+/y);
|
|
165
204
|
}
|
|
166
205
|
};
|
|
167
|
-
|
|
168
|
-
module.exports = { name, canonicalURL, dependencies, covers, grammar };
|
package/lib/match.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { createNode } from '@bablr/agast-helpers/tree';
|
|
2
|
+
import { Path } from './path.js';
|
|
3
|
+
import { resolveDependentLanguage } from './utils.js';
|
|
4
|
+
import * as sym from '@bablr/agast-helpers/symbols';
|
|
4
5
|
|
|
5
|
-
class Match {
|
|
6
|
+
export class Match {
|
|
6
7
|
constructor(parent, resolvedLanguage, id, attributes, path) {
|
|
7
8
|
this.parent = parent;
|
|
8
9
|
this.resolvedLanguage = resolvedLanguage;
|
|
@@ -41,16 +42,17 @@ class Match {
|
|
|
41
42
|
const { type } = id;
|
|
42
43
|
const isCover = covers.has(type);
|
|
43
44
|
const isNode = covers.get(sym.node).has(type);
|
|
44
|
-
const isFragment = covers.get(sym.fragment)?.has(type);
|
|
45
45
|
|
|
46
|
-
if (!isNode && !
|
|
46
|
+
if (!isNode && !isCover) {
|
|
47
47
|
throw new Error(`Top {type: ${type}} must be a node or fragment`);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const path = Path.from(id, attrs);
|
|
51
51
|
|
|
52
|
-
if (
|
|
53
|
-
path.node =
|
|
52
|
+
if (isNode && !isCover) {
|
|
53
|
+
path.node = createNode();
|
|
54
|
+
path.node.type = type;
|
|
55
|
+
path.node.language = resolvedLanguage.canonicalURL;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
return new Match(null, resolvedLanguage, id, attrs, path);
|
|
@@ -74,11 +76,11 @@ class Match {
|
|
|
74
76
|
} else {
|
|
75
77
|
path = path.generate(id, attrs);
|
|
76
78
|
}
|
|
77
|
-
path.node =
|
|
79
|
+
path.node = createNode();
|
|
80
|
+
path.node.type = Symbol.for(type);
|
|
81
|
+
path.node.language = resolvedLanguage.canonicalURL;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
return new Match(this, resolvedLanguage, id, { ...baseAttrs, ...attrs }, path);
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
|
-
|
|
84
|
-
module.exports = { Match };
|
package/lib/miniparser.js
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import escapeRegex from 'escape-string-regexp';
|
|
2
|
+
import arrayLast from 'iter-tools/methods/array-last';
|
|
3
|
+
import isString from 'iter-tools/methods/is-string';
|
|
4
|
+
import isObject from 'iter-tools/methods/is-object';
|
|
5
|
+
import * as sym from '@bablr/agast-helpers/symbols';
|
|
6
|
+
import { Match } from './match.js';
|
|
7
|
+
import { parsePath } from './path.js';
|
|
8
|
+
import { isRegex, isArray, getPrototypeOf } from './utils.js';
|
|
9
|
+
import { ReferenceTag, LiteralTag, ShiftTag } from '@bablr/agast-helpers/symbols';
|
|
10
|
+
import {
|
|
11
|
+
buildCloseNodeTag,
|
|
12
|
+
buildLiteralTag,
|
|
13
|
+
buildOpenNodeTag,
|
|
14
|
+
buildReferenceTag,
|
|
15
|
+
nodeFlags,
|
|
16
|
+
} from '@bablr/agast-helpers/builders';
|
|
17
|
+
import { add, buildGapTag, buildShiftTag, buildToken } from '@bablr/agast-helpers/tree';
|
|
18
|
+
import * as btree from '@bablr/agast-helpers/btree';
|
|
19
|
+
import { get } from '@bablr/agast-helpers/path';
|
|
20
|
+
|
|
21
|
+
const Escape = Symbol.for('Escape');
|
|
22
|
+
|
|
23
|
+
const getProduction = (grammar, type) => {
|
|
24
|
+
return getPrototypeOf(grammar)[type];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export class TemplateParser {
|
|
15
28
|
constructor(rootLanguage, quasis, expressions) {
|
|
16
29
|
if (!quasis) throw new Error();
|
|
17
30
|
|
|
@@ -75,7 +88,7 @@ class TemplateParser {
|
|
|
75
88
|
}
|
|
76
89
|
|
|
77
90
|
get matchIsFragment() {
|
|
78
|
-
return
|
|
91
|
+
return false;
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
get span() {
|
|
@@ -123,7 +136,7 @@ class TemplateParser {
|
|
|
123
136
|
}
|
|
124
137
|
}
|
|
125
138
|
|
|
126
|
-
eval(id, attrs = {}, props = {}) {
|
|
139
|
+
eval(id, attrs = {}, props = {}, shift = null) {
|
|
127
140
|
const parentMatch = this.m;
|
|
128
141
|
const parentPath = this.path?.node ? this.path : this.path?.parent;
|
|
129
142
|
const { type } = id;
|
|
@@ -143,13 +156,12 @@ class TemplateParser {
|
|
|
143
156
|
const { covers } = this.language;
|
|
144
157
|
const isNode = this.matchIsNode;
|
|
145
158
|
const isCover = this.matchIsCover;
|
|
146
|
-
const isFragment = this.matchIsFragment;
|
|
147
159
|
const isEmbedded = this.language !== this.m.parent?.resolvedLanguage;
|
|
148
160
|
const { path, grammar } = this;
|
|
149
161
|
|
|
150
162
|
if (!type) throw new Error('eval requires a type');
|
|
151
163
|
|
|
152
|
-
if (parentPath?.node && this.atExpression && (isNode || isCover
|
|
164
|
+
if (parentPath?.node && this.atExpression && !attrs.noInterpolate && (isNode || isCover)) {
|
|
153
165
|
const { quasisDone } = this;
|
|
154
166
|
|
|
155
167
|
if (quasisDone) throw new Error('there must be more quasis than expressions');
|
|
@@ -160,38 +172,20 @@ class TemplateParser {
|
|
|
160
172
|
this.quasiIdx++;
|
|
161
173
|
this.idx = 0;
|
|
162
174
|
|
|
163
|
-
if (parentPath?.node &&
|
|
164
|
-
const { properties, children } = parentPath.node;
|
|
165
|
-
|
|
166
|
-
if (result) {
|
|
167
|
-
children.push(...result.children);
|
|
168
|
-
|
|
169
|
-
for (const { 0: key, 1: property } of Object.entries(result.properties)) {
|
|
170
|
-
if (isArray(property)) {
|
|
171
|
-
for (const value of property) {
|
|
172
|
-
set(properties, { name: key, isArray: true }, value);
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
set(properties, { name: key, isArray: false }, property);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
} else if (parentPath?.node && (isNode || covers.has(type))) {
|
|
175
|
+
if (parentPath?.node && (isNode || covers.has(type))) {
|
|
180
176
|
const { properties, children } = parentPath.node;
|
|
181
177
|
const path = parsePath(this.m.attrs.path);
|
|
182
178
|
|
|
183
179
|
if (isArray(result)) {
|
|
184
180
|
for (const value of result) {
|
|
185
|
-
children.push(
|
|
186
|
-
|
|
187
|
-
// TODO interpolate separators!
|
|
181
|
+
children.push(path);
|
|
188
182
|
|
|
189
|
-
|
|
183
|
+
add(parentPath.node, path, value);
|
|
190
184
|
}
|
|
191
185
|
} else {
|
|
192
|
-
children.push(
|
|
186
|
+
children.push(path);
|
|
193
187
|
|
|
194
|
-
|
|
188
|
+
add(parentPath.node, path, result);
|
|
195
189
|
}
|
|
196
190
|
}
|
|
197
191
|
} else {
|
|
@@ -199,10 +193,18 @@ class TemplateParser {
|
|
|
199
193
|
this.spans.push({ type: 'Bare', guard: null });
|
|
200
194
|
}
|
|
201
195
|
|
|
202
|
-
if (!
|
|
196
|
+
if (!getProduction(grammar, type)) {
|
|
203
197
|
throw new Error(`Unknown production {type: ${type}}`);
|
|
204
198
|
}
|
|
205
199
|
|
|
200
|
+
if (isNode) {
|
|
201
|
+
let { node } = this.path;
|
|
202
|
+
node.children = btree.push(
|
|
203
|
+
node.children,
|
|
204
|
+
buildOpenNodeTag(nodeFlags, node.language, node.type),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
206
208
|
const result = getPrototypeOf(grammar)[type].call(grammar, this, props);
|
|
207
209
|
|
|
208
210
|
if (isEmbedded) {
|
|
@@ -213,14 +215,19 @@ class TemplateParser {
|
|
|
213
215
|
const { node } = this.path;
|
|
214
216
|
if (result?.attrs) {
|
|
215
217
|
node.attributes = result.attrs;
|
|
218
|
+
node.children = btree.replaceAt(
|
|
219
|
+
0,
|
|
220
|
+
node.children,
|
|
221
|
+
buildOpenNodeTag(nodeFlags, node.language, node.type, result.attrs),
|
|
222
|
+
);
|
|
216
223
|
}
|
|
217
224
|
|
|
218
|
-
|
|
219
|
-
const path = parsePath(this.m.attrs.path);
|
|
225
|
+
node.children = btree.push(node.children, buildCloseNodeTag());
|
|
220
226
|
|
|
221
|
-
|
|
227
|
+
if (parentPath?.node && !covers.has(type) && shift == null) {
|
|
228
|
+
const path = parsePath(this.m.attrs.path);
|
|
222
229
|
|
|
223
|
-
|
|
230
|
+
add(parentPath.node, path, node, shift);
|
|
224
231
|
}
|
|
225
232
|
}
|
|
226
233
|
}
|
|
@@ -284,37 +291,36 @@ class TemplateParser {
|
|
|
284
291
|
|
|
285
292
|
const path = parsePath(attrs.path);
|
|
286
293
|
|
|
287
|
-
children.push(
|
|
288
|
-
|
|
294
|
+
children.push(path);
|
|
295
|
+
add(this.node, path, held);
|
|
289
296
|
|
|
290
297
|
return held;
|
|
291
298
|
}
|
|
292
299
|
|
|
293
300
|
shiftProduction(id, attrs = {}, props = {}) {
|
|
294
|
-
const {
|
|
301
|
+
const { node } = this;
|
|
302
|
+
const { properties } = node;
|
|
295
303
|
// don't push a new path onto the stack
|
|
296
304
|
|
|
297
305
|
// get the most recently produced node and detach it from its parent
|
|
298
306
|
|
|
299
|
-
const
|
|
307
|
+
const ref = btree.getAt(-2, node.children);
|
|
308
|
+
|
|
309
|
+
if (!ref.value.flags.expression) throw new Error();
|
|
300
310
|
|
|
301
|
-
if (
|
|
311
|
+
if (ref.type !== ReferenceTag) {
|
|
302
312
|
throw new Error();
|
|
303
313
|
}
|
|
304
314
|
|
|
305
|
-
|
|
315
|
+
this.held = get(ref, node);
|
|
306
316
|
|
|
307
|
-
|
|
317
|
+
let id_ = this.buildId(id);
|
|
308
318
|
|
|
309
|
-
|
|
319
|
+
const shifted = this.eval(id_, attrs, props, 1);
|
|
310
320
|
|
|
311
|
-
|
|
312
|
-
properties[name].pop();
|
|
313
|
-
} else {
|
|
314
|
-
properties[name] = null;
|
|
315
|
-
}
|
|
321
|
+
add(node, ref, shifted, 1);
|
|
316
322
|
|
|
317
|
-
return
|
|
323
|
+
return shifted;
|
|
318
324
|
}
|
|
319
325
|
|
|
320
326
|
eat(pattern, type, attrs) {
|
|
@@ -332,10 +338,9 @@ class TemplateParser {
|
|
|
332
338
|
this.updateSpans(attrs);
|
|
333
339
|
|
|
334
340
|
const path_ = parsePath(attrs.path);
|
|
341
|
+
const language = this.language.canonicalURL;
|
|
335
342
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
this.node.children.push(ref(path_));
|
|
343
|
+
add(this.node, path_, buildToken(language, type, result, _attrs));
|
|
339
344
|
|
|
340
345
|
return result;
|
|
341
346
|
}
|
|
@@ -349,12 +354,7 @@ class TemplateParser {
|
|
|
349
354
|
if (!isString(type)) throw new Error('Cannot eatMatch anonymous token');
|
|
350
355
|
if (!isObject(attrs) || !attrs.path) throw new Error('a node must have a path');
|
|
351
356
|
|
|
352
|
-
|
|
353
|
-
if (this.atExpression) {
|
|
354
|
-
} else {
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
result = this.matchSticky(pattern, attrs, this);
|
|
357
|
+
const result = this.matchSticky(pattern, attrs, this);
|
|
358
358
|
|
|
359
359
|
if (result) {
|
|
360
360
|
this.updateSpans(attrs);
|
|
@@ -362,10 +362,9 @@ class TemplateParser {
|
|
|
362
362
|
this.idx += result.length;
|
|
363
363
|
|
|
364
364
|
const path = parsePath(attrs.path);
|
|
365
|
+
const language = this.language.canonicalURL;
|
|
365
366
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
this.node.children.push(ref(path));
|
|
367
|
+
add(this.node, path, buildToken(language, type, result));
|
|
369
368
|
}
|
|
370
369
|
return result;
|
|
371
370
|
}
|
|
@@ -377,7 +376,11 @@ class TemplateParser {
|
|
|
377
376
|
|
|
378
377
|
this.idx += result.length;
|
|
379
378
|
|
|
380
|
-
|
|
379
|
+
add(
|
|
380
|
+
this.node,
|
|
381
|
+
buildReferenceTag('#'),
|
|
382
|
+
buildToken('https://bablr.org/languages/core/en/space-tab-newline', 'Space', result),
|
|
383
|
+
);
|
|
381
384
|
|
|
382
385
|
return result;
|
|
383
386
|
}
|
|
@@ -388,7 +391,11 @@ class TemplateParser {
|
|
|
388
391
|
if (result) {
|
|
389
392
|
this.idx += result.length;
|
|
390
393
|
|
|
391
|
-
|
|
394
|
+
add(
|
|
395
|
+
this.node,
|
|
396
|
+
buildReferenceTag('#'),
|
|
397
|
+
buildToken('https://bablr.org/languages/core/en/space-tab-newline', 'Space', result),
|
|
398
|
+
);
|
|
392
399
|
}
|
|
393
400
|
|
|
394
401
|
return result;
|
|
@@ -401,7 +408,15 @@ class TemplateParser {
|
|
|
401
408
|
|
|
402
409
|
this.idx += result.length;
|
|
403
410
|
|
|
404
|
-
|
|
411
|
+
const raw = result;
|
|
412
|
+
const cooked = this.language.cookEscape(result, this.span);
|
|
413
|
+
const attributes = { cooked };
|
|
414
|
+
|
|
415
|
+
add(
|
|
416
|
+
this.node,
|
|
417
|
+
buildReferenceTag('@'),
|
|
418
|
+
buildToken(this.language.canonicalURL, 'Escape', raw, attributes),
|
|
419
|
+
);
|
|
405
420
|
|
|
406
421
|
return result;
|
|
407
422
|
}
|
|
@@ -412,7 +427,15 @@ class TemplateParser {
|
|
|
412
427
|
if (result) {
|
|
413
428
|
this.idx += result.length;
|
|
414
429
|
|
|
415
|
-
|
|
430
|
+
const raw = result;
|
|
431
|
+
const cooked = this.language.cookEscape(result, this.span);
|
|
432
|
+
const attributes = { cooked };
|
|
433
|
+
|
|
434
|
+
add(
|
|
435
|
+
this.node,
|
|
436
|
+
buildReferenceTag('@'),
|
|
437
|
+
buildToken(this.language.canonicalURL, 'Escape', raw, attributes),
|
|
438
|
+
);
|
|
416
439
|
}
|
|
417
440
|
|
|
418
441
|
return result;
|
|
@@ -425,7 +448,7 @@ class TemplateParser {
|
|
|
425
448
|
|
|
426
449
|
this.idx += result.length;
|
|
427
450
|
|
|
428
|
-
this.node.children.push(
|
|
451
|
+
this.node.children.push(buildLiteralTag(result));
|
|
429
452
|
|
|
430
453
|
return result;
|
|
431
454
|
}
|
|
@@ -436,7 +459,7 @@ class TemplateParser {
|
|
|
436
459
|
if (result) {
|
|
437
460
|
this.idx += result.length;
|
|
438
461
|
|
|
439
|
-
this.node.children.push(
|
|
462
|
+
this.node.children.push(buildLiteralTag(result));
|
|
440
463
|
}
|
|
441
464
|
|
|
442
465
|
return result;
|
|
@@ -462,5 +485,3 @@ class TemplateParser {
|
|
|
462
485
|
throw new Error(`miniparser: parsing \`${this.quasis}\` failed`);
|
|
463
486
|
}
|
|
464
487
|
}
|
|
465
|
-
|
|
466
|
-
module.exports = { TemplateParser };
|