diff --git a/src/r-bridge/lang-4.x/ast/model/model.ts b/src/r-bridge/lang-4.x/ast/model/model.ts index 78e08db983..d9111b893a 100644 --- a/src/r-bridge/lang-4.x/ast/model/model.ts +++ b/src/r-bridge/lang-4.x/ast/model/model.ts @@ -31,7 +31,7 @@ export type NoInfo = object; * Will be used to reconstruct the source of the given element in the R-ast. * This will not be part of most comparisons as it is mainly of interest to the reconstruction of R code. */ -interface Source { +export interface Source { /** * The range is different from the assigned {@link Location} as it refers to the complete source range covered by the given * element. diff --git a/src/r-bridge/lang-4.x/ast/model/processing/decorate.ts b/src/r-bridge/lang-4.x/ast/model/processing/decorate.ts index 26ac2dbcb2..b15e5da13c 100644 --- a/src/r-bridge/lang-4.x/ast/model/processing/decorate.ts +++ b/src/r-bridge/lang-4.x/ast/model/processing/decorate.ts @@ -9,7 +9,7 @@ * @module */ -import type { NoInfo, RNode } from '../model'; +import type { NoInfo, RNode, Source } from '../model'; import { guard } from '../../../../../util/assert'; import type { SourceRange } from '../../../../../util/range'; import { BiMap } from '../../../../../util/collections/bimap'; @@ -463,3 +463,73 @@ function createFoldForFunctionArgument(info: FoldInfo) { return decorated; }; } + + +export function mapAstInfo(ast: RNode, down: Down, infoMapper: (node: RNode, down: Down) => NewInfo, downUpdater: (node: RNode, down: Down) => Down = (_node, down) => down): RNode { + const fullInfoMapper = (node: RNode, down: Down): NewInfo & Source => { + const sourceInfo = { + ...(node.info.fullRange !== undefined ? { fullRange: node.info.fullRange } : {}), + ...(node.info.fullLexeme !== undefined ? { fullLexeme: node.info.fullLexeme } : {}), + ...(node.info.additionalTokens !== undefined ? { additionalTokens: node.info.additionalTokens } : {}), + ...(node.info.file !== undefined ? { file: node.info.file } : {}) + }; + const mappedInfo = infoMapper(node, down); + return { ...sourceInfo, ...mappedInfo }; + }; + + function updateInfo(n: RNode, down: Down): RNode { + (n.info as NewInfo) = fullInfoMapper(n, down); + return n as unknown as RNode; + } + + return foldAstStateful(ast, down, { + down: downUpdater, + foldNumber: updateInfo, + foldString: updateInfo, + foldLogical: updateInfo, + foldSymbol: updateInfo, + foldAccess: (node, _name, _access, down) => updateInfo(node, down), + foldBinaryOp: (op, _lhs, _rhs, down) => updateInfo(op, down), + foldPipe: (op, _lhs, _rhs, down) => updateInfo(op, down), + foldUnaryOp: (op, _operand, down) => updateInfo(op, down), + loop: { + foldFor: (loop, _variable, _vector, _body, down) => updateInfo(loop, down), + foldWhile: (loop, _condition, _body, down) => updateInfo(loop, down), + foldRepeat: (loop, _body, down) => updateInfo(loop, down), + foldNext: (next, down) => updateInfo(next, down), + foldBreak: (next, down) => updateInfo(next, down), + }, + other: { + foldComment: (comment, down) => updateInfo(comment, down), + foldLineDirective: (comment, down) => updateInfo(comment, down), + }, + foldIfThenElse: (ifThenExpr, _condition, _then, _otherwise, down ) => + updateInfo(ifThenExpr, down), + foldExprList: (exprList, _grouping, _expressions, down) => updateInfo(exprList, down), + functions: { + foldFunctionDefinition: (definition, _parameters, _body, down) => updateInfo(definition, down), + /** folds named and unnamed function calls */ + foldFunctionCall: (call, _functionNameOrExpression, _args, down) => updateInfo(call, down), + /** The `name` is `undefined` if the argument is unnamed, the value, if we have something like `x=,...` */ + foldArgument: (argument, _name, _value, down) => updateInfo(argument, down), + /** The `defaultValue` is `undefined` if the argument was not initialized with a default value */ + foldParameter: (parameter, _name, _defaultValue, down) => updateInfo(parameter, down) + } + }); +} + +export function mapNormalizedAstInfo(normalizedAst: NormalizedAst, down: Down, infoMapper: (node: RNode, down: Down) => NewInfo, downUpdater: (node: RNode, down: Down) => Down = (_node, down) => down): NormalizedAst { + const parentInfoPreservingMapper = (node: RNode, down: Down): NewInfo & ParentInformation => { + const parentInfo = { + id: node.info.id, + parent: node.info.parent, + role: node.info.role, + nesting: node.info.nesting, + index: node.info.index + }; + const mappedInfo = infoMapper(node, down); + return { ...parentInfo, ...mappedInfo }; + }; + mapAstInfo(normalizedAst.ast, down, parentInfoPreservingMapper, downUpdater); + return normalizedAst as unknown as NormalizedAst; +} \ No newline at end of file