@shapeshift-labs/frontier-lang-parser 0.3.10 → 0.3.12

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.
@@ -0,0 +1,121 @@
1
+ const FAMILIES = {
2
+ type: { field: 'typeConstraints', sourceKey: 'sourceTypes', targetKey: 'targetTypes' },
3
+ typeConstraint: { field: 'typeConstraints', sourceKey: 'sourceTypes', targetKey: 'targetTypes' },
4
+ controlFlow: { field: 'controlFlowConstraints', sourceKey: 'sourceControlFlows', targetKey: 'targetControlFlows' },
5
+ controlFlowConstraint: { field: 'controlFlowConstraints', sourceKey: 'sourceControlFlows', targetKey: 'targetControlFlows' },
6
+ lifetime: { field: 'lifetimeConstraints', sourceKey: 'sourceLifetimeConstraints', targetKey: 'targetLifetimeConstraints' },
7
+ lifetimeConstraint: { field: 'lifetimeConstraints', sourceKey: 'sourceLifetimeConstraints', targetKey: 'targetLifetimeConstraints' },
8
+ callableBoundary: { field: 'callableBoundaryConstraints', sourceKey: 'sourceCallables', targetKey: 'targetCallables' },
9
+ adtPattern: { field: 'adtPatternConstraints', sourceKey: 'sourcePatterns', targetKey: 'targetPatterns' },
10
+ dataLayout: { field: 'dataLayoutConstraints', sourceKey: 'sourceLayouts', targetKey: 'targetLayouts' },
11
+ effect: { field: 'effectConstraints', sourceKey: 'sourceEffects', targetKey: 'targetEffects' },
12
+ concurrencyModel: { field: 'concurrencyModelConstraints', sourceKey: 'sourceConcurrencyModels', targetKey: 'targetConcurrencyModels' },
13
+ errorModel: { field: 'errorModelConstraints', sourceKey: 'sourceErrors', targetKey: 'targetErrors' },
14
+ evaluationModel: { field: 'evaluationModelConstraints', sourceKey: 'sourceEvaluations', targetKey: 'targetEvaluations' },
15
+ hostEnvironment: { field: 'hostEnvironmentConstraints', sourceKey: 'sourceHosts', targetKey: 'targetHosts' },
16
+ memoryModel: { field: 'memoryModelConstraints', sourceKey: 'sourceMemoryModels', targetKey: 'targetMemoryModels' },
17
+ metaprogramming: { field: 'metaprogrammingConstraints', sourceKey: 'sourceMetaprograms', targetKey: 'targetMetaprograms' },
18
+ module: { field: 'moduleConstraints', sourceKey: 'sourceModules', targetKey: 'targetModules' },
19
+ scopeBinding: { field: 'scopeBindingConstraints', sourceKey: 'sourceBindings', targetKey: 'targetBindings' },
20
+ numericSemantics: { field: 'numericSemanticsConstraints', sourceKey: 'sourceNumerics', targetKey: 'targetNumerics' },
21
+ textSemantics: { field: 'textSemanticsConstraints', sourceKey: 'sourceTexts', targetKey: 'targetTexts' },
22
+ collectionSemantics: { field: 'collectionSemanticsConstraints', sourceKey: 'sourceCollections', targetKey: 'targetCollections' },
23
+ serializationSemantics: { field: 'serializationSemanticsConstraints', sourceKey: 'sourceSerializations', targetKey: 'targetSerializations' },
24
+ dependencySemantics: { field: 'dependencySemanticsConstraints', sourceKey: 'sourceDependencies', targetKey: 'targetDependencies' },
25
+ objectModel: { field: 'objectModelConstraints', sourceKey: 'sourceObjects', targetKey: 'targetObjects' },
26
+ protocol: { field: 'protocolConstraints', sourceKey: 'sourceProtocols', targetKey: 'targetProtocols' }
27
+ };
28
+
29
+ export function parseConversionBlock(block) {
30
+ const name = nameFrom(block.header);
31
+ const plan = { id: idFrom(block.header, `conversion_${name}`), targets: [], metadata: { name } };
32
+ for (const rawLine of block.body.split('\n')) {
33
+ const line = rawLine.trim();
34
+ if (!line || line.startsWith('#')) continue;
35
+ const target = /^target\s+([^\s,]+)/.exec(line)?.[1];
36
+ const sourceLanguage = /^sourceLanguage\s+([^\s,]+)/.exec(line)?.[1] ?? /^source\s+([^\s,]+)/.exec(line)?.[1];
37
+ const constraint = /^constraint\s+([A-Za-z_$][\w$-]*)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
38
+ if (target) plan.targets.push(target);
39
+ else if (sourceLanguage) plan.sourceLanguage = sourceLanguage;
40
+ else if (constraint) addConstraint(plan, constraint[1], constraint[2], constraint[3]);
41
+ }
42
+ return cleanRecord({ ...plan, targets: unique(plan.targets) });
43
+ }
44
+
45
+ function addConstraint(plan, family, name, text) {
46
+ const config = FAMILIES[family] ?? { field: family.endsWith('s') ? family : `${family}Constraints`, sourceKey: 'sourceRecords', targetKey: 'targetRecords' };
47
+ const role = readInlineWord('role', text) ?? 'source';
48
+ const record = parseConstraintRecord(name, text, role);
49
+ const entry = cleanRecord({
50
+ id: idFrom(text, `${config.field}_${name}`),
51
+ sourceLanguage: readInlineWord('sourceLanguage', text) ?? plan.sourceLanguage,
52
+ target: readInlineWord('target', text) ?? plan.targets[0],
53
+ mode: readInlineWord('mode', text),
54
+ evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
55
+ missingEvidence: readInlineList(text, 'missingEvidence'),
56
+ blockers: readInlineList(text, 'blocker', 'blockers'),
57
+ review: readInlineList(text, 'review'),
58
+ metadata: { name, family, authoredConversionBlockId: plan.id }
59
+ });
60
+ const recordKey = role === 'target' ? config.targetKey : config.sourceKey;
61
+ entry[recordKey] = [record];
62
+ plan[config.field] = [...(plan[config.field] ?? []), entry];
63
+ }
64
+
65
+ function parseConstraintRecord(name, text, role) {
66
+ const kind = readInlineWord('kind', text) ?? readInlineWord('constraintKind', text);
67
+ return cleanRecord({
68
+ id: readInlineWord('recordId', text) ?? idFrom(text, `constraint_record_${name}`),
69
+ role,
70
+ kind,
71
+ name: readInlineWord('name', text) ?? name,
72
+ constraintKind: readInlineWord('constraintKind', text),
73
+ constraintKinds: readInlineList(text, 'constraint', 'constraints', 'constraintKind', 'constraintKinds') ?? (kind ? [kind] : undefined),
74
+ factKinds: readInlineList(text, 'fact', 'facts', 'factKind', 'factKinds'),
75
+ symbolId: readInlineWord('symbol', text) ?? readInlineWord('symbolId', text),
76
+ symbolName: readInlineWord('symbolName', text),
77
+ localName: readInlineWord('localName', text),
78
+ typeKind: readInlineWord('typeKind', text),
79
+ signatureHash: readInlineWord('signatureHash', text),
80
+ contractHash: readInlineWord('contractHash', text),
81
+ typeHash: readInlineWord('typeHash', text),
82
+ flowKind: readInlineWord('flowKind', text),
83
+ sourceId: readInlineWord('from', text) ?? readInlineWord('sourceId', text),
84
+ targetId: readInlineWord('to', text) ?? readInlineWord('targetId', text),
85
+ label: readInlineWord('label', text),
86
+ conditionHash: readInlineWord('conditionHash', text),
87
+ orderingKey: readInlineWord('orderingKey', text) ?? readInlineWord('orderKey', text),
88
+ lifetimeKind: readInlineWord('lifetimeKind', text),
89
+ regionKind: readInlineWord('regionKind', text),
90
+ predicate: readInlineQuoted('predicate', text) ?? readInlineWord('predicate', text),
91
+ resourceId: readInlineWord('resource', text) ?? readInlineWord('resourceId', text),
92
+ sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text),
93
+ sourceHash: readInlineWord('sourceHash', text),
94
+ evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
95
+ nullable: readInlineFlag('nullable', text),
96
+ optional: readInlineFlag('optional', text),
97
+ publicContract: readInlineFlag('publicContract', text),
98
+ async: readInlineFlag('async', text),
99
+ generator: readInlineFlag('generator', text),
100
+ exceptional: readInlineFlag('exceptional', text),
101
+ cancellable: readInlineFlag('cancellable', text),
102
+ metadata: { name }
103
+ });
104
+ }
105
+
106
+ function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
107
+ function nameFrom(header) { return /^([A-Za-z_$][\w$]*)/.exec(header)?.[1] ?? 'Conversion'; }
108
+ function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
109
+ function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
110
+ function readInlineFlag(label, text) { return new RegExp('(?:^|\\s)' + label + '(?:\\s|$)').test(text) || undefined; }
111
+ function readInlineList(text, ...labels) {
112
+ for (const label of labels) {
113
+ const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
114
+ if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
115
+ }
116
+ return undefined;
117
+ }
118
+ function cleanRecord(record) {
119
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0)));
120
+ }
121
+ function unique(values) { return [...new Set(values.filter(Boolean))]; }
package/dist/index.js CHANGED
@@ -1,17 +1,5 @@
1
- import {
2
- actionNode,
3
- capabilityNode,
4
- createDocument,
5
- effectNode,
6
- entityNode,
7
- externNode,
8
- latticeNode,
9
- migrationNode,
10
- nativeSourceNode,
11
- stateNode,
12
- targetNode,
13
- typeNode
14
- } from '@shapeshift-labs/frontier-lang-kernel';
1
+ import { actionNode, capabilityNode, createDocument, effectNode, entityNode, externNode, latticeNode, migrationNode, nativeSourceNode, stateNode, targetNode, typeNode } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { parseConversionBlock } from './conversion.js';
15
3
  import { createParsedMetadata } from './metadata.js';
16
4
  import { parseSemanticOperationsBlock } from './operations.js';
17
5
  import { parseParadigmBlock } from './paradigm.js';
@@ -23,6 +11,7 @@ export function parseFrontierSource(source, options = {}) {
23
11
  const proofBlocks = [];
24
12
  const paradigmBlocks = [];
25
13
  const operationBlocks = [];
14
+ const conversionBlocks = [];
26
15
  const documentId = options.id ?? readId(source) ?? 'mod_frontier';
27
16
  const documentName = options.name ?? readName(source) ?? 'FrontierModule';
28
17
  for (const block of readBlocks(source)) {
@@ -41,8 +30,9 @@ export function parseFrontierSource(source, options = {}) {
41
30
  if (block.kind === 'proof') proofBlocks.push(parseProofBlock(block));
42
31
  if (block.kind === 'paradigm' || block.kind === 'paradigmSemantics') paradigmBlocks.push(parseParadigmBlock(block));
43
32
  if (block.kind === 'operations' || block.kind === 'semanticOperations') operationBlocks.push(parseSemanticOperationsBlock(block));
33
+ if (block.kind === 'conversion' || block.kind === 'universalConversionPlan') conversionBlocks.push(parseConversionBlock(block));
44
34
  }
45
- const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks });
35
+ const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks });
46
36
  return createDocument({ id: documentId, name: documentName, nodes, ...(metadata ? { metadata } : {}) });
47
37
  }
48
38
 
@@ -52,7 +42,7 @@ function readName(source) { return /module\s+([A-Za-z_$][\w$]*)/.exec(source)?.[
52
42
  function readId(source) { return /module\s+[A-Za-z_$][\w$]*\s+@id\(\s*["']([^"']+)["']\s*\)/.exec(source)?.[1]; }
53
43
  function readBlocks(source) {
54
44
  const blocks = [];
55
- const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof|paradigm|paradigmSemantics|operations|semanticOperations)\s+([^{}]+)\{/g;
45
+ const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof|paradigm|paradigmSemantics|operations|semanticOperations|conversion|universalConversionPlan)\s+([^{}]+)\{/g;
56
46
  let match;
57
47
  while ((match = header.exec(source))) {
58
48
  let depth = 1; let index = header.lastIndex;
package/dist/metadata.js CHANGED
@@ -21,11 +21,12 @@ const PARADIGM_GROUPS = [
21
21
  'loweringRecords'
22
22
  ];
23
23
 
24
- export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [] } = {}) {
24
+ export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], conversionBlocks = [] } = {}) {
25
25
  const metadata = {};
26
26
  if (proofBlocks.length) metadata.proof = mergeProofBlocks(proofBlocks);
27
27
  if (paradigmBlocks.length) metadata.paradigmSemantics = mergeParadigmBlocks(paradigmBlocks);
28
28
  if (operationBlocks.length) metadata.semanticOperations = mergeOperationBlocks(operationBlocks);
29
+ if (conversionBlocks.length) metadata.universalConversionPlan = mergeConversionBlocks(conversionBlocks);
29
30
  return Object.keys(metadata).length ? metadata : undefined;
30
31
  }
31
32
 
@@ -54,3 +55,18 @@ function mergeOperationBlocks(blocks) {
54
55
  metadata: { authoredSemanticOperationBlockIds: blocks.map((block) => block.id) }
55
56
  };
56
57
  }
58
+
59
+ function mergeConversionBlocks(blocks) {
60
+ const plan = {
61
+ id: blocks.length === 1 ? blocks[0].id : 'universalConversionPlan:source',
62
+ targets: [...new Set(blocks.flatMap((block) => block.targets ?? []))],
63
+ metadata: { authoredConversionBlockIds: blocks.map((block) => block.id) }
64
+ };
65
+ for (const block of blocks) {
66
+ if (block.sourceLanguage && !plan.sourceLanguage) plan.sourceLanguage = block.sourceLanguage;
67
+ for (const [key, value] of Object.entries(block)) {
68
+ if (Array.isArray(value) && key !== 'targets') plan[key] = [...(plan[key] ?? []), ...value];
69
+ }
70
+ }
71
+ return plan;
72
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-parser",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "Parser for the first Frontier Lang .frontier syntax slice.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",