@bablr/agast-helpers 0.10.2 → 0.10.4
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/builders.js +16 -4
- package/lib/path.js +10 -12
- package/lib/spans.js +163 -0
- package/lib/tags.js +16 -14
- package/lib/template.js +2 -2
- package/lib/tree.js +2 -2
- package/package.json +3 -2
package/lib/builders.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
TreeNode,
|
|
19
19
|
} from './symbols.js';
|
|
20
20
|
|
|
21
|
-
const { freeze } = Object;
|
|
21
|
+
const { freeze, values } = Object;
|
|
22
22
|
const { isArray } = Array;
|
|
23
23
|
|
|
24
24
|
export const deepFreeze = (object) => {
|
|
@@ -27,16 +27,28 @@ export const deepFreeze = (object) => {
|
|
|
27
27
|
let item = list.value;
|
|
28
28
|
list = list.pop();
|
|
29
29
|
|
|
30
|
-
for (const value of
|
|
30
|
+
for (const value of values(item)) {
|
|
31
31
|
if (isObject(value)) {
|
|
32
32
|
list = list.push(value);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
freeze(item);
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
export const buildSpan = (name, guard = null, props = {}) => {
|
|
41
|
+
if (!name) throw new Error();
|
|
42
|
+
deepFreeze(props);
|
|
43
|
+
return freeze({ type: 'Explicit', name, guard, props });
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const buildTypedSpan = (type, name, guard = null, props = {}) => {
|
|
47
|
+
if (!type || !name) throw new Error();
|
|
48
|
+
deepFreeze(props);
|
|
49
|
+
return freeze({ type, name, guard, props });
|
|
50
|
+
};
|
|
51
|
+
|
|
40
52
|
export const buildProperty = (tags, shift) => {
|
|
41
53
|
if (shift && !shift.index) throw new Error();
|
|
42
54
|
freeze(tags);
|
|
@@ -163,7 +175,7 @@ export const buildShiftTag = () => {
|
|
|
163
175
|
return freeze({ type: ShiftTag, value });
|
|
164
176
|
};
|
|
165
177
|
|
|
166
|
-
export const buildDoctypeTag = (version = 0, attributes =
|
|
178
|
+
export const buildDoctypeTag = (version = 0, attributes = freeze({})) => {
|
|
167
179
|
return freeze({
|
|
168
180
|
type: DoctypeTag,
|
|
169
181
|
value: freeze({ doctype: 'cstml', version, attributes }),
|
package/lib/path.js
CHANGED
|
@@ -377,10 +377,10 @@ export const getChildPropertyIndex = (agAstNode, tagsIndex) => {
|
|
|
377
377
|
|
|
378
378
|
if (Array.isArray(value)) {
|
|
379
379
|
let childNode = value;
|
|
380
|
-
let {
|
|
380
|
+
let { names } = Tags.getSums(childNode);
|
|
381
381
|
|
|
382
382
|
let res;
|
|
383
|
-
if ((res = Tags.getAtName(name,
|
|
383
|
+
if ((res = Tags.getAtName(name, names))) {
|
|
384
384
|
count += res;
|
|
385
385
|
}
|
|
386
386
|
}
|
|
@@ -401,7 +401,7 @@ export const getPropertyTagsIndex = (agAstNode, type, name, index, shiftIndex) =
|
|
|
401
401
|
let prop = firstPropsIndex == null ? null : Tags.getAt(firstPropsIndex, getTags(agAstNode));
|
|
402
402
|
let ref = prop?.value.reference;
|
|
403
403
|
let sums = Tags.getSums(agAstNode.value.tags);
|
|
404
|
-
let counts = name ? sums.
|
|
404
|
+
let counts = name ? sums.names : sums.types;
|
|
405
405
|
|
|
406
406
|
if (ref?.flags.array) {
|
|
407
407
|
if (index < 0) {
|
|
@@ -453,9 +453,7 @@ const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
|
|
|
453
453
|
if (!sums) return null;
|
|
454
454
|
|
|
455
455
|
let valueNameCount =
|
|
456
|
-
name != null
|
|
457
|
-
? Tags.getAtName(name, sums.references)
|
|
458
|
-
: Tags.getAtName(type, sums.specialTypes);
|
|
456
|
+
name != null ? Tags.getAtName(name, sums.names) : Tags.getAtName(type, sums.types);
|
|
459
457
|
|
|
460
458
|
if (nameCount + valueNameCount < index) {
|
|
461
459
|
return null;
|
|
@@ -484,8 +482,8 @@ const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
|
|
|
484
482
|
if (
|
|
485
483
|
nameCount +
|
|
486
484
|
(name != null
|
|
487
|
-
? Tags.getAtName(name, valueSums.
|
|
488
|
-
: Tags.getAtName(type, valueSums.
|
|
485
|
+
? Tags.getAtName(name, valueSums.names)
|
|
486
|
+
: Tags.getAtName(type, valueSums.types)) >
|
|
489
487
|
index
|
|
490
488
|
) {
|
|
491
489
|
node = value;
|
|
@@ -493,8 +491,8 @@ const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
|
|
|
493
491
|
} else {
|
|
494
492
|
nameCount +=
|
|
495
493
|
(name != null
|
|
496
|
-
? Tags.getAtName(name, valueSums.
|
|
497
|
-
: Tags.getAtName(type, valueSums.
|
|
494
|
+
? Tags.getAtName(name, valueSums.names)
|
|
495
|
+
: Tags.getAtName(type, valueSums.types)) ?? 0;
|
|
498
496
|
idx += Tags.getSize(value);
|
|
499
497
|
}
|
|
500
498
|
}
|
|
@@ -653,7 +651,7 @@ export const countList = (name, node) => {
|
|
|
653
651
|
|
|
654
652
|
if (name == null) throw new Error('Bad path');
|
|
655
653
|
|
|
656
|
-
return Tags.getAtName(name, Tags.getSums(getTags(node)).
|
|
654
|
+
return Tags.getAtName(name, Tags.getSums(getTags(node)).names);
|
|
657
655
|
};
|
|
658
656
|
|
|
659
657
|
export function* allTagPathsFor(range, options = {}) {
|
|
@@ -1473,7 +1471,7 @@ export const Path = class AgastPath {
|
|
|
1473
1471
|
rootIdx != null &&
|
|
1474
1472
|
!referencesAreEqual(
|
|
1475
1473
|
tag.value,
|
|
1476
|
-
Tags.getAt(rootIdx, getTags(targetPath.node)).value.
|
|
1474
|
+
Tags.getAt(rootIdx, getTags(targetPath.node)).value.reference,
|
|
1477
1475
|
)
|
|
1478
1476
|
) {
|
|
1479
1477
|
throw new Error('mismatched references');
|
package/lib/spans.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { buildModule, defaultNodeSize } from '@bablr/btree/enhanceable';
|
|
2
|
+
|
|
3
|
+
const { isArray } = Array;
|
|
4
|
+
const { freeze } = Object;
|
|
5
|
+
|
|
6
|
+
export { defaultNodeSize };
|
|
7
|
+
|
|
8
|
+
const __getAtName = (name, sortedArray, startIdx = 0, endIdx = sortedArray.length - 1) => {
|
|
9
|
+
if (!sortedArray.length || endIdx < startIdx) return null;
|
|
10
|
+
|
|
11
|
+
let idx = startIdx + Math.floor((endIdx - startIdx) / 2 + 0.1);
|
|
12
|
+
let entry = sortedArray[idx];
|
|
13
|
+
let { 0: key, 1: value } = entry;
|
|
14
|
+
|
|
15
|
+
let direction = compareNames(name, key);
|
|
16
|
+
|
|
17
|
+
if (direction === 0) {
|
|
18
|
+
return value;
|
|
19
|
+
} else {
|
|
20
|
+
if (startIdx === endIdx) return null;
|
|
21
|
+
|
|
22
|
+
if (direction > 0) {
|
|
23
|
+
return __getAtName(name, sortedArray, idx + 1, endIdx);
|
|
24
|
+
} else {
|
|
25
|
+
return __getAtName(name, sortedArray, startIdx, idx - 1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getAtName = (name, sortedArray, startIdx = 0, endIdx = sortedArray.length - 1) => {
|
|
31
|
+
return __getAtName(name, sortedArray, startIdx, endIdx);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const compareNames = (a, b) => (a > b ? 1 : b > a ? -1 : 0);
|
|
35
|
+
|
|
36
|
+
export const sumValues = (values) => {
|
|
37
|
+
let mapStats = values.reduce(
|
|
38
|
+
(acc, val) => {
|
|
39
|
+
const { names } = acc;
|
|
40
|
+
if (isArray(val)) {
|
|
41
|
+
let arr = getSums(val).names;
|
|
42
|
+
let map = names;
|
|
43
|
+
|
|
44
|
+
for (const { 0: key, 1: value } of arr) {
|
|
45
|
+
let count = map.get(key)?.[1] ?? 0;
|
|
46
|
+
|
|
47
|
+
map.set(key, freeze([key, count + value]));
|
|
48
|
+
}
|
|
49
|
+
} else if (val) {
|
|
50
|
+
let span = val;
|
|
51
|
+
|
|
52
|
+
const { name } = span;
|
|
53
|
+
|
|
54
|
+
if (name) {
|
|
55
|
+
let count = names.get(name)?.[1] ?? 0;
|
|
56
|
+
|
|
57
|
+
names.set(name, freeze([name, count + 1]));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return acc;
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
names: new Map(),
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
let stats = {
|
|
68
|
+
names: [...mapStats.names.values()].sort((a, b) => compareNames(a[0], b[0])),
|
|
69
|
+
};
|
|
70
|
+
freeze(stats);
|
|
71
|
+
freeze(stats.names);
|
|
72
|
+
return stats;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const module_ = buildModule(defaultNodeSize, sumValues);
|
|
76
|
+
|
|
77
|
+
const {
|
|
78
|
+
from,
|
|
79
|
+
fromValues,
|
|
80
|
+
push,
|
|
81
|
+
pop,
|
|
82
|
+
addAt,
|
|
83
|
+
isValidNode,
|
|
84
|
+
assertValidNode,
|
|
85
|
+
getSize,
|
|
86
|
+
getValues,
|
|
87
|
+
getSums,
|
|
88
|
+
setValues,
|
|
89
|
+
traverse,
|
|
90
|
+
traverseInner,
|
|
91
|
+
findPath,
|
|
92
|
+
getAt,
|
|
93
|
+
replaceAt,
|
|
94
|
+
removeAt,
|
|
95
|
+
} = module_;
|
|
96
|
+
|
|
97
|
+
const getSpan = (name, spans) => {
|
|
98
|
+
let nameCount = 0;
|
|
99
|
+
let node = spans;
|
|
100
|
+
let idx = -1;
|
|
101
|
+
let index = getAtName(name, getSums(spans).names);
|
|
102
|
+
|
|
103
|
+
// drill into subtrees, passing over subtrees with too few references of the desired name
|
|
104
|
+
outer: while (node) {
|
|
105
|
+
let sums = getSums(node);
|
|
106
|
+
|
|
107
|
+
if (!sums) return null;
|
|
108
|
+
|
|
109
|
+
let valueNameCount = getAtName(name, sums.names);
|
|
110
|
+
|
|
111
|
+
if (nameCount + valueNameCount < index) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (const value of getValues(node)) {
|
|
116
|
+
if (!isArray(value)) {
|
|
117
|
+
idx++;
|
|
118
|
+
let span = value;
|
|
119
|
+
|
|
120
|
+
if (name != null && span.name === name) {
|
|
121
|
+
nameCount += 1;
|
|
122
|
+
if (nameCount === index) {
|
|
123
|
+
return getAt(idx, spans);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
let valueSums = getSums(value);
|
|
128
|
+
if (nameCount + getAtName(name, valueSums.names) > index) {
|
|
129
|
+
node = value;
|
|
130
|
+
continue outer;
|
|
131
|
+
} else {
|
|
132
|
+
nameCount += getAtName(name, valueSums.names) ?? 0;
|
|
133
|
+
idx += getSize(value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return null;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export {
|
|
145
|
+
from,
|
|
146
|
+
fromValues,
|
|
147
|
+
push,
|
|
148
|
+
pop,
|
|
149
|
+
addAt,
|
|
150
|
+
isValidNode,
|
|
151
|
+
assertValidNode,
|
|
152
|
+
getSize,
|
|
153
|
+
getValues,
|
|
154
|
+
getSums,
|
|
155
|
+
setValues,
|
|
156
|
+
traverse,
|
|
157
|
+
traverseInner,
|
|
158
|
+
findPath,
|
|
159
|
+
getAt,
|
|
160
|
+
replaceAt,
|
|
161
|
+
removeAt,
|
|
162
|
+
getSpan,
|
|
163
|
+
};
|
package/lib/tags.js
CHANGED
|
@@ -48,14 +48,14 @@ export const compareNames = (a, b) => (a > b ? 1 : b > a ? -1 : 0);
|
|
|
48
48
|
export const sumValues = (values) => {
|
|
49
49
|
let mapStats = values.reduce(
|
|
50
50
|
(acc, val) => {
|
|
51
|
-
const {
|
|
51
|
+
const { names, types } = acc;
|
|
52
52
|
if (isArray(val)) {
|
|
53
53
|
acc.lineBreaks += getSums(val).lineBreaks;
|
|
54
54
|
acc.gaps += getSums(val).gaps;
|
|
55
55
|
|
|
56
56
|
let arrs = [
|
|
57
|
-
[getSums(val).
|
|
58
|
-
[getSums(val).
|
|
57
|
+
[getSums(val).names, names],
|
|
58
|
+
[getSums(val).types, types],
|
|
59
59
|
];
|
|
60
60
|
|
|
61
61
|
for (let { 0: arr, 1: map } of arrs) {
|
|
@@ -89,17 +89,19 @@ export const sumValues = (values) => {
|
|
|
89
89
|
const { type, name } = ref;
|
|
90
90
|
|
|
91
91
|
if (name) {
|
|
92
|
-
let count =
|
|
92
|
+
let count = names.get(name)?.[1] ?? 0;
|
|
93
93
|
|
|
94
94
|
if (!ref.flags.array && referenceIsSingular(ref) && count > 0) throw new Error();
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
96
|
+
names.set(name, freeze([name, count + 1, ref]));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (type) {
|
|
100
|
+
let count = types.get(type)?.[1] ?? 0;
|
|
99
101
|
|
|
100
102
|
if (!ref.flags.array && referenceIsSingular(ref) && count > 0) throw new Error();
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
types.set(type, freeze([type, count + 1, ref]));
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
}
|
|
@@ -109,8 +111,8 @@ export const sumValues = (values) => {
|
|
|
109
111
|
{
|
|
110
112
|
gaps: 0,
|
|
111
113
|
lineBreaks: 0,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
names: new Map(),
|
|
115
|
+
types: new Map(),
|
|
114
116
|
},
|
|
115
117
|
);
|
|
116
118
|
|
|
@@ -118,12 +120,12 @@ export const sumValues = (values) => {
|
|
|
118
120
|
let stats = {
|
|
119
121
|
gaps,
|
|
120
122
|
lineBreaks,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
names: [...mapStats.names.values()].sort((a, b) => compareNames(a[0], b[0])),
|
|
124
|
+
types: [...mapStats.types.values()].sort((a, b) => compareNames(a[0], b[0])),
|
|
123
125
|
};
|
|
124
126
|
freeze(stats);
|
|
125
|
-
freeze(stats.
|
|
126
|
-
freeze(stats.
|
|
127
|
+
freeze(stats.names);
|
|
128
|
+
freeze(stats.types);
|
|
127
129
|
return stats;
|
|
128
130
|
};
|
|
129
131
|
|
package/lib/template.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as t from './builders.js';
|
|
2
|
-
import { getTags, isMultiFragment, Path } from './path.js';
|
|
2
|
+
import { buildNullNode, getTags, isMultiFragment, Path } from './path.js';
|
|
3
3
|
import { OpenNodeTag, CloseNodeTag, DoctypeTag, Property, GapNode } from './symbols.js';
|
|
4
4
|
import * as Tags from './tags.js';
|
|
5
5
|
import { getOpenTag, isNull } from './tree.js';
|
|
@@ -21,7 +21,7 @@ export const interpolate = (node, expressions) => {
|
|
|
21
21
|
let tag;
|
|
22
22
|
while ((tag = Tags.getAt(i, getTags(path.node)))) {
|
|
23
23
|
if (path.node.type === GapNode) {
|
|
24
|
-
path = path.replaceWith(expression);
|
|
24
|
+
path = path.replaceWith(expression ?? buildNullNode());
|
|
25
25
|
continue outer;
|
|
26
26
|
} else if (tag.type === Property && tag.value.tags[2]) {
|
|
27
27
|
let tags = getTags(tag.value.tags[2]);
|
package/lib/tree.js
CHANGED
|
@@ -167,8 +167,8 @@ function* __streamFromTree(doctypeTag, rootNode, options) {
|
|
|
167
167
|
if (tagPath.tag.type === CloseNodeTag) count--;
|
|
168
168
|
|
|
169
169
|
let gapNode;
|
|
170
|
-
if (getGapNode && tagPath.tag.type === GapTag && (gapNode = getGapNode(tagPath.
|
|
171
|
-
stack.push({ tagPath, count });
|
|
170
|
+
if (getGapNode && tagPath.tag.type === GapTag && (gapNode = getGapNode(tagPath.path.node))) {
|
|
171
|
+
stack.push({ tagPath: unshift ? tagPath.nextUnshifted : tagPath.next, count });
|
|
172
172
|
tagPath = TagPath.fromNode(gapNode, 0);
|
|
173
173
|
count = 0;
|
|
174
174
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bablr/agast-helpers",
|
|
3
3
|
"description": "Helper functions for working with agAST trees",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.4",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"./path": "./lib/path.js",
|
|
16
16
|
"./print": "./lib/print.js",
|
|
17
17
|
"./shorthand": "./lib/shorthand.js",
|
|
18
|
+
"./spans": "./lib/spans.js",
|
|
18
19
|
"./stream": "./lib/stream.js",
|
|
19
20
|
"./symbols": "./lib/symbols.js",
|
|
20
21
|
"./tags": "./lib/tags.js",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"test": "mocha test/*.test.js"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@bablr/btree": "0.4.
|
|
30
|
+
"@bablr/btree": "0.4.4",
|
|
30
31
|
"@bablr/coroutine": "0.1.0",
|
|
31
32
|
"@bablr/stream-iterator": "2.0.0",
|
|
32
33
|
"@iter-tools/imm-stack": "1.2.0"
|