feat(phase8): add field type data structures and extractor interface

This commit is contained in:
Oleg Dizus 2026-03-24 14:00:59 +06:00
parent f0540b33fb
commit 5b36d04447
2 changed files with 137 additions and 0 deletions

View file

@ -0,0 +1,81 @@
// gitnexus/src/core/ingestion/field-extractor.ts
import type { SyntaxNode } from './utils.js';
import { SupportedLanguages } from '../../config/supported-languages.js';
import type {
FieldExtractorContext,
ExtractedFields
} from './field-types.js';
/**
* Language-specific field extractor
*/
export interface FieldExtractor {
/** Language this extractor handles */
language: SupportedLanguages;
/**
* Extract fields from a class/struct/interface declaration
*/
extract(node: SyntaxNode, context: FieldExtractorContext): ExtractedFields | null;
/**
* Check if this node represents a type declaration with fields
*/
isTypeDeclaration(node: SyntaxNode): boolean;
}
/**
* Registry of field extractors by language
*/
export class FieldExtractorRegistry {
private extractors = new Map<SupportedLanguages, FieldExtractor>();
register(extractor: FieldExtractor): void {
this.extractors.set(extractor.language, extractor);
}
get(language: SupportedLanguages): FieldExtractor | undefined {
return this.extractors.get(language);
}
has(language: SupportedLanguages): boolean {
return this.extractors.has(language);
}
}
/**
* Base class for field extractors with common utilities
*/
export abstract class BaseFieldExtractor implements FieldExtractor {
abstract language: SupportedLanguages;
abstract extract(node: SyntaxNode, context: FieldExtractorContext): ExtractedFields | null;
abstract isTypeDeclaration(node: SyntaxNode): boolean;
protected normalizeType(type: string | null): string | null {
if (!type) return null;
return type.trim().replace(/\s+/g, ' ');
}
protected resolveType(typeName: string, context: FieldExtractorContext): string | null {
const { typeEnv, symbolTable, filePath } = context;
// Try to find in type environment (check file scope first)
const fileEnv = typeEnv.get('');
if (fileEnv) {
const local = fileEnv.get(typeName);
if (local) return local;
}
// Try symbol table lookup in current file
const symbols = symbolTable.lookupExactAll(filePath, typeName);
if (symbols.length === 1) {
return symbols[0].nodeId;
}
return typeName;
}
protected abstract extractVisibility(node: SyntaxNode): string;
}

View file

@ -0,0 +1,56 @@
// gitnexus/src/core/ingestion/field-types.ts
import type { TypeEnv } from './type-env.js';
import type { SymbolTable } from './symbol-table.js';
import { SupportedLanguages } from '../../config/supported-languages.js';
/**
* Represents a field or property within a class/struct/interface
*/
export interface FieldInfo {
/** Field name */
name: string;
/** Resolved type (may be primitive, FQN, or generic) */
type: string | null;
/** Visibility: public, private, protected, internal */
visibility: string;
/** Is this a static member? */
isStatic: boolean;
/** Is this readonly/const? */
isReadonly: boolean;
/** Source file path */
sourceFile: string;
/** Line number */
line: number;
}
/**
* Maps owner type FQN to its fields
*/
export type FieldTypeMap = Map<string, FieldInfo[]>;
/**
* Context for field extraction
*/
export interface FieldExtractorContext {
/** Type environment for resolution */
typeEnv: TypeEnv;
/** Symbol table for FQN lookups */
symbolTable: SymbolTable;
/** Current file path */
filePath: string;
/** Language ID */
language: SupportedLanguages;
}
/**
* Result of field extraction from a type declaration
*/
export interface ExtractedFields {
/** Owner type FQN */
ownerFqn: string;
/** Extracted fields */
fields: FieldInfo[];
/** Nested types found during extraction */
nestedTypes: string[];
}