@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
135 lines • 6.79 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveNode = resolveNode;
exports.resolveAsVector = resolveAsVector;
exports.resolveAsSeq = resolveAsSeq;
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");
const r_value_1 = require("../../../util/r-value");
const interval_constants_1 = require("../values/intervals/interval-constants");
const logical_constants_1 = require("../values/logical/logical-constants");
const r_value_2 = require("../values/r-value");
const string_constants_1 = require("../values/string/string-constants");
const vector_constants_1 = require("../values/vectors/vector-constants");
const scalar_constants_1 = require("../values/scalar/scalar-constants");
const identifier_1 = require("../../environments/identifier");
const df_helper_1 = require("../../graph/df-helper");
const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id");
const resolve_helper_1 = require("../../environments/resolve-helper");
/**
* The {@link BuiltInEvalHandler} the given name resolves to in the current environment, just like the
* {@link BuiltInIdentifierDefinition#processor} a call resolves to; `undefined` if the name is shadowed
* by a user definition or its built-in declares no handler.
*/
function evalHandlerOf(name, environment, ctx) {
const defs = environment ?
resolve_helper_1.Resolve.byNameAndType(name, environment, identifier_1.ReferenceType.BuiltInFunction)
: ctx.env.builtInEnvironment.memory.get(identifier_1.Identifier.getName(name));
const def = defs?.length === 1 ? defs[0] : undefined;
return def?.type === identifier_1.ReferenceType.BuiltInFunction ? def.evalHandler : undefined;
}
/** the node types that may fold through a built-in: calls, operators, and the `(`/`{` groupings */
const EvalNodeTypes = [type_1.RType.FunctionCall, type_1.RType.BinaryOp, type_1.RType.UnaryOp, type_1.RType.ExpressionList];
/**
* The name of the built-in the given node folds through, `undefined` if it is no call to one or if it may just
* as well resolve to something else (a user redefinition, a conditional one, ...), which must not be folded.
*/
function evalNameOf({ node, graph }) {
if (graph === undefined || !EvalNodeTypes.includes(node.type)) {
return undefined;
}
let name;
for (const origin of df_helper_1.Dataflow.origin(graph, node.info.id) ?? []) {
if (origin.type === 2 /* OriginType.FunctionCallOrigin */ || origin.type === 1 /* OriginType.WriteVariableOrigin */) {
/* the call may go to a definition of the program instead, so we know too little to fold it */
return undefined;
}
else if (origin.type !== 3 /* OriginType.BuiltInFunctionOrigin */) {
continue;
}
/* an alias like `f <- c` keeps the built-in it stands for in `proc`, a direct call names itself */
const pkgFn = node_id_1.NodeId.toPkgFn(origin.proc);
const named = pkgFn !== undefined ? identifier_1.Identifier.make(pkgFn[1], pkgFn[0])
: node_id_1.NodeId.isBuiltIn(origin.proc) ? node_id_1.NodeId.fromBuiltIn(origin.proc) : origin.fn.name;
if (name !== undefined && identifier_1.Identifier.getName(name) !== identifier_1.Identifier.getName(named)) {
return undefined;
}
name = named;
}
return name;
}
/**
* Helper function used by {@link Resolve.toValue}, please use that instead, if
* you want to resolve the value of an identifier / node
*
* This function converts an RNode to its Value, either directly for a constant or by handing the node
* to the {@link BuiltInEvalHandler} its built-in declares, which may resolve further nodes recursively.
* @returns resolved value or top/bottom
*/
function resolveNode(args) {
const { node, environment, ctx } = args;
const nt = node.type;
if (nt === type_1.RType.String) {
return (0, string_constants_1.stringFrom)(node.content.str);
}
else if (nt === type_1.RType.Number) {
return (0, interval_constants_1.intervalFrom)(node.content.num, node.content.num);
}
else if (nt === type_1.RType.Logical) {
return node.content.valueOf() ? logical_constants_1.ValueLogicalTrue : logical_constants_1.ValueLogicalFalse;
}
else if (nt === type_1.RType.FunctionDefinition) {
return { type: 'function-definition' };
}
const name = evalNameOf(args);
const handler = name === undefined ? undefined : evalHandlerOf(name, environment, ctx);
return handler?.(args) ?? r_value_2.Top;
}
/**
* Helper function used by {@link Resolve.toValue}, please use that instead, if
* you want to resolve the value of an identifier / node
*
* This function resolves a vector function call `c` to a {@link ValueVector}
* by recursively resolving the values of the arguments by calling {@link Resolve.toValue}
* @returns ValueVector or Top
*/
function resolveAsVector(args) {
const node = args.node;
if (node.type !== type_1.RType.FunctionCall) {
return r_value_2.Top;
}
return (0, vector_constants_1.vectorFrom)((0, vector_constants_1.flattenVectorElements)(node.arguments.map(arg => arg !== r_function_call_1.EmptyArgument ? resolve_helper_1.Resolve.toValue(arg.value, args) : r_value_2.Top)));
}
/**
* Helper function used by {@link Resolve.toValue}, please use that instead, if
* you want to resolve the value of an identifier / node
*
* This function resolves a binary sequence operator `:` to a {@link ValueVector} of {@link ValueNumber}s
* by recursively resolving the values of the arguments by calling {@link Resolve.toValue}
* @returns ValueVector of ValueNumbers or Top
*/
function resolveAsSeq(args) {
const operator = args.node;
if (operator.type !== type_1.RType.BinaryOp) {
return r_value_2.Top;
}
const leftValue = (0, r_value_1.unliftRValue)(resolve_helper_1.Resolve.toValue(operator.lhs, args));
const rightValue = (0, r_value_1.unliftRValue)(resolve_helper_1.Resolve.toValue(operator.rhs, args));
if ((0, r_value_1.isRNumberValue)(leftValue) && (0, r_value_1.isRNumberValue)(rightValue)) {
return (0, vector_constants_1.vectorFrom)(createNumberSequence(leftValue, rightValue).map(scalar_constants_1.liftScalar));
}
return r_value_2.Top;
}
function createNumberSequence(start, end) {
const sequence = [];
const min = Math.min(start.num, end.num);
const max = Math.max(start.num, end.num);
for (let i = min; i <= max; i++) {
sequence.push({ ...start, num: i });
}
if (start > end) {
sequence.reverse();
}
return sequence;
}
//# sourceMappingURL=resolve.js.map