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