@eagleoutice/flowr 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/benchmark/slicer.js +30 -5
- package/benchmark/stats/print.js +28 -9
- package/benchmark/stats/stats.d.ts +5 -0
- package/benchmark/summarizer/data.d.ts +10 -1
- package/benchmark/summarizer/first-phase/process.d.ts +1 -1
- package/benchmark/summarizer/first-phase/process.js +83 -30
- package/benchmark/summarizer/second-phase/process.js +20 -4
- package/cli/repl/commands/commands.js +19 -1
- package/cli/slicer-app.js +1 -1
- package/dataflow/environments/append.js +1 -2
- package/dataflow/environments/built-in.js +2 -1
- package/dataflow/environments/clone.js +1 -1
- package/dataflow/environments/diff.d.ts +1 -1
- package/dataflow/environments/diff.js +16 -18
- package/dataflow/environments/environment.d.ts +4 -7
- package/dataflow/environments/environment.js +5 -8
- package/dataflow/environments/identifier.d.ts +2 -1
- package/dataflow/environments/overwrite.js +1 -2
- package/dataflow/environments/scoping.js +1 -1
- package/dataflow/graph/diff.js +11 -6
- package/dataflow/graph/graph.d.ts +6 -2
- package/dataflow/graph/graph.js +13 -7
- package/dataflow/graph/vertex.d.ts +2 -1
- package/dataflow/info.d.ts +10 -1
- package/dataflow/info.js +54 -2
- package/dataflow/internal/linker.d.ts +1 -1
- package/dataflow/internal/linker.js +1 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -5
- package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +21 -25
- package/dataflow/internal/process/functions/call/built-in/built-in-get.js +6 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +10 -8
- package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.d.ts +1 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.js +1 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +1 -1
- package/dataflow/internal/process/functions/call/default-call-handling.js +1 -1
- package/dataflow/internal/process/functions/call/unnamed-call-handling.js +1 -1
- package/dataflow/processor.d.ts +2 -3
- package/package.json +4 -2
- package/r-bridge/data/data.d.ts +1 -1
- package/r-bridge/data/data.js +1 -1
- package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/operators.js +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/decorate.js +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/stateful-fold.js +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/visitor.js +2 -2
- package/r-bridge/lang-4.x/ast/parser/xml/internal/functions/normalize-call.js +2 -2
- package/r-bridge/lang-4.x/ast/parser/xml/internal/operators/normalize-binary.js +1 -1
- package/r-bridge/retriever.d.ts +1 -1
- package/r-bridge/retriever.js +3 -2
- package/r-bridge/shell.js +4 -3
- package/reconstruct/reconstruct.d.ts +3 -3
- package/reconstruct/reconstruct.js +40 -41
- package/slicing/criterion/filters/all-variables.js +1 -1
- package/slicing/static/static-slicer.js +2 -2
- package/statistics/features/common-syntax-probability.js +1 -1
- package/statistics/features/supported/control-flow/control-flow.js +1 -1
- package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
- package/statistics/features/supported/loops/loops.js +1 -1
- package/statistics/features/supported/used-functions/used-functions.js +1 -1
- package/util/assert.d.ts +1 -1
- package/util/mermaid/ast.js +4 -0
- package/util/mermaid/dfg.js +15 -5
- package/util/mermaid/mermaid.js +21 -1
- package/util/version.js +1 -1
|
@@ -24,12 +24,17 @@ function processGet(name, args, rootId, data) {
|
|
|
24
24
|
location: retrieve.location,
|
|
25
25
|
namespace: undefined
|
|
26
26
|
};
|
|
27
|
-
const { information } = (0, known_call_handling_1.processKnownFunctionCall)({
|
|
27
|
+
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({
|
|
28
28
|
name,
|
|
29
29
|
args: (0, make_argument_1.wrapArgumentsUnnamed)([treatTargetAsSymbol], data.completeAst.idMap),
|
|
30
30
|
rootId,
|
|
31
31
|
data
|
|
32
32
|
});
|
|
33
|
+
const firstArg = processedArguments[0];
|
|
34
|
+
if (firstArg) {
|
|
35
|
+
// get 'reads' its first argument
|
|
36
|
+
information.graph.addEdge(rootId, firstArg.entryPoint, { type: 1 /* EdgeType.Reads */ });
|
|
37
|
+
}
|
|
33
38
|
return information;
|
|
34
39
|
}
|
|
35
40
|
exports.processGet = processGet;
|
|
@@ -57,21 +57,23 @@ function processIfThenElse(name, args, rootId, data) {
|
|
|
57
57
|
const thenEnvironment = then?.environment ?? cond.environment;
|
|
58
58
|
// if there is no "else" case, we have to recover whatever we had before as it may be not executed
|
|
59
59
|
const finalEnvironment = (0, append_1.appendEnvironment)(thenEnvironment, otherwise ? otherwise.environment : cond.environment);
|
|
60
|
+
const cdTrue = { id: rootId, when: true };
|
|
61
|
+
const cdFalse = { id: rootId, when: false };
|
|
60
62
|
// again within an if-then-else we consider all actives to be read
|
|
61
63
|
const ingoing = [
|
|
62
64
|
...cond.in,
|
|
63
|
-
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.in, nextGraph, finalEnvironment, false,
|
|
64
|
-
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.in, nextGraph, finalEnvironment, false,
|
|
65
|
+
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.in, nextGraph, finalEnvironment, false, cdTrue) : then?.in ?? []),
|
|
66
|
+
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.in, nextGraph, finalEnvironment, false, cdFalse) : otherwise?.in ?? []),
|
|
65
67
|
...cond.unknownReferences,
|
|
66
|
-
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.unknownReferences, nextGraph, finalEnvironment, false,
|
|
67
|
-
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.unknownReferences, nextGraph, finalEnvironment, false,
|
|
68
|
+
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.unknownReferences, nextGraph, finalEnvironment, false, cdTrue) : then?.unknownReferences ?? []),
|
|
69
|
+
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.unknownReferences, nextGraph, finalEnvironment, false, cdFalse) : otherwise?.unknownReferences ?? []),
|
|
68
70
|
];
|
|
69
71
|
// we assign all with a maybe marker
|
|
70
72
|
// we do not merge even if they appear in both branches because the maybe links will refer to different ids
|
|
71
73
|
const outgoing = [
|
|
72
74
|
...cond.out,
|
|
73
|
-
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.out, nextGraph, finalEnvironment, true,
|
|
74
|
-
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.out, nextGraph, finalEnvironment, true,
|
|
75
|
+
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.out, nextGraph, finalEnvironment, true, cdTrue) : then?.out ?? []),
|
|
76
|
+
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.out, nextGraph, finalEnvironment, true, cdFalse) : otherwise?.out ?? []),
|
|
75
77
|
];
|
|
76
78
|
(0, common_1.patchFunctionCall)({
|
|
77
79
|
nextGraph,
|
|
@@ -83,8 +85,8 @@ function processIfThenElse(name, args, rootId, data) {
|
|
|
83
85
|
// as an if always evaluates its condition, we add a 'reads'-edge
|
|
84
86
|
nextGraph.addEdge(name.info.id, cond.entryPoint, { type: 1 /* EdgeType.Reads */ });
|
|
85
87
|
const exitPoints = [
|
|
86
|
-
...(then?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeThenMaybe ? [...data.controlDependencies ?? []] : e.controlDependencies })),
|
|
87
|
-
...(otherwise?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeOtherwiseMaybe ? [...data.controlDependencies ?? []] : e.controlDependencies }))
|
|
88
|
+
...(then?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeThenMaybe ? [...data.controlDependencies ?? [], { id: rootId, when: true }] : e.controlDependencies })),
|
|
89
|
+
...(otherwise?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeOtherwiseMaybe ? [...data.controlDependencies ?? [], { id: rootId, when: false }] : e.controlDependencies }))
|
|
88
90
|
];
|
|
89
91
|
return {
|
|
90
92
|
unknownReferences: [],
|
|
@@ -6,4 +6,5 @@ import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/node
|
|
|
6
6
|
import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
7
7
|
export declare function processSpecialBinOp<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
|
|
8
8
|
lazy: boolean;
|
|
9
|
+
evalRhsWhen: boolean;
|
|
9
10
|
}): DataflowInformation;
|
|
@@ -14,8 +14,7 @@ function processSpecialBinOp(name, args, rootId, data, config) {
|
|
|
14
14
|
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data,
|
|
15
15
|
patchData: (d, i) => {
|
|
16
16
|
if (i === 1) {
|
|
17
|
-
|
|
18
|
-
return { ...d, controlDependencies: [...d.controlDependencies ?? [], name.info.id] };
|
|
17
|
+
return { ...d, controlDependencies: [...d.controlDependencies ?? [], { id: name.info.id, when: config.evalRhsWhen }] };
|
|
19
18
|
}
|
|
20
19
|
return d;
|
|
21
20
|
}
|
|
@@ -28,7 +28,7 @@ function processWhileLoop(name, args, rootId, data) {
|
|
|
28
28
|
markAsNSE: [1],
|
|
29
29
|
patchData: (d, i) => {
|
|
30
30
|
if (i === 1) {
|
|
31
|
-
return { ...d, controlDependencies: [...d.controlDependencies ?? [], name.info.id] };
|
|
31
|
+
return { ...d, controlDependencies: [...d.controlDependencies ?? [], { id: name.info.id, when: true }] };
|
|
32
32
|
}
|
|
33
33
|
return d;
|
|
34
34
|
}
|
|
@@ -4,7 +4,7 @@ exports.processFunctionCall = void 0;
|
|
|
4
4
|
const named_call_handling_1 = require("./named-call-handling");
|
|
5
5
|
const unnamed_call_handling_1 = require("./unnamed-call-handling");
|
|
6
6
|
function processFunctionCall(functionCall, data) {
|
|
7
|
-
if (functionCall.
|
|
7
|
+
if (functionCall.named) {
|
|
8
8
|
return (0, named_call_handling_1.processNamedCall)(functionCall.functionName, functionCall.arguments, functionCall.info.id, data);
|
|
9
9
|
}
|
|
10
10
|
else {
|
package/dataflow/processor.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Based on a two-way fold, this processor will automatically supply scope information
|
|
3
3
|
*/
|
|
4
|
-
import type { DataflowInformation } from './info';
|
|
4
|
+
import type { ControlDependency, DataflowInformation } from './info';
|
|
5
5
|
import type { NormalizedAst, ParentInformation, RNodeWithParent } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
6
6
|
import type { REnvironmentInformation } from './environments/environment';
|
|
7
7
|
import type { RParseRequest } from '../r-bridge/retriever';
|
|
8
|
-
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
9
8
|
import type { RNode } from '../r-bridge/lang-4.x/ast/model/model';
|
|
10
9
|
export interface DataflowProcessorInformation<OtherInfo> {
|
|
11
10
|
/**
|
|
@@ -33,7 +32,7 @@ export interface DataflowProcessorInformation<OtherInfo> {
|
|
|
33
32
|
/**
|
|
34
33
|
* The chain of control-flow {@link NodeId}s that lead to the current node (e.g. of known ifs).
|
|
35
34
|
*/
|
|
36
|
-
readonly controlDependencies:
|
|
35
|
+
readonly controlDependencies: ControlDependency[] | undefined;
|
|
37
36
|
}
|
|
38
37
|
export type DataflowProcessor<OtherInfo, NodeType extends RNodeWithParent<OtherInfo>> = (node: NodeType, data: DataflowProcessorInformation<OtherInfo>) => DataflowInformation;
|
|
39
38
|
type NodeWithKey<OtherInfo, Key> = RNode<OtherInfo & ParentInformation> & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
|
|
5
5
|
"types": "dist/src/index.d.ts",
|
|
6
6
|
"repository": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"url": "https://github.com/Code-Inspect/flowr/issues"
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"main": "npm run build && node dist/src/cli/flowr.js",
|
|
15
|
+
"main": "npm run build:bundle-flowr && node dist/src/cli/flowr.min.js",
|
|
16
16
|
"publish-library": "cp .npmignore package.json README.md LICENSE dist/src/ && cd dist/src && npm publish --access public",
|
|
17
17
|
"release": "npx release-it --ci",
|
|
18
18
|
"stats": "ts-node src/cli/statistics-app.ts",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"export-quads": "ts-node src/cli/export-quads-app.ts",
|
|
25
25
|
"capabilities-markdown": "ts-node src/r-bridge/data/print.ts",
|
|
26
26
|
"build": "tsc --project .",
|
|
27
|
+
"build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --bundle --minify --target=node18 --outfile=dist/src/cli/flowr.min.js",
|
|
27
28
|
"lint-local": "npx eslint --version && npx eslint src/ test/ --rule \"no-warning-comments: off\"",
|
|
28
29
|
"lint": "npm run license-compat -- --summary && npx eslint --version && npx eslint src/ test/",
|
|
29
30
|
"license-compat": "license-checker --onlyAllow 'MIT;MIT OR X11;GPLv2;LGPL;GNUGPL;ISC;Apache-2.0;FreeBSD;BSD-2-Clause;clearbsd;ModifiedBSD;BSD-3-Clause;Python-2.0;Unlicense;WTFPL;BlueOak-1.0.0;CC-BY-4.0;CC-BY-3.0;CC0-1.0;0BSD'",
|
|
@@ -200,6 +201,7 @@
|
|
|
200
201
|
"@typescript-eslint/eslint-plugin": "^7.8.0",
|
|
201
202
|
"chai": "^4.3.16",
|
|
202
203
|
"chai-as-promised": "^7.1.1",
|
|
204
|
+
"esbuild": "0.21.3",
|
|
203
205
|
"eslint": "^8.57.0",
|
|
204
206
|
"eslint-plugin-check-file": "^2.8.0",
|
|
205
207
|
"eslint-plugin-import": "^2.29.1",
|
package/r-bridge/data/data.d.ts
CHANGED
|
@@ -373,7 +373,7 @@ export declare const flowrCapabilities: {
|
|
|
373
373
|
readonly name: "Sequencing";
|
|
374
374
|
readonly id: "built-in-sequencing";
|
|
375
375
|
readonly supported: "not";
|
|
376
|
-
readonly description: "_Handle `:`, `seq`, ...
|
|
376
|
+
readonly description: "_Handle `:`, `seq`, ... by gathering value information using abstract interpretation._";
|
|
377
377
|
}, {
|
|
378
378
|
readonly name: "Internal and Primitive Functions";
|
|
379
379
|
readonly id: "built-in-internal-and-primitive-functions";
|
package/r-bridge/data/data.js
CHANGED
|
@@ -464,7 +464,7 @@ exports.flowrCapabilities = {
|
|
|
464
464
|
name: 'Sequencing',
|
|
465
465
|
id: 'built-in-sequencing',
|
|
466
466
|
supported: 'not',
|
|
467
|
-
description: '_Handle `:`, `seq`, ...
|
|
467
|
+
description: '_Handle `:`, `seq`, ... by gathering value information using abstract interpretation._'
|
|
468
468
|
},
|
|
469
469
|
{
|
|
470
470
|
name: 'Internal and Primitive Functions',
|
|
@@ -11,7 +11,7 @@ export type RFunctionArgument<Info = NoInfo> = RArgument<Info> | typeof EmptyArg
|
|
|
11
11
|
*/
|
|
12
12
|
export interface RNamedFunctionCall<Info = NoInfo> extends Base<Info>, Location {
|
|
13
13
|
readonly type: RType.FunctionCall;
|
|
14
|
-
readonly
|
|
14
|
+
readonly named: true;
|
|
15
15
|
functionName: RSymbol<Info>;
|
|
16
16
|
/** arguments can be empty, for example when calling as `a(1, ,3)` */
|
|
17
17
|
readonly arguments: readonly RFunctionArgument<Info>[];
|
|
@@ -23,7 +23,7 @@ export interface RNamedFunctionCall<Info = NoInfo> extends Base<Info>, Location
|
|
|
23
23
|
*/
|
|
24
24
|
export interface RUnnamedFunctionCall<Info = NoInfo> extends Base<Info>, Location {
|
|
25
25
|
readonly type: RType.FunctionCall;
|
|
26
|
-
readonly
|
|
26
|
+
readonly named: false | undefined;
|
|
27
27
|
calledFunction: RNode<Info>;
|
|
28
28
|
/** marks function calls like `3 %xx% 4` which have been written in special infix notation; deprecated in v2 */
|
|
29
29
|
infixSpecial?: boolean;
|
|
@@ -41,7 +41,7 @@ exports.OperatorDatabase = {
|
|
|
41
41
|
'=': { name: 'equal assignment', stringUsedInRAst: "EQ_ASSIGN" /* RawRType.EqualAssign */, stringUsedInternally: '=', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'local-equal-assignment', 'function-calls'] },
|
|
42
42
|
/* others */
|
|
43
43
|
/* maybe introduce custom in-r-ast flavor for these? we consider it arithmetic, as it works on numbers => if we change this we have to create custom tests! (with arithmetic, there is the automatic test set) */
|
|
44
|
-
':': { name: 'sequence operator', stringUsedInRAst: ":" /* RawRType.Colon */, stringUsedInternally: ':', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'operation', capabilities: ['binary-operator', 'infix-calls', 'function-calls'
|
|
44
|
+
':': { name: 'sequence operator', stringUsedInRAst: ":" /* RawRType.Colon */, stringUsedInternally: ':', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'operation', capabilities: ['binary-operator', 'infix-calls', 'function-calls'] },
|
|
45
45
|
'?': { name: 'question', stringUsedInRAst: "?" /* RawRType.Question */, stringUsedInternally: '?', writtenAs: 'prefix', arity: 1 /* OperatorArity.Unary */, usedAs: 'operation', capabilities: ['unary-operator', 'built-in-help'] }
|
|
46
46
|
};
|
|
47
47
|
/* eslint-enable */
|
|
@@ -269,7 +269,7 @@ function createFoldForFunctionCall(info) {
|
|
|
269
269
|
return (data, functionName, args, depth) => {
|
|
270
270
|
const id = info.getId(data);
|
|
271
271
|
let decorated;
|
|
272
|
-
if (data.
|
|
272
|
+
if (data.named) {
|
|
273
273
|
decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, functionName, arguments: args };
|
|
274
274
|
}
|
|
275
275
|
else {
|
|
@@ -37,7 +37,7 @@ function foldAstStateful(ast, down, folds) {
|
|
|
37
37
|
case "RRepeatLoop" /* RType.RepeatLoop */:
|
|
38
38
|
return folds.loop.foldRepeat(ast, foldAstStateful(ast.body, down, folds), down);
|
|
39
39
|
case "RFunctionCall" /* RType.FunctionCall */:
|
|
40
|
-
return folds.functions.foldFunctionCall(ast, foldAstStateful(ast.
|
|
40
|
+
return folds.functions.foldFunctionCall(ast, foldAstStateful(ast.named ? ast.functionName : ast.calledFunction, down, folds), ast.arguments.map(param => param === r_function_call_1.EmptyArgument ? param : foldAstStateful(param, down, folds)), down);
|
|
41
41
|
case "RFunctionDefinition" /* RType.FunctionDefinition */:
|
|
42
42
|
return folds.functions.foldFunctionDefinition(ast, ast.parameters.map(param => foldAstStateful(param, down, folds)), foldAstStateful(ast.body, down, folds), down);
|
|
43
43
|
case "RParameter" /* RType.Parameter */:
|
|
@@ -15,11 +15,11 @@ class NodeVisitor {
|
|
|
15
15
|
if (this.onEnter?.(node)) {
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
/* let the type system know
|
|
18
|
+
/* let the type system know that the type does not change */
|
|
19
19
|
const type = node.type;
|
|
20
20
|
switch (type) {
|
|
21
21
|
case "RFunctionCall" /* RType.FunctionCall */:
|
|
22
|
-
this.visitSingle(node.
|
|
22
|
+
this.visitSingle(node.named ? node.functionName : node.calledFunction);
|
|
23
23
|
this.visit(node.arguments);
|
|
24
24
|
break;
|
|
25
25
|
case "RFunctionDefinition" /* RType.FunctionDefinition */:
|
|
@@ -96,7 +96,7 @@ function tryParseUnnamedFunctionCall(data, mappedWithName, location, content) {
|
|
|
96
96
|
}
|
|
97
97
|
return {
|
|
98
98
|
type: "RFunctionCall" /* RType.FunctionCall */,
|
|
99
|
-
|
|
99
|
+
named: undefined,
|
|
100
100
|
location,
|
|
101
101
|
lexeme: content,
|
|
102
102
|
calledFunction: calledFunction,
|
|
@@ -129,7 +129,7 @@ function parseNamedFunctionCall(data, symbolContent, mappedWithName, location, c
|
|
|
129
129
|
const parsedArguments = parseArguments(mappedWithName, data);
|
|
130
130
|
return {
|
|
131
131
|
type: "RFunctionCall" /* RType.FunctionCall */,
|
|
132
|
-
|
|
132
|
+
named: true,
|
|
133
133
|
location,
|
|
134
134
|
lexeme: content,
|
|
135
135
|
functionName,
|
|
@@ -37,7 +37,7 @@ function parseBinaryOp(data, lhs, operator, rhs) {
|
|
|
37
37
|
// parse as infix function call!
|
|
38
38
|
return {
|
|
39
39
|
type: "RFunctionCall" /* RType.FunctionCall */,
|
|
40
|
-
|
|
40
|
+
named: true,
|
|
41
41
|
infixSpecial: true,
|
|
42
42
|
lexeme: data.currentLexeme ?? content,
|
|
43
43
|
location,
|
package/r-bridge/retriever.d.ts
CHANGED
|
@@ -49,4 +49,4 @@ export declare function removeRQuotes(str: string): string;
|
|
|
49
49
|
/**
|
|
50
50
|
* Needs to be called *after* {@link retrieveParseDataFromRCode} (or {@link retrieveNormalizedAstFromRCode})
|
|
51
51
|
*/
|
|
52
|
-
export declare function retrieveNumberOfRTokensOfLastParse(shell: RShell): Promise<number>;
|
|
52
|
+
export declare function retrieveNumberOfRTokensOfLastParse(shell: RShell, ignoreComments?: boolean): Promise<number>;
|
package/r-bridge/retriever.js
CHANGED
|
@@ -95,8 +95,9 @@ exports.removeRQuotes = removeRQuotes;
|
|
|
95
95
|
/**
|
|
96
96
|
* Needs to be called *after* {@link retrieveParseDataFromRCode} (or {@link retrieveNormalizedAstFromRCode})
|
|
97
97
|
*/
|
|
98
|
-
async function retrieveNumberOfRTokensOfLastParse(shell) {
|
|
99
|
-
const
|
|
98
|
+
async function retrieveNumberOfRTokensOfLastParse(shell, ignoreComments = false) {
|
|
99
|
+
const rows = ignoreComments ? `flowr_output[flowr_output$token != "${"COMMENT" /* RawRType.Comment */}", ]` : 'flowr_output';
|
|
100
|
+
const result = await shell.sendCommandWithOutput(`cat(nrow(${rows}),${(0, convert_values_1.ts2r)(shell.options.eol)})`);
|
|
100
101
|
(0, assert_1.guard)(result.length === 1, () => `expected exactly one line to obtain the number of R tokens, but got: ${JSON.stringify(result)}`);
|
|
101
102
|
return Number(result[0]);
|
|
102
103
|
}
|
package/r-bridge/shell.js
CHANGED
|
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.RShell = exports.DEFAULT_R_SHELL_OPTIONS = exports.DEFAULT_R_SHELL_EXEC_OPTIONS = exports.DEFAULT_R_PATH = exports.DEFAULT_OUTPUT_COLLECTOR_CONFIGURATION = void 0;
|
|
30
|
-
const
|
|
30
|
+
const child_process_1 = require("child_process");
|
|
31
31
|
const objects_1 = require("../util/objects");
|
|
32
32
|
const readline = __importStar(require("readline"));
|
|
33
33
|
const log_1 = require("../util/log");
|
|
@@ -196,7 +196,8 @@ class RShell {
|
|
|
196
196
|
*/
|
|
197
197
|
clearEnvironment() {
|
|
198
198
|
this.log.debug('clearing environment');
|
|
199
|
-
|
|
199
|
+
// run rm(list=ls()) but ignore 'flowr_get_ast', which is the compile command installed
|
|
200
|
+
this._sendCommand('rm(list=setdiff(ls(), "flowr_get_ast"))');
|
|
200
201
|
}
|
|
201
202
|
/**
|
|
202
203
|
* Obtain the temporary directory used by R.
|
|
@@ -231,7 +232,7 @@ class RShellSession {
|
|
|
231
232
|
options;
|
|
232
233
|
collectionTimeout;
|
|
233
234
|
constructor(options, log) {
|
|
234
|
-
this.bareSession = (0,
|
|
235
|
+
this.bareSession = (0, child_process_1.spawn)(options.pathToRExecutable, options.commandLineOptions, {
|
|
235
236
|
env: options.env,
|
|
236
237
|
cwd: options.cwd,
|
|
237
238
|
windowsHide: true
|
|
@@ -14,8 +14,8 @@ export declare function doNotAutoSelect(_node: RNode<ParentInformation>): boolea
|
|
|
14
14
|
export declare function autoSelectLibrary(node: RNode<ParentInformation>): boolean;
|
|
15
15
|
export interface ReconstructionResult {
|
|
16
16
|
code: string;
|
|
17
|
-
/** number of nodes that triggered the `autoSelectIf` predicate {@link reconstructToCode} */
|
|
18
|
-
|
|
17
|
+
/** number of lines that contain nodes that triggered the `autoSelectIf` predicate {@link reconstructToCode} */
|
|
18
|
+
linesWithAutoSelected: number;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Reconstructs parts of a normalized R ast into R code on an expression basis.
|
|
@@ -24,7 +24,7 @@ export interface ReconstructionResult {
|
|
|
24
24
|
* @param selection - The selection of nodes to be reconstructed (probably the {@link NodeId|NodeIds} identified by the slicer)
|
|
25
25
|
* @param autoSelectIf - A predicate that can be used to force the reconstruction of a node (for example to reconstruct library call statements, see {@link autoSelectLibrary}, {@link doNotAutoSelect})
|
|
26
26
|
*
|
|
27
|
-
* @returns The number of
|
|
27
|
+
* @returns The number of lines for which `autoSelectIf` triggered, as well as the reconstructed code itself.
|
|
28
28
|
*/
|
|
29
29
|
export declare function reconstructToCode<Info>(ast: NormalizedAst<Info>, selection: Selection, autoSelectIf?: AutoSelectPredicate): ReconstructionResult;
|
|
30
30
|
export {};
|
|
@@ -129,31 +129,36 @@ function reconstructForLoop(loop, variable, vector, body, config) {
|
|
|
129
129
|
];
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return body;
|
|
132
|
+
function reconstructBodyWithHeader(header, body, onEmpty) {
|
|
133
|
+
if (body.length === 0) {
|
|
134
|
+
return [{ line: `${header.line}${onEmpty}`, indent: header.indent }];
|
|
136
135
|
}
|
|
137
|
-
else if (body.length
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
else if (body.length === 1) {
|
|
137
|
+
return [
|
|
138
|
+
{ line: `${header.line} ${body[0].line}`, indent: header.indent }
|
|
139
|
+
];
|
|
140
140
|
}
|
|
141
141
|
else if (body[0].line === '{' && body[body.length - 1].line === '}') {
|
|
142
|
-
// 'block'
|
|
143
142
|
return [
|
|
144
|
-
{ line:
|
|
143
|
+
{ line: `${header.line} {`, indent: header.indent },
|
|
145
144
|
...body.slice(1, body.length - 1),
|
|
146
|
-
{ line: '}', indent:
|
|
145
|
+
{ line: '}', indent: header.indent }
|
|
147
146
|
];
|
|
148
147
|
}
|
|
149
148
|
else {
|
|
150
|
-
// unknown
|
|
151
149
|
return [
|
|
152
|
-
|
|
150
|
+
header,
|
|
153
151
|
...indentBy(body, 1)
|
|
154
152
|
];
|
|
155
153
|
}
|
|
156
154
|
}
|
|
155
|
+
function reconstructRepeatLoop(loop, body, configuration) {
|
|
156
|
+
const sel = isSelected(configuration, loop);
|
|
157
|
+
if (!sel) {
|
|
158
|
+
return body;
|
|
159
|
+
}
|
|
160
|
+
return reconstructBodyWithHeader({ line: 'repeat', indent: 0 }, body, '{}');
|
|
161
|
+
}
|
|
157
162
|
function reconstructIfThenElse(ifThenElse, condition, then, otherwise, config) {
|
|
158
163
|
otherwise ??= [];
|
|
159
164
|
if (then.length === 0 && otherwise.length === 0) {
|
|
@@ -169,10 +174,7 @@ function reconstructIfThenElse(ifThenElse, condition, then, otherwise, config) {
|
|
|
169
174
|
}
|
|
170
175
|
else if (otherwise.length === 0) {
|
|
171
176
|
if (isSelected(config, ifThenElse)) {
|
|
172
|
-
return
|
|
173
|
-
{ line: `if(${getLexeme(ifThenElse.condition)}) ${then[0].line}`, indent: 0 },
|
|
174
|
-
...indentBy(then.splice(1), 1)
|
|
175
|
-
];
|
|
177
|
+
return reconstructBodyWithHeader({ line: `if(${getLexeme(ifThenElse.condition)})`, indent: 0 }, then, '{}');
|
|
176
178
|
}
|
|
177
179
|
else {
|
|
178
180
|
return then;
|
|
@@ -180,10 +182,7 @@ function reconstructIfThenElse(ifThenElse, condition, then, otherwise, config) {
|
|
|
180
182
|
}
|
|
181
183
|
else if (then.length === 0) {
|
|
182
184
|
if (isSelected(config, ifThenElse)) {
|
|
183
|
-
return
|
|
184
|
-
{ line: `if(${getLexeme(ifThenElse.condition)}) { } else ${otherwise[0].line}`, indent: 0 },
|
|
185
|
-
...indentBy(otherwise.splice(1), 1)
|
|
186
|
-
];
|
|
185
|
+
return reconstructBodyWithHeader({ line: `if(${getLexeme(ifThenElse.condition)}) { } else`, indent: 0 }, then, '{}');
|
|
187
186
|
}
|
|
188
187
|
else {
|
|
189
188
|
return otherwise;
|
|
@@ -283,7 +282,7 @@ function reconstructFunctionDefinition(definition, functionParameters, body, con
|
|
|
283
282
|
const empty = body === undefined || body.length === 0;
|
|
284
283
|
const selected = isSelected(config, definition);
|
|
285
284
|
if (empty && selected) { // give function stub
|
|
286
|
-
return plain(
|
|
285
|
+
return plain(`${definition.lexeme}(${reconstructParameters(definition.parameters).join(', ')}) { }`);
|
|
287
286
|
}
|
|
288
287
|
else if (!selected) { // do not require function
|
|
289
288
|
return body;
|
|
@@ -294,25 +293,25 @@ function reconstructFunctionDefinition(definition, functionParameters, body, con
|
|
|
294
293
|
// 'inline'
|
|
295
294
|
const bodyStr = body.length === 0 ? '{ }' : `${body[0].line}`;
|
|
296
295
|
// we keep the braces in every case because I do not like no-brace functions
|
|
297
|
-
return [{ line:
|
|
296
|
+
return [{ line: `${definition.lexeme}(${parameters}) ${bodyStr}`, indent: 0 }];
|
|
298
297
|
}
|
|
299
298
|
else {
|
|
300
299
|
// 'block'
|
|
301
300
|
return [
|
|
302
|
-
{ line:
|
|
301
|
+
{ line: `${definition.lexeme}(${parameters}) ${body[0].line}`, indent: 0 },
|
|
303
302
|
...body.slice(1),
|
|
304
303
|
];
|
|
305
304
|
}
|
|
306
305
|
}
|
|
307
306
|
function reconstructSpecialInfixFunctionCall(args, call) {
|
|
308
307
|
(0, assert_1.guard)(args.length === 2, () => `infix special call must have exactly two arguments, got: ${args.length} (${JSON.stringify(args)})`);
|
|
309
|
-
(0, assert_1.guard)(call.
|
|
308
|
+
(0, assert_1.guard)(call.named, `infix special call must be named, got: ${call.named}`);
|
|
310
309
|
const [lhs, rhs] = args;
|
|
311
310
|
if ((lhs === undefined || lhs.length === 0) && (rhs === undefined || rhs.length === 0)) {
|
|
312
311
|
return [];
|
|
313
312
|
}
|
|
314
313
|
// else if (rhs === undefined || rhs.length === 0) {
|
|
315
|
-
// if rhs is undefined we still
|
|
314
|
+
// if rhs is undefined we still have to keep both now, but reconstruct manually :/
|
|
316
315
|
if (lhs !== r_function_call_1.EmptyArgument && lhs.length > 0) {
|
|
317
316
|
const lhsText = lhs.map(l => `${getIndentString(l.indent)}${l.line}`).join('\n');
|
|
318
317
|
if (rhs !== r_function_call_1.EmptyArgument && rhs.length > 0) {
|
|
@@ -339,7 +338,7 @@ function reconstructFunctionCall(call, functionName, args, configuration) {
|
|
|
339
338
|
if (call.infixSpecial === true) {
|
|
340
339
|
return reconstructSpecialInfixFunctionCall(args, call);
|
|
341
340
|
}
|
|
342
|
-
if (call.
|
|
341
|
+
if (call.named && selected) {
|
|
343
342
|
return plain(getLexeme(call));
|
|
344
343
|
}
|
|
345
344
|
const filteredArgs = args.filter(a => a !== undefined && a.length > 0);
|
|
@@ -349,14 +348,12 @@ function reconstructFunctionCall(call, functionName, args, configuration) {
|
|
|
349
348
|
if (args.length === 0) {
|
|
350
349
|
(0, assert_1.guard)(functionName.length > 0, `without args, we need the function name to be present! got: ${JSON.stringify(functionName)}`);
|
|
351
350
|
const last = functionName[functionName.length - 1];
|
|
352
|
-
if (call.
|
|
351
|
+
if (!call.named && !last.line.endsWith(')')) {
|
|
353
352
|
functionName[0].line = `(${functionName[0].line}`;
|
|
354
353
|
last.line += ')';
|
|
355
354
|
}
|
|
356
|
-
if
|
|
357
|
-
|
|
358
|
-
last.line += '()';
|
|
359
|
-
}
|
|
355
|
+
// add empty call braces if not present
|
|
356
|
+
last.line += '()';
|
|
360
357
|
return functionName;
|
|
361
358
|
}
|
|
362
359
|
else {
|
|
@@ -369,7 +366,7 @@ function doNotAutoSelect(_node) {
|
|
|
369
366
|
exports.doNotAutoSelect = doNotAutoSelect;
|
|
370
367
|
const libraryFunctionCall = /^(library|require|((require|load|attach)Namespace))$/;
|
|
371
368
|
function autoSelectLibrary(node) {
|
|
372
|
-
if (node.type !== "RFunctionCall" /* RType.FunctionCall */ || node.
|
|
369
|
+
if (node.type !== "RFunctionCall" /* RType.FunctionCall */ || !node.named) {
|
|
373
370
|
return false;
|
|
374
371
|
}
|
|
375
372
|
return libraryFunctionCall.test(node.functionName.content);
|
|
@@ -416,13 +413,13 @@ function getIndentString(indent) {
|
|
|
416
413
|
function prettyPrintCodeToString(code, lf = '\n') {
|
|
417
414
|
return code.map(({ line, indent }) => `${getIndentString(indent)}${line}`).join(lf);
|
|
418
415
|
}
|
|
419
|
-
function removeOuterExpressionListIfApplicable(result,
|
|
416
|
+
function removeOuterExpressionListIfApplicable(result, linesWithAutoSelected) {
|
|
420
417
|
if (result.length > 1 && result[0].line === '{' && result[result.length - 1].line === '}') {
|
|
421
418
|
// remove outer block
|
|
422
|
-
return { code: prettyPrintCodeToString(indentBy(result.slice(1, result.length - 1), -1)),
|
|
419
|
+
return { code: prettyPrintCodeToString(indentBy(result.slice(1, result.length - 1), -1)), linesWithAutoSelected };
|
|
423
420
|
}
|
|
424
421
|
else {
|
|
425
|
-
return { code: prettyPrintCodeToString(result),
|
|
422
|
+
return { code: prettyPrintCodeToString(result), linesWithAutoSelected };
|
|
426
423
|
}
|
|
427
424
|
}
|
|
428
425
|
/**
|
|
@@ -432,25 +429,27 @@ function removeOuterExpressionListIfApplicable(result, autoSelected) {
|
|
|
432
429
|
* @param selection - The selection of nodes to be reconstructed (probably the {@link NodeId|NodeIds} identified by the slicer)
|
|
433
430
|
* @param autoSelectIf - A predicate that can be used to force the reconstruction of a node (for example to reconstruct library call statements, see {@link autoSelectLibrary}, {@link doNotAutoSelect})
|
|
434
431
|
*
|
|
435
|
-
* @returns The number of
|
|
432
|
+
* @returns The number of lines for which `autoSelectIf` triggered, as well as the reconstructed code itself.
|
|
436
433
|
*/
|
|
437
434
|
function reconstructToCode(ast, selection, autoSelectIf = autoSelectLibrary) {
|
|
438
435
|
if (exports.reconstructLogger.settings.minLevel <= 1 /* LogLevel.Trace */) {
|
|
439
436
|
exports.reconstructLogger.trace(`reconstruct ast with ids: ${JSON.stringify([...selection])}`);
|
|
440
437
|
}
|
|
441
|
-
// we use a wrapper to count the number of
|
|
442
|
-
|
|
438
|
+
// we use a wrapper to count the number of lines for which the autoSelectIf predicate triggered
|
|
439
|
+
const linesWithAutoSelected = new Set();
|
|
443
440
|
const autoSelectIfWrapper = (node) => {
|
|
444
441
|
const result = autoSelectIf(node);
|
|
445
|
-
if (result) {
|
|
446
|
-
|
|
442
|
+
if (result && node.location) {
|
|
443
|
+
for (let i = node.location[0]; i <= node.location[2]; i++) {
|
|
444
|
+
linesWithAutoSelected.add(i);
|
|
445
|
+
}
|
|
447
446
|
}
|
|
448
447
|
return result;
|
|
449
448
|
};
|
|
450
449
|
// fold of the normalized ast
|
|
451
450
|
const result = (0, stateful_fold_1.foldAstStateful)(ast.ast, { selection, autoSelectIf: autoSelectIfWrapper }, reconstructAstFolds);
|
|
452
451
|
(0, log_1.expensiveTrace)(exports.reconstructLogger, () => `reconstructed ast before string conversion: ${JSON.stringify(result)}`);
|
|
453
|
-
return removeOuterExpressionListIfApplicable(result,
|
|
452
|
+
return removeOuterExpressionListIfApplicable(result, linesWithAutoSelected.size);
|
|
454
453
|
}
|
|
455
454
|
exports.reconstructToCode = reconstructToCode;
|
|
456
455
|
//# sourceMappingURL=reconstruct.js.map
|
|
@@ -38,7 +38,7 @@ const defaultAllVariablesCollectorFolds = {
|
|
|
38
38
|
foldFunctionDefinition: (_, a, b) => [...a.flat(), ...b],
|
|
39
39
|
foldFunctionCall: (c, a, b) => {
|
|
40
40
|
const args = b.flatMap(b => b !== r_function_call_1.EmptyArgument ? b.flat() : []);
|
|
41
|
-
if (c.
|
|
41
|
+
if (c.named) {
|
|
42
42
|
return c.functionName.content === 'library' ? args.slice(1) : args;
|
|
43
43
|
}
|
|
44
44
|
else {
|
|
@@ -48,8 +48,8 @@ function staticSlicing(graph, ast, criteria, threshold = 75) {
|
|
|
48
48
|
if (currentVertex.controlDependencies) {
|
|
49
49
|
const topLevel = graph.isRoot(id) || sliceSeedIds.has(id);
|
|
50
50
|
for (const cd of currentVertex.controlDependencies) {
|
|
51
|
-
if (!topLevel || (ast.idMap.get(cd)?.info.depth ?? 0) <= minDepth) {
|
|
52
|
-
queue.add(cd, baseEnvironment, baseEnvFingerprint, false);
|
|
51
|
+
if (!topLevel || (ast.idMap.get(cd.id)?.info.depth ?? 0) <= minDepth) {
|
|
52
|
+
queue.add(cd.id, baseEnvironment, baseEnvFingerprint, false);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -21,7 +21,7 @@ function visitIfThenElse(info, input) {
|
|
|
21
21
|
const ifThenElseStack = [];
|
|
22
22
|
(0, visitor_1.visitAst)(input.normalizedRAst.ast, node => {
|
|
23
23
|
if (node.type !== "RIfThenElse" /* RType.IfThenElse */) {
|
|
24
|
-
if (node.type === "RFunctionCall" /* RType.FunctionCall */ && node.
|
|
24
|
+
if (node.type === "RFunctionCall" /* RType.FunctionCall */ && node.named && node.functionName.content === 'switch') {
|
|
25
25
|
const initialArg = (0, unpack_argument_1.unpackArgument)(node.arguments[0]);
|
|
26
26
|
if (initialArg) {
|
|
27
27
|
info.switchCase = (0, common_syntax_probability_1.updateCommonSyntaxTypeCounts)(info.switchCase, initialArg);
|
|
@@ -100,7 +100,7 @@ function visitDefinitions(info, input) {
|
|
|
100
100
|
// track all calls with the same name that do not already have a bound calls edge, superfluous if recursive tracking is explicit
|
|
101
101
|
const recursiveCalls = [];
|
|
102
102
|
(0, visitor_1.visitAst)(node.body, n => {
|
|
103
|
-
if (n.type === "RFunctionCall" /* RType.FunctionCall */ && n.
|
|
103
|
+
if (n.type === "RFunctionCall" /* RType.FunctionCall */ && n.named && assigned.has(n.functionName.lexeme)) {
|
|
104
104
|
recursiveCalls.push(n);
|
|
105
105
|
}
|
|
106
106
|
});
|
|
@@ -33,7 +33,7 @@ function visitLoops(info, input) {
|
|
|
33
33
|
info.breakStatements++;
|
|
34
34
|
return;
|
|
35
35
|
case "RFunctionCall" /* RType.FunctionCall */:
|
|
36
|
-
if (node.
|
|
36
|
+
if (node.named && isImplicitLoop.test(node.functionName.lexeme)) {
|
|
37
37
|
info.implicitLoops++;
|
|
38
38
|
(0, statistics_file_1.appendStatisticsFile)(exports.loops.name, 'implicit-loop', [node.functionName.info.fullLexeme ?? node.functionName.lexeme], input.filepath);
|
|
39
39
|
}
|