@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
185 lines (184 loc) • 9.8 kB
TypeScript
import type { BuiltInMemory } from './built-in';
import type { IdentifierDefinition } from './identifier';
import { Identifier } from './identifier';
import type { ControlDependency } from '../info';
import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
/** A single entry/scope within an {@link REnvironmentInformation} */
export interface IEnvironment {
/** Unique internally generated identifier, used for debugging not comparison */
readonly id: number;
/** Lexical parent of the environment, if any (can be manipulated by R code) */
parent: IEnvironment;
/** Maps to exactly one definition of an identifier if the source is known, otherwise to a list of all possible definitions */
memory: BuiltInMemory;
/** Built-in environment that must not change; only for the top-most envs. */
builtInEnv?: true | undefined;
}
export declare enum EnvType {
Namespace = "ns",
Imports = "imp",
/** `requireNamespace("pkg")`: `pkg::fn` resolves, bare `fn` does not */
LoadedNamespace = "lns"
}
interface Jsonified {
id: NodeId;
parent: Jsonified | undefined;
builtInEnv?: true;
memory: BuiltInMemory;
n?: string;
t?: EnvType;
globalEnv?: true;
}
/**
* Use only if you do not know the object type; otherwise rely on {@link IEnvironment#builtInEnv}.
*/
export declare function isDefaultBuiltInEnvironment(obj: unknown): boolean;
/** @see REnvironmentInformation */
export declare class Environment implements IEnvironment {
readonly id: number;
/** Optional name for namespaced/non-anonymous environments, please only set if you know what you are doing */
n?: string;
/** which search-path layer this env is (package/namespace/imports), if any */
t?: EnvType;
/** if created by a closure, the node id of that closure */
private c?;
parent: Environment;
memory: BuiltInMemory;
cache?: Map<Identifier, IdentifierDefinition[]>;
builtInEnv?: true;
/** {@link memory} is shared with a clone; writing needs {@link writableMemory} to unshare it first */
private sharedMemory?;
/** marks the global environment (`.GlobalEnv`); attached packages (see {@link EnvType}) live below it */
globalEnv?: true;
constructor(parent: Environment, isBuiltInDefault?: true | undefined);
/** Marks this as an attached-package layer (see {@link EnvType}) for package `name`. */
asLibrary(name: string, type: EnvType): this;
/** Marks this as the global environment (`.GlobalEnv`); see {@link globalEnv}. */
asGlobal(): this;
/** please only use if you know what you are doing */
setClosureNodeId(nodeId: NodeId): void;
/** Provides the closure linked to this environment. */
get closure(): NodeId | undefined;
/**
* This environment's {@link memory}, ready to be written to. Every in-place write must go through this
* rather than through {@link memory} directly, as {@link clone} hands the map itself to the clone and only
* the first writer of either side copies it (copy-on-write).
*/
get writableMemory(): BuiltInMemory;
/**
* Create a clone of this environment.
*
* The clone shares this environment's {@link memory} until either side writes to it (see
* {@link writableMemory}); cloning a frame is therefore independent of how many definitions it holds, which
* matters because attached packages contribute frames with thousands of them.
* @param recurseParents - Whether to also clone parent environments
*/
clone(recurseParents: boolean): Environment;
/**
* Define a new identifier definition within this environment.
* @param definition - The definition to add.
*/
define(definition: IdentifierDefinition & {
name: Identifier;
}): Environment;
/**
* Define several identifiers at once in a more performant fashion.
* @param definitions - The definitions to add.
*/
defineAll(definitions: Iterable<IdentifierDefinition & {
name: Identifier;
}>): Environment;
/** Only sound on an environment nobody else holds yet. */
private apply;
private defineInNamespace;
defineSuper(definition: IdentifierDefinition & {
name: Identifier;
}): Environment;
/**
* Definitions within `other` replace those here by name; if all of `other`'s are maybe, they are appended instead (turning existing ones maybe too), like {@link appendEnvironment}. Always recurses parents.
*/
overwrite(other: Environment | undefined, applyCds?: readonly ControlDependency[]): Environment;
/**
* Adds all writes of `other` to this environment (`other`'s operations *might* happen). Always recurses parents.
*/
append(other: Environment | undefined): Environment;
/**
* The environment a merge with `other` settles on without touching either memory, `undefined` if the
* memories have to be merged. Package blocks are always unioned, never overwritten or appended to.
*/
private mergeShortcut;
/**
* Unions two attached-package blocks, keeping every package once (memory merged for a package in both).
*/
private mergePackageBlocks;
remove(id: Identifier): this;
removeAll(names: readonly {
name: Identifier;
}[]): Environment;
toJSON(): Jsonified;
}
/** Walks up to the global environment (see {@link Environment#globalEnv}), falling back to the last non-builtin env. */
declare function findGlobalEnvironment(this: void, env: Environment): Environment;
/** Walks up to the built-in environment. */
declare function findBuiltInEnvironment(this: void, env: Environment): Environment;
/** The `search()` position directly below the global environment; where R attaches by default. */
export declare const DefaultAttachPosition = 2;
/** Prefix of a package's entry in R's `search()` list. */
export declare const SearchPathPackagePrefix = "package:";
/** Name of the global environment in R's `search()` list. */
export declare const GlobalEnvEntryName = ".GlobalEnv";
/**
* Splices a package block (`blockTop`..`blockBottom`) into the search path at the 1-based `search()` position `pos`
* ({@link DefaultAttachPosition|2} being directly below the global environment, the default). A position past the end
* of the search path attaches directly above the built-in environment, mirroring R's clamping. Returns a fresh
* `current`, cloning only the path down to the insertion point.
*/
declare function attachPackageAt(this: void, current: Environment, blockTop: Environment, blockBottom: Environment, pos?: number): Environment;
/**
* The 1-based `search()` position of the entry called `name` (`.GlobalEnv`, `package:x`, or a bare package name),
* or `undefined` if no such entry is on the search path. `package:base` resolves to the built-in environment at the
* very bottom if base R is not attached as its own layer.
*/
declare function searchPositionOf(this: void, env: Environment, name: string): number | undefined;
/**
* The packages attached below the global environment, i.e. those whose exports R resolves without a namespace.
* Base is always among them, as it backs the built-in environment even when it is no layer of its own.
*/
declare function attachedPackagesOf(this: void, env: Environment): Set<string>;
/**
* Helpers for navigating and manipulating {@link REnvironmentInformation|environments} around the global environment and attached-package search path.
*/
export declare const REnvironment: {
readonly name: "REnvironment";
/** Walks up to the global environment (`.GlobalEnv`); see {@link findGlobalEnvironment}. */
readonly findGlobal: typeof findGlobalEnvironment;
/** Walks up to the built-in environment; see {@link findBuiltInEnvironment}. */
readonly findBuiltIn: typeof findBuiltInEnvironment;
/** Attaches a package block at a `search()` position, below the global by default; see {@link attachPackageAt}. */
readonly attachAt: typeof attachPackageAt;
/** The `search()` position of a named entry; see {@link searchPositionOf}. */
readonly searchPosition: typeof searchPositionOf;
/** The packages on the search path; see {@link attachedPackagesOf}. */
readonly attachedPackages: typeof attachedPackagesOf;
};
/**
* An environment describes a ({@link IEnvironment#parent|scoped}) mapping of names to their definitions ({@link BuiltIns}).
*
* The {@link BuiltIns|BuiltInEnvironment} holds R's built-in functions and constants; during serialization use {@link builtInEnvJsonReplacer} to avoid inlining it.
* @see {@link define} - to define a new {@link IdentifierDefinition|identifier definition} within an environment
* @see {@link Resolve.byNameAndType} - to resolve an {@link Identifier|identifier/name} to its {@link IdentifierDefinition|definitions} within an environment
* @see {@link makeReferenceMaybe} - to attach control dependencies to a reference
* @see {@link pushLocalEnvironment} - to create a new local scope
* @see {@link popLocalEnvironment} - to remove the current local scope
* @see {@link appendEnvironment} - to append an environment to the current one
* @see {@link overwriteEnvironment} - to overwrite the definitions in the current environment with those of another one
*/
export interface REnvironmentInformation {
/** The currently active environment (the stack is represented by the {@link IEnvironment#parent} chain). */
readonly current: Environment;
/** nesting level of the environment, will be `0` for the global/root environment */
readonly level: number;
}
/** Serializes an environment, replacing the built-in environment with a placeholder. */
export declare function builtInEnvJsonReplacer(k: unknown, v: unknown): unknown;
export {};