@shapeshift-labs/frontier-lang-parser 0.3.62 → 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 CHANGED
@@ -1,4 +1,4 @@
1
- import { actionNode, capabilityNode, createDocument, effectNode, entityNode, externNode, latticeNode, migrationNode, stateNode, targetNode, typeNode } from '@shapeshift-labs/frontier-lang-kernel';
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
- const name = nameFrom(block.header);
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,30 +246,6 @@ 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 = [];
306
251
  const seenNames = new Set();
@@ -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,7 +39,13 @@ 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
+ });
27
49
  const seenVariantNames = new Set();
28
50
  const seenVariantIds = new Set();
29
51
  for (const line of readBodyLines(source, block)) {
@@ -72,6 +94,46 @@ function readTypeSyntaxChildren(source, block, options) {
72
94
  return children;
73
95
  }
74
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
+
75
137
  function readConversionSyntaxChildren(source, block, options) {
76
138
  const children = [];
77
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.62",
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",