@bablr/agast-helpers 0.7.1 → 0.9.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 ADDED
@@ -0,0 +1,236 @@
1
+ import { buildModule, defaultNodeSize } from '@bablr/btree/enhanceable';
2
+ import { CloseNodeTag, LiteralTag, OpenNodeTag, PropertyWrapper, ReferenceTag } from './symbols.js';
3
+ import { isObject } from './object.js';
4
+
5
+ const { isArray } = Array;
6
+ const { freeze } = Object;
7
+
8
+ export { defaultNodeSize };
9
+
10
+ const __getAtName = (name, sortedArray, startIdx = 0, endIdx = sortedArray.length - 1) => {
11
+ if (!sortedArray.length || endIdx < startIdx) return null;
12
+
13
+ let idx = startIdx + Math.floor((endIdx - startIdx) / 2 + 0.1);
14
+ let entry = sortedArray[idx];
15
+ let { 0: key, 1: value } = entry;
16
+
17
+ let direction = compareNames(name, key);
18
+
19
+ if (direction === 0) {
20
+ return value;
21
+ } else {
22
+ if (startIdx === endIdx) return null;
23
+
24
+ if (direction > 0) {
25
+ return __getAtName(name, sortedArray, idx + 1, endIdx);
26
+ } else {
27
+ return __getAtName(name, sortedArray, startIdx, idx - 1);
28
+ }
29
+ }
30
+ };
31
+
32
+ export const getAtName = (name, sortedArray, startIdx = 0, endIdx = sortedArray.length - 1) => {
33
+ return __getAtName(name, sortedArray, startIdx, endIdx);
34
+ };
35
+
36
+ export const compareNames = (a, b) => (a > b ? 1 : b > a ? -1 : 0);
37
+
38
+ const module_ = buildModule(
39
+ defaultNodeSize,
40
+ (acc, val) => {
41
+ const { references, specialTypes } = acc;
42
+ if (isArray(val)) {
43
+ acc.lineBreaks += getSums(val).lineBreaks;
44
+
45
+ for (const { 0: key, 1: value } of getSums(val).references) {
46
+ references.set(key, (references.get(key) ?? 0) + value);
47
+ }
48
+
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++;
58
+ }
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);
69
+ }
70
+ }
71
+ }
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
+ );
93
+
94
+ const {
95
+ from,
96
+ fromValues,
97
+ addAt,
98
+ isValidNode,
99
+ assertValidNode,
100
+ getValues,
101
+ getSums,
102
+ setValues,
103
+ traverse,
104
+ getSize,
105
+ findPath,
106
+ } = module_;
107
+
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];
113
+ };
114
+
115
+ const push = (tags, tag) => {
116
+ let firstTag = getAt(0, tags);
117
+ if (isArray(tag)) {
118
+ if (!firstTag || firstTag.type !== OpenNodeTag) {
119
+ return module_.concat(tags, tag);
120
+ }
121
+ let children = getValues(tags)[1] ?? module_.fromValues([]);
122
+
123
+ let { 0: open } = getValues(tags);
124
+
125
+ children = module_.concat(children, tag);
126
+
127
+ return module_.fromValues([open, children]);
128
+ } else {
129
+ if (tag.type === OpenNodeTag) {
130
+ if (getSize(tags) !== 0) throw new Error();
131
+ return module_.push(tags, tag);
132
+ } else {
133
+ if (!firstTag || firstTag.type !== OpenNodeTag) {
134
+ return module_.push(tags, tag);
135
+ }
136
+
137
+ let children = getValues(tags)[1] ?? [];
138
+
139
+ // sometimes this is the top level
140
+ if (firstTag) {
141
+ if (tag.type === CloseNodeTag) {
142
+ let { 0: open, 1: children = module_.fromValues([]) } = getValues(tags);
143
+
144
+ return module_.fromValues([open, children, tag]);
145
+ } else {
146
+ let { 0: open } = getValues(tags);
147
+
148
+ children = module_.push(children, tag);
149
+
150
+ return module_.fromValues([open, children]);
151
+ }
152
+ } else {
153
+ return module_.push(tags, tag);
154
+ }
155
+ }
156
+ }
157
+ };
158
+
159
+ const replaceAt = (idx, tree, value) => {
160
+ let idx_ = idx < 0 ? getSize(tree) + idx : idx;
161
+
162
+ if (value.type !== getAt(idx_, tree).type) throw new Error();
163
+ let firstTag = getAt(0, tree);
164
+
165
+ if (firstTag && firstTag.type === OpenNodeTag) {
166
+ let newTags = [...getValues(tree)];
167
+ if (idx_ === 0) {
168
+ newTags[0] = value;
169
+ } else if (isObject(value) && value.type === CloseNodeTag) {
170
+ if (idx_ !== getSize(tree) - 1) throw new Error();
171
+ newTags[2] = value;
172
+ } else {
173
+ let children = getValues(tree)[1];
174
+
175
+ children = module_.replaceAt(idx_ - 1, children, value);
176
+
177
+ newTags[1] = children;
178
+ }
179
+ return module_.fromValues(newTags);
180
+ } else {
181
+ return module_.replaceAt(idx_, tree, value);
182
+ }
183
+ };
184
+
185
+ const removeAt = (idx, tree) => {
186
+ let idx_ = idx < 0 ? getSize(tree) + idx : idx;
187
+
188
+ let firstTag = getAt(0, tree);
189
+
190
+ if (firstTag && firstTag.type === OpenNodeTag) {
191
+ let newTags = [...getValues(tree)];
192
+ if (idx_ === 0) {
193
+ newTags.splice(0, 1);
194
+ } else if (idx_ === getSize(tree - 1)) {
195
+ newTags.splice(2, 1);
196
+ } else {
197
+ let children = getValues(tree)[1];
198
+
199
+ children = module_.removeAt(idx_ - 1, children);
200
+
201
+ newTags[1] = children;
202
+ }
203
+ return module_.fromValues(newTags);
204
+ } else {
205
+ return module_.removeAt(idx_, tree);
206
+ }
207
+ };
208
+
209
+ function* traverseInner(tree) {
210
+ for (let tag of module_.traverse(tree)) {
211
+ if (tag.type === PropertyWrapper) {
212
+ yield* tag.value.tags;
213
+ } else {
214
+ yield tag;
215
+ }
216
+ }
217
+ }
218
+
219
+ export {
220
+ from,
221
+ fromValues,
222
+ push,
223
+ addAt,
224
+ isValidNode,
225
+ assertValidNode,
226
+ getSize,
227
+ getValues,
228
+ getSums,
229
+ setValues,
230
+ traverse,
231
+ traverseInner,
232
+ findPath,
233
+ getAt,
234
+ replaceAt,
235
+ removeAt,
236
+ };
package/lib/template.js CHANGED
@@ -5,11 +5,11 @@ import {
5
5
  OpenNodeTag,
6
6
  CloseNodeTag,
7
7
  DoctypeTag,
8
- EmbeddedNode,
9
- GapTag,
8
+ Property,
9
+ BindingTag,
10
10
  } from './symbols.js';
11
- import * as sumtree from './sumtree.js';
12
- import { getOpenTag, get, isFragmentNode } from './tree.js';
11
+ import * as Tags from './tags.js';
12
+ import { getOpenTag, isFragmentNode } from './tree.js';
13
13
 
14
14
  const { freeze } = Object;
15
15
 
@@ -18,19 +18,20 @@ export const buildFilledGapFunction = (expressions) => (value) => {
18
18
  return t.buildGapTag();
19
19
  };
20
20
 
21
- export function* interpolateFragment(node, ref, expressions) {
22
- const open = getOpenTag(node);
21
+ export const interpolateFragment = (node, ref, expressions) => {
22
+ return __interpolateFragment(node, ref, expressions);
23
+ };
23
24
 
24
- if (node.type !== null) throw new Error();
25
+ function* __interpolateFragment(node, ref, expressions) {
26
+ const open = getOpenTag(node);
25
27
 
26
28
  const gap = buildFilledGapFunction(expressions);
27
29
 
28
- const counters = new Map();
29
-
30
30
  if (!open.value.type) {
31
31
  let currentRef = null;
32
+ let currentBinding = null;
32
33
  let isFragment = isFragmentNode(node);
33
- for (let tag of sumtree.traverse(node.children)) {
34
+ for (let tag of Tags.traverseInner(node.tags)) {
34
35
  switch (tag.type) {
35
36
  case DoctypeTag: {
36
37
  break;
@@ -48,11 +49,15 @@ export function* interpolateFragment(node, ref, expressions) {
48
49
  break;
49
50
  }
50
51
 
52
+ case BindingTag: {
53
+ currentBinding = tag;
54
+ break;
55
+ }
56
+
51
57
  case InitializerTag: {
52
- const { name } = currentRef.value;
53
- counters.set(name, -1);
54
- if (name === '.') {
55
- yield freeze({ ...ref });
58
+ const { type } = currentRef.value;
59
+ if (type === '.') {
60
+ yield freeze(ref);
56
61
  } else {
57
62
  yield currentRef;
58
63
  }
@@ -60,35 +65,21 @@ export function* interpolateFragment(node, ref, expressions) {
60
65
  break;
61
66
  }
62
67
 
63
- case GapTag: {
64
- const { name, isArray, flags } = currentRef.value;
68
+ case Property: {
69
+ let { reference } = tag.value;
70
+ const { type } = reference;
65
71
 
66
- if (name === '.') {
72
+ if (type === '.') {
67
73
  // TODO check/combine flags
68
- yield freeze({ ...ref });
74
+ yield ref;
69
75
  } else {
70
76
  yield currentRef;
71
77
  }
72
78
 
73
- const count = counters.get(name) + 1;
74
-
75
- counters.set(name, count);
79
+ yield currentBinding;
76
80
 
77
- const resolvedRef = t.buildReferenceTag(name, isArray, flags, count);
81
+ yield gap(tag.value.node);
78
82
 
79
- yield gap(get(resolvedRef, node));
80
-
81
- break;
82
- }
83
-
84
- case EmbeddedNode: {
85
- const { name } = currentRef.value;
86
- if (name === '.') {
87
- yield freeze({ ...ref });
88
- } else {
89
- yield currentRef;
90
- }
91
- yield gap(tag.value);
92
83
  break;
93
84
  }
94
85
 
@@ -99,8 +90,8 @@ export function* interpolateFragment(node, ref, expressions) {
99
90
  }
100
91
  }
101
92
  } else if (open.type === OpenNodeTag) {
102
- yield freeze({ ...ref });
103
- yield gap(get(ref, node));
93
+ yield freeze(ref);
94
+ yield gap(node);
104
95
  } else {
105
96
  throw new Error();
106
97
  }