UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

138 lines 6.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.tryNormalizeFunctionCall = tryNormalizeFunctionCall; const parser_1 = require("../../../json/parser"); const normalize_meta_1 = require("../../normalize-meta"); const arrays_1 = require("../../../../../../../util/collections/arrays"); const assert_1 = require("../../../../../../../util/assert"); const normalize_argument_1 = require("./normalize-argument"); const r_function_call_1 = require("../../../../model/nodes/r-function-call"); const type_1 = require("../../../../model/type"); const normalize_expression_1 = require("../expression/normalize-expression"); const normalize_string_1 = require("../values/normalize-string"); const normalize_symbol_1 = require("../values/normalize-symbol"); /** * Tries to parse the given data as a function call. * @param data - The data used by the parser (see {@link NormalizerData}) * @param mappedWithName - The JSON object to extract the meta-information from * @returns The parsed {@link RFunctionCall} (either named or unnamed) or `undefined` if the given construct is not a function call * May return a {@link RNext} or {@link RBreak} as `next()` and `break()` work as such. */ function tryNormalizeFunctionCall(data, mappedWithName) { const fnBase = mappedWithName[0]; if (fnBase.name !== type_1.RawRType.Expression && fnBase.name !== type_1.RawRType.ExprOfAssignOrHelp && fnBase.name !== type_1.RawRType.LegacyEqualAssign) { parser_1.parseLog.trace(`expected function call name to be wrapped an expression, yet received ${fnBase.name}`); return undefined; } if (mappedWithName.length < 3 || mappedWithName[1].name !== type_1.RawRType.ParenLeft || mappedWithName[mappedWithName.length - 1].name !== type_1.RawRType.ParenRight) { parser_1.parseLog.trace('expected function call to have parenthesis for a call, but was not'); return undefined; } const { content, location } = (0, normalize_meta_1.retrieveMetaStructure)(fnBase.content); const symbolContent = fnBase.content.children; const namedSymbolContent = (0, normalize_meta_1.getWithTokenType)(symbolContent); if (namedSymbolContent.length === 1 && namedSymbolContent[0].name === type_1.RawRType.StringConst) { // special handling when someone calls a function by string return parseNamedFunctionCall(data, namedSymbolContent, mappedWithName, location, content); } else if (namedSymbolContent.findIndex(x => x.name === type_1.RawRType.SymbolFunctionCall) < 0) { parser_1.parseLog.trace(`is not named function call, as the name is not of type ${type_1.RType.FunctionCall}, but: ${namedSymbolContent.map(n => n.name).join(',')}`); return tryParseUnnamedFunctionCall(data, mappedWithName, location, content); } else { return parseNamedFunctionCall(data, namedSymbolContent, mappedWithName, location, content); } } function parseArguments(mappedWithName, data) { const argContainer = mappedWithName.slice(1); (0, assert_1.guard)(argContainer.length > 1 && argContainer[0].name === type_1.RawRType.ParenLeft && argContainer[argContainer.length - 1].name === type_1.RawRType.ParenRight, 'expected args in parenthesis'); const splitArgumentsOnComma = (0, arrays_1.splitArrayOn)(argContainer.slice(1, argContainer.length - 1), x => x.name === type_1.RawRType.Comma); return splitArgumentsOnComma.map(x => { parser_1.parseLog.trace('trying to parse argument'); return (0, normalize_argument_1.tryToNormalizeArgument)(data, x); }); } function tryParseUnnamedFunctionCall(data, mappedWithName, location, content) { // maybe remove symbol-content again because I just use the root expr of mapped with name if (mappedWithName.length < 3) { parser_1.parseLog.trace('expected unnamed function call to have 3 elements [like (<func>)], but was not'); return undefined; } parser_1.parseLog.trace('Assuming structure to be a function call'); // we parse an expression to allow function calls const calledFunction = (0, normalize_expression_1.normalizeExpression)(data, mappedWithName[0].content); const parsedArguments = parseArguments(mappedWithName, data); if (parsedArguments.length === 0) { // interestingly, next() and break() work if (calledFunction.type === type_1.RType.Next) { return { type: type_1.RType.Next, lexeme: content, location, info: { fullRange: data.currentRange, adToks: [], fullLexeme: data.currentLexeme } }; } else if (calledFunction.type === type_1.RType.Break) { return { type: type_1.RType.Break, lexeme: content, location, info: { fullRange: data.currentRange, adToks: [], fullLexeme: data.currentLexeme } }; } } return { type: type_1.RType.FunctionCall, named: undefined, location, lexeme: content, calledFunction: calledFunction, arguments: parsedArguments.map(x => x ?? r_function_call_1.EmptyArgument), info: { fullRange: data.currentRange, adToks: [], fullLexeme: data.currentLexeme } }; } function parseNamedFunctionCall(data, symbolContent, mappedWithName, location, content) { let functionName; if (symbolContent.length === 1 && symbolContent[0].name === type_1.RawRType.StringConst) { const stringBase = (0, normalize_string_1.normalizeString)(data, symbolContent[0].content); functionName = { type: type_1.RType.Symbol, lexeme: stringBase.lexeme, info: stringBase.info, location: stringBase.location, content: stringBase.content.str }; } else { functionName = (0, normalize_symbol_1.tryNormalizeSymbol)(data, symbolContent); } (0, assert_1.guard)(functionName !== undefined, 'expected function name to be a symbol, yet received none'); (0, assert_1.guard)(functionName.type === type_1.RType.Symbol, () => `expected function name to be a symbol, yet received ${JSON.stringify(functionName)}`); const parsedArguments = parseArguments(mappedWithName, data); return { type: type_1.RType.FunctionCall, named: true, location, lexeme: content, functionName, arguments: parsedArguments.map(x => x ?? r_function_call_1.EmptyArgument), info: { fullRange: data.currentRange, adToks: [], fullLexeme: data.currentLexeme } }; } //# sourceMappingURL=normalize-call.js.map