@shapeshift-labs/frontier-lang-parser 0.3.69 → 0.3.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,116 @@
1
+ export function proofObligationRecord(name, text, authoredLine, rowKind) {
2
+ return cleanRecord({
3
+ kind: 'frontier.lang.gateAdmissionEvidence.proofObligation',
4
+ version: 1,
5
+ id: idFrom(text, `gate_admission_obligation_${safeId(name)}`),
6
+ name,
7
+ obligationKind: readInlineWord('kind', text) ?? readInlineWord('obligationKind', text) ?? 'proof',
8
+ status: readInlineWord('status', text) ?? 'missing',
9
+ readiness: readInlineWord('readiness', text),
10
+ capability: readInlineWord('capability', text),
11
+ routeId: readInlineWord('route', text) ?? readInlineWord('routeId', text),
12
+ routeIds: readInlineList(text, 'routeIds'),
13
+ language: readInlineWord('language', text) ?? readInlineWord('sourceLanguage', text),
14
+ sourceLanguage: readInlineWord('sourceLanguage', text) ?? readInlineWord('language', text),
15
+ target: readInlineWord('target', text) ?? readInlineWord('targetLanguage', text),
16
+ sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text),
17
+ sourceHash: readInlineWord('sourceHash', text),
18
+ targetHash: readInlineWord('targetHash', text),
19
+ outputHash: readInlineWord('outputHash', text),
20
+ telemetryHash: readInlineWord('telemetryHash', text),
21
+ gateIds: readInlineList(text, 'gate', 'gateId', 'gateIds'),
22
+ admissionIds: readInlineList(text, 'admission', 'admissionId', 'admissionIds'),
23
+ evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
24
+ proofEvidenceIds: readInlineList(text, 'proofEvidence', 'proofEvidenceIds'),
25
+ requiredSignals: readInlineList(text, 'requiredSignal', 'requiredSignals'),
26
+ providedSignals: readInlineList(text, 'providedSignal', 'providedSignals'),
27
+ missingSignals: readInlineList(text, 'missingSignal', 'missingSignals'),
28
+ missingEvidence: readInlineList(text, 'missingEvidence'),
29
+ reasonCodes: readInlineList(text, 'reasonCode', 'reasonCodes', 'reason', 'reasons'),
30
+ summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
31
+ failClosed: true,
32
+ ...falseClaims(),
33
+ authoredText: authoredLine.text,
34
+ sourceSpan: authoredLine.sourceSpan,
35
+ authoredSourceSpan: authoredLine.sourceSpan,
36
+ metadata: cleanRecord({ authoredName: name, authoredRowKind: rowKind, ...falseClaims() })
37
+ });
38
+ }
39
+
40
+ export function unknownRowRecord(line, authoredLine) {
41
+ const rowKind = /^([A-Za-z_$][\w$-]*)\b/.exec(line)?.[1];
42
+ const name = new RegExp('^[A-Za-z_$][\\w$-]*\\s+([A-Za-z_$@/.:*-][\\w$./@:*+-]*)').exec(line)?.[1];
43
+ return cleanRecord({
44
+ kind: 'frontier.lang.gateAdmissionEvidence.unknownRow',
45
+ version: 1,
46
+ id: `gate_admission_unknown_${safeId(rowKind ?? 'row')}_${authoredLine.startOffset}`,
47
+ rowKind,
48
+ normalizedRowKind: 'unknown',
49
+ name: name ?? rowKind ?? 'unknown',
50
+ status: 'unsupported',
51
+ reason: 'unsupported-gate-admission-row',
52
+ code: 'unsupported-gate-admission-row',
53
+ failClosed: true,
54
+ ...falseClaims(),
55
+ authoredText: line,
56
+ sourceSpan: authoredLine.sourceSpan,
57
+ authoredSourceSpan: authoredLine.sourceSpan,
58
+ metadata: cleanRecord({ authoredRowKind: rowKind, ...falseClaims() })
59
+ });
60
+ }
61
+
62
+ export function unsupportedRowProofGapRecord(unknownRow, authoredLine) {
63
+ return cleanRecord({
64
+ kind: 'frontier.lang.gateAdmissionEvidence.proofGap',
65
+ version: 1,
66
+ id: `gate_admission_gap_unsupported_${authoredLine.startOffset}`,
67
+ name: unknownRow.name,
68
+ code: 'unsupported-gate-admission-row',
69
+ status: 'missing',
70
+ evidenceIds: [unknownRow.id],
71
+ summary: `Unsupported gateEvidence row "${unknownRow.rowKind ?? 'unknown'}" requires parser support before admission.`,
72
+ failClosed: true,
73
+ ...falseClaims(),
74
+ authoredText: authoredLine.text,
75
+ sourceSpan: authoredLine.sourceSpan,
76
+ authoredSourceSpan: authoredLine.sourceSpan,
77
+ metadata: cleanRecord({ unknownRowId: unknownRow.id, authoredRowKind: unknownRow.rowKind, ...falseClaims() })
78
+ });
79
+ }
80
+
81
+ export function parserUnsupportedRowError(unknownRow) {
82
+ return cleanRecord({
83
+ code: 'unsupported-gate-admission-row',
84
+ message: `Unsupported gateEvidence row "${unknownRow.rowKind ?? 'unknown'}" must be reviewed before admission.`,
85
+ rowId: unknownRow.id,
86
+ rowKind: unknownRow.rowKind,
87
+ sourceSpan: unknownRow.sourceSpan
88
+ });
89
+ }
90
+
91
+ function falseClaims() {
92
+ return {
93
+ autoMergeClaim: false,
94
+ semanticEquivalenceClaim: false,
95
+ runtimeEquivalenceClaim: false,
96
+ gatePassImpliesAdmissionClaim: false,
97
+ targetAdapterReadinessClaim: false
98
+ };
99
+ }
100
+
101
+ function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
102
+ function safeId(value) { return String(value ?? 'record').replace(/[^A-Za-z0-9_$-]+/g, '_'); }
103
+ function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(unquotedText(text))?.[1]?.trim(); }
104
+ function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
105
+ function readInlineList(text, ...labels) {
106
+ const source = unquotedText(text);
107
+ for (const label of labels) {
108
+ const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(source)?.[1]?.trim();
109
+ if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
110
+ }
111
+ return undefined;
112
+ }
113
+ function unquotedText(text) { return text.replace(/"[^"]*"|'[^']*'/g, (match) => ' '.repeat(match.length)); }
114
+ function cleanRecord(record) {
115
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0) && !(value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)));
116
+ }
@@ -0,0 +1,319 @@
1
+ import { parserUnsupportedRowError, proofObligationRecord, unknownRowRecord, unsupportedRowProofGapRecord } from './gate-admission-evidence-proof-obligations.js';
2
+
3
+ export function parseGateAdmissionEvidenceBlock(block) {
4
+ const name = nameFrom(block.header);
5
+ const surface = {
6
+ kind: 'frontier.lang.authoredGateAdmissionEvidenceInput',
7
+ version: 1,
8
+ schema: 'frontier.lang.authoredGateAdmissionEvidenceInput.v1',
9
+ id: idFrom(block.header, `gate_admission_${safeId(name)}`),
10
+ name,
11
+ gates: [],
12
+ evidence: [],
13
+ admissions: [],
14
+ proofObligations: [],
15
+ proofGaps: [],
16
+ unknownRows: [],
17
+ parser: { status: 'authored', errors: [] },
18
+ claims: falseClaims(),
19
+ metadata: { authoredName: name, authoredBlockKind: block.kind }
20
+ };
21
+ for (const authoredLine of readAuthoredLines(block)) {
22
+ const line = authoredLine.text;
23
+ if (!line || line.startsWith('#')) continue;
24
+ const match = /^(gate|evidence|proofEvidence|admission|admissionDecision|proofObligation|obligation|gap|proofGap)\s+([A-Za-z_$@/.:*-][\w$./@:*+-]*)(.*)$/.exec(line);
25
+ if (!match) {
26
+ const unknownRow = unknownRowRecord(line, authoredLine);
27
+ surface.unknownRows.push(unknownRow);
28
+ surface.proofGaps.push(unsupportedRowProofGapRecord(unknownRow, authoredLine));
29
+ surface.parser.status = 'needs-review';
30
+ surface.parser.errors.push(parserUnsupportedRowError(unknownRow));
31
+ continue;
32
+ }
33
+ const [, rowKind, rowName, text] = match;
34
+ if (rowKind === 'gate') surface.gates.push(gateRecord(rowName, text, authoredLine));
35
+ else if (rowKind === 'evidence' || rowKind === 'proofEvidence') surface.evidence.push(evidenceRecord(rowName, text, authoredLine, rowKind));
36
+ else if (rowKind === 'admission' || rowKind === 'admissionDecision') surface.admissions.push(admissionRecord(rowName, text, authoredLine));
37
+ else if (rowKind === 'proofObligation' || rowKind === 'obligation') surface.proofObligations.push(proofObligationRecord(rowName, text, authoredLine, rowKind));
38
+ else surface.proofGaps.push(proofGapRecord(rowName, text, authoredLine));
39
+ }
40
+ surface.gateIds = ids(surface.gates);
41
+ surface.evidenceIds = ids(surface.evidence);
42
+ surface.proofEvidenceIds = surface.evidence.filter((record) => record.proof).map((record) => record.id).filter(Boolean);
43
+ surface.admissionIds = ids(surface.admissions);
44
+ surface.proofObligationIds = ids(surface.proofObligations);
45
+ surface.unknownRowIds = ids(surface.unknownRows);
46
+ surface.proofGapCodes = uniqueStrings(surface.proofGaps.map((record) => record.code));
47
+ surface.missingEvidence = uniqueStrings([
48
+ ...surface.gates.flatMap((record) => record.missingEvidence ?? []),
49
+ ...surface.admissions.flatMap((record) => record.missingEvidence ?? []),
50
+ ...surface.proofObligations.flatMap((record) => record.missingEvidence ?? []),
51
+ ...surface.proofObligations.flatMap((record) => record.missingSignals ?? []),
52
+ ...surface.proofGaps.map((record) => record.code)
53
+ ]);
54
+ surface.summary = {
55
+ gateCount: surface.gates.length,
56
+ evidenceCount: surface.evidence.length,
57
+ proofEvidenceCount: surface.proofEvidenceIds.length,
58
+ admissionCount: surface.admissions.length,
59
+ proofObligationCount: surface.proofObligations.length,
60
+ proofGapCount: surface.proofGaps.length,
61
+ unknownRowCount: surface.unknownRows.length,
62
+ missingEvidenceCount: surface.missingEvidence.length,
63
+ passedGateCount: surface.gates.filter((record) => record.status === 'passed').length,
64
+ failedGateCount: surface.gates.filter((record) => record.status === 'failed').length,
65
+ openProofObligationCount: surface.proofObligations.filter((record) => /missing|blocked|review|open|unknown/.test(record.status ?? '')).length,
66
+ blockedAdmissionCount: surface.admissions.filter((record) => /blocked|missing|failed|review/.test(record.status ?? '')).length
67
+ };
68
+ return surface;
69
+ }
70
+
71
+ export function mergeGateAdmissionEvidenceBlocks(blocks) {
72
+ const gates = uniqueRecords(blocks.flatMap((block) => block.gates ?? []));
73
+ const evidence = uniqueRecords(blocks.flatMap((block) => block.evidence ?? []));
74
+ const admissions = uniqueRecords(blocks.flatMap((block) => block.admissions ?? []));
75
+ const proofObligations = uniqueRecords(blocks.flatMap((block) => block.proofObligations ?? []));
76
+ const proofGaps = uniqueRecords(blocks.flatMap((block) => block.proofGaps ?? []));
77
+ const unknownRows = uniqueRecords(blocks.flatMap((block) => block.unknownRows ?? []));
78
+ const proofEvidenceIds = evidence.filter((record) => record.proof).map((record) => record.id).filter(Boolean);
79
+ const missingEvidence = uniqueStrings([
80
+ ...gates.flatMap((record) => record.missingEvidence ?? []),
81
+ ...admissions.flatMap((record) => record.missingEvidence ?? []),
82
+ ...proofObligations.flatMap((record) => record.missingEvidence ?? []),
83
+ ...proofObligations.flatMap((record) => record.missingSignals ?? []),
84
+ ...proofGaps.map((record) => record.code)
85
+ ]);
86
+ const parserErrors = blocks.flatMap((block) => block.parser?.errors ?? []);
87
+ return {
88
+ kind: 'frontier.lang.authoredGateAdmissionEvidenceInput',
89
+ version: 1,
90
+ schema: 'frontier.lang.authoredGateAdmissionEvidenceInput.v1',
91
+ id: blocks.length === 1 ? blocks[0].id : 'gateAdmissionEvidence:source',
92
+ blocks,
93
+ blockIds: ids(blocks),
94
+ gates,
95
+ evidence,
96
+ admissions,
97
+ proofObligations,
98
+ proofGaps,
99
+ unknownRows,
100
+ gateIds: ids(gates),
101
+ evidenceIds: ids(evidence),
102
+ proofEvidenceIds,
103
+ admissionIds: ids(admissions),
104
+ proofObligationIds: ids(proofObligations),
105
+ unknownRowIds: ids(unknownRows),
106
+ proofGapCodes: uniqueStrings(proofGaps.map((record) => record.code)),
107
+ missingEvidence,
108
+ parser: {
109
+ status: parserErrors.length ? 'needs-review' : 'authored',
110
+ errors: parserErrors
111
+ },
112
+ summary: {
113
+ blockCount: blocks.length,
114
+ gateCount: gates.length,
115
+ evidenceCount: evidence.length,
116
+ proofEvidenceCount: proofEvidenceIds.length,
117
+ admissionCount: admissions.length,
118
+ proofObligationCount: proofObligations.length,
119
+ proofGapCount: proofGaps.length,
120
+ unknownRowCount: unknownRows.length,
121
+ missingEvidenceCount: missingEvidence.length,
122
+ passedGateCount: gates.filter((record) => record.status === 'passed').length,
123
+ failedGateCount: gates.filter((record) => record.status === 'failed').length,
124
+ openProofObligationCount: proofObligations.filter((record) => /missing|blocked|review|open|unknown/.test(record.status ?? '')).length,
125
+ blockedAdmissionCount: admissions.filter((record) => /blocked|missing|failed|review/.test(record.status ?? '')).length
126
+ },
127
+ claims: falseClaims(),
128
+ metadata: { authoredGateAdmissionBlockIds: ids(blocks) }
129
+ };
130
+ }
131
+
132
+ function gateRecord(name, text, authoredLine) {
133
+ const evidenceIds = readInlineList(text, 'evidence', 'evidenceIds');
134
+ return cleanRecord({
135
+ kind: 'frontier.lang.gateAdmissionEvidence.gate',
136
+ version: 1,
137
+ id: idFrom(text, `gate_${safeId(name)}`),
138
+ name,
139
+ gateKind: readInlineWord('kind', text) ?? readInlineWord('gateKind', text) ?? 'gate',
140
+ status: readInlineWord('status', text) ?? 'unknown',
141
+ required: readInlineFlag('required', text),
142
+ command: readInlineQuoted('command', text) ?? readInlineWord('command', text),
143
+ routeId: readInlineWord('route', text) ?? readInlineWord('routeId', text),
144
+ routeIds: readInlineList(text, 'routeIds'),
145
+ language: readInlineWord('language', text) ?? readInlineWord('sourceLanguage', text),
146
+ sourceLanguage: readInlineWord('sourceLanguage', text) ?? readInlineWord('language', text),
147
+ target: readInlineWord('target', text) ?? readInlineWord('targetLanguage', text),
148
+ sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text),
149
+ sourceHash: readInlineWord('sourceHash', text),
150
+ outputHash: readInlineWord('outputHash', text),
151
+ telemetryHash: readInlineWord('telemetryHash', text),
152
+ subjectIds: readInlineList(text, 'subject', 'subjects', 'subjectId', 'subjectIds'),
153
+ semanticChangeIds: readInlineList(text, 'semanticChange', 'semanticChangeId', 'semanticChangeIds'),
154
+ semanticEditScriptIds: readInlineList(text, 'semanticEditScript', 'semanticEditScriptId', 'semanticEditScriptIds', 'script', 'scriptIds'),
155
+ semanticEditReplayIds: readInlineList(text, 'semanticEditReplay', 'semanticEditReplayId', 'semanticEditReplayIds', 'replay', 'replayIds'),
156
+ evidenceIds,
157
+ proofEvidenceIds: readInlineList(text, 'proofEvidence', 'proofEvidenceIds'),
158
+ missingEvidence: readInlineList(text, 'missingEvidence'),
159
+ reasonCodes: readInlineList(text, 'reasonCode', 'reasonCodes'),
160
+ failClosed: true,
161
+ claims: falseClaims(),
162
+ authoredText: authoredLine.text,
163
+ sourceSpan: authoredLine.sourceSpan,
164
+ authoredSourceSpan: authoredLine.sourceSpan,
165
+ metadata: cleanRecord({ authoredName: name, summary: readInlineQuoted('summary', text), ...falseClaims() })
166
+ });
167
+ }
168
+
169
+ function evidenceRecord(name, text, authoredLine, rowKind) {
170
+ return cleanRecord({
171
+ kind: 'frontier.lang.gateAdmissionEvidence.evidence',
172
+ version: 1,
173
+ id: idFrom(text, `evidence_${safeId(name)}`),
174
+ name,
175
+ evidenceKind: readInlineWord('kind', text) ?? 'evidence',
176
+ status: readInlineWord('status', text) ?? 'unknown',
177
+ proof: rowKind === 'proofEvidence' || readInlineFlag('proof', text),
178
+ path: readInlineWord('path', text),
179
+ hash: readInlineWord('hash', text),
180
+ command: readInlineQuoted('command', text) ?? readInlineWord('command', text),
181
+ probeId: readInlineWord('probeId', text) ?? readInlineWord('probe', text),
182
+ routeId: readInlineWord('route', text) ?? readInlineWord('routeId', text),
183
+ routeIds: readInlineList(text, 'routeIds'),
184
+ language: readInlineWord('language', text) ?? readInlineWord('sourceLanguage', text),
185
+ sourceLanguage: readInlineWord('sourceLanguage', text) ?? readInlineWord('language', text),
186
+ target: readInlineWord('target', text) ?? readInlineWord('targetLanguage', text),
187
+ sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text),
188
+ sourceHash: readInlineWord('sourceHash', text),
189
+ outputHash: readInlineWord('outputHash', text),
190
+ telemetryHash: readInlineWord('telemetryHash', text),
191
+ gateIds: readInlineList(text, 'gate', 'gateId', 'gateIds'),
192
+ admissionIds: readInlineList(text, 'admission', 'admissionId', 'admissionIds'),
193
+ summary: readInlineQuoted('summary', text),
194
+ failClosed: true,
195
+ autoMergeClaim: false,
196
+ semanticEquivalenceClaim: false,
197
+ runtimeEquivalenceClaim: false,
198
+ targetAdapterReadinessClaim: false,
199
+ authoredText: authoredLine.text,
200
+ sourceSpan: authoredLine.sourceSpan,
201
+ authoredSourceSpan: authoredLine.sourceSpan,
202
+ metadata: cleanRecord({ authoredName: name, authoredRowKind: rowKind, ...falseClaims() })
203
+ });
204
+ }
205
+
206
+ function admissionRecord(name, text, authoredLine) {
207
+ const status = readInlineWord('status', text) ?? readInlineWord('admissionStatus', text) ?? 'review';
208
+ return cleanRecord({
209
+ kind: 'frontier.lang.gateAdmissionEvidence.admission',
210
+ version: 1,
211
+ id: idFrom(text, `admission_${safeId(name)}`),
212
+ name,
213
+ status,
214
+ action: readInlineWord('action', text) ?? readInlineWord('admissionAction', text) ?? 'review',
215
+ readiness: readInlineWord('readiness', text) ?? readInlineWord('admissionReadiness', text),
216
+ decision: readInlineWord('decision', text),
217
+ classification: readInlineWord('classification', text),
218
+ routeId: readInlineWord('route', text) ?? readInlineWord('routeId', text),
219
+ routeIds: readInlineList(text, 'routeIds'),
220
+ language: readInlineWord('language', text) ?? readInlineWord('sourceLanguage', text),
221
+ sourceLanguage: readInlineWord('sourceLanguage', text) ?? readInlineWord('language', text),
222
+ target: readInlineWord('target', text) ?? readInlineWord('targetLanguage', text),
223
+ sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text),
224
+ baseHash: readInlineWord('baseHash', text),
225
+ targetHash: readInlineWord('targetHash', text),
226
+ gateIds: readInlineList(text, 'gate', 'gateId', 'gateIds'),
227
+ evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
228
+ proofEvidenceIds: readInlineList(text, 'proofEvidence', 'proofEvidenceIds'),
229
+ missingEvidence: readInlineList(text, 'missingEvidence'),
230
+ conflictKeys: readInlineList(text, 'conflictKey', 'conflictKeys'),
231
+ reasonCodes: readInlineList(text, 'reasonCode', 'reasonCodes', 'reason', 'reasons'),
232
+ semanticEditScriptIds: readInlineList(text, 'semanticEditScript', 'semanticEditScriptId', 'semanticEditScriptIds', 'script', 'scriptIds'),
233
+ semanticEditReplayIds: readInlineList(text, 'semanticEditReplay', 'semanticEditReplayId', 'semanticEditReplayIds', 'replay', 'replayIds'),
234
+ reviewRequired: true,
235
+ failClosed: true,
236
+ autoMergeClaim: false,
237
+ semanticEquivalenceClaim: false,
238
+ runtimeEquivalenceClaim: false,
239
+ targetAdapterReadinessClaim: false,
240
+ authoredText: authoredLine.text,
241
+ sourceSpan: authoredLine.sourceSpan,
242
+ authoredSourceSpan: authoredLine.sourceSpan,
243
+ metadata: cleanRecord({ authoredName: name, summary: readInlineQuoted('summary', text), ...falseClaims() })
244
+ });
245
+ }
246
+
247
+ function proofGapRecord(name, text, authoredLine) {
248
+ const code = readInlineWord('code', text) ?? name;
249
+ return cleanRecord({
250
+ kind: 'frontier.lang.gateAdmissionEvidence.proofGap',
251
+ version: 1,
252
+ id: idFrom(text, `gate_admission_gap_${safeId(code)}`),
253
+ name,
254
+ code,
255
+ status: readInlineWord('status', text) ?? 'missing',
256
+ routeId: readInlineWord('route', text) ?? readInlineWord('routeId', text),
257
+ routeIds: readInlineList(text, 'routeIds'),
258
+ gateIds: readInlineList(text, 'gate', 'gateId', 'gateIds'),
259
+ admissionIds: readInlineList(text, 'admission', 'admissionId', 'admissionIds'),
260
+ evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
261
+ summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
262
+ failClosed: true,
263
+ ...falseClaims(),
264
+ authoredText: authoredLine.text,
265
+ sourceSpan: authoredLine.sourceSpan,
266
+ authoredSourceSpan: authoredLine.sourceSpan,
267
+ metadata: cleanRecord({ authoredName: name, ...falseClaims() })
268
+ });
269
+ }
270
+
271
+ function readAuthoredLines(block) {
272
+ const lines = block.body.split('\n');
273
+ const records = [];
274
+ let lineStart = block.syntax?.bodyStartOffset ?? 0;
275
+ for (const rawLine of lines) {
276
+ const rawEnd = lineStart + rawLine.length;
277
+ const leading = /^\s*/.exec(rawLine)?.[0].length ?? 0;
278
+ const trailing = /\s*$/.exec(rawLine)?.[0].length ?? 0;
279
+ const startOffset = lineStart + leading;
280
+ const endOffset = Math.max(startOffset, rawEnd - trailing);
281
+ records.push({ text: rawLine.trim(), startOffset, endOffset, sourceSpan: typeof block.sourceSpan === 'function' ? block.sourceSpan(startOffset, endOffset) : undefined });
282
+ lineStart = rawEnd + 1;
283
+ }
284
+ return records;
285
+ }
286
+
287
+ function falseClaims() {
288
+ return { autoMergeClaim: false, semanticEquivalenceClaim: false, runtimeEquivalenceClaim: false, gatePassImpliesAdmissionClaim: false, targetAdapterReadinessClaim: false };
289
+ }
290
+
291
+ function ids(records = []) { return records.map((record) => record?.id).filter(Boolean); }
292
+ function uniqueRecords(records) {
293
+ const seen = new Set();
294
+ return records.filter((record) => {
295
+ const key = record?.id ?? JSON.stringify(record);
296
+ if (seen.has(key)) return false;
297
+ seen.add(key);
298
+ return true;
299
+ });
300
+ }
301
+ function uniqueStrings(values = []) { return [...new Set(values.filter(Boolean).map(String))]; }
302
+ function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
303
+ function nameFrom(header) { return /^([A-Za-z_$][\w$]*)/.exec(header)?.[1] ?? 'GateAdmissionEvidence'; }
304
+ function safeId(value) { return String(value ?? 'record').replace(/[^A-Za-z0-9_$-]+/g, '_'); }
305
+ function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(unquotedText(text))?.[1]?.trim(); }
306
+ function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
307
+ function readInlineFlag(label, text) { return new RegExp('(?:^|\\s)' + label + '(?:\\s|$)').test(unquotedText(text)) || undefined; }
308
+ function readInlineList(text, ...labels) {
309
+ const source = unquotedText(text);
310
+ for (const label of labels) {
311
+ const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(source)?.[1]?.trim();
312
+ if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
313
+ }
314
+ return undefined;
315
+ }
316
+ function unquotedText(text) { return text.replace(/"[^"]*"|'[^']*'/g, (match) => ' '.repeat(match.length)); }
317
+ function cleanRecord(record) {
318
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0) && !(value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)));
319
+ }
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { parseApplicationSurfaceBlock } from './application-surface.js';
6
6
  import { parseCanvasSurfaceBlock } from './canvas-surface.js';
7
7
  import { parseDecisionGraphBlock } from './decision-graph.js';
8
8
  import { parseDialectRegistryBlock } from './dialect-registry.js';
9
+ import { parseGateAdmissionEvidenceBlock } from './gate-admission-evidence.js';
9
10
  import { parseInterlinguaBlock } from './interlingua.js';
10
11
  import { createParsedMetadata } from './metadata.js';
11
12
  import { parseEntityBlock, parseStateBlock, readTypeFields } from './member-records.js';
@@ -34,6 +35,7 @@ export function parseFrontierSource(source, options = {}) {
34
35
  const conversionBlocks = [];
35
36
  const constraintSpaceBlocks = [];
36
37
  const decisionGraphBlocks = [];
38
+ const gateAdmissionEvidenceBlocks = [];
37
39
  const dialectRegistryBlocks = [];
38
40
  const interlinguaBlocks = [];
39
41
  const resourceGraphBlocks = [];
@@ -68,6 +70,7 @@ export function parseFrontierSource(source, options = {}) {
68
70
  if (block.kind === 'conversion' || block.kind === 'universalConversionPlan') conversionBlocks.push(parseConversionBlock(block));
69
71
  if (block.kind === 'constraintSpace' || block.kind === 'possibilitySpace') constraintSpaceBlocks.push(parseConstraintSpaceBlock(block));
70
72
  if (block.kind === 'decisionGraph' || block.kind === 'admissionGraph') decisionGraphBlocks.push(parseDecisionGraphBlock(block));
73
+ if (block.kind === 'gateEvidence' || block.kind === 'admissionEvidence' || block.kind === 'routeEvidence') gateAdmissionEvidenceBlocks.push(parseGateAdmissionEvidenceBlock(block));
71
74
  if (block.kind === 'dialectRegistry' || block.kind === 'universalDialectRegistry') dialectRegistryBlocks.push(parseDialectRegistryBlock(block));
72
75
  if (block.kind === 'interlingua' || block.kind === 'universalInterlingua') interlinguaBlocks.push(parseInterlinguaBlock(block));
73
76
  if (block.kind === 'resourceGraph' || block.kind === 'semanticResourceGraph') resourceGraphBlocks.push(parseResourceGraphBlock(block));
@@ -76,7 +79,7 @@ export function parseFrontierSource(source, options = {}) {
76
79
  if (block.kind === 'applicationSurface' || block.kind === 'appHost' || block.kind === 'plugin' || block.kind === 'pluginSurface' || block.kind === 'pluginContract') applicationSurfaceBlocks.push(parseApplicationSurfaceBlock(block));
77
80
  if (block.kind === 'runtimeCapabilities' || block.kind === 'runtimeCapabilityMatrix' || block.kind === 'runtimeHosts') runtimeCapabilityBlocks.push(parseRuntimeCapabilityBlock(block));
78
81
  }
79
- const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, semanticEditBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks, applicationSurfaceBlocks, runtimeCapabilityBlocks, targetProjectionTargets });
82
+ const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, semanticEditBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, gateAdmissionEvidenceBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks, applicationSurfaceBlocks, runtimeCapabilityBlocks, targetProjectionTargets });
80
83
  return createDocument({ id: documentId, name: documentName, nodes, ...(metadata ? { metadata } : {}) });
81
84
  }
82
85
 
package/dist/metadata.js CHANGED
@@ -4,6 +4,7 @@ import { mergeRuntimeCapabilityBlocks } from './runtime-capability.js';
4
4
  import { mergeTargetProjectionTargets } from './target-projection-aggregate.js';
5
5
  import { mergeConversionBlocks } from './conversion-metadata.js';
6
6
  import { mergeSemanticEditBlocks } from './semantic-edit-metadata.js';
7
+ import { mergeGateAdmissionEvidenceBlocks } from './gate-admission-evidence.js';
7
8
 
8
9
  const PROOF_GROUPS = ['contracts', 'refinements', 'invariants', 'termination', 'temporal', 'obligations', 'artifacts', 'assumptions'];
9
10
  const PARADIGM_GROUPS = [
@@ -12,7 +13,7 @@ const PARADIGM_GROUPS = [
12
13
  'clockModels', 'objectModels', 'macroExpansions', 'reflectionBoundaries', 'loweringRecords'
13
14
  ];
14
15
 
15
- export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], semanticEditBlocks = [], conversionBlocks = [], constraintSpaceBlocks = [], decisionGraphBlocks = [], dialectRegistryBlocks = [], interlinguaBlocks = [], resourceGraphBlocks = [], nativeSourceBlocks = [], packageManifestBlocks = [], canvasSurfaceBlocks = [], applicationSurfaceBlocks = [], runtimeCapabilityBlocks = [], targetProjectionTargets = [] } = {}) {
16
+ export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], semanticEditBlocks = [], conversionBlocks = [], constraintSpaceBlocks = [], decisionGraphBlocks = [], gateAdmissionEvidenceBlocks = [], dialectRegistryBlocks = [], interlinguaBlocks = [], resourceGraphBlocks = [], nativeSourceBlocks = [], packageManifestBlocks = [], canvasSurfaceBlocks = [], applicationSurfaceBlocks = [], runtimeCapabilityBlocks = [], targetProjectionTargets = [] } = {}) {
16
17
  const metadata = {};
17
18
  if (proofBlocks.length) metadata.proof = mergeProofBlocks(proofBlocks);
18
19
  if (paradigmBlocks.length) metadata.paradigmSemantics = mergeParadigmBlocks(paradigmBlocks);
@@ -21,6 +22,7 @@ export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], op
21
22
  if (conversionBlocks.length) metadata.universalConversionPlan = mergeConversionBlocks(conversionBlocks);
22
23
  if (constraintSpaceBlocks.length) metadata.constraintSpaces = mergeConstraintSpaceBlocks(constraintSpaceBlocks);
23
24
  if (decisionGraphBlocks.length) metadata.decisionGraph = mergeDecisionGraphBlocks(decisionGraphBlocks);
25
+ if (gateAdmissionEvidenceBlocks.length) metadata.gateAdmissionEvidence = mergeGateAdmissionEvidenceBlocks(gateAdmissionEvidenceBlocks);
24
26
  if (dialectRegistryBlocks.length) metadata.dialects = mergeDialectRegistryBlocks(dialectRegistryBlocks);
25
27
  if (interlinguaBlocks.length) metadata.universalInterlingua = mergeInterlinguaBlocks(interlinguaBlocks);
26
28
  if (resourceGraphBlocks.length) metadata.semanticResourceGraphs = mergeResourceGraphBlocks(resourceGraphBlocks);
@@ -24,6 +24,9 @@ export const FrontierSourceBlockKinds = Object.freeze([
24
24
  'possibilitySpace',
25
25
  'decisionGraph',
26
26
  'admissionGraph',
27
+ 'gateEvidence',
28
+ 'admissionEvidence',
29
+ 'routeEvidence',
27
30
  'dialectRegistry',
28
31
  'universalDialectRegistry',
29
32
  'interlingua',
@@ -227,9 +227,9 @@ function readGenericRowSyntaxChildren(source, block, options, config) {
227
227
  for (const line of readBodyLines(source, block)) {
228
228
  if (!line.text || line.text.startsWith('#')) continue;
229
229
  const row = rowPattern.exec(line.text);
230
- if (!row) continue;
230
+ if (!row) { if (config.childKind === 'gateAdmissionEvidenceRow') children.push(genericUnknownRowChild(source, block, options, line, config, undefined, undefined, 'unsupported-gate-admission-row')); continue; }
231
231
  const [, rowKind, name, rest] = row;
232
- if (!config.rowKinds.has(rowKind)) continue;
232
+ if (!config.rowKinds.has(rowKind)) { if (config.childKind === 'gateAdmissionEvidenceRow') children.push(genericUnknownRowChild(source, block, options, line, config, rowKind, name, 'unsupported-gate-admission-row')); continue; }
233
233
  const normalizedRowKind = config.normalize?.(rowKind) ?? rowKind;
234
234
  const id = idFrom(rest, `${config.idPrefix}_${safeId(normalizedRowKind)}_${safeId(name)}`);
235
235
  let recognized = true;
@@ -264,10 +264,19 @@ function readGenericRowSyntaxChildren(source, block, options, config) {
264
264
  return children;
265
265
  }
266
266
 
267
- function readBodyLines(source, block) {
268
- return readTextLines(source, block.bodyStartOffset, block.bodyEndOffset);
267
+ function genericUnknownRowChild(source, block, options, line, config, rowKind, name, reason) {
268
+ const resolvedName = name ?? rowKind ?? 'unknown';
269
+ return cleanRecord({
270
+ kind: config.childKind === 'gateAdmissionEvidenceRow' ? 'gateAdmissionUnknownRow' : 'genericUnknownRow', rowKind,
271
+ normalizedRowKind: 'unknown', name: resolvedName, id: `gate_admission_unknown_${safeId(rowKind ?? 'row')}_${line.startOffset}`,
272
+ header: line.text, startOffset: line.startOffset, endOffset: line.endOffset, location: sourcePosition(source, line.startOffset),
273
+ parentKind: block.kind, parentId: block.id, parentName: block.name, moduleId: block.moduleId, moduleName: block.moduleName,
274
+ sourceSpan: sourceSpan(source, block, line.startOffset, line.endOffset, options), recognized: false, reason
275
+ });
269
276
  }
270
277
 
278
+ function readBodyLines(source, block) { return readTextLines(source, block.bodyStartOffset, block.bodyEndOffset); }
279
+
271
280
  function readTextLines(source, startOffset, endOffset) {
272
281
  const body = source.slice(startOffset, endOffset);
273
282
  const lines = body.split('\n');
@@ -6,6 +6,7 @@ const interlinguaRows = words('layer constraint edge obligation proofObligation
6
6
  const packageRows = words('metadata dependency script export gap proofGap evidence proofEvidence');
7
7
  const runtimeRows = words('host runtimeHost hostProfile sourceHost targetHost capability hostCapability hostBinding binding requirement runtimeRequirement requiredRuntime evidence proofEvidence gap proofGap');
8
8
  const semanticEditRows = words('script semanticEditScript projection semanticEditProjection replay semanticEditReplay');
9
+ const gateAdmissionRows = words('gate evidence proofEvidence admission admissionDecision proofObligation obligation gap proofGap');
9
10
 
10
11
  export const ROW_SYNTAX_CONFIG = Object.freeze({
11
12
  interlingua: rowConfig('interlinguaRow', 'interlingua_row', interlinguaRows, normalizeInterlinguaRow),
@@ -32,6 +33,9 @@ export const ROW_SYNTAX_CONFIG = Object.freeze({
32
33
  possibilitySpace: rowConfig('constraintSpaceRow', 'constraint_space_row', constraintRows, normalizeConstraintSpaceRow),
33
34
  decisionGraph: rowConfig('decisionGraphRow', 'decision_graph_row', words('node edge chunk gate evidence semanticChange change patchEvent patch admissionDecision admission candidateDecision candidate mergeDecision merge replay tournament tournamentCandidate panelProjection panel rsiLoop improvementFeedback feedback'), normalizeDecisionGraphRow),
34
35
  admissionGraph: rowConfig('decisionGraphRow', 'decision_graph_row', words('node edge chunk gate evidence semanticChange change patchEvent patch admissionDecision admission candidateDecision candidate mergeDecision merge replay tournament tournamentCandidate panelProjection panel rsiLoop improvementFeedback feedback'), normalizeDecisionGraphRow),
36
+ gateEvidence: rowConfig('gateAdmissionEvidenceRow', 'gate_admission_evidence_row', gateAdmissionRows, normalizeGateAdmissionRow),
37
+ admissionEvidence: rowConfig('gateAdmissionEvidenceRow', 'gate_admission_evidence_row', gateAdmissionRows, normalizeGateAdmissionRow),
38
+ routeEvidence: rowConfig('gateAdmissionEvidenceRow', 'gate_admission_evidence_row', gateAdmissionRows, normalizeGateAdmissionRow),
35
39
  operations: rowConfig('semanticOperationRow', 'semantic_operation_row', words('operation op'), normalizeOperationRow),
36
40
  semanticOperations: rowConfig('semanticOperationRow', 'semantic_operation_row', words('operation op'), normalizeOperationRow),
37
41
  semanticEdits: rowConfig('semanticEditRecordRow', 'semantic_edit_record_row', semanticEditRows, normalizeSemanticEditRow),
@@ -128,6 +132,14 @@ function normalizeSemanticEditRow(rowKind) {
128
132
  return rowKind;
129
133
  }
130
134
 
135
+ function normalizeGateAdmissionRow(rowKind) {
136
+ if (rowKind === 'proofEvidence') return 'evidence';
137
+ if (rowKind === 'admissionDecision') return 'admission';
138
+ if (rowKind === 'obligation') return 'proofObligation';
139
+ if (rowKind === 'gap') return 'proofGap';
140
+ return rowKind;
141
+ }
142
+
131
143
  function normalizeParadigmRow(rowKind) {
132
144
  if (rowKind === 'ownership') return 'ownershipModel';
133
145
  if (rowKind === 'lifetime') return 'lifetimeModel';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-parser",
3
- "version": "0.3.69",
3
+ "version": "0.3.71",
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/member-identity-smoke.mjs && node test/generic-row-parse-identity-smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/semantic-operation-edit-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
25
+ "test": "npm run build && node test/smoke.mjs && node test/member-identity-smoke.mjs && node test/generic-row-parse-identity-smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/semantic-operation-edit-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/gate-admission-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
26
26
  "typecheck": "node ./node_modules/typescript/bin/tsc --noEmit -p test/tsconfig.json",
27
27
  "fuzz": "npm run build && node fuzz/smoke.mjs",
28
28
  "bench": "npm run build && node bench/smoke.mjs",