UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

178 lines 6.82 kB
"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