@bablr/agast-vm-helpers 0.2.0 → 0.3.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
@@ -4,6 +4,7 @@ import {
4
4
  interpolateArrayChildren,
5
5
  interpolateString,
6
6
  } from '@bablr/agast-helpers/template';
7
+ import { isNull } from '@bablr/agast-helpers/tree';
7
8
  import * as t from '@bablr/agast-helpers/shorthand';
8
9
  import * as l from './languages.js';
9
10
 
@@ -26,7 +27,7 @@ export const buildReference = (name, isArray) => {
26
27
  [t.ref`name`, ...when(isArray, [t.ref`arrayOperatorToken`]), t.ref`sigilToken`],
27
28
  {
28
29
  name: buildIdentifier(name),
29
- arrayOperatorToken: isArray ? t.s_node(l.CSTML, 'Punctuator', '[]') : null,
30
+ arrayOperatorToken: isArray ? t.s_node(l.CSTML, 'Punctuator', '[]') : t.null_node(),
30
31
  sigilToken: t.s_node(l.CSTML, 'Punctuator', ':'),
31
32
  },
32
33
  );
@@ -60,11 +61,11 @@ export const buildFlags = (flags = {}) => {
60
61
  ...when(expression, [t.ref`expressionToken`]),
61
62
  ],
62
63
  properties: {
63
- triviaToken: trivia && t.s_node(l.CSTML, 'Punctuator', '#'),
64
- intrinsicToken: intrinsic && t.s_node(l.CSTML, 'Punctuator', '~'),
65
- tokenToken: token && t.s_node(l.CSTML, 'Punctuator', '*'),
66
- escapeToken: escape && t.s_node(l.CSTML, 'Punctuator', '@'),
67
- expressionToken: expression && t.s_node(l.CSTML, 'Punctuator', '+'),
64
+ triviaToken: trivia ? t.s_node(l.CSTML, 'Punctuator', '#') : t.null_node(),
65
+ intrinsicToken: intrinsic ? t.s_node(l.CSTML, 'Punctuator', '~') : t.null_node(),
66
+ tokenToken: token ? t.s_node(l.CSTML, 'Punctuator', '*') : t.null_node(),
67
+ escapeToken: escape ? t.s_node(l.CSTML, 'Punctuator', '@') : t.null_node(),
68
+ expressionToken: expression ? t.s_node(l.CSTML, 'Punctuator', '+') : t.null_node(),
68
69
  },
69
70
  };
70
71
  };
@@ -82,9 +83,15 @@ export const buildFullyQualifiedSpamMatcher = (
82
83
  ) => {
83
84
  const attributes_ = buildAttributes(attributes);
84
85
 
85
- const lArr = language ? [...language] : [];
86
+ let language_;
86
87
 
87
- let language_ = lArr.length === 0 ? null : lArr;
88
+ if (isString(language)) {
89
+ language_ = language;
90
+ } else {
91
+ let lArr = isString(language) ? language : language ? [...language] : [];
92
+
93
+ language_ = lArr.length === 0 ? null : lArr;
94
+ }
88
95
 
89
96
  return t.node(
90
97
  l.Spamex,
@@ -102,23 +109,17 @@ export const buildFullyQualifiedSpamMatcher = (
102
109
  {
103
110
  openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
104
111
  flags: buildFlags(flags),
105
- language: buildLanguage(language_),
106
- languageSeparator: language_ && type && t.s_node(l.CSTML, 'Punctuator', ':'),
107
- type: type && buildIdentifier(type),
108
- intrinsicValue: intrinsicValue && buildString(intrinsicValue),
112
+ language: language_ ? buildLanguage(language_) : t.null_node(),
113
+ languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
114
+ type: type ? buildIdentifier(type) : t.null_node(),
115
+ intrinsicValue: intrinsicValue ? buildString(intrinsicValue) : t.null_node(),
109
116
  attributes: attributes_,
110
117
  closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
111
118
  },
112
119
  );
113
120
  };
114
121
 
115
- export const buildNodeOpenTag = (
116
- flags,
117
- language,
118
- type = null,
119
- intrinsicValue = null,
120
- attributes = {},
121
- ) => {
122
+ export const buildNodeOpenTag = (flags, language, type = null, attributes = {}) => {
122
123
  const attributes_ = buildAttributes(attributes);
123
124
 
124
125
  let language_ = !language || language.length === 0 ? null : language;
@@ -131,21 +132,17 @@ export const buildNodeOpenTag = (
131
132
  t.ref`flags`,
132
133
  ...when(language_, [t.ref`language`, t.ref`languageSeparator`]),
133
134
  ...when(type, [t.ref`type`]),
134
- ...when(intrinsicValue, [t.embedded(buildSpace()), t.ref`intrinsicValue`]),
135
135
  ...when(attributes_.length, [t.embedded(buildSpace())]),
136
136
  ...interpolateArrayChildren(attributes_, t.ref`attributes[]`, t.embedded(buildSpace())),
137
- when(intrinsicValue, [t.embedded(buildSpace), t.ref`selfClosingToken`]),
138
137
  t.ref`closeToken`,
139
138
  ],
140
139
  {
141
140
  openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
142
141
  flags: buildFlags(flags),
143
- language: buildLanguage(language_),
144
- languageSeparator: language_ && type && t.s_node(l.CSTML, 'Punctuator', ':'),
145
- type: type && buildIdentifier(type),
146
- intrinsicValue: intrinsicValue && buildString(intrinsicValue),
142
+ language: language_ && type ? buildLanguage(language_) : t.null_node(),
143
+ languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
144
+ type: type ? buildIdentifier(type) : t.null_node(),
147
145
  attributes: attributes_,
148
- selfClosingToken: intrinsicValue && t.s_node(l.CSTML, 'Punctuator', '/'),
149
146
  closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
150
147
  },
151
148
  );
@@ -178,7 +175,7 @@ export const buildDoctypeTag = (attributes) => {
178
175
  };
179
176
 
180
177
  export const buildIdentifierPath = (path) => {
181
- const path_ = [...path];
178
+ const path_ = isString(path) ? [path] : [...path];
182
179
  const segments = path_.map((name) => buildIdentifier(name));
183
180
  const separators = path_.slice(0, -1).map((_) => t.s_node(l.CSTML, 'Punctuator', '.'));
184
181
 
@@ -198,7 +195,9 @@ export const buildIdentifierPath = (path) => {
198
195
  };
199
196
 
200
197
  export const buildLanguage = (language) => {
201
- return language && buildIdentifierPath(language);
198
+ return language && isString(language) && language.startsWith('https://')
199
+ ? buildString(language)
200
+ : buildIdentifierPath(language);
202
201
  };
203
202
 
204
203
  export const buildNodeCloseTag = (type, language) => {
@@ -214,9 +213,9 @@ export const buildNodeCloseTag = (type, language) => {
214
213
  ],
215
214
  {
216
215
  openToken: t.s_node(l.CSTML, 'Punctuator', '</'),
217
- language: buildLanguage(language),
218
- languageSeparator: language && type ? t.s_node(l.CSTML, 'Punctuator', ':') : null,
219
- type: type && buildIdentifier(type),
216
+ language: language ? buildLanguage(language) : t.null_node(),
217
+ languageSeparator: language && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
218
+ type: type ? buildIdentifier(type) : t.null_node(),
220
219
  closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
221
220
  },
222
221
  );
@@ -354,7 +353,7 @@ export const buildString = (value) => {
354
353
  if (escapables[chr]) {
355
354
  value = t.node(l.CSTML, 'EscapeCode', [t.ref`sigilToken`], {
356
355
  sigilToken: buildKeyword(escapables[chr]),
357
- digits: null,
356
+ digits: t.null_node(),
358
357
  });
359
358
  } else if (chr.charCodeAt(0) < 32) {
360
359
  const hexDigits = chr.charCodeAt(0).toString(16).padStart(4, '0');
@@ -372,7 +371,7 @@ export const buildString = (value) => {
372
371
  }
373
372
 
374
373
  terminals.push(
375
- t.buildEmbedded(
374
+ t.buildEmbeddedNode(
376
375
  t.e_node(
377
376
  l.CSTML,
378
377
  'EscapeSequence',
@@ -509,7 +508,7 @@ export const buildMappingAttribute = (key, value) => {
509
508
 
510
509
  export const buildBooleanAttribute = (key, value) => {
511
510
  return t.node(l.CSTML, 'BooleanAttribute', [...when(!value, [t.ref`negateToken`]), t.ref`key`], {
512
- negateToken: !value ? t.s_node(l.CSTML, 'Puncutator', '!') : null,
511
+ negateToken: !value ? t.s_node(l.CSTML, 'Puncutator', '!') : t.null_node(),
513
512
  key: buildIdentifier(key),
514
513
  });
515
514
  };
@@ -519,7 +518,7 @@ export const buildAttribute = (key, value) => {
519
518
  };
520
519
 
521
520
  export const buildExpression = (expr) => {
522
- if (expr == null) return buildNull();
521
+ if (isNull(expr)) return buildNull();
523
522
 
524
523
  switch (typeof expr) {
525
524
  case 'boolean':
@@ -572,7 +571,7 @@ export const buildNodeMatcher = (flags, language, type, attributes = {}) => {
572
571
  {
573
572
  openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
574
573
  language: buildLanguage(language_),
575
- languageSeparator: language_ && type && t.s_node(l.CSTML, 'Punctuator', ':'),
574
+ languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
576
575
  flags: flags_,
577
576
  type: buildIdentifier(type),
578
577
  attributes: attributes_,
package/lib/deembed.js ADDED
@@ -0,0 +1,17 @@
1
+ const { isArray } = Array;
2
+ const isString = (val) => typeof val === 'string';
3
+ const isNumber = (val) => typeof val === 'number';
4
+
5
+ export const deembedExpression = (expr) => {
6
+ if (isString(expr) || expr == null || typeof expr === 'boolean' || isNumber(expr)) {
7
+ return expr;
8
+ } else if (isArray(expr)) {
9
+ return expr.map((value) => deembedExpression(value));
10
+ } else if (typeof expr === 'object') {
11
+ return Object.fromEntries(
12
+ Object.entries(expr.value).map(({ 0: key, 1: value }) => [key, deembedExpression(value)]),
13
+ );
14
+ } else {
15
+ throw new Error();
16
+ }
17
+ };
package/lib/embed.js ADDED
@@ -0,0 +1,21 @@
1
+ import { buildEmbeddedExpression } from './internal-builders.js';
2
+
3
+ const { isArray } = Array;
4
+ const isString = (val) => typeof val === 'string';
5
+ const isNumber = (val) => typeof val === 'number';
6
+
7
+ export const embedExpression = (expr) => {
8
+ if (isString(expr) || expr == null || typeof expr === 'boolean' || isNumber(expr)) {
9
+ return expr;
10
+ } else if (isArray(expr)) {
11
+ return expr.map((value) => embedExpression(value));
12
+ } else if (typeof expr === 'object') {
13
+ return buildEmbeddedExpression(
14
+ Object.fromEntries(
15
+ Object.entries(expr).map(({ 0: key, 1: value }) => [key, embedExpression(value)]),
16
+ ),
17
+ );
18
+ } else {
19
+ throw new Error();
20
+ }
21
+ };
package/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { getCooked } from '@bablr/agast-helpers/tree';
1
+ import { getCooked, isNull } from '@bablr/agast-helpers/tree';
2
+ import * as sym from '@bablr/agast-helpers/symbols';
2
3
 
3
4
  export * from './builders.js';
4
5
 
@@ -31,28 +32,24 @@ const reifyFlags = (flags) => {
31
32
  let { triviaToken, escapeToken, tokenToken, expressionToken, intrinsicToken } = flags.properties;
32
33
 
33
34
  return {
34
- token: !!tokenToken,
35
- escape: !!escapeToken,
36
- trivia: !!triviaToken,
37
- intrinsic: !!intrinsicToken,
38
- expression: !!expressionToken,
35
+ token: reifyExpression(tokenToken),
36
+ escape: reifyExpression(escapeToken),
37
+ trivia: reifyExpression(triviaToken),
38
+ intrinsic: reifyExpression(intrinsicToken),
39
+ expression: reifyExpression(expressionToken),
39
40
  };
40
41
  };
41
42
 
42
43
  export const reifyLanguage = (language) => {
43
44
  const value = reifyExpression(language);
44
45
 
45
- if (typeof value === 'string' && !value.startsWith('https://')) {
46
- throw new Error('bad language');
47
- }
48
-
49
- return value;
46
+ return typeof value === 'string' && !value.startsWith('https://') ? [value] : value;
50
47
  };
51
48
 
52
49
  export const reifyExpression = (node) => {
53
50
  if (node instanceof Promise) throw new Error();
54
51
 
55
- if (!node) return null;
52
+ if (!node || node.type === sym.null) return null;
56
53
 
57
54
  if (node.language === 'https://bablr.org/languages/core/en/cstml') {
58
55
  switch (node.type) {
@@ -73,7 +70,7 @@ export const reifyExpression = (node) => {
73
70
 
74
71
  name = reifyExpression(name);
75
72
 
76
- return { type: 'Reference', value: { name, isArray: !!arrayOperatorToken } };
73
+ return { type: 'Reference', value: { name, isArray: !isNull(arrayOperatorToken) } };
77
74
  }
78
75
 
79
76
  case 'Literal': {
@@ -87,17 +84,16 @@ export const reifyExpression = (node) => {
87
84
  }
88
85
 
89
86
  case 'OpenNodeTag': {
90
- let { flags, language, type, attributes, intrinsicValue } = node.properties;
87
+ let { flags, language, type, attributes } = node.properties;
91
88
 
92
89
  flags = reifyFlags(flags);
93
90
  language = reifyLanguage(language);
94
91
  type = reifyExpression(type);
95
92
  attributes = reifyAttributes(attributes);
96
- intrinsicValue = reifyExpression(intrinsicValue);
97
93
 
98
94
  return {
99
95
  type: 'OpenNodeTag',
100
- value: { flags, language, type, intrinsicValue, properties: {}, attributes },
96
+ value: { flags, language, type, attributes },
101
97
  };
102
98
  }
103
99
 
@@ -119,6 +115,10 @@ export const reifyExpression = (node) => {
119
115
  return node.properties.sign === '-' ? -Infinity : Infinity;
120
116
  }
121
117
 
118
+ case 'Punctuator': {
119
+ return getCooked(node);
120
+ }
121
+
122
122
  case 'String':
123
123
  return node.properties.content ? getCooked(node.properties.content) : '';
124
124
 
@@ -148,7 +148,7 @@ export const reifyExpression = (node) => {
148
148
  let { flags, language, type, attributes, intrinsicValue } = node.properties;
149
149
 
150
150
  flags = reifyFlags(flags);
151
- language = reifyExpression(language);
151
+ language = reifyLanguage(language);
152
152
  type = reifyExpression(type);
153
153
  attributes = reifyAttributes(attributes);
154
154
  intrinsicValue = reifyExpression(intrinsicValue);
@@ -205,7 +205,7 @@ export const reifyExpression = (node) => {
205
205
  };
206
206
 
207
207
  export const reifyExpressionShallow = (node) => {
208
- if (!node) return null;
208
+ if (!node || node.type === sym.null) return null;
209
209
 
210
210
  if (
211
211
  ![
@@ -244,7 +244,7 @@ export const reifyAttributes = (attributes) => {
244
244
  if (attr.type === 'MappingAttribute') {
245
245
  return [reifyExpression(attr.properties.key), reifyExpression(attr.properties.value)];
246
246
  } else if (attr.type === 'BooleanAttribute') {
247
- return [reifyExpression(attr.properties.key), !attr.properties.negateToken];
247
+ return [reifyExpression(attr.properties.key), isNull(attr.properties.negateToken)];
248
248
  } else {
249
249
  throw new Error();
250
250
  }
@@ -0,0 +1 @@
1
+ export * from '@bablr/agast-helpers/builders';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/agast-vm-helpers",
3
3
  "description": "Helper functions for working with the BABLR VM",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -9,13 +9,16 @@
9
9
  ],
10
10
  "exports": {
11
11
  ".": "./lib/index.js",
12
- "./facades": "./lib/facades.js",
13
12
  "./builders": "./lib/builders.js",
13
+ "./deembed": "./lib/deembed.js",
14
+ "./embed": "./lib/embed.js",
15
+ "./facades": "./lib/facades.js",
16
+ "./internal-builders": "./lib/internal-builders.js",
14
17
  "./languages": "./lib/languages.js"
15
18
  },
16
19
  "sideEffects": false,
17
20
  "dependencies": {
18
- "@bablr/agast-helpers": "0.2.0"
21
+ "@bablr/agast-helpers": "0.3.0"
19
22
  },
20
23
  "devDependencies": {
21
24
  "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",