@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
138 lines • 6.9 kB
JavaScript
;
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