/**
 * Version of the normalization rules below. It is stored on every hash record
 * and folded into the cache directory name, so a change here is a clean cache
 * miss rather than a silent comparison of incomparable values.
 *
 * Bump this whenever the lexer, the keyword tables, or the digest material
 * change in any way.
 */
export declare const ENTITY_HASH_VERSION = 1;
/**
 * Which rules produced a hash record.
 *
 * - `lexer`: the full span was tokenized; every hash is present and comparable.
 * - `declaration`: the parser reported no real span (`endLine === line`, the
 *   regex-fallback tiers), so only the declaration line was available. Body
 *   evidence does not exist at this level.
 * - `none`: no source was available at all; only `bodyHash` is meaningful.
 *
 * Two hash records may only be compared when their `algo` **and**
 * `hashVersion` agree. A record produced from a declaration line must never be
 * accepted as evidence about a body — see `hashesComparable`.
 */
export type EntityHashAlgo = 'lexer' | 'declaration' | 'none';
export interface EntityHashes {
    hashVersion: number;
    algo: EntityHashAlgo;
    /**
     * Frozen `code.symbol.bodyHash` contract: sha256 over the exact source span,
     * unchanged from `computeBodyHash`. Byte-identical bodies and nothing else.
     */
    bodyHash: string;
    /** Exact manifest-pinned CST declaration bytes; legacy `bodyHash` stays frozen. */
    exactBodyHash?: string;
    /**
     * Comment-, whitespace-, and quote-style-blind heuristic. Equality may
     * support identity matching, but is never semantic no-op evidence.
     */
    normalizedHash?: string;
    /**
     * Additionally identifier- and literal-blind: the token *shape* stream only.
     * Equal means the two entities have the same structure, which is the
     * evidence that survives a rename — the entity's own name is elided by
     * construction, since every identifier collapses to one placeholder.
     */
    structuralHash?: string;
    /** Shape stream of the declaration alone, for signature-change detection. */
    signatureHash?: string;
    /** Token count, gating similarity scoring to entities big enough to carry signal. */
    tokenCount?: number;
}
export declare function computeExactBodyHash(source: string): string;
/**
 * Legacy normalization removes comments, whitespace, line breaks,
 * indentation, quote style, and numeric separators. It deliberately does
 * **not** make a semantic claim: comments and whitespace can be significant,
 * and this stream remains only a lineage-search heuristic.
 */
export interface EntityToken {
    shape: string;
    text: string;
}
interface LexSpec {
    lineComments: readonly string[];
    blockComments: readonly (readonly [string, string])[];
    quotes: readonly string[];
    /** Triple-quoted strings (Python) must be matched before single quotes. */
    longQuotes: readonly string[];
    keywords: ReadonlySet<string>;
}
/** Lexical dialect for a path. Unknown extensions fall back to the shared set. */
export declare function lexDialectForPath(relativePath: string): string;
export declare function lexSpecForDialect(dialect: string): LexSpec;
/**
 * Tokenize a source span into shape/text pairs, dropping comments and
 * whitespace entirely.
 *
 * This is a lexer, not a parser: it never needs the span to be a valid program,
 * which matters because an entity body is a fragment. Keywords keep their own
 * identity so control flow survives into the structural stream, while other
 * identifiers collapse to `$` and literals to their class — that is what makes
 * the structural hash rename-tolerant.
 */
export declare function tokenizeSource(source: string, dialect: string): EntityToken[];
export interface ComputeEntityHashesInput {
    /** The exact source span, as `readCodeSource` would return it. */
    source: string;
    /** Repo-relative path; selects the lexical dialect. */
    relativePath: string;
    /**
     * False when the parser reported no real span (`endLine === line`). The
     * record is then declaration-scoped and may not be used as body evidence.
     */
    spanExact: boolean;
}
export declare function computeEntityHashes(input: ComputeEntityHashesInput): EntityHashes;
/**
 * Whether two hash records may be compared at all.
 *
 * This gate is load-bearing. Without it, a machine where a tree-sitter grammar
 * failed to load (dropping a file to the regex tier, where only the declaration
 * line is available) would compare declaration hashes against body hashes and
 * manufacture lineage that does not exist. When it returns false the matcher
 * must fall through to name-based evidence and record that evidence was
 * unavailable — never guess.
 */
export declare function hashesComparable(a: EntityHashes | undefined, b: EntityHashes | undefined): boolean;
/** True only when both records are full-span; body evidence requires it. */
export declare function bodyEvidenceAvailable(a: EntityHashes | undefined, b: EntityHashes | undefined): boolean;
/** Overlapping shingles of the shape stream, the unit of similarity scoring. */
export declare function shingleSet(shapes: readonly string[], width?: number): Set<string>;
/** Sørensen–Dice: `2|A∩B| / (|A|+|B|)`. Symmetric, in `[0, 1]`, no sampling. */
export declare function diceSimilarity(a: ReadonlySet<string>, b: ReadonlySet<string>): number;
/** Similarity over identifier-blind shape shingles — the rename-tolerant signal. */
export declare function structuralSimilarity(a: string, b: string, relativePath: string, width?: number): number;
/** Similarity over identifier-aware tokens — the independent tiebreak signal. */
export declare function normalizedSimilarity(a: string, b: string, relativePath: string, width?: number): number;
export {};
