import type { BuiltInIdentifierConstant, BuiltInIdentifierDefinition } from './built-in';
import { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
import type { ControlDependency } from '../info';
import type { REnvironmentInformation } from './environment';
import type { Origin } from '../origin/dfg-get-origin';
/** this is just a safe-guard type to prevent mixing up branded identifiers with normal strings */
export type BrandedIdentifier = string & {
    __brand?: 'identifier';
};
/** this is just a safe-guard type to prevent mixing up branded namespaces with normal strings */
export type BrandedNamespace = string & {
    __brand?: 'namespace';
};
/**
 * Refers to an identifier by its name.
 * This can either be a simple name like `a` or a namespaced name like `pkg::a` (stored as ['a', 'pkg']).
 * By storing the namespace second, you can easily access the actual name via `id[0]`.
 * This represents the fundamental way to represent binding names in R.
 * @see {@link Identifier.getName} - to get the name part
 * @see {@link Identifier.getNamespace} - to get the namespace part
 * @see {@link Identifier.accessesInternal} - to check if the identifier accesses internal objects (`:::`)
 * @see {@link Identifier.toString} - to convert the identifier to a string representation
 */
export type Identifier = BrandedIdentifier | [id: BrandedIdentifier, namespace: BrandedNamespace, internal?: boolean];
/**
 * Helper functions to work with {@link Identifier|identifiers}.
 * Use {@link Identifier.matches} to check if two identifiers match according to R's scoping rules!
 * @example
 * ```ts
 * const id1 = Identifier.make('a', 'pkg');
 * const id2 = Identifier.parse('pkg::a');
 * const id3 = Identifier.parse('a');
 * Identifier.matches(id1, id2); // true
 * Identifier.matches(id3, id2); // true, as id3 has no namespace
 * ```
 */
export declare const Identifier: {
    readonly name: "Identifier";
    /**
     * Creates an identifier. Strips surrounding backticks from the name.
     * Prefer {@link Identifier.from} for static config entries where namespace is always present and name has no backticks.
     */
    readonly make: (this: void, name: BrandedIdentifier, namespace?: BrandedNamespace, internal?: boolean) => Identifier;
    /**
     * Fast-path factory: returns the tuple as-is with no allocation or runtime checks.
     * Use for static built-in config entries where name is a compile-time constant with no backticks.
     * @example
     * ```ts
     * Identifier.from(['map', 'purrr'])       // ['map', 'purrr']
     * Identifier.from(['map', 'purrr', true]) // ['map', 'purrr', true]
     * ```
     */
    readonly from: (this: void, arr: [BrandedIdentifier, BrandedNamespace] | [BrandedIdentifier, BrandedNamespace, boolean]) => Identifier;
    /**
     * The same fast path for many names of one package: the given array of names is cast in place,
     * so the literal you pass is the only array involved.
     * @example
     * ```ts
     * Identifier.fromAll('purrr', ['map', 'walk']) // [['map', 'purrr'], ['walk', 'purrr']]
     * ```
     */
    readonly fromAll: (this: void, namespace: BrandedNamespace, names: BrandedIdentifier[]) => Identifier[];
    /**
     * Verify whether an unknown element has a valid identifier shape!
     */
    readonly is: (this: void, id: unknown) => id is Identifier;
    /**
     * Parse an identifier from its string representation,
     * Please note, that in R if one writes `"pkg::a"` this refers to a symbol named `pkg::a` and NOT to the namespaced identifier `a` in package `pkg`.
     * In this scenario, see {@link Identifier.make} instead.
     */
    readonly parse: (this: void, str: string) => Identifier;
    /**
     * Get the name part of the identifier
     */
    readonly getName: (this: void, id: Identifier) => BrandedIdentifier;
    /**
     * Get the namespace part of the identifier, undefined if there is none
     */
    readonly getNamespace: (this: void, id: Identifier) => BrandedNamespace | undefined;
    /**
     * Check if the identifier accesses internal objects (`:::`)
     */
    readonly accessesInternal: (this: void, id: Identifier) => boolean | undefined;
    /**
     * Convert the identifier to a **valid R** string representation,
     * this will properly quote namespaces that contain `::` to avoid confusion.
     * @example
     * ```ts
     * Identifier.toString('a') // 'a'
     * Identifier.toString(['a', 'pkg']) // 'pkg::a'
     * Identifier.toString(['a', 'pkg:::internal', true]) // '"pkg:::internal":::a'
     * ```
     */
    readonly toString: (this: void, id: Identifier) => string;
    /**
     * Check if two identifiers match.
     * This differs from eq!
     * If the first identifier is not namespaced, it will match any namespace!
     * If we search for S3 methods (s3=true), the target may have an additional suffix after a dot.
     * If the first identifier is internal, it will match any target (internal or not).
     */
    readonly matches: (this: void, id: Identifier, target: Identifier, s3?: boolean) => boolean;
    /**
     * Helper to create a regular expression that matches against an array of {@link Identifier} values. If both the passed identifier and the matched identifier are namespaced, their namespaces are expected to match. If either is not namespaced, the namespace is ignored on both.
     */
    readonly regex: (this: void, ...identifiers: readonly Identifier[]) => RegExp;
    /** Special identifier for the `...` argument */
    readonly dotdotdot: (this: void) => BrandedIdentifier;
    /**
     * Check if the identifier is the special `...` argument / or one of its accesses like `..1`, `..2`, etc.
     * This always returns false for namespaced identifiers.
     */
    readonly isDotDotDotAccess: (this: void, id: Identifier) => boolean;
    /**
     * Functor over the name of the identifier
     */
    readonly mapName: (this: void, id: Identifier, fn: (name: BrandedIdentifier) => BrandedIdentifier) => Identifier;
    /**
     * Functor over the namespace of the identifier
     */
    readonly mapNamespace: (this: void, id: Identifier, fn: (ns: BrandedNamespace) => BrandedNamespace) => Identifier;
    /**
     * Convert the identifier to its array representation
     */
    readonly toArray: (this: void, id: Identifier) => [BrandedIdentifier, BrandedNamespace | undefined, boolean | undefined];
    /**
     * The package-qualified identifier of a call, resolved in order of decreasing certainty:
     * 1. a package export the {@link Origin|origins} resolve to (`map()` with `purrr` loaded yields `purrr::map`),
     * 2. an already-namespaced `name` returned unchanged (an explicit `pkg::fn` call),
     * 3. with `qualifyBaseR`, a bare base-R call qualified from its exporting package via {@link baseRExportOwner}
     *    (`sd` yields `stats::sd`), needing no loaded database or graph edge and skipped when the call resolves to
     *    a user definition, so a local `sd()` stays bare.
     *
     * Returns `undefined` when none apply. Steps 2 and 3 need the call's `name`.
     * @param qualifyBaseR - whether to also qualify a bare base-R call from its exporting package (default `true`)
     * @see {@link Dataflow.qualify} - the compact form, if you have the call's id and its graph
     */
    readonly toQualified: (this: void, origins: readonly Origin[] | undefined, name?: Identifier, qualifyBaseR?: boolean) => Identifier | undefined;
};
/**
 * Well-known R package names used in {@link DefaultBuiltinConfig}.
 * Using a const enum keeps the string values inlined at compile time.
 */
export declare const enum PkgName {
    Base = "base",
    Compiler = "compiler",
    Graphics = "graphics",
    GrDevices = "grDevices",
    Methods = "methods",
    Stats = "stats",
    Utils = "utils",
    AssertThat = "assertthat",
    Box = "box",
    Cli = "cli",
    CohortBuilder = "cohortBuilder",
    DataTable = "data.table",
    Dbi = "DBI",
    Devtools = "devtools",
    Dplyr = "dplyr",
    Fs = "fs",
    Functools = "functools",
    GgPlot2 = "ggplot2",
    Here = "here",
    Hmisc = "Hmisc",
    Import = "import",
    Inferference = "inferference",
    Janitor = "janitor",
    Lattice = "lattice",
    Magick = "magick",
    Magrittr = "magrittr",
    Msgr = "msgr",
    PkgLoad = "pkgload",
    Plyr = "plyr",
    Purrr = "purrr",
    Ragg = "ragg",
    R6 = "R6",
    RasterPdf = "rasterpdf",
    Rcpp = "Rcpp",
    Remotes = "remotes",
    Rlang = "rlang",
    RmethodsS3 = "R.methodsS3",
    Roo = "R.oo",
    RstudioApi = "rstudioapi",
    Rutils = "R.utils",
    S7 = "S7",
    Shiny = "shiny",
    ShinyCohortBuilder = "shinyCohortBuilder",
    ShinyFiles = "shinyFiles",
    ShinyJs = "shinyjs",
    Soda = "SoDA",
    SvDialogs = "svDialogs",
    Tcltk = "tcltk",
    Testthat = "testthat",
    TidyR = "tidyr",
    Tibble = "tibble",
    Glue = "glue",
    Stringr = "stringr",
    TinyPlot = "tinyplot",
    TryCatchLog = "tryCatchLog",
    Withr = "withr"
}
/**
 * Each reference has exactly one reference type, stored as the respective number.
 * However, when checking, we may want to allow for one of several types,
 * allowing the combination of the respective bitmasks.
 *
 * Having reference types is important as R separates a variable definition from
 * a function when resolving an {@link Identifier|identifier}.
 * In `c <- 3; print(c(1, 2))` the call to `c` works normally (as the vector constructor),
 * while writing `c <- function(...) ..1` overshadows the built-in and causes `print` to only output the first element.
 * @see {@link isReferenceType} - for checking if a (potentially joint) reference type contains a certain type
 * @see {@link ReferenceTypeReverseMapping} - for debugging
 */
export declare enum ReferenceType {
    /** The identifier type is unknown */
    Unknown = 1,
    /** The identifier is defined by a function (includes built-in function) */
    Function = 2,
    /** The identifier is defined by a variable (includes parameter and argument) */
    Variable = 4,
    /** The identifier is defined by a constant (includes built-in constant) */
    Constant = 8,
    /** The identifier is defined by a parameter (which we know nothing about at the moment) */
    Parameter = 16,
    /** The identifier is defined by an argument (which we know nothing about at the moment) */
    Argument = 32,
    /** The identifier is defined by a built-in value/constant */
    BuiltInConstant = 64,
    /** The identifier is defined by a built-in function */
    BuiltInFunction = 128,
    /** Prefix to identify S3 methods, use this, to for example dispatch a call to `f` which will then link to `f.*` */
    S3MethodPrefix = 256,
    /** Prefix to identify S7 methods, use this, to for example dispatch a call to `f` which will then link to `f<7>*` */
    S7MethodPrefix = 512,
    /**
     * Only ever a lookup target, never the type of a definition: everything a value position may see.
     * `id` in `id > 2` names data, so a function `id` in scope is not what the comparison reads.
     */
    NonFunction = 1024
}
/** Reverse mapping of the reference types so you can get the name from the bitmask (useful for debugging) */
export declare const ReferenceTypeReverseMapping: Map<ReferenceType, string>;
/**
 * Check if the reference types have an overlapping type!
 */
export declare function isReferenceType(t: ReferenceType, target: ReferenceType): boolean;
/**
 * Describes all types of reference (definitions) that can appear within a graph (i.e., that are not built-in like the
 * default definition for the assignment operator `<-`).
 * @see {@link InGraphIdentifierDefinition} - for the definition of an identifier within the graph
 */
export type InGraphReferenceType = Exclude<ReferenceType, ReferenceType.BuiltInConstant | ReferenceType.BuiltInFunction | ReferenceType.NonFunction>;
/**
 * An identifier reference points to a variable like `a` in `b <- a`.
 * Without any surrounding code, `a` will produce the identifier reference `a`.
 * Similarly, `b` will create a reference (although it will be an {@link IdentifierDefinition|identifier definition}
 * which adds even more information).
 *
 * In general,
 * references are merely pointers (with meta-information) to a vertex in the {@link DataflowGraph|dataflow graph}.
 * In the context of the extractor, for example,
 * they indicate the references that are currently (during the analysis at this given node)
 * {@link DataflowInformation#in|read (`in`)}, {@link DataflowInformation#out|written (`out`)},
 * or {@link DataflowInformation#unknownReferences|unknown (`unknownReferences`)}.
 * @see {@link InGraphIdentifierDefinition}
 */
export interface IdentifierReference {
    /**
     * The id of the node which represents the reference in the {@link NormalizedAst|normalized AST} and the {@link DataflowGraph|dataflow graph}.
     */
    readonly nodeId: NodeId;
    /** Name the reference is identified by (e.g., the name of the variable), undefined if the reference is "artificial" (e.g., anonymous) */
    readonly name: Identifier | undefined;
    /** Type of the reference to be resolved */
    readonly type: ReferenceType;
    /**
     * If the reference is only effective, if, for example, an if-then-else condition is true, this references the root of the `if`.
     * As a hacky intermediate solution (until we have pointer-analysis), an empty array may indicate a `maybe` which is due to pointer access (e.g., in `a[x] <- 3`).
     */
    cds?: ControlDependency[] | undefined;
}
/**
 * The definition of an {@link Identifier|identifier} within the {@link DataflowGraph|graph}.
 * This extends on the {@link IdentifierReference}
 * by adding the {@link NodeId} of the definition
 * (and using `type` to mark the object type).
 *
 * Within a code snippet like `a <- 3`, the symbol processor will first create an
 * {@link IdentifierReference|identifier reference} for `a` to reference the use
 * and then promote it to an {@link InGraphIdentifierDefinition|identifier definition}.
 * @see {@link IdentifierReference}
 */
export interface InGraphIdentifierDefinition extends IdentifierReference {
    readonly type: InGraphReferenceType;
    /**
     * The assignment node which ultimately defined this identifier
     * (the arrow operator for e.g. `x <- 3`, or `assign` call in `assign("x", 3)`)
     */
    readonly definedAt: NodeId;
    /**
     * For value tracking, this contains all nodeIds of constant values that may be made available to this identifier
     * For example, in `x <- 3; y <- x`, the definition of `y` will have the value `3` in its value set
     */
    readonly value?: NodeId[];
    /**
     * If this variable holds an R environment (created by `new.env()` etc.),
     * this tracks the current known state of that environment.
     * Use this to resolve variables assigned via `assign(name, val, envir=<this var>)`.
     */
    readonly envState?: REnvironmentInformation;
    /**
     * If this is a function that returns a tracked environment, stores the envState
     * that the function returns (best-effort: only set when statically detectable).
     */
    readonly returnsEnvState?: REnvironmentInformation;
    /**
     * Whether {@link value} names the sequence this identifier iterates over rather than what it holds, as a
     * `for` loop's variable does: it takes one element of that sequence at a time.
     */
    readonly iterated?: boolean;
}
/**
 * A narrowed variant of {@link InGraphIdentifierDefinition} that is guaranteed to have a non-undefined `name`.
 * Prefer this over the inline intersection `InGraphIdentifierDefinition & { name: Identifier }`.
 */
export type NamedInGraphIdentifierDefinition = InGraphIdentifierDefinition & {
    readonly name: Identifier;
};
/**
 * Stores the definition of an identifier within an {@link IEnvironment}.
 *
 * {@link BuiltInIdentifierDefinition} and {@link BuiltInIdentifierConstant} are used for built-in functions and constants only,
 * so the most important one for your day-to-day R script is the {@link InGraphIdentifierDefinition}.
 */
export type IdentifierDefinition = InGraphIdentifierDefinition | BuiltInIdentifierDefinition | BuiltInIdentifierConstant;
