@shapeshift-labs/frontier-lang-parser 0.3.61 → 0.3.63
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/dist/index.js +9 -59
- package/dist/member-records.js +74 -0
- package/dist/source-syntax-children.js +83 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { actionNode, capabilityNode, createDocument, effectNode,
|
|
1
|
+
import { actionNode, capabilityNode, createDocument, effectNode, externNode, latticeNode, migrationNode, targetNode, typeNode } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
2
|
import { readActionBodyRecords, stripNestedBlocks } from './action-body.js';
|
|
3
3
|
import { parseConstraintSpaceBlock } from './constraint-space.js';
|
|
4
4
|
import { parseConversionBlock } from './conversion.js';
|
|
@@ -8,6 +8,7 @@ import { parseDecisionGraphBlock } from './decision-graph.js';
|
|
|
8
8
|
import { parseDialectRegistryBlock } from './dialect-registry.js';
|
|
9
9
|
import { parseInterlinguaBlock } from './interlingua.js';
|
|
10
10
|
import { createParsedMetadata } from './metadata.js';
|
|
11
|
+
import { parseEntityBlock, parseStateBlock, readTypeFields } from './member-records.js';
|
|
11
12
|
import { parseSemanticOperationsBlock } from './operations.js';
|
|
12
13
|
import { parsePackageManifestBlock } from './package-manifest.js';
|
|
13
14
|
import { parseParadigmBlock } from './paradigm.js';
|
|
@@ -85,40 +86,8 @@ function readBlocks(source, options) {
|
|
|
85
86
|
}
|
|
86
87
|
function idFrom(header, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(header)?.[1] ?? fallback; }
|
|
87
88
|
function nameFrom(header) { return /^([A-Za-z_$][\w$]*)/.exec(header)?.[1] ?? 'Unnamed'; }
|
|
88
|
-
function parseEntity(block) {
|
|
89
|
-
|
|
90
|
-
const fields = [];
|
|
91
|
-
const fieldRe = /^\s*([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*([^@{\n]+)([^\n{]*)(?:\{([^}]*)\})?/gm;
|
|
92
|
-
let m;
|
|
93
|
-
while ((m = fieldRe.exec(block.body))) {
|
|
94
|
-
const mergeText = (m[4] ?? '') + ' ' + (m[5] ?? '');
|
|
95
|
-
fields.push({
|
|
96
|
-
id: m[2] ?? `field_${name}_${m[1]}`,
|
|
97
|
-
name: m[1],
|
|
98
|
-
type: parseTypeExpression(m[3].trim()),
|
|
99
|
-
key: /@key/.test(m[4] ?? ''),
|
|
100
|
-
merge: parseMerge(mergeText),
|
|
101
|
-
semantic: parseSemantic(mergeText)
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return entityNode({ id: idFrom(block.header, `ent_${name}`), name, fields });
|
|
105
|
-
}
|
|
106
|
-
function parseState(block) {
|
|
107
|
-
const name = nameFrom(block.header);
|
|
108
|
-
const collections = [];
|
|
109
|
-
const re = /^\s*([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*([^@{\n]+)(?:\{([^}]*)\})?/gm;
|
|
110
|
-
let m;
|
|
111
|
-
while ((m = re.exec(block.body))) {
|
|
112
|
-
collections.push({
|
|
113
|
-
id: m[2] ?? `collection_${name}_${m[1]}`,
|
|
114
|
-
name: m[1],
|
|
115
|
-
type: parseTypeExpression(m[3].trim()),
|
|
116
|
-
merge: parseMerge(m[4] ?? ''),
|
|
117
|
-
semantic: parseSemantic(m[4] ?? '')
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
return stateNode({ id: idFrom(block.header, `state_${name}`), name, collections });
|
|
121
|
-
}
|
|
89
|
+
function parseEntity(block) { return parseEntityBlock(block, idFrom, nameFrom); }
|
|
90
|
+
function parseState(block) { return parseStateBlock(block, idFrom, nameFrom); }
|
|
122
91
|
function parseAction(block) {
|
|
123
92
|
const name = nameFrom(block.header);
|
|
124
93
|
const topLevelBody = stripNestedBlocks('body', block.body);
|
|
@@ -277,38 +246,19 @@ function readChangeRecords(body) {
|
|
|
277
246
|
}
|
|
278
247
|
return changes;
|
|
279
248
|
}
|
|
280
|
-
function parseMerge(text) {
|
|
281
|
-
const kind = /merge\s+([A-Za-z][\w-]*)/.exec(text)?.[1];
|
|
282
|
-
if (!kind) return undefined;
|
|
283
|
-
const law = /law\s+([A-Za-z][\w-]*)/.exec(text)?.[1];
|
|
284
|
-
const laws = /laws\s+([A-Za-z][\w-]*(?:\s*,\s*[A-Za-z][\w-]*)*)/.exec(text)?.[1]?.split(',').map((item) => item.trim()).filter(Boolean);
|
|
285
|
-
const latticeId = /lattice\s+([A-Za-z_$][\w$-]*)/.exec(text)?.[1];
|
|
286
|
-
return { kind, law, laws, latticeId };
|
|
287
|
-
}
|
|
288
|
-
function parseSemantic(text) {
|
|
289
|
-
const crdtType = /crdt\s+([A-Za-z][\w-]*)/.exec(text)?.[1];
|
|
290
|
-
const latticeId = /lattice\s+([A-Za-z_$][\w$-]*)/.exec(text)?.[1];
|
|
291
|
-
if (crdtType) return { kind: 'crdt', latticeId, crdt: { type: crdtType } };
|
|
292
|
-
if (latticeId) return { kind: 'lattice', latticeId };
|
|
293
|
-
return undefined;
|
|
294
|
-
}
|
|
295
|
-
function readTypeFields(body) {
|
|
296
|
-
const fields = [];
|
|
297
|
-
const re = /^\s*([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*([^\n]+)/gm;
|
|
298
|
-
let match;
|
|
299
|
-
while ((match = re.exec(body))) {
|
|
300
|
-
fields.push({ id: match[2] ?? `type_field_${match[1]}`, name: match[1], type: parseTypeExpression(match[3].trim()) });
|
|
301
|
-
}
|
|
302
|
-
return fields.length ? fields : undefined;
|
|
303
|
-
}
|
|
304
249
|
function readVariants(body) {
|
|
305
250
|
const variants = [];
|
|
251
|
+
const seenNames = new Set();
|
|
252
|
+
const seenIds = new Set();
|
|
306
253
|
const re = /^\s*variant\s+([A-Za-z_$][\w$]*)(.*)$/gm;
|
|
307
254
|
let match;
|
|
308
255
|
while ((match = re.exec(body))) {
|
|
309
256
|
const fields = readVariantPayloadFields(match[2] ?? '', match[1], parseTypeExpression);
|
|
310
257
|
if (fields === null) continue;
|
|
311
258
|
const id = /@id\(\s*["']([^"']+)["']\s*\)/.exec(match[2] ?? '')?.[1];
|
|
259
|
+
const variantId = id ?? `type_variant_${safeId(match[1])}`;
|
|
260
|
+
if (seenNames.has(match[1]) || seenIds.has(variantId)) continue;
|
|
261
|
+
seenNames.add(match[1]); seenIds.add(variantId);
|
|
312
262
|
variants.push({ ...(id ? { id } : {}), name: match[1], ...(fields?.length ? { fields } : {}) });
|
|
313
263
|
}
|
|
314
264
|
return variants.length ? variants : undefined;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { entityNode, stateNode } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { parseTypeExpression } from './type-expressions.js';
|
|
3
|
+
|
|
4
|
+
export function parseEntityBlock(block, idFrom, nameFrom) {
|
|
5
|
+
const name = nameFrom(block.header);
|
|
6
|
+
const fields = [], seen = identitySets();
|
|
7
|
+
const fieldRe = /^\s*([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*([^@{\n]+)([^\n{]*)(?:\{([^}]*)\})?/gm;
|
|
8
|
+
let m;
|
|
9
|
+
while ((m = fieldRe.exec(block.body))) {
|
|
10
|
+
const mergeText = (m[4] ?? '') + ' ' + (m[5] ?? '');
|
|
11
|
+
appendUnique(fields, seen, {
|
|
12
|
+
id: m[2] ?? `field_${name}_${m[1]}`,
|
|
13
|
+
name: m[1],
|
|
14
|
+
type: parseTypeExpression(m[3].trim()),
|
|
15
|
+
key: /@key/.test(m[4] ?? ''),
|
|
16
|
+
merge: parseMerge(mergeText),
|
|
17
|
+
semantic: parseSemantic(mergeText)
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return entityNode({ id: idFrom(block.header, `ent_${name}`), name, fields });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function parseStateBlock(block, idFrom, nameFrom) {
|
|
24
|
+
const name = nameFrom(block.header);
|
|
25
|
+
const collections = [], seen = identitySets();
|
|
26
|
+
const re = /^\s*([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*([^@{\n]+)(?:\{([^}]*)\})?/gm;
|
|
27
|
+
let m;
|
|
28
|
+
while ((m = re.exec(block.body))) {
|
|
29
|
+
appendUnique(collections, seen, {
|
|
30
|
+
id: m[2] ?? `collection_${name}_${m[1]}`,
|
|
31
|
+
name: m[1],
|
|
32
|
+
type: parseTypeExpression(m[3].trim()),
|
|
33
|
+
merge: parseMerge(m[4] ?? ''),
|
|
34
|
+
semantic: parseSemantic(m[4] ?? '')
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return stateNode({ id: idFrom(block.header, `state_${name}`), name, collections });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function readTypeFields(body) {
|
|
41
|
+
const fields = [], seen = identitySets();
|
|
42
|
+
const re = /^\s*([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*([^\n]+)/gm;
|
|
43
|
+
let match;
|
|
44
|
+
while ((match = re.exec(body))) {
|
|
45
|
+
appendUnique(fields, seen, { id: match[2] ?? `type_field_${match[1]}`, name: match[1], type: parseTypeExpression(match[3].trim()) });
|
|
46
|
+
}
|
|
47
|
+
return fields.length ? fields : undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function identitySets() {
|
|
51
|
+
return { names: new Set(), ids: new Set() };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function appendUnique(records, seen, record) {
|
|
55
|
+
if (seen.names.has(record.name) || seen.ids.has(record.id)) return;
|
|
56
|
+
seen.names.add(record.name); seen.ids.add(record.id); records.push(record);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseMerge(text) {
|
|
60
|
+
const kind = /merge\s+([A-Za-z][\w-]*)/.exec(text)?.[1];
|
|
61
|
+
if (!kind) return undefined;
|
|
62
|
+
const law = /law\s+([A-Za-z][\w-]*)/.exec(text)?.[1];
|
|
63
|
+
const laws = /laws\s+([A-Za-z][\w-]*(?:\s*,\s*[A-Za-z][\w-]*)*)/.exec(text)?.[1]?.split(',').map((item) => item.trim()).filter(Boolean);
|
|
64
|
+
const latticeId = /lattice\s+([A-Za-z_$][\w$-]*)/.exec(text)?.[1];
|
|
65
|
+
return { kind, law, laws, latticeId };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseSemantic(text) {
|
|
69
|
+
const crdtType = /crdt\s+([A-Za-z][\w-]*)/.exec(text)?.[1];
|
|
70
|
+
const latticeId = /lattice\s+([A-Za-z_$][\w$-]*)/.exec(text)?.[1];
|
|
71
|
+
if (crdtType) return { kind: 'crdt', latticeId, crdt: { type: crdtType } };
|
|
72
|
+
if (latticeId) return { kind: 'lattice', latticeId };
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
@@ -15,6 +15,22 @@ export function readSourceSyntaxChildren(source, block, options = {}) {
|
|
|
15
15
|
children = readConversionSyntaxChildren(source, block, options);
|
|
16
16
|
} else if (block.kind === 'type') {
|
|
17
17
|
children = readTypeSyntaxChildren(source, block, options);
|
|
18
|
+
} else if (block.kind === 'entity') {
|
|
19
|
+
children = readMemberSyntaxChildren(source, block, options, {
|
|
20
|
+
kind: 'entityField',
|
|
21
|
+
rowKind: 'field',
|
|
22
|
+
idPrefix: `field_${safeId(block.name)}_`,
|
|
23
|
+
duplicateNameReason: 'duplicate-entity-field-name',
|
|
24
|
+
duplicateIdReason: 'duplicate-entity-field-id'
|
|
25
|
+
});
|
|
26
|
+
} else if (block.kind === 'state') {
|
|
27
|
+
children = readMemberSyntaxChildren(source, block, options, {
|
|
28
|
+
kind: 'stateCollection',
|
|
29
|
+
rowKind: 'collection',
|
|
30
|
+
idPrefix: `collection_${safeId(block.name)}_`,
|
|
31
|
+
duplicateNameReason: 'duplicate-state-collection-name',
|
|
32
|
+
duplicateIdReason: 'duplicate-state-collection-id'
|
|
33
|
+
});
|
|
18
34
|
} else {
|
|
19
35
|
const rowConfig = ROW_SYNTAX_CONFIG[block.kind];
|
|
20
36
|
if (rowConfig) children = readGenericRowSyntaxChildren(source, block, options, rowConfig);
|
|
@@ -23,19 +39,42 @@ export function readSourceSyntaxChildren(source, block, options = {}) {
|
|
|
23
39
|
}
|
|
24
40
|
|
|
25
41
|
function readTypeSyntaxChildren(source, block, options) {
|
|
26
|
-
const children =
|
|
42
|
+
const children = readMemberSyntaxChildren(source, block, options, {
|
|
43
|
+
kind: 'typeField',
|
|
44
|
+
rowKind: 'typeField',
|
|
45
|
+
idPrefix: 'type_field_',
|
|
46
|
+
duplicateNameReason: 'duplicate-type-field-name',
|
|
47
|
+
duplicateIdReason: 'duplicate-type-field-id'
|
|
48
|
+
});
|
|
49
|
+
const seenVariantNames = new Set();
|
|
50
|
+
const seenVariantIds = new Set();
|
|
27
51
|
for (const line of readBodyLines(source, block)) {
|
|
28
52
|
if (!line.text || line.text.startsWith('#')) continue;
|
|
29
53
|
const variant = /^variant\s+([A-Za-z_$][\w$]*)(.*)$/.exec(line.text);
|
|
30
54
|
if (!variant) continue;
|
|
31
55
|
const [, name, rest] = variant;
|
|
32
56
|
const payload = inspectVariantPayload(rest ?? '', inspectTypeExpressionSyntax);
|
|
57
|
+
const id = idFrom(rest, `type_variant_${safeId(name)}`);
|
|
58
|
+
let recognized = payload.ok;
|
|
59
|
+
let reason = payload.ok ? undefined : payload.reason;
|
|
60
|
+
if (recognized && seenVariantNames.has(name)) {
|
|
61
|
+
recognized = false;
|
|
62
|
+
reason = 'duplicate-type-variant-name';
|
|
63
|
+
}
|
|
64
|
+
if (recognized && seenVariantIds.has(id)) {
|
|
65
|
+
recognized = false;
|
|
66
|
+
reason = 'duplicate-type-variant-id';
|
|
67
|
+
}
|
|
68
|
+
if (recognized) {
|
|
69
|
+
seenVariantNames.add(name);
|
|
70
|
+
seenVariantIds.add(id);
|
|
71
|
+
}
|
|
33
72
|
children.push(cleanRecord({
|
|
34
73
|
kind: 'typeVariant',
|
|
35
74
|
rowKind: 'variant',
|
|
36
75
|
normalizedRowKind: 'variant',
|
|
37
76
|
name,
|
|
38
|
-
id
|
|
77
|
+
id,
|
|
39
78
|
header: line.text,
|
|
40
79
|
startOffset: line.startOffset,
|
|
41
80
|
endOffset: line.endOffset,
|
|
@@ -46,8 +85,8 @@ function readTypeSyntaxChildren(source, block, options) {
|
|
|
46
85
|
moduleId: block.moduleId,
|
|
47
86
|
moduleName: block.moduleName,
|
|
48
87
|
sourceSpan: sourceSpan(source, block, line.startOffset, line.endOffset, options),
|
|
49
|
-
recognized
|
|
50
|
-
reason
|
|
88
|
+
recognized,
|
|
89
|
+
reason,
|
|
51
90
|
fieldCount: payload.fieldCount,
|
|
52
91
|
fieldIds: payload.fieldIds
|
|
53
92
|
}));
|
|
@@ -55,6 +94,46 @@ function readTypeSyntaxChildren(source, block, options) {
|
|
|
55
94
|
return children;
|
|
56
95
|
}
|
|
57
96
|
|
|
97
|
+
function readMemberSyntaxChildren(source, block, options, config) {
|
|
98
|
+
const children = [];
|
|
99
|
+
const seenNames = new Set(), seenIds = new Set();
|
|
100
|
+
for (const line of readBodyLines(source, block)) {
|
|
101
|
+
if (!line.text || line.text.startsWith('#')) continue;
|
|
102
|
+
const member = /^([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*(.+)$/.exec(line.text);
|
|
103
|
+
if (!member) continue;
|
|
104
|
+
const [, name, explicitId] = member;
|
|
105
|
+
const id = explicitId ?? `${config.idPrefix}${safeId(name)}`;
|
|
106
|
+
let recognized = true, reason;
|
|
107
|
+
if (seenNames.has(name)) { recognized = false; reason = config.duplicateNameReason; }
|
|
108
|
+
if (recognized && seenIds.has(id)) { recognized = false; reason = config.duplicateIdReason; }
|
|
109
|
+
if (recognized) { seenNames.add(name); seenIds.add(id); }
|
|
110
|
+
children.push(memberChild(source, block, options, line, { ...config, name, id, recognized, reason }));
|
|
111
|
+
}
|
|
112
|
+
return children;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function memberChild(source, block, options, line, input) {
|
|
116
|
+
return cleanRecord({
|
|
117
|
+
kind: input.kind,
|
|
118
|
+
rowKind: input.rowKind,
|
|
119
|
+
normalizedRowKind: input.rowKind,
|
|
120
|
+
name: input.name,
|
|
121
|
+
id: input.id,
|
|
122
|
+
header: line.text,
|
|
123
|
+
startOffset: line.startOffset,
|
|
124
|
+
endOffset: line.endOffset,
|
|
125
|
+
location: sourcePosition(source, line.startOffset),
|
|
126
|
+
parentKind: block.kind,
|
|
127
|
+
parentId: block.id,
|
|
128
|
+
parentName: block.name,
|
|
129
|
+
moduleId: block.moduleId,
|
|
130
|
+
moduleName: block.moduleName,
|
|
131
|
+
sourceSpan: sourceSpan(source, block, line.startOffset, line.endOffset, options),
|
|
132
|
+
recognized: input.recognized,
|
|
133
|
+
reason: input.reason
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
58
137
|
function readConversionSyntaxChildren(source, block, options) {
|
|
59
138
|
const children = [];
|
|
60
139
|
for (const line of readBodyLines(source, block)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shapeshift-labs/frontier-lang-parser",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.63",
|
|
4
4
|
"description": "Parser for the first Frontier Lang .frontier syntax slice.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "node scripts/build.mjs",
|
|
25
|
-
"test": "npm run build && node test/smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
|
|
25
|
+
"test": "npm run build && node test/smoke.mjs && node test/member-identity-smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
|
|
26
26
|
"typecheck": "node ./node_modules/typescript/bin/tsc --noEmit -p test/tsconfig.json",
|
|
27
27
|
"fuzz": "npm run build && node fuzz/smoke.mjs",
|
|
28
28
|
"bench": "npm run build && node bench/smoke.mjs",
|