@shapeshift-labs/frontier-lang-parser 0.3.9 → 0.3.10
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 +10 -11
- package/dist/metadata.js +56 -0
- package/dist/operations.js +59 -0
- package/dist/paradigm.js +93 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,12 +12,17 @@ import {
|
|
|
12
12
|
targetNode,
|
|
13
13
|
typeNode
|
|
14
14
|
} from '@shapeshift-labs/frontier-lang-kernel';
|
|
15
|
+
import { createParsedMetadata } from './metadata.js';
|
|
16
|
+
import { parseSemanticOperationsBlock } from './operations.js';
|
|
17
|
+
import { parseParadigmBlock } from './paradigm.js';
|
|
15
18
|
import { parseProofBlock } from './proof.js';
|
|
16
19
|
import { parseViewBlock } from './view.js';
|
|
17
20
|
|
|
18
21
|
export function parseFrontierSource(source, options = {}) {
|
|
19
22
|
const nodes = [];
|
|
20
23
|
const proofBlocks = [];
|
|
24
|
+
const paradigmBlocks = [];
|
|
25
|
+
const operationBlocks = [];
|
|
21
26
|
const documentId = options.id ?? readId(source) ?? 'mod_frontier';
|
|
22
27
|
const documentName = options.name ?? readName(source) ?? 'FrontierModule';
|
|
23
28
|
for (const block of readBlocks(source)) {
|
|
@@ -34,8 +39,11 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
34
39
|
if (block.kind === 'nativeSource') nodes.push(parseNativeSource(block));
|
|
35
40
|
if (block.kind === 'target') nodes.push(parseTarget(block));
|
|
36
41
|
if (block.kind === 'proof') proofBlocks.push(parseProofBlock(block));
|
|
42
|
+
if (block.kind === 'paradigm' || block.kind === 'paradigmSemantics') paradigmBlocks.push(parseParadigmBlock(block));
|
|
43
|
+
if (block.kind === 'operations' || block.kind === 'semanticOperations') operationBlocks.push(parseSemanticOperationsBlock(block));
|
|
37
44
|
}
|
|
38
|
-
|
|
45
|
+
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks });
|
|
46
|
+
return createDocument({ id: documentId, name: documentName, nodes, ...(metadata ? { metadata } : {}) });
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
export function parseFrontierFile(name, source) { return parseFrontierSource(source, { name: name.replace(/\.frontier$/, '') }); }
|
|
@@ -44,7 +52,7 @@ function readName(source) { return /module\s+([A-Za-z_$][\w$]*)/.exec(source)?.[
|
|
|
44
52
|
function readId(source) { return /module\s+[A-Za-z_$][\w$]*\s+@id\(\s*["']([^"']+)["']\s*\)/.exec(source)?.[1]; }
|
|
45
53
|
function readBlocks(source) {
|
|
46
54
|
const blocks = [];
|
|
47
|
-
const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof)\s+([^{}]+)\{/g;
|
|
55
|
+
const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof|paradigm|paradigmSemantics|operations|semanticOperations)\s+([^{}]+)\{/g;
|
|
48
56
|
let match;
|
|
49
57
|
while ((match = header.exec(source))) {
|
|
50
58
|
let depth = 1; let index = header.lastIndex;
|
|
@@ -54,15 +62,6 @@ function readBlocks(source) {
|
|
|
54
62
|
}
|
|
55
63
|
return blocks;
|
|
56
64
|
}
|
|
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
65
|
function idFrom(header, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(header)?.[1] ?? fallback; }
|
|
67
66
|
function nameFrom(header) { return /^([A-Za-z_$][\w$]*)/.exec(header)?.[1] ?? 'Unnamed'; }
|
|
68
67
|
function parseEntity(block) {
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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 = [] } = {}) {
|
|
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
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mergeProofBlocks(blocks) {
|
|
33
|
+
const proof = {
|
|
34
|
+
id: blocks.length === 1 ? blocks[0].id : 'proof:source',
|
|
35
|
+
metadata: { authoredProofBlockIds: blocks.map((block) => block.id) }
|
|
36
|
+
};
|
|
37
|
+
for (const group of PROOF_GROUPS) proof[group] = blocks.flatMap((block) => block[group] ?? []);
|
|
38
|
+
return proof;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function mergeParadigmBlocks(blocks) {
|
|
42
|
+
const paradigm = {
|
|
43
|
+
id: blocks.length === 1 ? blocks[0].id : 'paradigm:source',
|
|
44
|
+
metadata: { authoredParadigmBlockIds: blocks.map((block) => block.id) }
|
|
45
|
+
};
|
|
46
|
+
for (const group of PARADIGM_GROUPS) paradigm[group] = blocks.flatMap((block) => block[group] ?? []);
|
|
47
|
+
return paradigm;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mergeOperationBlocks(blocks) {
|
|
51
|
+
return {
|
|
52
|
+
id: blocks.length === 1 ? blocks[0].id : 'semanticOperations:source',
|
|
53
|
+
operations: blocks.flatMap((block) => block.operations ?? []),
|
|
54
|
+
metadata: { authoredSemanticOperationBlockIds: blocks.map((block) => block.id) }
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -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
|
+
}
|