@shapeshift-labs/frontier-lang-parser 0.3.9 → 0.3.11
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/conversion.js +121 -0
- package/dist/index.js +14 -25
- package/dist/metadata.js +72 -0
- package/dist/operations.js +59 -0
- package/dist/paradigm.js +93 -0
- package/package.json +1 -1
|
@@ -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) ?? readInlineWord('source', 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,23 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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';
|
|
3
|
+
import { createParsedMetadata } from './metadata.js';
|
|
4
|
+
import { parseSemanticOperationsBlock } from './operations.js';
|
|
5
|
+
import { parseParadigmBlock } from './paradigm.js';
|
|
15
6
|
import { parseProofBlock } from './proof.js';
|
|
16
7
|
import { parseViewBlock } from './view.js';
|
|
17
8
|
|
|
18
9
|
export function parseFrontierSource(source, options = {}) {
|
|
19
10
|
const nodes = [];
|
|
20
11
|
const proofBlocks = [];
|
|
12
|
+
const paradigmBlocks = [];
|
|
13
|
+
const operationBlocks = [];
|
|
14
|
+
const conversionBlocks = [];
|
|
21
15
|
const documentId = options.id ?? readId(source) ?? 'mod_frontier';
|
|
22
16
|
const documentName = options.name ?? readName(source) ?? 'FrontierModule';
|
|
23
17
|
for (const block of readBlocks(source)) {
|
|
@@ -34,8 +28,12 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
34
28
|
if (block.kind === 'nativeSource') nodes.push(parseNativeSource(block));
|
|
35
29
|
if (block.kind === 'target') nodes.push(parseTarget(block));
|
|
36
30
|
if (block.kind === 'proof') proofBlocks.push(parseProofBlock(block));
|
|
31
|
+
if (block.kind === 'paradigm' || block.kind === 'paradigmSemantics') paradigmBlocks.push(parseParadigmBlock(block));
|
|
32
|
+
if (block.kind === 'operations' || block.kind === 'semanticOperations') operationBlocks.push(parseSemanticOperationsBlock(block));
|
|
33
|
+
if (block.kind === 'conversion' || block.kind === 'universalConversionPlan') conversionBlocks.push(parseConversionBlock(block));
|
|
37
34
|
}
|
|
38
|
-
|
|
35
|
+
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks });
|
|
36
|
+
return createDocument({ id: documentId, name: documentName, nodes, ...(metadata ? { metadata } : {}) });
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
export function parseFrontierFile(name, source) { return parseFrontierSource(source, { name: name.replace(/\.frontier$/, '') }); }
|
|
@@ -44,7 +42,7 @@ function readName(source) { return /module\s+([A-Za-z_$][\w$]*)/.exec(source)?.[
|
|
|
44
42
|
function readId(source) { return /module\s+[A-Za-z_$][\w$]*\s+@id\(\s*["']([^"']+)["']\s*\)/.exec(source)?.[1]; }
|
|
45
43
|
function readBlocks(source) {
|
|
46
44
|
const blocks = [];
|
|
47
|
-
const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof)\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;
|
|
48
46
|
let match;
|
|
49
47
|
while ((match = header.exec(source))) {
|
|
50
48
|
let depth = 1; let index = header.lastIndex;
|
|
@@ -54,15 +52,6 @@ function readBlocks(source) {
|
|
|
54
52
|
}
|
|
55
53
|
return blocks;
|
|
56
54
|
}
|
|
57
|
-
const PROOF_GROUPS = ['contracts', 'refinements', 'invariants', 'termination', 'temporal', 'obligations', 'artifacts', 'assumptions'];
|
|
58
|
-
function mergeProofBlocks(blocks) {
|
|
59
|
-
const proof = {
|
|
60
|
-
id: blocks.length === 1 ? blocks[0].id : 'proof:source',
|
|
61
|
-
metadata: { authoredProofBlockIds: blocks.map((block) => block.id) }
|
|
62
|
-
};
|
|
63
|
-
for (const group of PROOF_GROUPS) proof[group] = blocks.flatMap((block) => block[group] ?? []);
|
|
64
|
-
return proof;
|
|
65
|
-
}
|
|
66
55
|
function idFrom(header, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(header)?.[1] ?? fallback; }
|
|
67
56
|
function nameFrom(header) { return /^([A-Za-z_$][\w$]*)/.exec(header)?.[1] ?? 'Unnamed'; }
|
|
68
57
|
function parseEntity(block) {
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const PROOF_GROUPS = ['contracts', 'refinements', 'invariants', 'termination', 'temporal', 'obligations', 'artifacts', 'assumptions'];
|
|
2
|
+
const PARADIGM_GROUPS = [
|
|
3
|
+
'bindingScopes',
|
|
4
|
+
'bindings',
|
|
5
|
+
'patterns',
|
|
6
|
+
'typeConstraints',
|
|
7
|
+
'evaluationModels',
|
|
8
|
+
'memoryLocations',
|
|
9
|
+
'effectRegions',
|
|
10
|
+
'controlRegions',
|
|
11
|
+
'logicPrograms',
|
|
12
|
+
'actorSystems',
|
|
13
|
+
'stackEffects',
|
|
14
|
+
'arrayShapes',
|
|
15
|
+
'numericKernels',
|
|
16
|
+
'dataflowNetworks',
|
|
17
|
+
'clockModels',
|
|
18
|
+
'objectModels',
|
|
19
|
+
'macroExpansions',
|
|
20
|
+
'reflectionBoundaries',
|
|
21
|
+
'loweringRecords'
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], conversionBlocks = [] } = {}) {
|
|
25
|
+
const metadata = {};
|
|
26
|
+
if (proofBlocks.length) metadata.proof = mergeProofBlocks(proofBlocks);
|
|
27
|
+
if (paradigmBlocks.length) metadata.paradigmSemantics = mergeParadigmBlocks(paradigmBlocks);
|
|
28
|
+
if (operationBlocks.length) metadata.semanticOperations = mergeOperationBlocks(operationBlocks);
|
|
29
|
+
if (conversionBlocks.length) metadata.universalConversionPlan = mergeConversionBlocks(conversionBlocks);
|
|
30
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function mergeProofBlocks(blocks) {
|
|
34
|
+
const proof = {
|
|
35
|
+
id: blocks.length === 1 ? blocks[0].id : 'proof:source',
|
|
36
|
+
metadata: { authoredProofBlockIds: blocks.map((block) => block.id) }
|
|
37
|
+
};
|
|
38
|
+
for (const group of PROOF_GROUPS) proof[group] = blocks.flatMap((block) => block[group] ?? []);
|
|
39
|
+
return proof;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function mergeParadigmBlocks(blocks) {
|
|
43
|
+
const paradigm = {
|
|
44
|
+
id: blocks.length === 1 ? blocks[0].id : 'paradigm:source',
|
|
45
|
+
metadata: { authoredParadigmBlockIds: blocks.map((block) => block.id) }
|
|
46
|
+
};
|
|
47
|
+
for (const group of PARADIGM_GROUPS) paradigm[group] = blocks.flatMap((block) => block[group] ?? []);
|
|
48
|
+
return paradigm;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function mergeOperationBlocks(blocks) {
|
|
52
|
+
return {
|
|
53
|
+
id: blocks.length === 1 ? blocks[0].id : 'semanticOperations:source',
|
|
54
|
+
operations: blocks.flatMap((block) => block.operations ?? []),
|
|
55
|
+
metadata: { authoredSemanticOperationBlockIds: blocks.map((block) => block.id) }
|
|
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
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export function parseSemanticOperationsBlock(block) {
|
|
2
|
+
const name = nameFrom(block.header);
|
|
3
|
+
const operationSet = { id: idFrom(block.header, `semanticOperations_${name}`), operations: [], metadata: { name } };
|
|
4
|
+
for (const rawLine of block.body.split('\n')) {
|
|
5
|
+
const line = rawLine.trim();
|
|
6
|
+
if (!line || line.startsWith('#')) continue;
|
|
7
|
+
const match = /^(operation|op)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
8
|
+
if (match) operationSet.operations.push(parseSemanticOperationRecord(match[2], match[3]));
|
|
9
|
+
}
|
|
10
|
+
return operationSet;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function parseSemanticOperationRecord(name, text) {
|
|
14
|
+
return cleanRecord({
|
|
15
|
+
id: idFrom(text, `semanticOperation_${name}`),
|
|
16
|
+
operationKind: readInlineWord('operationKind', text) ?? readInlineWord('op', text) ?? readInlineWord('kind', text),
|
|
17
|
+
language: readInlineWord('language', text),
|
|
18
|
+
name,
|
|
19
|
+
target: readInlineWord('target', text),
|
|
20
|
+
nativeSourceId: readInlineWord('nativeSource', text) ?? readInlineWord('nativeSourceId', text),
|
|
21
|
+
nativeAstId: readInlineWord('nativeAst', text) ?? readInlineWord('nativeAstId', text),
|
|
22
|
+
nativeAstNodeIds: readInlineList(text, 'nativeAstNode', 'nativeAstNodes', 'nativeAstNodeIds'),
|
|
23
|
+
semanticNodeIds: readInlineList(text, 'semanticNode', 'semanticNodes', 'semanticNodeIds'),
|
|
24
|
+
semanticSymbolIds: readInlineList(text, 'semanticSymbol', 'semanticSymbols', 'semanticSymbolIds'),
|
|
25
|
+
semanticOccurrenceIds: readInlineList(text, 'semanticOccurrence', 'semanticOccurrences', 'semanticOccurrenceIds'),
|
|
26
|
+
sourceMapIds: readInlineList(text, 'sourceMap', 'sourceMaps', 'sourceMapIds'),
|
|
27
|
+
sourceMapMappingIds: readInlineList(text, 'sourceMapMapping', 'sourceMapMappings', 'sourceMapMappingIds'),
|
|
28
|
+
proofObligationIds: readInlineList(text, 'proofObligation', 'proofObligations', 'proofObligationIds'),
|
|
29
|
+
proofArtifactIds: readInlineList(text, 'proofArtifact', 'proofArtifacts', 'proofArtifactIds'),
|
|
30
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
31
|
+
lossIds: readInlineList(text, 'loss', 'lossIds'),
|
|
32
|
+
reads: readInlineList(text, 'read', 'reads'),
|
|
33
|
+
writes: readInlineList(text, 'write', 'writes'),
|
|
34
|
+
effectIds: readInlineList(text, 'effect', 'effects', 'effectIds'),
|
|
35
|
+
resources: readInlineList(text, 'resource', 'resources'),
|
|
36
|
+
ownershipKeys: readInlineList(text, 'ownerKey', 'ownershipKey', 'ownershipKeys'),
|
|
37
|
+
conflictKeys: readInlineList(text, 'conflictKey', 'conflictKeys'),
|
|
38
|
+
readiness: readInlineWord('readiness', text),
|
|
39
|
+
dynamic: readInlineFlag('dynamic', text),
|
|
40
|
+
opaque: readInlineFlag('opaque', text),
|
|
41
|
+
summary: readInlineQuoted('summary', text)
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
46
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$]*)/.exec(header)?.[1] ?? 'SemanticOperations'; }
|
|
47
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
48
|
+
function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
|
|
49
|
+
function readInlineFlag(label, text) { return new RegExp('(?:^|\\s)' + label + '(?:\\s|$)').test(text) || undefined; }
|
|
50
|
+
function readInlineList(text, ...labels) {
|
|
51
|
+
for (const label of labels) {
|
|
52
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
|
|
53
|
+
if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
function cleanRecord(record) {
|
|
58
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0)));
|
|
59
|
+
}
|
package/dist/paradigm.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const GROUPS = {
|
|
2
|
+
bindingScope: 'bindingScopes',
|
|
3
|
+
binding: 'bindings',
|
|
4
|
+
pattern: 'patterns',
|
|
5
|
+
typeConstraint: 'typeConstraints',
|
|
6
|
+
evaluationModel: 'evaluationModels',
|
|
7
|
+
memoryLocation: 'memoryLocations',
|
|
8
|
+
effectRegion: 'effectRegions',
|
|
9
|
+
controlRegion: 'controlRegions',
|
|
10
|
+
logicProgram: 'logicPrograms',
|
|
11
|
+
actorSystem: 'actorSystems',
|
|
12
|
+
stackEffect: 'stackEffects',
|
|
13
|
+
arrayShape: 'arrayShapes',
|
|
14
|
+
numericKernel: 'numericKernels',
|
|
15
|
+
dataflowNetwork: 'dataflowNetworks',
|
|
16
|
+
clockModel: 'clockModels',
|
|
17
|
+
objectModel: 'objectModels',
|
|
18
|
+
macroExpansion: 'macroExpansions',
|
|
19
|
+
reflectionBoundary: 'reflectionBoundaries',
|
|
20
|
+
lowering: 'loweringRecords',
|
|
21
|
+
loweringRecord: 'loweringRecords'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function parseParadigmBlock(block) {
|
|
25
|
+
const name = nameFrom(block.header);
|
|
26
|
+
const layer = { id: idFrom(block.header, `paradigm_${name}`), metadata: { name } };
|
|
27
|
+
for (const group of new Set(Object.values(GROUPS))) layer[group] = [];
|
|
28
|
+
for (const rawLine of block.body.split('\n')) {
|
|
29
|
+
const line = rawLine.trim();
|
|
30
|
+
if (!line || line.startsWith('#')) continue;
|
|
31
|
+
const match = /^([A-Za-z_$][\w$-]*)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
32
|
+
if (!match) continue;
|
|
33
|
+
const [, section, recordName, rest] = match;
|
|
34
|
+
const group = GROUPS[section];
|
|
35
|
+
if (group) layer[group].push(parseParadigmRecord(section, recordName, rest));
|
|
36
|
+
}
|
|
37
|
+
return omitEmptyArrays(layer);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseParadigmRecord(section, name, text) {
|
|
41
|
+
return cleanRecord({
|
|
42
|
+
id: idFrom(text, `paradigm_${section}_${name}`),
|
|
43
|
+
kind: readInlineWord('kind', text) ?? section,
|
|
44
|
+
subjectKind: readInlineWord('subjectKind', text),
|
|
45
|
+
subjectId: readInlineWord('subject', text) ?? readInlineWord('subjectId', text),
|
|
46
|
+
semanticNodeId: readInlineWord('semanticNode', text) ?? readInlineWord('semanticNodeId', text),
|
|
47
|
+
semanticSymbolId: readInlineWord('semanticSymbol', text) ?? readInlineWord('semanticSymbolId', text),
|
|
48
|
+
semanticOccurrenceId: readInlineWord('semanticOccurrence', text) ?? readInlineWord('semanticOccurrenceId', text),
|
|
49
|
+
nativeSourceId: readInlineWord('nativeSource', text) ?? readInlineWord('nativeSourceId', text),
|
|
50
|
+
nativeAstId: readInlineWord('nativeAst', text) ?? readInlineWord('nativeAstId', text),
|
|
51
|
+
nativeAstNodeId: readInlineWord('nativeAstNode', text) ?? readInlineWord('nativeAstNodeId', text),
|
|
52
|
+
sourceMapId: readInlineWord('sourceMap', text) ?? readInlineWord('sourceMapId', text),
|
|
53
|
+
sourceMapMappingId: readInlineWord('sourceMapMapping', text) ?? readInlineWord('sourceMapMappingId', text),
|
|
54
|
+
bindingScopeId: readInlineWord('bindingScope', text) ?? readInlineWord('bindingScopeId', text),
|
|
55
|
+
parentScopeId: readInlineWord('parentScope', text) ?? readInlineWord('parentScopeId', text),
|
|
56
|
+
bindingId: readInlineWord('binding', text) ?? readInlineWord('bindingId', text),
|
|
57
|
+
patternId: readInlineWord('pattern', text) ?? readInlineWord('patternId', text),
|
|
58
|
+
typeConstraintId: readInlineWord('typeConstraint', text) ?? readInlineWord('typeConstraintId', text),
|
|
59
|
+
evaluationModelId: readInlineWord('evaluationModel', text) ?? readInlineWord('evaluationModelId', text),
|
|
60
|
+
memoryLocationId: readInlineWord('memoryLocation', text) ?? readInlineWord('memoryLocationId', text),
|
|
61
|
+
effectRegionId: readInlineWord('effectRegion', text) ?? readInlineWord('effectRegionId', text),
|
|
62
|
+
controlRegionId: readInlineWord('controlRegion', text) ?? readInlineWord('controlRegionId', text),
|
|
63
|
+
loweringRecordId: readInlineWord('lowering', text) ?? readInlineWord('loweringRecordId', text),
|
|
64
|
+
sourceRecordId: readInlineWord('sourceRecord', text) ?? readInlineWord('sourceRecordId', text),
|
|
65
|
+
targetRecordId: readInlineWord('targetRecord', text) ?? readInlineWord('targetRecordId', text),
|
|
66
|
+
effectIds: readInlineList(text, 'effect', 'effects', 'effectIds'),
|
|
67
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
68
|
+
lossIds: readInlineList(text, 'loss', 'lossIds'),
|
|
69
|
+
relatedRecordIds: readInlineList(text, 'related', 'relatedRecordIds'),
|
|
70
|
+
statement: readInlineQuoted('statement', text),
|
|
71
|
+
description: readInlineQuoted('description', text),
|
|
72
|
+
language: readInlineWord('language', text),
|
|
73
|
+
metadata: { name }
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
78
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$]*)/.exec(header)?.[1] ?? 'Paradigm'; }
|
|
79
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
80
|
+
function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
|
|
81
|
+
function readInlineList(text, ...labels) {
|
|
82
|
+
for (const label of labels) {
|
|
83
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
|
|
84
|
+
if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
function cleanRecord(record) {
|
|
89
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0)));
|
|
90
|
+
}
|
|
91
|
+
function omitEmptyArrays(record) {
|
|
92
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => !Array.isArray(value) || value.length > 0));
|
|
93
|
+
}
|