@shapeshift-labs/frontier-lang-compiler 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/bench/smoke.mjs +42 -1
- package/dist/index.d.ts +386 -0
- package/dist/index.js +1724 -54
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -73,27 +73,42 @@ export const NativeImportTaxonomyKinds = Object.freeze([
|
|
|
73
73
|
'opaqueBodies',
|
|
74
74
|
'macroExpansion',
|
|
75
75
|
'preprocessor',
|
|
76
|
+
'conditionalCompilation',
|
|
76
77
|
'metaprogramming',
|
|
78
|
+
'reflection',
|
|
77
79
|
'generatedCode',
|
|
80
|
+
'overloadTypeInference',
|
|
78
81
|
'sourcePreservation',
|
|
82
|
+
'commentsTrivia',
|
|
79
83
|
'parserDiagnostics',
|
|
80
84
|
'unsupportedSyntax',
|
|
81
85
|
'partialSemanticIndex',
|
|
82
|
-
'sourceMapApproximation'
|
|
86
|
+
'sourceMapApproximation',
|
|
87
|
+
'targetProjectionLoss'
|
|
83
88
|
]);
|
|
84
89
|
|
|
85
90
|
export const NativeImportLossKinds = Object.freeze([
|
|
86
91
|
'declarationOnlyCoverage',
|
|
87
92
|
'opaqueNative',
|
|
88
93
|
'macroExpansion',
|
|
94
|
+
'macroHygiene',
|
|
89
95
|
'preprocessor',
|
|
96
|
+
'conditionalCompilation',
|
|
90
97
|
'metaprogramming',
|
|
98
|
+
'reflection',
|
|
99
|
+
'dynamicRuntime',
|
|
100
|
+
'dynamicDispatch',
|
|
91
101
|
'generatedCode',
|
|
102
|
+
'overloadResolution',
|
|
103
|
+
'typeInference',
|
|
92
104
|
'sourcePreservation',
|
|
105
|
+
'commentsTrivia',
|
|
93
106
|
'parserDiagnostic',
|
|
94
107
|
'unsupportedSyntax',
|
|
108
|
+
'unsupportedSemantic',
|
|
95
109
|
'partialSemanticIndex',
|
|
96
|
-
'sourceMapApproximation'
|
|
110
|
+
'sourceMapApproximation',
|
|
111
|
+
'targetProjectionLoss'
|
|
97
112
|
]);
|
|
98
113
|
|
|
99
114
|
export const NativeImportReadinessBySeverity = Object.freeze({
|
|
@@ -103,6 +118,62 @@ export const NativeImportReadinessBySeverity = Object.freeze({
|
|
|
103
118
|
error: 'blocked'
|
|
104
119
|
});
|
|
105
120
|
|
|
121
|
+
export const NativeImportLanguageProfiles = Object.freeze([
|
|
122
|
+
nativeImportLanguageProfile('javascript', {
|
|
123
|
+
aliases: ['js', 'mjs', 'cjs', 'jsx'],
|
|
124
|
+
extensions: ['.js', '.mjs', '.cjs', '.jsx'],
|
|
125
|
+
parserAdapters: ['estree', 'babel', 'tree-sitter'],
|
|
126
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime']
|
|
127
|
+
}),
|
|
128
|
+
nativeImportLanguageProfile('typescript', {
|
|
129
|
+
aliases: ['ts', 'tsx'],
|
|
130
|
+
extensions: ['.ts', '.tsx'],
|
|
131
|
+
parserAdapters: ['typescript-compiler-api', 'babel', 'tree-sitter'],
|
|
132
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax']
|
|
133
|
+
}),
|
|
134
|
+
nativeImportLanguageProfile('python', {
|
|
135
|
+
aliases: ['py'],
|
|
136
|
+
extensions: ['.py', '.pyi'],
|
|
137
|
+
parserAdapters: ['python-ast', 'libcst', 'parso', 'tree-sitter'],
|
|
138
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime']
|
|
139
|
+
}),
|
|
140
|
+
nativeImportLanguageProfile('rust', {
|
|
141
|
+
aliases: ['rs'],
|
|
142
|
+
extensions: ['.rs'],
|
|
143
|
+
parserAdapters: ['syn', 'rust-analyzer-rowan', 'tree-sitter'],
|
|
144
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation']
|
|
145
|
+
}),
|
|
146
|
+
nativeImportLanguageProfile('c', {
|
|
147
|
+
aliases: ['h'],
|
|
148
|
+
extensions: ['.c', '.h'],
|
|
149
|
+
parserAdapters: ['clang', 'libclang', 'tree-sitter'],
|
|
150
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'preprocessor', 'sourceMapApproximation', 'sourcePreservation']
|
|
151
|
+
}),
|
|
152
|
+
nativeImportLanguageProfile('cpp', {
|
|
153
|
+
aliases: ['c++', 'cc', 'cxx', 'hpp'],
|
|
154
|
+
extensions: ['.cc', '.cpp', '.cxx', '.hpp', '.hh'],
|
|
155
|
+
parserAdapters: ['clang', 'libclang', 'tree-sitter'],
|
|
156
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'preprocessor', 'metaprogramming', 'sourceMapApproximation', 'sourcePreservation']
|
|
157
|
+
}),
|
|
158
|
+
nativeImportLanguageProfile('java', { extensions: ['.java'], parserAdapters: ['javac', 'jdt', 'javaparser', 'tree-sitter'] }),
|
|
159
|
+
nativeImportLanguageProfile('go', { extensions: ['.go'], parserAdapters: ['go/parser', 'tree-sitter'] }),
|
|
160
|
+
nativeImportLanguageProfile('swift', { extensions: ['.swift'], parserAdapters: ['swift-syntax', 'tree-sitter'] }),
|
|
161
|
+
nativeImportLanguageProfile('csharp', { aliases: ['c#', 'cs'], extensions: ['.cs'], parserAdapters: ['roslyn', 'tree-sitter'] }),
|
|
162
|
+
nativeImportLanguageProfile('php', { extensions: ['.php'], parserAdapters: ['php-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'metaprogramming', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
163
|
+
nativeImportLanguageProfile('ruby', { aliases: ['rb'], extensions: ['.rb', '.rake'], parserAdapters: ['prism', 'ripper', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'metaprogramming', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
164
|
+
nativeImportLanguageProfile('kotlin', { aliases: ['kt', 'kts'], extensions: ['.kt', '.kts'], parserAdapters: ['kotlin-compiler', 'intellij-psi', 'tree-sitter'] }),
|
|
165
|
+
nativeImportLanguageProfile('scala', { aliases: ['sc'], extensions: ['.scala', '.sc'], parserAdapters: ['scala-compiler', 'scalameta', 'tree-sitter'] }),
|
|
166
|
+
nativeImportLanguageProfile('dart', { extensions: ['.dart'], parserAdapters: ['dart-analyzer', 'tree-sitter'] }),
|
|
167
|
+
nativeImportLanguageProfile('lua', { extensions: ['.lua'], parserAdapters: ['luaparse', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
168
|
+
nativeImportLanguageProfile('shell', { aliases: ['sh', 'bash', 'zsh'], extensions: ['.sh', '.bash', '.zsh'], parserAdapters: ['bash-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
169
|
+
nativeImportLanguageProfile('sql', { aliases: ['postgresql', 'postgres', 'mysql', 'sqlite'], extensions: ['.sql'], parserAdapters: ['sqlparser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'unsupportedSyntax', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
170
|
+
nativeImportLanguageProfile('zig', { extensions: ['.zig'], parserAdapters: ['zig-ast', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'generatedCode', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
171
|
+
nativeImportLanguageProfile('elixir', { aliases: ['ex', 'exs'], extensions: ['.ex', '.exs'], parserAdapters: ['elixir-quoted', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
172
|
+
nativeImportLanguageProfile('erlang', { aliases: ['erl', 'hrl'], extensions: ['.erl', '.hrl'], parserAdapters: ['erl_parse', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'preprocessor', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
173
|
+
nativeImportLanguageProfile('haskell', { aliases: ['hs'], extensions: ['.hs', '.lhs'], parserAdapters: ['ghc-api', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation'] }),
|
|
174
|
+
nativeImportLanguageProfile('r', { aliases: ['R'], extensions: ['.r', '.R'], parserAdapters: ['r-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] })
|
|
175
|
+
]);
|
|
176
|
+
|
|
106
177
|
export function normalizeCompileTarget(target) {
|
|
107
178
|
const normalized = String(target ?? 'typescript').toLowerCase();
|
|
108
179
|
const canonical = canonicalTargets[normalized] ?? normalized;
|
|
@@ -256,6 +327,181 @@ export function classifyNativeImportReadiness(losses = [], options = {}) {
|
|
|
256
327
|
};
|
|
257
328
|
}
|
|
258
329
|
|
|
330
|
+
export function createNativeImportCoverageMatrix(input = {}) {
|
|
331
|
+
const imports = input.imports ?? [];
|
|
332
|
+
const adapters = input.adapters ?? [];
|
|
333
|
+
const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters);
|
|
334
|
+
const languages = profiles.map((profile) => nativeImportCoverageForProfile(profile, imports, adapters));
|
|
335
|
+
const summary = languages.reduce((totals, entry) => {
|
|
336
|
+
totals.languages += 1;
|
|
337
|
+
if (entry.supportsLightweightScan) totals.lightweightScanners += 1;
|
|
338
|
+
if (entry.parserAdapters.length) totals.parserAdapterSlots += entry.parserAdapters.length;
|
|
339
|
+
totals.imports += entry.imports.total;
|
|
340
|
+
totals.symbols += entry.imports.symbols;
|
|
341
|
+
totals.sourceMaps += entry.imports.sourceMaps;
|
|
342
|
+
totals.sourceMapMappings += entry.imports.sourceMapMappings;
|
|
343
|
+
totals.losses += entry.imports.losses;
|
|
344
|
+
totals.byReadiness[entry.imports.readiness] = (totals.byReadiness[entry.imports.readiness] ?? 0) + 1;
|
|
345
|
+
for (const [kind, count] of Object.entries(entry.imports.lossKinds)) {
|
|
346
|
+
totals.lossKinds[kind] = (totals.lossKinds[kind] ?? 0) + count;
|
|
347
|
+
}
|
|
348
|
+
return totals;
|
|
349
|
+
}, {
|
|
350
|
+
languages: 0,
|
|
351
|
+
lightweightScanners: 0,
|
|
352
|
+
parserAdapterSlots: 0,
|
|
353
|
+
imports: 0,
|
|
354
|
+
symbols: 0,
|
|
355
|
+
sourceMaps: 0,
|
|
356
|
+
sourceMapMappings: 0,
|
|
357
|
+
losses: 0,
|
|
358
|
+
byReadiness: {},
|
|
359
|
+
lossKinds: {}
|
|
360
|
+
});
|
|
361
|
+
return {
|
|
362
|
+
kind: 'frontier.lang.nativeImportCoverageMatrix',
|
|
363
|
+
version: 1,
|
|
364
|
+
generatedAt: input.generatedAt ?? Date.now(),
|
|
365
|
+
languages,
|
|
366
|
+
summary,
|
|
367
|
+
metadata: {
|
|
368
|
+
compileTargets: [...FrontierCompileTargets],
|
|
369
|
+
note: 'Coverage is evidence and capability metadata, not a claim that every language feature is losslessly portable.'
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function createNativeSourcePreservation(options) {
|
|
375
|
+
if (!options || typeof options.sourceText !== 'string') {
|
|
376
|
+
throw new Error('createNativeSourcePreservation requires sourceText');
|
|
377
|
+
}
|
|
378
|
+
const language = options.language ?? 'source';
|
|
379
|
+
const sourceText = options.sourceText;
|
|
380
|
+
const computedSourceHash = hashSemanticValue(sourceText);
|
|
381
|
+
const declaredSourceHash = options.sourceHash;
|
|
382
|
+
const sourceHash = computedSourceHash;
|
|
383
|
+
const tokensAndTrivia = scanPreservedSourceTokens(sourceText, {
|
|
384
|
+
language,
|
|
385
|
+
sourcePath: options.sourcePath,
|
|
386
|
+
sourceHash,
|
|
387
|
+
includeTokens: options.includeTokens !== false,
|
|
388
|
+
includeTrivia: options.includeTrivia !== false,
|
|
389
|
+
maxTokens: options.maxTokens,
|
|
390
|
+
maxTrivia: options.maxTrivia
|
|
391
|
+
});
|
|
392
|
+
const directiveScan = options.includeDirectives === false
|
|
393
|
+
? { directives: [], truncated: false }
|
|
394
|
+
: scanPreservedSourceDirectives(sourceText, {
|
|
395
|
+
language,
|
|
396
|
+
sourcePath: options.sourcePath,
|
|
397
|
+
sourceHash,
|
|
398
|
+
maxDirectives: options.maxDirectives
|
|
399
|
+
});
|
|
400
|
+
const directives = directiveScan.directives;
|
|
401
|
+
const newline = detectNewlineStyle(sourceText);
|
|
402
|
+
return {
|
|
403
|
+
kind: 'frontier.lang.nativeSourcePreservation',
|
|
404
|
+
version: 1,
|
|
405
|
+
id: options.id ?? `native_source_preservation_${idFragment(options.sourcePath ?? language)}_${idFragment(sourceHash)}`,
|
|
406
|
+
language,
|
|
407
|
+
sourcePath: options.sourcePath,
|
|
408
|
+
sourceHash,
|
|
409
|
+
sourceBytes: Buffer.byteLength(sourceText, options.encoding ?? 'utf8'),
|
|
410
|
+
lineCount: sourceText.length ? sourceText.split(/\r\n|\r|\n/).length : 0,
|
|
411
|
+
newline,
|
|
412
|
+
encoding: options.encoding ?? 'utf8',
|
|
413
|
+
...(options.includeSourceText === false ? {} : { sourceText }),
|
|
414
|
+
tokens: tokensAndTrivia.tokens,
|
|
415
|
+
trivia: tokensAndTrivia.trivia,
|
|
416
|
+
directives,
|
|
417
|
+
summary: {
|
|
418
|
+
tokens: tokensAndTrivia.tokens.length,
|
|
419
|
+
trivia: tokensAndTrivia.trivia.length,
|
|
420
|
+
directives: directives.length,
|
|
421
|
+
comments: tokensAndTrivia.trivia.filter((entry) => entry.kind === 'comment').length,
|
|
422
|
+
whitespace: tokensAndTrivia.trivia.filter((entry) => entry.kind === 'whitespace' || entry.kind === 'newline').length,
|
|
423
|
+
exactSourceAvailable: options.includeSourceText !== false,
|
|
424
|
+
truncated: tokensAndTrivia.truncated || directiveScan.truncated
|
|
425
|
+
},
|
|
426
|
+
metadata: {
|
|
427
|
+
preservation: 'source-text-token-trivia-directive-evidence',
|
|
428
|
+
tokenization: 'frontier-lightweight-lexical-scan',
|
|
429
|
+
...(declaredSourceHash ? {
|
|
430
|
+
declaredSourceHash,
|
|
431
|
+
sourceHashVerified: declaredSourceHash === computedSourceHash
|
|
432
|
+
} : {}),
|
|
433
|
+
...options.metadata
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function createSemanticImportSidecar(importResult, options = {}) {
|
|
439
|
+
const imports = Array.isArray(importResult?.imports) ? importResult.imports : [importResult].filter(Boolean);
|
|
440
|
+
const importEntries = imports.map((imported, index) => semanticImportSidecarEntry(imported, index, options));
|
|
441
|
+
const symbols = importEntries.flatMap((entry) => entry.symbols);
|
|
442
|
+
const ownershipRegions = uniqueRecordsById(importEntries.flatMap((entry) => entry.ownershipRegions));
|
|
443
|
+
const sourceMaps = imports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? []);
|
|
444
|
+
const sourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap?.mappings ?? []);
|
|
445
|
+
const losses = imports.flatMap((imported) => imported?.losses ?? []);
|
|
446
|
+
const evidence = uniqueRecordsById(imports.flatMap((imported) => imported?.evidence ?? []));
|
|
447
|
+
const mergeCandidates = imports.flatMap((imported) => imported?.mergeCandidates ?? []);
|
|
448
|
+
const lossSummary = summarizeNativeImportLosses(losses, { evidence });
|
|
449
|
+
const readiness = mergeCandidates.reduce(
|
|
450
|
+
(current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
|
|
451
|
+
lossSummary.semanticMergeReadiness
|
|
452
|
+
);
|
|
453
|
+
const patchHints = ownershipRegions.map((region) => semanticPatchHintForRegion(region, readiness, options));
|
|
454
|
+
return {
|
|
455
|
+
kind: 'frontier.lang.semanticImportSidecar',
|
|
456
|
+
version: 1,
|
|
457
|
+
id: options.id ?? `semantic_import_${idFragment(importResult?.id ?? importResult?.projectRoot ?? imports[0]?.sourcePath ?? imports[0]?.language ?? 'source')}`,
|
|
458
|
+
generatedAt: options.generatedAt ?? Date.now(),
|
|
459
|
+
language: importResult?.language ?? (imports.length === 1 ? imports[0]?.language : 'mixed'),
|
|
460
|
+
projectRoot: importResult?.projectRoot,
|
|
461
|
+
imports: importEntries.map(({ ownershipRegions: _regions, symbols: _symbols, ...entry }) => entry),
|
|
462
|
+
symbols,
|
|
463
|
+
ownershipRegions,
|
|
464
|
+
sourceMaps: {
|
|
465
|
+
total: sourceMaps.length,
|
|
466
|
+
mappings: sourceMapMappings.length,
|
|
467
|
+
ids: sourceMaps.map((sourceMap) => sourceMap.id).filter(Boolean)
|
|
468
|
+
},
|
|
469
|
+
patchHints,
|
|
470
|
+
mergeCandidates: mergeCandidates.map((candidate) => ({
|
|
471
|
+
id: candidate.id,
|
|
472
|
+
readiness: candidate.readiness,
|
|
473
|
+
reasons: candidate.reasons ?? [],
|
|
474
|
+
risk: candidate.risk,
|
|
475
|
+
operationCount: candidate.operations?.length ?? candidate.patch?.operations?.length ?? 0
|
|
476
|
+
})),
|
|
477
|
+
losses: {
|
|
478
|
+
total: losses.length,
|
|
479
|
+
byKind: lossSummary.byKind,
|
|
480
|
+
bySeverity: lossSummary.bySeverity,
|
|
481
|
+
categories: lossSummary.categories,
|
|
482
|
+
blockingLossIds: lossSummary.blockingLossIds,
|
|
483
|
+
reviewLossIds: lossSummary.reviewLossIds
|
|
484
|
+
},
|
|
485
|
+
evidence: {
|
|
486
|
+
total: evidence.length,
|
|
487
|
+
failed: evidence.filter((record) => record.status === 'failed').map((record) => record.id),
|
|
488
|
+
ids: evidence.map((record) => record.id)
|
|
489
|
+
},
|
|
490
|
+
summary: {
|
|
491
|
+
imports: imports.length,
|
|
492
|
+
symbols: symbols.length,
|
|
493
|
+
ownershipRegions: ownershipRegions.length,
|
|
494
|
+
sourceMapMappings: sourceMapMappings.length,
|
|
495
|
+
readiness,
|
|
496
|
+
emptySemanticIndex: symbols.length === 0
|
|
497
|
+
},
|
|
498
|
+
metadata: {
|
|
499
|
+
note: 'Sidecar is source-addressable semantic evidence for merge admission; lightweight scanner regions remain review-required unless exact parser evidence upgrades readiness.',
|
|
500
|
+
...options.metadata
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
259
505
|
export function createEstreeNativeImporterAdapter(options = {}) {
|
|
260
506
|
return createJavaScriptSyntaxImporterAdapter({
|
|
261
507
|
id: 'frontier.estree-native-importer',
|
|
@@ -291,6 +537,20 @@ export function createTypeScriptCompilerNativeImporterAdapter(options = {}) {
|
|
|
291
537
|
parser: options.parser ?? 'typescript-compiler-api',
|
|
292
538
|
version: options.version,
|
|
293
539
|
capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
|
|
540
|
+
coverage: nativeImporterAdapterCoverage({
|
|
541
|
+
exactness: 'exact-parser-ast',
|
|
542
|
+
exactAst: true,
|
|
543
|
+
tokens: false,
|
|
544
|
+
trivia: false,
|
|
545
|
+
diagnostics: true,
|
|
546
|
+
sourceRanges: true,
|
|
547
|
+
generatedRanges: false,
|
|
548
|
+
semanticCoverage: declarationSemanticCoverage(),
|
|
549
|
+
notes: [
|
|
550
|
+
'Normalizes a caller-owned TypeScript SourceFile into native AST nodes and declaration-level semantic index records.',
|
|
551
|
+
'Type resolution, reference resolution, control flow, generated ranges, and parser token/trivia streams require host-supplied adapter evidence.'
|
|
552
|
+
]
|
|
553
|
+
}, options.coverage),
|
|
294
554
|
supportedExtensions: options.supportedExtensions ?? ['.ts', '.tsx', '.js', '.jsx'],
|
|
295
555
|
diagnostics: options.diagnostics,
|
|
296
556
|
parse(input) {
|
|
@@ -321,6 +581,20 @@ export function createTreeSitterNativeImporterAdapter(options = {}) {
|
|
|
321
581
|
parser: options.parserName ?? options.parser ?? 'tree-sitter',
|
|
322
582
|
version: options.version,
|
|
323
583
|
capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
|
|
584
|
+
coverage: nativeImporterAdapterCoverage({
|
|
585
|
+
exactness: 'parser-tree',
|
|
586
|
+
exactAst: true,
|
|
587
|
+
tokens: false,
|
|
588
|
+
trivia: false,
|
|
589
|
+
diagnostics: true,
|
|
590
|
+
sourceRanges: true,
|
|
591
|
+
generatedRanges: false,
|
|
592
|
+
semanticCoverage: declarationSemanticCoverage(),
|
|
593
|
+
notes: [
|
|
594
|
+
'Normalizes a caller-owned tree-sitter tree into native AST nodes and declaration-level semantic index records.',
|
|
595
|
+
'The built-in wrapper walks named syntax nodes; exact token/trivia streams and generated ranges require adapter-specific evidence.'
|
|
596
|
+
]
|
|
597
|
+
}, options.coverage),
|
|
324
598
|
supportedExtensions: options.supportedExtensions ?? [],
|
|
325
599
|
diagnostics: options.diagnostics,
|
|
326
600
|
parse(input) {
|
|
@@ -425,7 +699,14 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
425
699
|
parseResult.losses,
|
|
426
700
|
diagnostics.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, summary, parseInput))
|
|
427
701
|
);
|
|
428
|
-
const
|
|
702
|
+
const adapterSummary = {
|
|
703
|
+
...summary,
|
|
704
|
+
coverage: observeNativeImporterAdapterCoverage(summary.coverage, parseResult, {
|
|
705
|
+
diagnostics,
|
|
706
|
+
losses
|
|
707
|
+
})
|
|
708
|
+
};
|
|
709
|
+
const sourceEvidence = adapterDiagnosticsEvidence(adapterSummary, diagnostics, {
|
|
429
710
|
language,
|
|
430
711
|
parser,
|
|
431
712
|
parserVersion,
|
|
@@ -447,8 +728,9 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
447
728
|
metadata: {
|
|
448
729
|
adapterId: summary.id,
|
|
449
730
|
adapterVersion: summary.version,
|
|
450
|
-
adapterCapabilities:
|
|
451
|
-
|
|
731
|
+
adapterCapabilities: adapterSummary.capabilities,
|
|
732
|
+
adapterCoverage: adapterSummary.coverage,
|
|
733
|
+
supportedExtensions: adapterSummary.supportedExtensions,
|
|
452
734
|
diagnostics: diagnostics.map(serializableDiagnostic),
|
|
453
735
|
...input.metadata,
|
|
454
736
|
...parseResult.metadata
|
|
@@ -457,6 +739,7 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
457
739
|
adapterId: summary.id,
|
|
458
740
|
adapterVersion: summary.version,
|
|
459
741
|
parser,
|
|
742
|
+
adapterCoverage: adapterSummary.coverage,
|
|
460
743
|
...input.nativeAstMetadata,
|
|
461
744
|
...parseResult.nativeAstMetadata
|
|
462
745
|
},
|
|
@@ -464,6 +747,7 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
464
747
|
adapterId: summary.id,
|
|
465
748
|
adapterVersion: summary.version,
|
|
466
749
|
parser,
|
|
750
|
+
adapterCoverage: adapterSummary.coverage,
|
|
467
751
|
...input.nativeSourceMetadata,
|
|
468
752
|
...parseResult.nativeSourceMetadata
|
|
469
753
|
},
|
|
@@ -482,26 +766,119 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
|
|
|
482
766
|
};
|
|
483
767
|
return {
|
|
484
768
|
...importNativeSource(importInput),
|
|
485
|
-
adapter:
|
|
769
|
+
adapter: adapterSummary,
|
|
486
770
|
diagnostics
|
|
487
771
|
};
|
|
488
772
|
}
|
|
489
773
|
|
|
774
|
+
export function projectNativeImportToSource(importResult, options = {}) {
|
|
775
|
+
if (!importResult || typeof importResult !== 'object') {
|
|
776
|
+
throw new Error('projectNativeImportToSource requires a native import result');
|
|
777
|
+
}
|
|
778
|
+
const context = nativeImportProjectionContext(importResult, options);
|
|
779
|
+
const candidateSource = nativeProjectionSourceCandidate(context, options);
|
|
780
|
+
const declarations = nativeProjectionDeclarations(importResult, context);
|
|
781
|
+
const preserveSource = options.preferPreservedSource !== false && candidateSource?.exact === true;
|
|
782
|
+
const mode = preserveSource ? 'preserved-source' : 'native-source-stubs';
|
|
783
|
+
const sourceText = preserveSource
|
|
784
|
+
? candidateSource.sourceText
|
|
785
|
+
: renderNativeProjectionStubs(context, declarations, options);
|
|
786
|
+
const losses = preserveSource ? [] : nativeProjectionStubLosses(context, candidateSource, declarations, options);
|
|
787
|
+
const evidence = [{
|
|
788
|
+
id: options.evidenceId ?? `evidence_${context.idPart}_native_source_projection`,
|
|
789
|
+
kind: 'projection',
|
|
790
|
+
status: losses.some((loss) => loss.severity === 'error') ? 'failed' : 'passed',
|
|
791
|
+
path: context.sourcePath,
|
|
792
|
+
summary: preserveSource
|
|
793
|
+
? `Preserved exact ${context.language} source for native projection.`
|
|
794
|
+
: `Projected ${context.language} native import to ${declarations.length} declaration stub(s).`,
|
|
795
|
+
metadata: {
|
|
796
|
+
mode,
|
|
797
|
+
language: context.language,
|
|
798
|
+
sourcePath: context.sourcePath,
|
|
799
|
+
expectedSourceHash: context.sourceHash,
|
|
800
|
+
providedSourceHash: candidateSource?.sourceHash,
|
|
801
|
+
sourcePreservationId: candidateSource?.sourcePreservationId,
|
|
802
|
+
sourceHashVerified: candidateSource?.hashVerified ?? false,
|
|
803
|
+
declarationCount: declarations.length
|
|
804
|
+
}
|
|
805
|
+
}];
|
|
806
|
+
const lossSummary = summarizeNativeImportLosses(losses, {
|
|
807
|
+
evidence,
|
|
808
|
+
parser: context.parser,
|
|
809
|
+
scanKind: 'native-source-projection',
|
|
810
|
+
semanticStatus: context.semanticStatus
|
|
811
|
+
});
|
|
812
|
+
const readiness = classifyNativeImportReadiness(losses, {
|
|
813
|
+
evidence,
|
|
814
|
+
parser: context.parser,
|
|
815
|
+
scanKind: 'native-source-projection',
|
|
816
|
+
semanticStatus: context.semanticStatus
|
|
817
|
+
});
|
|
818
|
+
const nativeImportLossSummary = importResult.metadata?.nativeImportLossSummary ?? summarizeNativeImportLosses(importResult.losses ?? context.nativeAst?.losses ?? [], {
|
|
819
|
+
evidence: importResult.evidence,
|
|
820
|
+
parser: context.parser,
|
|
821
|
+
semanticStatus: context.semanticStatus
|
|
822
|
+
});
|
|
823
|
+
return {
|
|
824
|
+
kind: 'frontier.lang.nativeSourceProjection',
|
|
825
|
+
version: 1,
|
|
826
|
+
id: options.id ?? `native_source_projection_${context.idPart}`,
|
|
827
|
+
language: context.language,
|
|
828
|
+
sourcePath: context.sourcePath,
|
|
829
|
+
sourceHash: context.sourceHash,
|
|
830
|
+
mode,
|
|
831
|
+
sourceText,
|
|
832
|
+
outputHash: hashSemanticValue(sourceText),
|
|
833
|
+
declarations,
|
|
834
|
+
losses,
|
|
835
|
+
lossSummary,
|
|
836
|
+
readiness,
|
|
837
|
+
evidence,
|
|
838
|
+
metadata: {
|
|
839
|
+
nativeImportId: importResult.id,
|
|
840
|
+
nativeSourceId: context.nativeSource?.id,
|
|
841
|
+
nativeAstId: context.nativeAst?.id,
|
|
842
|
+
semanticIndexId: context.semanticIndex?.id,
|
|
843
|
+
universalAstId: importResult.universalAst?.id,
|
|
844
|
+
exactSourceAvailable: candidateSource?.exact === true,
|
|
845
|
+
sourceTextAvailable: typeof candidateSource?.sourceText === 'string',
|
|
846
|
+
sourcePreservationId: candidateSource?.sourcePreservationId,
|
|
847
|
+
sourceHashVerified: candidateSource?.hashVerified ?? false,
|
|
848
|
+
nativeImportLossSummary,
|
|
849
|
+
...options.metadata
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
490
854
|
export function importNativeSource(input) {
|
|
491
855
|
const language = input.language ?? input.nativeAst?.language;
|
|
492
856
|
if (!language) throw new Error('importNativeSource requires a language or nativeAst.language');
|
|
493
857
|
const sourcePath = input.sourcePath ?? input.nativeAst?.sourcePath;
|
|
494
|
-
const
|
|
858
|
+
const declaredSourceHash = input.sourceHash ?? input.nativeAst?.sourceHash;
|
|
859
|
+
const sourceHash = typeof input.sourceText === 'string'
|
|
860
|
+
? hashSemanticValue(input.sourceText)
|
|
861
|
+
: declaredSourceHash ?? hashSemanticValue(input.nativeAst?.nodes ?? input.nativeAst ?? {});
|
|
495
862
|
const targetPath = input.targetPath ?? input.target?.emitPath;
|
|
496
863
|
const targetHash = input.targetHash;
|
|
497
864
|
const importIdPart = idFragment(input.id ?? input.nativeSourceId ?? sourcePath ?? language);
|
|
865
|
+
const sourcePreservation = input.sourcePreservation ?? (typeof input.sourceText === 'string'
|
|
866
|
+
? createNativeSourcePreservation({
|
|
867
|
+
language,
|
|
868
|
+
sourcePath,
|
|
869
|
+
sourceHash: declaredSourceHash,
|
|
870
|
+
sourceText: input.sourceText,
|
|
871
|
+
metadata: { importIdPart }
|
|
872
|
+
})
|
|
873
|
+
: undefined);
|
|
498
874
|
const lightweight = !input.nativeAst && !input.nodes && input.sourceText
|
|
499
875
|
? createLightweightNativeImport({
|
|
500
876
|
language,
|
|
501
877
|
sourceText: input.sourceText,
|
|
502
878
|
sourcePath,
|
|
503
879
|
sourceHash,
|
|
504
|
-
parser: input.parser
|
|
880
|
+
parser: input.parser,
|
|
881
|
+
sourcePreservation
|
|
505
882
|
})
|
|
506
883
|
: undefined;
|
|
507
884
|
const nativeAst = input.nativeAst ?? createNativeAstRecord({
|
|
@@ -524,6 +901,15 @@ export function importNativeSource(input) {
|
|
|
524
901
|
losses: input.losses ?? lightweight?.losses,
|
|
525
902
|
metadata: {
|
|
526
903
|
...(input.sourceText ? { sourceBytes: input.sourceText.length } : {}),
|
|
904
|
+
...(sourcePreservation ? {
|
|
905
|
+
sourcePreservationId: sourcePreservation.id,
|
|
906
|
+
sourcePreservationSummary: sourcePreservation.summary,
|
|
907
|
+
sourcePreservation
|
|
908
|
+
} : {}),
|
|
909
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
910
|
+
declaredSourceHash,
|
|
911
|
+
sourceHashVerified: false
|
|
912
|
+
} : {}),
|
|
527
913
|
...lightweight?.metadata,
|
|
528
914
|
...input.nativeAstMetadata
|
|
529
915
|
}
|
|
@@ -548,6 +934,14 @@ export function importNativeSource(input) {
|
|
|
548
934
|
metadata: {
|
|
549
935
|
semanticStatus,
|
|
550
936
|
mappings: input.mappings ?? [],
|
|
937
|
+
...(sourcePreservation ? {
|
|
938
|
+
sourcePreservationId: sourcePreservation.id,
|
|
939
|
+
sourcePreservation
|
|
940
|
+
} : {}),
|
|
941
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
942
|
+
declaredSourceHash,
|
|
943
|
+
sourceHashVerified: false
|
|
944
|
+
} : {}),
|
|
551
945
|
...input.nativeSourceMetadata
|
|
552
946
|
}
|
|
553
947
|
});
|
|
@@ -571,7 +965,16 @@ export function importNativeSource(input) {
|
|
|
571
965
|
metadata: {
|
|
572
966
|
parser: nativeAst.parser,
|
|
573
967
|
sourcePath,
|
|
574
|
-
semanticStatus
|
|
968
|
+
semanticStatus,
|
|
969
|
+
...(sourcePreservation ? {
|
|
970
|
+
sourcePreservationId: sourcePreservation.id,
|
|
971
|
+
sourcePreservationSummary: sourcePreservation.summary
|
|
972
|
+
} : {})
|
|
973
|
+
,
|
|
974
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
975
|
+
declaredSourceHash,
|
|
976
|
+
sourceHashVerified: false
|
|
977
|
+
} : {})
|
|
575
978
|
}
|
|
576
979
|
}];
|
|
577
980
|
const lossSummary = summarizeNativeImportLosses(losses, {
|
|
@@ -651,6 +1054,14 @@ export function importNativeSource(input) {
|
|
|
651
1054
|
sourcePath,
|
|
652
1055
|
semanticStatus,
|
|
653
1056
|
nativeImportLossSummary: lossSummary,
|
|
1057
|
+
...(sourcePreservation ? {
|
|
1058
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1059
|
+
sourcePreservation
|
|
1060
|
+
} : {}),
|
|
1061
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1062
|
+
declaredSourceHash,
|
|
1063
|
+
sourceHashVerified: false
|
|
1064
|
+
} : {}),
|
|
654
1065
|
...input.universalAstMetadata
|
|
655
1066
|
}
|
|
656
1067
|
});
|
|
@@ -670,6 +1081,14 @@ export function importNativeSource(input) {
|
|
|
670
1081
|
semanticIndexId: semanticIndex?.id,
|
|
671
1082
|
universalAstId: universalAst.id,
|
|
672
1083
|
sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
|
|
1084
|
+
...(sourcePreservation ? {
|
|
1085
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1086
|
+
sourcePreservationSummary: sourcePreservation.summary
|
|
1087
|
+
} : {}),
|
|
1088
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1089
|
+
declaredSourceHash,
|
|
1090
|
+
sourceHashVerified: false
|
|
1091
|
+
} : {}),
|
|
673
1092
|
nativeImportLossSummary: lossSummary
|
|
674
1093
|
}
|
|
675
1094
|
});
|
|
@@ -692,6 +1111,14 @@ export function importNativeSource(input) {
|
|
|
692
1111
|
sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
|
|
693
1112
|
semanticStatus,
|
|
694
1113
|
mappings: resultSourceMapMappings,
|
|
1114
|
+
...(sourcePreservation ? {
|
|
1115
|
+
sourcePreservationId: sourcePreservation.id,
|
|
1116
|
+
sourcePreservation
|
|
1117
|
+
} : {}),
|
|
1118
|
+
...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
|
|
1119
|
+
declaredSourceHash,
|
|
1120
|
+
sourceHashVerified: false
|
|
1121
|
+
} : {}),
|
|
695
1122
|
nativeImportLossSummary: lossSummary,
|
|
696
1123
|
...input.metadata
|
|
697
1124
|
}
|
|
@@ -725,6 +1152,7 @@ function createLightweightNativeImport(input) {
|
|
|
725
1152
|
const evidenceId = `evidence_${idFragment(input.sourcePath ?? input.language)}_lightweight_scan`;
|
|
726
1153
|
|
|
727
1154
|
for (const declaration of declarations) {
|
|
1155
|
+
const ownershipRegion = semanticOwnershipRegionForDeclaration(input, declaration, documentId);
|
|
728
1156
|
nodes[rootId].children.push(declaration.nodeId);
|
|
729
1157
|
nodes[declaration.nodeId] = {
|
|
730
1158
|
id: declaration.nodeId,
|
|
@@ -733,7 +1161,11 @@ function createLightweightNativeImport(input) {
|
|
|
733
1161
|
span: declaration.span,
|
|
734
1162
|
value: declaration.name ?? declaration.importPath ?? null,
|
|
735
1163
|
fields: declaration.fields,
|
|
736
|
-
metadata:
|
|
1164
|
+
metadata: {
|
|
1165
|
+
...declaration.metadata,
|
|
1166
|
+
ownershipRegionId: ownershipRegion.id,
|
|
1167
|
+
ownershipRegionKey: ownershipRegion.key
|
|
1168
|
+
}
|
|
737
1169
|
};
|
|
738
1170
|
if (declaration.symbolId) {
|
|
739
1171
|
const occurrenceId = `occ_${idFragment(declaration.nodeId)}_def`;
|
|
@@ -745,7 +1177,11 @@ function createLightweightNativeImport(input) {
|
|
|
745
1177
|
language: input.language,
|
|
746
1178
|
nativeAstNodeId: declaration.nodeId,
|
|
747
1179
|
signatureHash: hashSemanticValue([input.language, declaration.kind, declaration.name, declaration.fields ?? {}]),
|
|
748
|
-
definitionSpan: declaration.span
|
|
1180
|
+
definitionSpan: declaration.span,
|
|
1181
|
+
metadata: {
|
|
1182
|
+
ownershipRegionId: ownershipRegion.id,
|
|
1183
|
+
ownershipRegionKey: ownershipRegion.key
|
|
1184
|
+
}
|
|
749
1185
|
});
|
|
750
1186
|
occurrences.push({
|
|
751
1187
|
id: occurrenceId,
|
|
@@ -766,6 +1202,11 @@ function createLightweightNativeImport(input) {
|
|
|
766
1202
|
predicate: 'nativeKind',
|
|
767
1203
|
subjectId: declaration.symbolId,
|
|
768
1204
|
value: declaration.languageKind
|
|
1205
|
+
}, {
|
|
1206
|
+
id: `fact_${idFragment(declaration.nodeId)}_ownership_region`,
|
|
1207
|
+
predicate: 'semanticOwnershipRegion',
|
|
1208
|
+
subjectId: declaration.symbolId,
|
|
1209
|
+
value: ownershipRegion
|
|
769
1210
|
});
|
|
770
1211
|
mappings.push({
|
|
771
1212
|
id: `map_${idFragment(declaration.nodeId)}`,
|
|
@@ -775,12 +1216,13 @@ function createLightweightNativeImport(input) {
|
|
|
775
1216
|
sourceSpan: declaration.span,
|
|
776
1217
|
evidenceIds: [evidenceId],
|
|
777
1218
|
lossIds: declaration.loss ? [declaration.loss.id] : [],
|
|
1219
|
+
ownershipRegionId: ownershipRegion.id,
|
|
778
1220
|
precision: 'declaration'
|
|
779
1221
|
});
|
|
780
1222
|
}
|
|
781
1223
|
if (declaration.loss) losses.push(declaration.loss);
|
|
782
1224
|
}
|
|
783
|
-
losses.push(...lightweightCoverageLosses(input, declarations));
|
|
1225
|
+
losses.push(...lightweightCoverageLosses(input, declarations, input.sourcePreservation));
|
|
784
1226
|
|
|
785
1227
|
const semanticIndex = createSemanticIndexRecord({
|
|
786
1228
|
id: `index_${idFragment(input.sourcePath ?? input.language)}`,
|
|
@@ -816,10 +1258,339 @@ function createLightweightNativeImport(input) {
|
|
|
816
1258
|
losses,
|
|
817
1259
|
semanticIndex,
|
|
818
1260
|
mappings,
|
|
819
|
-
metadata: {
|
|
1261
|
+
metadata: {
|
|
1262
|
+
parser,
|
|
1263
|
+
scanKind: 'lightweight-declaration-scan',
|
|
1264
|
+
declarationCount: declarations.length,
|
|
1265
|
+
...(input.sourcePreservation ? {
|
|
1266
|
+
sourcePreservationId: input.sourcePreservation.id,
|
|
1267
|
+
sourcePreservationSummary: input.sourcePreservation.summary
|
|
1268
|
+
} : {})
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function nativeImportProjectionContext(importResult, options) {
|
|
1274
|
+
const nativeSource = options.nativeSource
|
|
1275
|
+
?? importResult.nativeSource
|
|
1276
|
+
?? importResult.nativeSources?.[0]
|
|
1277
|
+
?? importResult.universalAst?.nativeSources?.[0];
|
|
1278
|
+
const nativeAst = options.nativeAst
|
|
1279
|
+
?? importResult.nativeAst
|
|
1280
|
+
?? nativeSource?.ast
|
|
1281
|
+
?? importResult.universalAst?.nativeSources?.[0]?.ast;
|
|
1282
|
+
const semanticIndex = options.semanticIndex
|
|
1283
|
+
?? importResult.semanticIndex
|
|
1284
|
+
?? importResult.universalAst?.semanticIndex;
|
|
1285
|
+
const language = options.language
|
|
1286
|
+
?? importResult.language
|
|
1287
|
+
?? nativeSource?.language
|
|
1288
|
+
?? nativeAst?.language
|
|
1289
|
+
?? importResult.universalAst?.metadata?.sourceLanguage
|
|
1290
|
+
?? 'source';
|
|
1291
|
+
const sourcePath = options.sourcePath
|
|
1292
|
+
?? importResult.sourcePath
|
|
1293
|
+
?? nativeSource?.sourcePath
|
|
1294
|
+
?? nativeAst?.sourcePath
|
|
1295
|
+
?? importResult.universalAst?.metadata?.sourcePath;
|
|
1296
|
+
const sourceHash = options.expectedSourceHash
|
|
1297
|
+
?? importResult.sourceHash
|
|
1298
|
+
?? nativeSource?.sourceHash
|
|
1299
|
+
?? nativeAst?.sourceHash;
|
|
1300
|
+
return {
|
|
1301
|
+
nativeSource,
|
|
1302
|
+
nativeAst,
|
|
1303
|
+
semanticIndex,
|
|
1304
|
+
language,
|
|
1305
|
+
sourcePath,
|
|
1306
|
+
sourceHash,
|
|
1307
|
+
parser: options.parser ?? nativeAst?.parser ?? nativeSource?.parser,
|
|
1308
|
+
semanticStatus: options.semanticStatus ?? importResult.metadata?.semanticStatus ?? nativeSource?.metadata?.semanticStatus,
|
|
1309
|
+
idPart: idFragment(options.id ?? importResult.id ?? nativeSource?.id ?? sourcePath ?? language)
|
|
820
1310
|
};
|
|
821
1311
|
}
|
|
822
1312
|
|
|
1313
|
+
function nativeProjectionSourceCandidate(context, options) {
|
|
1314
|
+
const preservation = sourcePreservationFromProjectionContext(context);
|
|
1315
|
+
const explicitSourceText = options.sourceText ?? options.preservedSourceText ?? options.exactSourceText;
|
|
1316
|
+
const sourceText = explicitSourceText ?? preservation?.sourceText;
|
|
1317
|
+
if (typeof sourceText !== 'string') return undefined;
|
|
1318
|
+
const computedSourceHash = hashSemanticValue(sourceText);
|
|
1319
|
+
const declaredSourceHash = options.sourceHash ?? (explicitSourceText === undefined ? preservation?.sourceHash : undefined);
|
|
1320
|
+
const sourceHash = computedSourceHash;
|
|
1321
|
+
const hashVerified = Boolean(context.sourceHash);
|
|
1322
|
+
const exact = !context.sourceHash || sourceHash === context.sourceHash || options.verifySourceHash === false;
|
|
1323
|
+
return {
|
|
1324
|
+
sourceText,
|
|
1325
|
+
sourceHash,
|
|
1326
|
+
declaredSourceHash,
|
|
1327
|
+
hashVerified,
|
|
1328
|
+
exact,
|
|
1329
|
+
mismatch: hashVerified && sourceHash !== context.sourceHash && options.verifySourceHash !== false,
|
|
1330
|
+
sourcePreservationId: preservation?.id
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
function sourcePreservationFromProjectionContext(context) {
|
|
1335
|
+
return context.nativeSource?.metadata?.sourcePreservation
|
|
1336
|
+
?? context.nativeAst?.metadata?.sourcePreservation
|
|
1337
|
+
?? context.nativeSource?.ast?.metadata?.sourcePreservation;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
function nativeProjectionDeclarations(importResult, context) {
|
|
1341
|
+
const semanticIndex = context.semanticIndex;
|
|
1342
|
+
const occurrencesBySymbol = new Map();
|
|
1343
|
+
for (const occurrence of semanticIndex?.occurrences ?? []) {
|
|
1344
|
+
const list = occurrencesBySymbol.get(occurrence.symbolId) ?? [];
|
|
1345
|
+
list.push(occurrence);
|
|
1346
|
+
occurrencesBySymbol.set(occurrence.symbolId, list);
|
|
1347
|
+
}
|
|
1348
|
+
const declarations = (semanticIndex?.symbols ?? [])
|
|
1349
|
+
.filter((symbol) => !nativeProjectionImportOnlySymbol(symbol, occurrencesBySymbol.get(symbol.id)))
|
|
1350
|
+
.map((symbol) => {
|
|
1351
|
+
const occurrence = occurrencesBySymbol.get(symbol.id)?.find((item) => item.role !== 'import');
|
|
1352
|
+
const mapping = (importResult.sourceMaps ?? importResult.universalAst?.sourceMaps ?? [])
|
|
1353
|
+
.flatMap((sourceMap) => sourceMap.mappings ?? [])
|
|
1354
|
+
.find((item) => item.semanticSymbolId === symbol.id);
|
|
1355
|
+
return {
|
|
1356
|
+
name: symbol.name,
|
|
1357
|
+
kind: nativeProjectionDeclarationKind(symbol.kind),
|
|
1358
|
+
symbolId: symbol.id,
|
|
1359
|
+
nativeAstNodeId: symbol.nativeAstNodeId ?? occurrence?.nativeAstNodeId,
|
|
1360
|
+
sourceSpan: symbol.definitionSpan ?? occurrence?.span ?? mapping?.sourceSpan,
|
|
1361
|
+
ownershipRegionId: mapping?.ownershipRegionId ?? symbol.metadata?.ownershipRegionId,
|
|
1362
|
+
metadata: {
|
|
1363
|
+
semanticKind: symbol.kind,
|
|
1364
|
+
language: symbol.language,
|
|
1365
|
+
signatureHash: symbol.signatureHash
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
})
|
|
1369
|
+
.filter((declaration) => declaration.name);
|
|
1370
|
+
if (declarations.length) return uniqueNativeProjectionDeclarations(declarations);
|
|
1371
|
+
return uniqueNativeProjectionDeclarations(Object.values(context.nativeAst?.nodes ?? {})
|
|
1372
|
+
.map((node) => {
|
|
1373
|
+
const name = typeof node.value === 'string' && node.value.trim() ? node.value.trim() : node.fields?.name;
|
|
1374
|
+
const kind = nativeProjectionKindForNode(node);
|
|
1375
|
+
if (!name || !kind) return undefined;
|
|
1376
|
+
return {
|
|
1377
|
+
name,
|
|
1378
|
+
kind,
|
|
1379
|
+
nativeAstNodeId: node.id,
|
|
1380
|
+
sourceSpan: node.span,
|
|
1381
|
+
ownershipRegionId: node.metadata?.ownershipRegionId,
|
|
1382
|
+
metadata: { nativeKind: node.kind, language: context.language }
|
|
1383
|
+
};
|
|
1384
|
+
})
|
|
1385
|
+
.filter(Boolean));
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
function nativeProjectionImportOnlySymbol(symbol, occurrences = []) {
|
|
1389
|
+
if (String(symbol.id ?? '').includes(':import:')) return true;
|
|
1390
|
+
if (occurrences.length && occurrences.every((occurrence) => occurrence.role === 'import')) return true;
|
|
1391
|
+
return symbol.kind === 'module' && occurrences.some((occurrence) => occurrence.role === 'import');
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
function nativeProjectionDeclarationKind(kind) {
|
|
1395
|
+
const normalized = String(kind ?? 'value').toLowerCase();
|
|
1396
|
+
if (normalized === 'function' || normalized === 'method' || normalized === 'procedure') return 'function';
|
|
1397
|
+
if (normalized === 'class') return 'class';
|
|
1398
|
+
if (normalized === 'interface' || normalized === 'protocol') return 'interface';
|
|
1399
|
+
if (normalized === 'trait') return 'trait';
|
|
1400
|
+
if (normalized === 'type' || normalized === 'struct' || normalized === 'enum' || normalized === 'record') return 'type';
|
|
1401
|
+
if (normalized === 'constant' || normalized === 'const') return 'constant';
|
|
1402
|
+
if (normalized === 'variable' || normalized === 'property' || normalized === 'field') return 'variable';
|
|
1403
|
+
if (normalized === 'module' || normalized === 'namespace' || normalized === 'package') return 'module';
|
|
1404
|
+
return normalized;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
function nativeProjectionKindForNode(node) {
|
|
1408
|
+
const kind = String(node?.kind ?? node?.languageKind ?? '').toLowerCase();
|
|
1409
|
+
if (/function|method|procedure|funcdecl|itemfn|fndeclaration|\bdef\b/.test(kind)) return 'function';
|
|
1410
|
+
if (/class/.test(kind)) return 'class';
|
|
1411
|
+
if (/interface|protocol/.test(kind)) return 'interface';
|
|
1412
|
+
if (/trait/.test(kind)) return 'trait';
|
|
1413
|
+
if (/struct|enum|record|typedef|typealias|type/.test(kind)) return 'type';
|
|
1414
|
+
if (/const|constant|macro|define/.test(kind)) return 'constant';
|
|
1415
|
+
if (/var|property|field/.test(kind)) return 'variable';
|
|
1416
|
+
if (/module|namespace|package/.test(kind)) return 'module';
|
|
1417
|
+
return undefined;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
function uniqueNativeProjectionDeclarations(declarations) {
|
|
1421
|
+
const seen = new Set();
|
|
1422
|
+
return declarations.filter((declaration) => {
|
|
1423
|
+
const key = `${declaration.kind}:${declaration.name}:${declaration.symbolId ?? declaration.nativeAstNodeId ?? ''}`;
|
|
1424
|
+
if (seen.has(key)) return false;
|
|
1425
|
+
seen.add(key);
|
|
1426
|
+
return true;
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
function nativeProjectionStubLosses(context, candidateSource, declarations, options) {
|
|
1431
|
+
const reason = candidateSource?.mismatch
|
|
1432
|
+
? 'source-hash-mismatch'
|
|
1433
|
+
: options.preferPreservedSource === false && candidateSource?.sourceText
|
|
1434
|
+
? 'preserved-source-disabled'
|
|
1435
|
+
: 'exact-source-unavailable';
|
|
1436
|
+
const message = candidateSource?.mismatch
|
|
1437
|
+
? 'Provided native source text hash did not match the import result source hash; emitted declaration stubs instead of preserving stale source.'
|
|
1438
|
+
: options.preferPreservedSource === false && candidateSource?.sourceText
|
|
1439
|
+
? 'Native source projection was asked to emit declaration stubs instead of preserving available source text.'
|
|
1440
|
+
: 'Exact native source text was not provided; emitted declaration stubs reconstructed from import metadata.';
|
|
1441
|
+
const losses = [nativeProjectionLoss(context, {
|
|
1442
|
+
id: `loss_${context.idPart}_native_source_stub`,
|
|
1443
|
+
kind: candidateSource?.mismatch ? 'sourcePreservation' : 'targetProjectionLoss',
|
|
1444
|
+
severity: 'warning',
|
|
1445
|
+
message,
|
|
1446
|
+
metadata: {
|
|
1447
|
+
reason,
|
|
1448
|
+
projectionMode: 'native-source-stubs',
|
|
1449
|
+
expectedSourceHash: context.sourceHash,
|
|
1450
|
+
providedSourceHash: candidateSource?.sourceHash,
|
|
1451
|
+
declaredSourceHash: candidateSource?.declaredSourceHash
|
|
1452
|
+
}
|
|
1453
|
+
})];
|
|
1454
|
+
if (!declarations.length) {
|
|
1455
|
+
losses.push(nativeProjectionLoss(context, {
|
|
1456
|
+
id: `loss_${context.idPart}_native_source_stub_empty`,
|
|
1457
|
+
kind: 'declarationOnlyCoverage',
|
|
1458
|
+
severity: 'warning',
|
|
1459
|
+
message: 'Native import result did not expose semantic declarations for source stub generation.',
|
|
1460
|
+
metadata: { reason: 'no-stub-declarations', projectionMode: 'native-source-stubs' }
|
|
1461
|
+
}));
|
|
1462
|
+
}
|
|
1463
|
+
return losses;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function nativeProjectionLoss(context, input) {
|
|
1467
|
+
const rootSpan = context.nativeAst?.nodes?.[context.nativeAst.rootId]?.span;
|
|
1468
|
+
return {
|
|
1469
|
+
id: input.id,
|
|
1470
|
+
severity: input.severity,
|
|
1471
|
+
phase: 'emit',
|
|
1472
|
+
sourceFormat: context.language,
|
|
1473
|
+
kind: input.kind,
|
|
1474
|
+
message: input.message,
|
|
1475
|
+
span: rootSpan ?? {
|
|
1476
|
+
sourceId: context.sourceHash,
|
|
1477
|
+
path: context.sourcePath,
|
|
1478
|
+
startLine: 1,
|
|
1479
|
+
startColumn: 1
|
|
1480
|
+
},
|
|
1481
|
+
metadata: {
|
|
1482
|
+
nativeSourceId: context.nativeSource?.id,
|
|
1483
|
+
nativeAstId: context.nativeAst?.id,
|
|
1484
|
+
parser: context.parser,
|
|
1485
|
+
...input.metadata
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function renderNativeProjectionStubs(context, declarations, options) {
|
|
1491
|
+
const language = String(context.language ?? 'source').toLowerCase();
|
|
1492
|
+
const header = nativeProjectionStubHeader(language, context, options);
|
|
1493
|
+
let body;
|
|
1494
|
+
if (language === 'typescript' || language === 'ts') body = renderTypeScriptProjectionStubs(declarations);
|
|
1495
|
+
else if (language === 'javascript' || language === 'js') body = renderJavaScriptProjectionStubs(declarations);
|
|
1496
|
+
else if (language === 'python' || language === 'py') body = renderPythonProjectionStubs(declarations);
|
|
1497
|
+
else if (language === 'rust' || language === 'rs') body = renderRustProjectionStubs(declarations);
|
|
1498
|
+
else if (language === 'c' || language === 'cpp' || language === 'c++' || language === 'h') body = renderCProjectionStubs(declarations);
|
|
1499
|
+
else body = renderGenericProjectionStubs(declarations, language);
|
|
1500
|
+
return ensureTrailingNewline([header, body].filter(Boolean).join('\n'));
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
function nativeProjectionStubHeader(language, context, options) {
|
|
1504
|
+
if (options.stubBanner === false) return '';
|
|
1505
|
+
if (typeof options.stubBanner === 'string') return ensureTrailingNewline(options.stubBanner).trimEnd();
|
|
1506
|
+
const comment = nativeProjectionLineComment(language);
|
|
1507
|
+
const suffix = context.sourcePath ? ` for ${oneLine(context.sourcePath)}` : '';
|
|
1508
|
+
return [
|
|
1509
|
+
`${comment} Frontier native source stubs${suffix}`,
|
|
1510
|
+
`${comment} Exact source text was unavailable; declarations are reconstructed from native import metadata.`
|
|
1511
|
+
].join('\n');
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
function nativeProjectionLineComment(language) {
|
|
1515
|
+
if (language === 'python' || language === 'py' || language === 'ruby' || language === 'rb' || language === 'shell' || language === 'sh') return '#';
|
|
1516
|
+
if (language === 'sql') return '--';
|
|
1517
|
+
return '//';
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
function renderJavaScriptProjectionStubs(declarations) {
|
|
1521
|
+
const used = new Set();
|
|
1522
|
+
if (!declarations.length) return 'export {};';
|
|
1523
|
+
return declarations.map((declaration) => {
|
|
1524
|
+
const name = reserveUniqueId(safeProjectionIdentifier(declaration.name), used);
|
|
1525
|
+
if (declaration.kind === 'function') return `export function ${name}(...args) {\n throw new Error('Frontier native source stub: implementation unavailable.');\n}`;
|
|
1526
|
+
if (declaration.kind === 'class') return `export class ${name} {}`;
|
|
1527
|
+
return `export const ${name} = undefined;`;
|
|
1528
|
+
}).join('\n\n');
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
function renderTypeScriptProjectionStubs(declarations) {
|
|
1532
|
+
const used = new Set();
|
|
1533
|
+
if (!declarations.length) return 'export {};';
|
|
1534
|
+
return declarations.map((declaration) => {
|
|
1535
|
+
const name = reserveUniqueId(safeProjectionIdentifier(declaration.name), used);
|
|
1536
|
+
if (declaration.kind === 'function') return `export function ${name}(...args: unknown[]): never {\n throw new Error('Frontier native source stub: implementation unavailable.');\n}`;
|
|
1537
|
+
if (declaration.kind === 'class') return `export class ${name} {}`;
|
|
1538
|
+
if (declaration.kind === 'interface') return `export interface ${name} {}`;
|
|
1539
|
+
if (declaration.kind === 'type' || declaration.kind === 'trait') return `export type ${name} = unknown;`;
|
|
1540
|
+
return `export const ${name}: unknown = undefined;`;
|
|
1541
|
+
}).join('\n\n');
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function renderPythonProjectionStubs(declarations) {
|
|
1545
|
+
if (!declarations.length) return 'pass';
|
|
1546
|
+
return declarations.map((declaration) => {
|
|
1547
|
+
const name = safeProjectionIdentifier(declaration.name);
|
|
1548
|
+
if (declaration.kind === 'function') return `def ${name}(*args, **kwargs):\n raise NotImplementedError("Frontier native source stub")`;
|
|
1549
|
+
if (declaration.kind === 'class') return `class ${name}:\n pass`;
|
|
1550
|
+
return `${name} = None`;
|
|
1551
|
+
}).join('\n\n');
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
function renderRustProjectionStubs(declarations) {
|
|
1555
|
+
if (!declarations.length) return '';
|
|
1556
|
+
return declarations.map((declaration) => {
|
|
1557
|
+
const name = safeProjectionIdentifier(declaration.name);
|
|
1558
|
+
if (declaration.kind === 'function') return `pub fn ${name}() {\n unimplemented!(\"Frontier native source stub\");\n}`;
|
|
1559
|
+
if (declaration.kind === 'type' || declaration.kind === 'class') return `pub struct ${upperFirst(name)};`;
|
|
1560
|
+
return `pub const ${name.toUpperCase()}: () = ();`;
|
|
1561
|
+
}).join('\n\n');
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function renderCProjectionStubs(declarations) {
|
|
1565
|
+
if (!declarations.length) return '';
|
|
1566
|
+
return declarations.map((declaration) => {
|
|
1567
|
+
const name = safeProjectionIdentifier(declaration.name);
|
|
1568
|
+
if (declaration.kind === 'function') return `void ${name}(void);`;
|
|
1569
|
+
if (declaration.kind === 'type' || declaration.kind === 'class') return `typedef struct ${upperFirst(name)} ${upperFirst(name)};`;
|
|
1570
|
+
return `extern const int ${name};`;
|
|
1571
|
+
}).join('\n');
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
function renderGenericProjectionStubs(declarations, language) {
|
|
1575
|
+
if (!declarations.length) return `${nativeProjectionLineComment(language)} no declarations available`;
|
|
1576
|
+
return declarations.map((declaration) => `${declaration.kind} ${declaration.name}`).join('\n');
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
function safeProjectionIdentifier(name) {
|
|
1580
|
+
const text = String(name ?? 'value').split('.').at(-1).replace(/[^A-Za-z0-9_$]/g, '_');
|
|
1581
|
+
const identifier = text || 'value';
|
|
1582
|
+
return /^[A-Za-z_$]/.test(identifier) ? identifier : `_${identifier}`;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
function ensureTrailingNewline(value) {
|
|
1586
|
+
const text = String(value ?? '');
|
|
1587
|
+
return text.endsWith('\n') ? text : `${text}\n`;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
function oneLine(value) {
|
|
1591
|
+
return String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
1592
|
+
}
|
|
1593
|
+
|
|
823
1594
|
function scanNativeDeclarations(input) {
|
|
824
1595
|
const language = String(input.language).toLowerCase();
|
|
825
1596
|
if (language === 'javascript' || language === 'typescript') return scanJavaScriptLike(input);
|
|
@@ -848,23 +1619,63 @@ function scanNativeDeclarations(input) {
|
|
|
848
1619
|
|
|
849
1620
|
function scanJavaScriptLike(input) {
|
|
850
1621
|
const declarations = [];
|
|
1622
|
+
let currentClass;
|
|
1623
|
+
let classDepth = 0;
|
|
851
1624
|
for (const { line, number } of sourceLines(input.sourceText)) {
|
|
852
1625
|
const trimmed = line.trim();
|
|
1626
|
+
const declarationLine = trimmed.replace(/^(?:export\s+)?(?:declare\s+)?/, '');
|
|
853
1627
|
let match;
|
|
854
|
-
if ((match = trimmed.match(/^
|
|
855
|
-
declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
|
|
856
|
-
} else if ((match = trimmed.match(/^(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/))) {
|
|
857
|
-
declarations.push(nativeDeclaration(input, number, 'ClassDeclaration', 'class', match[1], {}, trimmed.includes('{')));
|
|
858
|
-
} else if ((match = trimmed.match(/^(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/))) {
|
|
859
|
-
declarations.push(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, trimmed.includes('{')));
|
|
860
|
-
} else if ((match = trimmed.match(/^(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*=/))) {
|
|
861
|
-
declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
|
|
862
|
-
} else if ((match = trimmed.match(/^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
|
|
863
|
-
declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
|
|
864
|
-
} else if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
|
|
1628
|
+
if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
|
|
865
1629
|
declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'module'));
|
|
866
|
-
} else if ((match = trimmed.match(/^
|
|
1630
|
+
} else if ((match = trimmed.match(/^import\s*\(\s*['"]([^'"]+)['"]\s*\)/))) {
|
|
1631
|
+
declarations.push(nativeImportDeclaration(input, number, match[1], 'DynamicImportExpression', 'module'));
|
|
1632
|
+
} else if ((match = trimmed.match(/^export\s+(?:\*\s+from|\{[^}]*\}\s+from)\s+['"]([^'"]+)['"]/))) {
|
|
867
1633
|
declarations.push(nativeImportDeclaration(input, number, match[1], 'ExportFromDeclaration', 'module'));
|
|
1634
|
+
} else if ((match = declarationLine.match(/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)/))) {
|
|
1635
|
+
declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, declarationLine.includes('{')));
|
|
1636
|
+
} else if ((match = trimmed.match(/^export\s+default\s+(?:async\s+)?function\*?\s*([A-Za-z_$][\w$]*)?\s*\(([^)]*)\)/))) {
|
|
1637
|
+
declarations.push(nativeDeclaration(input, number, 'ExportDefaultFunctionDeclaration', 'function', match[1] ?? 'default', { parameters: splitParameters(match[2]), exportDefault: true }, trimmed.includes('{')));
|
|
1638
|
+
} else if ((match = declarationLine.match(/^(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/))) {
|
|
1639
|
+
declarations.push(nativeDeclaration(input, number, 'ClassDeclaration', 'class', match[1], {}, declarationLine.includes('{')));
|
|
1640
|
+
if (declarationLine.includes('{') && !declarationLine.includes('}')) {
|
|
1641
|
+
currentClass = match[1];
|
|
1642
|
+
classDepth = 0;
|
|
1643
|
+
}
|
|
1644
|
+
} else if ((match = declarationLine.match(/^interface\s+([A-Za-z_$][\w$]*)/))) {
|
|
1645
|
+
declarations.push(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, declarationLine.includes('{')));
|
|
1646
|
+
} else if ((match = declarationLine.match(/^(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)/))) {
|
|
1647
|
+
declarations.push(nativeDeclaration(input, number, 'EnumDeclaration', 'type', match[1], {}, declarationLine.includes('{')));
|
|
1648
|
+
} else if ((match = declarationLine.match(/^(?:namespace|module)\s+([A-Za-z_$][\w$.]*)/))) {
|
|
1649
|
+
declarations.push(nativeDeclaration(input, number, 'ModuleDeclaration', 'module', match[1], {}, declarationLine.includes('{')));
|
|
1650
|
+
} else if ((match = declarationLine.match(/^type\s+([A-Za-z_$][\w$]*)\s*=/))) {
|
|
1651
|
+
declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
|
|
1652
|
+
} else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
|
|
1653
|
+
declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
|
|
1654
|
+
} else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/))) {
|
|
1655
|
+
declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
|
|
1656
|
+
} else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?function\*?\s*\(([^)]*)\)/))) {
|
|
1657
|
+
declarations.push(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
|
|
1658
|
+
} else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
|
|
1659
|
+
declarations.push(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false));
|
|
1660
|
+
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
|
|
1661
|
+
declarations.push(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
|
|
1662
|
+
methodName: match[1],
|
|
1663
|
+
owner: currentClass,
|
|
1664
|
+
parameters: splitParameters(match[2])
|
|
1665
|
+
}, declarationLine.includes('{') || declarationLine.includes('=>')));
|
|
1666
|
+
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*([A-Za-z_$][\w$]*)\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
|
|
1667
|
+
declarations.push(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
|
|
1668
|
+
propertyName: match[1],
|
|
1669
|
+
owner: currentClass,
|
|
1670
|
+
valueType: match[2]?.trim()
|
|
1671
|
+
}, false));
|
|
1672
|
+
}
|
|
1673
|
+
if (currentClass) {
|
|
1674
|
+
classDepth += braceDelta(trimmed);
|
|
1675
|
+
if (classDepth <= 0) {
|
|
1676
|
+
currentClass = undefined;
|
|
1677
|
+
classDepth = 0;
|
|
1678
|
+
}
|
|
868
1679
|
}
|
|
869
1680
|
}
|
|
870
1681
|
return declarations;
|
|
@@ -953,17 +1764,41 @@ function scanJava(input) {
|
|
|
953
1764
|
|
|
954
1765
|
function scanGo(input) {
|
|
955
1766
|
const declarations = [];
|
|
1767
|
+
let inImportBlock = false;
|
|
956
1768
|
for (const { line, number } of sourceLines(input.sourceText)) {
|
|
957
1769
|
const trimmed = line.trim();
|
|
958
1770
|
let match;
|
|
1771
|
+
if (inImportBlock) {
|
|
1772
|
+
if (trimmed === ')') {
|
|
1773
|
+
inImportBlock = false;
|
|
1774
|
+
} else if ((match = trimmed.match(/^(?:(?:[A-Za-z_]\w*|[_.])\s+)?["']([^"']+)["']/))) {
|
|
1775
|
+
declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportSpec', 'package'));
|
|
1776
|
+
}
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
959
1779
|
if ((match = trimmed.match(/^package\s+([A-Za-z_]\w*)/))) {
|
|
960
1780
|
declarations.push(nativeDeclaration(input, number, 'PackageClause', 'package', match[1], {}, false));
|
|
961
|
-
} else if (
|
|
1781
|
+
} else if (/^import\s*\(/.test(trimmed)) {
|
|
1782
|
+
inImportBlock = true;
|
|
1783
|
+
} else if ((match = trimmed.match(/^import\s+(?:(?:[A-Za-z_]\w*|[_.])\s+)?["']([^"']+)["']/))) {
|
|
962
1784
|
declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportSpec', 'package'));
|
|
1785
|
+
} else if ((match = trimmed.match(/^type\s+([A-Za-z_]\w*)\s*=\s*(.+)$/))) {
|
|
1786
|
+
declarations.push(nativeDeclaration(input, number, 'TypeAlias', 'type', match[1], { target: match[2].trim() }, false));
|
|
963
1787
|
} else if ((match = trimmed.match(/^type\s+([A-Za-z_]\w*)\s+(struct|interface)\b/))) {
|
|
964
1788
|
declarations.push(nativeDeclaration(input, number, match[2] === 'struct' ? 'TypeSpecStruct' : 'TypeSpecInterface', 'type', match[1], {}, trimmed.includes('{')));
|
|
965
|
-
} else if ((match = trimmed.match(/^func\s
|
|
966
|
-
|
|
1789
|
+
} else if ((match = trimmed.match(/^func\s+\(([^)]*)\)\s*([A-Za-z_]\w*)(?:\s*\[([^\]]+)\])?\s*\(([^)]*)\)/))) {
|
|
1790
|
+
const receiver = parseGoReceiver(match[1]);
|
|
1791
|
+
declarations.push(nativeDeclaration(input, number, 'MethodDecl', 'method', goReceiverMethodName(receiver, match[2]), {
|
|
1792
|
+
methodName: match[2],
|
|
1793
|
+
receiver,
|
|
1794
|
+
typeParameters: splitTypeParameters(match[3]),
|
|
1795
|
+
parameters: splitParameters(match[4])
|
|
1796
|
+
}, trimmed.includes('{')));
|
|
1797
|
+
} else if ((match = trimmed.match(/^func\s+([A-Za-z_]\w*)(?:\s*\[([^\]]+)\])?\s*\(([^)]*)\)/))) {
|
|
1798
|
+
declarations.push(nativeDeclaration(input, number, 'FuncDecl', 'function', match[1], {
|
|
1799
|
+
typeParameters: splitTypeParameters(match[2]),
|
|
1800
|
+
parameters: splitParameters(match[3])
|
|
1801
|
+
}, trimmed.includes('{')));
|
|
967
1802
|
} else if ((match = trimmed.match(/^var\s+([A-Za-z_]\w*)\b/))) {
|
|
968
1803
|
declarations.push(nativeDeclaration(input, number, 'VarDecl', 'variable', match[1], {}, false));
|
|
969
1804
|
} else if ((match = trimmed.match(/^const\s+([A-Za-z_]\w*)\b/))) {
|
|
@@ -975,17 +1810,35 @@ function scanGo(input) {
|
|
|
975
1810
|
|
|
976
1811
|
function scanSwift(input) {
|
|
977
1812
|
const declarations = [];
|
|
1813
|
+
const protocols = new Set();
|
|
978
1814
|
for (const { line, number } of sourceLines(input.sourceText)) {
|
|
979
1815
|
const trimmed = line.trim();
|
|
1816
|
+
const declarationLine = trimmed.replace(/^(?:@[A-Za-z_][\w.]+(?:\([^)]*\))?\s+)*/, '');
|
|
980
1817
|
let match;
|
|
981
|
-
if ((match =
|
|
1818
|
+
if ((match = declarationLine.match(/^import\s+(?:(?:struct|class|enum|protocol|func|var)\s+)?([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
|
|
982
1819
|
declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDecl', 'module'));
|
|
983
|
-
} else if ((match =
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1820
|
+
} else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|final|indirect)\s+)*(struct|class|enum|protocol|actor)\s+([A-Za-z_]\w*)/))) {
|
|
1821
|
+
if (match[1] === 'protocol') protocols.add(match[2]);
|
|
1822
|
+
declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Decl`, swiftSymbolKind(match[1]), match[2], {}, declarationLine.includes('{')));
|
|
1823
|
+
} else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open)\s+)*extension\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)(.*)$/))) {
|
|
1824
|
+
const extensionFields = parseSwiftExtensionTail(match[2]);
|
|
1825
|
+
const isProtocolExtension = protocols.has(match[1]) || /Protocol$/.test(match[1]);
|
|
1826
|
+
declarations.push(nativeDeclaration(input, number, isProtocolExtension ? 'ProtocolExtensionDecl' : 'ExtensionDecl', 'implementation', `${match[1]}.${isProtocolExtension ? 'protocolExtension' : 'extension'}`, {
|
|
1827
|
+
extendedType: match[1],
|
|
1828
|
+
...extensionFields
|
|
1829
|
+
}, declarationLine.includes('{')));
|
|
1830
|
+
} else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|static|class|mutating|nonmutating|override|required|convenience|isolated|nonisolated)\s+)*func\s+([A-Za-z_]\w*|`[^`]+`)(?:\s*<([^>]+)>)?\s*\(([^)]*)\)/))) {
|
|
1831
|
+
declarations.push(nativeDeclaration(input, number, 'FunctionDecl', 'function', unquoteSwiftIdentifier(match[1]), {
|
|
1832
|
+
typeParameters: splitTypeParameters(match[2]),
|
|
1833
|
+
parameters: splitParameters(match[3])
|
|
1834
|
+
}, declarationLine.includes('{')));
|
|
1835
|
+
} else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|static|class|final|lazy|weak|unowned|override|required|nonisolated)\s+)*(let|var)\s+([A-Za-z_]\w*)\b(?::\s*([^={]+))?/))) {
|
|
1836
|
+
declarations.push(nativeDeclaration(input, number, 'PropertyDecl', 'property', match[2], {
|
|
1837
|
+
binding: match[1],
|
|
1838
|
+
valueType: match[3]?.trim()
|
|
1839
|
+
}, declarationLine.includes('{') || declarationLine.includes('=>')));
|
|
1840
|
+
} else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open)\s+)*typealias\s+([A-Za-z_]\w*)\b(?:\s*=\s*(.+))?/))) {
|
|
1841
|
+
declarations.push(nativeDeclaration(input, number, 'TypealiasDecl', 'type', match[1], { target: match[2]?.trim() }, false));
|
|
989
1842
|
}
|
|
990
1843
|
}
|
|
991
1844
|
return declarations;
|
|
@@ -996,14 +1849,36 @@ function scanCSharp(input) {
|
|
|
996
1849
|
for (const { line, number } of sourceLines(input.sourceText)) {
|
|
997
1850
|
const trimmed = line.trim();
|
|
998
1851
|
let match;
|
|
999
|
-
if ((match = trimmed.match(/^using\s+(
|
|
1852
|
+
if ((match = trimmed.match(/^using\s+([A-Za-z_]\w*)\s*=\s*(.+?)\s*;/))) {
|
|
1853
|
+
declarations.push(nativeDeclaration(input, number, 'UsingAliasDirective', 'type', match[1], { target: match[2].trim() }, false));
|
|
1854
|
+
} else if ((match = trimmed.match(/^using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/))) {
|
|
1000
1855
|
declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingDirective', 'namespace'));
|
|
1001
1856
|
} else if ((match = trimmed.match(/^namespace\s+([A-Za-z_][\w.]*)/))) {
|
|
1002
1857
|
declarations.push(nativeDeclaration(input, number, 'NamespaceDeclaration', 'namespace', match[1], {}, trimmed.includes('{')));
|
|
1003
|
-
} else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|
|
|
1004
|
-
declarations.push(nativeDeclaration(input, number,
|
|
1005
|
-
|
|
1006
|
-
|
|
1858
|
+
} else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|unsafe|new)\s+)*delegate\s+(.+?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*;/))) {
|
|
1859
|
+
declarations.push(nativeDeclaration(input, number, 'DelegateDeclaration', 'type', match[2], {
|
|
1860
|
+
returnType: match[1].trim(),
|
|
1861
|
+
parameters: splitParameters(match[3])
|
|
1862
|
+
}, false));
|
|
1863
|
+
} else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|abstract|sealed|static|partial|readonly|ref|unsafe)\s+)*(class|interface|struct|enum|record(?:\s+(?:class|struct))?)\s+([A-Za-z_]\w*)/))) {
|
|
1864
|
+
declarations.push(nativeDeclaration(input, number, csharpDeclarationKind(match[1]), csharpSymbolKind(match[1]), match[2], { csharpKind: match[1].replace(/\s+/g, ' ') }, trimmed.includes('{')));
|
|
1865
|
+
} else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|virtual|override|async|partial|sealed|abstract|extern|new|unsafe|readonly)\s+)*(?:[A-Za-z_][\w<>\[\].?,\s]*\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:=>.*|\{|;)?$/))) {
|
|
1866
|
+
const parameters = splitParameters(match[2]);
|
|
1867
|
+
const extensionReceiver = csharpExtensionReceiver(parameters);
|
|
1868
|
+
declarations.push(nativeDeclaration(input, number, extensionReceiver ? 'ExtensionMethodDeclaration' : 'MethodDeclaration', 'method', match[1], {
|
|
1869
|
+
parameters,
|
|
1870
|
+
...(extensionReceiver ? { extensionReceiver } : {})
|
|
1871
|
+
}, trimmed.includes('{') || trimmed.includes('=>')));
|
|
1872
|
+
} else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|unsafe)\s+)*event\s+(.+?)\s+([A-Za-z_]\w*)\s*(?:[;{=]|=>)/))) {
|
|
1873
|
+
declarations.push(nativeDeclaration(input, number, 'EventDeclaration', 'event', match[2], {
|
|
1874
|
+
eventType: match[1].trim(),
|
|
1875
|
+
accessors: csharpAccessors(trimmed)
|
|
1876
|
+
}, trimmed.includes('{')));
|
|
1877
|
+
} else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|required|readonly|unsafe)\s+)*([A-Za-z_][\w<>\[\].?,\s]*\??)\s+([A-Za-z_]\w*)\s*(?:\{|=>)/))) {
|
|
1878
|
+
declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'property', match[2], {
|
|
1879
|
+
propertyType: match[1].trim(),
|
|
1880
|
+
accessors: csharpAccessors(trimmed)
|
|
1881
|
+
}, trimmed.includes('{') || trimmed.includes('=>')));
|
|
1007
1882
|
}
|
|
1008
1883
|
}
|
|
1009
1884
|
return declarations;
|
|
@@ -1347,6 +2222,48 @@ function upperFirst(value) {
|
|
|
1347
2222
|
return String(value).charAt(0).toUpperCase() + String(value).slice(1);
|
|
1348
2223
|
}
|
|
1349
2224
|
|
|
2225
|
+
function parseGoReceiver(raw) {
|
|
2226
|
+
const value = String(raw ?? '').trim();
|
|
2227
|
+
const match = value.match(/^(?:(\w+)\s+)?(.+)$/);
|
|
2228
|
+
const rawType = String(match?.[2] ?? value).trim();
|
|
2229
|
+
return {
|
|
2230
|
+
raw: value,
|
|
2231
|
+
...(match?.[1] ? { name: match[1] } : {}),
|
|
2232
|
+
rawType,
|
|
2233
|
+
type: normalizeGoReceiverType(rawType)
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
function normalizeGoReceiverType(rawType) {
|
|
2238
|
+
return String(rawType ?? '')
|
|
2239
|
+
.trim()
|
|
2240
|
+
.replace(/^[*&\s]+/, '')
|
|
2241
|
+
.replace(/\[[^\]]+\]/g, '')
|
|
2242
|
+
.replace(/\s+/g, ' ');
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
function goReceiverMethodName(receiver, methodName) {
|
|
2246
|
+
return receiver?.type ? `${receiver.type}.${methodName}` : methodName;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
function parseSwiftExtensionTail(rawTail) {
|
|
2250
|
+
let tail = String(rawTail ?? '').split('{')[0].trim();
|
|
2251
|
+
const fields = {};
|
|
2252
|
+
const whereMatch = tail.match(/\bwhere\b(.+)$/);
|
|
2253
|
+
if (whereMatch) {
|
|
2254
|
+
fields.constraints = whereMatch[1].trim();
|
|
2255
|
+
tail = tail.slice(0, whereMatch.index).trim();
|
|
2256
|
+
}
|
|
2257
|
+
if (tail.startsWith(':')) {
|
|
2258
|
+
fields.conformances = tail.slice(1).split(',').map((part) => part.trim()).filter(Boolean);
|
|
2259
|
+
}
|
|
2260
|
+
return fields;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
function unquoteSwiftIdentifier(identifier) {
|
|
2264
|
+
return String(identifier).replace(/^`|`$/g, '');
|
|
2265
|
+
}
|
|
2266
|
+
|
|
1350
2267
|
function javaSymbolKind(kind) {
|
|
1351
2268
|
if (kind === 'interface' || kind === '@interface') return 'interface';
|
|
1352
2269
|
if (kind === 'enum' || kind === 'record') return 'type';
|
|
@@ -1361,11 +2278,29 @@ function swiftSymbolKind(kind) {
|
|
|
1361
2278
|
}
|
|
1362
2279
|
|
|
1363
2280
|
function csharpSymbolKind(kind) {
|
|
1364
|
-
|
|
1365
|
-
if (
|
|
2281
|
+
const normalized = String(kind).replace(/\s+/g, ' ');
|
|
2282
|
+
if (normalized === 'interface') return 'interface';
|
|
2283
|
+
if (normalized === 'struct' || normalized === 'enum' || normalized.startsWith('record')) return 'type';
|
|
1366
2284
|
return 'class';
|
|
1367
2285
|
}
|
|
1368
2286
|
|
|
2287
|
+
function csharpDeclarationKind(kind) {
|
|
2288
|
+
const normalized = String(kind).replace(/\s+/g, ' ');
|
|
2289
|
+
if (normalized === 'record struct') return 'RecordStructDeclaration';
|
|
2290
|
+
if (normalized === 'record class') return 'RecordClassDeclaration';
|
|
2291
|
+
if (normalized === 'record') return 'RecordDeclaration';
|
|
2292
|
+
return `${upperFirst(normalized)}Declaration`;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
function csharpExtensionReceiver(parameters) {
|
|
2296
|
+
const match = String(parameters?.[0] ?? '').match(/^this\s+(.+?)\s+([A-Za-z_]\w*)$/);
|
|
2297
|
+
return match ? { type: match[1].trim(), name: match[2] } : undefined;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
function csharpAccessors(source) {
|
|
2301
|
+
return uniqueStrings([...String(source ?? '').matchAll(/\b(get|set|init|add|remove)\b/g)].map((match) => match[1]));
|
|
2302
|
+
}
|
|
2303
|
+
|
|
1369
2304
|
function phpSymbolKind(kind) {
|
|
1370
2305
|
if (kind === 'interface') return 'interface';
|
|
1371
2306
|
if (kind === 'trait') return 'trait';
|
|
@@ -1517,7 +2452,7 @@ function opaqueBodyLoss(input, lineNumber, nodeId, name) {
|
|
|
1517
2452
|
};
|
|
1518
2453
|
}
|
|
1519
2454
|
|
|
1520
|
-
function lightweightCoverageLosses(input, declarations) {
|
|
2455
|
+
function lightweightCoverageLosses(input, declarations, sourcePreservation) {
|
|
1521
2456
|
const id = idFragment(input.sourcePath ?? input.language);
|
|
1522
2457
|
const span = declarations[0]?.span ?? {
|
|
1523
2458
|
sourceId: input.sourceHash,
|
|
@@ -1559,12 +2494,275 @@ function lightweightCoverageLosses(input, declarations) {
|
|
|
1559
2494
|
phase: 'read',
|
|
1560
2495
|
sourceFormat: input.language,
|
|
1561
2496
|
kind: 'sourcePreservation',
|
|
1562
|
-
message:
|
|
1563
|
-
|
|
2497
|
+
message: sourcePreservation
|
|
2498
|
+
? 'Comments, whitespace, token order, directives, and formatting are preserved as opaque native source evidence; exact structural edits still require a parser adapter.'
|
|
2499
|
+
: 'Comments, whitespace, token order, directives, and formatting are not preserved by the lightweight importer.',
|
|
2500
|
+
span,
|
|
2501
|
+
metadata: sourcePreservation ? {
|
|
2502
|
+
sourcePreservationId: sourcePreservation.id,
|
|
2503
|
+
sourcePreservationSummary: sourcePreservation.summary
|
|
2504
|
+
} : undefined
|
|
1564
2505
|
}
|
|
1565
2506
|
];
|
|
1566
2507
|
}
|
|
1567
2508
|
|
|
2509
|
+
function scanPreservedSourceTokens(sourceText, input) {
|
|
2510
|
+
const tokens = [];
|
|
2511
|
+
const trivia = [];
|
|
2512
|
+
const includeTokens = input.includeTokens !== false;
|
|
2513
|
+
const includeTrivia = input.includeTrivia !== false;
|
|
2514
|
+
const maxTokens = Number.isFinite(input.maxTokens) ? Math.max(0, input.maxTokens) : 20000;
|
|
2515
|
+
const maxTrivia = Number.isFinite(input.maxTrivia) ? Math.max(0, input.maxTrivia) : 20000;
|
|
2516
|
+
let offset = 0;
|
|
2517
|
+
let line = 1;
|
|
2518
|
+
let column = 1;
|
|
2519
|
+
let truncated = false;
|
|
2520
|
+
const push = (target, kind, text, start) => {
|
|
2521
|
+
if ((target === tokens && !includeTokens) || (target === trivia && !includeTrivia)) return;
|
|
2522
|
+
const max = target === tokens ? maxTokens : maxTrivia;
|
|
2523
|
+
if (target.length >= max) {
|
|
2524
|
+
truncated = true;
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
target.push(preservedSourceSegment({
|
|
2528
|
+
index: target.length,
|
|
2529
|
+
kind,
|
|
2530
|
+
text,
|
|
2531
|
+
start,
|
|
2532
|
+
end: { offset, line, column },
|
|
2533
|
+
sourceHash: input.sourceHash,
|
|
2534
|
+
sourcePath: input.sourcePath
|
|
2535
|
+
}));
|
|
2536
|
+
};
|
|
2537
|
+
while (offset < sourceText.length) {
|
|
2538
|
+
const start = { offset, line, column };
|
|
2539
|
+
const char = sourceText[offset];
|
|
2540
|
+
const next = sourceText[offset + 1];
|
|
2541
|
+
if (char === '\r' || char === '\n') {
|
|
2542
|
+
const text = char === '\r' && next === '\n' ? '\r\n' : char;
|
|
2543
|
+
offset += text.length;
|
|
2544
|
+
line += 1;
|
|
2545
|
+
column = 1;
|
|
2546
|
+
push(trivia, 'newline', text, start);
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
if (char === ' ' || char === '\t' || char === '\v' || char === '\f') {
|
|
2550
|
+
let text = '';
|
|
2551
|
+
while (offset < sourceText.length && /[ \t\v\f]/.test(sourceText[offset])) {
|
|
2552
|
+
text += sourceText[offset];
|
|
2553
|
+
offset += 1;
|
|
2554
|
+
column += 1;
|
|
2555
|
+
}
|
|
2556
|
+
push(trivia, 'whitespace', text, start);
|
|
2557
|
+
continue;
|
|
2558
|
+
}
|
|
2559
|
+
if (char === '/' && next === '/') {
|
|
2560
|
+
let text = '';
|
|
2561
|
+
while (offset < sourceText.length && sourceText[offset] !== '\n' && sourceText[offset] !== '\r') {
|
|
2562
|
+
text += sourceText[offset];
|
|
2563
|
+
offset += 1;
|
|
2564
|
+
column += 1;
|
|
2565
|
+
}
|
|
2566
|
+
push(trivia, 'comment', text, start);
|
|
2567
|
+
continue;
|
|
2568
|
+
}
|
|
2569
|
+
if (char === '/' && next === '*') {
|
|
2570
|
+
let text = '';
|
|
2571
|
+
while (offset < sourceText.length) {
|
|
2572
|
+
const current = sourceText[offset];
|
|
2573
|
+
text += current;
|
|
2574
|
+
offset += 1;
|
|
2575
|
+
if (current === '\n') {
|
|
2576
|
+
line += 1;
|
|
2577
|
+
column = 1;
|
|
2578
|
+
} else {
|
|
2579
|
+
column += 1;
|
|
2580
|
+
}
|
|
2581
|
+
if (current === '*' && sourceText[offset] === '/') {
|
|
2582
|
+
text += '/';
|
|
2583
|
+
offset += 1;
|
|
2584
|
+
column += 1;
|
|
2585
|
+
break;
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
push(trivia, 'comment', text, start);
|
|
2589
|
+
continue;
|
|
2590
|
+
}
|
|
2591
|
+
if (char === '#' && isHashCommentLanguage(input.language)) {
|
|
2592
|
+
let text = '';
|
|
2593
|
+
while (offset < sourceText.length && sourceText[offset] !== '\n' && sourceText[offset] !== '\r') {
|
|
2594
|
+
text += sourceText[offset];
|
|
2595
|
+
offset += 1;
|
|
2596
|
+
column += 1;
|
|
2597
|
+
}
|
|
2598
|
+
push(trivia, preservedHashLineKind(text), text, start);
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
if (char === '"' || char === '\'' || char === '`') {
|
|
2602
|
+
const quote = char;
|
|
2603
|
+
let text = char;
|
|
2604
|
+
offset += 1;
|
|
2605
|
+
column += 1;
|
|
2606
|
+
let escaped = false;
|
|
2607
|
+
while (offset < sourceText.length) {
|
|
2608
|
+
const current = sourceText[offset];
|
|
2609
|
+
text += current;
|
|
2610
|
+
offset += 1;
|
|
2611
|
+
if (current === '\n') {
|
|
2612
|
+
line += 1;
|
|
2613
|
+
column = 1;
|
|
2614
|
+
} else {
|
|
2615
|
+
column += 1;
|
|
2616
|
+
}
|
|
2617
|
+
if (escaped) {
|
|
2618
|
+
escaped = false;
|
|
2619
|
+
} else if (current === '\\') {
|
|
2620
|
+
escaped = true;
|
|
2621
|
+
} else if (current === quote) {
|
|
2622
|
+
break;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
push(tokens, 'string', text, start);
|
|
2626
|
+
continue;
|
|
2627
|
+
}
|
|
2628
|
+
if (/[0-9]/.test(char)) {
|
|
2629
|
+
let text = '';
|
|
2630
|
+
while (offset < sourceText.length && /[0-9a-fA-F_xXoObBeE.+-]/.test(sourceText[offset])) {
|
|
2631
|
+
text += sourceText[offset];
|
|
2632
|
+
offset += 1;
|
|
2633
|
+
column += 1;
|
|
2634
|
+
}
|
|
2635
|
+
push(tokens, 'number', text, start);
|
|
2636
|
+
continue;
|
|
2637
|
+
}
|
|
2638
|
+
if (isIdentifierStart(char)) {
|
|
2639
|
+
let text = '';
|
|
2640
|
+
while (offset < sourceText.length && isIdentifierPart(sourceText[offset])) {
|
|
2641
|
+
text += sourceText[offset];
|
|
2642
|
+
offset += 1;
|
|
2643
|
+
column += 1;
|
|
2644
|
+
}
|
|
2645
|
+
push(tokens, preservedKeywordSet.has(text) ? 'keyword' : 'identifier', text, start);
|
|
2646
|
+
continue;
|
|
2647
|
+
}
|
|
2648
|
+
let text = char;
|
|
2649
|
+
if (/[=+\-*/%&|^!<>?:.]/.test(char)) {
|
|
2650
|
+
while (offset + text.length < sourceText.length && /[=+\-*/%&|^!<>?:.]/.test(sourceText[offset + text.length])) text += sourceText[offset + text.length];
|
|
2651
|
+
offset += text.length;
|
|
2652
|
+
column += text.length;
|
|
2653
|
+
push(tokens, 'operator', text, start);
|
|
2654
|
+
} else {
|
|
2655
|
+
offset += 1;
|
|
2656
|
+
column += 1;
|
|
2657
|
+
push(tokens, /[()[\]{};,]/.test(char) ? 'punctuation' : 'unknown', text, start);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
return { tokens, trivia, truncated };
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
function scanPreservedSourceDirectives(sourceText, input) {
|
|
2664
|
+
const directives = [];
|
|
2665
|
+
const maxDirectives = Number.isFinite(input.maxDirectives) ? Math.max(0, input.maxDirectives) : 20000;
|
|
2666
|
+
let truncated = false;
|
|
2667
|
+
let offset = 0;
|
|
2668
|
+
for (const { line, number } of sourceLines(sourceText)) {
|
|
2669
|
+
const trimmed = line.trim();
|
|
2670
|
+
const directiveKind = preservedDirectiveKind(trimmed, input.language);
|
|
2671
|
+
if (directiveKind) {
|
|
2672
|
+
if (directives.length >= maxDirectives) {
|
|
2673
|
+
truncated = true;
|
|
2674
|
+
offset += line.length + 1;
|
|
2675
|
+
continue;
|
|
2676
|
+
}
|
|
2677
|
+
const startColumn = Math.max(1, line.indexOf(trimmed) + 1);
|
|
2678
|
+
directives.push({
|
|
2679
|
+
id: `directive_${idFragment(input.sourcePath ?? input.language)}_${directives.length + 1}`,
|
|
2680
|
+
kind: directiveKind,
|
|
2681
|
+
text: trimmed,
|
|
2682
|
+
textHash: hashSemanticValue(trimmed),
|
|
2683
|
+
span: {
|
|
2684
|
+
sourceId: input.sourceHash,
|
|
2685
|
+
path: input.sourcePath,
|
|
2686
|
+
start: offset + startColumn - 1,
|
|
2687
|
+
end: offset + startColumn - 1 + trimmed.length,
|
|
2688
|
+
startLine: number,
|
|
2689
|
+
startColumn,
|
|
2690
|
+
endLine: number,
|
|
2691
|
+
endColumn: startColumn + trimmed.length
|
|
2692
|
+
},
|
|
2693
|
+
metadata: { language: input.language }
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
offset += line.length + 1;
|
|
2697
|
+
}
|
|
2698
|
+
return { directives, truncated };
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
function preservedSourceSegment(input) {
|
|
2702
|
+
const id = `${input.kind}_${input.index + 1}_${idFragment(input.start.offset)}`;
|
|
2703
|
+
return {
|
|
2704
|
+
id,
|
|
2705
|
+
kind: input.kind,
|
|
2706
|
+
text: input.text,
|
|
2707
|
+
textHash: hashSemanticValue(input.text),
|
|
2708
|
+
span: {
|
|
2709
|
+
sourceId: input.sourceHash,
|
|
2710
|
+
path: input.sourcePath,
|
|
2711
|
+
start: input.start.offset,
|
|
2712
|
+
end: input.end.offset,
|
|
2713
|
+
startLine: input.start.line,
|
|
2714
|
+
startColumn: input.start.column,
|
|
2715
|
+
endLine: input.end.line,
|
|
2716
|
+
endColumn: input.end.column
|
|
2717
|
+
}
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
function preservedDirectiveKind(trimmed, language) {
|
|
2722
|
+
if (!trimmed) return undefined;
|
|
2723
|
+
if (/^#\s*(include|define|if|ifdef|ifndef|elif|else|endif|pragma)\b/.test(trimmed)) return 'preprocessor';
|
|
2724
|
+
if (/^#!\s*/.test(trimmed)) return 'shebang';
|
|
2725
|
+
if (/^['"]use strict['"];?$/.test(trimmed)) return 'runtime-directive';
|
|
2726
|
+
if (/^(import|export|package|module|namespace|use|using|from|require)\b/.test(trimmed)) return 'module-directive';
|
|
2727
|
+
if (normalizeNativeLanguageId(language) === 'python' && /^from\s+\S+\s+import\b/.test(trimmed)) return 'module-directive';
|
|
2728
|
+
return undefined;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
function preservedHashLineKind(text) {
|
|
2732
|
+
return preservedDirectiveKind(String(text).trim(), 'c') ? 'directive' : 'comment';
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
function isHashCommentLanguage(language) {
|
|
2736
|
+
return ['python', 'ruby', 'shell', 'bash', 'zsh', 'r', 'perl', 'yaml', 'toml'].includes(normalizeNativeLanguageId(language));
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
function detectNewlineStyle(sourceText) {
|
|
2740
|
+
const crlf = (sourceText.match(/\r\n/g) ?? []).length;
|
|
2741
|
+
const normalized = sourceText.replace(/\r\n/g, '');
|
|
2742
|
+
const lf = (normalized.match(/\n/g) ?? []).length;
|
|
2743
|
+
const cr = (normalized.match(/\r/g) ?? []).length;
|
|
2744
|
+
const kinds = [crlf ? 'crlf' : undefined, lf ? 'lf' : undefined, cr ? 'cr' : undefined].filter(Boolean);
|
|
2745
|
+
if (!kinds.length) return 'none';
|
|
2746
|
+
if (kinds.length > 1 || cr) return 'mixed';
|
|
2747
|
+
return kinds[0];
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
const preservedKeywordSet = new Set([
|
|
2751
|
+
'abstract', 'as', 'async', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'def', 'defer',
|
|
2752
|
+
'do', 'else', 'enum', 'export', 'extends', 'extern', 'false', 'final', 'fn', 'for', 'from', 'func', 'function',
|
|
2753
|
+
'if', 'impl', 'import', 'in', 'interface', 'let', 'match', 'mod', 'module', 'mut', 'namespace', 'new', 'nil',
|
|
2754
|
+
'none', 'null', 'package', 'private', 'protected', 'pub', 'public', 'return', 'self', 'static', 'struct',
|
|
2755
|
+
'switch', 'this', 'throw', 'trait', 'true', 'try', 'type', 'use', 'using', 'var', 'while', 'yield'
|
|
2756
|
+
]);
|
|
2757
|
+
|
|
2758
|
+
function isIdentifierStart(char) {
|
|
2759
|
+
return /[A-Za-z_$]/.test(char ?? '');
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
function isIdentifierPart(char) {
|
|
2763
|
+
return /[A-Za-z0-9_$]/.test(char ?? '');
|
|
2764
|
+
}
|
|
2765
|
+
|
|
1568
2766
|
function sourceLines(sourceText) {
|
|
1569
2767
|
return String(sourceText ?? '').split(/\r?\n/).map((line, index) => ({ line, number: index + 1 }));
|
|
1570
2768
|
}
|
|
@@ -1586,6 +2784,23 @@ function splitParameters(raw) {
|
|
|
1586
2784
|
.filter(Boolean);
|
|
1587
2785
|
}
|
|
1588
2786
|
|
|
2787
|
+
function splitTypeParameters(raw) {
|
|
2788
|
+
return splitParameters(raw);
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
function braceDelta(source) {
|
|
2792
|
+
let delta = 0;
|
|
2793
|
+
for (const char of String(source ?? '')) {
|
|
2794
|
+
if (char === '{') delta += 1;
|
|
2795
|
+
if (char === '}') delta -= 1;
|
|
2796
|
+
}
|
|
2797
|
+
return delta;
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
function jsControlKeyword(value) {
|
|
2801
|
+
return ['if', 'for', 'while', 'switch', 'catch', 'with'].includes(String(value));
|
|
2802
|
+
}
|
|
2803
|
+
|
|
1589
2804
|
function inferSourceMapMappings(input) {
|
|
1590
2805
|
const semanticIndex = input.semanticIndex;
|
|
1591
2806
|
const nativeAst = input.nativeAst;
|
|
@@ -1887,14 +3102,261 @@ function nativeImportCategoryForLossKind(kind) {
|
|
|
1887
3102
|
if (kind === 'preprocessor' || kind === 'conditionalCompilation' || kind === 'macroHygiene') return 'preprocessor';
|
|
1888
3103
|
if (kind === 'metaprogramming' || kind === 'reflection' || kind === 'dynamicDispatch' || kind === 'dynamicRuntime') return 'metaprogramming';
|
|
1889
3104
|
if (kind === 'generatedCode') return 'generatedCode';
|
|
1890
|
-
if (kind === '
|
|
3105
|
+
if (kind === 'overloadResolution' || kind === 'typeInference') return 'overloadTypeInference';
|
|
3106
|
+
if (kind === 'sourcePreservation' || kind === 'commentsTrivia' || kind === 'nonRoundTrippable') return 'sourcePreservation';
|
|
1891
3107
|
if (kind === 'parserDiagnostic') return 'parserDiagnostics';
|
|
1892
3108
|
if (kind === 'unsupportedSyntax' || kind === 'unsupportedSemantic') return 'unsupportedSyntax';
|
|
1893
3109
|
if (kind === 'partialSemanticIndex') return 'partialSemanticIndex';
|
|
1894
3110
|
if (kind === 'sourceMapApproximation') return 'sourceMapApproximation';
|
|
3111
|
+
if (kind === 'targetProjectionLoss') return 'targetProjectionLoss';
|
|
1895
3112
|
return String(kind ?? 'opaqueNative');
|
|
1896
3113
|
}
|
|
1897
3114
|
|
|
3115
|
+
function nativeImportLanguageProfile(language, input = {}) {
|
|
3116
|
+
const lossKinds = input.lossKinds ?? ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation'];
|
|
3117
|
+
return Object.freeze({
|
|
3118
|
+
language,
|
|
3119
|
+
aliases: Object.freeze(uniqueStrings(input.aliases ?? [])),
|
|
3120
|
+
extensions: Object.freeze(uniqueStrings(input.extensions ?? [])),
|
|
3121
|
+
supportsLightweightScan: input.supportsLightweightScan !== false,
|
|
3122
|
+
parserAdapters: Object.freeze(uniqueStrings(input.parserAdapters ?? ['tree-sitter'])),
|
|
3123
|
+
projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ?? FrontierCompileTargets)),
|
|
3124
|
+
knownLossKinds: Object.freeze(uniqueStrings(lossKinds)),
|
|
3125
|
+
defaultReadiness: input.defaultReadiness ?? 'needs-review',
|
|
3126
|
+
notes: Object.freeze(uniqueStrings(input.notes ?? ['lightweight scanner records declarations only; exact parser adapters must be injected by the host']))
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
function mergeNativeImportProfiles(languages, imports, adapters) {
|
|
3131
|
+
const profilesByLanguage = new Map();
|
|
3132
|
+
for (const profile of languages) {
|
|
3133
|
+
const normalized = normalizeNativeLanguageId(profile.language ?? profile);
|
|
3134
|
+
profilesByLanguage.set(normalized, normalizeNativeImportLanguageProfile(profile, normalized));
|
|
3135
|
+
}
|
|
3136
|
+
for (const imported of imports) {
|
|
3137
|
+
const normalized = normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language);
|
|
3138
|
+
if (!normalized || profilesByLanguage.has(normalized)) continue;
|
|
3139
|
+
profilesByLanguage.set(normalized, nativeImportLanguageProfile(normalized, {
|
|
3140
|
+
supportsLightweightScan: false,
|
|
3141
|
+
parserAdapters: [],
|
|
3142
|
+
defaultReadiness: 'blocked',
|
|
3143
|
+
lossKinds: ['unsupportedSyntax'],
|
|
3144
|
+
notes: ['language appeared in import evidence but has no declared Frontier coverage profile']
|
|
3145
|
+
}));
|
|
3146
|
+
}
|
|
3147
|
+
for (const adapter of adapters) {
|
|
3148
|
+
const normalized = normalizeNativeLanguageId(adapter?.language);
|
|
3149
|
+
if (!normalized) continue;
|
|
3150
|
+
const existing = profilesByLanguage.get(normalized) ?? nativeImportLanguageProfile(normalized, { supportsLightweightScan: false, parserAdapters: [] });
|
|
3151
|
+
profilesByLanguage.set(normalized, {
|
|
3152
|
+
...existing,
|
|
3153
|
+
parserAdapters: uniqueStrings([...(existing.parserAdapters ?? []), adapter.parser ?? adapter.id].filter(Boolean))
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
return [...profilesByLanguage.values()].sort((left, right) => left.language.localeCompare(right.language));
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
function normalizeNativeImportLanguageProfile(profile, fallbackLanguage) {
|
|
3160
|
+
const language = normalizeNativeLanguageId(profile.language ?? fallbackLanguage);
|
|
3161
|
+
return {
|
|
3162
|
+
language,
|
|
3163
|
+
aliases: uniqueStrings(profile.aliases ?? []),
|
|
3164
|
+
extensions: uniqueStrings(profile.extensions ?? []),
|
|
3165
|
+
supportsLightweightScan: profile.supportsLightweightScan !== false,
|
|
3166
|
+
parserAdapters: uniqueStrings(profile.parserAdapters ?? []),
|
|
3167
|
+
projectionTargets: uniqueStrings(profile.projectionTargets ?? FrontierCompileTargets),
|
|
3168
|
+
knownLossKinds: uniqueStrings(profile.knownLossKinds ?? profile.lossKinds ?? []),
|
|
3169
|
+
defaultReadiness: profile.defaultReadiness ?? 'needs-review',
|
|
3170
|
+
notes: uniqueStrings(profile.notes ?? [])
|
|
3171
|
+
};
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
function nativeImportCoverageForProfile(profile, imports, adapters) {
|
|
3175
|
+
const aliases = new Set([profile.language, ...(profile.aliases ?? [])].map(normalizeNativeLanguageId).filter(Boolean));
|
|
3176
|
+
const matchingImports = imports.filter((imported) => aliases.has(normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language)));
|
|
3177
|
+
const matchingAdapters = adapters.filter((adapter) => aliases.has(normalizeNativeLanguageId(adapter?.language)));
|
|
3178
|
+
const lossSummary = summarizeNativeImportLosses(matchingImports.flatMap((imported) => imported?.losses ?? []), {
|
|
3179
|
+
evidence: matchingImports.flatMap((imported) => imported?.evidence ?? [])
|
|
3180
|
+
});
|
|
3181
|
+
const readiness = matchingImports.length
|
|
3182
|
+
? lossSummary.semanticMergeReadiness
|
|
3183
|
+
: profile.supportsLightweightScan ? profile.defaultReadiness : 'blocked';
|
|
3184
|
+
const importedParsers = uniqueStrings(matchingImports.map((imported) => imported?.nativeAst?.parser ?? imported?.parser ?? imported?.metadata?.parser).filter(Boolean));
|
|
3185
|
+
const sourceMaps = matchingImports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? []);
|
|
3186
|
+
return {
|
|
3187
|
+
language: profile.language,
|
|
3188
|
+
aliases: profile.aliases,
|
|
3189
|
+
extensions: profile.extensions,
|
|
3190
|
+
supportsLightweightScan: profile.supportsLightweightScan,
|
|
3191
|
+
parserAdapters: uniqueStrings([...(profile.parserAdapters ?? []), ...matchingAdapters.map((adapter) => adapter.parser ?? adapter.id).filter(Boolean)]),
|
|
3192
|
+
projectionTargets: profile.projectionTargets,
|
|
3193
|
+
knownLossKinds: uniqueStrings([...(profile.knownLossKinds ?? []), ...Object.keys(lossSummary.byKind)]),
|
|
3194
|
+
defaultReadiness: profile.defaultReadiness,
|
|
3195
|
+
notes: profile.notes,
|
|
3196
|
+
imports: {
|
|
3197
|
+
total: matchingImports.length,
|
|
3198
|
+
parsers: importedParsers,
|
|
3199
|
+
readiness,
|
|
3200
|
+
readinessReasons: matchingImports.length ? lossSummary.readinessReasons : nativeImportCoverageReasons(profile),
|
|
3201
|
+
symbols: matchingImports.reduce((sum, imported) => sum + (imported?.semanticIndex?.symbols?.length ?? imported?.universalAst?.semanticIndex?.symbols?.length ?? 0), 0),
|
|
3202
|
+
sourceMaps: sourceMaps.length,
|
|
3203
|
+
sourceMapMappings: sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap?.mappings?.length ?? 0), 0),
|
|
3204
|
+
losses: lossSummary.total,
|
|
3205
|
+
lossKinds: lossSummary.byKind,
|
|
3206
|
+
lossCategories: lossSummary.categories
|
|
3207
|
+
}
|
|
3208
|
+
};
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
function semanticImportSidecarEntry(imported, index, options) {
|
|
3212
|
+
const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
|
|
3213
|
+
const nativeAst = imported?.nativeAst ?? imported?.nativeSource?.ast;
|
|
3214
|
+
const sourceMaps = imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? [];
|
|
3215
|
+
const sourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap?.mappings ?? []);
|
|
3216
|
+
const mappingsBySymbolId = new Map();
|
|
3217
|
+
for (const mapping of sourceMapMappings) {
|
|
3218
|
+
if (mapping.semanticSymbolId && !mappingsBySymbolId.has(mapping.semanticSymbolId)) {
|
|
3219
|
+
mappingsBySymbolId.set(mapping.semanticSymbolId, mapping);
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
const symbols = [];
|
|
3223
|
+
const regions = [];
|
|
3224
|
+
for (const symbol of semanticIndex?.symbols ?? []) {
|
|
3225
|
+
const mapping = mappingsBySymbolId.get(symbol.id);
|
|
3226
|
+
const nativeNode = symbol.nativeAstNodeId ? nativeAst?.nodes?.[symbol.nativeAstNodeId] : undefined;
|
|
3227
|
+
const region = semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode, options);
|
|
3228
|
+
regions.push(region);
|
|
3229
|
+
symbols.push({
|
|
3230
|
+
id: symbol.id,
|
|
3231
|
+
name: symbol.name,
|
|
3232
|
+
kind: symbol.kind,
|
|
3233
|
+
language: symbol.language ?? imported?.language,
|
|
3234
|
+
nativeAstNodeId: symbol.nativeAstNodeId,
|
|
3235
|
+
semanticOccurrenceId: mapping?.semanticOccurrenceId,
|
|
3236
|
+
sourceMapMappingId: mapping?.id,
|
|
3237
|
+
sourceSpan: mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span,
|
|
3238
|
+
signatureHash: symbol.signatureHash,
|
|
3239
|
+
ownershipRegionId: region.id,
|
|
3240
|
+
ownershipKey: region.key,
|
|
3241
|
+
readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review'
|
|
3242
|
+
});
|
|
3243
|
+
}
|
|
3244
|
+
return {
|
|
3245
|
+
id: imported?.id ?? `import_${index + 1}`,
|
|
3246
|
+
language: imported?.language,
|
|
3247
|
+
sourcePath: imported?.sourcePath ?? imported?.nativeSource?.sourcePath ?? nativeAst?.sourcePath,
|
|
3248
|
+
sourceHash: imported?.nativeSource?.sourceHash ?? nativeAst?.sourceHash,
|
|
3249
|
+
parser: imported?.nativeAst?.parser ?? nativeAst?.parser,
|
|
3250
|
+
nativeSourceId: imported?.nativeSource?.id,
|
|
3251
|
+
nativeAstId: nativeAst?.id,
|
|
3252
|
+
semanticIndexId: semanticIndex?.id,
|
|
3253
|
+
universalAstId: imported?.universalAst?.id,
|
|
3254
|
+
symbolCount: symbols.length,
|
|
3255
|
+
sourceMapCount: sourceMaps.length,
|
|
3256
|
+
sourceMapMappingCount: sourceMapMappings.length,
|
|
3257
|
+
readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review',
|
|
3258
|
+
emptySemanticIndex: symbols.length === 0,
|
|
3259
|
+
symbols,
|
|
3260
|
+
ownershipRegions: uniqueRecordsById(regions)
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
function semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode, options = {}) {
|
|
3265
|
+
const sourcePath = mapping?.sourceSpan?.path ?? symbol.definitionSpan?.path ?? nativeNode?.span?.path ?? imported?.sourcePath ?? imported?.nativeSource?.sourcePath ?? imported?.nativeAst?.sourcePath;
|
|
3266
|
+
const language = symbol.language ?? imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language;
|
|
3267
|
+
const sourceSpan = mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span;
|
|
3268
|
+
const key = [
|
|
3269
|
+
options.regionPrefix ?? 'source',
|
|
3270
|
+
sourcePath ?? `${language}:memory`,
|
|
3271
|
+
symbol.kind ?? 'symbol',
|
|
3272
|
+
symbol.name ?? symbol.id
|
|
3273
|
+
].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
|
|
3274
|
+
return {
|
|
3275
|
+
id: `region_${idFragment(key)}`,
|
|
3276
|
+
key,
|
|
3277
|
+
granularity: 'symbol',
|
|
3278
|
+
language,
|
|
3279
|
+
sourcePath,
|
|
3280
|
+
sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash,
|
|
3281
|
+
symbolId: symbol.id,
|
|
3282
|
+
symbolName: symbol.name,
|
|
3283
|
+
symbolKind: symbol.kind,
|
|
3284
|
+
nativeAstNodeId: symbol.nativeAstNodeId ?? nativeNode?.id,
|
|
3285
|
+
sourceSpan,
|
|
3286
|
+
precision: mapping?.precision ?? (sourceSpan ? 'declaration' : 'unknown'),
|
|
3287
|
+
mergePolicy: 'single-writer-review-required'
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
function semanticOwnershipRegionForDeclaration(input, declaration, documentId) {
|
|
3292
|
+
const name = declaration.name ?? declaration.importPath ?? declaration.nodeId ?? declaration.nativeNode?.id;
|
|
3293
|
+
const kind = declaration.symbolKind ?? declaration.kind ?? declaration.nativeNode?.kind ?? 'symbol';
|
|
3294
|
+
const sourcePath = declaration.span?.path ?? declaration.nativeNode?.span?.path ?? input.sourcePath ?? `${input.language}:memory`;
|
|
3295
|
+
const key = ['source', sourcePath, kind, name].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
|
|
3296
|
+
return {
|
|
3297
|
+
id: `region_${idFragment(key)}`,
|
|
3298
|
+
key,
|
|
3299
|
+
granularity: 'symbol',
|
|
3300
|
+
language: input.language,
|
|
3301
|
+
documentId,
|
|
3302
|
+
sourcePath,
|
|
3303
|
+
sourceHash: input.sourceHash,
|
|
3304
|
+
symbolId: declaration.symbolId,
|
|
3305
|
+
symbolName: name,
|
|
3306
|
+
symbolKind: kind,
|
|
3307
|
+
nativeAstNodeId: declaration.nodeId ?? declaration.nativeNode?.id,
|
|
3308
|
+
sourceSpan: declaration.span ?? declaration.nativeNode?.span,
|
|
3309
|
+
precision: declaration.span || declaration.nativeNode?.span ? 'declaration' : 'unknown',
|
|
3310
|
+
mergePolicy: 'single-writer-review-required'
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
function semanticPatchHintForRegion(region, readiness, options = {}) {
|
|
3315
|
+
return {
|
|
3316
|
+
id: `hint_${idFragment(region.id)}`,
|
|
3317
|
+
kind: 'source-region-patch',
|
|
3318
|
+
ownershipRegionId: region.id,
|
|
3319
|
+
ownershipKey: region.key,
|
|
3320
|
+
sourcePath: region.sourcePath,
|
|
3321
|
+
sourceHash: region.sourceHash,
|
|
3322
|
+
sourceSpan: region.sourceSpan,
|
|
3323
|
+
readiness,
|
|
3324
|
+
precision: region.precision,
|
|
3325
|
+
supportedOperations: ['replace-region', 'insert-before-region', 'insert-after-region'],
|
|
3326
|
+
projection: {
|
|
3327
|
+
sourceLanguage: region.language,
|
|
3328
|
+
targetPath: options.targetPath ?? region.sourcePath,
|
|
3329
|
+
requiresSourceMap: true
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
function nativeImportCoverageReasons(profile) {
|
|
3335
|
+
if (!profile.supportsLightweightScan) return ['No built-in scanner coverage profile; host must provide an exact adapter or mark unsupported.'];
|
|
3336
|
+
return ['Built-in coverage is declaration-level only; use injected parser adapters for exact AST/CST, tokens, trivia, type resolution, and round-trip evidence.'];
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
function normalizeNativeLanguageId(value) {
|
|
3340
|
+
if (!value) return '';
|
|
3341
|
+
const text = String(value).trim().toLowerCase();
|
|
3342
|
+
if (text === 'js' || text === 'mjs' || text === 'cjs' || text === 'jsx') return 'javascript';
|
|
3343
|
+
if (text === 'ts' || text === 'tsx') return 'typescript';
|
|
3344
|
+
if (text === 'py' || text === 'pyi') return 'python';
|
|
3345
|
+
if (text === 'rs') return 'rust';
|
|
3346
|
+
if (text === 'h') return 'c';
|
|
3347
|
+
if (text === 'c++' || text === 'cc' || text === 'cxx' || text === 'hpp' || text === 'hh') return 'cpp';
|
|
3348
|
+
if (text === 'c#' || text === 'cs') return 'csharp';
|
|
3349
|
+
if (text === 'rb' || text === 'rake') return 'ruby';
|
|
3350
|
+
if (text === 'kt' || text === 'kts') return 'kotlin';
|
|
3351
|
+
if (text === 'sc') return 'scala';
|
|
3352
|
+
if (text === 'sh' || text === 'bash' || text === 'zsh') return 'shell';
|
|
3353
|
+
if (text === 'postgresql' || text === 'postgres' || text === 'mysql' || text === 'sqlite') return 'sql';
|
|
3354
|
+
if (text === 'ex' || text === 'exs') return 'elixir';
|
|
3355
|
+
if (text === 'erl' || text === 'hrl') return 'erlang';
|
|
3356
|
+
if (text === 'hs' || text === 'lhs') return 'haskell';
|
|
3357
|
+
return text;
|
|
3358
|
+
}
|
|
3359
|
+
|
|
1898
3360
|
function semanticMergeAdmissionForSeverity(severity) {
|
|
1899
3361
|
if (severity === 'error') return 'blocked';
|
|
1900
3362
|
if (severity === 'warning') return 'review';
|
|
@@ -2010,6 +3472,20 @@ function createJavaScriptSyntaxImporterAdapter(options) {
|
|
|
2010
3472
|
parser: options.parser,
|
|
2011
3473
|
version: options.version,
|
|
2012
3474
|
capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
|
|
3475
|
+
coverage: nativeImporterAdapterCoverage({
|
|
3476
|
+
exactness: 'exact-parser-ast',
|
|
3477
|
+
exactAst: true,
|
|
3478
|
+
tokens: false,
|
|
3479
|
+
trivia: false,
|
|
3480
|
+
diagnostics: true,
|
|
3481
|
+
sourceRanges: true,
|
|
3482
|
+
generatedRanges: false,
|
|
3483
|
+
semanticCoverage: declarationSemanticCoverage(),
|
|
3484
|
+
notes: [
|
|
3485
|
+
'Normalizes a caller-owned ESTree/Babel-compatible AST into native AST nodes and declaration-level semantic index records.',
|
|
3486
|
+
'The wrapper ignores parser token/trivia/comment arrays unless a host adapter explicitly maps them into preservation evidence.'
|
|
3487
|
+
]
|
|
3488
|
+
}, options.coverage),
|
|
2013
3489
|
supportedExtensions: options.supportedExtensions,
|
|
2014
3490
|
diagnostics: options.diagnostics,
|
|
2015
3491
|
parse(input) {
|
|
@@ -2335,6 +3811,19 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
|
|
|
2335
3811
|
for (const declaration of declarations) {
|
|
2336
3812
|
const symbolId = declaration.symbolId ?? `symbol:${input.language}:${declaration.role === 'import' ? 'import:' : ''}${idFragment(declaration.name)}`;
|
|
2337
3813
|
const occurrenceId = `occ_${idFragment(declaration.nativeNode.id)}_${declaration.role ?? 'definition'}`;
|
|
3814
|
+
const ownershipRegion = semanticOwnershipRegionForDeclaration(input, {
|
|
3815
|
+
...declaration,
|
|
3816
|
+
nodeId: declaration.nativeNode.id,
|
|
3817
|
+
kind: declaration.nativeNode.kind,
|
|
3818
|
+
languageKind: declaration.nativeNode.languageKind,
|
|
3819
|
+
span: declaration.nativeNode.span,
|
|
3820
|
+
symbolId
|
|
3821
|
+
}, documentId);
|
|
3822
|
+
declaration.nativeNode.metadata = {
|
|
3823
|
+
...declaration.nativeNode.metadata,
|
|
3824
|
+
ownershipRegionId: ownershipRegion.id,
|
|
3825
|
+
ownershipRegionKey: ownershipRegion.key
|
|
3826
|
+
};
|
|
2338
3827
|
symbols.push({
|
|
2339
3828
|
id: symbolId,
|
|
2340
3829
|
scheme: 'frontier',
|
|
@@ -2343,7 +3832,11 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
|
|
|
2343
3832
|
language: input.language,
|
|
2344
3833
|
nativeAstNodeId: declaration.nativeNode.id,
|
|
2345
3834
|
signatureHash: hashSemanticValue([input.language, declaration.nativeNode.kind, declaration.name, declaration.nativeNode.fields ?? {}]),
|
|
2346
|
-
definitionSpan: declaration.nativeNode.span
|
|
3835
|
+
definitionSpan: declaration.nativeNode.span,
|
|
3836
|
+
metadata: {
|
|
3837
|
+
ownershipRegionId: ownershipRegion.id,
|
|
3838
|
+
ownershipRegionKey: ownershipRegion.key
|
|
3839
|
+
}
|
|
2347
3840
|
});
|
|
2348
3841
|
occurrences.push({
|
|
2349
3842
|
id: occurrenceId,
|
|
@@ -2364,6 +3857,11 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
|
|
|
2364
3857
|
predicate: 'nativeKind',
|
|
2365
3858
|
subjectId: symbolId,
|
|
2366
3859
|
value: declaration.nativeNode.languageKind
|
|
3860
|
+
}, {
|
|
3861
|
+
id: `fact_${idFragment(declaration.nativeNode.id)}_ownership_region`,
|
|
3862
|
+
predicate: 'semanticOwnershipRegion',
|
|
3863
|
+
subjectId: symbolId,
|
|
3864
|
+
value: ownershipRegion
|
|
2367
3865
|
});
|
|
2368
3866
|
mappings.push({
|
|
2369
3867
|
id: `map_${idFragment(declaration.nativeNode.id)}`,
|
|
@@ -2373,6 +3871,7 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
|
|
|
2373
3871
|
sourceSpan: declaration.nativeNode.span,
|
|
2374
3872
|
evidenceIds: [evidenceId],
|
|
2375
3873
|
lossIds: [],
|
|
3874
|
+
ownershipRegionId: ownershipRegion.id,
|
|
2376
3875
|
precision: declaration.nativeNode.span ? 'declaration' : 'unknown'
|
|
2377
3876
|
});
|
|
2378
3877
|
}
|
|
@@ -2437,6 +3936,14 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
2437
3936
|
mergeCandidates.push(...(result.mergeCandidates ?? []));
|
|
2438
3937
|
operations.push(...(result.patch?.operations ?? []));
|
|
2439
3938
|
}
|
|
3939
|
+
const uniqueLosses = uniqueByLossId(losses);
|
|
3940
|
+
const uniqueEvidence = uniqueByEvidenceId(evidence);
|
|
3941
|
+
const nativeImportLossSummary = summarizeNativeImportLosses(uniqueLosses, {
|
|
3942
|
+
evidence: uniqueEvidence,
|
|
3943
|
+
scanKind: 'native-project-import',
|
|
3944
|
+
semanticStatus: uniqueLosses.some((loss) => loss.severity === 'error') ? 'partial' : 'mapped'
|
|
3945
|
+
});
|
|
3946
|
+
const sourcePreservationSummary = summarizeProjectSourcePreservation(imports);
|
|
2440
3947
|
const document = createDocument({
|
|
2441
3948
|
id: input.documentId ?? `document_${idPart}`,
|
|
2442
3949
|
name: input.documentName ?? input.name ?? 'NativeProject',
|
|
@@ -2447,6 +3954,8 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
2447
3954
|
semanticStatus: losses.some((loss) => loss.severity === 'error') ? 'partial' : 'mapped',
|
|
2448
3955
|
projectRoot: input.projectRoot,
|
|
2449
3956
|
sourceCount: imports.length,
|
|
3957
|
+
nativeImportLossSummary,
|
|
3958
|
+
sourcePreservationSummary,
|
|
2450
3959
|
...input.documentMetadata
|
|
2451
3960
|
}
|
|
2452
3961
|
});
|
|
@@ -2456,12 +3965,14 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
2456
3965
|
nativeSources,
|
|
2457
3966
|
semanticIndex,
|
|
2458
3967
|
sourceMaps,
|
|
2459
|
-
losses:
|
|
2460
|
-
evidence:
|
|
3968
|
+
losses: uniqueLosses,
|
|
3969
|
+
evidence: uniqueEvidence,
|
|
2461
3970
|
metadata: {
|
|
2462
3971
|
sourceLanguage: input.language ?? 'mixed',
|
|
2463
3972
|
projectRoot: input.projectRoot,
|
|
2464
3973
|
sourceCount: imports.length,
|
|
3974
|
+
nativeImportLossSummary,
|
|
3975
|
+
sourcePreservationSummary,
|
|
2465
3976
|
...input.universalAstMetadata
|
|
2466
3977
|
}
|
|
2467
3978
|
});
|
|
@@ -2470,12 +3981,14 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
2470
3981
|
author: input.author ?? '@shapeshift-labs/frontier-lang-compiler/importNativeProject',
|
|
2471
3982
|
risk: losses.some((loss) => loss.severity === 'error') ? 'high' : losses.some((loss) => loss.severity === 'warning') ? 'medium' : 'low',
|
|
2472
3983
|
operations,
|
|
2473
|
-
evidence:
|
|
3984
|
+
evidence: uniqueEvidence,
|
|
2474
3985
|
metadata: {
|
|
2475
3986
|
semanticIndexId: semanticIndex?.id,
|
|
2476
3987
|
universalAstId: universalAst.id,
|
|
2477
3988
|
sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
|
|
2478
|
-
sourceCount: imports.length
|
|
3989
|
+
sourceCount: imports.length,
|
|
3990
|
+
nativeImportLossSummary,
|
|
3991
|
+
sourcePreservationSummary
|
|
2479
3992
|
}
|
|
2480
3993
|
});
|
|
2481
3994
|
return {
|
|
@@ -2491,17 +4004,35 @@ function createNativeProjectImportResult(input, imports) {
|
|
|
2491
4004
|
semanticIndex,
|
|
2492
4005
|
universalAst,
|
|
2493
4006
|
sourceMaps,
|
|
2494
|
-
losses:
|
|
2495
|
-
evidence:
|
|
4007
|
+
losses: uniqueLosses,
|
|
4008
|
+
evidence: uniqueEvidence,
|
|
2496
4009
|
mergeCandidates,
|
|
2497
4010
|
metadata: {
|
|
2498
4011
|
sourceCount: imports.length,
|
|
2499
4012
|
sourcePaths: imports.map((result) => result.sourcePath).filter(Boolean),
|
|
4013
|
+
nativeImportLossSummary,
|
|
4014
|
+
sourcePreservationSummary,
|
|
2500
4015
|
...input.metadata
|
|
2501
4016
|
}
|
|
2502
4017
|
};
|
|
2503
4018
|
}
|
|
2504
4019
|
|
|
4020
|
+
function summarizeProjectSourcePreservation(imports) {
|
|
4021
|
+
const records = imports
|
|
4022
|
+
.map((result) => result.metadata?.sourcePreservation ?? result.nativeSource?.metadata?.sourcePreservation ?? result.nativeAst?.metadata?.sourcePreservation)
|
|
4023
|
+
.filter(Boolean);
|
|
4024
|
+
return {
|
|
4025
|
+
total: records.length,
|
|
4026
|
+
exactSourceAvailable: records.filter((record) => record.summary?.exactSourceAvailable).length,
|
|
4027
|
+
sourceBytes: records.reduce((sum, record) => sum + (record.sourceBytes ?? 0), 0),
|
|
4028
|
+
tokens: records.reduce((sum, record) => sum + (record.summary?.tokens ?? record.tokens?.length ?? 0), 0),
|
|
4029
|
+
trivia: records.reduce((sum, record) => sum + (record.summary?.trivia ?? record.trivia?.length ?? 0), 0),
|
|
4030
|
+
directives: records.reduce((sum, record) => sum + (record.summary?.directives ?? record.directives?.length ?? 0), 0),
|
|
4031
|
+
truncated: records.some((record) => record.summary?.truncated === true),
|
|
4032
|
+
ids: records.map((record) => record.id).filter(Boolean)
|
|
4033
|
+
};
|
|
4034
|
+
}
|
|
4035
|
+
|
|
2505
4036
|
function mergeSemanticIndexes(imports, input, idPart) {
|
|
2506
4037
|
const indexes = imports.map((result) => result.semanticIndex ?? result.universalAst?.semanticIndex).filter(Boolean);
|
|
2507
4038
|
if (!indexes.length) return undefined;
|
|
@@ -2547,9 +4078,15 @@ function normalizeNativeImporterAdapter(adapter) {
|
|
|
2547
4078
|
parser: String(adapter.parser),
|
|
2548
4079
|
version: adapter.version === undefined ? undefined : String(adapter.version)
|
|
2549
4080
|
};
|
|
4081
|
+
const capabilities = normalizeStringList(adapter.capabilities);
|
|
2550
4082
|
return Object.freeze({
|
|
2551
4083
|
...summaryInput,
|
|
2552
|
-
capabilities
|
|
4084
|
+
capabilities,
|
|
4085
|
+
coverage: normalizeNativeImporterAdapterCoverage(adapter.coverage, {
|
|
4086
|
+
capabilities,
|
|
4087
|
+
language: adapter.language,
|
|
4088
|
+
parser: String(adapter.parser)
|
|
4089
|
+
}),
|
|
2553
4090
|
supportedExtensions: normalizeStringList(adapter.supportedExtensions).map((extension) => extension.startsWith('.') ? extension.toLowerCase() : `.${extension.toLowerCase()}`),
|
|
2554
4091
|
diagnostics: normalizeAdapterDiagnostics(adapter.diagnostics, summaryInput, {
|
|
2555
4092
|
language: adapter.language,
|
|
@@ -2559,6 +4096,138 @@ function normalizeNativeImporterAdapter(adapter) {
|
|
|
2559
4096
|
});
|
|
2560
4097
|
}
|
|
2561
4098
|
|
|
4099
|
+
function nativeImporterAdapterCoverage(defaults = {}, overrides = {}) {
|
|
4100
|
+
return {
|
|
4101
|
+
...defaults,
|
|
4102
|
+
...overrides,
|
|
4103
|
+
semanticCoverage: {
|
|
4104
|
+
...(defaults.semanticCoverage ?? {}),
|
|
4105
|
+
...(overrides.semanticCoverage ?? {})
|
|
4106
|
+
},
|
|
4107
|
+
notes: uniqueStrings([...(defaults.notes ?? []), ...(overrides.notes ?? [])])
|
|
4108
|
+
};
|
|
4109
|
+
}
|
|
4110
|
+
|
|
4111
|
+
function normalizeNativeImporterAdapterCoverage(value = {}, context = {}) {
|
|
4112
|
+
const capabilities = new Set(normalizeStringList(context.capabilities).map((capability) => capability.toLowerCase()));
|
|
4113
|
+
const hasCapability = (...names) => names.some((name) => capabilities.has(String(name).toLowerCase()));
|
|
4114
|
+
const exactAst = Boolean(value.exactAst ?? hasCapability('exactAst', 'exactAstImport'));
|
|
4115
|
+
const sourceRanges = Boolean(value.sourceRanges ?? hasCapability('sourceRanges', 'sourceRange', 'ranges', 'sourceMaps'));
|
|
4116
|
+
const generatedRanges = Boolean(value.generatedRanges ?? hasCapability('generatedRanges', 'generatedRange', 'generatedSourceMaps'));
|
|
4117
|
+
const diagnostics = Boolean(value.diagnostics ?? hasCapability('diagnostics', 'parserDiagnostics'));
|
|
4118
|
+
return Object.freeze({
|
|
4119
|
+
exactness: String(value.exactness ?? inferredAdapterExactness(exactAst, capabilities)),
|
|
4120
|
+
exactAst,
|
|
4121
|
+
tokens: Boolean(value.tokens ?? hasCapability('tokens', 'tokenStream')),
|
|
4122
|
+
trivia: Boolean(value.trivia ?? hasCapability('trivia', 'comments', 'formatting')),
|
|
4123
|
+
diagnostics,
|
|
4124
|
+
sourceRanges,
|
|
4125
|
+
generatedRanges,
|
|
4126
|
+
semanticCoverage: normalizeNativeImporterSemanticCoverage(value.semanticCoverage, {
|
|
4127
|
+
capabilities,
|
|
4128
|
+
sourceRanges,
|
|
4129
|
+
generatedRanges
|
|
4130
|
+
}),
|
|
4131
|
+
notes: uniqueStrings(value.notes ?? inferredAdapterCoverageNotes(context, {
|
|
4132
|
+
exactAst,
|
|
4133
|
+
sourceRanges,
|
|
4134
|
+
generatedRanges,
|
|
4135
|
+
diagnostics
|
|
4136
|
+
}))
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
|
|
4140
|
+
function observeNativeImporterAdapterCoverage(coverage, parseResult = {}, context = {}) {
|
|
4141
|
+
const nodes = parseResult.nativeAst?.nodes ?? parseResult.nodes ?? {};
|
|
4142
|
+
const nodeList = Object.values(nodes);
|
|
4143
|
+
const sourceMapMappings = parseResult.sourceMaps?.flatMap((sourceMap) => sourceMap.mappings ?? []) ?? parseResult.mappings ?? [];
|
|
4144
|
+
const semanticIndex = parseResult.semanticIndex;
|
|
4145
|
+
const semanticSymbols = semanticIndex?.symbols?.length ?? 0;
|
|
4146
|
+
const observedSourceRanges = nodeList.some((node) => Boolean(node?.span)) || sourceMapMappings.some((mapping) => Boolean(mapping?.sourceSpan));
|
|
4147
|
+
const observedGeneratedRanges = sourceMapMappings.some((mapping) => Boolean(mapping?.generatedSpan));
|
|
4148
|
+
const observedSemanticCoverage = normalizeNativeImporterSemanticCoverage({
|
|
4149
|
+
...coverage.semanticCoverage,
|
|
4150
|
+
level: semanticSymbols
|
|
4151
|
+
? maxSemanticCoverageLevel(coverage.semanticCoverage?.level, 'declaration-index')
|
|
4152
|
+
: coverage.semanticCoverage?.level,
|
|
4153
|
+
declarations: coverage.semanticCoverage?.declarations || semanticSymbols > 0,
|
|
4154
|
+
symbols: coverage.semanticCoverage?.symbols || semanticSymbols > 0
|
|
4155
|
+
}, {});
|
|
4156
|
+
return Object.freeze({
|
|
4157
|
+
...coverage,
|
|
4158
|
+
diagnostics: coverage.diagnostics || (context.diagnostics?.length ?? 0) > 0,
|
|
4159
|
+
sourceRanges: coverage.sourceRanges || observedSourceRanges,
|
|
4160
|
+
generatedRanges: coverage.generatedRanges || observedGeneratedRanges,
|
|
4161
|
+
semanticCoverage: observedSemanticCoverage,
|
|
4162
|
+
observed: {
|
|
4163
|
+
diagnostics: context.diagnostics?.length ?? 0,
|
|
4164
|
+
losses: context.losses?.length ?? 0,
|
|
4165
|
+
nativeAstNodes: nodeList.length,
|
|
4166
|
+
semanticSymbols,
|
|
4167
|
+
sourceMapMappings: sourceMapMappings.length,
|
|
4168
|
+
sourceRanges: observedSourceRanges,
|
|
4169
|
+
generatedRanges: observedGeneratedRanges
|
|
4170
|
+
}
|
|
4171
|
+
});
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4174
|
+
function declarationSemanticCoverage() {
|
|
4175
|
+
return {
|
|
4176
|
+
level: 'declaration-index',
|
|
4177
|
+
declarations: true,
|
|
4178
|
+
symbols: true,
|
|
4179
|
+
references: false,
|
|
4180
|
+
types: false,
|
|
4181
|
+
controlFlow: false
|
|
4182
|
+
};
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4185
|
+
function normalizeNativeImporterSemanticCoverage(value = {}, context = {}) {
|
|
4186
|
+
const capabilities = context.capabilities ?? new Set();
|
|
4187
|
+
const hasCapability = (...names) => names.some((name) => capabilities.has(String(name).toLowerCase()));
|
|
4188
|
+
const declarations = Boolean(value.declarations ?? hasCapability('semanticIndex', 'declarations'));
|
|
4189
|
+
const symbols = Boolean(value.symbols ?? declarations);
|
|
4190
|
+
const references = Boolean(value.references ?? hasCapability('references', 'referenceIndex'));
|
|
4191
|
+
const types = Boolean(value.types ?? hasCapability('types', 'typeResolution', 'typeChecking'));
|
|
4192
|
+
const controlFlow = Boolean(value.controlFlow ?? hasCapability('controlFlow', 'cfg'));
|
|
4193
|
+
return Object.freeze({
|
|
4194
|
+
level: String(value.level ?? inferredSemanticCoverageLevel({ declarations, symbols, references, types, controlFlow })),
|
|
4195
|
+
declarations,
|
|
4196
|
+
symbols,
|
|
4197
|
+
references,
|
|
4198
|
+
types,
|
|
4199
|
+
controlFlow
|
|
4200
|
+
});
|
|
4201
|
+
}
|
|
4202
|
+
|
|
4203
|
+
function inferredAdapterExactness(exactAst, capabilities) {
|
|
4204
|
+
if (exactAst) return 'exact-parser-ast';
|
|
4205
|
+
if (capabilities.has('nativeast')) return 'adapter-reported-native-ast';
|
|
4206
|
+
return 'loss-aware-native-ast';
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
function inferredSemanticCoverageLevel(input) {
|
|
4210
|
+
if (input.references || input.types || input.controlFlow) return 'semantic-index';
|
|
4211
|
+
if (input.declarations || input.symbols) return 'declaration-index';
|
|
4212
|
+
return 'native-ast';
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4215
|
+
function maxSemanticCoverageLevel(left, right) {
|
|
4216
|
+
const ranks = { 'native-ast': 0, 'declaration-index': 1, 'semantic-index': 2 };
|
|
4217
|
+
const leftRank = ranks[left] ?? 0;
|
|
4218
|
+
const rightRank = ranks[right] ?? 0;
|
|
4219
|
+
return rightRank > leftRank ? right : left;
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
function inferredAdapterCoverageNotes(context, coverage) {
|
|
4223
|
+
const notes = [];
|
|
4224
|
+
if (!coverage.exactAst) notes.push('Adapter did not declare exact parser AST/CST coverage; import readiness depends on losses and evidence.');
|
|
4225
|
+
if (!coverage.generatedRanges) notes.push('Adapter does not declare generated-range coverage unless parse output includes generated spans.');
|
|
4226
|
+
if (!coverage.diagnostics) notes.push('Adapter did not declare parser diagnostics support.');
|
|
4227
|
+
if (context.language && context.parser) notes.push(`Coverage summary applies to ${context.language} via ${context.parser}.`);
|
|
4228
|
+
return notes;
|
|
4229
|
+
}
|
|
4230
|
+
|
|
2562
4231
|
function normalizeStringList(value) {
|
|
2563
4232
|
if (value === undefined || value === null) return [];
|
|
2564
4233
|
if (Array.isArray(value)) return value.map((item) => String(item)).filter(Boolean);
|
|
@@ -2652,6 +4321,7 @@ function adapterDiagnosticsEvidence(adapter, diagnostics, input) {
|
|
|
2652
4321
|
parserVersion: input.parserVersion,
|
|
2653
4322
|
sourceHash: input.sourceHash,
|
|
2654
4323
|
capabilities: adapter.capabilities,
|
|
4324
|
+
coverage: adapter.coverage,
|
|
2655
4325
|
supportedExtensions: adapter.supportedExtensions,
|
|
2656
4326
|
diagnostics: diagnostics.map(serializableDiagnostic),
|
|
2657
4327
|
errors,
|