@bablr/agast-helpers 0.8.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/builders.js CHANGED
@@ -1,5 +1,5 @@
1
- import * as btree from './btree.js';
2
- import { printType } from './print.js';
1
+ import emptyStack from '@iter-tools/imm-stack';
2
+ import { isObject, isSymbol } from './object.js';
3
3
  import {
4
4
  DoctypeTag,
5
5
  OpenNodeTag,
@@ -8,98 +8,128 @@ import {
8
8
  ShiftTag,
9
9
  GapTag,
10
10
  NullTag,
11
- InitializerTag,
12
11
  LiteralTag,
13
- TokenGroup,
14
12
  AttributeDefinition,
15
13
  BindingTag,
14
+ Document,
15
+ Property,
16
+ NullNode,
17
+ GapNode,
18
+ TreeNode,
16
19
  } from './symbols.js';
17
20
 
18
- const { freeze, hasOwn } = Object;
21
+ const { freeze } = Object;
19
22
  const { isArray } = Array;
20
23
 
21
- function* relatedNodes(properties) {
22
- for (const value of Object.values(properties)) {
23
- if (isArray(value)) {
24
- for (let value of btree.traverse(value)) {
25
- yield value.node;
24
+ export const deepFreeze = (object) => {
25
+ let list = emptyStack.push(object);
26
+ while (list.size) {
27
+ let item = list.value;
28
+ list = list.pop();
29
+
30
+ for (const value of Object.values(item)) {
31
+ if (isObject(value)) {
32
+ list = list.push(value);
26
33
  }
27
- } else {
28
- yield value.node;
29
34
  }
30
- }
31
- }
32
35
 
33
- const find = (predicate, iterable) => {
34
- for (const value of iterable) {
35
- if (predicate(value)) return value;
36
+ Object.freeze(item);
36
37
  }
37
38
  };
38
39
 
39
- export const buildProperty = (reference, binding, node) => {
40
- if (node?.binding) throw new Error();
41
- if (!reference.flags) throw new Error();
42
- if (!isArray(node) && typeof node === 'object' && !hasOwn(node, 'properties')) throw new Error();
40
+ export const buildProperty = (tags, shift) => {
41
+ if (shift && !shift.index) throw new Error();
42
+ freeze(tags);
43
43
 
44
- if (reference.value) throw new Error();
44
+ if (tags[0] && ![ReferenceTag, ShiftTag].includes(tags[0].type)) throw new Error();
45
+ if (tags.length > 1 && !isArray(tags[1])) throw new Error();
45
46
 
46
- return freeze({ reference, binding, node });
47
- };
47
+ // if (property.node && !(tags.length === 3)) throw new Error();
48
+ // if (tags[0].type === ShiftTag && !property.shift) throw new Error();
48
49
 
49
- export const buildFacadeProperty = (reference, binding, node) => {
50
- if (node?.binding) throw new Error();
51
- if (!reference.flags) throw new Error();
52
- if (!isArray(node) && typeof node === 'object' && !node.properties) throw new Error();
50
+ if ((tags[0]?.type == ShiftTag) !== !!shift) throw new Error();
51
+ if (tags[2] && ![NullNode, GapNode, TreeNode].includes(tags[2].type)) throw new Error();
53
52
 
54
- if (reference.value) throw new Error();
53
+ return freeze({
54
+ tags,
55
+ reference: tags[0]?.type === ShiftTag ? null : tags[0]?.value || buildReference(),
56
+ bindings: freeze(tags[1]?.map((tag) => tag.value) || []),
57
+ node: tags[2] || null,
58
+ shift: tags[0]?.type == ShiftTag ? shift : null,
59
+ });
60
+ };
55
61
 
56
- return freeze({ reference, binding, node });
62
+ export const buildPropertyTag = (tags, shift) => {
63
+ return freeze({ type: Property, value: buildProperty(tags, shift) });
57
64
  };
58
65
 
59
- export const buildTokenGroup = (tokens) => {
60
- return freeze({ type: TokenGroup, value: tokens });
66
+ export const buildDocument = (doctypeTag, tree) => {
67
+ return freeze({ type: Document, value: freeze({ doctypeTag, tree }) });
61
68
  };
62
69
 
63
70
  export const buildBeginningOfStreamToken = () => {
64
71
  return freeze({ type: Symbol.for('@bablr/beginning-of-stream'), value: undefined });
65
72
  };
66
73
 
67
- export const buildReferenceTag = (
68
- type = null,
69
- name = null,
70
- isArray = false,
71
- flags = referenceFlags,
72
- index,
73
- ) => {
74
- if (type != null && !isString(type)) throw new Error();
75
- if (name != null && !isString(name)) throw new Error();
76
- if (name == null && type == null) throw new Error();
77
- if (index) throw new Error();
78
- let { hasGap, expression } = flags;
79
-
80
- hasGap = !!hasGap;
81
- expression = !!expression;
82
-
74
+ export const buildReferenceTag = (type = null, name = null, flags = referenceFlags) => {
75
+ if (!Object.isFrozen(flags)) throw new Error();
83
76
  return freeze({
84
77
  type: ReferenceTag,
85
- value: freeze({ type, name, isArray, flags: freeze({ hasGap, expression }) }),
78
+ value: buildReference(type, name, flags),
86
79
  });
87
80
  };
88
81
 
82
+ export const buildReference = (type = null, name = null, flags = referenceFlags) => {
83
+ if (type != null && !['_', '.', '#', '@'].includes(type)) throw new Error();
84
+ if (type && ['_', '#', '@'].includes(type) && (flags.intrinsic || flags.hasGap))
85
+ throw new Error();
86
+ if (name != null && (!isString(name) || !name)) throw new Error();
87
+ let { array, expression, intrinsic, hasGap } = flags;
88
+
89
+ array = !!array;
90
+ expression = !!expression;
91
+ intrinsic = !!intrinsic;
92
+ hasGap = !!hasGap;
93
+
94
+ let type_ = type == null && name == null ? '.' : type;
95
+
96
+ return freeze({ type: type_, name, flags: freeze({ array, expression, intrinsic, hasGap }) });
97
+ };
98
+
99
+ export const referenceFromMatcher = (matcher) => {
100
+ let { type, name, flags } = matcher;
101
+ return buildReference(type, name, flags);
102
+ };
103
+
104
+ export const buildShift = (index, height) => {
105
+ if (index == null || height == null) throw new Error();
106
+ return freeze({ index, height });
107
+ };
108
+
89
109
  export const buildNullTag = () => {
90
110
  return freeze({ type: NullTag, value: undefined });
91
111
  };
92
112
 
93
- export const buildInitializerTag = (isArray = false) => {
94
- return freeze({ type: InitializerTag, value: freeze({ isArray }) });
113
+ export const buildBinding = (segments = []) => {
114
+ // TODO relax this restriction
115
+ if (!isArray(segments) || !segments.length) throw new Error();
116
+ if (segments.includes(undefined)) throw new Error();
117
+ return freeze({
118
+ segments: segments.map((segment) =>
119
+ freeze(isString(segment) ? { type: null, name: segment } : segment),
120
+ ),
121
+ });
95
122
  };
96
123
 
97
- export const buildBindingTag = (languagePath = []) => {
98
- if (!isArray(languagePath)) throw new Error();
99
- return freeze({ type: BindingTag, value: freeze({ languagePath }) });
124
+ export const buildBindingTag = (segments = []) => {
125
+ return freeze({
126
+ type: BindingTag,
127
+ value: buildBinding(segments),
128
+ });
100
129
  };
101
130
 
102
131
  export const buildChild = (type, value) => {
132
+ if (!isSymbol(type)) throw new Error();
103
133
  return freeze({ type, value });
104
134
  };
105
135
 
@@ -107,28 +137,74 @@ export const buildGapTag = () => {
107
137
  return freeze({ type: GapTag, value: undefined });
108
138
  };
109
139
 
110
- export const buildShiftTag = (index, height) => {
111
- if (!Number.isFinite(index)) throw new Error();
112
- if (!Number.isFinite(height)) throw new Error();
113
- return freeze({ type: ShiftTag, value: freeze({ index, height }) });
140
+ export const buildShiftTag = () => {
141
+ let value = Object.defineProperties(
142
+ {},
143
+ {
144
+ index: {
145
+ get() {
146
+ throw new Error('moved');
147
+ },
148
+ },
149
+ height: {
150
+ get() {
151
+ throw new Error('moved');
152
+ },
153
+ },
154
+ },
155
+ );
156
+ return freeze({ type: ShiftTag, value });
114
157
  };
115
158
 
116
- export const buildDoctypeTag = (attributes = {}, version = 0) => {
159
+ export const buildDoctypeTag = (version = 0, attributes = Object.freeze({})) => {
117
160
  return freeze({
118
161
  type: DoctypeTag,
119
- value: freeze({ doctype: 'cstml', version, attributes: freeze(attributes) }),
162
+ value: freeze({ doctype: 'cstml', version, attributes }),
120
163
  });
121
164
  };
122
165
 
123
- export const buildOpenNodeTag = (flags = nodeFlags, type = null, attributes = {}) => {
124
- if (printType(type).startsWith('https://')) throw new Error();
166
+ export const buildOpenFragmentTag = (flags = nodeFlags, selfClosing = false) => {
167
+ return buildFullOpenNodeTag(flags, Symbol.for('__'), null, null, {}, selfClosing);
168
+ };
169
+
170
+ export const buildOpenCoverTag = (flags = nodeFlags, name = null, selfClosing = false) => {
171
+ return buildFullOpenNodeTag(flags, Symbol.for('_'), name, null, {}, selfClosing);
172
+ };
173
+
174
+ export const buildOpenNodeTag = (
175
+ flags,
176
+ name,
177
+ literalValue = null,
178
+ attributes = {},
179
+ selfClosing = !!literalValue,
180
+ ) => {
181
+ if (!name && !flags.token) throw new Error();
182
+ return buildFullOpenNodeTag(flags, null, name, literalValue, attributes, selfClosing);
183
+ };
184
+
185
+ export const buildFullOpenNodeTag = (
186
+ flags = nodeFlags,
187
+ type = Symbol.for('__'),
188
+ name = null,
189
+ literalValue = null,
190
+ attributes = {},
191
+ selfClosing = !!literalValue,
192
+ ) => {
193
+ if (!isObject(attributes)) throw new Error();
194
+ if (literalValue && !isString(literalValue)) throw new Error();
195
+ if (!type && !name && !flags.token && !literalValue) throw new Error();
196
+
197
+ deepFreeze(attributes);
125
198
 
126
199
  return freeze({
127
200
  type: OpenNodeTag,
128
201
  value: freeze({
129
202
  flags: freeze(flags),
203
+ name: isString(name) ? Symbol.for(name) : name,
130
204
  type: isString(type) ? Symbol.for(type) : type,
205
+ literalValue,
131
206
  attributes,
207
+ selfClosing,
132
208
  }),
133
209
  });
134
210
  };
@@ -144,8 +220,10 @@ export const buildLiteralTag = (value) => {
144
220
  return freeze({ type: LiteralTag, value });
145
221
  };
146
222
 
147
- export const buildAttributeDefinition = (path, value) => {
148
- return freeze({ type: AttributeDefinition, value: freeze({ path, value }) });
223
+ export const buildAttributeDefinition = (key, value) => {
224
+ if (!key?.length) throw new Error();
225
+ freeze(key);
226
+ return freeze({ type: AttributeDefinition, value: freeze({ path: key, value }) });
149
227
  };
150
228
 
151
229
  const flagsWithGap = new WeakMap();
@@ -155,7 +233,10 @@ export const getFlagsWithGap = (flags, hasGap = true) => {
155
233
 
156
234
  let gapFlags = flagsWithGap.get(flags);
157
235
  if (!gapFlags) {
158
- gapFlags = { ...flags, hasGap };
236
+ gapFlags = freeze({
237
+ token: flags.token,
238
+ hasGap,
239
+ });
159
240
  flagsWithGap.set(flags, gapFlags);
160
241
  }
161
242
  return gapFlags;
@@ -164,47 +245,37 @@ export const getFlagsWithGap = (flags, hasGap = true) => {
164
245
  export const nodeFlags = freeze({
165
246
  token: false,
166
247
  hasGap: false,
167
- fragment: false,
168
- cover: false,
169
248
  });
170
249
 
171
- export const fragmentFlags = freeze({
172
- token: false,
173
- hasGap: false,
174
- fragment: true,
175
- cover: false,
176
- });
177
-
178
- const hasGap = (properties) => {
179
- return find((node) => node.flags.hasGap, relatedNodes(properties));
180
- };
181
-
182
- const getFlags = (flags, properties) => {
183
- if (!hasGap(properties)) {
184
- return flags;
185
- } else {
186
- return getFlagsWithGap(flags);
187
- }
188
- };
189
-
190
250
  export const tokenFlags = freeze({
191
251
  token: true,
192
252
  hasGap: false,
193
- fragment: false,
194
- cover: false,
195
253
  });
196
254
 
197
255
  export const referenceFlags = freeze({
256
+ array: false,
257
+ expression: false,
258
+ intrinsic: false,
259
+ hasGap: false,
260
+ });
261
+
262
+ export const intrinsicReferenceFlags = freeze({
263
+ array: false,
198
264
  expression: false,
265
+ intrinsic: true,
199
266
  hasGap: false,
200
267
  });
201
268
 
202
269
  export const gapReferenceFlags = freeze({
270
+ array: false,
203
271
  expression: false,
272
+ intrinsic: false,
204
273
  hasGap: true,
205
274
  });
206
275
 
207
276
  export const expressionReferenceFlags = freeze({
277
+ array: false,
208
278
  expression: true,
279
+ intrinsic: false,
209
280
  hasGap: false,
210
281
  });