@shapeshift-labs/frontier-lang-compiler 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -2
- package/bench/smoke.mjs +13 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.js +903 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -67,33 +67,57 @@ const semanticMergeReadinessRank = Object.freeze({
|
|
|
67
67
|
blocked: 3
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
export const NativeImportRoundtripReadinessStatuses = Object.freeze([
|
|
71
|
+
'exact',
|
|
72
|
+
'preserved-source',
|
|
73
|
+
'stub-only',
|
|
74
|
+
'blocked',
|
|
75
|
+
'needs-review'
|
|
76
|
+
]);
|
|
77
|
+
|
|
70
78
|
export const NativeImportTaxonomyKinds = Object.freeze([
|
|
71
79
|
'exactAstImport',
|
|
72
80
|
'declarationsOnly',
|
|
73
81
|
'opaqueBodies',
|
|
74
82
|
'macroExpansion',
|
|
75
83
|
'preprocessor',
|
|
84
|
+
'conditionalCompilation',
|
|
76
85
|
'metaprogramming',
|
|
86
|
+
'reflection',
|
|
77
87
|
'generatedCode',
|
|
88
|
+
'overloadTypeInference',
|
|
78
89
|
'sourcePreservation',
|
|
90
|
+
'commentsTrivia',
|
|
79
91
|
'parserDiagnostics',
|
|
80
92
|
'unsupportedSyntax',
|
|
81
93
|
'partialSemanticIndex',
|
|
82
|
-
'sourceMapApproximation'
|
|
94
|
+
'sourceMapApproximation',
|
|
95
|
+
'targetProjectionLoss'
|
|
83
96
|
]);
|
|
84
97
|
|
|
85
98
|
export const NativeImportLossKinds = Object.freeze([
|
|
86
99
|
'declarationOnlyCoverage',
|
|
87
100
|
'opaqueNative',
|
|
88
101
|
'macroExpansion',
|
|
102
|
+
'macroHygiene',
|
|
89
103
|
'preprocessor',
|
|
104
|
+
'conditionalCompilation',
|
|
90
105
|
'metaprogramming',
|
|
106
|
+
'reflection',
|
|
107
|
+
'dynamicRuntime',
|
|
108
|
+
'dynamicDispatch',
|
|
91
109
|
'generatedCode',
|
|
110
|
+
'overloadResolution',
|
|
111
|
+
'typeInference',
|
|
92
112
|
'sourcePreservation',
|
|
113
|
+
'commentsTrivia',
|
|
93
114
|
'parserDiagnostic',
|
|
94
115
|
'unsupportedSyntax',
|
|
116
|
+
'unsupportedSemantic',
|
|
117
|
+
'unverifiedNativeAst',
|
|
95
118
|
'partialSemanticIndex',
|
|
96
|
-
'sourceMapApproximation'
|
|
119
|
+
'sourceMapApproximation',
|
|
120
|
+
'targetProjectionLoss'
|
|
97
121
|
]);
|
|
98
122
|
|
|
99
123
|
export const NativeImportReadinessBySeverity = Object.freeze({
|
|
@@ -312,6 +336,134 @@ export function classifyNativeImportReadiness(losses = [], options = {}) {
|
|
|
312
336
|
};
|
|
313
337
|
}
|
|
314
338
|
|
|
339
|
+
export function classifyNativeImportRoundtripReadiness(importResult, options = {}) {
|
|
340
|
+
if (!importResult || typeof importResult !== 'object') {
|
|
341
|
+
throw new Error('classifyNativeImportRoundtripReadiness requires a native import result');
|
|
342
|
+
}
|
|
343
|
+
const imports = nativeImportEntries(importResult);
|
|
344
|
+
const importLosses = uniqueByLossId([
|
|
345
|
+
...(importResult.losses ?? []),
|
|
346
|
+
...imports.flatMap((imported) => imported?.losses ?? [])
|
|
347
|
+
]);
|
|
348
|
+
const importEvidence = uniqueByEvidenceId([
|
|
349
|
+
...(importResult.evidence ?? []),
|
|
350
|
+
...imports.flatMap((imported) => imported?.evidence ?? [])
|
|
351
|
+
]);
|
|
352
|
+
const exactAst = imports.length > 0 && imports.every((imported) => nativeImportHasExactAstCoverage(imported));
|
|
353
|
+
const importReadiness = classifyNativeImportReadiness(importLosses, {
|
|
354
|
+
exactAst,
|
|
355
|
+
evidence: importEvidence,
|
|
356
|
+
parser: nativeImportRoundtripParser(importResult, imports),
|
|
357
|
+
scanKind: importResult.metadata?.nativeImportLossSummary?.scanKind,
|
|
358
|
+
semanticStatus: importResult.metadata?.semanticStatus ?? importResult.universalAst?.metadata?.semanticStatus
|
|
359
|
+
});
|
|
360
|
+
const projection = options.projection ?? projectNativeImportToSource(importResult, options);
|
|
361
|
+
const projectionReadiness = projection.readiness ?? classifyNativeImportReadiness(projection.losses ?? [], {
|
|
362
|
+
evidence: projection.evidence,
|
|
363
|
+
parser: projection.metadata?.nativeImportLossSummary?.parser,
|
|
364
|
+
scanKind: 'native-source-projection'
|
|
365
|
+
});
|
|
366
|
+
const universalAst = importResult.universalAst;
|
|
367
|
+
const universalAstIssues = universalAst
|
|
368
|
+
? validateUniversalAstEnvelope(universalAst)
|
|
369
|
+
: ['missing-universal-ast'];
|
|
370
|
+
const universalAstNativeSources = universalAst?.nativeSources?.length ?? importResult.nativeSources?.length ?? (importResult.nativeSource ? 1 : 0);
|
|
371
|
+
const semanticIndex = importResult.semanticIndex ?? universalAst?.semanticIndex;
|
|
372
|
+
const semanticSymbols = semanticIndex?.symbols?.length ?? 0;
|
|
373
|
+
const sourceMaps = importResult.sourceMaps ?? universalAst?.sourceMaps ?? [];
|
|
374
|
+
const sourceMapMappings = sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap?.mappings?.length ?? 0), 0);
|
|
375
|
+
const projectionMatchesSourceHash = Boolean(projection.sourceHash && projection.outputHash === projection.sourceHash);
|
|
376
|
+
const preservedSource = projection.mode === 'preserved-source';
|
|
377
|
+
const failedEvidenceIds = uniqueStrings([
|
|
378
|
+
...importEvidence.filter((record) => record?.status === 'failed').map((record) => record.id),
|
|
379
|
+
...(projection.evidence ?? []).filter((record) => record?.status === 'failed').map((record) => record.id)
|
|
380
|
+
]);
|
|
381
|
+
const blockingReasons = [
|
|
382
|
+
...(importReadiness.readiness === 'blocked' ? importReadiness.reasons : []),
|
|
383
|
+
...(projectionReadiness.readiness === 'blocked' ? projectionReadiness.reasons : []),
|
|
384
|
+
...(failedEvidenceIds.length ? [`Failed evidence prevents native roundtrip readiness: ${failedEvidenceIds.join(', ')}`] : []),
|
|
385
|
+
...(universalAstIssues.length ? [`Universal AST validation failed: ${universalAstIssues.join('; ')}`] : [])
|
|
386
|
+
];
|
|
387
|
+
const reviewReasons = [
|
|
388
|
+
...(semanticSymbols === 0 ? ['Universal AST semantic index has no symbols for source projection review.'] : []),
|
|
389
|
+
...(sourceMapMappings === 0 ? ['Universal AST has no native source-map mappings for roundtrip review.'] : []),
|
|
390
|
+
...(preservedSource && !projectionMatchesSourceHash ? ['Projected source was preserved without a verified import source hash match.'] : []),
|
|
391
|
+
...importReadiness.reasons.filter((reason) => importReadiness.readiness !== 'ready' || !exactAst),
|
|
392
|
+
...projectionReadiness.reasons.filter((reason) => projectionReadiness.readiness !== 'ready')
|
|
393
|
+
];
|
|
394
|
+
let status;
|
|
395
|
+
if (blockingReasons.length) {
|
|
396
|
+
status = 'blocked';
|
|
397
|
+
} else if (projection.mode === 'native-source-stubs') {
|
|
398
|
+
status = 'stub-only';
|
|
399
|
+
} else if (reviewReasons.some((reason) => reason.startsWith('Universal AST')) || (preservedSource && !projectionMatchesSourceHash)) {
|
|
400
|
+
status = 'needs-review';
|
|
401
|
+
} else if (exactAst && preservedSource && projectionMatchesSourceHash && projectionReadiness.readiness === 'ready') {
|
|
402
|
+
status = 'exact';
|
|
403
|
+
} else if (preservedSource && projectionMatchesSourceHash) {
|
|
404
|
+
status = 'preserved-source';
|
|
405
|
+
} else {
|
|
406
|
+
status = 'needs-review';
|
|
407
|
+
}
|
|
408
|
+
const reasons = nativeImportRoundtripReasons(status, {
|
|
409
|
+
blockingReasons,
|
|
410
|
+
reviewReasons,
|
|
411
|
+
projection,
|
|
412
|
+
importReadiness,
|
|
413
|
+
projectionReadiness
|
|
414
|
+
});
|
|
415
|
+
return {
|
|
416
|
+
kind: 'frontier.lang.nativeImportRoundtripReadiness',
|
|
417
|
+
version: 1,
|
|
418
|
+
status,
|
|
419
|
+
semanticMergeReadiness: maxSemanticMergeReadiness(importReadiness.readiness, projectionReadiness.readiness),
|
|
420
|
+
reasons,
|
|
421
|
+
importReadiness,
|
|
422
|
+
projectionReadiness,
|
|
423
|
+
projectionMode: projection.mode,
|
|
424
|
+
checks: {
|
|
425
|
+
nativeImport: {
|
|
426
|
+
imports: imports.length,
|
|
427
|
+
exactAst,
|
|
428
|
+
losses: importReadiness.summary.total,
|
|
429
|
+
readiness: importReadiness.readiness
|
|
430
|
+
},
|
|
431
|
+
universalAst: {
|
|
432
|
+
present: Boolean(universalAst),
|
|
433
|
+
valid: universalAstIssues.length === 0,
|
|
434
|
+
issues: universalAstIssues,
|
|
435
|
+
nativeSources: universalAstNativeSources,
|
|
436
|
+
semanticSymbols,
|
|
437
|
+
sourceMaps: sourceMaps.length,
|
|
438
|
+
sourceMapMappings
|
|
439
|
+
},
|
|
440
|
+
projectedSource: {
|
|
441
|
+
mode: projection.mode,
|
|
442
|
+
outputHash: projection.outputHash,
|
|
443
|
+
expectedSourceHash: projection.sourceHash,
|
|
444
|
+
sourceHashVerified: projectionMatchesSourceHash,
|
|
445
|
+
declarations: projection.declarations?.length ?? 0,
|
|
446
|
+
losses: projection.lossSummary?.total ?? projection.losses?.length ?? 0,
|
|
447
|
+
readiness: projectionReadiness.readiness
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
evidence: {
|
|
451
|
+
importEvidenceIds: importEvidence.map((record) => record.id).filter(Boolean),
|
|
452
|
+
projectionEvidenceIds: (projection.evidence ?? []).map((record) => record.id).filter(Boolean),
|
|
453
|
+
failedEvidenceIds
|
|
454
|
+
},
|
|
455
|
+
metadata: {
|
|
456
|
+
nativeImportId: importResult.id,
|
|
457
|
+
universalAstId: universalAst?.id,
|
|
458
|
+
projectionId: projection.id,
|
|
459
|
+
sourcePath: projection.sourcePath ?? importResult.sourcePath,
|
|
460
|
+
language: projection.language ?? importResult.language,
|
|
461
|
+
sourcePreservationId: projection.metadata?.sourcePreservationId,
|
|
462
|
+
...options.metadata
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
315
467
|
export function createNativeImportCoverageMatrix(input = {}) {
|
|
316
468
|
const imports = input.imports ?? [];
|
|
317
469
|
const adapters = input.adapters ?? [];
|
|
@@ -356,6 +508,70 @@ export function createNativeImportCoverageMatrix(input = {}) {
|
|
|
356
508
|
};
|
|
357
509
|
}
|
|
358
510
|
|
|
511
|
+
export function createNativeSourcePreservation(options) {
|
|
512
|
+
if (!options || typeof options.sourceText !== 'string') {
|
|
513
|
+
throw new Error('createNativeSourcePreservation requires sourceText');
|
|
514
|
+
}
|
|
515
|
+
const language = options.language ?? 'source';
|
|
516
|
+
const sourceText = options.sourceText;
|
|
517
|
+
const computedSourceHash = hashSemanticValue(sourceText);
|
|
518
|
+
const declaredSourceHash = options.sourceHash;
|
|
519
|
+
const sourceHash = computedSourceHash;
|
|
520
|
+
const tokensAndTrivia = scanPreservedSourceTokens(sourceText, {
|
|
521
|
+
language,
|
|
522
|
+
sourcePath: options.sourcePath,
|
|
523
|
+
sourceHash,
|
|
524
|
+
includeTokens: options.includeTokens !== false,
|
|
525
|
+
includeTrivia: options.includeTrivia !== false,
|
|
526
|
+
maxTokens: options.maxTokens,
|
|
527
|
+
maxTrivia: options.maxTrivia
|
|
528
|
+
});
|
|
529
|
+
const directiveScan = options.includeDirectives === false
|
|
530
|
+
? { directives: [], truncated: false }
|
|
531
|
+
: scanPreservedSourceDirectives(sourceText, {
|
|
532
|
+
language,
|
|
533
|
+
sourcePath: options.sourcePath,
|
|
534
|
+
sourceHash,
|
|
535
|
+
maxDirectives: options.maxDirectives
|
|
536
|
+
});
|
|
537
|
+
const directives = directiveScan.directives;
|
|
538
|
+
const newline = detectNewlineStyle(sourceText);
|
|
539
|
+
return {
|
|
540
|
+
kind: 'frontier.lang.nativeSourcePreservation',
|
|
541
|
+
version: 1,
|
|
542
|
+
id: options.id ?? `native_source_preservation_${idFragment(options.sourcePath ?? language)}_${idFragment(sourceHash)}`,
|
|
543
|
+
language,
|
|
544
|
+
sourcePath: options.sourcePath,
|
|
545
|
+
sourceHash,
|
|
546
|
+
sourceBytes: Buffer.byteLength(sourceText, options.encoding ?? 'utf8'),
|
|
547
|
+
lineCount: sourceText.length ? sourceText.split(/\r\n|\r|\n/).length : 0,
|
|
548
|
+
newline,
|
|
549
|
+
encoding: options.encoding ?? 'utf8',
|
|
550
|
+
...(options.includeSourceText === false ? {} : { sourceText }),
|
|
551
|
+
tokens: tokensAndTrivia.tokens,
|
|
552
|
+
trivia: tokensAndTrivia.trivia,
|
|
553
|
+
directives,
|
|
554
|
+
summary: {
|
|
555
|
+
tokens: tokensAndTrivia.tokens.length,
|
|
556
|
+
trivia: tokensAndTrivia.trivia.length,
|
|
557
|
+
directives: directives.length,
|
|
558
|
+
comments: tokensAndTrivia.trivia.filter((entry) => entry.kind === 'comment').length,
|
|
559
|
+
whitespace: tokensAndTrivia.trivia.filter((entry) => entry.kind === 'whitespace' || entry.kind === 'newline').length,
|
|
560
|
+
exactSourceAvailable: options.includeSourceText !== false,
|
|
561
|
+
truncated: tokensAndTrivia.truncated || directiveScan.truncated
|
|
562
|
+
},
|
|
563
|
+
metadata: {
|
|
564
|
+
preservation: 'source-text-token-trivia-directive-evidence',
|
|
565
|
+
tokenization: 'frontier-lightweight-lexical-scan',
|
|
566
|
+
...(declaredSourceHash ? {
|
|
567
|
+
declaredSourceHash,
|
|
568
|
+
sourceHashVerified: declaredSourceHash === computedSourceHash
|
|
569
|
+
} : {}),
|
|
570
|
+
...options.metadata
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
359
575
|
export function createSemanticImportSidecar(importResult, options = {}) {
|
|
360
576
|
const imports = Array.isArray(importResult?.imports) ? importResult.imports : [importResult].filter(Boolean);
|
|
361
577
|
const importEntries = imports.map((imported, index) => semanticImportSidecarEntry(imported, index, options));
|
|
@@ -458,6 +674,20 @@ export function createTypeScriptCompilerNativeImporterAdapter(options = {}) {
|
|
|
458
674
|
parser: options.parser ?? 'typescript-compiler-api',
|
|
459
675
|
version: options.version,
|
|
460
676
|
capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
|
|
677
|
+
coverage: nativeImporterAdapterCoverage({
|
|
678
|
+
exactness: 'exact-parser-ast',
|
|
679
|
+
exactAst: true,
|
|
680
|
+
tokens: false,
|
|
681
|
+
trivia: false,
|
|
682
|
+
diagnostics: true,
|
|
683
|
+
sourceRanges: true,
|
|
684
|
+
generatedRanges: false,
|
|
685
|
+
semanticCoverage: declarationSemanticCoverage(),
|
|
686
|
+
notes: [
|
|
687
|
+
'Normalizes a caller-owned TypeScript SourceFile into native AST nodes and declaration-level semantic index records.',
|
|
688
|
+
'Type resolution, reference resolution, control flow, generated ranges, and parser token/trivia streams require host-supplied adapter evidence.'
|
|
689
|
+
]
|
|
690
|
+
}, options.coverage),
|
|
461
691
|
supportedExtensions: options.supportedExtensions ?? ['.ts', '.tsx', '.js', '.jsx'],
|
|
462
692
|
diagnostics: options.diagnostics,
|
|
463
693
|
parse(input) {
|
|
@@ -488,6 +718,20 @@ export function createTreeSitterNativeImporterAdapter(options = {}) {
|
|
|
488
718
|
parser: options.parserName ?? options.parser ?? 'tree-sitter',
|
|
489
719
|
version: options.version,
|
|
490
720
|
capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
|
|
721
|
+
coverage: nativeImporterAdapterCoverage({
|
|
722
|
+
exactness: 'parser-tree',
|
|
723
|
+
exactAst: true,
|
|
724
|
+
tokens: false,
|
|
725
|
+
trivia: false,
|
|
726
|
+
diagnostics: true,
|
|
727
|
+
sourceRanges: true,
|
|
728
|
+
generatedRanges: false,
|
|
729
|
+
semanticCoverage: declarationSemanticCoverage(),
|
|
730
|
+
notes: [
|
|
731
|
+
'Normalizes a caller-owned tree-sitter tree into native AST nodes and declaration-level semantic index records.',
|
|
732
|
+
'The built-in wrapper walks named syntax nodes; exact token/trivia streams and generated ranges require adapter-specific evidence.'
|
|
733
|
+
]
|
|
734
|
+
}, options.coverage),
|
|
491
735
|
supportedExtensions: options.supportedExtensions ?? [],
|
|
492
736
|
diagnostics: options.diagnostics,
|
|
493
737
|
parse(input) {
|
|
@@ -592,7 +836,14 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
592
836
|
parseResult.losses,
|
|
593
837
|
diagnostics.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, summary, parseInput))
|
|
594
838
|
);
|
|
595
|
-
const
|
|
839
|
+
const adapterSummary = {
|
|
840
|
+
...summary,
|
|
841
|
+
coverage: observeNativeImporterAdapterCoverage(summary.coverage, parseResult, {
|
|
842
|
+
diagnostics,
|
|
843
|
+
losses
|
|
844
|
+
})
|
|
845
|
+
};
|
|
846
|
+
const sourceEvidence = adapterDiagnosticsEvidence(adapterSummary, diagnostics, {
|
|
596
847
|
language,
|
|
597
848
|
parser,
|
|
598
849
|
parserVersion,
|
|
@@ -614,8 +865,9 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
614
865
|
metadata: {
|
|
615
866
|
adapterId: summary.id,
|
|
616
867
|
adapterVersion: summary.version,
|
|
617
|
-
adapterCapabilities:
|
|
618
|
-
|
|
868
|
+
adapterCapabilities: adapterSummary.capabilities,
|
|
869
|
+
adapterCoverage: adapterSummary.coverage,
|
|
870
|
+
supportedExtensions: adapterSummary.supportedExtensions,
|
|
619
871
|
diagnostics: diagnostics.map(serializableDiagnostic),
|
|
620
872
|
...input.metadata,
|
|
621
873
|
...parseResult.metadata
|
|
@@ -624,6 +876,7 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
624
876
|
adapterId: summary.id,
|
|
625
877
|
adapterVersion: summary.version,
|
|
626
878
|
parser,
|
|
879
|
+
adapterCoverage: adapterSummary.coverage,
|
|
627
880
|
...input.nativeAstMetadata,
|
|
628
881
|
...parseResult.nativeAstMetadata
|
|
629
882
|
},
|
|
@@ -631,6 +884,7 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
631
884
|
adapterId: summary.id,
|
|
632
885
|
adapterVersion: summary.version,
|
|
633
886
|
parser,
|
|
887
|
+
adapterCoverage: adapterSummary.coverage,
|
|
634
888
|
...input.nativeSourceMetadata,
|
|
635
889
|
...parseResult.nativeSourceMetadata
|
|
636
890
|
},
|
|
@@ -649,7 +903,7 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
649
903
|
};
|
|
650
904
|
return {
|
|
651
905
|
...importNativeSource(importInput),
|
|
652
|
-
adapter:
|
|
906
|
+
adapter: adapterSummary,
|
|
653
907
|
diagnostics
|
|
654
908
|
};
|
|
655
909
|
}
|
|
@@ -681,6 +935,7 @@ export function projectNativeImportToSource(importResult, options = {}) {
|
|
|
681
935
|
sourcePath: context.sourcePath,
|
|
682
936
|
expectedSourceHash: context.sourceHash,
|
|
683
937
|
providedSourceHash: candidateSource?.sourceHash,
|
|
938
|
+
sourcePreservationId: candidateSource?.sourcePreservationId,
|
|
684
939
|
sourceHashVerified: candidateSource?.hashVerified ?? false,
|
|
685
940
|
declarationCount: declarations.length
|
|
686
941
|
}
|
|
@@ -725,6 +980,7 @@ export function projectNativeImportToSource(importResult, options = {}) {
|
|
|
725
980
|
universalAstId: importResult.universalAst?.id,
|
|
726
981
|
exactSourceAvailable: candidateSource?.exact === true,
|
|
727
982
|
sourceTextAvailable: typeof candidateSource?.sourceText === 'string',
|
|
983
|
+
sourcePreservationId: candidateSource?.sourcePreservationId,
|
|
728
984
|
sourceHashVerified: candidateSource?.hashVerified ?? false,
|
|
729
985
|
nativeImportLossSummary,
|
|
730
986
|
...options.metadata
|
|
@@ -736,17 +992,30 @@ export function importNativeSource(input) {
|
|
|
736
992
|
const language = input.language ?? input.nativeAst?.language;
|
|
737
993
|
if (!language) throw new Error('importNativeSource requires a language or nativeAst.language');
|
|
738
994
|
const sourcePath = input.sourcePath ?? input.nativeAst?.sourcePath;
|
|
739
|
-
const
|
|
995
|
+
const declaredSourceHash = input.sourceHash ?? input.nativeAst?.sourceHash;
|
|
996
|
+
const sourceHash = typeof input.sourceText === 'string'
|
|
997
|
+
? hashSemanticValue(input.sourceText)
|
|
998
|
+
: declaredSourceHash ?? hashSemanticValue(input.nativeAst?.nodes ?? input.nativeAst ?? {});
|
|
740
999
|
const targetPath = input.targetPath ?? input.target?.emitPath;
|
|
741
1000
|
const targetHash = input.targetHash;
|
|
742
1001
|
const importIdPart = idFragment(input.id ?? input.nativeSourceId ?? sourcePath ?? language);
|
|
1002
|
+
const sourcePreservation = input.sourcePreservation ?? (typeof input.sourceText === 'string'
|
|
1003
|
+
? createNativeSourcePreservation({
|
|
1004
|
+
language,
|
|
1005
|
+
sourcePath,
|
|
1006
|
+
sourceHash: declaredSourceHash,
|
|
1007
|
+
sourceText: input.sourceText,
|
|
1008
|
+
metadata: { importIdPart }
|
|
1009
|
+
})
|
|
1010
|
+
: undefined);
|
|
743
1011
|
const lightweight = !input.nativeAst && !input.nodes && input.sourceText
|
|
744
1012
|
? createLightweightNativeImport({
|
|
745
1013
|
language,
|
|
746
1014
|
sourceText: input.sourceText,
|
|
747
1015
|
sourcePath,
|
|
748
1016
|
sourceHash,
|
|
749
|
-
parser: input.parser
|
|
1017
|
+
parser: input.parser,
|
|
1018
|
+
sourcePreservation
|
|
750
1019
|
})
|
|
751
1020
|
: undefined;
|
|
752
1021
|
const nativeAst = input.nativeAst ?? createNativeAstRecord({
|
|
@@ -769,6 +1038,15 @@ export function importNativeSource(input) {
|
|
|
769
1038
|
losses: input.losses ?? lightweight?.losses,
|
|
770
1039
|
metadata: {
|
|
771
1040
|
...(input.sourceText ? { sourceBytes: input.sourceText.length } : {}),
|
|
1041
|
+
...(sourcePreservation ? {
|
|
1042
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1043
|
+
sourcePreservationSummary: sourcePreservation.summary,
|
|
1044
|
+
sourcePreservation
|
|
1045
|
+
} : {}),
|
|
1046
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1047
|
+
declaredSourceHash,
|
|
1048
|
+
sourceHashVerified: false
|
|
1049
|
+
} : {}),
|
|
772
1050
|
...lightweight?.metadata,
|
|
773
1051
|
...input.nativeAstMetadata
|
|
774
1052
|
}
|
|
@@ -776,7 +1054,17 @@ export function importNativeSource(input) {
|
|
|
776
1054
|
const frontierNodeIds = input.frontierNodeIds ?? input.semanticNodes?.map((node) => node.id) ?? [];
|
|
777
1055
|
const semanticNodes = input.semanticNodes ?? [];
|
|
778
1056
|
const semanticStatus = input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only');
|
|
779
|
-
const
|
|
1057
|
+
const nativeAstExact = hasNativeExactAstEvidence(input, nativeAst, lightweight);
|
|
1058
|
+
const baseLosses = normalizeNativeLossRecords(input.losses ?? nativeAst.losses ?? lightweight?.losses ?? []);
|
|
1059
|
+
const losses = normalizeNativeLossRecords([
|
|
1060
|
+
...baseLosses,
|
|
1061
|
+
...unverifiedNativeAstLosses(input, nativeAst, {
|
|
1062
|
+
importIdPart,
|
|
1063
|
+
exactAst: nativeAstExact,
|
|
1064
|
+
hasLosses: baseLosses.length > 0,
|
|
1065
|
+
lightweight
|
|
1066
|
+
})
|
|
1067
|
+
]);
|
|
780
1068
|
const nativeSource = nativeSourceNode({
|
|
781
1069
|
id: input.nativeSourceId ?? `native_source_${importIdPart}`,
|
|
782
1070
|
name: input.name ?? sourcePath?.split(/[\\/]/).filter(Boolean).at(-1) ?? `${language}NativeSource`,
|
|
@@ -793,6 +1081,14 @@ export function importNativeSource(input) {
|
|
|
793
1081
|
metadata: {
|
|
794
1082
|
semanticStatus,
|
|
795
1083
|
mappings: input.mappings ?? [],
|
|
1084
|
+
...(sourcePreservation ? {
|
|
1085
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1086
|
+
sourcePreservation
|
|
1087
|
+
} : {}),
|
|
1088
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1089
|
+
declaredSourceHash,
|
|
1090
|
+
sourceHashVerified: false
|
|
1091
|
+
} : {}),
|
|
796
1092
|
...input.nativeSourceMetadata
|
|
797
1093
|
}
|
|
798
1094
|
});
|
|
@@ -816,11 +1112,20 @@ export function importNativeSource(input) {
|
|
|
816
1112
|
metadata: {
|
|
817
1113
|
parser: nativeAst.parser,
|
|
818
1114
|
sourcePath,
|
|
819
|
-
semanticStatus
|
|
1115
|
+
semanticStatus,
|
|
1116
|
+
...(sourcePreservation ? {
|
|
1117
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1118
|
+
sourcePreservationSummary: sourcePreservation.summary
|
|
1119
|
+
} : {})
|
|
1120
|
+
,
|
|
1121
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1122
|
+
declaredSourceHash,
|
|
1123
|
+
sourceHashVerified: false
|
|
1124
|
+
} : {})
|
|
820
1125
|
}
|
|
821
1126
|
}];
|
|
822
1127
|
const lossSummary = summarizeNativeImportLosses(losses, {
|
|
823
|
-
exactAst:
|
|
1128
|
+
exactAst: nativeAstExact,
|
|
824
1129
|
evidence: baseEvidence,
|
|
825
1130
|
parser: nativeAst.parser,
|
|
826
1131
|
scanKind: lightweight?.metadata?.scanKind,
|
|
@@ -896,6 +1201,14 @@ export function importNativeSource(input) {
|
|
|
896
1201
|
sourcePath,
|
|
897
1202
|
semanticStatus,
|
|
898
1203
|
nativeImportLossSummary: lossSummary,
|
|
1204
|
+
...(sourcePreservation ? {
|
|
1205
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1206
|
+
sourcePreservation
|
|
1207
|
+
} : {}),
|
|
1208
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1209
|
+
declaredSourceHash,
|
|
1210
|
+
sourceHashVerified: false
|
|
1211
|
+
} : {}),
|
|
899
1212
|
...input.universalAstMetadata
|
|
900
1213
|
}
|
|
901
1214
|
});
|
|
@@ -915,6 +1228,14 @@ export function importNativeSource(input) {
|
|
|
915
1228
|
semanticIndexId: semanticIndex?.id,
|
|
916
1229
|
universalAstId: universalAst.id,
|
|
917
1230
|
sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
|
|
1231
|
+
...(sourcePreservation ? {
|
|
1232
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1233
|
+
sourcePreservationSummary: sourcePreservation.summary
|
|
1234
|
+
} : {}),
|
|
1235
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1236
|
+
declaredSourceHash,
|
|
1237
|
+
sourceHashVerified: false
|
|
1238
|
+
} : {}),
|
|
918
1239
|
nativeImportLossSummary: lossSummary
|
|
919
1240
|
}
|
|
920
1241
|
});
|
|
@@ -937,6 +1258,14 @@ export function importNativeSource(input) {
|
|
|
937
1258
|
sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
|
|
938
1259
|
semanticStatus,
|
|
939
1260
|
mappings: resultSourceMapMappings,
|
|
1261
|
+
...(sourcePreservation ? {
|
|
1262
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1263
|
+
sourcePreservation
|
|
1264
|
+
} : {}),
|
|
1265
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1266
|
+
declaredSourceHash,
|
|
1267
|
+
sourceHashVerified: false
|
|
1268
|
+
} : {}),
|
|
940
1269
|
nativeImportLossSummary: lossSummary,
|
|
941
1270
|
...input.metadata
|
|
942
1271
|
}
|
|
@@ -1040,7 +1369,7 @@ function createLightweightNativeImport(input) {
|
|
|
1040
1369
|
}
|
|
1041
1370
|
if (declaration.loss) losses.push(declaration.loss);
|
|
1042
1371
|
}
|
|
1043
|
-
losses.push(...lightweightCoverageLosses(input, declarations));
|
|
1372
|
+
losses.push(...lightweightCoverageLosses(input, declarations, input.sourcePreservation));
|
|
1044
1373
|
|
|
1045
1374
|
const semanticIndex = createSemanticIndexRecord({
|
|
1046
1375
|
id: `index_${idFragment(input.sourcePath ?? input.language)}`,
|
|
@@ -1076,7 +1405,15 @@ function createLightweightNativeImport(input) {
|
|
|
1076
1405
|
losses,
|
|
1077
1406
|
semanticIndex,
|
|
1078
1407
|
mappings,
|
|
1079
|
-
metadata: {
|
|
1408
|
+
metadata: {
|
|
1409
|
+
parser,
|
|
1410
|
+
scanKind: 'lightweight-declaration-scan',
|
|
1411
|
+
declarationCount: declarations.length,
|
|
1412
|
+
...(input.sourcePreservation ? {
|
|
1413
|
+
sourcePreservationId: input.sourcePreservation.id,
|
|
1414
|
+
sourcePreservationSummary: input.sourcePreservation.summary
|
|
1415
|
+
} : {})
|
|
1416
|
+
}
|
|
1080
1417
|
};
|
|
1081
1418
|
}
|
|
1082
1419
|
|
|
@@ -1121,20 +1458,32 @@ function nativeImportProjectionContext(importResult, options) {
|
|
|
1121
1458
|
}
|
|
1122
1459
|
|
|
1123
1460
|
function nativeProjectionSourceCandidate(context, options) {
|
|
1124
|
-
const
|
|
1461
|
+
const preservation = sourcePreservationFromProjectionContext(context);
|
|
1462
|
+
const explicitSourceText = options.sourceText ?? options.preservedSourceText ?? options.exactSourceText;
|
|
1463
|
+
const sourceText = explicitSourceText ?? preservation?.sourceText;
|
|
1125
1464
|
if (typeof sourceText !== 'string') return undefined;
|
|
1126
|
-
const
|
|
1465
|
+
const computedSourceHash = hashSemanticValue(sourceText);
|
|
1466
|
+
const declaredSourceHash = options.sourceHash ?? (explicitSourceText === undefined ? preservation?.sourceHash : undefined);
|
|
1467
|
+
const sourceHash = computedSourceHash;
|
|
1127
1468
|
const hashVerified = Boolean(context.sourceHash);
|
|
1128
1469
|
const exact = !context.sourceHash || sourceHash === context.sourceHash || options.verifySourceHash === false;
|
|
1129
1470
|
return {
|
|
1130
1471
|
sourceText,
|
|
1131
1472
|
sourceHash,
|
|
1473
|
+
declaredSourceHash,
|
|
1132
1474
|
hashVerified,
|
|
1133
1475
|
exact,
|
|
1134
|
-
mismatch: hashVerified && sourceHash !== context.sourceHash && options.verifySourceHash !== false
|
|
1476
|
+
mismatch: hashVerified && sourceHash !== context.sourceHash && options.verifySourceHash !== false,
|
|
1477
|
+
sourcePreservationId: preservation?.id
|
|
1135
1478
|
};
|
|
1136
1479
|
}
|
|
1137
1480
|
|
|
1481
|
+
function sourcePreservationFromProjectionContext(context) {
|
|
1482
|
+
return context.nativeSource?.metadata?.sourcePreservation
|
|
1483
|
+
?? context.nativeAst?.metadata?.sourcePreservation
|
|
1484
|
+
?? context.nativeSource?.ast?.metadata?.sourcePreservation;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1138
1487
|
function nativeProjectionDeclarations(importResult, context) {
|
|
1139
1488
|
const semanticIndex = context.semanticIndex;
|
|
1140
1489
|
const occurrencesBySymbol = new Map();
|
|
@@ -1238,14 +1587,15 @@ function nativeProjectionStubLosses(context, candidateSource, declarations, opti
|
|
|
1238
1587
|
: 'Exact native source text was not provided; emitted declaration stubs reconstructed from import metadata.';
|
|
1239
1588
|
const losses = [nativeProjectionLoss(context, {
|
|
1240
1589
|
id: `loss_${context.idPart}_native_source_stub`,
|
|
1241
|
-
kind: 'sourcePreservation',
|
|
1590
|
+
kind: candidateSource?.mismatch ? 'sourcePreservation' : 'targetProjectionLoss',
|
|
1242
1591
|
severity: 'warning',
|
|
1243
1592
|
message,
|
|
1244
1593
|
metadata: {
|
|
1245
1594
|
reason,
|
|
1246
1595
|
projectionMode: 'native-source-stubs',
|
|
1247
1596
|
expectedSourceHash: context.sourceHash,
|
|
1248
|
-
providedSourceHash: candidateSource?.sourceHash
|
|
1597
|
+
providedSourceHash: candidateSource?.sourceHash,
|
|
1598
|
+
declaredSourceHash: candidateSource?.declaredSourceHash
|
|
1249
1599
|
}
|
|
1250
1600
|
})];
|
|
1251
1601
|
if (!declarations.length) {
|
|
@@ -2249,7 +2599,7 @@ function opaqueBodyLoss(input, lineNumber, nodeId, name) {
|
|
|
2249
2599
|
};
|
|
2250
2600
|
}
|
|
2251
2601
|
|
|
2252
|
-
function lightweightCoverageLosses(input, declarations) {
|
|
2602
|
+
function lightweightCoverageLosses(input, declarations, sourcePreservation) {
|
|
2253
2603
|
const id = idFragment(input.sourcePath ?? input.language);
|
|
2254
2604
|
const span = declarations[0]?.span ?? {
|
|
2255
2605
|
sourceId: input.sourceHash,
|
|
@@ -2291,12 +2641,275 @@ function lightweightCoverageLosses(input, declarations) {
|
|
|
2291
2641
|
phase: 'read',
|
|
2292
2642
|
sourceFormat: input.language,
|
|
2293
2643
|
kind: 'sourcePreservation',
|
|
2294
|
-
message:
|
|
2295
|
-
|
|
2644
|
+
message: sourcePreservation
|
|
2645
|
+
? 'Comments, whitespace, token order, directives, and formatting are preserved as opaque native source evidence; exact structural edits still require a parser adapter.'
|
|
2646
|
+
: 'Comments, whitespace, token order, directives, and formatting are not preserved by the lightweight importer.',
|
|
2647
|
+
span,
|
|
2648
|
+
metadata: sourcePreservation ? {
|
|
2649
|
+
sourcePreservationId: sourcePreservation.id,
|
|
2650
|
+
sourcePreservationSummary: sourcePreservation.summary
|
|
2651
|
+
} : undefined
|
|
2296
2652
|
}
|
|
2297
2653
|
];
|
|
2298
2654
|
}
|
|
2299
2655
|
|
|
2656
|
+
function scanPreservedSourceTokens(sourceText, input) {
|
|
2657
|
+
const tokens = [];
|
|
2658
|
+
const trivia = [];
|
|
2659
|
+
const includeTokens = input.includeTokens !== false;
|
|
2660
|
+
const includeTrivia = input.includeTrivia !== false;
|
|
2661
|
+
const maxTokens = Number.isFinite(input.maxTokens) ? Math.max(0, input.maxTokens) : 20000;
|
|
2662
|
+
const maxTrivia = Number.isFinite(input.maxTrivia) ? Math.max(0, input.maxTrivia) : 20000;
|
|
2663
|
+
let offset = 0;
|
|
2664
|
+
let line = 1;
|
|
2665
|
+
let column = 1;
|
|
2666
|
+
let truncated = false;
|
|
2667
|
+
const push = (target, kind, text, start) => {
|
|
2668
|
+
if ((target === tokens && !includeTokens) || (target === trivia && !includeTrivia)) return;
|
|
2669
|
+
const max = target === tokens ? maxTokens : maxTrivia;
|
|
2670
|
+
if (target.length >= max) {
|
|
2671
|
+
truncated = true;
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
target.push(preservedSourceSegment({
|
|
2675
|
+
index: target.length,
|
|
2676
|
+
kind,
|
|
2677
|
+
text,
|
|
2678
|
+
start,
|
|
2679
|
+
end: { offset, line, column },
|
|
2680
|
+
sourceHash: input.sourceHash,
|
|
2681
|
+
sourcePath: input.sourcePath
|
|
2682
|
+
}));
|
|
2683
|
+
};
|
|
2684
|
+
while (offset < sourceText.length) {
|
|
2685
|
+
const start = { offset, line, column };
|
|
2686
|
+
const char = sourceText[offset];
|
|
2687
|
+
const next = sourceText[offset + 1];
|
|
2688
|
+
if (char === '\r' || char === '\n') {
|
|
2689
|
+
const text = char === '\r' && next === '\n' ? '\r\n' : char;
|
|
2690
|
+
offset += text.length;
|
|
2691
|
+
line += 1;
|
|
2692
|
+
column = 1;
|
|
2693
|
+
push(trivia, 'newline', text, start);
|
|
2694
|
+
continue;
|
|
2695
|
+
}
|
|
2696
|
+
if (char === ' ' || char === '\t' || char === '\v' || char === '\f') {
|
|
2697
|
+
let text = '';
|
|
2698
|
+
while (offset < sourceText.length && /[ \t\v\f]/.test(sourceText[offset])) {
|
|
2699
|
+
text += sourceText[offset];
|
|
2700
|
+
offset += 1;
|
|
2701
|
+
column += 1;
|
|
2702
|
+
}
|
|
2703
|
+
push(trivia, 'whitespace', text, start);
|
|
2704
|
+
continue;
|
|
2705
|
+
}
|
|
2706
|
+
if (char === '/' && next === '/') {
|
|
2707
|
+
let text = '';
|
|
2708
|
+
while (offset < sourceText.length && sourceText[offset] !== '\n' && sourceText[offset] !== '\r') {
|
|
2709
|
+
text += sourceText[offset];
|
|
2710
|
+
offset += 1;
|
|
2711
|
+
column += 1;
|
|
2712
|
+
}
|
|
2713
|
+
push(trivia, 'comment', text, start);
|
|
2714
|
+
continue;
|
|
2715
|
+
}
|
|
2716
|
+
if (char === '/' && next === '*') {
|
|
2717
|
+
let text = '';
|
|
2718
|
+
while (offset < sourceText.length) {
|
|
2719
|
+
const current = sourceText[offset];
|
|
2720
|
+
text += current;
|
|
2721
|
+
offset += 1;
|
|
2722
|
+
if (current === '\n') {
|
|
2723
|
+
line += 1;
|
|
2724
|
+
column = 1;
|
|
2725
|
+
} else {
|
|
2726
|
+
column += 1;
|
|
2727
|
+
}
|
|
2728
|
+
if (current === '*' && sourceText[offset] === '/') {
|
|
2729
|
+
text += '/';
|
|
2730
|
+
offset += 1;
|
|
2731
|
+
column += 1;
|
|
2732
|
+
break;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
push(trivia, 'comment', text, start);
|
|
2736
|
+
continue;
|
|
2737
|
+
}
|
|
2738
|
+
if (char === '#' && isHashCommentLanguage(input.language)) {
|
|
2739
|
+
let text = '';
|
|
2740
|
+
while (offset < sourceText.length && sourceText[offset] !== '\n' && sourceText[offset] !== '\r') {
|
|
2741
|
+
text += sourceText[offset];
|
|
2742
|
+
offset += 1;
|
|
2743
|
+
column += 1;
|
|
2744
|
+
}
|
|
2745
|
+
push(trivia, preservedHashLineKind(text), text, start);
|
|
2746
|
+
continue;
|
|
2747
|
+
}
|
|
2748
|
+
if (char === '"' || char === '\'' || char === '`') {
|
|
2749
|
+
const quote = char;
|
|
2750
|
+
let text = char;
|
|
2751
|
+
offset += 1;
|
|
2752
|
+
column += 1;
|
|
2753
|
+
let escaped = false;
|
|
2754
|
+
while (offset < sourceText.length) {
|
|
2755
|
+
const current = sourceText[offset];
|
|
2756
|
+
text += current;
|
|
2757
|
+
offset += 1;
|
|
2758
|
+
if (current === '\n') {
|
|
2759
|
+
line += 1;
|
|
2760
|
+
column = 1;
|
|
2761
|
+
} else {
|
|
2762
|
+
column += 1;
|
|
2763
|
+
}
|
|
2764
|
+
if (escaped) {
|
|
2765
|
+
escaped = false;
|
|
2766
|
+
} else if (current === '\\') {
|
|
2767
|
+
escaped = true;
|
|
2768
|
+
} else if (current === quote) {
|
|
2769
|
+
break;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
push(tokens, 'string', text, start);
|
|
2773
|
+
continue;
|
|
2774
|
+
}
|
|
2775
|
+
if (/[0-9]/.test(char)) {
|
|
2776
|
+
let text = '';
|
|
2777
|
+
while (offset < sourceText.length && /[0-9a-fA-F_xXoObBeE.+-]/.test(sourceText[offset])) {
|
|
2778
|
+
text += sourceText[offset];
|
|
2779
|
+
offset += 1;
|
|
2780
|
+
column += 1;
|
|
2781
|
+
}
|
|
2782
|
+
push(tokens, 'number', text, start);
|
|
2783
|
+
continue;
|
|
2784
|
+
}
|
|
2785
|
+
if (isIdentifierStart(char)) {
|
|
2786
|
+
let text = '';
|
|
2787
|
+
while (offset < sourceText.length && isIdentifierPart(sourceText[offset])) {
|
|
2788
|
+
text += sourceText[offset];
|
|
2789
|
+
offset += 1;
|
|
2790
|
+
column += 1;
|
|
2791
|
+
}
|
|
2792
|
+
push(tokens, preservedKeywordSet.has(text) ? 'keyword' : 'identifier', text, start);
|
|
2793
|
+
continue;
|
|
2794
|
+
}
|
|
2795
|
+
let text = char;
|
|
2796
|
+
if (/[=+\-*/%&|^!<>?:.]/.test(char)) {
|
|
2797
|
+
while (offset + text.length < sourceText.length && /[=+\-*/%&|^!<>?:.]/.test(sourceText[offset + text.length])) text += sourceText[offset + text.length];
|
|
2798
|
+
offset += text.length;
|
|
2799
|
+
column += text.length;
|
|
2800
|
+
push(tokens, 'operator', text, start);
|
|
2801
|
+
} else {
|
|
2802
|
+
offset += 1;
|
|
2803
|
+
column += 1;
|
|
2804
|
+
push(tokens, /[()[\]{};,]/.test(char) ? 'punctuation' : 'unknown', text, start);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
return { tokens, trivia, truncated };
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
function scanPreservedSourceDirectives(sourceText, input) {
|
|
2811
|
+
const directives = [];
|
|
2812
|
+
const maxDirectives = Number.isFinite(input.maxDirectives) ? Math.max(0, input.maxDirectives) : 20000;
|
|
2813
|
+
let truncated = false;
|
|
2814
|
+
let offset = 0;
|
|
2815
|
+
for (const { line, number } of sourceLines(sourceText)) {
|
|
2816
|
+
const trimmed = line.trim();
|
|
2817
|
+
const directiveKind = preservedDirectiveKind(trimmed, input.language);
|
|
2818
|
+
if (directiveKind) {
|
|
2819
|
+
if (directives.length >= maxDirectives) {
|
|
2820
|
+
truncated = true;
|
|
2821
|
+
offset += line.length + 1;
|
|
2822
|
+
continue;
|
|
2823
|
+
}
|
|
2824
|
+
const startColumn = Math.max(1, line.indexOf(trimmed) + 1);
|
|
2825
|
+
directives.push({
|
|
2826
|
+
id: `directive_${idFragment(input.sourcePath ?? input.language)}_${directives.length + 1}`,
|
|
2827
|
+
kind: directiveKind,
|
|
2828
|
+
text: trimmed,
|
|
2829
|
+
textHash: hashSemanticValue(trimmed),
|
|
2830
|
+
span: {
|
|
2831
|
+
sourceId: input.sourceHash,
|
|
2832
|
+
path: input.sourcePath,
|
|
2833
|
+
start: offset + startColumn - 1,
|
|
2834
|
+
end: offset + startColumn - 1 + trimmed.length,
|
|
2835
|
+
startLine: number,
|
|
2836
|
+
startColumn,
|
|
2837
|
+
endLine: number,
|
|
2838
|
+
endColumn: startColumn + trimmed.length
|
|
2839
|
+
},
|
|
2840
|
+
metadata: { language: input.language }
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
offset += line.length + 1;
|
|
2844
|
+
}
|
|
2845
|
+
return { directives, truncated };
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
function preservedSourceSegment(input) {
|
|
2849
|
+
const id = `${input.kind}_${input.index + 1}_${idFragment(input.start.offset)}`;
|
|
2850
|
+
return {
|
|
2851
|
+
id,
|
|
2852
|
+
kind: input.kind,
|
|
2853
|
+
text: input.text,
|
|
2854
|
+
textHash: hashSemanticValue(input.text),
|
|
2855
|
+
span: {
|
|
2856
|
+
sourceId: input.sourceHash,
|
|
2857
|
+
path: input.sourcePath,
|
|
2858
|
+
start: input.start.offset,
|
|
2859
|
+
end: input.end.offset,
|
|
2860
|
+
startLine: input.start.line,
|
|
2861
|
+
startColumn: input.start.column,
|
|
2862
|
+
endLine: input.end.line,
|
|
2863
|
+
endColumn: input.end.column
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
function preservedDirectiveKind(trimmed, language) {
|
|
2869
|
+
if (!trimmed) return undefined;
|
|
2870
|
+
if (/^#\s*(include|define|if|ifdef|ifndef|elif|else|endif|pragma)\b/.test(trimmed)) return 'preprocessor';
|
|
2871
|
+
if (/^#!\s*/.test(trimmed)) return 'shebang';
|
|
2872
|
+
if (/^['"]use strict['"];?$/.test(trimmed)) return 'runtime-directive';
|
|
2873
|
+
if (/^(import|export|package|module|namespace|use|using|from|require)\b/.test(trimmed)) return 'module-directive';
|
|
2874
|
+
if (normalizeNativeLanguageId(language) === 'python' && /^from\s+\S+\s+import\b/.test(trimmed)) return 'module-directive';
|
|
2875
|
+
return undefined;
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
function preservedHashLineKind(text) {
|
|
2879
|
+
return preservedDirectiveKind(String(text).trim(), 'c') ? 'directive' : 'comment';
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
function isHashCommentLanguage(language) {
|
|
2883
|
+
return ['python', 'ruby', 'shell', 'bash', 'zsh', 'r', 'perl', 'yaml', 'toml'].includes(normalizeNativeLanguageId(language));
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
function detectNewlineStyle(sourceText) {
|
|
2887
|
+
const crlf = (sourceText.match(/\r\n/g) ?? []).length;
|
|
2888
|
+
const normalized = sourceText.replace(/\r\n/g, '');
|
|
2889
|
+
const lf = (normalized.match(/\n/g) ?? []).length;
|
|
2890
|
+
const cr = (normalized.match(/\r/g) ?? []).length;
|
|
2891
|
+
const kinds = [crlf ? 'crlf' : undefined, lf ? 'lf' : undefined, cr ? 'cr' : undefined].filter(Boolean);
|
|
2892
|
+
if (!kinds.length) return 'none';
|
|
2893
|
+
if (kinds.length > 1 || cr) return 'mixed';
|
|
2894
|
+
return kinds[0];
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
const preservedKeywordSet = new Set([
|
|
2898
|
+
'abstract', 'as', 'async', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'def', 'defer',
|
|
2899
|
+
'do', 'else', 'enum', 'export', 'extends', 'extern', 'false', 'final', 'fn', 'for', 'from', 'func', 'function',
|
|
2900
|
+
'if', 'impl', 'import', 'in', 'interface', 'let', 'match', 'mod', 'module', 'mut', 'namespace', 'new', 'nil',
|
|
2901
|
+
'none', 'null', 'package', 'private', 'protected', 'pub', 'public', 'return', 'self', 'static', 'struct',
|
|
2902
|
+
'switch', 'this', 'throw', 'trait', 'true', 'try', 'type', 'use', 'using', 'var', 'while', 'yield'
|
|
2903
|
+
]);
|
|
2904
|
+
|
|
2905
|
+
function isIdentifierStart(char) {
|
|
2906
|
+
return /[A-Za-z_$]/.test(char ?? '');
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
function isIdentifierPart(char) {
|
|
2910
|
+
return /[A-Za-z0-9_$]/.test(char ?? '');
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2300
2913
|
function sourceLines(sourceText) {
|
|
2301
2914
|
return String(sourceText ?? '').split(/\r?\n/).map((line, index) => ({ line, number: index + 1 }));
|
|
2302
2915
|
}
|
|
@@ -2636,11 +3249,13 @@ function nativeImportCategoryForLossKind(kind) {
|
|
|
2636
3249
|
if (kind === 'preprocessor' || kind === 'conditionalCompilation' || kind === 'macroHygiene') return 'preprocessor';
|
|
2637
3250
|
if (kind === 'metaprogramming' || kind === 'reflection' || kind === 'dynamicDispatch' || kind === 'dynamicRuntime') return 'metaprogramming';
|
|
2638
3251
|
if (kind === 'generatedCode') return 'generatedCode';
|
|
2639
|
-
if (kind === '
|
|
3252
|
+
if (kind === 'overloadResolution' || kind === 'typeInference') return 'overloadTypeInference';
|
|
3253
|
+
if (kind === 'sourcePreservation' || kind === 'commentsTrivia' || kind === 'nonRoundTrippable') return 'sourcePreservation';
|
|
2640
3254
|
if (kind === 'parserDiagnostic') return 'parserDiagnostics';
|
|
2641
3255
|
if (kind === 'unsupportedSyntax' || kind === 'unsupportedSemantic') return 'unsupportedSyntax';
|
|
2642
|
-
if (kind === 'partialSemanticIndex') return 'partialSemanticIndex';
|
|
3256
|
+
if (kind === 'partialSemanticIndex' || kind === 'unverifiedNativeAst') return 'partialSemanticIndex';
|
|
2643
3257
|
if (kind === 'sourceMapApproximation') return 'sourceMapApproximation';
|
|
3258
|
+
if (kind === 'targetProjectionLoss') return 'targetProjectionLoss';
|
|
2644
3259
|
return String(kind ?? 'opaqueNative');
|
|
2645
3260
|
}
|
|
2646
3261
|
|
|
@@ -2924,6 +3539,36 @@ function attachNativeImportLossSummary(evidence, lossSummary) {
|
|
|
2924
3539
|
}));
|
|
2925
3540
|
}
|
|
2926
3541
|
|
|
3542
|
+
function hasNativeExactAstEvidence(input, nativeAst, lightweight) {
|
|
3543
|
+
if (lightweight) return false;
|
|
3544
|
+
if (!(input?.nativeAst || input?.nodes)) return false;
|
|
3545
|
+
if (input.exactAst === true || input.metadata?.exactAst === true || input.nativeAstMetadata?.exactAst === true) return true;
|
|
3546
|
+
const coverage = input.metadata?.adapterCoverage
|
|
3547
|
+
?? input.nativeAstMetadata?.adapterCoverage
|
|
3548
|
+
?? input.nativeSourceMetadata?.adapterCoverage
|
|
3549
|
+
?? nativeAst?.metadata?.adapterCoverage;
|
|
3550
|
+
if (coverage?.exactAst !== true) return false;
|
|
3551
|
+
const observedNodes = coverage.observed?.nativeAstNodes;
|
|
3552
|
+
return observedNodes === undefined || observedNodes > 0;
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
function unverifiedNativeAstLosses(input, nativeAst, context) {
|
|
3556
|
+
if (context.lightweight || context.exactAst || context.hasLosses) return [];
|
|
3557
|
+
if (!(input?.nativeAst || input?.nodes)) return [];
|
|
3558
|
+
return [{
|
|
3559
|
+
id: `loss_${context.importIdPart}_unverified_native_ast`,
|
|
3560
|
+
severity: 'warning',
|
|
3561
|
+
kind: 'unverifiedNativeAst',
|
|
3562
|
+
nodeId: nativeAst?.rootId,
|
|
3563
|
+
message: 'Caller supplied native AST nodes without explicit exactAst or adapter coverage evidence.',
|
|
3564
|
+
metadata: {
|
|
3565
|
+
reason: 'missing-exact-ast-evidence',
|
|
3566
|
+
nativeAstId: nativeAst?.id,
|
|
3567
|
+
parser: nativeAst?.parser
|
|
3568
|
+
}
|
|
3569
|
+
}];
|
|
3570
|
+
}
|
|
3571
|
+
|
|
2927
3572
|
function withNativeImportReadiness(importResult, lossSummary) {
|
|
2928
3573
|
const mergeCandidates = (importResult.mergeCandidates ?? []).map((candidate) => {
|
|
2929
3574
|
const readiness = maxSemanticMergeReadiness(candidate.readiness, lossSummary.semanticMergeReadiness);
|
|
@@ -2959,6 +3604,49 @@ function withNativeImportReadiness(importResult, lossSummary) {
|
|
|
2959
3604
|
};
|
|
2960
3605
|
}
|
|
2961
3606
|
|
|
3607
|
+
function nativeImportEntries(importResult) {
|
|
3608
|
+
if (Array.isArray(importResult?.imports)) return importResult.imports.filter(Boolean);
|
|
3609
|
+
return [importResult].filter(Boolean);
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
function nativeImportHasExactAstCoverage(imported) {
|
|
3613
|
+
if (imported?.metadata?.nativeImportLossSummary?.exactAst === true) return true;
|
|
3614
|
+
if (imported?.adapter?.coverage?.exactAst === true && !(imported?.losses?.length)) return true;
|
|
3615
|
+
return false;
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
function nativeImportRoundtripParser(importResult, imports) {
|
|
3619
|
+
const parsers = uniqueStrings([
|
|
3620
|
+
importResult.nativeAst?.parser,
|
|
3621
|
+
importResult.nativeSource?.parser,
|
|
3622
|
+
importResult.metadata?.parser,
|
|
3623
|
+
...imports.map((imported) => imported?.nativeAst?.parser ?? imported?.nativeSource?.parser ?? imported?.metadata?.parser)
|
|
3624
|
+
].filter(Boolean));
|
|
3625
|
+
return parsers.length === 1 ? parsers[0] : parsers.length ? parsers.join(',') : undefined;
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3628
|
+
function nativeImportRoundtripReasons(status, input) {
|
|
3629
|
+
if (status === 'blocked') return uniqueStrings(input.blockingReasons);
|
|
3630
|
+
if (status === 'stub-only') {
|
|
3631
|
+
return uniqueStrings([
|
|
3632
|
+
`Native source projection emitted declaration stubs in ${input.projection.mode} mode.`,
|
|
3633
|
+
...input.projectionReadiness.reasons,
|
|
3634
|
+
...input.importReadiness.reasons.filter((reason) => input.importReadiness.readiness !== 'ready')
|
|
3635
|
+
]);
|
|
3636
|
+
}
|
|
3637
|
+
if (status === 'needs-review') return uniqueStrings(input.reviewReasons);
|
|
3638
|
+
if (status === 'exact') {
|
|
3639
|
+
return ['Exact native AST import and verified preserved source projection are available.'];
|
|
3640
|
+
}
|
|
3641
|
+
if (status === 'preserved-source') {
|
|
3642
|
+
return uniqueStrings([
|
|
3643
|
+
'Verified native source text is preserved for projection; semantic import evidence may still require review.',
|
|
3644
|
+
...input.importReadiness.reasons.filter((reason) => input.importReadiness.readiness !== 'ready')
|
|
3645
|
+
]);
|
|
3646
|
+
}
|
|
3647
|
+
return ['Native import roundtrip readiness requires review.'];
|
|
3648
|
+
}
|
|
3649
|
+
|
|
2962
3650
|
function maxSemanticMergeReadiness(left, right) {
|
|
2963
3651
|
const leftRank = semanticMergeReadinessRank[left] ?? semanticMergeReadinessRank['needs-review'];
|
|
2964
3652
|
const rightRank = semanticMergeReadinessRank[right] ?? semanticMergeReadinessRank['needs-review'];
|
|
@@ -3004,6 +3692,20 @@ function createJavaScriptSyntaxImporterAdapter(options) {
|
|
|
3004
3692
|
parser: options.parser,
|
|
3005
3693
|
version: options.version,
|
|
3006
3694
|
capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
|
|
3695
|
+
coverage: nativeImporterAdapterCoverage({
|
|
3696
|
+
exactness: 'exact-parser-ast',
|
|
3697
|
+
exactAst: true,
|
|
3698
|
+
tokens: false,
|
|
3699
|
+
trivia: false,
|
|
3700
|
+
diagnostics: true,
|
|
3701
|
+
sourceRanges: true,
|
|
3702
|
+
generatedRanges: false,
|
|
3703
|
+
semanticCoverage: declarationSemanticCoverage(),
|
|
3704
|
+
notes: [
|
|
3705
|
+
'Normalizes a caller-owned ESTree/Babel-compatible AST into native AST nodes and declaration-level semantic index records.',
|
|
3706
|
+
'The wrapper ignores parser token/trivia/comment arrays unless a host adapter explicitly maps them into preservation evidence.'
|
|
3707
|
+
]
|
|
3708
|
+
}, options.coverage),
|
|
3007
3709
|
supportedExtensions: options.supportedExtensions,
|
|
3008
3710
|
diagnostics: options.diagnostics,
|
|
3009
3711
|
parse(input) {
|
|
@@ -3454,6 +4156,14 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
3454
4156
|
mergeCandidates.push(...(result.mergeCandidates ?? []));
|
|
3455
4157
|
operations.push(...(result.patch?.operations ?? []));
|
|
3456
4158
|
}
|
|
4159
|
+
const uniqueLosses = uniqueByLossId(losses);
|
|
4160
|
+
const uniqueEvidence = uniqueByEvidenceId(evidence);
|
|
4161
|
+
const nativeImportLossSummary = summarizeNativeImportLosses(uniqueLosses, {
|
|
4162
|
+
evidence: uniqueEvidence,
|
|
4163
|
+
scanKind: 'native-project-import',
|
|
4164
|
+
semanticStatus: uniqueLosses.some((loss) => loss.severity === 'error') ? 'partial' : 'mapped'
|
|
4165
|
+
});
|
|
4166
|
+
const sourcePreservationSummary = summarizeProjectSourcePreservation(imports);
|
|
3457
4167
|
const document = createDocument({
|
|
3458
4168
|
id: input.documentId ?? `document_${idPart}`,
|
|
3459
4169
|
name: input.documentName ?? input.name ?? 'NativeProject',
|
|
@@ -3464,6 +4174,8 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
3464
4174
|
semanticStatus: losses.some((loss) => loss.severity === 'error') ? 'partial' : 'mapped',
|
|
3465
4175
|
projectRoot: input.projectRoot,
|
|
3466
4176
|
sourceCount: imports.length,
|
|
4177
|
+
nativeImportLossSummary,
|
|
4178
|
+
sourcePreservationSummary,
|
|
3467
4179
|
...input.documentMetadata
|
|
3468
4180
|
}
|
|
3469
4181
|
});
|
|
@@ -3473,12 +4185,14 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
3473
4185
|
nativeSources,
|
|
3474
4186
|
semanticIndex,
|
|
3475
4187
|
sourceMaps,
|
|
3476
|
-
losses:
|
|
3477
|
-
evidence:
|
|
4188
|
+
losses: uniqueLosses,
|
|
4189
|
+
evidence: uniqueEvidence,
|
|
3478
4190
|
metadata: {
|
|
3479
4191
|
sourceLanguage: input.language ?? 'mixed',
|
|
3480
4192
|
projectRoot: input.projectRoot,
|
|
3481
4193
|
sourceCount: imports.length,
|
|
4194
|
+
nativeImportLossSummary,
|
|
4195
|
+
sourcePreservationSummary,
|
|
3482
4196
|
...input.universalAstMetadata
|
|
3483
4197
|
}
|
|
3484
4198
|
});
|
|
@@ -3487,12 +4201,14 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
3487
4201
|
author: input.author ?? '@shapeshift-labs/frontier-lang-compiler/importNativeProject',
|
|
3488
4202
|
risk: losses.some((loss) => loss.severity === 'error') ? 'high' : losses.some((loss) => loss.severity === 'warning') ? 'medium' : 'low',
|
|
3489
4203
|
operations,
|
|
3490
|
-
evidence:
|
|
4204
|
+
evidence: uniqueEvidence,
|
|
3491
4205
|
metadata: {
|
|
3492
4206
|
semanticIndexId: semanticIndex?.id,
|
|
3493
4207
|
universalAstId: universalAst.id,
|
|
3494
4208
|
sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
|
|
3495
|
-
sourceCount: imports.length
|
|
4209
|
+
sourceCount: imports.length,
|
|
4210
|
+
nativeImportLossSummary,
|
|
4211
|
+
sourcePreservationSummary
|
|
3496
4212
|
}
|
|
3497
4213
|
});
|
|
3498
4214
|
return {
|
|
@@ -3508,17 +4224,35 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
3508
4224
|
semanticIndex,
|
|
3509
4225
|
universalAst,
|
|
3510
4226
|
sourceMaps,
|
|
3511
|
-
losses:
|
|
3512
|
-
evidence:
|
|
4227
|
+
losses: uniqueLosses,
|
|
4228
|
+
evidence: uniqueEvidence,
|
|
3513
4229
|
mergeCandidates,
|
|
3514
4230
|
metadata: {
|
|
3515
4231
|
sourceCount: imports.length,
|
|
3516
4232
|
sourcePaths: imports.map((result) => result.sourcePath).filter(Boolean),
|
|
4233
|
+
nativeImportLossSummary,
|
|
4234
|
+
sourcePreservationSummary,
|
|
3517
4235
|
...input.metadata
|
|
3518
4236
|
}
|
|
3519
4237
|
};
|
|
3520
4238
|
}
|
|
3521
4239
|
|
|
4240
|
+
function summarizeProjectSourcePreservation(imports) {
|
|
4241
|
+
const records = imports
|
|
4242
|
+
.map((result) => result.metadata?.sourcePreservation ?? result.nativeSource?.metadata?.sourcePreservation ?? result.nativeAst?.metadata?.sourcePreservation)
|
|
4243
|
+
.filter(Boolean);
|
|
4244
|
+
return {
|
|
4245
|
+
total: records.length,
|
|
4246
|
+
exactSourceAvailable: records.filter((record) => record.summary?.exactSourceAvailable).length,
|
|
4247
|
+
sourceBytes: records.reduce((sum, record) => sum + (record.sourceBytes ?? 0), 0),
|
|
4248
|
+
tokens: records.reduce((sum, record) => sum + (record.summary?.tokens ?? record.tokens?.length ?? 0), 0),
|
|
4249
|
+
trivia: records.reduce((sum, record) => sum + (record.summary?.trivia ?? record.trivia?.length ?? 0), 0),
|
|
4250
|
+
directives: records.reduce((sum, record) => sum + (record.summary?.directives ?? record.directives?.length ?? 0), 0),
|
|
4251
|
+
truncated: records.some((record) => record.summary?.truncated === true),
|
|
4252
|
+
ids: records.map((record) => record.id).filter(Boolean)
|
|
4253
|
+
};
|
|
4254
|
+
}
|
|
4255
|
+
|
|
3522
4256
|
function mergeSemanticIndexes(imports, input, idPart) {
|
|
3523
4257
|
const indexes = imports.map((result) => result.semanticIndex ?? result.universalAst?.semanticIndex).filter(Boolean);
|
|
3524
4258
|
if (!indexes.length) return undefined;
|
|
@@ -3564,9 +4298,15 @@ function normalizeNativeImporterAdapter(adapter) {
|
|
|
3564
4298
|
parser: String(adapter.parser),
|
|
3565
4299
|
version: adapter.version === undefined ? undefined : String(adapter.version)
|
|
3566
4300
|
};
|
|
4301
|
+
const capabilities = normalizeStringList(adapter.capabilities);
|
|
3567
4302
|
return Object.freeze({
|
|
3568
4303
|
...summaryInput,
|
|
3569
|
-
capabilities
|
|
4304
|
+
capabilities,
|
|
4305
|
+
coverage: normalizeNativeImporterAdapterCoverage(adapter.coverage, {
|
|
4306
|
+
capabilities,
|
|
4307
|
+
language: adapter.language,
|
|
4308
|
+
parser: String(adapter.parser)
|
|
4309
|
+
}),
|
|
3570
4310
|
supportedExtensions: normalizeStringList(adapter.supportedExtensions).map((extension) => extension.startsWith('.') ? extension.toLowerCase() : `.${extension.toLowerCase()}`),
|
|
3571
4311
|
diagnostics: normalizeAdapterDiagnostics(adapter.diagnostics, summaryInput, {
|
|
3572
4312
|
language: adapter.language,
|
|
@@ -3576,6 +4316,138 @@ function normalizeNativeImporterAdapter(adapter) {
|
|
|
3576
4316
|
});
|
|
3577
4317
|
}
|
|
3578
4318
|
|
|
4319
|
+
function nativeImporterAdapterCoverage(defaults = {}, overrides = {}) {
|
|
4320
|
+
return {
|
|
4321
|
+
...defaults,
|
|
4322
|
+
...overrides,
|
|
4323
|
+
semanticCoverage: {
|
|
4324
|
+
...(defaults.semanticCoverage ?? {}),
|
|
4325
|
+
...(overrides.semanticCoverage ?? {})
|
|
4326
|
+
},
|
|
4327
|
+
notes: uniqueStrings([...(defaults.notes ?? []), ...(overrides.notes ?? [])])
|
|
4328
|
+
};
|
|
4329
|
+
}
|
|
4330
|
+
|
|
4331
|
+
function normalizeNativeImporterAdapterCoverage(value = {}, context = {}) {
|
|
4332
|
+
const capabilities = new Set(normalizeStringList(context.capabilities).map((capability) => capability.toLowerCase()));
|
|
4333
|
+
const hasCapability = (...names) => names.some((name) => capabilities.has(String(name).toLowerCase()));
|
|
4334
|
+
const exactAst = Boolean(value.exactAst ?? hasCapability('exactAst', 'exactAstImport'));
|
|
4335
|
+
const sourceRanges = Boolean(value.sourceRanges ?? hasCapability('sourceRanges', 'sourceRange', 'ranges', 'sourceMaps'));
|
|
4336
|
+
const generatedRanges = Boolean(value.generatedRanges ?? hasCapability('generatedRanges', 'generatedRange', 'generatedSourceMaps'));
|
|
4337
|
+
const diagnostics = Boolean(value.diagnostics ?? hasCapability('diagnostics', 'parserDiagnostics'));
|
|
4338
|
+
return Object.freeze({
|
|
4339
|
+
exactness: String(value.exactness ?? inferredAdapterExactness(exactAst, capabilities)),
|
|
4340
|
+
exactAst,
|
|
4341
|
+
tokens: Boolean(value.tokens ?? hasCapability('tokens', 'tokenStream')),
|
|
4342
|
+
trivia: Boolean(value.trivia ?? hasCapability('trivia', 'comments', 'formatting')),
|
|
4343
|
+
diagnostics,
|
|
4344
|
+
sourceRanges,
|
|
4345
|
+
generatedRanges,
|
|
4346
|
+
semanticCoverage: normalizeNativeImporterSemanticCoverage(value.semanticCoverage, {
|
|
4347
|
+
capabilities,
|
|
4348
|
+
sourceRanges,
|
|
4349
|
+
generatedRanges
|
|
4350
|
+
}),
|
|
4351
|
+
notes: uniqueStrings(value.notes ?? inferredAdapterCoverageNotes(context, {
|
|
4352
|
+
exactAst,
|
|
4353
|
+
sourceRanges,
|
|
4354
|
+
generatedRanges,
|
|
4355
|
+
diagnostics
|
|
4356
|
+
}))
|
|
4357
|
+
});
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
function observeNativeImporterAdapterCoverage(coverage, parseResult = {}, context = {}) {
|
|
4361
|
+
const nodes = parseResult.nativeAst?.nodes ?? parseResult.nodes ?? {};
|
|
4362
|
+
const nodeList = Object.values(nodes);
|
|
4363
|
+
const sourceMapMappings = parseResult.sourceMaps?.flatMap((sourceMap) => sourceMap.mappings ?? []) ?? parseResult.mappings ?? [];
|
|
4364
|
+
const semanticIndex = parseResult.semanticIndex;
|
|
4365
|
+
const semanticSymbols = semanticIndex?.symbols?.length ?? 0;
|
|
4366
|
+
const observedSourceRanges = nodeList.some((node) => Boolean(node?.span)) || sourceMapMappings.some((mapping) => Boolean(mapping?.sourceSpan));
|
|
4367
|
+
const observedGeneratedRanges = sourceMapMappings.some((mapping) => Boolean(mapping?.generatedSpan));
|
|
4368
|
+
const observedSemanticCoverage = normalizeNativeImporterSemanticCoverage({
|
|
4369
|
+
...coverage.semanticCoverage,
|
|
4370
|
+
level: semanticSymbols
|
|
4371
|
+
? maxSemanticCoverageLevel(coverage.semanticCoverage?.level, 'declaration-index')
|
|
4372
|
+
: coverage.semanticCoverage?.level,
|
|
4373
|
+
declarations: coverage.semanticCoverage?.declarations || semanticSymbols > 0,
|
|
4374
|
+
symbols: coverage.semanticCoverage?.symbols || semanticSymbols > 0
|
|
4375
|
+
}, {});
|
|
4376
|
+
return Object.freeze({
|
|
4377
|
+
...coverage,
|
|
4378
|
+
diagnostics: coverage.diagnostics || (context.diagnostics?.length ?? 0) > 0,
|
|
4379
|
+
sourceRanges: coverage.sourceRanges || observedSourceRanges,
|
|
4380
|
+
generatedRanges: coverage.generatedRanges || observedGeneratedRanges,
|
|
4381
|
+
semanticCoverage: observedSemanticCoverage,
|
|
4382
|
+
observed: {
|
|
4383
|
+
diagnostics: context.diagnostics?.length ?? 0,
|
|
4384
|
+
losses: context.losses?.length ?? 0,
|
|
4385
|
+
nativeAstNodes: nodeList.length,
|
|
4386
|
+
semanticSymbols,
|
|
4387
|
+
sourceMapMappings: sourceMapMappings.length,
|
|
4388
|
+
sourceRanges: observedSourceRanges,
|
|
4389
|
+
generatedRanges: observedGeneratedRanges
|
|
4390
|
+
}
|
|
4391
|
+
});
|
|
4392
|
+
}
|
|
4393
|
+
|
|
4394
|
+
function declarationSemanticCoverage() {
|
|
4395
|
+
return {
|
|
4396
|
+
level: 'declaration-index',
|
|
4397
|
+
declarations: true,
|
|
4398
|
+
symbols: true,
|
|
4399
|
+
references: false,
|
|
4400
|
+
types: false,
|
|
4401
|
+
controlFlow: false
|
|
4402
|
+
};
|
|
4403
|
+
}
|
|
4404
|
+
|
|
4405
|
+
function normalizeNativeImporterSemanticCoverage(value = {}, context = {}) {
|
|
4406
|
+
const capabilities = context.capabilities ?? new Set();
|
|
4407
|
+
const hasCapability = (...names) => names.some((name) => capabilities.has(String(name).toLowerCase()));
|
|
4408
|
+
const declarations = Boolean(value.declarations ?? hasCapability('semanticIndex', 'declarations'));
|
|
4409
|
+
const symbols = Boolean(value.symbols ?? declarations);
|
|
4410
|
+
const references = Boolean(value.references ?? hasCapability('references', 'referenceIndex'));
|
|
4411
|
+
const types = Boolean(value.types ?? hasCapability('types', 'typeResolution', 'typeChecking'));
|
|
4412
|
+
const controlFlow = Boolean(value.controlFlow ?? hasCapability('controlFlow', 'cfg'));
|
|
4413
|
+
return Object.freeze({
|
|
4414
|
+
level: String(value.level ?? inferredSemanticCoverageLevel({ declarations, symbols, references, types, controlFlow })),
|
|
4415
|
+
declarations,
|
|
4416
|
+
symbols,
|
|
4417
|
+
references,
|
|
4418
|
+
types,
|
|
4419
|
+
controlFlow
|
|
4420
|
+
});
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
function inferredAdapterExactness(exactAst, capabilities) {
|
|
4424
|
+
if (exactAst) return 'exact-parser-ast';
|
|
4425
|
+
if (capabilities.has('nativeast')) return 'adapter-reported-native-ast';
|
|
4426
|
+
return 'loss-aware-native-ast';
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
function inferredSemanticCoverageLevel(input) {
|
|
4430
|
+
if (input.references || input.types || input.controlFlow) return 'semantic-index';
|
|
4431
|
+
if (input.declarations || input.symbols) return 'declaration-index';
|
|
4432
|
+
return 'native-ast';
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4435
|
+
function maxSemanticCoverageLevel(left, right) {
|
|
4436
|
+
const ranks = { 'native-ast': 0, 'declaration-index': 1, 'semantic-index': 2 };
|
|
4437
|
+
const leftRank = ranks[left] ?? 0;
|
|
4438
|
+
const rightRank = ranks[right] ?? 0;
|
|
4439
|
+
return rightRank > leftRank ? right : left;
|
|
4440
|
+
}
|
|
4441
|
+
|
|
4442
|
+
function inferredAdapterCoverageNotes(context, coverage) {
|
|
4443
|
+
const notes = [];
|
|
4444
|
+
if (!coverage.exactAst) notes.push('Adapter did not declare exact parser AST/CST coverage; import readiness depends on losses and evidence.');
|
|
4445
|
+
if (!coverage.generatedRanges) notes.push('Adapter does not declare generated-range coverage unless parse output includes generated spans.');
|
|
4446
|
+
if (!coverage.diagnostics) notes.push('Adapter did not declare parser diagnostics support.');
|
|
4447
|
+
if (context.language && context.parser) notes.push(`Coverage summary applies to ${context.language} via ${context.parser}.`);
|
|
4448
|
+
return notes;
|
|
4449
|
+
}
|
|
4450
|
+
|
|
3579
4451
|
function normalizeStringList(value) {
|
|
3580
4452
|
if (value === undefined || value === null) return [];
|
|
3581
4453
|
if (Array.isArray(value)) return value.map((item) => String(item)).filter(Boolean);
|
|
@@ -3669,6 +4541,7 @@ function adapterDiagnosticsEvidence(adapter, diagnostics, input) {
|
|
|
3669
4541
|
parserVersion: input.parserVersion,
|
|
3670
4542
|
sourceHash: input.sourceHash,
|
|
3671
4543
|
capabilities: adapter.capabilities,
|
|
4544
|
+
coverage: adapter.coverage,
|
|
3672
4545
|
supportedExtensions: adapter.supportedExtensions,
|
|
3673
4546
|
diagnostics: diagnostics.map(serializableDiagnostic),
|
|
3674
4547
|
errors,
|