@shapeshift-labs/frontier-lang-compiler 0.2.26 → 0.2.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -332,7 +332,9 @@ Use injected parser adapters when a real language parser is available but should
332
332
  ```js
333
333
  import {
334
334
  createBabelNativeImporterAdapter,
335
+ createClangAstNativeImporterAdapter,
335
336
  createPythonAstNativeImporterAdapter,
337
+ createRustSynNativeImporterAdapter,
336
338
  importNativeProject,
337
339
  runNativeImporterAdapter
338
340
  } from '@shapeshift-labs/frontier-lang-compiler';
@@ -343,6 +345,15 @@ const babelAdapter = createBabelNativeImporterAdapter({
343
345
  const pythonAstAdapter = createPythonAstNativeImporterAdapter({
344
346
  parserModule: hostPythonAstParser
345
347
  });
348
+ const rustSynAdapter = createRustSynNativeImporterAdapter({
349
+ parserModule: hostRustSynParser,
350
+ rustEdition: '2021'
351
+ });
352
+ const clangAstAdapter = createClangAstNativeImporterAdapter({
353
+ parserModule: hostClangJsonParser,
354
+ cStandard: 'c11',
355
+ compileFlags: ['-std=c11']
356
+ });
346
357
 
347
358
  const imported = await runNativeImporterAdapter(babelAdapter, {
348
359
  sourcePath: 'src/todo.ts',
@@ -351,10 +362,12 @@ const imported = await runNativeImporterAdapter(babelAdapter, {
351
362
 
352
363
  const project = await importNativeProject({
353
364
  projectRoot: 'src',
354
- adapters: [babelAdapter, pythonAstAdapter],
365
+ adapters: [babelAdapter, pythonAstAdapter, rustSynAdapter, clangAstAdapter],
355
366
  sources: [
356
367
  { language: 'typescript', adapter: babelAdapter.id, sourcePath: 'src/todo.ts', sourceText },
357
- { language: 'python', adapter: pythonAstAdapter.id, sourcePath: 'tools/todo.py', sourceText: pythonSource }
368
+ { language: 'python', adapter: pythonAstAdapter.id, sourcePath: 'tools/todo.py', sourceText: pythonSource },
369
+ { language: 'rust', adapter: rustSynAdapter.id, sourcePath: 'src/todo.rs', sourceText: rustSource },
370
+ { language: 'c', adapter: clangAstAdapter.id, sourcePath: 'native/todo.c', sourceText: cSource }
358
371
  ]
359
372
  });
360
373
 
@@ -374,6 +387,8 @@ The built-in adapter factories are dependency-light wrappers for caller-owned pa
374
387
  - `createBabelNativeImporterAdapter`
375
388
  - `createTypeScriptCompilerNativeImporterAdapter`
376
389
  - `createPythonAstNativeImporterAdapter`
390
+ - `createRustSynNativeImporterAdapter`
391
+ - `createClangAstNativeImporterAdapter`
377
392
  - `createTreeSitterNativeImporterAdapter`
378
393
 
379
394
  Adapter summaries include a structured `coverage` record so merge queues can distinguish exact parser AST imports from declaration scans. The record declares exactness, parser token/trivia support, diagnostics support, source-range and generated-range support, and semantic coverage. Built-in wrappers normalize native AST/CST nodes and declaration-level semantic indexes; they do not claim resolved references, types, control flow, generated ranges, or token/trivia fidelity unless the host adapter supplies that evidence.
package/bench/smoke.mjs CHANGED
@@ -2,12 +2,14 @@ import { performance } from 'node:perf_hooks';
2
2
  import {
3
3
  compileNativeSource,
4
4
  compileFrontierSource,
5
+ createClangAstNativeImporterAdapter,
5
6
  createEstreeNativeImporterAdapter,
6
7
  createNativeImportCoverageMatrix,
7
8
  createNativeParserAstFormatMatrix,
8
9
  createProjectionTargetLossMatrix,
9
10
  createNativeSourcePreservation,
10
11
  createPythonAstNativeImporterAdapter,
12
+ createRustSynNativeImporterAdapter,
11
13
  createSemanticImportSidecar,
12
14
  diffNativeSources,
13
15
  importExternalSemanticIndex,
@@ -78,7 +80,7 @@ const matrixDurationMs = performance.now() - matrixStart;
78
80
  const parserFormatMatrixStart = performance.now();
79
81
  const parserFormatMatrix = createNativeParserAstFormatMatrix({
80
82
  imports: nativeImportResults,
81
- adapters: [estreeAdapter, createPythonAstNativeImporterAdapter()]
83
+ adapters: [estreeAdapter, createPythonAstNativeImporterAdapter(), createRustSynNativeImporterAdapter(), createClangAstNativeImporterAdapter()]
82
84
  });
83
85
  const parserFormatMatrixDurationMs = performance.now() - parserFormatMatrixStart;
84
86
 
package/dist/index.d.ts CHANGED
@@ -1388,6 +1388,53 @@ export interface PythonAstNativeImporterAdapterOptions {
1388
1388
  readonly maxNodes?: number;
1389
1389
  }
1390
1390
 
1391
+ export interface RustSynNativeImporterAdapterOptions {
1392
+ readonly id?: string;
1393
+ readonly language?: FrontierSourceLanguage;
1394
+ readonly parser?: string;
1395
+ readonly version?: string;
1396
+ readonly capabilities?: readonly string[];
1397
+ readonly coverage?: NativeImporterAdapterCoverageInput;
1398
+ readonly supportedExtensions?: readonly string[];
1399
+ readonly diagnostics?: readonly NativeImporterAdapterDiagnostic[];
1400
+ readonly ast?: unknown;
1401
+ readonly file?: unknown;
1402
+ readonly sourceFile?: unknown;
1403
+ readonly parse?: (sourceText: string, options: Record<string, unknown>) => unknown;
1404
+ readonly parserModule?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
1405
+ readonly rustSyn?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
1406
+ readonly syn?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
1407
+ readonly parserOptions?: Record<string, unknown>;
1408
+ readonly rustEdition?: '2015' | '2018' | '2021' | '2024' | string;
1409
+ readonly includeAttributes?: boolean;
1410
+ readonly maxNodes?: number;
1411
+ }
1412
+
1413
+ export interface ClangAstNativeImporterAdapterOptions {
1414
+ readonly id?: string;
1415
+ readonly language?: FrontierSourceLanguage;
1416
+ readonly parser?: string;
1417
+ readonly version?: string;
1418
+ readonly capabilities?: readonly string[];
1419
+ readonly coverage?: NativeImporterAdapterCoverageInput;
1420
+ readonly supportedExtensions?: readonly string[];
1421
+ readonly diagnostics?: readonly NativeImporterAdapterDiagnostic[];
1422
+ readonly ast?: unknown;
1423
+ readonly translationUnit?: unknown;
1424
+ readonly tu?: unknown;
1425
+ readonly parse?: (sourceText: string, options: Record<string, unknown>) => unknown;
1426
+ readonly parserModule?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
1427
+ readonly clang?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
1428
+ readonly libclang?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
1429
+ readonly parserOptions?: Record<string, unknown>;
1430
+ readonly cStandard?: 'c89' | 'c99' | 'c11' | 'c17' | 'c23' | 'gnu11' | 'gnu17' | 'gnu23' | string;
1431
+ readonly compileFlags?: readonly string[];
1432
+ readonly includeSystemHeaders?: boolean;
1433
+ readonly preprocessorRecords?: readonly unknown[] | { readonly records?: readonly unknown[] };
1434
+ readonly includeGraph?: unknown;
1435
+ readonly maxNodes?: number;
1436
+ }
1437
+
1391
1438
  export interface TreeSitterNativeImporterAdapterOptions {
1392
1439
  readonly id?: string;
1393
1440
  readonly language?: FrontierSourceLanguage;
@@ -1758,6 +1805,8 @@ export declare function createEstreeNativeImporterAdapter(options?: JavaScriptNa
1758
1805
  export declare function createBabelNativeImporterAdapter(options?: JavaScriptNativeImporterAdapterOptions): NativeImporterAdapter;
1759
1806
  export declare function createTypeScriptCompilerNativeImporterAdapter(options?: TypeScriptCompilerNativeImporterAdapterOptions): NativeImporterAdapter;
1760
1807
  export declare function createPythonAstNativeImporterAdapter(options?: PythonAstNativeImporterAdapterOptions): NativeImporterAdapter;
1808
+ export declare function createRustSynNativeImporterAdapter(options?: RustSynNativeImporterAdapterOptions): NativeImporterAdapter;
1809
+ export declare function createClangAstNativeImporterAdapter(options?: ClangAstNativeImporterAdapterOptions): NativeImporterAdapter;
1761
1810
  export declare function createTreeSitterNativeImporterAdapter(options?: TreeSitterNativeImporterAdapterOptions): NativeImporterAdapter;
1762
1811
  export declare function runNativeImporterAdapter(adapter: NativeImporterAdapter, input: RunNativeImporterAdapterOptions): Promise<NativeImporterAdapterImportResult>;
1763
1812
  export declare function runNativeTargetProjectionAdapter(adapter: NativeTargetProjectionAdapter, input: NativeTargetProjectionAdapterInput): NativeTargetProjectionResult;
package/dist/index.js CHANGED
@@ -378,6 +378,43 @@ export const NativeParserAstFormatProfiles = Object.freeze([
378
378
  supportsErrorRecovery: false,
379
379
  notes: ['Python stdlib AST exposes versioned abstract grammar and source locations, but not formatting trivia.']
380
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('clang-ast-json', {
407
+ aliases: ['clang', 'libclang', 'clang-json', 'clang-ast-dump-json'],
408
+ kind: 'compiler-ast',
409
+ languages: ['c', 'cpp'],
410
+ parserAdapters: ['clang', 'libclang', 'clang-ast-json'],
411
+ exactness: 'exact-parser-ast',
412
+ sourceRangeModel: 'clang-loc-range',
413
+ preservesTokens: false,
414
+ preservesTrivia: false,
415
+ supportsErrorRecovery: false,
416
+ notes: ['Clang JSON AST dumps expose compiler AST declarations and source ranges after preprocessing; compile commands, macros, inactive branches, type checking, and lossless formatting remain host-owned evidence.']
417
+ }),
381
418
  nativeParserAstFormatProfile('tree-sitter', {
382
419
  kind: 'concrete-syntax-tree',
383
420
  languages: ['mixed'],
@@ -2631,6 +2668,119 @@ export function createPythonAstNativeImporterAdapter(options = {}) {
2631
2668
  };
2632
2669
  }
2633
2670
 
2671
+ export function createRustSynNativeImporterAdapter(options = {}) {
2672
+ return {
2673
+ id: options.id ?? 'frontier.rust-syn-native-importer',
2674
+ language: options.language ?? 'rust',
2675
+ parser: options.parser ?? 'syn',
2676
+ version: options.version,
2677
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
2678
+ coverage: nativeImporterAdapterCoverage({
2679
+ exactness: 'exact-parser-ast',
2680
+ exactAst: true,
2681
+ tokens: false,
2682
+ trivia: false,
2683
+ diagnostics: true,
2684
+ sourceRanges: false,
2685
+ generatedRanges: false,
2686
+ semanticCoverage: declarationSemanticCoverage(),
2687
+ notes: [
2688
+ 'Normalizes caller-owned syn-shaped Rust ASTs into native AST nodes and declaration-level semantic index records.',
2689
+ 'syn does not expand macros, resolve names, type-check, or preserve full concrete syntax/trivia; attach rust-analyzer/rustc evidence for those claims.'
2690
+ ]
2691
+ }, options.coverage),
2692
+ supportedExtensions: options.supportedExtensions ?? ['.rs'],
2693
+ diagnostics: options.diagnostics,
2694
+ parse(input) {
2695
+ const parsed = input.options?.ast
2696
+ ?? input.options?.nativeAst
2697
+ ?? input.options?.file
2698
+ ?? input.options?.sourceFile
2699
+ ?? options.ast
2700
+ ?? options.file
2701
+ ?? options.sourceFile
2702
+ ?? parseRustSynSource(input, options);
2703
+ const root = rustSynAstRoot(parsed);
2704
+ if (!root) {
2705
+ return missingInjectedParserResult(input, {
2706
+ parser: options.parser ?? 'syn',
2707
+ adapterId: options.id ?? 'frontier.rust-syn-native-importer',
2708
+ message: 'createRustSynNativeImporterAdapter requires an injected syn-shaped AST object, parserModule.parse function, parse function, or adapterOptions.ast.'
2709
+ });
2710
+ }
2711
+ const parseDiagnostics = normalizeParserErrors(parsed?.errors ?? parsed?.diagnostics, input, {
2712
+ parser: options.parser ?? 'syn'
2713
+ });
2714
+ return createNativeImportFromRustSyn(root, input, {
2715
+ parser: options.parser ?? 'syn',
2716
+ astFormat: 'rust-syn',
2717
+ maxNodes: options.maxNodes,
2718
+ diagnostics: parseDiagnostics,
2719
+ rustEdition: options.rustEdition ?? input.options?.rustEdition ?? parsed?.rustEdition,
2720
+ includeAttributes: options.includeAttributes ?? input.options?.includeAttributes
2721
+ });
2722
+ }
2723
+ };
2724
+ }
2725
+
2726
+ export function createClangAstNativeImporterAdapter(options = {}) {
2727
+ return {
2728
+ id: options.id ?? 'frontier.clang-ast-native-importer',
2729
+ language: options.language ?? 'c',
2730
+ parser: options.parser ?? 'clang',
2731
+ version: options.version,
2732
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
2733
+ coverage: nativeImporterAdapterCoverage({
2734
+ exactness: 'exact-parser-ast',
2735
+ exactAst: true,
2736
+ tokens: false,
2737
+ trivia: false,
2738
+ diagnostics: true,
2739
+ sourceRanges: true,
2740
+ generatedRanges: false,
2741
+ semanticCoverage: declarationSemanticCoverage(),
2742
+ notes: [
2743
+ 'Normalizes caller-owned Clang -ast-dump=json shaped ASTs into native AST nodes and declaration-level semantic index records.',
2744
+ 'Clang JSON ASTs reflect a preprocessed compiler view; compile commands, macros, inactive preprocessor branches, comments/trivia, and full type/reference analysis require host evidence.'
2745
+ ]
2746
+ }, options.coverage),
2747
+ supportedExtensions: options.supportedExtensions ?? ['.c', '.h', '.cc', '.cpp', '.cxx', '.hpp', '.hh'],
2748
+ diagnostics: options.diagnostics,
2749
+ parse(input) {
2750
+ const parsed = input.options?.ast
2751
+ ?? input.options?.nativeAst
2752
+ ?? input.options?.translationUnit
2753
+ ?? input.options?.tu
2754
+ ?? options.ast
2755
+ ?? options.translationUnit
2756
+ ?? options.tu
2757
+ ?? parseClangAstSource(input, options);
2758
+ const root = clangAstRoot(parsed);
2759
+ if (!root) {
2760
+ return missingInjectedParserResult(input, {
2761
+ parser: options.parser ?? 'clang',
2762
+ adapterId: options.id ?? 'frontier.clang-ast-native-importer',
2763
+ message: 'createClangAstNativeImporterAdapter requires an injected Clang JSON AST object, parserModule.parse function, parse function, or adapterOptions.ast.'
2764
+ });
2765
+ }
2766
+ const parseDiagnostics = normalizeParserErrors(parsed?.errors ?? parsed?.diagnostics, input, {
2767
+ parser: options.parser ?? 'clang'
2768
+ });
2769
+ return createNativeImportFromClangAst(root, input, {
2770
+ parser: options.parser ?? 'clang',
2771
+ astFormat: 'clang-ast-json',
2772
+ maxNodes: options.maxNodes,
2773
+ diagnostics: parseDiagnostics,
2774
+ cStandard: options.cStandard ?? input.options?.cStandard ?? parsed?.cStandard,
2775
+ compileFlags: options.compileFlags ?? input.options?.compileFlags ?? parsed?.compileFlags,
2776
+ includeSystemHeaders: options.includeSystemHeaders ?? input.options?.includeSystemHeaders,
2777
+ preprocessorRecords: input.options?.preprocessorRecords ?? options.preprocessorRecords ?? parsed?.preprocessorRecords,
2778
+ includeGraph: input.options?.includeGraph ?? options.includeGraph ?? parsed?.includeGraph
2779
+ });
2780
+ }
2781
+ };
2782
+ }
2783
+
2634
2784
  export function createTreeSitterNativeImporterAdapter(options = {}) {
2635
2785
  return {
2636
2786
  id: options.id ?? `frontier.tree-sitter-${idFragment(options.language ?? 'source')}-native-importer`,
@@ -7069,6 +7219,9 @@ function parserAstFormatIdForParser(parser) {
7069
7219
  const text = normalizeParserAstFormatId(parser);
7070
7220
  if (text.includes('typescript')) return 'typescript-compiler-api';
7071
7221
  if (text.includes('python') && text.includes('ast')) return 'python-ast';
7222
+ if (text === 'syn' || text.includes('rust-syn')) return 'rust-syn';
7223
+ if (text.includes('rust-analyzer') || text.includes('rowan')) return 'rust-analyzer-rowan';
7224
+ if (text.includes('clang') || text.includes('libclang')) return 'clang-ast-json';
7072
7225
  if (text.includes('tree-sitter') || text.includes('treesitter')) return 'tree-sitter';
7073
7226
  if (text.includes('babel')) return 'babel';
7074
7227
  if (text.includes('estree')) return 'estree';
@@ -8103,6 +8256,37 @@ function parsePythonAstSource(input, options) {
8103
8256
  return parse(input.sourceText, parserOptions);
8104
8257
  }
8105
8258
 
8259
+ function parseRustSynSource(input, options) {
8260
+ const parse = options.parse ?? options.parserModule?.parse ?? options.rustSyn?.parse ?? options.syn?.parse;
8261
+ if (typeof parse !== 'function') return undefined;
8262
+ const parserOptions = {
8263
+ sourcePath: input.sourcePath,
8264
+ filename: input.sourcePath,
8265
+ edition: options.rustEdition ?? input.options?.rustEdition ?? '2021',
8266
+ includeAttributes: options.includeAttributes ?? input.options?.includeAttributes,
8267
+ ...(options.parserOptions ?? {}),
8268
+ ...(input.options?.parserOptions ?? {})
8269
+ };
8270
+ return parse(input.sourceText, parserOptions);
8271
+ }
8272
+
8273
+ function parseClangAstSource(input, options) {
8274
+ const parse = options.parse ?? options.parserModule?.parse ?? options.clang?.parse ?? options.libclang?.parse;
8275
+ if (typeof parse !== 'function') return undefined;
8276
+ const parserOptions = {
8277
+ sourcePath: input.sourcePath,
8278
+ filename: input.sourcePath,
8279
+ language: options.language ?? input.language,
8280
+ standard: options.cStandard ?? input.options?.cStandard,
8281
+ compileFlags: options.compileFlags ?? input.options?.compileFlags,
8282
+ includeSystemHeaders: options.includeSystemHeaders ?? input.options?.includeSystemHeaders,
8283
+ astDumpFormat: 'json',
8284
+ ...(options.parserOptions ?? {}),
8285
+ ...(input.options?.parserOptions ?? {})
8286
+ };
8287
+ return parse(input.sourceText, parserOptions);
8288
+ }
8289
+
8106
8290
  function createNativeImportFromSyntaxAst(ast, input, options) {
8107
8291
  const root = normalizeSyntaxAstRoot(ast, options.astFormat);
8108
8292
  if (!root) {
@@ -8193,6 +8377,72 @@ function createNativeImportFromPythonAst(root, input, options) {
8193
8377
  };
8194
8378
  }
8195
8379
 
8380
+ function createNativeImportFromRustSyn(root, input, options) {
8381
+ const context = createAstNormalizationContext(input, options);
8382
+ visitRustSynNode(root, context, 'root');
8383
+ if (context.truncated) {
8384
+ context.losses.push(truncatedAstLoss(input, context, options));
8385
+ }
8386
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
8387
+ return {
8388
+ rootId: context.rootId,
8389
+ nodes: context.nodes,
8390
+ semanticIndex: semantic.semanticIndex,
8391
+ mappings: semantic.mappings,
8392
+ losses: mergeNativeLosses(context.losses, options.diagnostics?.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, {
8393
+ id: input.adapterId,
8394
+ version: input.adapterVersion
8395
+ }, input)) ?? []),
8396
+ evidence: semantic.evidence,
8397
+ diagnostics: options.diagnostics,
8398
+ metadata: {
8399
+ astFormat: options.astFormat,
8400
+ parser: options.parser,
8401
+ rustEdition: options.rustEdition,
8402
+ includeAttributes: Boolean(options.includeAttributes),
8403
+ normalizedNodeCount: Object.keys(context.nodes).length,
8404
+ declarationCount: context.declarations.length,
8405
+ truncated: context.truncated
8406
+ }
8407
+ };
8408
+ }
8409
+
8410
+ function createNativeImportFromClangAst(root, input, options) {
8411
+ const context = createAstNormalizationContext(input, options);
8412
+ visitClangAstNode(root, context, 'root');
8413
+ for (const [index, record] of clangPreprocessorRecords(options.preprocessorRecords).entries()) {
8414
+ visitClangAstNode(record, context, `preprocessorRecords[${index}]`);
8415
+ }
8416
+ if (context.truncated) {
8417
+ context.losses.push(truncatedAstLoss(input, context, options));
8418
+ }
8419
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
8420
+ return {
8421
+ rootId: context.rootId,
8422
+ nodes: context.nodes,
8423
+ semanticIndex: semantic.semanticIndex,
8424
+ mappings: semantic.mappings,
8425
+ losses: mergeNativeLosses(context.losses, options.diagnostics?.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, {
8426
+ id: input.adapterId,
8427
+ version: input.adapterVersion
8428
+ }, input)) ?? []),
8429
+ evidence: semantic.evidence,
8430
+ diagnostics: options.diagnostics,
8431
+ metadata: {
8432
+ astFormat: options.astFormat,
8433
+ parser: options.parser,
8434
+ cStandard: options.cStandard,
8435
+ compileFlags: Array.isArray(options.compileFlags) ? options.compileFlags.slice() : options.compileFlags,
8436
+ includeSystemHeaders: Boolean(options.includeSystemHeaders),
8437
+ preprocessorRecordCount: clangPreprocessorRecords(options.preprocessorRecords).length,
8438
+ includeGraph: serializableIncludeGraphSummary(options.includeGraph),
8439
+ normalizedNodeCount: Object.keys(context.nodes).length,
8440
+ declarationCount: context.declarations.length,
8441
+ truncated: context.truncated
8442
+ }
8443
+ };
8444
+ }
8445
+
8196
8446
  function createNativeImportFromTreeSitter(root, input, options) {
8197
8447
  const context = createAstNormalizationContext(input, options);
8198
8448
  visitTreeSitterNode(root, context, 'root');
@@ -8376,6 +8626,128 @@ function visitPythonAstNode(node, context, propertyPath) {
8376
8626
  return id;
8377
8627
  }
8378
8628
 
8629
+ function visitRustSynNode(node, context, propertyPath) {
8630
+ if (!isRustSynAstNode(node) || context.truncated) return undefined;
8631
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
8632
+ if (context.counter >= context.maxNodes) {
8633
+ context.truncated = true;
8634
+ return undefined;
8635
+ }
8636
+ const kind = rustSynKind(node);
8637
+ const payload = rustSynPayload(node);
8638
+ const span = spanFromRustSynNode(payload, context.input);
8639
+ const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
8640
+ context.objectIds.set(node, id);
8641
+ if (!context.rootId) context.rootId = id;
8642
+ const children = [];
8643
+ for (const [field, value] of rustSynChildEntries(node)) {
8644
+ if (Array.isArray(value)) {
8645
+ value.forEach((entry, index) => {
8646
+ const childId = visitRustSynNode(entry, context, `${propertyPath}.${field}[${index}]`);
8647
+ if (childId) children.push(childId);
8648
+ });
8649
+ } else {
8650
+ const childId = visitRustSynNode(value, context, `${propertyPath}.${field}`);
8651
+ if (childId) children.push(childId);
8652
+ }
8653
+ }
8654
+ const declaration = rustSynDeclaration(payload, kind, id, context.input);
8655
+ const nativeNode = {
8656
+ id,
8657
+ kind,
8658
+ languageKind: `${context.input.language}.${kind}`,
8659
+ span,
8660
+ value: declaration?.name ?? rustSynNodeValue(payload),
8661
+ fields: primitiveRustSynFields(payload, kind),
8662
+ children,
8663
+ metadata: {
8664
+ astFormat: context.options.astFormat,
8665
+ propertyPath,
8666
+ spanKind: rustSynSpanKind(payload)
8667
+ }
8668
+ };
8669
+ context.nodes[id] = nativeNode;
8670
+ if (declaration) context.declarations.push({ ...declaration, nativeNode });
8671
+ if (rustSynMacroKind(kind)) {
8672
+ context.losses.push({
8673
+ id: `loss_${idFragment(id)}_rust_macro_expansion`,
8674
+ severity: 'warning',
8675
+ phase: 'parse',
8676
+ sourceFormat: context.input.language,
8677
+ kind: 'macroExpansion',
8678
+ message: 'Rust macro syntax was parsed but macro expansion and generated items require host compiler evidence.',
8679
+ span,
8680
+ nodeId: id,
8681
+ metadata: {
8682
+ parser: context.options.parser,
8683
+ astFormat: context.options.astFormat
8684
+ }
8685
+ });
8686
+ }
8687
+ return id;
8688
+ }
8689
+
8690
+ function visitClangAstNode(node, context, propertyPath) {
8691
+ if (!isClangAstNode(node) || context.truncated) return undefined;
8692
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
8693
+ if (context.counter >= context.maxNodes) {
8694
+ context.truncated = true;
8695
+ return undefined;
8696
+ }
8697
+ const kind = clangAstKind(node);
8698
+ const span = spanFromClangAstNode(node, context.input);
8699
+ const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
8700
+ context.objectIds.set(node, id);
8701
+ if (!context.rootId) context.rootId = id;
8702
+ const children = [];
8703
+ for (const [field, value] of clangAstChildEntries(node)) {
8704
+ if (Array.isArray(value)) {
8705
+ value.forEach((entry, index) => {
8706
+ const childId = visitClangAstNode(entry, context, `${propertyPath}.${field}[${index}]`);
8707
+ if (childId) children.push(childId);
8708
+ });
8709
+ } else {
8710
+ const childId = visitClangAstNode(value, context, `${propertyPath}.${field}`);
8711
+ if (childId) children.push(childId);
8712
+ }
8713
+ }
8714
+ const declaration = clangAstDeclaration(node, kind, id, context.input);
8715
+ const nativeNode = {
8716
+ id,
8717
+ kind,
8718
+ languageKind: `${context.input.language}.${kind}`,
8719
+ span,
8720
+ value: declaration?.name ?? clangAstNodeValue(node),
8721
+ fields: primitiveClangAstFields(node, kind),
8722
+ children,
8723
+ metadata: {
8724
+ astFormat: context.options.astFormat,
8725
+ propertyPath,
8726
+ clangId: typeof node.id === 'string' || typeof node.id === 'number' ? String(node.id) : undefined,
8727
+ locationKind: clangLocationKind(node)
8728
+ }
8729
+ };
8730
+ context.nodes[id] = nativeNode;
8731
+ if (declaration) context.declarations.push({ ...declaration, nativeNode });
8732
+ if (clangPreprocessorKind(kind)) {
8733
+ context.losses.push({
8734
+ id: `loss_${idFragment(id)}_clang_preprocessor`,
8735
+ severity: 'warning',
8736
+ phase: 'parse',
8737
+ sourceFormat: context.input.language,
8738
+ kind: 'preprocessor',
8739
+ message: 'Clang AST preprocessor records were imported, but macro expansion, inactive branches, and compile-command provenance require host evidence.',
8740
+ span,
8741
+ nodeId: id,
8742
+ metadata: {
8743
+ parser: context.options.parser,
8744
+ astFormat: context.options.astFormat
8745
+ }
8746
+ });
8747
+ }
8748
+ return id;
8749
+ }
8750
+
8379
8751
  function visitTreeSitterNode(node, context, propertyPath) {
8380
8752
  if (!node || typeof node !== 'object' || context.truncated) return undefined;
8381
8753
  if (context.objectIds.has(node)) return context.objectIds.get(node);
@@ -9530,6 +9902,87 @@ function pythonAstKind(node) {
9530
9902
  return node?._type ?? node?.type ?? node?.kind ?? node?.nodeType;
9531
9903
  }
9532
9904
 
9905
+ function rustSynAstRoot(value) {
9906
+ if (!value || typeof value !== 'object') return undefined;
9907
+ if (isRustSynAstNode(value)) return value;
9908
+ if (isRustSynAstNode(value.ast)) return value.ast;
9909
+ if (isRustSynAstNode(value.file)) return value.file;
9910
+ if (isRustSynAstNode(value.root)) return value.root;
9911
+ if (isRustSynAstNode(value.module)) return value.module;
9912
+ if (isRustSynAstNode(value.sourceFile)) return value.sourceFile;
9913
+ return undefined;
9914
+ }
9915
+
9916
+ function isRustSynAstNode(value) {
9917
+ return Boolean(value && typeof value === 'object' && typeof rustSynKind(value) === 'string');
9918
+ }
9919
+
9920
+ function rustSynKind(node) {
9921
+ const declared = node?._type ?? node?.type ?? node?.kind ?? node?.nodeType ?? node?.synKind;
9922
+ if (typeof declared === 'string') return normalizeRustSynKind(declared);
9923
+ const wrapper = rustSynWrapperKind(node);
9924
+ if (wrapper) return normalizeRustSynKind(wrapper);
9925
+ if (Array.isArray(node?.items)) return 'File';
9926
+ if (node?.sig && node?.block) return 'ItemFn';
9927
+ if (node?.ident && node?.fields && Array.isArray(node?.variants)) return 'ItemEnum';
9928
+ if (node?.ident && node?.fields) return 'ItemStruct';
9929
+ if (node?.ident && Array.isArray(node?.items)) return 'ItemMod';
9930
+ if (node?.trait_ || node?.self_ty || node?.selfType) return 'ItemImpl';
9931
+ if (node?.path && (node?.tree || node?.trees)) return 'ItemUse';
9932
+ return undefined;
9933
+ }
9934
+
9935
+ function rustSynPayload(node) {
9936
+ if (!node || typeof node !== 'object') return node;
9937
+ const wrapper = rustSynWrapperKind(node);
9938
+ return wrapper ? node[wrapper] : node;
9939
+ }
9940
+
9941
+ function rustSynWrapperKind(node) {
9942
+ if (!node || typeof node !== 'object') return undefined;
9943
+ const keys = Object.keys(node).filter((key) => !ignoredRustSynField(key));
9944
+ if (keys.length !== 1) return undefined;
9945
+ const key = keys[0];
9946
+ const value = node[key];
9947
+ if (!value || typeof value !== 'object') return undefined;
9948
+ if (/^(?:Fn|Struct|Enum|Trait|Impl|Use|Mod|Type|Const|Static|Union|Macro)$/.test(key)) return key;
9949
+ if (/^(?:Item|ImplItem|TraitItem|ForeignItem)/.test(key)) return key;
9950
+ return undefined;
9951
+ }
9952
+
9953
+ function normalizeRustSynKind(kind) {
9954
+ const text = String(kind);
9955
+ const compact = text.replace(/^(?:syn::)?/, '').replace(/^Item::/, 'Item').replace(/^ImplItem::/, 'ImplItem').replace(/^TraitItem::/, 'TraitItem');
9956
+ if (/^fn$/i.test(compact)) return 'ItemFn';
9957
+ if (/^struct$/i.test(compact)) return 'ItemStruct';
9958
+ if (/^enum$/i.test(compact)) return 'ItemEnum';
9959
+ if (/^trait$/i.test(compact)) return 'ItemTrait';
9960
+ if (/^impl$/i.test(compact)) return 'ItemImpl';
9961
+ if (/^use$/i.test(compact)) return 'ItemUse';
9962
+ if (/^mod$/i.test(compact)) return 'ItemMod';
9963
+ if (/^type$/i.test(compact)) return 'ItemType';
9964
+ if (/^const$/i.test(compact)) return 'ItemConst';
9965
+ if (/^static$/i.test(compact)) return 'ItemStatic';
9966
+ if (/^union$/i.test(compact)) return 'ItemUnion';
9967
+ if (/^item_fn$/i.test(compact)) return 'ItemFn';
9968
+ if (/^item_struct$/i.test(compact)) return 'ItemStruct';
9969
+ if (/^item_enum$/i.test(compact)) return 'ItemEnum';
9970
+ if (/^item_trait$/i.test(compact)) return 'ItemTrait';
9971
+ if (/^item_impl$/i.test(compact)) return 'ItemImpl';
9972
+ if (/^item_use$/i.test(compact)) return 'ItemUse';
9973
+ if (/^item_mod$/i.test(compact)) return 'ItemMod';
9974
+ if (/^item_type$/i.test(compact)) return 'ItemType';
9975
+ if (/^item_const$/i.test(compact)) return 'ItemConst';
9976
+ if (/^item_static$/i.test(compact)) return 'ItemStatic';
9977
+ if (/^item_union$/i.test(compact)) return 'ItemUnion';
9978
+ if (/^item_macro$/i.test(compact)) return 'ItemMacro';
9979
+ if (/^macro$/i.test(compact)) return 'Macro';
9980
+ if (/^impl_item_fn$/i.test(compact)) return 'ImplItemFn';
9981
+ if (/^trait_item_fn$/i.test(compact)) return 'TraitItemFn';
9982
+ if (/^foreign_item_fn$/i.test(compact)) return 'ForeignItemFn';
9983
+ return compact;
9984
+ }
9985
+
9533
9986
  function ignoredSyntaxField(key) {
9534
9987
  return key === 'type'
9535
9988
  || key === 'kind'
@@ -9564,6 +10017,18 @@ function ignoredPythonAstField(key) {
9564
10017
  || key === 'parent';
9565
10018
  }
9566
10019
 
10020
+ function ignoredRustSynField(key) {
10021
+ return key === '_type'
10022
+ || key === 'type'
10023
+ || key === 'kind'
10024
+ || key === 'nodeType'
10025
+ || key === 'synKind'
10026
+ || key === 'span'
10027
+ || key === 'tokens'
10028
+ || key === 'tokenStream'
10029
+ || key === 'parent';
10030
+ }
10031
+
9567
10032
  function primitiveSyntaxFields(node) {
9568
10033
  const fields = {};
9569
10034
  for (const key of ['name', 'operator', 'sourceType', 'async', 'generator', 'computed', 'static', 'exportKind', 'importKind', 'optional']) {
@@ -9595,6 +10060,22 @@ function primitivePythonAstFields(node, kind) {
9595
10060
  return fields;
9596
10061
  }
9597
10062
 
10063
+ function primitiveRustSynFields(node, kind) {
10064
+ const fields = { kind };
10065
+ const ident = rustSynIdentName(node.ident ?? node.name ?? node.sig?.ident);
10066
+ if (ident) fields.ident = ident;
10067
+ const path = rustSynPathName(node.path ?? node.trait_ ?? node.self_ty ?? node.selfType ?? node.ty);
10068
+ if (path) fields.path = path;
10069
+ const visibility = rustSynVisibility(node.vis ?? node.visibility);
10070
+ if (visibility) fields.visibility = visibility;
10071
+ for (const key of ['mutability', 'defaultness', 'constness', 'asyncness', 'unsafety', 'abi']) {
10072
+ const value = node[key];
10073
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) fields[key] = value;
10074
+ }
10075
+ if (Array.isArray(node.attrs) && node.attrs.length) fields.attributeCount = node.attrs.length;
10076
+ return fields;
10077
+ }
10078
+
9598
10079
  function literalSyntaxValue(node) {
9599
10080
  if (node.value === null || typeof node.value === 'string' || typeof node.value === 'number' || typeof node.value === 'boolean') return node.value;
9600
10081
  return undefined;
@@ -9635,6 +10116,36 @@ function spanFromPythonAstNode(node, input) {
9635
10116
  };
9636
10117
  }
9637
10118
 
10119
+ function spanFromRustSynNode(node, input) {
10120
+ const span = node.span ?? node.ident?.span ?? node.sig?.ident?.span ?? node.name?.span;
10121
+ if (!span || typeof span !== 'object') return undefined;
10122
+ const start = span.start ?? span.lo ?? span.begin;
10123
+ const end = span.end ?? span.hi;
10124
+ const startLine = span.startLine ?? span.line ?? start?.line;
10125
+ const startColumn = span.startColumn ?? span.column ?? start?.column;
10126
+ const endLine = span.endLine ?? end?.line;
10127
+ const endColumn = span.endColumn ?? end?.column;
10128
+ if (typeof startLine !== 'number') return undefined;
10129
+ return {
10130
+ sourceId: input.sourceHash,
10131
+ path: input.sourcePath,
10132
+ startLine,
10133
+ startColumn: typeof startColumn === 'number' ? rustSynColumnToOneBased(startColumn, span) : undefined,
10134
+ endLine: typeof endLine === 'number' ? endLine : undefined,
10135
+ endColumn: typeof endColumn === 'number' ? rustSynColumnToOneBased(endColumn, span) : undefined
10136
+ };
10137
+ }
10138
+
10139
+ function rustSynColumnToOneBased(column, span) {
10140
+ return span.columnBase === 1 || span.columnsOneBased ? column : column + 1;
10141
+ }
10142
+
10143
+ function rustSynSpanKind(node) {
10144
+ const span = node.span ?? node.ident?.span ?? node.sig?.ident?.span ?? node.name?.span;
10145
+ if (!span || typeof span !== 'object') return undefined;
10146
+ return span.kind ?? span.source ?? 'host-span';
10147
+ }
10148
+
9638
10149
  function syntaxDeclaration(node, nativeNodeId, input) {
9639
10150
  const kind = String(node.type ?? node.kind ?? '');
9640
10151
  if (kind === 'ImportDeclaration') {
@@ -9671,6 +10182,46 @@ function pythonAstDeclaration(node, kind, nativeNodeId, input) {
9671
10182
  return undefined;
9672
10183
  }
9673
10184
 
10185
+ function rustSynDeclaration(node, kind, nativeNodeId, input) {
10186
+ if (kind === 'ItemUse' || kind === 'UseTree' || kind === 'UsePath' || kind === 'UseName') {
10187
+ const name = rustSynUseName(node);
10188
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
10189
+ }
10190
+ if (kind === 'ItemFn' || kind === 'ForeignItemFn') {
10191
+ const name = rustSynIdentName(node.sig?.ident ?? node.ident ?? node.name);
10192
+ if (name) return declarationRecord(input, nativeNodeId, name, 'function', 'definition');
10193
+ }
10194
+ if (kind === 'ImplItemFn' || kind === 'TraitItemFn') {
10195
+ const name = rustSynIdentName(node.sig?.ident ?? node.ident ?? node.name);
10196
+ if (name) return declarationRecord(input, nativeNodeId, name, 'method', 'definition');
10197
+ }
10198
+ if (kind === 'ItemStruct' || kind === 'ItemUnion') {
10199
+ const name = rustSynIdentName(node.ident ?? node.name);
10200
+ if (name) return declarationRecord(input, nativeNodeId, name, 'class', 'definition');
10201
+ }
10202
+ if (kind === 'ItemEnum' || kind === 'ItemTrait' || kind === 'ItemType') {
10203
+ const name = rustSynIdentName(node.ident ?? node.name);
10204
+ if (name) return declarationRecord(input, nativeNodeId, name, 'type', 'definition');
10205
+ }
10206
+ if (kind === 'ItemMod') {
10207
+ const name = rustSynIdentName(node.ident ?? node.name);
10208
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'definition');
10209
+ }
10210
+ if (kind === 'ItemConst' || kind === 'ItemStatic') {
10211
+ const name = rustSynIdentName(node.ident ?? node.name);
10212
+ if (name) return declarationRecord(input, nativeNodeId, name, 'variable', 'definition');
10213
+ }
10214
+ if (kind === 'ItemImpl') {
10215
+ const name = rustSynImplName(node);
10216
+ if (name) return declarationRecord(input, nativeNodeId, name, 'type', 'definition');
10217
+ }
10218
+ if (rustSynMacroKind(kind)) {
10219
+ const name = rustSynIdentName(node.ident ?? node.mac?.path ?? node.path);
10220
+ if (name) return declarationRecord(input, nativeNodeId, name, 'macro', 'definition');
10221
+ }
10222
+ return undefined;
10223
+ }
10224
+
9674
10225
  function typeScriptDeclaration(node, kind, nativeNodeId, input) {
9675
10226
  if (kind === 'ImportDeclaration' || kind === 'ImportEqualsDeclaration') {
9676
10227
  const name = stringFromTsExpression(node.moduleSpecifier) ?? stringFromTsExpression(node.externalModuleReference?.expression);
@@ -9725,10 +10276,26 @@ function pythonAstChildEntries(node) {
9725
10276
  : isPythonAstNode(value));
9726
10277
  }
9727
10278
 
10279
+ function rustSynChildEntries(node) {
10280
+ const payload = rustSynPayload(node);
10281
+ const fieldNames = Object.keys(payload).filter((key) => !ignoredRustSynField(key));
10282
+ return fieldNames
10283
+ .map((field) => [field, payload[field]])
10284
+ .filter(([, value]) => Array.isArray(value)
10285
+ ? value.some(isRustSynAstNode)
10286
+ : isRustSynAstNode(value));
10287
+ }
10288
+
9728
10289
  function pythonAstNodeValue(node) {
9729
10290
  return node.name ?? node.id ?? node.arg ?? node.module ?? pythonAstLiteralValue(node);
9730
10291
  }
9731
10292
 
10293
+ function rustSynNodeValue(node) {
10294
+ return rustSynIdentName(node.ident ?? node.name ?? node.sig?.ident)
10295
+ ?? rustSynPathName(node.path ?? node.trait_ ?? node.self_ty ?? node.selfType ?? node.ty)
10296
+ ?? rustSynLiteralValue(node);
10297
+ }
10298
+
9732
10299
  function pythonAliasName(alias) {
9733
10300
  if (!alias) return undefined;
9734
10301
  if (typeof alias === 'string') return alias;
@@ -9757,6 +10324,339 @@ function pythonTargetName(target) {
9757
10324
  return undefined;
9758
10325
  }
9759
10326
 
10327
+ function rustSynIdentName(value) {
10328
+ if (!value) return undefined;
10329
+ if (typeof value === 'string') return value;
10330
+ if (typeof value.ident === 'string') return value.ident;
10331
+ if (typeof value.name === 'string') return value.name;
10332
+ if (typeof value.text === 'string') return value.text;
10333
+ if (typeof value.sym === 'string') return value.sym;
10334
+ if (typeof value.value === 'string') return value.value;
10335
+ if (typeof value.path === 'string') return value.path;
10336
+ return rustSynPathName(value.path ?? value);
10337
+ }
10338
+
10339
+ function rustSynPathName(value) {
10340
+ if (!value) return undefined;
10341
+ if (typeof value === 'string') return value;
10342
+ if (typeof value.ident === 'string') return value.ident;
10343
+ if (typeof value.name === 'string') return value.name;
10344
+ if (typeof value.path === 'string') return value.path;
10345
+ if (value.path && typeof value.path === 'object') return rustSynPathName(value.path);
10346
+ if (Array.isArray(value.segments)) {
10347
+ const names = value.segments
10348
+ .map((segment) => rustSynIdentName(segment.ident ?? segment))
10349
+ .filter(Boolean);
10350
+ return names.length ? names.join('::') : undefined;
10351
+ }
10352
+ if (Array.isArray(value.qself)) return value.qself.map(rustSynPathName).filter(Boolean).join('::');
10353
+ if (value.leading_colon && value.segments) {
10354
+ const name = rustSynPathName({ segments: value.segments });
10355
+ return name ? `::${name}` : undefined;
10356
+ }
10357
+ return undefined;
10358
+ }
10359
+
10360
+ function rustSynUseName(node) {
10361
+ if (!node) return undefined;
10362
+ if (node.prefix && node.tree) {
10363
+ const prefix = rustSynPathName(node.prefix);
10364
+ const child = rustSynUseName(node.tree);
10365
+ return [prefix, child].filter(Boolean).join('::') || undefined;
10366
+ }
10367
+ if (node.ident && node.tree) {
10368
+ const prefix = rustSynIdentName(node.ident);
10369
+ const child = rustSynUseName(node.tree);
10370
+ return [prefix, child].filter(Boolean).join('::') || undefined;
10371
+ }
10372
+ if (node.path && node.tree) {
10373
+ const prefix = rustSynPathName(node.path);
10374
+ const child = rustSynUseName(node.tree);
10375
+ return [prefix, child].filter(Boolean).join('::') || undefined;
10376
+ }
10377
+ if (Array.isArray(node.trees)) return node.trees.map(rustSynUseName).find(Boolean);
10378
+ if (node.tree) return rustSynUseName(node.tree);
10379
+ if (node.path) return rustSynPathName(node.path);
10380
+ if (node.name) return rustSynIdentName(node.name);
10381
+ if (node.ident) return rustSynIdentName(node.ident);
10382
+ if (node.rename) return rustSynIdentName(node.rename);
10383
+ return undefined;
10384
+ }
10385
+
10386
+ function rustSynImplName(node) {
10387
+ const selfType = rustSynPathName(node.self_ty ?? node.selfType ?? node.ty);
10388
+ const traitPath = Array.isArray(node.trait_)
10389
+ ? rustSynPathName(node.trait_[1] ?? node.trait_[0])
10390
+ : rustSynPathName(node.trait_);
10391
+ if (selfType && traitPath) return `${selfType}.${traitPath}.impl`;
10392
+ if (selfType) return `${selfType}.impl`;
10393
+ if (traitPath) return `${traitPath}.impl`;
10394
+ return undefined;
10395
+ }
10396
+
10397
+ function rustSynVisibility(value) {
10398
+ if (!value) return undefined;
10399
+ if (typeof value === 'string') return value;
10400
+ if (typeof value.kind === 'string') return value.kind;
10401
+ if (typeof value.type === 'string') return value.type;
10402
+ if (value.pub || value.Public) return 'pub';
10403
+ if (value.restricted || value.Restricted) return 'restricted';
10404
+ return undefined;
10405
+ }
10406
+
10407
+ function rustSynLiteralValue(node) {
10408
+ const value = node.value ?? node.lit?.value ?? node.lit;
10409
+ if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;
10410
+ return undefined;
10411
+ }
10412
+
10413
+ function rustSynMacroKind(kind) {
10414
+ return /Macro|MacroRules|MacroCall/.test(String(kind));
10415
+ }
10416
+
10417
+ function clangAstRoot(value) {
10418
+ if (!value || typeof value !== 'object') return undefined;
10419
+ if (Array.isArray(value)) return { kind: 'TranslationUnitDecl', inner: value };
10420
+ if (isClangAstNode(value)) return value;
10421
+ if (Array.isArray(value.ast)) return { kind: 'TranslationUnitDecl', inner: value.ast };
10422
+ if (isClangAstNode(value.ast)) return value.ast;
10423
+ if (isClangAstNode(value.root)) return value.root;
10424
+ if (isClangAstNode(value.translationUnit)) return value.translationUnit;
10425
+ if (isClangAstNode(value.tu)) return value.tu;
10426
+ if (isClangAstNode(value.file)) return value.file;
10427
+ if (isClangAstNode(value.sourceFile)) return value.sourceFile;
10428
+ return undefined;
10429
+ }
10430
+
10431
+ function isClangAstNode(value) {
10432
+ return Boolean(value && typeof value === 'object' && typeof clangAstKind(value) === 'string');
10433
+ }
10434
+
10435
+ function clangAstKind(node) {
10436
+ if (!node || typeof node !== 'object') return undefined;
10437
+ const declared = node.kind ?? node._type ?? node.type ?? node.nodeType ?? node.declKind ?? node.stmtKind;
10438
+ if (typeof declared === 'string') return declared;
10439
+ if (Array.isArray(node.inner) || Array.isArray(node.children) || Array.isArray(node.decls)) return 'TranslationUnitDecl';
10440
+ return undefined;
10441
+ }
10442
+
10443
+ function ignoredClangAstField(key) {
10444
+ return key === '_type'
10445
+ || key === 'type'
10446
+ || key === 'kind'
10447
+ || key === 'nodeType'
10448
+ || key === 'declKind'
10449
+ || key === 'stmtKind'
10450
+ || key === 'id'
10451
+ || key === 'loc'
10452
+ || key === 'range'
10453
+ || key === 'name'
10454
+ || key === 'displayName'
10455
+ || key === 'qualifiedName'
10456
+ || key === 'mangledName'
10457
+ || key === 'parent'
10458
+ || key === 'parentDeclContextId'
10459
+ || key === 'previousDecl'
10460
+ || key === 'referencedDecl';
10461
+ }
10462
+
10463
+ function primitiveClangAstFields(node, kind) {
10464
+ const fields = { kind };
10465
+ const name = clangDeclarationName(node);
10466
+ if (name) fields.name = name;
10467
+ const type = clangTypeName(node.type ?? node.qualType);
10468
+ if (type) fields.type = type;
10469
+ for (const key of [
10470
+ 'mangledName',
10471
+ 'storageClass',
10472
+ 'tagUsed',
10473
+ 'completeDefinition',
10474
+ 'isThisDeclarationADefinition',
10475
+ 'inline',
10476
+ 'isUsed',
10477
+ 'isReferenced',
10478
+ 'valueCategory',
10479
+ 'opcode',
10480
+ 'castKind'
10481
+ ]) {
10482
+ const value = node[key];
10483
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) fields[key] = value;
10484
+ }
10485
+ const includePath = clangIncludePath(node);
10486
+ if (includePath) fields.includePath = includePath;
10487
+ const literal = clangLiteralValue(node);
10488
+ if (literal !== undefined) fields.literal = literal;
10489
+ const referenced = clangDeclarationName(node.referencedDecl);
10490
+ if (referenced) fields.referenced = referenced;
10491
+ return fields;
10492
+ }
10493
+
10494
+ function spanFromClangAstNode(node, input) {
10495
+ const range = node.range ?? {};
10496
+ const begin = range.begin ?? node.loc ?? node.spellingLoc ?? node.expansionLoc;
10497
+ const end = range.end ?? node.end;
10498
+ const start = clangLocPosition(begin);
10499
+ if (!start) return undefined;
10500
+ const finish = clangLocPosition(end);
10501
+ return {
10502
+ sourceId: input.sourceHash,
10503
+ path: start.path ?? finish?.path ?? input.sourcePath,
10504
+ startLine: start.line,
10505
+ startColumn: start.column,
10506
+ endLine: finish?.line,
10507
+ endColumn: finish?.column
10508
+ };
10509
+ }
10510
+
10511
+ function clangLocPosition(loc) {
10512
+ if (!loc || typeof loc !== 'object') return undefined;
10513
+ const expanded = loc.expansionLoc ?? loc.spellingLoc ?? loc;
10514
+ const line = expanded.line ?? expanded.startLine ?? expanded.begin?.line;
10515
+ const column = expanded.col ?? expanded.column ?? expanded.startColumn ?? expanded.begin?.col ?? expanded.begin?.column;
10516
+ if (typeof line !== 'number') return undefined;
10517
+ return {
10518
+ path: expanded.file ?? expanded.filename ?? expanded.path,
10519
+ line,
10520
+ column: typeof column === 'number' ? column : undefined
10521
+ };
10522
+ }
10523
+
10524
+ function clangLocationKind(node) {
10525
+ const loc = node.loc ?? node.range?.begin;
10526
+ if (!loc || typeof loc !== 'object') return undefined;
10527
+ if (loc.expansionLoc) return 'expansionLoc';
10528
+ if (loc.spellingLoc) return 'spellingLoc';
10529
+ if (loc.includedFrom) return 'includedFrom';
10530
+ return 'clang-loc';
10531
+ }
10532
+
10533
+ function clangAstDeclaration(node, kind, nativeNodeId, input) {
10534
+ if (kind === 'FunctionDecl' || kind === 'CXXMethodDecl' || kind === 'FunctionTemplateDecl') {
10535
+ const name = clangDeclarationName(node);
10536
+ if (name) return declarationRecord(input, nativeNodeId, name, kind === 'CXXMethodDecl' ? 'method' : 'function', clangDeclarationAction(node));
10537
+ }
10538
+ if (kind === 'ParmVarDecl') {
10539
+ const name = clangDeclarationName(node);
10540
+ if (name) return declarationRecord(input, nativeNodeId, name, 'parameter', 'definition');
10541
+ }
10542
+ if (kind === 'RecordDecl' || kind === 'CXXRecordDecl' || kind === 'ClassTemplateDecl') {
10543
+ const name = clangDeclarationName(node);
10544
+ if (name) return declarationRecord(input, nativeNodeId, name, kind === 'CXXRecordDecl' ? 'class' : 'type', clangDeclarationAction(node));
10545
+ }
10546
+ if (kind === 'FieldDecl') {
10547
+ const name = clangDeclarationName(node);
10548
+ if (name) return declarationRecord(input, nativeNodeId, name, 'property', 'definition');
10549
+ }
10550
+ if (kind === 'TypedefDecl' || kind === 'TypeAliasDecl' || kind === 'TypeAliasTemplateDecl') {
10551
+ const name = clangDeclarationName(node);
10552
+ if (name) return declarationRecord(input, nativeNodeId, name, 'type', 'definition');
10553
+ }
10554
+ if (kind === 'EnumDecl') {
10555
+ const name = clangDeclarationName(node);
10556
+ if (name) return declarationRecord(input, nativeNodeId, name, 'type', clangDeclarationAction(node));
10557
+ }
10558
+ if (kind === 'EnumConstantDecl') {
10559
+ const name = clangDeclarationName(node);
10560
+ if (name) return declarationRecord(input, nativeNodeId, name, 'enumMember', 'definition');
10561
+ }
10562
+ if (kind === 'VarDecl') {
10563
+ const name = clangDeclarationName(node);
10564
+ if (name) return declarationRecord(input, nativeNodeId, name, 'variable', clangDeclarationAction(node));
10565
+ }
10566
+ if (/IncludeDirective|InclusionDirective/.test(kind)) {
10567
+ const name = clangIncludePath(node);
10568
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
10569
+ }
10570
+ if (/MacroDefinition|MacroExpansion|MacroInstantiation/.test(kind)) {
10571
+ const name = clangDeclarationName(node) ?? clangIncludePath(node);
10572
+ if (name) return declarationRecord(input, nativeNodeId, name, 'macro', 'definition');
10573
+ }
10574
+ return undefined;
10575
+ }
10576
+
10577
+ function clangDeclarationAction(node) {
10578
+ if (node.isThisDeclarationADefinition === false) return 'declaration';
10579
+ if (node.isThisDeclarationADefinition === true) return 'definition';
10580
+ if (Array.isArray(node.inner) && node.inner.some((entry) => ['CompoundStmt', 'FieldDecl', 'EnumConstantDecl'].includes(clangAstKind(entry)))) return 'definition';
10581
+ return 'declaration';
10582
+ }
10583
+
10584
+ function clangAstChildEntries(node) {
10585
+ const fieldNames = Object.keys(node).filter((key) => !ignoredClangAstField(key));
10586
+ return fieldNames
10587
+ .map((field) => [field, node[field]])
10588
+ .filter(([, value]) => Array.isArray(value)
10589
+ ? value.some(isClangAstNode)
10590
+ : isClangAstNode(value));
10591
+ }
10592
+
10593
+ function clangAstNodeValue(node) {
10594
+ return clangDeclarationName(node)
10595
+ ?? clangIncludePath(node)
10596
+ ?? clangTypeName(node.type ?? node.qualType)
10597
+ ?? clangLiteralValue(node);
10598
+ }
10599
+
10600
+ function clangDeclarationName(node) {
10601
+ if (!node || typeof node !== 'object') return undefined;
10602
+ for (const key of ['qualifiedName', 'displayName', 'name', 'mangledName']) {
10603
+ if (typeof node[key] === 'string' && node[key]) return node[key];
10604
+ }
10605
+ if (node.name && typeof node.name === 'object') return clangDeclarationName(node.name);
10606
+ if (node.referencedDecl && typeof node.referencedDecl === 'object') return clangDeclarationName(node.referencedDecl);
10607
+ if (node.decl && typeof node.decl === 'object') return clangDeclarationName(node.decl);
10608
+ return undefined;
10609
+ }
10610
+
10611
+ function clangIncludePath(node) {
10612
+ if (!node || typeof node !== 'object') return undefined;
10613
+ for (const key of ['file', 'filename', 'path', 'spelling', 'source']) {
10614
+ if (typeof node[key] === 'string' && node[key]) return node[key];
10615
+ }
10616
+ if (node.include && typeof node.include === 'object') return clangIncludePath(node.include);
10617
+ return undefined;
10618
+ }
10619
+
10620
+ function clangTypeName(value) {
10621
+ if (!value) return undefined;
10622
+ if (typeof value === 'string') return value;
10623
+ if (typeof value.qualType === 'string') return value.qualType;
10624
+ if (typeof value.desugaredQualType === 'string') return value.desugaredQualType;
10625
+ if (typeof value.name === 'string') return value.name;
10626
+ if (value.type && typeof value.type === 'object') return clangTypeName(value.type);
10627
+ return undefined;
10628
+ }
10629
+
10630
+ function clangLiteralValue(node) {
10631
+ const value = node.value ?? node.val ?? node.literal;
10632
+ if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;
10633
+ if (typeof node.valueAsString === 'string') return node.valueAsString;
10634
+ return undefined;
10635
+ }
10636
+
10637
+ function clangPreprocessorKind(kind) {
10638
+ return /Macro|Preprocess|Preprocessor|IfDirective|IfdefDirective|IfndefDirective|ElifDirective|ElseDirective|EndifDirective/.test(String(kind));
10639
+ }
10640
+
10641
+ function clangPreprocessorRecords(value) {
10642
+ if (!value) return [];
10643
+ if (Array.isArray(value)) return value.filter(isClangAstNode);
10644
+ if (typeof value === 'object' && Array.isArray(value.records)) return value.records.filter(isClangAstNode);
10645
+ if (isClangAstNode(value)) return [value];
10646
+ return [];
10647
+ }
10648
+
10649
+ function serializableIncludeGraphSummary(value) {
10650
+ if (!value || typeof value !== 'object') return undefined;
10651
+ if (Array.isArray(value)) return { edgeCount: value.length };
10652
+ const summary = {};
10653
+ if (typeof value.hash === 'string') summary.hash = value.hash;
10654
+ if (typeof value.root === 'string') summary.root = value.root;
10655
+ if (Array.isArray(value.edges)) summary.edgeCount = value.edges.length;
10656
+ if (Array.isArray(value.includes)) summary.includeCount = value.includes.length;
10657
+ return Object.keys(summary).length ? summary : { present: true };
10658
+ }
10659
+
9760
10660
  function declarationRecord(input, nativeNodeId, name, symbolKind, role = 'definition') {
9761
10661
  return {
9762
10662
  name: String(name),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",