@shapeshift-labs/frontier-lang-compiler 0.2.25 → 0.2.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -333,6 +333,125 @@ export const NativeImportLanguageProfiles = Object.freeze([
333
333
  nativeImportLanguageProfile('r', { aliases: ['R'], extensions: ['.r', '.R'], parserAdapters: ['r-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] })
334
334
  ]);
335
335
 
336
+ export const NativeParserAstFormatProfiles = Object.freeze([
337
+ nativeParserAstFormatProfile('estree', {
338
+ kind: 'abstract-ast',
339
+ languages: ['javascript'],
340
+ parserAdapters: ['estree'],
341
+ exactness: 'exact-parser-ast',
342
+ sourceRangeModel: 'loc-range',
343
+ preservesTokens: false,
344
+ preservesTrivia: false,
345
+ supportsErrorRecovery: false,
346
+ notes: ['Community JavaScript AST shape used by many JS tooling parsers.']
347
+ }),
348
+ nativeParserAstFormatProfile('babel', {
349
+ kind: 'abstract-ast',
350
+ languages: ['javascript', 'typescript'],
351
+ parserAdapters: ['babel'],
352
+ exactness: 'exact-parser-ast',
353
+ sourceRangeModel: 'loc-range',
354
+ preservesTokens: false,
355
+ preservesTrivia: false,
356
+ supportsErrorRecovery: true,
357
+ notes: ['Babel-compatible ESTree-like ASTs can report parser errors when error recovery is enabled.']
358
+ }),
359
+ nativeParserAstFormatProfile('typescript-compiler-api', {
360
+ kind: 'compiler-ast',
361
+ languages: ['typescript', 'javascript'],
362
+ parserAdapters: ['typescript-compiler-api'],
363
+ exactness: 'exact-parser-ast',
364
+ sourceRangeModel: 'pos-end',
365
+ preservesTokens: false,
366
+ preservesTrivia: false,
367
+ supportsErrorRecovery: true,
368
+ notes: ['TypeScript SourceFile trees can be parsed without a full Program; richer type/checker evidence remains host-owned.']
369
+ }),
370
+ nativeParserAstFormatProfile('python-ast', {
371
+ kind: 'abstract-ast',
372
+ languages: ['python'],
373
+ parserAdapters: ['python-ast'],
374
+ exactness: 'exact-parser-ast',
375
+ sourceRangeModel: 'lineno-col-offset',
376
+ preservesTokens: false,
377
+ preservesTrivia: false,
378
+ supportsErrorRecovery: false,
379
+ notes: ['Python stdlib AST exposes versioned abstract grammar and source locations, but not formatting trivia.']
380
+ }),
381
+ nativeParserAstFormatProfile('rust-syn', {
382
+ aliases: ['syn'],
383
+ kind: 'abstract-ast',
384
+ languages: ['rust'],
385
+ parserAdapters: ['syn', 'rust-syn'],
386
+ exactness: 'exact-parser-ast',
387
+ sourceRangeModel: 'proc-macro2-span',
388
+ preservesTokens: false,
389
+ preservesTrivia: false,
390
+ supportsErrorRecovery: false,
391
+ notes: ['syn parses Rust token streams into an abstract syntax tree; macro expansion, name resolution, type checking, and lossless trivia remain host-owned evidence.']
392
+ }),
393
+ nativeParserAstFormatProfile('rust-analyzer-rowan', {
394
+ aliases: ['rowan', 'rust-analyzer'],
395
+ kind: 'concrete-syntax-tree',
396
+ languages: ['rust'],
397
+ parserAdapters: ['rust-analyzer-rowan'],
398
+ exactness: 'parser-tree',
399
+ sourceRangeModel: 'text-range',
400
+ preservesTokens: true,
401
+ preservesTrivia: true,
402
+ supportsIncremental: true,
403
+ supportsErrorRecovery: true,
404
+ notes: ['rust-analyzer uses rowan-backed concrete syntax trees with AST wrappers; semantic richness still depends on host analysis evidence.']
405
+ }),
406
+ nativeParserAstFormatProfile('tree-sitter', {
407
+ kind: 'concrete-syntax-tree',
408
+ languages: ['mixed'],
409
+ parserAdapters: ['tree-sitter'],
410
+ exactness: 'parser-tree',
411
+ sourceRangeModel: 'row-column',
412
+ preservesTokens: false,
413
+ preservesTrivia: false,
414
+ supportsIncremental: true,
415
+ supportsErrorRecovery: true,
416
+ notes: ['Tree-sitter provides cross-language concrete syntax trees; language-specific queries still decide semantic richness.']
417
+ }),
418
+ nativeParserAstFormatProfile('libcst', {
419
+ kind: 'concrete-syntax-tree',
420
+ languages: ['python'],
421
+ parserAdapters: ['libcst'],
422
+ exactness: 'parser-tree',
423
+ sourceRangeModel: 'metadata-position-provider',
424
+ preservesTokens: true,
425
+ preservesTrivia: true,
426
+ supportsErrorRecovery: false,
427
+ notes: ['LibCST-style trees preserve formatting and are best treated as host-owned evidence until normalized explicitly.']
428
+ }),
429
+ nativeParserAstFormatProfile('scip', {
430
+ kind: 'semantic-index',
431
+ languages: ['mixed'],
432
+ parserAdapters: ['scip'],
433
+ exactness: 'loss-aware-native-ast',
434
+ sourceRangeModel: 'range-tuples',
435
+ preservesTokens: false,
436
+ preservesTrivia: false,
437
+ supportsErrorRecovery: false,
438
+ notes: ['SCIP is semantic index evidence rather than a full parser AST; it is useful for symbols/references and source maps.']
439
+ }),
440
+ nativeParserAstFormatProfile('lsif', {
441
+ kind: 'semantic-index',
442
+ languages: ['mixed'],
443
+ parserAdapters: ['lsif'],
444
+ exactness: 'loss-aware-native-ast',
445
+ sourceRangeModel: 'lsp-ranges',
446
+ preservesTokens: false,
447
+ preservesTrivia: false,
448
+ supportsErrorRecovery: false,
449
+ notes: ['LSIF graph dumps are semantic/source-map evidence, not complete native ASTs.']
450
+ })
451
+ ]);
452
+
453
+ export const NativeParserAstFormats = Object.freeze(NativeParserAstFormatProfiles.map((profile) => profile.id));
454
+
336
455
  export const ExternalSemanticIndexFormats = Object.freeze([
337
456
  'frontier-semantic-index',
338
457
  'scip',
@@ -2125,6 +2244,57 @@ export function createNativeImportCoverageMatrix(input = {}) {
2125
2244
  };
2126
2245
  }
2127
2246
 
2247
+ export function getNativeParserAstFormatProfile(format) {
2248
+ const normalized = normalizeParserAstFormatId(format);
2249
+ return NativeParserAstFormatProfiles.find((profile) => profile.id === normalized || profile.aliases.includes(normalized));
2250
+ }
2251
+
2252
+ export function createNativeParserAstFormatMatrix(input = {}) {
2253
+ const imports = input.imports ?? [];
2254
+ const adapters = input.adapters ?? [];
2255
+ const profiles = mergeNativeParserAstFormatProfiles(input.formats ?? NativeParserAstFormatProfiles, imports, adapters);
2256
+ const formats = profiles.map((profile) => nativeParserAstFormatCoverageForProfile(profile, imports, adapters));
2257
+ const summary = formats.reduce((totals, entry) => {
2258
+ totals.formats += 1;
2259
+ totals.adapterSlots += entry.parserAdapters.length;
2260
+ totals.adapters += entry.adapters.total;
2261
+ totals.imports += entry.imports.total;
2262
+ totals.nativeAstNodes += entry.imports.nativeAstNodes;
2263
+ totals.symbols += entry.imports.symbols;
2264
+ totals.sourceMapMappings += entry.imports.sourceMapMappings;
2265
+ totals.losses += entry.imports.losses;
2266
+ totals.byKind[entry.kind] = (totals.byKind[entry.kind] ?? 0) + 1;
2267
+ totals.byReadiness[entry.imports.readiness] = (totals.byReadiness[entry.imports.readiness] ?? 0) + 1;
2268
+ for (const [capability, count] of Object.entries(entry.adapters.effectiveCapabilities)) {
2269
+ totals.effectiveCapabilities[capability] = (totals.effectiveCapabilities[capability] ?? 0) + count;
2270
+ }
2271
+ return totals;
2272
+ }, {
2273
+ formats: 0,
2274
+ adapterSlots: 0,
2275
+ adapters: 0,
2276
+ imports: 0,
2277
+ nativeAstNodes: 0,
2278
+ symbols: 0,
2279
+ sourceMapMappings: 0,
2280
+ losses: 0,
2281
+ byKind: {},
2282
+ byReadiness: {},
2283
+ effectiveCapabilities: {}
2284
+ });
2285
+ return {
2286
+ kind: 'frontier.lang.nativeParserAstFormatMatrix',
2287
+ version: 1,
2288
+ generatedAt: input.generatedAt ?? Date.now(),
2289
+ formats,
2290
+ summary,
2291
+ metadata: {
2292
+ note: 'Parser AST format coverage describes normalization evidence and host-parser obligations; it is not a lossless portability claim.',
2293
+ profileIds: profiles.map((profile) => profile.id)
2294
+ }
2295
+ };
2296
+ }
2297
+
2128
2298
  export function createProjectionTargetLossMatrix(input = {}) {
2129
2299
  const imports = input.imports ?? [];
2130
2300
  const adapters = input.adapters ?? [];
@@ -2435,6 +2605,112 @@ export function createTypeScriptCompilerNativeImporterAdapter(options = {}) {
2435
2605
  };
2436
2606
  }
2437
2607
 
2608
+ export function createPythonAstNativeImporterAdapter(options = {}) {
2609
+ return {
2610
+ id: options.id ?? 'frontier.python-ast-native-importer',
2611
+ language: options.language ?? 'python',
2612
+ parser: options.parser ?? 'python-ast',
2613
+ version: options.version,
2614
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
2615
+ coverage: nativeImporterAdapterCoverage({
2616
+ exactness: 'exact-parser-ast',
2617
+ exactAst: true,
2618
+ tokens: false,
2619
+ trivia: false,
2620
+ diagnostics: true,
2621
+ sourceRanges: true,
2622
+ generatedRanges: false,
2623
+ semanticCoverage: declarationSemanticCoverage(),
2624
+ notes: [
2625
+ 'Normalizes caller-owned Python stdlib ast trees into native AST nodes and declaration-level semantic index records.',
2626
+ 'Python ast does not preserve comments, whitespace, or concrete formatting; use LibCST/parso-style host evidence for round-trip trivia.'
2627
+ ]
2628
+ }, options.coverage),
2629
+ supportedExtensions: options.supportedExtensions ?? ['.py', '.pyi'],
2630
+ diagnostics: options.diagnostics,
2631
+ parse(input) {
2632
+ const parsed = input.options?.ast
2633
+ ?? input.options?.nativeAst
2634
+ ?? options.ast
2635
+ ?? parsePythonAstSource(input, options);
2636
+ const root = pythonAstRoot(parsed);
2637
+ if (!root) {
2638
+ return missingInjectedParserResult(input, {
2639
+ parser: options.parser ?? 'python-ast',
2640
+ adapterId: options.id ?? 'frontier.python-ast-native-importer',
2641
+ message: 'createPythonAstNativeImporterAdapter requires an injected Python AST object, parserModule.parse function, parse function, or adapterOptions.ast.'
2642
+ });
2643
+ }
2644
+ const parseDiagnostics = normalizeParserErrors(parsed?.errors ?? parsed?.diagnostics, input, {
2645
+ parser: options.parser ?? 'python-ast'
2646
+ });
2647
+ return createNativeImportFromPythonAst(root, input, {
2648
+ parser: options.parser ?? 'python-ast',
2649
+ astFormat: 'python-ast',
2650
+ maxNodes: options.maxNodes,
2651
+ diagnostics: parseDiagnostics,
2652
+ pythonVersion: options.pythonVersion ?? input.options?.pythonVersion ?? parsed?.pythonVersion,
2653
+ includeAttributes: options.includeAttributes ?? input.options?.includeAttributes
2654
+ });
2655
+ }
2656
+ };
2657
+ }
2658
+
2659
+ export function createRustSynNativeImporterAdapter(options = {}) {
2660
+ return {
2661
+ id: options.id ?? 'frontier.rust-syn-native-importer',
2662
+ language: options.language ?? 'rust',
2663
+ parser: options.parser ?? 'syn',
2664
+ version: options.version,
2665
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
2666
+ coverage: nativeImporterAdapterCoverage({
2667
+ exactness: 'exact-parser-ast',
2668
+ exactAst: true,
2669
+ tokens: false,
2670
+ trivia: false,
2671
+ diagnostics: true,
2672
+ sourceRanges: false,
2673
+ generatedRanges: false,
2674
+ semanticCoverage: declarationSemanticCoverage(),
2675
+ notes: [
2676
+ 'Normalizes caller-owned syn-shaped Rust ASTs into native AST nodes and declaration-level semantic index records.',
2677
+ 'syn does not expand macros, resolve names, type-check, or preserve full concrete syntax/trivia; attach rust-analyzer/rustc evidence for those claims.'
2678
+ ]
2679
+ }, options.coverage),
2680
+ supportedExtensions: options.supportedExtensions ?? ['.rs'],
2681
+ diagnostics: options.diagnostics,
2682
+ parse(input) {
2683
+ const parsed = input.options?.ast
2684
+ ?? input.options?.nativeAst
2685
+ ?? input.options?.file
2686
+ ?? input.options?.sourceFile
2687
+ ?? options.ast
2688
+ ?? options.file
2689
+ ?? options.sourceFile
2690
+ ?? parseRustSynSource(input, options);
2691
+ const root = rustSynAstRoot(parsed);
2692
+ if (!root) {
2693
+ return missingInjectedParserResult(input, {
2694
+ parser: options.parser ?? 'syn',
2695
+ adapterId: options.id ?? 'frontier.rust-syn-native-importer',
2696
+ message: 'createRustSynNativeImporterAdapter requires an injected syn-shaped AST object, parserModule.parse function, parse function, or adapterOptions.ast.'
2697
+ });
2698
+ }
2699
+ const parseDiagnostics = normalizeParserErrors(parsed?.errors ?? parsed?.diagnostics, input, {
2700
+ parser: options.parser ?? 'syn'
2701
+ });
2702
+ return createNativeImportFromRustSyn(root, input, {
2703
+ parser: options.parser ?? 'syn',
2704
+ astFormat: 'rust-syn',
2705
+ maxNodes: options.maxNodes,
2706
+ diagnostics: parseDiagnostics,
2707
+ rustEdition: options.rustEdition ?? input.options?.rustEdition ?? parsed?.rustEdition,
2708
+ includeAttributes: options.includeAttributes ?? input.options?.includeAttributes
2709
+ });
2710
+ }
2711
+ };
2712
+ }
2713
+
2438
2714
  export function createTreeSitterNativeImporterAdapter(options = {}) {
2439
2715
  return {
2440
2716
  id: options.id ?? `frontier.tree-sitter-${idFragment(options.language ?? 'source')}-native-importer`,
@@ -6767,6 +7043,134 @@ function nativeImportLanguageProfile(language, input = {}) {
6767
7043
  });
6768
7044
  }
6769
7045
 
7046
+ function nativeParserAstFormatProfile(id, input = {}) {
7047
+ return Object.freeze({
7048
+ id,
7049
+ aliases: Object.freeze(uniqueStrings(input.aliases ?? [])),
7050
+ kind: input.kind ?? 'abstract-ast',
7051
+ languages: Object.freeze(uniqueStrings(input.languages ?? [])),
7052
+ parserAdapters: Object.freeze(uniqueStrings(input.parserAdapters ?? [id])),
7053
+ exactness: input.exactness ?? 'unknown',
7054
+ sourceRangeModel: input.sourceRangeModel ?? 'unknown',
7055
+ preservesTokens: Boolean(input.preservesTokens),
7056
+ preservesTrivia: Boolean(input.preservesTrivia),
7057
+ supportsIncremental: Boolean(input.supportsIncremental),
7058
+ supportsErrorRecovery: Boolean(input.supportsErrorRecovery),
7059
+ notes: Object.freeze(uniqueStrings(input.notes ?? []))
7060
+ });
7061
+ }
7062
+
7063
+ function normalizeParserAstFormatId(format) {
7064
+ return String(format ?? '').trim().toLowerCase().replace(/[_\s]+/g, '-');
7065
+ }
7066
+
7067
+ function mergeNativeParserAstFormatProfiles(profiles, imports, adapters) {
7068
+ const byId = new Map((profiles ?? []).map((profile) => [normalizeParserAstFormatId(profile.id ?? profile), nativeParserAstFormatProfile(normalizeParserAstFormatId(profile.id ?? profile), profile)]));
7069
+ for (const adapter of adapters ?? []) {
7070
+ const summary = safeNativeImporterAdapterSummary(adapter);
7071
+ if (!summary) continue;
7072
+ const formatId = parserAstFormatIdForParser(summary.parser);
7073
+ if (!byId.has(formatId)) {
7074
+ byId.set(formatId, nativeParserAstFormatProfile(formatId, {
7075
+ languages: [summary.language],
7076
+ parserAdapters: [summary.parser],
7077
+ exactness: summary.coverage.exactness,
7078
+ sourceRangeModel: summary.coverage.sourceRanges ? 'adapter-reported' : 'unknown'
7079
+ }));
7080
+ }
7081
+ }
7082
+ for (const imported of imports ?? []) {
7083
+ const formatId = parserAstFormatIdForImport(imported);
7084
+ if (formatId && !byId.has(formatId)) {
7085
+ byId.set(formatId, nativeParserAstFormatProfile(formatId, {
7086
+ languages: [imported.language].filter(Boolean),
7087
+ parserAdapters: [imported.parser ?? imported.nativeAst?.parser ?? formatId],
7088
+ exactness: 'unknown',
7089
+ sourceRangeModel: (imported.sourceMaps ?? []).some((sourceMap) => sourceMap.mappings?.some((mapping) => mapping.sourceSpan)) ? 'adapter-reported' : 'unknown'
7090
+ }));
7091
+ }
7092
+ }
7093
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
7094
+ }
7095
+
7096
+ function nativeParserAstFormatCoverageForProfile(profile, imports, adapters) {
7097
+ const formatIds = new Set([profile.id, ...profile.aliases].map(normalizeParserAstFormatId));
7098
+ const adapterParsers = new Set(profile.parserAdapters.map(parserAstFormatIdForParser));
7099
+ const matchingAdapters = (adapters ?? [])
7100
+ .map((adapter) => safeNativeImporterAdapterSummary(adapter))
7101
+ .filter(Boolean)
7102
+ .filter((adapter) => formatIds.has(parserAstFormatIdForParser(adapter.parser)) || adapterParsers.has(parserAstFormatIdForParser(adapter.parser)));
7103
+ const matchingImports = (imports ?? [])
7104
+ .filter((imported) => {
7105
+ const formatId = parserAstFormatIdForImport(imported);
7106
+ return formatId && (formatIds.has(formatId) || adapterParsers.has(formatId));
7107
+ });
7108
+ const effectiveCapabilities = {};
7109
+ for (const adapter of matchingAdapters) {
7110
+ for (const row of adapter.coverage.capabilityEvidence?.capabilities ?? []) {
7111
+ if (row.effective) effectiveCapabilities[row.capability] = (effectiveCapabilities[row.capability] ?? 0) + 1;
7112
+ }
7113
+ }
7114
+ const readiness = matchingImports.reduce(
7115
+ (current, imported) => maxSemanticMergeReadiness(current, nativeImportReadiness(imported)),
7116
+ matchingImports.length ? 'ready' : 'needs-review'
7117
+ );
7118
+ return {
7119
+ id: profile.id,
7120
+ kind: profile.kind,
7121
+ languages: profile.languages,
7122
+ parserAdapters: profile.parserAdapters,
7123
+ exactness: profile.exactness,
7124
+ sourceRangeModel: profile.sourceRangeModel,
7125
+ preservesTokens: profile.preservesTokens,
7126
+ preservesTrivia: profile.preservesTrivia,
7127
+ supportsIncremental: profile.supportsIncremental,
7128
+ supportsErrorRecovery: profile.supportsErrorRecovery,
7129
+ notes: profile.notes,
7130
+ adapters: {
7131
+ total: matchingAdapters.length,
7132
+ ids: matchingAdapters.map((adapter) => adapter.id),
7133
+ parsers: uniqueStrings(matchingAdapters.map((adapter) => adapter.parser)),
7134
+ effectiveCapabilities
7135
+ },
7136
+ imports: {
7137
+ total: matchingImports.length,
7138
+ sourcePaths: matchingImports.map((imported) => imported.sourcePath).filter(Boolean),
7139
+ readiness,
7140
+ nativeAstNodes: matchingImports.reduce((sum, imported) => sum + Object.keys(imported.nativeAst?.nodes ?? {}).length, 0),
7141
+ symbols: matchingImports.reduce((sum, imported) => sum + (imported.semanticIndex?.symbols?.length ?? 0), 0),
7142
+ sourceMapMappings: matchingImports.reduce((sum, imported) => sum + (imported.sourceMaps ?? []).reduce((mapSum, sourceMap) => mapSum + (sourceMap.mappings?.length ?? 0), 0), 0),
7143
+ losses: matchingImports.reduce((sum, imported) => sum + (imported.losses?.length ?? 0), 0)
7144
+ }
7145
+ };
7146
+ }
7147
+
7148
+ function parserAstFormatIdForParser(parser) {
7149
+ const text = normalizeParserAstFormatId(parser);
7150
+ if (text.includes('typescript')) return 'typescript-compiler-api';
7151
+ if (text.includes('python') && text.includes('ast')) return 'python-ast';
7152
+ if (text === 'syn' || text.includes('rust-syn')) return 'rust-syn';
7153
+ if (text.includes('rust-analyzer') || text.includes('rowan')) return 'rust-analyzer-rowan';
7154
+ if (text.includes('tree-sitter') || text.includes('treesitter')) return 'tree-sitter';
7155
+ if (text.includes('babel')) return 'babel';
7156
+ if (text.includes('estree')) return 'estree';
7157
+ if (text.includes('libcst')) return 'libcst';
7158
+ if (text.includes('scip')) return 'scip';
7159
+ if (text.includes('lsif')) return 'lsif';
7160
+ return text || 'unknown';
7161
+ }
7162
+
7163
+ function parserAstFormatIdForImport(imported) {
7164
+ return parserAstFormatIdForParser(
7165
+ imported?.metadata?.adapter?.parser
7166
+ ?? imported?.metadata?.astFormat
7167
+ ?? imported?.nativeAst?.metadata?.astFormat
7168
+ ?? imported?.nativeAst?.parser
7169
+ ?? imported?.parser
7170
+ ?? imported?.metadata?.parser
7171
+ );
7172
+ }
7173
+
6770
7174
  function mergeNativeImportProfiles(languages, imports, adapters, targetAdapters = []) {
6771
7175
  const profilesByLanguage = new Map();
6772
7176
  for (const profile of languages) {
@@ -7764,6 +8168,37 @@ function parseTreeSitterSource(input, options) {
7764
8168
  return undefined;
7765
8169
  }
7766
8170
 
8171
+ function parsePythonAstSource(input, options) {
8172
+ const parse = options.parse ?? options.parserModule?.parse ?? options.pythonAst?.parse;
8173
+ if (typeof parse !== 'function') return undefined;
8174
+ const parserOptions = {
8175
+ sourcePath: input.sourcePath,
8176
+ filename: input.sourcePath,
8177
+ mode: options.mode ?? input.options?.mode ?? 'exec',
8178
+ typeComments: options.typeComments ?? input.options?.typeComments,
8179
+ featureVersion: options.featureVersion ?? input.options?.featureVersion,
8180
+ optimize: options.optimize ?? input.options?.optimize,
8181
+ includeAttributes: options.includeAttributes ?? input.options?.includeAttributes,
8182
+ ...(options.parserOptions ?? {}),
8183
+ ...(input.options?.parserOptions ?? {})
8184
+ };
8185
+ return parse(input.sourceText, parserOptions);
8186
+ }
8187
+
8188
+ function parseRustSynSource(input, options) {
8189
+ const parse = options.parse ?? options.parserModule?.parse ?? options.rustSyn?.parse ?? options.syn?.parse;
8190
+ if (typeof parse !== 'function') return undefined;
8191
+ const parserOptions = {
8192
+ sourcePath: input.sourcePath,
8193
+ filename: input.sourcePath,
8194
+ edition: options.rustEdition ?? input.options?.rustEdition ?? '2021',
8195
+ includeAttributes: options.includeAttributes ?? input.options?.includeAttributes,
8196
+ ...(options.parserOptions ?? {}),
8197
+ ...(input.options?.parserOptions ?? {})
8198
+ };
8199
+ return parse(input.sourceText, parserOptions);
8200
+ }
8201
+
7767
8202
  function createNativeImportFromSyntaxAst(ast, input, options) {
7768
8203
  const root = normalizeSyntaxAstRoot(ast, options.astFormat);
7769
8204
  if (!root) {
@@ -7824,6 +8259,66 @@ function createNativeImportFromTypeScriptAst(sourceFile, input, options) {
7824
8259
  };
7825
8260
  }
7826
8261
 
8262
+ function createNativeImportFromPythonAst(root, input, options) {
8263
+ const context = createAstNormalizationContext(input, options);
8264
+ visitPythonAstNode(root, context, 'root');
8265
+ if (context.truncated) {
8266
+ context.losses.push(truncatedAstLoss(input, context, options));
8267
+ }
8268
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
8269
+ return {
8270
+ rootId: context.rootId,
8271
+ nodes: context.nodes,
8272
+ semanticIndex: semantic.semanticIndex,
8273
+ mappings: semantic.mappings,
8274
+ losses: mergeNativeLosses(context.losses, options.diagnostics?.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, {
8275
+ id: input.adapterId,
8276
+ version: input.adapterVersion
8277
+ }, input)) ?? []),
8278
+ evidence: semantic.evidence,
8279
+ diagnostics: options.diagnostics,
8280
+ metadata: {
8281
+ astFormat: options.astFormat,
8282
+ parser: options.parser,
8283
+ pythonVersion: options.pythonVersion,
8284
+ includeAttributes: Boolean(options.includeAttributes),
8285
+ normalizedNodeCount: Object.keys(context.nodes).length,
8286
+ declarationCount: context.declarations.length,
8287
+ truncated: context.truncated
8288
+ }
8289
+ };
8290
+ }
8291
+
8292
+ function createNativeImportFromRustSyn(root, input, options) {
8293
+ const context = createAstNormalizationContext(input, options);
8294
+ visitRustSynNode(root, context, 'root');
8295
+ if (context.truncated) {
8296
+ context.losses.push(truncatedAstLoss(input, context, options));
8297
+ }
8298
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
8299
+ return {
8300
+ rootId: context.rootId,
8301
+ nodes: context.nodes,
8302
+ semanticIndex: semantic.semanticIndex,
8303
+ mappings: semantic.mappings,
8304
+ losses: mergeNativeLosses(context.losses, options.diagnostics?.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, {
8305
+ id: input.adapterId,
8306
+ version: input.adapterVersion
8307
+ }, input)) ?? []),
8308
+ evidence: semantic.evidence,
8309
+ diagnostics: options.diagnostics,
8310
+ metadata: {
8311
+ astFormat: options.astFormat,
8312
+ parser: options.parser,
8313
+ rustEdition: options.rustEdition,
8314
+ includeAttributes: Boolean(options.includeAttributes),
8315
+ normalizedNodeCount: Object.keys(context.nodes).length,
8316
+ declarationCount: context.declarations.length,
8317
+ truncated: context.truncated
8318
+ }
8319
+ };
8320
+ }
8321
+
7827
8322
  function createNativeImportFromTreeSitter(root, input, options) {
7828
8323
  const context = createAstNormalizationContext(input, options);
7829
8324
  visitTreeSitterNode(root, context, 'root');
@@ -7960,6 +8455,114 @@ function visitTypeScriptAstNode(node, sourceFile, context, propertyPath, ts) {
7960
8455
  return id;
7961
8456
  }
7962
8457
 
8458
+ function visitPythonAstNode(node, context, propertyPath) {
8459
+ if (!isPythonAstNode(node) || context.truncated) return undefined;
8460
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
8461
+ if (context.counter >= context.maxNodes) {
8462
+ context.truncated = true;
8463
+ return undefined;
8464
+ }
8465
+ const kind = pythonAstKind(node);
8466
+ const span = spanFromPythonAstNode(node, context.input);
8467
+ const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
8468
+ context.objectIds.set(node, id);
8469
+ if (!context.rootId) context.rootId = id;
8470
+ const children = [];
8471
+ for (const [field, value] of pythonAstChildEntries(node)) {
8472
+ if (Array.isArray(value)) {
8473
+ value.forEach((entry, index) => {
8474
+ const childId = visitPythonAstNode(entry, context, `${propertyPath}.${field}[${index}]`);
8475
+ if (childId) children.push(childId);
8476
+ });
8477
+ } else {
8478
+ const childId = visitPythonAstNode(value, context, `${propertyPath}.${field}`);
8479
+ if (childId) children.push(childId);
8480
+ }
8481
+ }
8482
+ const declaration = pythonAstDeclaration(node, kind, id, context.input);
8483
+ const nativeNode = {
8484
+ id,
8485
+ kind,
8486
+ languageKind: `${context.input.language}.${kind}`,
8487
+ span,
8488
+ value: declaration?.name ?? pythonAstNodeValue(node),
8489
+ fields: primitivePythonAstFields(node, kind),
8490
+ children,
8491
+ metadata: {
8492
+ astFormat: context.options.astFormat,
8493
+ propertyPath,
8494
+ lineno: numberOrUndefined(node.lineno ?? node.line),
8495
+ colOffset: numberOrUndefined(node.col_offset ?? node.colOffset),
8496
+ endLineno: numberOrUndefined(node.end_lineno ?? node.endLine),
8497
+ endColOffset: numberOrUndefined(node.end_col_offset ?? node.endColOffset)
8498
+ }
8499
+ };
8500
+ context.nodes[id] = nativeNode;
8501
+ if (declaration) context.declarations.push({ ...declaration, nativeNode });
8502
+ return id;
8503
+ }
8504
+
8505
+ function visitRustSynNode(node, context, propertyPath) {
8506
+ if (!isRustSynAstNode(node) || context.truncated) return undefined;
8507
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
8508
+ if (context.counter >= context.maxNodes) {
8509
+ context.truncated = true;
8510
+ return undefined;
8511
+ }
8512
+ const kind = rustSynKind(node);
8513
+ const payload = rustSynPayload(node);
8514
+ const span = spanFromRustSynNode(payload, context.input);
8515
+ const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
8516
+ context.objectIds.set(node, id);
8517
+ if (!context.rootId) context.rootId = id;
8518
+ const children = [];
8519
+ for (const [field, value] of rustSynChildEntries(node)) {
8520
+ if (Array.isArray(value)) {
8521
+ value.forEach((entry, index) => {
8522
+ const childId = visitRustSynNode(entry, context, `${propertyPath}.${field}[${index}]`);
8523
+ if (childId) children.push(childId);
8524
+ });
8525
+ } else {
8526
+ const childId = visitRustSynNode(value, context, `${propertyPath}.${field}`);
8527
+ if (childId) children.push(childId);
8528
+ }
8529
+ }
8530
+ const declaration = rustSynDeclaration(payload, kind, id, context.input);
8531
+ const nativeNode = {
8532
+ id,
8533
+ kind,
8534
+ languageKind: `${context.input.language}.${kind}`,
8535
+ span,
8536
+ value: declaration?.name ?? rustSynNodeValue(payload),
8537
+ fields: primitiveRustSynFields(payload, kind),
8538
+ children,
8539
+ metadata: {
8540
+ astFormat: context.options.astFormat,
8541
+ propertyPath,
8542
+ spanKind: rustSynSpanKind(payload)
8543
+ }
8544
+ };
8545
+ context.nodes[id] = nativeNode;
8546
+ if (declaration) context.declarations.push({ ...declaration, nativeNode });
8547
+ if (rustSynMacroKind(kind)) {
8548
+ context.losses.push({
8549
+ id: `loss_${idFragment(id)}_rust_macro_expansion`,
8550
+ severity: 'warning',
8551
+ phase: 'parse',
8552
+ sourceFormat: context.input.language,
8553
+ kind: 'macroExpansion',
8554
+ message: 'Rust macro syntax was parsed but macro expansion and generated items require host compiler evidence.',
8555
+ span,
8556
+ nodeId: id,
8557
+ metadata: {
8558
+ parser: context.options.parser,
8559
+ astFormat: context.options.astFormat
8560
+ }
8561
+ });
8562
+ }
8563
+ return id;
8564
+ }
8565
+
7963
8566
  function visitTreeSitterNode(node, context, propertyPath) {
7964
8567
  if (!node || typeof node !== 'object' || context.truncated) return undefined;
7965
8568
  if (context.objectIds.has(node)) return context.objectIds.get(node);
@@ -8455,6 +9058,14 @@ function nativeTargetProjectionDiagnosticToLoss(diagnostic, index, adapter, inpu
8455
9058
  };
8456
9059
  }
8457
9060
 
9061
+ function safeNativeImporterAdapterSummary(adapter) {
9062
+ try {
9063
+ return normalizeNativeImporterAdapter(adapter);
9064
+ } catch {
9065
+ return undefined;
9066
+ }
9067
+ }
9068
+
8458
9069
  function normalizeNativeImporterAdapter(adapter) {
8459
9070
  if (!adapter || typeof adapter !== 'object') {
8460
9071
  throw new Error('Native importer adapter must be an object');
@@ -9089,6 +9700,104 @@ function isSyntaxAstNode(value) {
9089
9700
  return Boolean(value && typeof value === 'object' && typeof (value.type ?? value.kind) === 'string');
9090
9701
  }
9091
9702
 
9703
+ function pythonAstRoot(value) {
9704
+ if (!value || typeof value !== 'object') return undefined;
9705
+ if (isPythonAstNode(value)) return value;
9706
+ if (isPythonAstNode(value.ast)) return value.ast;
9707
+ if (isPythonAstNode(value.root)) return value.root;
9708
+ if (isPythonAstNode(value.module)) return value.module;
9709
+ return undefined;
9710
+ }
9711
+
9712
+ function isPythonAstNode(value) {
9713
+ return Boolean(value && typeof value === 'object' && typeof pythonAstKind(value) === 'string');
9714
+ }
9715
+
9716
+ function pythonAstKind(node) {
9717
+ return node?._type ?? node?.type ?? node?.kind ?? node?.nodeType;
9718
+ }
9719
+
9720
+ function rustSynAstRoot(value) {
9721
+ if (!value || typeof value !== 'object') return undefined;
9722
+ if (isRustSynAstNode(value)) return value;
9723
+ if (isRustSynAstNode(value.ast)) return value.ast;
9724
+ if (isRustSynAstNode(value.file)) return value.file;
9725
+ if (isRustSynAstNode(value.root)) return value.root;
9726
+ if (isRustSynAstNode(value.module)) return value.module;
9727
+ if (isRustSynAstNode(value.sourceFile)) return value.sourceFile;
9728
+ return undefined;
9729
+ }
9730
+
9731
+ function isRustSynAstNode(value) {
9732
+ return Boolean(value && typeof value === 'object' && typeof rustSynKind(value) === 'string');
9733
+ }
9734
+
9735
+ function rustSynKind(node) {
9736
+ const declared = node?._type ?? node?.type ?? node?.kind ?? node?.nodeType ?? node?.synKind;
9737
+ if (typeof declared === 'string') return normalizeRustSynKind(declared);
9738
+ const wrapper = rustSynWrapperKind(node);
9739
+ if (wrapper) return normalizeRustSynKind(wrapper);
9740
+ if (Array.isArray(node?.items)) return 'File';
9741
+ if (node?.sig && node?.block) return 'ItemFn';
9742
+ if (node?.ident && node?.fields && Array.isArray(node?.variants)) return 'ItemEnum';
9743
+ if (node?.ident && node?.fields) return 'ItemStruct';
9744
+ if (node?.ident && Array.isArray(node?.items)) return 'ItemMod';
9745
+ if (node?.trait_ || node?.self_ty || node?.selfType) return 'ItemImpl';
9746
+ if (node?.path && (node?.tree || node?.trees)) return 'ItemUse';
9747
+ return undefined;
9748
+ }
9749
+
9750
+ function rustSynPayload(node) {
9751
+ if (!node || typeof node !== 'object') return node;
9752
+ const wrapper = rustSynWrapperKind(node);
9753
+ return wrapper ? node[wrapper] : node;
9754
+ }
9755
+
9756
+ function rustSynWrapperKind(node) {
9757
+ if (!node || typeof node !== 'object') return undefined;
9758
+ const keys = Object.keys(node).filter((key) => !ignoredRustSynField(key));
9759
+ if (keys.length !== 1) return undefined;
9760
+ const key = keys[0];
9761
+ const value = node[key];
9762
+ if (!value || typeof value !== 'object') return undefined;
9763
+ if (/^(?:Fn|Struct|Enum|Trait|Impl|Use|Mod|Type|Const|Static|Union|Macro)$/.test(key)) return key;
9764
+ if (/^(?:Item|ImplItem|TraitItem|ForeignItem)/.test(key)) return key;
9765
+ return undefined;
9766
+ }
9767
+
9768
+ function normalizeRustSynKind(kind) {
9769
+ const text = String(kind);
9770
+ const compact = text.replace(/^(?:syn::)?/, '').replace(/^Item::/, 'Item').replace(/^ImplItem::/, 'ImplItem').replace(/^TraitItem::/, 'TraitItem');
9771
+ if (/^fn$/i.test(compact)) return 'ItemFn';
9772
+ if (/^struct$/i.test(compact)) return 'ItemStruct';
9773
+ if (/^enum$/i.test(compact)) return 'ItemEnum';
9774
+ if (/^trait$/i.test(compact)) return 'ItemTrait';
9775
+ if (/^impl$/i.test(compact)) return 'ItemImpl';
9776
+ if (/^use$/i.test(compact)) return 'ItemUse';
9777
+ if (/^mod$/i.test(compact)) return 'ItemMod';
9778
+ if (/^type$/i.test(compact)) return 'ItemType';
9779
+ if (/^const$/i.test(compact)) return 'ItemConst';
9780
+ if (/^static$/i.test(compact)) return 'ItemStatic';
9781
+ if (/^union$/i.test(compact)) return 'ItemUnion';
9782
+ if (/^item_fn$/i.test(compact)) return 'ItemFn';
9783
+ if (/^item_struct$/i.test(compact)) return 'ItemStruct';
9784
+ if (/^item_enum$/i.test(compact)) return 'ItemEnum';
9785
+ if (/^item_trait$/i.test(compact)) return 'ItemTrait';
9786
+ if (/^item_impl$/i.test(compact)) return 'ItemImpl';
9787
+ if (/^item_use$/i.test(compact)) return 'ItemUse';
9788
+ if (/^item_mod$/i.test(compact)) return 'ItemMod';
9789
+ if (/^item_type$/i.test(compact)) return 'ItemType';
9790
+ if (/^item_const$/i.test(compact)) return 'ItemConst';
9791
+ if (/^item_static$/i.test(compact)) return 'ItemStatic';
9792
+ if (/^item_union$/i.test(compact)) return 'ItemUnion';
9793
+ if (/^item_macro$/i.test(compact)) return 'ItemMacro';
9794
+ if (/^macro$/i.test(compact)) return 'Macro';
9795
+ if (/^impl_item_fn$/i.test(compact)) return 'ImplItemFn';
9796
+ if (/^trait_item_fn$/i.test(compact)) return 'TraitItemFn';
9797
+ if (/^foreign_item_fn$/i.test(compact)) return 'ForeignItemFn';
9798
+ return compact;
9799
+ }
9800
+
9092
9801
  function ignoredSyntaxField(key) {
9093
9802
  return key === 'type'
9094
9803
  || key === 'kind'
@@ -9105,6 +9814,36 @@ function ignoredSyntaxField(key) {
9105
9814
  || key === 'parent';
9106
9815
  }
9107
9816
 
9817
+ function ignoredPythonAstField(key) {
9818
+ return key === '_type'
9819
+ || key === 'type'
9820
+ || key === 'kind'
9821
+ || key === 'nodeType'
9822
+ || key === '_fields'
9823
+ || key === 'lineno'
9824
+ || key === 'col_offset'
9825
+ || key === 'end_lineno'
9826
+ || key === 'end_col_offset'
9827
+ || key === 'line'
9828
+ || key === 'colOffset'
9829
+ || key === 'endLine'
9830
+ || key === 'endColOffset'
9831
+ || key === 'ctx'
9832
+ || key === 'parent';
9833
+ }
9834
+
9835
+ function ignoredRustSynField(key) {
9836
+ return key === '_type'
9837
+ || key === 'type'
9838
+ || key === 'kind'
9839
+ || key === 'nodeType'
9840
+ || key === 'synKind'
9841
+ || key === 'span'
9842
+ || key === 'tokens'
9843
+ || key === 'tokenStream'
9844
+ || key === 'parent';
9845
+ }
9846
+
9108
9847
  function primitiveSyntaxFields(node) {
9109
9848
  const fields = {};
9110
9849
  for (const key of ['name', 'operator', 'sourceType', 'async', 'generator', 'computed', 'static', 'exportKind', 'importKind', 'optional']) {
@@ -9118,11 +9857,52 @@ function primitiveSyntaxFields(node) {
9118
9857
  return fields;
9119
9858
  }
9120
9859
 
9860
+ function primitivePythonAstFields(node, kind) {
9861
+ const fields = { kind };
9862
+ for (const key of ['name', 'id', 'arg', 'module', 'level', 'attr', 'asname', 'type_comment']) {
9863
+ if (typeof node[key] === 'string' || typeof node[key] === 'number' || typeof node[key] === 'boolean' || node[key] === null) {
9864
+ fields[key] = node[key];
9865
+ }
9866
+ }
9867
+ if (Array.isArray(node.names)) {
9868
+ fields.names = node.names
9869
+ .map((entry) => pythonAliasName(entry))
9870
+ .filter(Boolean)
9871
+ .join(',');
9872
+ }
9873
+ const literal = pythonAstLiteralValue(node);
9874
+ if (literal !== undefined) fields.literal = literal;
9875
+ return fields;
9876
+ }
9877
+
9878
+ function primitiveRustSynFields(node, kind) {
9879
+ const fields = { kind };
9880
+ const ident = rustSynIdentName(node.ident ?? node.name ?? node.sig?.ident);
9881
+ if (ident) fields.ident = ident;
9882
+ const path = rustSynPathName(node.path ?? node.trait_ ?? node.self_ty ?? node.selfType ?? node.ty);
9883
+ if (path) fields.path = path;
9884
+ const visibility = rustSynVisibility(node.vis ?? node.visibility);
9885
+ if (visibility) fields.visibility = visibility;
9886
+ for (const key of ['mutability', 'defaultness', 'constness', 'asyncness', 'unsafety', 'abi']) {
9887
+ const value = node[key];
9888
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) fields[key] = value;
9889
+ }
9890
+ if (Array.isArray(node.attrs) && node.attrs.length) fields.attributeCount = node.attrs.length;
9891
+ return fields;
9892
+ }
9893
+
9121
9894
  function literalSyntaxValue(node) {
9122
9895
  if (node.value === null || typeof node.value === 'string' || typeof node.value === 'number' || typeof node.value === 'boolean') return node.value;
9123
9896
  return undefined;
9124
9897
  }
9125
9898
 
9899
+ function pythonAstLiteralValue(node) {
9900
+ if (node.value === null || typeof node.value === 'string' || typeof node.value === 'number' || typeof node.value === 'boolean') return node.value;
9901
+ if (typeof node.s === 'string' || typeof node.s === 'number') return node.s;
9902
+ if (typeof node.n === 'number') return node.n;
9903
+ return undefined;
9904
+ }
9905
+
9126
9906
  function spanFromLoc(loc, input) {
9127
9907
  if (!loc?.start) return undefined;
9128
9908
  return {
@@ -9135,6 +9915,52 @@ function spanFromLoc(loc, input) {
9135
9915
  };
9136
9916
  }
9137
9917
 
9918
+ function spanFromPythonAstNode(node, input) {
9919
+ const line = node.lineno ?? node.line;
9920
+ if (typeof line !== 'number') return undefined;
9921
+ const col = node.col_offset ?? node.colOffset;
9922
+ const endLine = node.end_lineno ?? node.endLine;
9923
+ const endCol = node.end_col_offset ?? node.endColOffset;
9924
+ return {
9925
+ sourceId: input.sourceHash,
9926
+ path: input.sourcePath,
9927
+ startLine: line,
9928
+ startColumn: typeof col === 'number' ? col + 1 : undefined,
9929
+ endLine: typeof endLine === 'number' ? endLine : undefined,
9930
+ endColumn: typeof endCol === 'number' ? endCol + 1 : undefined
9931
+ };
9932
+ }
9933
+
9934
+ function spanFromRustSynNode(node, input) {
9935
+ const span = node.span ?? node.ident?.span ?? node.sig?.ident?.span ?? node.name?.span;
9936
+ if (!span || typeof span !== 'object') return undefined;
9937
+ const start = span.start ?? span.lo ?? span.begin;
9938
+ const end = span.end ?? span.hi;
9939
+ const startLine = span.startLine ?? span.line ?? start?.line;
9940
+ const startColumn = span.startColumn ?? span.column ?? start?.column;
9941
+ const endLine = span.endLine ?? end?.line;
9942
+ const endColumn = span.endColumn ?? end?.column;
9943
+ if (typeof startLine !== 'number') return undefined;
9944
+ return {
9945
+ sourceId: input.sourceHash,
9946
+ path: input.sourcePath,
9947
+ startLine,
9948
+ startColumn: typeof startColumn === 'number' ? rustSynColumnToOneBased(startColumn, span) : undefined,
9949
+ endLine: typeof endLine === 'number' ? endLine : undefined,
9950
+ endColumn: typeof endColumn === 'number' ? rustSynColumnToOneBased(endColumn, span) : undefined
9951
+ };
9952
+ }
9953
+
9954
+ function rustSynColumnToOneBased(column, span) {
9955
+ return span.columnBase === 1 || span.columnsOneBased ? column : column + 1;
9956
+ }
9957
+
9958
+ function rustSynSpanKind(node) {
9959
+ const span = node.span ?? node.ident?.span ?? node.sig?.ident?.span ?? node.name?.span;
9960
+ if (!span || typeof span !== 'object') return undefined;
9961
+ return span.kind ?? span.source ?? 'host-span';
9962
+ }
9963
+
9138
9964
  function syntaxDeclaration(node, nativeNodeId, input) {
9139
9965
  const kind = String(node.type ?? node.kind ?? '');
9140
9966
  if (kind === 'ImportDeclaration') {
@@ -9153,6 +9979,64 @@ function syntaxDeclaration(node, nativeNodeId, input) {
9153
9979
  return undefined;
9154
9980
  }
9155
9981
 
9982
+ function pythonAstDeclaration(node, kind, nativeNodeId, input) {
9983
+ if (kind === 'Import') {
9984
+ const name = (node.names ?? []).map((entry) => pythonAliasName(entry)).find(Boolean);
9985
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
9986
+ }
9987
+ if (kind === 'ImportFrom') {
9988
+ const name = node.module ?? (node.names ?? []).map((entry) => pythonAliasName(entry)).find(Boolean);
9989
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
9990
+ }
9991
+ if (kind === 'FunctionDef' || kind === 'AsyncFunctionDef') return declarationRecord(input, nativeNodeId, node.name, 'function', 'definition');
9992
+ if (kind === 'ClassDef') return declarationRecord(input, nativeNodeId, node.name, 'class', 'definition');
9993
+ if (kind === 'AnnAssign' || kind === 'Assign') {
9994
+ const name = pythonAssignmentName(node);
9995
+ if (name) return declarationRecord(input, nativeNodeId, name, 'variable', 'definition');
9996
+ }
9997
+ return undefined;
9998
+ }
9999
+
10000
+ function rustSynDeclaration(node, kind, nativeNodeId, input) {
10001
+ if (kind === 'ItemUse' || kind === 'UseTree' || kind === 'UsePath' || kind === 'UseName') {
10002
+ const name = rustSynUseName(node);
10003
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
10004
+ }
10005
+ if (kind === 'ItemFn' || kind === 'ForeignItemFn') {
10006
+ const name = rustSynIdentName(node.sig?.ident ?? node.ident ?? node.name);
10007
+ if (name) return declarationRecord(input, nativeNodeId, name, 'function', 'definition');
10008
+ }
10009
+ if (kind === 'ImplItemFn' || kind === 'TraitItemFn') {
10010
+ const name = rustSynIdentName(node.sig?.ident ?? node.ident ?? node.name);
10011
+ if (name) return declarationRecord(input, nativeNodeId, name, 'method', 'definition');
10012
+ }
10013
+ if (kind === 'ItemStruct' || kind === 'ItemUnion') {
10014
+ const name = rustSynIdentName(node.ident ?? node.name);
10015
+ if (name) return declarationRecord(input, nativeNodeId, name, 'class', 'definition');
10016
+ }
10017
+ if (kind === 'ItemEnum' || kind === 'ItemTrait' || kind === 'ItemType') {
10018
+ const name = rustSynIdentName(node.ident ?? node.name);
10019
+ if (name) return declarationRecord(input, nativeNodeId, name, 'type', 'definition');
10020
+ }
10021
+ if (kind === 'ItemMod') {
10022
+ const name = rustSynIdentName(node.ident ?? node.name);
10023
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'definition');
10024
+ }
10025
+ if (kind === 'ItemConst' || kind === 'ItemStatic') {
10026
+ const name = rustSynIdentName(node.ident ?? node.name);
10027
+ if (name) return declarationRecord(input, nativeNodeId, name, 'variable', 'definition');
10028
+ }
10029
+ if (kind === 'ItemImpl') {
10030
+ const name = rustSynImplName(node);
10031
+ if (name) return declarationRecord(input, nativeNodeId, name, 'type', 'definition');
10032
+ }
10033
+ if (rustSynMacroKind(kind)) {
10034
+ const name = rustSynIdentName(node.ident ?? node.mac?.path ?? node.path);
10035
+ if (name) return declarationRecord(input, nativeNodeId, name, 'macro', 'definition');
10036
+ }
10037
+ return undefined;
10038
+ }
10039
+
9156
10040
  function typeScriptDeclaration(node, kind, nativeNodeId, input) {
9157
10041
  if (kind === 'ImportDeclaration' || kind === 'ImportEqualsDeclaration') {
9158
10042
  const name = stringFromTsExpression(node.moduleSpecifier) ?? stringFromTsExpression(node.externalModuleReference?.expression);
@@ -9196,6 +10080,155 @@ function namedDeclaration(input, nativeNodeId, nameNode, symbolKind) {
9196
10080
  return name ? declarationRecord(input, nativeNodeId, name, symbolKind, 'definition') : undefined;
9197
10081
  }
9198
10082
 
10083
+ function pythonAstChildEntries(node) {
10084
+ const fieldNames = Array.isArray(node._fields)
10085
+ ? node._fields
10086
+ : Object.keys(node).filter((key) => !ignoredPythonAstField(key));
10087
+ return fieldNames
10088
+ .map((field) => [field, node[field]])
10089
+ .filter(([, value]) => Array.isArray(value)
10090
+ ? value.some(isPythonAstNode)
10091
+ : isPythonAstNode(value));
10092
+ }
10093
+
10094
+ function rustSynChildEntries(node) {
10095
+ const payload = rustSynPayload(node);
10096
+ const fieldNames = Object.keys(payload).filter((key) => !ignoredRustSynField(key));
10097
+ return fieldNames
10098
+ .map((field) => [field, payload[field]])
10099
+ .filter(([, value]) => Array.isArray(value)
10100
+ ? value.some(isRustSynAstNode)
10101
+ : isRustSynAstNode(value));
10102
+ }
10103
+
10104
+ function pythonAstNodeValue(node) {
10105
+ return node.name ?? node.id ?? node.arg ?? node.module ?? pythonAstLiteralValue(node);
10106
+ }
10107
+
10108
+ function rustSynNodeValue(node) {
10109
+ return rustSynIdentName(node.ident ?? node.name ?? node.sig?.ident)
10110
+ ?? rustSynPathName(node.path ?? node.trait_ ?? node.self_ty ?? node.selfType ?? node.ty)
10111
+ ?? rustSynLiteralValue(node);
10112
+ }
10113
+
10114
+ function pythonAliasName(alias) {
10115
+ if (!alias) return undefined;
10116
+ if (typeof alias === 'string') return alias;
10117
+ return alias.name ?? alias.asname ?? alias.id;
10118
+ }
10119
+
10120
+ function pythonAssignmentName(node) {
10121
+ if (node.target) return pythonTargetName(node.target);
10122
+ for (const target of node.targets ?? []) {
10123
+ const name = pythonTargetName(target);
10124
+ if (name) return name;
10125
+ }
10126
+ return undefined;
10127
+ }
10128
+
10129
+ function pythonTargetName(target) {
10130
+ if (!target) return undefined;
10131
+ if (typeof target === 'string') return target;
10132
+ if (typeof target.id === 'string') return target.id;
10133
+ if (typeof target.name === 'string') return target.name;
10134
+ if (typeof target.arg === 'string') return target.arg;
10135
+ if (target.attr && target.value) {
10136
+ const base = pythonTargetName(target.value);
10137
+ return base ? `${base}.${target.attr}` : target.attr;
10138
+ }
10139
+ return undefined;
10140
+ }
10141
+
10142
+ function rustSynIdentName(value) {
10143
+ if (!value) return undefined;
10144
+ if (typeof value === 'string') return value;
10145
+ if (typeof value.ident === 'string') return value.ident;
10146
+ if (typeof value.name === 'string') return value.name;
10147
+ if (typeof value.text === 'string') return value.text;
10148
+ if (typeof value.sym === 'string') return value.sym;
10149
+ if (typeof value.value === 'string') return value.value;
10150
+ if (typeof value.path === 'string') return value.path;
10151
+ return rustSynPathName(value.path ?? value);
10152
+ }
10153
+
10154
+ function rustSynPathName(value) {
10155
+ if (!value) return undefined;
10156
+ if (typeof value === 'string') return value;
10157
+ if (typeof value.ident === 'string') return value.ident;
10158
+ if (typeof value.name === 'string') return value.name;
10159
+ if (typeof value.path === 'string') return value.path;
10160
+ if (value.path && typeof value.path === 'object') return rustSynPathName(value.path);
10161
+ if (Array.isArray(value.segments)) {
10162
+ const names = value.segments
10163
+ .map((segment) => rustSynIdentName(segment.ident ?? segment))
10164
+ .filter(Boolean);
10165
+ return names.length ? names.join('::') : undefined;
10166
+ }
10167
+ if (Array.isArray(value.qself)) return value.qself.map(rustSynPathName).filter(Boolean).join('::');
10168
+ if (value.leading_colon && value.segments) {
10169
+ const name = rustSynPathName({ segments: value.segments });
10170
+ return name ? `::${name}` : undefined;
10171
+ }
10172
+ return undefined;
10173
+ }
10174
+
10175
+ function rustSynUseName(node) {
10176
+ if (!node) return undefined;
10177
+ if (node.prefix && node.tree) {
10178
+ const prefix = rustSynPathName(node.prefix);
10179
+ const child = rustSynUseName(node.tree);
10180
+ return [prefix, child].filter(Boolean).join('::') || undefined;
10181
+ }
10182
+ if (node.ident && node.tree) {
10183
+ const prefix = rustSynIdentName(node.ident);
10184
+ const child = rustSynUseName(node.tree);
10185
+ return [prefix, child].filter(Boolean).join('::') || undefined;
10186
+ }
10187
+ if (node.path && node.tree) {
10188
+ const prefix = rustSynPathName(node.path);
10189
+ const child = rustSynUseName(node.tree);
10190
+ return [prefix, child].filter(Boolean).join('::') || undefined;
10191
+ }
10192
+ if (Array.isArray(node.trees)) return node.trees.map(rustSynUseName).find(Boolean);
10193
+ if (node.tree) return rustSynUseName(node.tree);
10194
+ if (node.path) return rustSynPathName(node.path);
10195
+ if (node.name) return rustSynIdentName(node.name);
10196
+ if (node.ident) return rustSynIdentName(node.ident);
10197
+ if (node.rename) return rustSynIdentName(node.rename);
10198
+ return undefined;
10199
+ }
10200
+
10201
+ function rustSynImplName(node) {
10202
+ const selfType = rustSynPathName(node.self_ty ?? node.selfType ?? node.ty);
10203
+ const traitPath = Array.isArray(node.trait_)
10204
+ ? rustSynPathName(node.trait_[1] ?? node.trait_[0])
10205
+ : rustSynPathName(node.trait_);
10206
+ if (selfType && traitPath) return `${selfType}.${traitPath}.impl`;
10207
+ if (selfType) return `${selfType}.impl`;
10208
+ if (traitPath) return `${traitPath}.impl`;
10209
+ return undefined;
10210
+ }
10211
+
10212
+ function rustSynVisibility(value) {
10213
+ if (!value) return undefined;
10214
+ if (typeof value === 'string') return value;
10215
+ if (typeof value.kind === 'string') return value.kind;
10216
+ if (typeof value.type === 'string') return value.type;
10217
+ if (value.pub || value.Public) return 'pub';
10218
+ if (value.restricted || value.Restricted) return 'restricted';
10219
+ return undefined;
10220
+ }
10221
+
10222
+ function rustSynLiteralValue(node) {
10223
+ const value = node.value ?? node.lit?.value ?? node.lit;
10224
+ if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;
10225
+ return undefined;
10226
+ }
10227
+
10228
+ function rustSynMacroKind(kind) {
10229
+ return /Macro|MacroRules|MacroCall/.test(String(kind));
10230
+ }
10231
+
9199
10232
  function declarationRecord(input, nativeNodeId, name, symbolKind, role = 'definition') {
9200
10233
  return {
9201
10234
  name: String(name),