@shapeshift-labs/frontier-lang-parser 0.3.26 → 0.3.28
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/README.md +58 -1
- package/dist/canvas-surface.js +206 -0
- package/dist/conversion-constraint-record.js +65 -4
- package/dist/index.js +8 -2
- package/dist/metadata.js +71 -5
- package/dist/package-manifest.js +162 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -260,6 +260,55 @@ and events stay attached to the child render node instead of leaking onto the
|
|
|
260
260
|
parent. `kind component` records `component` instead of a literal HTML tag, so
|
|
261
261
|
target adapters can decide how to project it.
|
|
262
262
|
|
|
263
|
+
## Authored package and canvas syntax
|
|
264
|
+
|
|
265
|
+
`.frontier` files can describe package-management and canvas semantic surfaces
|
|
266
|
+
directly, without requiring the source of truth to be a generated JSON tree.
|
|
267
|
+
These blocks make dependency, script, export, draw-command, command-trace, and
|
|
268
|
+
proof-gap records queryable as source-level evidence. They intentionally do not
|
|
269
|
+
claim package install equivalence, canvas runtime equivalence, or visual
|
|
270
|
+
equivalence.
|
|
271
|
+
|
|
272
|
+
```frontier
|
|
273
|
+
packageManifest AppPackage @id("pkg_manifest_app") {
|
|
274
|
+
sourcePath package.json
|
|
275
|
+
sourceHash sha256:package
|
|
276
|
+
packageManager npm@11.0.0
|
|
277
|
+
evidence packageProbe @id("evidence_package_probe") kind test status passed path reports/package.json
|
|
278
|
+
metadata name @id("pkg_meta_name") value "@example/app" evidence evidence_package_probe
|
|
279
|
+
dependency react @id("pkg_dep_react") section dependencies range ^19.0.0 evidence evidence_package_probe
|
|
280
|
+
dependency typescript @id("pkg_dep_typescript") section peerDependencies range ^5.9.0 proofGap package-peer-compatibility-boundary evidence evidence_package_probe
|
|
281
|
+
script test @id("pkg_script_test") command "vitest --run" proofGap package-script-runtime-boundary evidence evidence_package_probe
|
|
282
|
+
export root @id("pkg_export_root") section exports name . target ./dist/index.js proofGap package-conditional-resolution-boundary evidence evidence_package_probe
|
|
283
|
+
gap workspace @id("pkg_gap_workspace") code package-workspace-graph-boundary summary "Workspace expansion requires repository graph evidence."
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
canvasSurface PreviewCanvas @id("canvas_surface_preview") {
|
|
287
|
+
sourcePath src/draw.js
|
|
288
|
+
sourceHash sha256:draw
|
|
289
|
+
evidence canvasProbe @id("evidence_canvas_probe") kind browser-probe status passed path reports/canvas.json
|
|
290
|
+
element preview @id("canvas_element_preview") name canvas category html-canvas order 1 identity canvas:preview attributes data-frontier-key=preview|width=100 evidence evidence_canvas_probe
|
|
291
|
+
command context @id("canvas_command_context") name getContext category context context 2d order 2 proofGap canvas-context-runtime-boundary evidence evidence_canvas_probe
|
|
292
|
+
state fillStyle @id("canvas_state_fill_style") name fillStyle category state order 3 proofGap canvas-stateful-render-order-boundary evidence evidence_canvas_probe
|
|
293
|
+
command fill @id("canvas_command_fill") name fillRect category draw context 2d order 4 proofGap canvas-stateful-render-order-boundary evidence evidence_canvas_probe
|
|
294
|
+
command offscreen @id("canvas_command_offscreen") name transferControlToOffscreen category offscreen order 5 proofGap canvas-offscreen-worker-boundary evidence evidence_canvas_probe
|
|
295
|
+
trace drawFrame @id("canvas_trace_draw_frame") commands getContext|fillStyle|fillRect|transferControlToOffscreen evidence evidence_canvas_probe
|
|
296
|
+
gap image @id("canvas_gap_image") code canvas-image-resource-boundary summary "Image drawing needs bitmap/resource evidence."
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The parser stores package blocks in `metadata.packageManifests` and canvas
|
|
301
|
+
blocks in `metadata.canvasSurfaces`, and mirrors both under
|
|
302
|
+
`metadata.universalAst` so compiler and merge tooling can see them next to
|
|
303
|
+
native-source source maps and merge candidates. Package records keep
|
|
304
|
+
`packageInstallEquivalenceClaim`, `installEquivalenceClaim`, and
|
|
305
|
+
`runtimeEquivalenceClaim` false. Canvas records keep
|
|
306
|
+
`browserRuntimeEquivalenceClaim`, `canvasRuntimeEquivalenceClaim`, and
|
|
307
|
+
`canvasVisualEquivalenceClaim` false.
|
|
308
|
+
Runtime probes, package-manager solver output, lockfile proof, browser pixels,
|
|
309
|
+
worker transfer evidence, and GPU validation must still be supplied by higher
|
|
310
|
+
layers before admission.
|
|
311
|
+
|
|
263
312
|
## Authored target projection syntax
|
|
264
313
|
|
|
265
314
|
`.frontier` target blocks can carry projection contracts next to their emit settings. These rows describe what a target lowering claims to represent, what it still needs proof for, and which losses or missing evidence must stay visible to merge and translation tooling.
|
|
@@ -368,12 +417,20 @@ conversion TodoJavascriptToRust @id("conversion_todo_js_rust") {
|
|
|
368
417
|
constraint memory-model todoMemory @id("memory_model_todo") role source kind stable-reference resource TodoDb.todos memoryKind shared-memory memoryOrder acquire lockId lock:todo shared evidence artifact_todo_title_probe
|
|
369
418
|
constraint effect-constraint todoWrite @id("effect_constraint_todo_write") role source kind storage-write capability storage.write resource TodoDb.todos adapterRequired evidence artifact_todo_title_probe
|
|
370
419
|
constraint host-environment browserFetch @id("host_environment_fetch") role source kind browser-api capability fetch apiName fetch globalName window permission network adapterRequired evidence artifact_todo_title_probe
|
|
420
|
+
constraint callable-boundary saveUser @id("callable_boundary_save_user") role source kind method-call callableKind function functionName saveUser parameterCount 2 parameterOrder user|options returnKind promise asyncKind async evidence artifact_todo_title_probe
|
|
421
|
+
constraint adt-pattern resultShape @id("adt_pattern_result") role source kind tagged-union adtKind union typeName UserResult variantNames Ok|Err payloadFieldNames value|error exhaustivenessKinds total evidence artifact_todo_title_probe
|
|
422
|
+
constraint numeric-semantics amountNumber @id("numeric_semantics_amount") role source kind integer numericKind int width 53 overflowMode safe-integer specialValues nan|infinity evidence artifact_todo_title_probe
|
|
423
|
+
constraint text-semantics titleText @id("text_semantics_title") role source kind string encoding utf-16 normalizationForm nfc boundaryKinds grapheme|word evidence artifact_todo_title_probe
|
|
424
|
+
constraint collection-semantics todoList @id("collection_semantics_todos") role source kind array collectionKind array elementKind Todo iterationOrder insertion duplicatePolicy allow evidence artifact_todo_title_probe
|
|
425
|
+
constraint serialization-semantics todoJson @id("serialization_semantics_todo_json") role source kind json format json codec JSON.stringify schemaName TodoPayload deterministic evidence artifact_todo_title_probe
|
|
426
|
+
constraint dependency-semantics npmReact @id("dependency_semantics_react") role source kind package packageManager npm packageName react versionRange ^19.0.0 lockfile package-lock.json integrity sha512-demo evidence artifact_todo_title_probe
|
|
427
|
+
constraint protocol serializable @id("protocol_serializable") role target kind trait-bound protocolKind trait traitName Serializable requirementNames serialize|deserialize evidence artifact_todo_title_probe
|
|
371
428
|
}
|
|
372
429
|
```
|
|
373
430
|
|
|
374
431
|
`sourceRuntime` and `targetRuntime` become runtime maps. `runtimeRequirement` rows become proof obligations for host/runtime capabilities, including authored `requiredSignals` denominators such as source hashes, target hashes, probe ids, runtime commands, telemetry hashes, and capability-specific trace hashes. `proofEvidence` and `evidence` attach evidence ids, but the compiler still requires bound evidence records before a proof obligation is satisfied. `dialect` and `extern` rows preserve dialect-specific constructs, projection readiness, loss/evidence ids, and binding metadata without requiring the authored Frontier file to drop down to raw JSON.
|
|
375
432
|
|
|
376
|
-
`constraint` rows accept every universal conversion constraint family used by route admission, including hyphenated spellings such as `module-constraint`, `scope-binding`, `memory-model`, `effect-constraint`, `control-flow`, `borrow-scope`, `borrow-checker`, `host-environment`, `data-layout`,
|
|
433
|
+
`constraint` rows accept every universal conversion constraint family used by route admission, including hyphenated spellings such as `module-constraint`, `scope-binding`, `memory-model`, `effect-constraint`, `control-flow`, `borrow-scope`, `borrow-checker`, `host-environment`, `callable-boundary`, `adt-pattern`, `data-layout`, `numeric-semantics`, `text-semantics`, `collection-semantics`, `serialization-semantics`, `dependency-semantics`, `object-model`, and `protocol`. The parser preserves family-specific fields such as module specifiers, package conditions, binding/reference ids, memory ordering, locks, capabilities, host permissions, callable signatures, pattern exhaustiveness, ABI/layout hints, numeric/text/collection behavior, wire formats, dependency lockfile evidence, and effect adapters as authored evidence inputs. Record-level targets use explicit labels such as `effectTarget` so `role target` rows cannot be mistaken for an authored target field. These rows do not prove translation equivalence; they make the required proof surface explicit for downstream gates and admission records.
|
|
377
434
|
|
|
378
435
|
## Authored dialect registry syntax
|
|
379
436
|
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
|
|
3
|
+
export function parseCanvasSurfaceBlock(block) {
|
|
4
|
+
const name = nameFrom(block.header);
|
|
5
|
+
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body);
|
|
6
|
+
const sourceHash = readLine('sourceHash', block.body);
|
|
7
|
+
const evidence = parseEvidenceRows(block.body);
|
|
8
|
+
const records = [];
|
|
9
|
+
const commandTraces = [];
|
|
10
|
+
const proofGaps = [];
|
|
11
|
+
for (const rawLine of block.body.split('\n')) {
|
|
12
|
+
const line = rawLine.trim();
|
|
13
|
+
if (!line || line.startsWith('#') || isPropertyLine(line)) continue;
|
|
14
|
+
const match = /^(element|command|state|stateWrite|trace|gap|proofGap)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
15
|
+
if (!match) continue;
|
|
16
|
+
const [, rowKind, rowName, rest] = match;
|
|
17
|
+
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
18
|
+
proofGaps.push(canvasProofGap(rowName, rest));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (rowKind === 'trace') {
|
|
22
|
+
commandTraces.push(canvasTrace(rowName, rest, { sourcePath }));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
records.push(canvasRecord(rowKind === 'stateWrite' ? 'state-write' : rowKind, rowName, rest, { sourcePath, sourceHash }));
|
|
26
|
+
}
|
|
27
|
+
const allGaps = [...records.flatMap((record) => record.proofGaps ?? []), ...proofGaps];
|
|
28
|
+
const tree = {
|
|
29
|
+
kind: 'frontier.lang.canvasSemanticTree',
|
|
30
|
+
version: 1,
|
|
31
|
+
id: idFrom(block.header, `canvas_surface_${name}`),
|
|
32
|
+
name,
|
|
33
|
+
sourcePath,
|
|
34
|
+
sourceHash,
|
|
35
|
+
records,
|
|
36
|
+
commandTraces,
|
|
37
|
+
proofGaps: allGaps,
|
|
38
|
+
evidence,
|
|
39
|
+
parser: { status: 'authored', errors: [] },
|
|
40
|
+
claims: {
|
|
41
|
+
autoMergeClaim: false,
|
|
42
|
+
semanticEquivalenceClaim: false,
|
|
43
|
+
browserRuntimeEquivalenceClaim: false,
|
|
44
|
+
canvasRuntimeEquivalenceClaim: false,
|
|
45
|
+
canvasVisualEquivalenceClaim: false
|
|
46
|
+
},
|
|
47
|
+
metadata: { authoredName: name }
|
|
48
|
+
};
|
|
49
|
+
tree.summary = summarizeCanvasSurface(tree);
|
|
50
|
+
tree.treeHash = hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTree.v1', records: records.map(hashableRecord), proofGaps: allGaps.map((gap) => gap.code), commandTraces: commandTraces.map((trace) => trace.traceHash) });
|
|
51
|
+
return tree;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function canvasRecord(kind, name, text, context) {
|
|
55
|
+
const category = readInlineWord('category', text) ?? defaultCategory(kind);
|
|
56
|
+
const recordName = readInlineWord('name', text) ?? name;
|
|
57
|
+
const order = readInlineNumber('order', text) ?? readInlineNumber('renderOrder', text) ?? 0;
|
|
58
|
+
const proofGaps = readInlineList(text, 'proofGap', 'proofGaps', 'gap', 'gaps')?.map((code) => canvasProofGap(code, '')) ?? [];
|
|
59
|
+
const textValue = readInlineQuoted('text', text) ?? readInlineWord('text', text) ?? `${kind}:${category}:${recordName}:${order}`;
|
|
60
|
+
return cleanRecord({
|
|
61
|
+
kind,
|
|
62
|
+
id: idFrom(text, `canvas_${kind.replace(/-/g, '_')}_${safeId(recordName)}`),
|
|
63
|
+
name: recordName,
|
|
64
|
+
category,
|
|
65
|
+
contextKind: readInlineWord('context', text) ?? readInlineWord('contextKind', text),
|
|
66
|
+
renderOrder: order,
|
|
67
|
+
identityKey: readInlineWord('identity', text) ?? readInlineWord('identityKey', text) ?? `canvas:${kind}:${category}:${recordName}:${order || safeId(recordName)}`,
|
|
68
|
+
sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text) ?? context.sourcePath,
|
|
69
|
+
sourceHash: readInlineWord('sourceHash', text) ?? context.sourceHash,
|
|
70
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text)),
|
|
71
|
+
textHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredRecordText.v1', text: textValue }),
|
|
72
|
+
attributes: parsePairs(readInlineWord('attributes', text) ?? readInlineWord('attrs', text) ?? readInlineWord('attr', text)),
|
|
73
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
74
|
+
proofGaps
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function canvasTrace(name, text, context) {
|
|
79
|
+
const commands = readInlineList(text, 'command', 'commands') ?? [];
|
|
80
|
+
const records = commands.map((command, index) => ({
|
|
81
|
+
kind: 'trace-command',
|
|
82
|
+
name: command,
|
|
83
|
+
category: readInlineWord('category', text) ?? 'custom',
|
|
84
|
+
ordinal: index + 1,
|
|
85
|
+
commandHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTraceCommand.v1', command, index }),
|
|
86
|
+
argsHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTraceArgs.v1', command, index }),
|
|
87
|
+
proofGaps: []
|
|
88
|
+
}));
|
|
89
|
+
return cleanRecord({
|
|
90
|
+
kind: 'frontier.lang.canvasCommandTrace',
|
|
91
|
+
version: 1,
|
|
92
|
+
id: idFrom(text, `canvas_trace_${name}`),
|
|
93
|
+
name,
|
|
94
|
+
sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text) ?? context.sourcePath,
|
|
95
|
+
traceHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTrace.v1', commands }),
|
|
96
|
+
records,
|
|
97
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
98
|
+
runtimeEquivalenceClaim: false,
|
|
99
|
+
visualEquivalenceClaim: false,
|
|
100
|
+
summary: {
|
|
101
|
+
commands: records.length,
|
|
102
|
+
drawCommands: records.filter((record) => ['draw', 'image', 'text', 'path'].includes(record.category)).length,
|
|
103
|
+
proofGaps: 0
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function canvasProofGap(name, text) {
|
|
109
|
+
const code = readInlineWord('code', text) ?? name;
|
|
110
|
+
return cleanRecord({
|
|
111
|
+
id: idFrom(text, `canvas_gap_${safeId(code)}`),
|
|
112
|
+
code,
|
|
113
|
+
status: readInlineWord('status', text) ?? 'not-claimed',
|
|
114
|
+
summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
|
|
115
|
+
failClosed: true,
|
|
116
|
+
semanticEquivalenceClaim: false,
|
|
117
|
+
browserRuntimeEquivalenceClaim: false,
|
|
118
|
+
canvasRuntimeEquivalenceClaim: false,
|
|
119
|
+
canvasVisualEquivalenceClaim: false,
|
|
120
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text))
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseEvidenceRows(body) {
|
|
125
|
+
const records = [];
|
|
126
|
+
for (const rawLine of body.split('\n')) {
|
|
127
|
+
const line = rawLine.trim();
|
|
128
|
+
const match = /^(?:evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
129
|
+
if (!match) continue;
|
|
130
|
+
records.push(cleanRecord({
|
|
131
|
+
id: idFrom(match[2], `evidence_${match[1]}`),
|
|
132
|
+
kind: readInlineWord('kind', match[2]) ?? 'note',
|
|
133
|
+
status: readInlineWord('status', match[2]) ?? 'unknown',
|
|
134
|
+
path: readInlineWord('path', match[2]),
|
|
135
|
+
summary: readInlineQuoted('summary', match[2]),
|
|
136
|
+
metadata: { name: match[1] }
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
return records;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function summarizeCanvasSurface(tree) {
|
|
143
|
+
return {
|
|
144
|
+
elements: tree.records.filter((record) => record.kind === 'element').length,
|
|
145
|
+
commands: tree.records.filter((record) => record.kind === 'command').length,
|
|
146
|
+
stateWrites: tree.records.filter((record) => record.kind === 'state-write').length,
|
|
147
|
+
drawCommands: tree.records.filter((record) => ['draw', 'image', 'text', 'path'].includes(record.category)).length,
|
|
148
|
+
offscreenCommands: tree.records.filter((record) => record.category === 'offscreen').length,
|
|
149
|
+
gpuCommands: tree.records.filter((record) => record.contextKind === 'webgl' || record.contextKind === 'webgl2' || record.contextKind === 'webgpu').length,
|
|
150
|
+
commandTraces: tree.commandTraces.length,
|
|
151
|
+
proofGaps: tree.proofGaps.length,
|
|
152
|
+
parseErrors: tree.parser.errors.length
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function defaultCategory(kind) {
|
|
157
|
+
if (kind === 'element') return 'html-canvas';
|
|
158
|
+
if (kind === 'state-write') return 'state';
|
|
159
|
+
return 'draw';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parsePairs(value) {
|
|
163
|
+
if (!value) return undefined;
|
|
164
|
+
const pairs = Object.fromEntries(value.split(/[|,]/).map((item) => {
|
|
165
|
+
const [key, rawValue] = item.split('=');
|
|
166
|
+
return [key, rawValue ?? true];
|
|
167
|
+
}).filter(([key]) => key));
|
|
168
|
+
return Object.keys(pairs).length ? pairs : undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function hashableRecord(record) {
|
|
172
|
+
return { kind: record.kind, name: record.name, category: record.category, contextKind: record.contextKind, renderOrder: record.renderOrder, proofGaps: record.proofGaps?.map((gap) => gap.code) };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function isPropertyLine(line) {
|
|
176
|
+
return /^(sourcePath|path|sourceHash|evidence|proofEvidence)\s+/.test(line);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
180
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$-]*)/.exec(header)?.[1] ?? 'CanvasSurface'; }
|
|
181
|
+
function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
|
|
182
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
183
|
+
function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
|
|
184
|
+
function readInlineNumber(label, text) {
|
|
185
|
+
const value = readInlineWord(label, text);
|
|
186
|
+
return value === undefined ? undefined : Number(value);
|
|
187
|
+
}
|
|
188
|
+
function readInlineList(text, ...labels) {
|
|
189
|
+
for (const label of labels) {
|
|
190
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
|
|
191
|
+
if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
|
|
192
|
+
}
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
function parseSpan(value) {
|
|
196
|
+
if (!value) return undefined;
|
|
197
|
+
const match = /^(.+?):(\d+):(\d+)-(\d+):(\d+)$/.exec(value);
|
|
198
|
+
if (!match) return { path: value };
|
|
199
|
+
return { path: match[1], startLine: Number(match[2]), startColumn: Number(match[3]), endLine: Number(match[4]), endColumn: Number(match[5]) };
|
|
200
|
+
}
|
|
201
|
+
function cleanRecord(record) {
|
|
202
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0) && (!value || typeof value !== 'object' || Object.keys(value).length > 0)));
|
|
203
|
+
}
|
|
204
|
+
function safeId(value) {
|
|
205
|
+
return String(value ?? 'unknown').replace(/[^A-Za-z0-9_$-]+/g, '_');
|
|
206
|
+
}
|
|
@@ -47,14 +47,75 @@ const WORD_FIELDS = [
|
|
|
47
47
|
['dropKind'], ['resourceKind'], ['scopeKind'], ['typeKind'], ['signatureHash'], ['contractHash'], ['typeHash'], ['flowKind'],
|
|
48
48
|
['controlFlowKind'], ['sourceControlFlowId'], ['sourceId', 'from', 'sourceId'], ['targetId', 'to', 'targetId'], ['label'],
|
|
49
49
|
['conditionHash'], ['orderingKey', 'orderingKey', 'orderKey'], ['lifetimeKind'], ['lifetimeRegionId', 'lifetimeRegion', 'lifetimeRegionId'],
|
|
50
|
-
['regionKind'], ['resourceId', 'resource', 'resourceId'],
|
|
50
|
+
['regionKind'], ['resourceId', 'resource', 'resourceId'],
|
|
51
|
+
['functionName'], ['methodName'], ['callableName'], ['callableKind'], ['callSignatureHash'],
|
|
52
|
+
['receiverKind', 'receiverKind', 'receiver'], ['thisBinding'], ['selfBinding'], ['returnKind', 'returnKind', 'returnType'],
|
|
53
|
+
['asyncKind', 'asyncKind', 'asyncMode'], ['generatorKind', 'generatorKind', 'yieldKind'], ['callbackKind'],
|
|
54
|
+
['closureCapture', 'closureCapture', 'captureKind'], ['overloadSet', 'overloadSet', 'overloads'],
|
|
55
|
+
['dispatchKind', 'dispatchKind', 'dispatchMode'], ['constructorKind'], ['abiKind'], ['ffiBoundary', 'ffiBoundary', 'foreignFunction'],
|
|
56
|
+
['exceptionModel'], ['adtKind'], ['patternKind', 'patternKind', 'matchKind', 'caseKind'], ['typeName'], ['enumName'], ['unionName'],
|
|
57
|
+
['symbolKind'], ['relationKind'], ['representationKind'], ['typeId', 'typeId', 'nodeId'], ['nodeId'], ['structId'], ['unionId'],
|
|
58
|
+
['enumId'], ['fieldId'], ['bitfieldId'], ['endianness', 'endianness', 'endian'], ['repr', 'repr', 'reprAttribute'],
|
|
59
|
+
['numericKind', 'numericKind', 'numberKind'], ['numberKind'], ['numericTypeName'], ['signedness'], ['overflowMode', 'overflowMode', 'overflowBehavior'],
|
|
60
|
+
['divisionMode', 'divisionMode', 'integerDivisionMode'], ['moduloMode', 'moduloMode', 'remainderMode'], ['floatPrecision'], ['roundingMode'],
|
|
61
|
+
['separatorPolicy'], ['coercionKind', 'coercionKind', 'conversionKind'], ['conversionKind'], ['literalKind'],
|
|
62
|
+
['textKind', 'textKind', 'stringKind'], ['stringKind'], ['stringTypeName'], ['encoding', 'encoding', 'charset', 'codepage'],
|
|
63
|
+
['charset'], ['codepage'], ['codeUnit', 'codeUnit', 'codeUnitWidth'], ['indexingUnit', 'indexingUnit', 'indexUnit'],
|
|
64
|
+
['normalizationForm', 'normalizationForm', 'normalization'], ['locale', 'locale', 'localePolicy'], ['collation', 'collation', 'collationPolicy'],
|
|
65
|
+
['caseMapping', 'caseMapping', 'caseFolding'], ['regexEngine', 'regexEngine', 'regexFlavor'], ['escapeMode', 'escapeMode', 'escaping'],
|
|
66
|
+
['interpolationMode', 'interpolationMode', 'interpolation'], ['termination', 'termination', 'nullTermination'], ['boundaryKind', 'boundaryKind', 'byteBoundary'],
|
|
67
|
+
['collectionKind', 'collectionKind', 'containerKind'], ['containerKind'], ['collectionTypeName'], ['elementKind', 'elementKind', 'elementType'],
|
|
68
|
+
['keyKind', 'keyKind', 'keyType'], ['valueKind', 'valueKind', 'valueType'], ['orderKind'], ['iterationOrder', 'iterationOrder', 'traversalOrder'],
|
|
69
|
+
['duplicatePolicy', 'duplicatePolicy', 'duplicates'], ['equality', 'equality', 'equalitySemantics'], ['hash', 'hash', 'hashSemantics'],
|
|
70
|
+
['comparator', 'comparator', 'comparison'], ['boundsBehavior', 'boundsBehavior', 'bounds'], ['lengthSemantics', 'lengthSemantics', 'sizeSemantics'],
|
|
71
|
+
['sparseSemantics', 'sparseSemantics', 'holes'], ['mutability', 'mutability', 'collectionMutability', 'stringMutability'],
|
|
72
|
+
['persistence', 'persistence', 'persistent'], ['iteratorInvalidation', 'iteratorInvalidation', 'invalidation'],
|
|
73
|
+
['traversal', 'traversal', 'laziness'], ['capacityGrowth', 'capacityGrowth', 'growth'], ['concurrency', 'concurrency', 'threadSafety'],
|
|
74
|
+
['format', 'format', 'wireFormat', 'serializationFormat'], ['wireFormat'], ['serializationFormat'], ['codec', 'codec', 'runtimeCodec'],
|
|
75
|
+
['schemaName', 'schemaName', 'schemaId', 'schema'], ['schemaId'], ['fieldNaming', 'fieldNaming', 'naming'], ['fieldOrder', 'fieldOrder', 'order'],
|
|
76
|
+
['omissionPolicy'], ['defaultValueSemantics'], ['nullSemantics', 'nullSemantics', 'nullability'], ['unknownFieldPolicy'],
|
|
77
|
+
['enumEncoding', 'enumEncoding', 'tagEncoding'], ['varint', 'varint', 'varintEncoding'], ['schemaVersion', 'schemaVersion', 'version'],
|
|
78
|
+
['compatibility'], ['canonicalization'], ['precision', 'precision', 'precisionLoss'], ['roundtrip', 'roundtrip', 'roundtripStability'],
|
|
79
|
+
['validation'], ['securityEscaping'], ['packageManager', 'packageManager', 'manager'], ['manager'], ['manifestSchema', 'manifestSchema', 'manifestKind'],
|
|
80
|
+
['manifestKind'], ['versionRange', 'versionRange', 'range'], ['resolvedVersion', 'resolvedVersion', 'version'], ['lockfile', 'lockfile', 'lockfilePath'],
|
|
81
|
+
['integrity', 'integrity', 'lockfileIntegrity'], ['dependencyClass', 'dependencyClass', 'dependencyType'], ['dependencyType'],
|
|
82
|
+
['workspace', 'workspace', 'workspaceBoundary'], ['registry', 'registry', 'registrySource'], ['sourceUrl'], ['nativeAbi', 'nativeAbi', 'abi'],
|
|
83
|
+
['buildTool', 'buildTool', 'builder'], ['packageManagerVersion', 'packageManagerVersion', 'managerVersion'],
|
|
84
|
+
['offlineCache', 'offlineCache', 'cachePolicy'], ['dedupeHoist', 'dedupeHoist', 'hoistPolicy'], ['provenance', 'provenance', 'sourceProvenance'],
|
|
85
|
+
['trust', 'trust', 'supplyChainTrust'], ['concurrencyKind'], ['constructId', 'constructId', 'taskId', 'threadId', 'actorId', 'channelId'],
|
|
86
|
+
['taskId'], ['threadId'], ['executor', 'executor', 'queue', 'runtime'], ['executorId'], ['isolationKey'], ['cancellationKey', 'cancellationKey', 'signalId', 'contextId'],
|
|
87
|
+
['signalId'], ['contextId'], ['errorKind'], ['errorType', 'errorType', 'exceptionType', 'resultType'], ['exceptionType'], ['resultType'],
|
|
88
|
+
['boundaryId', 'boundaryId', 'catchId', 'handlerId'], ['catchId'], ['handlerId'], ['evaluationKind', 'evaluationKind', 'expressionKind'],
|
|
89
|
+
['expressionKind'], ['expressionId', 'expressionId', 'nodeId'], ['operator'], ['evaluationOrder', 'evaluationOrder', 'order'],
|
|
90
|
+
['expansionKind', 'expansionKind', 'macroKind', 'templateKind', 'decoratorKind', 'generatorKind'], ['macroKind'], ['templateKind'],
|
|
91
|
+
['decoratorKind'], ['expansionId'], ['generatorId'], ['generatedSourcePath'], ['expandedHash', 'expandedHash', 'generatedHash'],
|
|
92
|
+
['generatedHash'], ['classId'], ['classKind'], ['prototypeId'], ['mixinId'], ['constructorId'], ['inheritanceKind'], ['referenceSemantics'],
|
|
93
|
+
['valueSemantics'], ['protocolKind'], ['protocolName'], ['traitName'], ['interfaceName'], ['subjectName', 'subjectName', 'receiverName', 'implementedFor'],
|
|
94
|
+
['implementedFor'], ['sourcePath', 'sourcePath', 'path'], ['sourceHash'], ['target', 'effectTarget', 'targetResource']
|
|
51
95
|
];
|
|
52
96
|
const LIST_FIELDS = [
|
|
53
97
|
['factKinds', 'fact', 'facts', 'factKind', 'factKinds'], ['importAttributes', 'importAttribute', 'importAttributes', 'assertion', 'assertions'],
|
|
54
|
-
['reads', 'read', 'reads'], ['writes', 'write', 'writes'],
|
|
98
|
+
['reads', 'read', 'reads'], ['writes', 'write', 'writes'],
|
|
99
|
+
['requiredParameters'], ['optionalParameters'], ['parameterOrder', 'parameterOrder', 'orderedParameters'], ['defaultParameters', 'defaultParameters', 'defaults'],
|
|
100
|
+
['namedArguments', 'namedArguments', 'keywordArguments'], ['effects'], ['variantNames', 'variantNames', 'variants', 'caseNames', 'cases', 'members'],
|
|
101
|
+
['constructorNames', 'constructorNames', 'constructors', 'caseConstructors'], ['payloadFieldNames', 'payloadFieldNames', 'payloadFields', 'fields', 'tupleFields', 'recordFields'],
|
|
102
|
+
['tagFieldNames', 'tagFieldNames', 'tagFields', 'discriminatorFields', 'discriminants'], ['matchArmNames', 'matchArmNames', 'matchArms', 'arms', 'switchCases'],
|
|
103
|
+
['guardKinds', 'guardKinds', 'guards', 'whereClauses', 'conditions'], ['destructuringKinds', 'destructuringKinds', 'destructuring', 'bindingPatterns', 'deconstruction'],
|
|
104
|
+
['exhaustivenessKinds', 'exhaustivenessKinds', 'exhaustiveness', 'coverageKinds'], ['fallbackKinds', 'fallbackKinds', 'fallbacks', 'defaultCases', 'wildcards'],
|
|
105
|
+
['genericParameterNames', 'genericParameterNames', 'typeParameters', 'genericParameters'], ['specialValues', 'specialValues', 'floatSpecialValues'],
|
|
106
|
+
['coercionKinds', 'coercionKinds', 'coercions'], ['literalKinds'], ['boundaryKinds', 'boundaryKinds', 'boundaries'],
|
|
107
|
+
['omittedFields', 'omittedFields', 'unknownFields'], ['defaultValues'], ['peerDependencies', 'peerDependencies', 'peers'], ['optionalDependencies'],
|
|
108
|
+
['devDependencies'], ['features', 'features', 'extras', 'flags'], ['lifecycleScripts', 'lifecycleScripts', 'scripts'],
|
|
109
|
+
['requirementNames', 'requirementNames', 'requirements', 'methods', 'members'], ['associatedTypeNames', 'associatedTypeNames', 'associatedTypes'],
|
|
110
|
+
['boundNames', 'boundNames', 'bounds', 'traitBounds', 'protocolBounds', 'whereBounds'], ['implementationKinds', 'implementationKinds', 'implKinds', 'implementations'],
|
|
111
|
+
['dispatchKinds', 'dispatchKinds', 'dispatchModes'], ['coherenceKinds', 'coherenceKinds', 'coherenceRules'], ['evidenceIds', 'evidence', 'evidenceIds']
|
|
55
112
|
];
|
|
56
|
-
const NUMBER_FIELDS = [
|
|
57
|
-
|
|
113
|
+
const NUMBER_FIELDS = [
|
|
114
|
+
['arity'], ['size'], ['alignment'], ['parameterCount'], ['requiredParameterCount'], ['optionalParameterCount'],
|
|
115
|
+
['sizeBytes'], ['alignmentBytes'], ['offsetBytes'], ['pointerWidth'], ['integerWidth'], ['bitWidth'], ['width'],
|
|
116
|
+
['codeUnitWidth'], ['indexBase']
|
|
117
|
+
];
|
|
118
|
+
const FLAG_FIELDS = ['nullable', 'optional', 'publicContract', 'closure', 'captured', 'writeExpr', 'mutable', 'shadowed', 'hoisted', 'typeOnly', 'isTypeReference', 'isValueReference', 'shared', 'volatile', 'atomic', 'adapterRequired', 'async', 'generator', 'exceptional', 'cancellable', 'variadic', 'signed', 'nan', 'infinity', 'deterministic', 'streaming', 'framing', 'copyOnWrite', 'multipleInheritance', 'reflection', 'staticDispatch', 'virtual', 'spawn', 'await', 'structured', 'reentrant', 'cancelable'];
|
|
58
119
|
|
|
59
120
|
export const FAMILIES = Object.freeze(Object.fromEntries(FAMILY_ROWS.flatMap(([name, field, sourceKey, targetKey, aliases = [], extra = {}]) => {
|
|
60
121
|
const config = { field, sourceKey, targetKey, ...extra };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { actionNode, capabilityNode, createDocument, effectNode, entityNode, externNode, latticeNode, migrationNode, stateNode, targetNode, typeNode } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
2
|
import { parseConstraintSpaceBlock } from './constraint-space.js';
|
|
3
3
|
import { parseConversionBlock } from './conversion.js';
|
|
4
|
+
import { parseCanvasSurfaceBlock } from './canvas-surface.js';
|
|
4
5
|
import { parseDecisionGraphBlock } from './decision-graph.js';
|
|
5
6
|
import { parseDialectRegistryBlock } from './dialect-registry.js';
|
|
6
7
|
import { parseInterlinguaBlock } from './interlingua.js';
|
|
7
8
|
import { createParsedMetadata } from './metadata.js';
|
|
8
9
|
import { parseSemanticOperationsBlock } from './operations.js';
|
|
10
|
+
import { parsePackageManifestBlock } from './package-manifest.js';
|
|
9
11
|
import { parseParadigmBlock } from './paradigm.js';
|
|
10
12
|
import { parseProofBlock } from './proof.js';
|
|
11
13
|
import { parseResourceGraphBlock } from './resource-graph.js';
|
|
@@ -25,6 +27,8 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
25
27
|
const interlinguaBlocks = [];
|
|
26
28
|
const resourceGraphBlocks = [];
|
|
27
29
|
const nativeSourceBlocks = [];
|
|
30
|
+
const packageManifestBlocks = [];
|
|
31
|
+
const canvasSurfaceBlocks = [];
|
|
28
32
|
const documentId = options.id ?? readId(source) ?? 'mod_frontier';
|
|
29
33
|
const documentName = options.name ?? readName(source) ?? 'FrontierModule';
|
|
30
34
|
for (const block of readBlocks(source)) {
|
|
@@ -53,8 +57,10 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
53
57
|
if (block.kind === 'dialectRegistry' || block.kind === 'universalDialectRegistry') dialectRegistryBlocks.push(parseDialectRegistryBlock(block));
|
|
54
58
|
if (block.kind === 'interlingua' || block.kind === 'universalInterlingua') interlinguaBlocks.push(parseInterlinguaBlock(block));
|
|
55
59
|
if (block.kind === 'resourceGraph' || block.kind === 'semanticResourceGraph') resourceGraphBlocks.push(parseResourceGraphBlock(block));
|
|
60
|
+
if (block.kind === 'packageManifest' || block.kind === 'packageGraph' || block.kind === 'packageSurface') packageManifestBlocks.push(parsePackageManifestBlock(block));
|
|
61
|
+
if (block.kind === 'canvasSurface' || block.kind === 'canvasGraph') canvasSurfaceBlocks.push(parseCanvasSurfaceBlock(block));
|
|
56
62
|
}
|
|
57
|
-
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks });
|
|
63
|
+
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks });
|
|
58
64
|
return createDocument({ id: documentId, name: documentName, nodes, ...(metadata ? { metadata } : {}) });
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -64,7 +70,7 @@ function readName(source) { return /module\s+([A-Za-z_$][\w$]*)/.exec(source)?.[
|
|
|
64
70
|
function readId(source) { return /module\s+[A-Za-z_$][\w$]*\s+@id\(\s*["']([^"']+)["']\s*\)/.exec(source)?.[1]; }
|
|
65
71
|
function readBlocks(source) {
|
|
66
72
|
const blocks = [];
|
|
67
|
-
const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof|paradigm|paradigmSemantics|operations|semanticOperations|conversion|universalConversionPlan|constraintSpace|possibilitySpace|decisionGraph|admissionGraph|dialectRegistry|universalDialectRegistry|interlingua|universalInterlingua|resourceGraph|semanticResourceGraph)\s+([^{}]+)\{/g;
|
|
73
|
+
const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof|paradigm|paradigmSemantics|operations|semanticOperations|conversion|universalConversionPlan|constraintSpace|possibilitySpace|decisionGraph|admissionGraph|dialectRegistry|universalDialectRegistry|interlingua|universalInterlingua|resourceGraph|semanticResourceGraph|packageManifest|packageGraph|packageSurface|canvasSurface|canvasGraph)\s+([^{}]+)\{/g;
|
|
68
74
|
let match;
|
|
69
75
|
while ((match = header.exec(source))) {
|
|
70
76
|
let depth = 1; let index = header.lastIndex;
|
package/dist/metadata.js
CHANGED
|
@@ -23,7 +23,7 @@ const PARADIGM_GROUPS = [
|
|
|
23
23
|
'loweringRecords'
|
|
24
24
|
];
|
|
25
25
|
|
|
26
|
-
export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], conversionBlocks = [], constraintSpaceBlocks = [], decisionGraphBlocks = [], dialectRegistryBlocks = [], interlinguaBlocks = [], resourceGraphBlocks = [], nativeSourceBlocks = [] } = {}) {
|
|
26
|
+
export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], conversionBlocks = [], constraintSpaceBlocks = [], decisionGraphBlocks = [], dialectRegistryBlocks = [], interlinguaBlocks = [], resourceGraphBlocks = [], nativeSourceBlocks = [], packageManifestBlocks = [], canvasSurfaceBlocks = [] } = {}) {
|
|
27
27
|
const metadata = {};
|
|
28
28
|
if (proofBlocks.length) metadata.proof = mergeProofBlocks(proofBlocks);
|
|
29
29
|
if (paradigmBlocks.length) metadata.paradigmSemantics = mergeParadigmBlocks(paradigmBlocks);
|
|
@@ -34,8 +34,10 @@ export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], op
|
|
|
34
34
|
if (dialectRegistryBlocks.length) metadata.dialects = mergeDialectRegistryBlocks(dialectRegistryBlocks);
|
|
35
35
|
if (interlinguaBlocks.length) metadata.universalInterlingua = mergeInterlinguaBlocks(interlinguaBlocks);
|
|
36
36
|
if (resourceGraphBlocks.length) metadata.semanticResourceGraphs = mergeResourceGraphBlocks(resourceGraphBlocks);
|
|
37
|
-
if (
|
|
38
|
-
|
|
37
|
+
if (packageManifestBlocks.length) metadata.packageManifests = mergePackageManifestBlocks(packageManifestBlocks);
|
|
38
|
+
if (canvasSurfaceBlocks.length) metadata.canvasSurfaces = mergeCanvasSurfaceBlocks(canvasSurfaceBlocks);
|
|
39
|
+
if (nativeSourceBlocks.some((block) => block.sourceMaps.length || block.mergeCandidates.length || block.evidence.length) || packageManifestBlocks.length || canvasSurfaceBlocks.length) {
|
|
40
|
+
metadata.universalAst = mergeUniversalAstBlocks(nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks);
|
|
39
41
|
}
|
|
40
42
|
return Object.keys(metadata).length ? metadata : undefined;
|
|
41
43
|
}
|
|
@@ -106,7 +108,7 @@ function mergeConstraintSpaceBlocks(blocks) {
|
|
|
106
108
|
};
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
function
|
|
111
|
+
function mergeUniversalAstBlocks(blocks, packageManifestBlocks = [], canvasSurfaceBlocks = []) {
|
|
110
112
|
return {
|
|
111
113
|
id: blocks.length === 1 ? `universalAst:${blocks[0].node.id}` : 'universalAst:source',
|
|
112
114
|
nativeSourceIds: blocks.map((block) => block.node.id),
|
|
@@ -114,7 +116,71 @@ function mergeNativeSourceBlocks(blocks) {
|
|
|
114
116
|
mergeCandidates: blocks.flatMap((block) => block.mergeCandidates ?? []),
|
|
115
117
|
evidence: blocks.flatMap((block) => block.evidence ?? []),
|
|
116
118
|
losses: blocks.flatMap((block) => block.losses ?? []),
|
|
117
|
-
|
|
119
|
+
packageManifests: packageManifestBlocks,
|
|
120
|
+
canvasSurfaces: canvasSurfaceBlocks,
|
|
121
|
+
packageManifestIds: ids(packageManifestBlocks),
|
|
122
|
+
canvasSurfaceIds: ids(canvasSurfaceBlocks),
|
|
123
|
+
metadata: {
|
|
124
|
+
authoredNativeSourceIds: blocks.map((block) => block.node.id),
|
|
125
|
+
authoredPackageManifestIds: ids(packageManifestBlocks),
|
|
126
|
+
authoredCanvasSurfaceIds: ids(canvasSurfaceBlocks)
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function mergePackageManifestBlocks(blocks) {
|
|
132
|
+
return {
|
|
133
|
+
id: blocks.length === 1 ? blocks[0].id : 'packageManifests:source',
|
|
134
|
+
manifests: blocks,
|
|
135
|
+
manifestIds: ids(blocks),
|
|
136
|
+
recordIds: blocks.flatMap((block) => ids(block.records)),
|
|
137
|
+
evidenceIds: blocks.flatMap((block) => ids(block.evidence)),
|
|
138
|
+
proofGapCodes: [...new Set(blocks.flatMap((block) => (block.proofGaps ?? []).map((gap) => gap.code).filter(Boolean)))],
|
|
139
|
+
summary: {
|
|
140
|
+
manifestCount: blocks.length,
|
|
141
|
+
recordCount: blocks.reduce((sum, block) => sum + (block.records?.length ?? 0), 0),
|
|
142
|
+
dependencyCount: blocks.reduce((sum, block) => sum + (block.summary?.dependencies ?? 0), 0),
|
|
143
|
+
scriptCount: blocks.reduce((sum, block) => sum + (block.summary?.scripts ?? 0), 0),
|
|
144
|
+
exportCount: blocks.reduce((sum, block) => sum + (block.summary?.exports ?? 0), 0),
|
|
145
|
+
proofGapCount: blocks.reduce((sum, block) => sum + (block.proofGaps?.length ?? 0), 0)
|
|
146
|
+
},
|
|
147
|
+
claims: {
|
|
148
|
+
autoMergeClaim: false,
|
|
149
|
+
semanticEquivalenceClaim: false,
|
|
150
|
+
packageInstallEquivalenceClaim: false,
|
|
151
|
+
installEquivalenceClaim: false,
|
|
152
|
+
runtimeEquivalenceClaim: false
|
|
153
|
+
},
|
|
154
|
+
metadata: { authoredPackageManifestIds: ids(blocks) }
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function mergeCanvasSurfaceBlocks(blocks) {
|
|
159
|
+
return {
|
|
160
|
+
id: blocks.length === 1 ? blocks[0].id : 'canvasSurfaces:source',
|
|
161
|
+
surfaces: blocks,
|
|
162
|
+
surfaceIds: ids(blocks),
|
|
163
|
+
recordIds: blocks.flatMap((block) => ids(block.records)),
|
|
164
|
+
commandTraceIds: blocks.flatMap((block) => ids(block.commandTraces)),
|
|
165
|
+
evidenceIds: blocks.flatMap((block) => ids(block.evidence)),
|
|
166
|
+
proofGapCodes: [...new Set(blocks.flatMap((block) => (block.proofGaps ?? []).map((gap) => gap.code).filter(Boolean)))],
|
|
167
|
+
summary: {
|
|
168
|
+
surfaceCount: blocks.length,
|
|
169
|
+
recordCount: blocks.reduce((sum, block) => sum + (block.records?.length ?? 0), 0),
|
|
170
|
+
commandTraceCount: blocks.reduce((sum, block) => sum + (block.commandTraces?.length ?? 0), 0),
|
|
171
|
+
drawCommandCount: blocks.reduce((sum, block) => sum + (block.summary?.drawCommands ?? 0), 0),
|
|
172
|
+
offscreenCommandCount: blocks.reduce((sum, block) => sum + (block.summary?.offscreenCommands ?? 0), 0),
|
|
173
|
+
gpuCommandCount: blocks.reduce((sum, block) => sum + (block.summary?.gpuCommands ?? 0), 0),
|
|
174
|
+
proofGapCount: blocks.reduce((sum, block) => sum + (block.proofGaps?.length ?? 0), 0)
|
|
175
|
+
},
|
|
176
|
+
claims: {
|
|
177
|
+
autoMergeClaim: false,
|
|
178
|
+
semanticEquivalenceClaim: false,
|
|
179
|
+
browserRuntimeEquivalenceClaim: false,
|
|
180
|
+
canvasRuntimeEquivalenceClaim: false,
|
|
181
|
+
canvasVisualEquivalenceClaim: false
|
|
182
|
+
},
|
|
183
|
+
metadata: { authoredCanvasSurfaceIds: ids(blocks) }
|
|
118
184
|
};
|
|
119
185
|
}
|
|
120
186
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
|
|
3
|
+
export function parsePackageManifestBlock(block) {
|
|
4
|
+
const name = nameFrom(block.header);
|
|
5
|
+
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body) ?? 'package.json';
|
|
6
|
+
const sourceHash = readLine('sourceHash', block.body);
|
|
7
|
+
const evidence = parseEvidenceRows(block.body);
|
|
8
|
+
const records = [];
|
|
9
|
+
const proofGaps = [];
|
|
10
|
+
for (const rawLine of block.body.split('\n')) {
|
|
11
|
+
const line = rawLine.trim();
|
|
12
|
+
if (!line || line.startsWith('#') || isPropertyLine(line)) continue;
|
|
13
|
+
const match = /^(metadata|dependency|script|export|gap|proofGap)\s+([A-Za-z_$.*@/][\w$./@*-]*)(.*)$/.exec(line);
|
|
14
|
+
if (!match) continue;
|
|
15
|
+
const [, rowKind, rowName, rest] = match;
|
|
16
|
+
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
17
|
+
proofGaps.push(packageProofGap(rowName, rest));
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const record = packageRecord(rowKind, rowName, rest, { sourcePath, sourceHash, evidence });
|
|
21
|
+
if (record) records.push(record);
|
|
22
|
+
}
|
|
23
|
+
const recordGaps = records.flatMap((record) => record.proofGaps ?? []);
|
|
24
|
+
const allGaps = [...recordGaps, ...proofGaps];
|
|
25
|
+
const tree = {
|
|
26
|
+
kind: 'frontier.lang.packageManifestSemanticTree',
|
|
27
|
+
version: 1,
|
|
28
|
+
id: idFrom(block.header, `package_manifest_${name}`),
|
|
29
|
+
name,
|
|
30
|
+
sourcePath,
|
|
31
|
+
sourceHash,
|
|
32
|
+
packageManager: readLine('packageManager', block.body),
|
|
33
|
+
records,
|
|
34
|
+
proofGaps: allGaps,
|
|
35
|
+
evidence,
|
|
36
|
+
parser: { status: 'authored', errors: [] },
|
|
37
|
+
claims: {
|
|
38
|
+
autoMergeClaim: false,
|
|
39
|
+
semanticEquivalenceClaim: false,
|
|
40
|
+
packageInstallEquivalenceClaim: false,
|
|
41
|
+
installEquivalenceClaim: false,
|
|
42
|
+
runtimeEquivalenceClaim: false
|
|
43
|
+
},
|
|
44
|
+
metadata: { authoredName: name }
|
|
45
|
+
};
|
|
46
|
+
tree.summary = summarizePackageManifest(tree);
|
|
47
|
+
tree.treeHash = hashSemanticValue({ kind: 'frontier.lang.package.authoredTree.v1', records: records.map(hashableRecord), proofGaps: allGaps.map((gap) => gap.code) });
|
|
48
|
+
return tree;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function packageRecord(kind, name, text, context) {
|
|
52
|
+
const section = readInlineWord('section', text) ?? defaultSection(kind);
|
|
53
|
+
const recordName = readInlineWord('name', text) ?? name;
|
|
54
|
+
const value = packageValue(kind, text);
|
|
55
|
+
const proofGaps = readInlineList(text, 'proofGap', 'proofGaps', 'gap', 'gaps')?.map((code) => packageProofGap(code, '')) ?? [];
|
|
56
|
+
return cleanRecord({
|
|
57
|
+
kind,
|
|
58
|
+
id: idFrom(text, `package_${kind}_${safeId(section)}_${safeId(recordName)}`),
|
|
59
|
+
section,
|
|
60
|
+
name: recordName,
|
|
61
|
+
value,
|
|
62
|
+
valueHash: hashSemanticValue({ kind: 'frontier.lang.package.authoredRecordValue.v1', value }),
|
|
63
|
+
identityKey: readInlineWord('identity', text) ?? readInlineWord('identityKey', text) ?? `${kind}:${section}:${recordName}`,
|
|
64
|
+
sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text) ?? context.sourcePath,
|
|
65
|
+
sourceHash: readInlineWord('sourceHash', text) ?? context.sourceHash,
|
|
66
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text)),
|
|
67
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
68
|
+
proofGaps
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function packageValue(kind, text) {
|
|
73
|
+
if (kind === 'dependency') return readInlineQuoted('value', text) ?? readInlineWord('range', text) ?? readInlineWord('value', text) ?? '*';
|
|
74
|
+
if (kind === 'script') return readInlineQuoted('command', text) ?? readInlineQuoted('value', text) ?? readInlineWord('command', text) ?? readInlineWord('value', text);
|
|
75
|
+
if (kind === 'export') return readInlineQuoted('target', text) ?? readInlineQuoted('value', text) ?? readInlineWord('target', text) ?? readInlineWord('value', text);
|
|
76
|
+
return readInlineQuoted('value', text) ?? readInlineWord('value', text);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function defaultSection(kind) {
|
|
80
|
+
if (kind === 'dependency') return 'dependencies';
|
|
81
|
+
if (kind === 'script') return 'scripts';
|
|
82
|
+
if (kind === 'export') return 'exports';
|
|
83
|
+
return 'metadata';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function packageProofGap(name, text) {
|
|
87
|
+
const code = readInlineWord('code', text) ?? name;
|
|
88
|
+
return cleanRecord({
|
|
89
|
+
id: idFrom(text, `package_gap_${safeId(code)}`),
|
|
90
|
+
code,
|
|
91
|
+
status: readInlineWord('status', text) ?? 'not-claimed',
|
|
92
|
+
summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
|
|
93
|
+
failClosed: true,
|
|
94
|
+
semanticEquivalenceClaim: false,
|
|
95
|
+
packageInstallEquivalenceClaim: false,
|
|
96
|
+
installEquivalenceClaim: false,
|
|
97
|
+
runtimeEquivalenceClaim: false,
|
|
98
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text))
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseEvidenceRows(body) {
|
|
103
|
+
const records = [];
|
|
104
|
+
for (const rawLine of body.split('\n')) {
|
|
105
|
+
const line = rawLine.trim();
|
|
106
|
+
const match = /^(?:evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
107
|
+
if (!match) continue;
|
|
108
|
+
records.push(cleanRecord({
|
|
109
|
+
id: idFrom(match[2], `evidence_${match[1]}`),
|
|
110
|
+
kind: readInlineWord('kind', match[2]) ?? 'note',
|
|
111
|
+
status: readInlineWord('status', match[2]) ?? 'unknown',
|
|
112
|
+
path: readInlineWord('path', match[2]),
|
|
113
|
+
summary: readInlineQuoted('summary', match[2]),
|
|
114
|
+
metadata: { name: match[1] }
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
return records;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function summarizePackageManifest(tree) {
|
|
121
|
+
return {
|
|
122
|
+
metadata: tree.records.filter((record) => record.kind === 'metadata').length,
|
|
123
|
+
dependencies: tree.records.filter((record) => record.kind === 'dependency').length,
|
|
124
|
+
scripts: tree.records.filter((record) => record.kind === 'script').length,
|
|
125
|
+
exports: tree.records.filter((record) => record.kind === 'export').length,
|
|
126
|
+
proofGaps: tree.proofGaps.length,
|
|
127
|
+
parseErrors: tree.parser.errors.length
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function hashableRecord(record) {
|
|
132
|
+
return { kind: record.kind, section: record.section, name: record.name, value: record.value, proofGaps: record.proofGaps?.map((gap) => gap.code) };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isPropertyLine(line) {
|
|
136
|
+
return /^(sourcePath|path|sourceHash|packageManager|evidence|proofEvidence)\s+/.test(line);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
140
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$-]*)/.exec(header)?.[1] ?? 'PackageManifest'; }
|
|
141
|
+
function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
|
|
142
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
143
|
+
function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
|
|
144
|
+
function readInlineList(text, ...labels) {
|
|
145
|
+
for (const label of labels) {
|
|
146
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
|
|
147
|
+
if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
function parseSpan(value) {
|
|
152
|
+
if (!value) return undefined;
|
|
153
|
+
const match = /^(.+?):(\d+):(\d+)-(\d+):(\d+)$/.exec(value);
|
|
154
|
+
if (!match) return { path: value };
|
|
155
|
+
return { path: match[1], startLine: Number(match[2]), startColumn: Number(match[3]), endLine: Number(match[4]), endColumn: Number(match[5]) };
|
|
156
|
+
}
|
|
157
|
+
function cleanRecord(record) {
|
|
158
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0) && (!value || typeof value !== 'object' || Object.keys(value).length > 0)));
|
|
159
|
+
}
|
|
160
|
+
function safeId(value) {
|
|
161
|
+
return String(value ?? 'unknown').replace(/[^A-Za-z0-9_$-]+/g, '_');
|
|
162
|
+
}
|
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.28",
|
|
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/conversion-constraint-fields-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/conversion-constraint-fields-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",
|