@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
178 lines • 6.82 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultNormalizedAstFold = void 0;
const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const type_1 = require("../r-bridge/lang-4.x/ast/model/type");
/**
* Default implementation of a fold over the normalized AST (using the classic fold traversal).
* To modify the behavior, please extend this class and overwrite the methods of interest.
* You can control the value passing (`Returns` generic)
* by providing sensible Monoid behavior overwriting the {@link DefaultNormalizedAstFold#concat|concat} method
* and supplying the empty value in the constructor.
*
* @note By providing `entry` and `exit` you can use this as an extension to the simpler {@link visitAst} function but without
* the early termination within the visitors (for this, you can overwrite the respective `fold*` methods).
*
* @example First you want to create your own fold:
*
* ```ts
* let marker = false;
* class MyNumberFold<Info> extends DefaultNormalizedAstFold<void, Info> {
* override foldRNumber(node: RNumber<Info>) {
* super.foldRNumber(node);
* marker = true;
* }
* }
* ```
* This one does explicitly not use the return functionality (and hence acts more as a conventional visitor).
* Now let us suppose we have a normalized AST as an {@link RNode} in the variable `ast`
* and want to check if the AST contains a number:
*
* ```ts
* const result = new MyNumberFold().fold(ast);
* ```
*
* Please take a look at the corresponding tests or the wiki pages for more information on how to use this fold.
*/
class DefaultNormalizedAstFold {
enter;
exit;
empty;
/**
* Empty must provide a sensible default whenever you want to have `Returns` as non-`void`
* (e.g., whenever you want your visitors to be able to return a value).
*/
constructor(empty, enter, exit) {
this.empty = empty;
this.enter = enter;
this.exit = exit;
}
/**
* Monoid::concat
*
*
* @see {@link https://en.wikipedia.org/wiki/Monoid}
* @see {@link DefaultNormalizedAstFold#concatAll|concatAll}
*/
concat(_a, _b) {
return this.empty;
}
/**
* overwrite this method, if you have a faster way to concat multiple nodes
*
* @see {@link DefaultNormalizedAstFold#concatAll|concatAll}
*/
concatAll(nodes) {
return nodes.reduce((acc, n) => this.concat(acc, n), this.empty);
}
fold(nodes) {
if (Array.isArray(nodes)) {
const n = nodes;
return this.concatAll(n.filter(n => n && n !== r_function_call_1.EmptyArgument).map(node => this.foldSingle(node)));
}
else if (nodes) {
return this.foldSingle(nodes);
}
return this.empty;
}
foldSingle(node) {
this.enter?.(node);
const type = node.type;
// @ts-expect-error -- ts may be unable to infer that the type is correct
const result = this.folds[type]?.(node);
this.exit?.(node);
return result;
}
foldRAccess(access) {
let accessed = this.foldSingle(access.accessed);
if (access.operator === '[' || access.operator === '[[') {
accessed = this.concat(accessed, this.fold(access.access));
}
return accessed;
}
foldRArgument(argument) {
return this.concat(this.fold(argument.name), this.fold(argument.value));
}
foldRBinaryOp(binaryOp) {
return this.concat(this.foldSingle(binaryOp.lhs), this.foldSingle(binaryOp.rhs));
}
foldRExpressionList(exprList) {
return this.concat(this.fold(exprList.grouping), this.fold(exprList.children));
}
foldRForLoop(loop) {
return this.concatAll([this.foldSingle(loop.variable), this.foldSingle(loop.vector), this.foldSingle(loop.body)]);
}
foldRFunctionCall(call) {
return this.concat(this.foldSingle(call.named ? call.functionName : call.calledFunction), this.fold(call.arguments));
}
foldRFunctionDefinition(definition) {
return this.concat(this.fold(definition.parameters), this.foldSingle(definition.body));
}
foldRIfThenElse(ite) {
return this.concatAll([this.foldSingle(ite.condition), this.foldSingle(ite.then), this.fold(ite.otherwise)]);
}
foldRParameter(parameter) {
return this.concat(this.foldSingle(parameter.name), this.fold(parameter.defaultValue));
}
foldRPipe(pipe) {
return this.concat(this.foldSingle(pipe.lhs), this.foldSingle(pipe.rhs));
}
foldRRepeatLoop(loop) {
return this.foldSingle(loop.body);
}
foldRUnaryOp(unaryOp) {
return this.foldSingle(unaryOp.operand);
}
foldRWhileLoop(loop) {
return this.concat(this.foldSingle(loop.condition), this.foldSingle(loop.body));
}
foldRBreak(_node) {
return this.empty;
}
foldRComment(_node) {
return this.empty;
}
foldRLineDirective(_node) {
return this.empty;
}
foldRLogical(_node) {
return this.empty;
}
foldRNext(_node) {
return this.empty;
}
foldRNumber(_node) {
return this.empty;
}
foldRString(_node) {
return this.empty;
}
foldRSymbol(_node) {
return this.empty;
}
folds = {
[type_1.RType.Access]: n => this.foldRAccess(n),
[type_1.RType.Argument]: n => this.foldRArgument(n),
[type_1.RType.BinaryOp]: n => this.foldRBinaryOp(n),
[type_1.RType.Break]: n => this.foldRBreak(n),
[type_1.RType.Comment]: n => this.foldRComment(n),
[type_1.RType.ExpressionList]: n => this.foldRExpressionList(n),
[type_1.RType.ForLoop]: n => this.foldRForLoop(n),
[type_1.RType.FunctionCall]: n => this.foldRFunctionCall(n),
[type_1.RType.FunctionDefinition]: n => this.foldRFunctionDefinition(n),
[type_1.RType.IfThenElse]: n => this.foldRIfThenElse(n),
[type_1.RType.LineDirective]: n => this.foldRLineDirective(n),
[type_1.RType.Logical]: n => this.foldRLogical(n),
[type_1.RType.Next]: n => this.foldRNext(n),
[type_1.RType.Number]: n => this.foldRNumber(n),
[type_1.RType.Parameter]: n => this.foldRParameter(n),
[type_1.RType.Pipe]: n => this.foldRPipe(n),
[type_1.RType.RepeatLoop]: n => this.foldRRepeatLoop(n),
[type_1.RType.String]: n => this.foldRString(n),
[type_1.RType.Symbol]: n => this.foldRSymbol(n),
[type_1.RType.UnaryOp]: n => this.foldRUnaryOp(n),
[type_1.RType.WhileLoop]: n => this.foldRWhileLoop(n),
};
}
exports.DefaultNormalizedAstFold = DefaultNormalizedAstFold;
//# sourceMappingURL=normalized-ast-fold.js.map