@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
184 lines (183 loc) • 8.5 kB
TypeScript
/**
* This module holds the definition of what a {@link Feature} that can be extracted from an R AST is.
*
* Furthermore, it contains the definition of all features that are known in {@link ALL_FEATURES}.
*
* @module
*/
import type { EvalOptions } from 'xpath-ts2/src/parse-api';
import type { MetaStatistics } from '../meta-statistics';
import type { StatisticsSummarizerConfiguration } from '../summarizer/summarizer';
import type { MergeableRecord } from '../../util/objects';
import type { DataflowInformation } from '../../dataflow/info';
import type { NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
import type { Document } from '@xmldom/xmldom';
/**
* Maps each sub-feature name to the number of occurrences of that sub-feature.
* Allows for one nesting level to denote hierarchical features.
* <p>
* Since we are writing to files {@link process}, we only count feature occurrences (some feature/parts are not written to file)
*/
export type FeatureInfo = Record<string, unknown> & MergeableRecord;
/**
* The information and context that a {@link FeatureProcessor} may operate in.
*/
export interface FeatureProcessorInput extends MergeableRecord {
/** The XML Document representing the parsed (non-normalized) R AST */
readonly parsedRAst: Document;
/** The R AST, after the normalization step */
readonly normalizedRAst: NormalizedAst;
/** The dataflow information for the given input */
readonly dataflow: DataflowInformation;
/** The filepath that the document originated from (if present, may be undefined if the input was provided as text). This can be relative to the common root directory of requests. */
readonly filepath: string | undefined;
}
/**
* A function that processes the analysis results of a document and returns the feature information.
*/
export type FeatureProcessor<T extends FeatureInfo> = (existing: T, input: FeatureProcessorInput) => T;
/**
* A feature is something to be retrieved by the statistics.
*
* @typeParam T - The type of what should be collected for the feature
*
* @see ALL_FEATURES
*/
export interface Feature<T extends FeatureInfo> {
/** A descriptive, yet unique name of the feature */
readonly name: string;
/** A description of the feature */
readonly description: string;
/** A function that retrieves the feature in the document appends it to the existing feature set (we could use a monoid :D), the filepath corresponds to the active file (if any) */
process: FeatureProcessor<T>;
/**
* If present, this feature allows to post-process the results of the feature extraction (for the summarizer).
* <p>
* The extraction can use the output path to write files to, and should return the final output.
*
* @param featureRoot - The root path to the feature directory which should contain all the files the feature can write to (already merged for every file processed)
* @param info - The feature statistic maps each file name/context encountered to the feature information as well as the meta statistics for the file
* @param outputPath - The path to write the output to (besides what is collected in the output and meta information)
* @param config - The configuration for the summarizer (e.g., to obtain the number of folders to skip for the feature root)
*/
postProcess?: (featureRoot: string, info: Map<string, FeatureStatisticsWithMeta>, outputPath: string, config: StatisticsSummarizerConfiguration) => void;
/** Values to start the existing track from */
initialValue: T;
}
/**
* The source of truth for all features that are supported by the statistics.
*/
export declare const ALL_FEATURES: {
readonly usedPackages: Feature<import("ts-essentials").Writable<{
library: number;
require: number;
loadNamespace: number;
requireNamespace: number;
attachNamespace: number;
withinApply: number;
'::': number;
':::': number;
'<loadedByVariable>': number;
}>>;
readonly comments: Feature<import("ts-essentials").Writable<{
totalAmount: number;
roxygenComments: number;
import: number;
importFrom: number;
importMethodsFrom: number;
importClassesFrom: number;
useDynLib: number;
export: number;
exportClass: number;
exportMethod: number;
exportS3Method: number;
exportPattern: number;
}>>;
readonly definedFunctions: Feature<import("ts-essentials").Writable<{
total: number;
lambdasOnly: number;
assignedFunctions: number;
nestedFunctions: number;
recursive: number;
deepestNesting: number;
}>>;
readonly usedFunctions: Feature<import("ts-essentials").Writable<{
allFunctionCalls: number;
args: Record<number, bigint | import("./common-syntax-probability").CommonSyntaxTypeCounts>;
nestedFunctionCalls: number;
deepestNesting: number;
unnamedCalls: number;
}>>;
readonly values: Feature<import("ts-essentials").Writable<{
allNumerics: number;
imaginaryNumbers: number;
integers: number;
floatHex: number;
logical: number;
specialConstants: number;
strings: number;
}>>;
readonly assignments: Feature<import("ts-essentials").Writable<{
assignmentOperator: Record<string, bigint>;
assigned: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
deepestNesting: number;
nestedOperatorAssignment: number;
}>>;
readonly loops: Feature<import("ts-essentials").Writable<{
forLoops: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
forLoopVar: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
forBody: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
whileLoops: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
whileBody: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
repeatLoops: bigint;
repeatBody: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
breakStatements: number;
nextStatements: number;
implicitLoops: number;
nestedExplicitLoops: number;
deepestExplicitNesting: number;
}>>;
readonly controlflow: Feature<import("ts-essentials").Writable<{
ifThen: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
thenBody: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
ifThenElse: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
elseBody: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
nestedIfThen: number;
nestedIfThenElse: number;
deepestNesting: number;
switchCase: import("./common-syntax-probability").CommonSyntaxTypeCounts<bigint>;
}>>;
readonly dataAccess: Feature<import("ts-essentials").Writable<{
singleBracket: Record<number, bigint | import("./common-syntax-probability").CommonSyntaxTypeCounts>;
doubleBracket: Record<number, bigint | import("./common-syntax-probability").CommonSyntaxTypeCounts>;
chainedOrNestedAccess: number;
longestChain: number;
deepestNesting: number;
byName: number;
bySlot: number;
}>>;
readonly expressionList: Feature<import("ts-essentials").Writable<{
allExpressionLists: number;
deepestNesting: number;
}>>;
readonly variables: Feature<import("ts-essentials").Writable<{
numberOfVariableUses: number;
numberOfDefinitions: number;
numberOfRedefinitions: number;
unknownVariables: number;
}>>;
};
export type FeatureKey = keyof typeof ALL_FEATURES;
export type FeatureValue<K extends FeatureKey> = ReturnType<typeof ALL_FEATURES[K]['process']>;
/** If the user passes `all`, this should be every feature present in {@link ALL_FEATURES} (see {@link allFeatureNames})*/
export type FeatureSelection = Set<FeatureKey>;
export declare const allFeatureNames: Set<FeatureKey>;
export type FeatureStatistics = {
[K in FeatureKey]: FeatureInfo;
};
export type FeatureStatisticsWithMeta = FeatureStatistics & {
stats: MetaStatistics;
};
export interface Query {
select(options?: EvalOptions): Node[];
}