@bablr/agast-helpers 0.9.0 → 0.10.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/tags.js CHANGED
@@ -1,6 +1,18 @@
1
1
  import { buildModule, defaultNodeSize } from '@bablr/btree/enhanceable';
2
- import { CloseNodeTag, LiteralTag, OpenNodeTag, PropertyWrapper, ReferenceTag } from './symbols.js';
2
+ import {
3
+ AttributeDefinition,
4
+ CloseNodeTag,
5
+ GapNode,
6
+ GapTag,
7
+ LiteralTag,
8
+ OpenNodeTag,
9
+ Property,
10
+ ReferenceTag,
11
+ ShiftTag,
12
+ } from './symbols.js';
3
13
  import { isObject } from './object.js';
14
+ import { referenceIsSingular } from './path.js';
15
+ import { buildReference } from './builders.js';
4
16
 
5
17
  const { isArray } = Array;
6
18
  const { freeze } = Object;
@@ -35,61 +47,89 @@ export const getAtName = (name, sortedArray, startIdx = 0, endIdx = sortedArray.
35
47
 
36
48
  export const compareNames = (a, b) => (a > b ? 1 : b > a ? -1 : 0);
37
49
 
38
- const module_ = buildModule(
39
- defaultNodeSize,
40
- (acc, val) => {
41
- const { references, specialTypes } = acc;
42
- if (isArray(val)) {
43
- acc.lineBreaks += getSums(val).lineBreaks;
50
+ export const sumValues = (values) => {
51
+ let mapStats = values.reduce(
52
+ (acc, val) => {
53
+ const { references, specialTypes } = acc;
54
+ if (isArray(val)) {
55
+ acc.lineBreaks += getSums(val).lineBreaks;
56
+ acc.gaps += getSums(val).gaps;
44
57
 
45
- for (const { 0: key, 1: value } of getSums(val).references) {
46
- references.set(key, (references.get(key) ?? 0) + value);
47
- }
58
+ let arrs = [
59
+ [getSums(val).references, references],
60
+ [getSums(val).specialTypes, specialTypes],
61
+ ];
48
62
 
49
- for (const { 0: key, 1: value } of getSums(val).specialTypes) {
50
- specialTypes.set(key, (specialTypes.get(key) ?? 0) + value);
51
- }
52
- } else if (val) {
53
- if (val.type === LiteralTag) {
54
- let text = val.value;
55
- let idx = 0;
56
- while ((idx = text.indexOf('\n', idx + 1)) >= 0) {
57
- acc.lineBreaks++;
63
+ for (let { 0: arr, 1: map } of arrs) {
64
+ for (const { 0: key, 1: value, 2: reference } of arr) {
65
+ let count = map.get(key)?.[1] ?? 0;
66
+
67
+ if (!reference.flags.array && referenceIsSingular(reference) && count > 0)
68
+ throw new Error();
69
+
70
+ map.set(key, freeze([key, count + value, reference]));
71
+ }
58
72
  }
59
- } else if (val.type === PropertyWrapper) {
60
- let { tags } = val.value;
61
- let refOrShiftTag = tags[0];
62
-
63
- if (refOrShiftTag.type === ReferenceTag) {
64
- const { type, name } = refOrShiftTag.value;
65
- if (name) {
66
- references.set(name, (references.get(name) ?? 0) + 1);
67
- } else {
68
- specialTypes.set(type, (specialTypes.get(type) ?? 0) + 1);
73
+ } else if (val) {
74
+ if (val.type === LiteralTag) {
75
+ let text = val.value;
76
+ let idx = 0;
77
+ while ((idx = text.indexOf('\n', idx + 1)) >= 0) {
78
+ acc.lineBreaks++;
79
+ }
80
+ } else if (val.type === GapTag) {
81
+ acc.gaps++;
82
+ } else if (val.type === Property) {
83
+ let { tags } = val.value;
84
+ let refOrShiftTag = tags[0];
85
+
86
+ if (tags[2]) {
87
+ acc.gaps += tags[2].value.tags[2].gaps;
88
+ }
89
+ if (refOrShiftTag?.type !== ShiftTag) {
90
+ let ref = refOrShiftTag?.value || buildReference();
91
+ const { type, name } = ref;
92
+
93
+ if (name) {
94
+ let count = references.get(name)?.[1] ?? 0;
95
+
96
+ if (!ref.flags.array && referenceIsSingular(ref) && count > 0) throw new Error();
97
+
98
+ references.set(name, freeze([name, count + 1, ref]));
99
+ } else {
100
+ let count = specialTypes.get(type)?.[1] ?? 0;
101
+
102
+ if (!ref.flags.array && referenceIsSingular(ref) && count > 0) throw new Error();
103
+
104
+ specialTypes.set(type, freeze([type, count + 1, ref]));
105
+ }
69
106
  }
70
107
  }
71
108
  }
72
- }
73
- return acc;
74
- },
75
- () => ({
76
- lineBreaks: 0,
77
- references: new Map(),
78
- specialTypes: new Map(),
79
- }),
80
- (mapStats) => {
81
- let { lineBreaks } = mapStats;
82
- let stats = {
83
- lineBreaks,
84
- references: [...mapStats.references].sort((a, b) => compareNames(a[0], b[0])),
85
- specialTypes: [...mapStats.specialTypes].sort((a, b) => compareNames(a[0], b[0])),
86
- };
87
- freeze(stats);
88
- freeze(stats.references);
89
- freeze(stats.specialTypes);
90
- return stats;
91
- },
92
- );
109
+ return acc;
110
+ },
111
+ {
112
+ gaps: 0,
113
+ lineBreaks: 0,
114
+ references: new Map(),
115
+ specialTypes: new Map(),
116
+ },
117
+ );
118
+
119
+ let { lineBreaks, gaps } = mapStats;
120
+ let stats = {
121
+ gaps,
122
+ lineBreaks,
123
+ references: [...mapStats.references.values()].sort((a, b) => compareNames(a[0], b[0])),
124
+ specialTypes: [...mapStats.specialTypes.values()].sort((a, b) => compareNames(a[0], b[0])),
125
+ };
126
+ freeze(stats);
127
+ freeze(stats.references);
128
+ freeze(stats.specialTypes);
129
+ return stats;
130
+ };
131
+
132
+ const module_ = buildModule(defaultNodeSize, sumValues);
93
133
 
94
134
  const {
95
135
  from,
@@ -105,15 +145,24 @@ const {
105
145
  findPath,
106
146
  } = module_;
107
147
 
108
- const getAt = (idx, tags, wrapperIndex) => {
109
- let result = module_.getAt(idx, tags);
110
- return wrapperIndex == null || result?.type !== PropertyWrapper
111
- ? result
112
- : result.value.tags[wrapperIndex];
148
+ const getAt = (idx, tags, propertyIndex) => {
149
+ if (propertyIndex !== undefined) {
150
+ throw new Error('propertyIndex');
151
+ }
152
+ return module_.getAt(idx, tags);
113
153
  };
114
154
 
115
155
  const push = (tags, tag) => {
116
156
  let firstTag = getAt(0, tags);
157
+
158
+ if (
159
+ !Array.isArray(tag) &&
160
+ ![OpenNodeTag, Property, CloseNodeTag, AttributeDefinition, LiteralTag, GapTag].includes(
161
+ tag.type,
162
+ )
163
+ )
164
+ throw new Error();
165
+
117
166
  if (isArray(tag)) {
118
167
  if (!firstTag || firstTag.type !== OpenNodeTag) {
119
168
  return module_.concat(tags, tag);
@@ -208,8 +257,8 @@ const removeAt = (idx, tree) => {
208
257
 
209
258
  function* traverseInner(tree) {
210
259
  for (let tag of module_.traverse(tree)) {
211
- if (tag.type === PropertyWrapper) {
212
- yield* tag.value.tags;
260
+ if (tag.type === Property) {
261
+ yield* module_.traverse(tag.value.tags);
213
262
  } else {
214
263
  yield tag;
215
264
  }
package/lib/template.js CHANGED
@@ -1,15 +1,8 @@
1
1
  import * as t from './builders.js';
2
- import {
3
- ReferenceTag,
4
- InitializerTag,
5
- OpenNodeTag,
6
- CloseNodeTag,
7
- DoctypeTag,
8
- Property,
9
- BindingTag,
10
- } from './symbols.js';
2
+ import { getTags, isMultiFragment, Path } from './path.js';
3
+ import { OpenNodeTag, CloseNodeTag, DoctypeTag, Property, GapNode } from './symbols.js';
11
4
  import * as Tags from './tags.js';
12
- import { getOpenTag, isFragmentNode } from './tree.js';
5
+ import { getOpenTag, isNull } from './tree.js';
13
6
 
14
7
  const { freeze } = Object;
15
8
 
@@ -18,67 +11,76 @@ export const buildFilledGapFunction = (expressions) => (value) => {
18
11
  return t.buildGapTag();
19
12
  };
20
13
 
14
+ export const interpolate = (node, expressions) => {
15
+ let path = Path.from(node);
16
+
17
+ outer: for (let expression of expressions) {
18
+ let i = 0;
19
+ do {
20
+ path = path.atDepth(0);
21
+ let tag;
22
+ while ((tag = Tags.getAt(i, getTags(path.node)))) {
23
+ if (path.node.type === GapNode) {
24
+ path = path.replaceWith(expression);
25
+ continue outer;
26
+ } else if (tag.type === Property && tag.value.tags[2]) {
27
+ let tags = getTags(tag.value.tags[2]);
28
+ if (tags[2].gaps) {
29
+ path = path.push(i);
30
+ i = 0;
31
+ continue;
32
+ }
33
+ }
34
+ i++;
35
+ }
36
+ i = path.parent?.tagsIndex + 1;
37
+ path = path.parent;
38
+ } while (path.depth);
39
+ }
40
+
41
+ return path.atDepth(0).node;
42
+ };
43
+
21
44
  export const interpolateFragment = (node, ref, expressions) => {
22
45
  return __interpolateFragment(node, ref, expressions);
23
46
  };
24
47
 
25
48
  function* __interpolateFragment(node, ref, expressions) {
49
+ if (isNull(node)) return;
50
+
26
51
  const open = getOpenTag(node);
27
52
 
28
53
  const gap = buildFilledGapFunction(expressions);
29
54
 
30
- if (!open.value.type) {
31
- let currentRef = null;
32
- let currentBinding = null;
33
- let isFragment = isFragmentNode(node);
34
- for (let tag of Tags.traverseInner(node.tags)) {
55
+ if (!open.value.name) {
56
+ let isMultiFragment_ = isMultiFragment(node);
57
+ for (let tag of Tags.traverse(getTags(node))) {
35
58
  switch (tag.type) {
36
59
  case DoctypeTag: {
37
60
  break;
38
61
  }
39
62
  case OpenNodeTag:
40
63
  case CloseNodeTag: {
41
- if (!isFragment) {
64
+ if (!isMultiFragment_) {
42
65
  yield tag;
43
66
  }
44
67
  break;
45
68
  }
46
69
 
47
- case ReferenceTag: {
48
- currentRef = tag;
49
- break;
50
- }
51
-
52
- case BindingTag: {
53
- currentBinding = tag;
54
- break;
55
- }
56
-
57
- case InitializerTag: {
58
- const { type } = currentRef.value;
59
- if (type === '.') {
60
- yield freeze(ref);
61
- } else {
62
- yield currentRef;
63
- }
64
- yield tag;
65
- break;
66
- }
67
-
68
70
  case Property: {
69
- let { reference } = tag.value;
71
+ let { reference, node, tags } = tag.value;
70
72
  const { type } = reference;
71
73
 
72
- if (type === '.') {
74
+ if (type === '_') {
73
75
  // TODO check/combine flags
74
76
  yield ref;
75
77
  } else {
76
- yield currentRef;
78
+ yield tags[0];
77
79
  }
78
80
 
79
- yield currentBinding;
81
+ yield* tags[1];
80
82
 
81
- yield gap(tag.value.node);
83
+ yield gap(node);
82
84
 
83
85
  break;
84
86
  }