@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 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 Object.values(item)) {
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
- Object.freeze(item);
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 = Object.freeze({})) => {
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 { references } = Tags.getSums(childNode);
380
+ let { names } = Tags.getSums(childNode);
381
381
 
382
382
  let res;
383
- if ((res = Tags.getAtName(name, references))) {
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.references : sums.specialTypes;
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.references)
488
- : Tags.getAtName(type, valueSums.specialTypes)) >
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.references)
497
- : Tags.getAtName(type, valueSums.specialTypes)) ?? 0;
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)).references);
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.property.reference,
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 { references, specialTypes } = acc;
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).references, references],
58
- [getSums(val).specialTypes, specialTypes],
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 = references.get(name)?.[1] ?? 0;
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
- references.set(name, freeze([name, count + 1, ref]));
97
- } else {
98
- let count = specialTypes.get(type)?.[1] ?? 0;
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
- specialTypes.set(type, freeze([type, count + 1, ref]));
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
- references: new Map(),
113
- specialTypes: new Map(),
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
- references: [...mapStats.references.values()].sort((a, b) => compareNames(a[0], b[0])),
122
- specialTypes: [...mapStats.specialTypes.values()].sort((a, b) => compareNames(a[0], b[0])),
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.references);
126
- freeze(stats.specialTypes);
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.tag))) {
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.2",
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.3",
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"