@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/builders.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as btree from './btree.js';
1
+ import { isSymbol } from './object.js';
2
2
  import { printType } from './print.js';
3
3
  import {
4
4
  DoctypeTag,
@@ -10,49 +10,63 @@ import {
10
10
  NullTag,
11
11
  InitializerTag,
12
12
  LiteralTag,
13
- TokenGroup,
14
- EmbeddedNode,
13
+ AttributeDefinition,
14
+ BindingTag,
15
+ Document,
15
16
  } from './symbols.js';
16
17
 
17
- const { freeze } = Object;
18
+ const { freeze, hasOwn } = Object;
18
19
  const { isArray } = Array;
19
20
 
20
- const isObject = (val) => val !== null && typeof val === 'object';
21
-
22
- function* relatedNodes(properties) {
23
- for (const value of Object.values(properties)) {
24
- if (isArray(value)) {
25
- for (let value of btree.traverse(value)) {
26
- yield value.node;
27
- }
28
- } else {
29
- yield value.node;
30
- }
31
- }
32
- }
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();
33
25
 
34
- const find = (predicate, iterable) => {
35
- for (const value of iterable) {
36
- if (predicate(value)) return value;
37
- }
26
+ if (reference.value) throw new Error();
27
+
28
+ return freeze({ reference, binding, node });
38
29
  };
39
30
 
40
- export const buildTokenGroup = (tokens) => {
41
- return freeze({ type: TokenGroup, value: tokens });
31
+ export const buildPropertyWrapper = (tags, property) => {
32
+ freeze(tags);
33
+ freeze(property);
34
+
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();
37
+
38
+ return freeze({ tags, property });
42
39
  };
43
40
 
44
- export const buildEmbeddedNode = (node) => {
45
- if (!isObject(node)) throw new Error();
46
- return freeze({ type: EmbeddedNode, value: node });
41
+ export const buildDocument = (doctypeTag, fragment) => {
42
+ return freeze({ type: Document, value: freeze({ doctypeTag, fragment }) });
43
+ };
44
+
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();
49
+
50
+ if (reference.value) throw new Error();
51
+
52
+ return freeze({ reference, binding, node });
47
53
  };
48
54
 
49
55
  export const buildBeginningOfStreamToken = () => {
50
56
  return freeze({ type: Symbol.for('@bablr/beginning-of-stream'), value: undefined });
51
57
  };
52
58
 
53
- export const buildReferenceTag = (name, isArray = false, flags = referenceFlags, index = null) => {
54
- if (name == null || !/[a-zA-Z.#@]/.test(name)) throw new Error('reference must have a name');
55
- if (index != null && !Number.isFinite(index)) throw new Error();
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();
56
70
  let { hasGap, expression } = flags;
57
71
 
58
72
  hasGap = !!hasGap;
@@ -60,49 +74,82 @@ export const buildReferenceTag = (name, isArray = false, flags = referenceFlags,
60
74
 
61
75
  return freeze({
62
76
  type: ReferenceTag,
63
- value: freeze({ name, isArray, index, flags: freeze({ hasGap, expression }) }),
77
+ value: freeze({ type, name, isArray, flags: freeze({ hasGap, expression }) }),
64
78
  });
65
79
  };
66
80
 
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;
89
+ };
90
+
67
91
  export const buildNullTag = () => {
68
92
  return freeze({ type: NullTag, value: undefined });
69
93
  };
70
94
 
95
+ export const buildInitializer = (isArray = false) => {
96
+ return freeze({ isArray });
97
+ };
98
+
71
99
  export const buildInitializerTag = (isArray = false) => {
72
- return freeze({ type: InitializerTag, value: { isArray } });
100
+ return freeze({ type: InitializerTag, value: buildInitializer(isArray) });
101
+ };
102
+
103
+ export const buildBinding = (languagePath = []) => {
104
+ if (!isArray(languagePath)) throw new Error();
105
+ return freeze({ languagePath });
106
+ };
107
+
108
+ export const buildBindingTag = (languagePath = []) => {
109
+ if (!isArray(languagePath)) throw new Error();
110
+ return freeze({ type: BindingTag, value: freeze({ languagePath }) });
111
+ };
112
+
113
+ export const buildChild = (type, value) => {
114
+ if (!isSymbol(type)) throw new Error();
115
+ return freeze({ type, value });
73
116
  };
74
117
 
75
118
  export const buildGapTag = () => {
76
119
  return freeze({ type: GapTag, value: undefined });
77
120
  };
78
121
 
79
- export const buildShiftTag = (index) => {
122
+ export const buildShiftTag = (index, height) => {
80
123
  if (!Number.isFinite(index)) throw new Error();
81
- return freeze({ type: ShiftTag, value: freeze({ index }) });
124
+ if (!Number.isFinite(height)) throw new Error();
125
+ return freeze({ type: ShiftTag, value: freeze({ index, height }) });
82
126
  };
83
127
 
84
- export const buildDoctypeTag = (attributes = {}) => {
128
+ export const buildDoctypeTag = (version = 0) => {
85
129
  return freeze({
86
130
  type: DoctypeTag,
87
- value: { doctype: 'cstml', version: 0, attributes: freeze(attributes) },
131
+ value: freeze({ doctype: 'cstml', version }),
88
132
  });
89
133
  };
90
134
 
91
135
  export const buildOpenNodeTag = (
92
136
  flags = nodeFlags,
93
- language = null,
94
137
  type = null,
95
138
  attributes = {},
139
+ literalValue = null,
140
+ selfClosing = !!literalValue,
96
141
  ) => {
97
142
  if (printType(type).startsWith('https://')) throw new Error();
143
+ if (literalValue && (!isString(literalValue) || !flags.token)) throw new Error();
98
144
 
99
145
  return freeze({
100
146
  type: OpenNodeTag,
101
147
  value: freeze({
102
148
  flags: freeze(flags),
103
- language,
104
149
  type: isString(type) ? Symbol.for(type) : type,
150
+ literalValue,
105
151
  attributes,
152
+ selfClosing,
106
153
  }),
107
154
  });
108
155
  };
@@ -118,12 +165,20 @@ export const buildLiteralTag = (value) => {
118
165
  return freeze({ type: LiteralTag, value });
119
166
  };
120
167
 
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 }) });
172
+ };
173
+
121
174
  const flagsWithGap = new WeakMap();
122
175
 
123
- export const getFlagsWithGap = (flags) => {
176
+ export const getFlagsWithGap = (flags, hasGap = true) => {
177
+ if (flags.hasGap === hasGap) return flags;
178
+
124
179
  let gapFlags = flagsWithGap.get(flags);
125
180
  if (!gapFlags) {
126
- gapFlags = { ...flags, hasGap: true };
181
+ gapFlags = { ...flags, hasGap };
127
182
  flagsWithGap.set(flags, gapFlags);
128
183
  }
129
184
  return gapFlags;
@@ -132,23 +187,29 @@ export const getFlagsWithGap = (flags) => {
132
187
  export const nodeFlags = freeze({
133
188
  token: false,
134
189
  hasGap: false,
190
+ fragment: false,
191
+ cover: false,
135
192
  });
136
193
 
137
- const hasGap = (properties) => {
138
- return find((node) => node.flags.hasGap, relatedNodes(properties));
139
- };
194
+ export const fragmentFlags = freeze({
195
+ token: false,
196
+ hasGap: false,
197
+ fragment: true,
198
+ cover: true,
199
+ });
140
200
 
141
- const getFlags = (flags, properties) => {
142
- if (!hasGap(properties)) {
143
- return flags;
144
- } else {
145
- return getFlagsWithGap(flags);
146
- }
147
- };
201
+ export const multiFragmentFlags = freeze({
202
+ token: false,
203
+ hasGap: false,
204
+ fragment: true,
205
+ cover: false,
206
+ });
148
207
 
149
208
  export const tokenFlags = freeze({
150
209
  token: true,
151
210
  hasGap: false,
211
+ fragment: false,
212
+ cover: false,
152
213
  });
153
214
 
154
215
  export const referenceFlags = freeze({
package/lib/object.js ADDED
@@ -0,0 +1,86 @@
1
+ export const { hasOwn, freeze, isFrozen, seal, isSealed, getPrototypeOf, getOwnPropertySymbols } =
2
+ Object;
3
+ export const { isArray } = Array;
4
+
5
+ export const has = (obj, path) => {
6
+ let value = obj;
7
+ for (const part of path) {
8
+ if (!hasOwn(value, part)) return false;
9
+ value = value[part];
10
+ }
11
+ return true;
12
+ };
13
+
14
+ export const get = (obj, path) => {
15
+ let value = obj;
16
+ for (const part of path) {
17
+ value = value[part];
18
+ }
19
+ return value;
20
+ };
21
+
22
+ export const set = (obj, path, value) => {
23
+ let obj_ = obj;
24
+
25
+ let lastKey;
26
+ for (let i = 0; i < path.length; i++) {
27
+ let key = path[i] < 0 ? obj_.length + path[i] : path[i];
28
+ let deeper = obj_[key];
29
+
30
+ if (path.length - 1 === i) {
31
+ lastKey = key;
32
+ } else if (deeper !== undefined) {
33
+ obj_ = deeper;
34
+ } else if (Number.isFinite(path[i + 1])) {
35
+ obj_ = deeper = obj_[key] = [];
36
+ } else {
37
+ obj_ = deeper = obj_[key] = {};
38
+ }
39
+ }
40
+
41
+ obj_[lastKey] = value;
42
+ };
43
+
44
+ export const immSet = (obj, path, value) => {
45
+ let obj_ = obj;
46
+
47
+ let objs = [];
48
+
49
+ for (let i = 0; i < path.length; i++) {
50
+ let key = path[i] < 0 ? obj_.length + path[i] : path[i];
51
+ let deeper = obj_[key];
52
+
53
+ if (obj_) {
54
+ objs.push(obj_);
55
+ }
56
+
57
+ if (path.length - 1 === i || hasOwn(obj_, deeper)) {
58
+ obj_ = deeper;
59
+ } else if (Number.isFinite(path[i + 1])) {
60
+ obj_ = [];
61
+ objs.push(obj_);
62
+ } else {
63
+ obj_ = {};
64
+ }
65
+ }
66
+
67
+ let newValue = value;
68
+ for (let i = objs.length - 1; i >= 0; i--) {
69
+ obj_ = objs[i];
70
+ obj_ = isArray(obj_) ? [...obj_] : { ...obj_ };
71
+ obj_[path[i]] = newValue;
72
+ freeze(obj_);
73
+ newValue = obj_;
74
+ }
75
+
76
+ return obj_;
77
+ };
78
+
79
+ export const isObject = (obj) => obj !== null && typeof obj === 'object';
80
+ export const isPlainObject = (val) => val && [Object.prototype, null].includes(getPrototypeOf(val));
81
+ export const isFunction = (obj) => typeof obj === 'function';
82
+ export const isSymbol = (obj) => typeof obj === 'symbol';
83
+ export const isString = (obj) => typeof obj === 'string';
84
+ export const isType = (obj) => isSymbol(obj) || isString(obj);
85
+ export const isRegex = (obj) => obj instanceof RegExp;
86
+ export const isPattern = (obj) => isString(obj) || isRegex(obj);
@@ -0,0 +1,44 @@
1
+ import { CloseNodeTag, OpenNodeTag, ReferenceTag } from './symbols.js';
2
+
3
+ export function* allTagPathsFor(range, options = {}) {
4
+ if (range == null) return;
5
+
6
+ const { unshift = false } = options;
7
+ let startPath = range[0];
8
+ let endPath = range[1];
9
+ let tagPath = startPath;
10
+
11
+ while (tagPath) {
12
+ if (tagPath.inner && tagPath.previousSibling.tag.type === ReferenceTag) {
13
+ tagPath = tagPath.inner.tagPathAt(0);
14
+ }
15
+
16
+ if (tagPath.path.depth < startPath.path.depth) {
17
+ return;
18
+ }
19
+
20
+ yield tagPath;
21
+
22
+ if (
23
+ tagPath.tag.type === CloseNodeTag ||
24
+ (tagPath.tag.type === OpenNodeTag && tagPath.tag.value.selfClosing)
25
+ ) {
26
+ let propPath = tagPath.path.parentPropertyPath;
27
+ if (endPath && propPath && endPath.equalTo(propPath.path.tagPathAt(propPath.tagsIndex, 2))) {
28
+ return;
29
+ }
30
+ }
31
+
32
+ if (endPath && endPath.equalTo(tagPath)) {
33
+ return;
34
+ }
35
+
36
+ tagPath = unshift ? tagPath.nextUnshifted : tagPath.next;
37
+ }
38
+ }
39
+
40
+ export function* allTagsFor(range, options = {}) {
41
+ for (let path of allTagPathsFor(range, options)) {
42
+ yield path.tag;
43
+ }
44
+ }