@eagleoutice/flowr 2.9.7 → 2.9.9
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/README.md +21 -21
- package/abstract-interpretation/absint-visitor.d.ts +5 -4
- package/abstract-interpretation/absint-visitor.js +12 -11
- package/control-flow/basic-cfg-guided-visitor.d.ts +3 -3
- package/control-flow/basic-cfg-guided-visitor.js +7 -6
- package/control-flow/cfg-dead-code.js +12 -9
- package/control-flow/cfg-properties.d.ts +2 -2
- package/control-flow/cfg-properties.js +3 -2
- package/control-flow/cfg-to-basic-blocks.js +9 -12
- package/control-flow/control-flow-graph.d.ts +382 -78
- package/control-flow/control-flow-graph.js +591 -106
- package/control-flow/dfg-cfg-guided-visitor.d.ts +4 -4
- package/control-flow/dfg-cfg-guided-visitor.js +3 -3
- package/control-flow/diff-cfg.js +35 -29
- package/control-flow/extract-cfg.d.ts +3 -3
- package/control-flow/extract-cfg.js +145 -125
- package/control-flow/happens-before.js +2 -1
- package/control-flow/semantic-cfg-guided-visitor.js +2 -1
- package/control-flow/simple-visitor.js +8 -6
- package/control-flow/syntax-cfg-guided-visitor.js +2 -1
- package/control-flow/useless-loop.js +2 -1
- package/documentation/wiki-cfg.js +21 -9
- package/documentation/wiki-mk/doc-maker.js +1 -1
- package/package.json +1 -1
- package/project/context/flowr-file.d.ts +5 -0
- package/project/context/flowr-file.js +7 -0
- package/project/plugins/file-plugins/files/flowr-description-file.d.ts +1 -0
- package/project/plugins/file-plugins/files/flowr-description-file.js +5 -0
- package/project/plugins/file-plugins/files/flowr-namespace-file.d.ts +4 -0
- package/project/plugins/file-plugins/files/flowr-namespace-file.js +8 -0
- package/project/plugins/file-plugins/files/flowr-news-file.d.ts +4 -0
- package/project/plugins/file-plugins/files/flowr-news-file.js +8 -0
- package/queries/catalog/control-flow-query/control-flow-query-format.js +1 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.d.ts +2 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +19 -19
- package/search/search-executor/search-generators.js +2 -2
- package/util/assert.d.ts +3 -3
- package/util/mermaid/cfg.d.ts +1 -7
- package/util/mermaid/cfg.js +37 -44
- package/util/version.js +1 -1
|
@@ -10,6 +10,7 @@ const vertex_1 = require("../dataflow/graph/vertex");
|
|
|
10
10
|
const info_1 = require("../dataflow/info");
|
|
11
11
|
const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
12
12
|
const assert_1 = require("../util/assert");
|
|
13
|
+
const control_flow_graph_1 = require("./control-flow-graph");
|
|
13
14
|
const semantic_cfg_guided_visitor_1 = require("./semantic-cfg-guided-visitor");
|
|
14
15
|
exports.loopyFunctions = new Set([built_in_1.BuiltInProcName.ForLoop, built_in_1.BuiltInProcName.WhileLoop, built_in_1.BuiltInProcName.RepeatLoop]);
|
|
15
16
|
/**
|
|
@@ -88,7 +89,7 @@ class CfgSingleIterationLoopDetector extends semantic_cfg_guided_visitor_1.Seman
|
|
|
88
89
|
startVisitor(_) {
|
|
89
90
|
const g = this.config.controlFlow.graph;
|
|
90
91
|
const ingoing = (i) => g.ingoingEdges(i);
|
|
91
|
-
const exits = new Set(g.getVertex(this.loopToCheck)
|
|
92
|
+
const exits = new Set(control_flow_graph_1.CfgVertex.getEnd(g.getVertex(this.loopToCheck)) ?? []);
|
|
92
93
|
(0, assert_1.guard)(exits.size !== 0, "Can't find end of loop");
|
|
93
94
|
const stack = [this.loopToCheck];
|
|
94
95
|
while (stack.length > 0) {
|
|
@@ -149,6 +149,12 @@ For readability, we structure this wiki page into various segments:
|
|
|
149
149
|
- [Sophisticated CFG Traversal](#cfg-traversal)
|
|
150
150
|
- [Working With Exit Points](#cfg-exit-points)
|
|
151
151
|
|
|
152
|
+
${(0, doc_structure_1.block)({
|
|
153
|
+
type: 'TIP',
|
|
154
|
+
content: `FlowR provides you with various helper objects to work with the CFG, such as ${ctx.link('CfgEdge', undefined, { type: 'variable' })} and ${ctx.link('CfgVertex', undefined, { type: 'variable' })},
|
|
155
|
+
which you can use to easily access the properties of the CFG and its vertices and edges.`
|
|
156
|
+
})}
|
|
157
|
+
|
|
152
158
|
${(0, doc_structure_1.section)('Initial Overview', 2, 'cfg-overview')}
|
|
153
159
|
|
|
154
160
|
For now, let's look at a CFG for a program without any branching:
|
|
@@ -360,7 +366,13 @@ And again it should be noted that even though the example code is more complicat
|
|
|
360
366
|
${(0, doc_structure_1.section)('Working with the CFG', 2, 'cfg-working')}
|
|
361
367
|
|
|
362
368
|
There is a plethora of functions that you can use the traverse the [normalized AST](${doc_files_1.FlowrWikiBaseRef}/Normalized-AST) and the [dataflow graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph).
|
|
363
|
-
Similarly, flowR provides you with a set of utility functions and classes that you can use to interact with the control flow graph
|
|
369
|
+
Similarly, flowR provides you with a set of utility functions and classes that you can use to interact with the control flow graph:
|
|
370
|
+
|
|
371
|
+
* ${ctx.link(simple_visitor_1.visitCfgInOrder)} and ${ctx.link(simple_visitor_1.visitCfgInReverseOrder)} for simple traversals
|
|
372
|
+
* ${ctx.link(basic_cfg_guided_visitor_1.BasicCfgGuidedVisitor)}, ${ctx.link(syntax_cfg_guided_visitor_1.SyntaxAwareCfgGuidedVisitor)}, ${ctx.link(dfg_cfg_guided_visitor_1.DataflowAwareCfgGuidedVisitor)}, and ${ctx.link(semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor)} for more sophisticated traversals
|
|
373
|
+
* ${ctx.link('CfgEdge', undefined, { type: 'variable' })} and ${ctx.link('CfgVertex', undefined, { type: 'variable' })} for easy access to the properties of the CFG and its vertices and edges
|
|
374
|
+
* ${ctx.link(cfg_properties_1.assertCfgSatisfiesProperties)} and ${ctx.link('CfgProperties')} to check for properties of the CFG
|
|
375
|
+
* ${ctx.link(diff_cfg_1.diffOfControlFlowGraphs)} to diff two CFGs
|
|
364
376
|
|
|
365
377
|
${(0, doc_structure_1.section)('Simple Traversal', 3, 'cfg-simple-traversal')}
|
|
366
378
|
|
|
@@ -515,12 +527,12 @@ But the control flow perspective gives you more! Given a simple addition like \`
|
|
|
515
527
|
${await (async function () {
|
|
516
528
|
const cfg = await (0, doc_cfg_1.getCfg)(shell, 'x + 1');
|
|
517
529
|
const [plusVertexId, plusVertex] = cfg.info.graph.vertices().entries().filter(([n]) => (0, node_id_1.recoverName)(n, cfg.ast.idMap) === '+').toArray()[0];
|
|
518
|
-
(0, assert_1.guard)(
|
|
519
|
-
const numOfExits = plusVertex
|
|
520
|
-
(0, assert_1.guard)(
|
|
530
|
+
(0, assert_1.guard)(control_flow_graph_1.CfgVertex.isExpression(plusVertex));
|
|
531
|
+
const numOfExits = control_flow_graph_1.CfgVertex.getEnd(plusVertex)?.length ?? 0;
|
|
532
|
+
(0, assert_1.guard)(numOfExits === 1);
|
|
521
533
|
return `${await (0, doc_cfg_1.printCfgCode)(shell, 'x + 1', { showCode: true, prefix: 'flowchart RL\n' })}
|
|
522
534
|
|
|
523
|
-
Looking at the binary operation vertex for \`+\` (with id \`${plusVertexId}\`) we see that it is linked to a single exit ("end marker") point: \`${plusVertex
|
|
535
|
+
Looking at the binary operation vertex for \`+\` (with id \`${plusVertexId}\`) we see that it is linked to a single exit ("end marker") point: \`${control_flow_graph_1.CfgVertex.getEnd(plusVertex)?.[0] ?? '??'}\`.
|
|
524
536
|
Checking this vertex essentially reveals all exit points of the expression ‐ in this case, this simply refers to the operands of the addition.
|
|
525
537
|
However, the idea transfers to more complex expressions as well...
|
|
526
538
|
`;
|
|
@@ -530,12 +542,12 @@ ${(0, doc_structure_1.details)('Example: Exit Points for an if', await (async fu
|
|
|
530
542
|
const expr = 'if(u) 3 else 2';
|
|
531
543
|
const cfg = await (0, doc_cfg_1.getCfg)(shell, expr);
|
|
532
544
|
const [ifVertexId, ifVertex] = [...cfg.info.graph.vertices()].filter(([n]) => (0, node_id_1.recoverName)(n, cfg.ast.idMap) === 'if')[0];
|
|
533
|
-
(0, assert_1.guard)(
|
|
534
|
-
const numOfExits = ifVertex
|
|
535
|
-
(0, assert_1.guard)(
|
|
545
|
+
(0, assert_1.guard)(control_flow_graph_1.CfgVertex.isStatement(ifVertex));
|
|
546
|
+
const numOfExits = control_flow_graph_1.CfgVertex.getEnd(ifVertex)?.length ?? 0;
|
|
547
|
+
(0, assert_1.guard)(numOfExits === 1);
|
|
536
548
|
return `${await (0, doc_cfg_1.printCfgCode)(shell, expr, { showCode: true, prefix: 'flowchart RL\n' })}
|
|
537
549
|
|
|
538
|
-
Looking at the if vertex for (with id \`${ifVertexId}\`) we see that it is again linked to a single exit point: \`${ifVertex
|
|
550
|
+
Looking at the if vertex for (with id \`${ifVertexId}\`) we see that it is again linked to a single exit point: \`${control_flow_graph_1.CfgVertex.getEnd(ifVertex)?.[0] ?? '??'}\`.
|
|
539
551
|
Yet, now this exit vertex is linked to the two branches of the if statement (the \`then\` and \`else\` branch).
|
|
540
552
|
`;
|
|
541
553
|
})())}
|
|
@@ -11,7 +11,7 @@ const DefaultReplacementPatterns = [
|
|
|
11
11
|
// eslint-disable-next-line no-irregular-whitespace -- we may produce it in output
|
|
12
12
|
[/\d+(\.\d+)?( |\s*)?ms/g, ''],
|
|
13
13
|
[/tmp[%A-Za-z0-9-]+/g, ''],
|
|
14
|
-
[/"?(timing|searchTimeMs|processTimeMs|id|
|
|
14
|
+
[/"?(timing|searchTimeMs|processTimeMs|id|tsId)"?:\s*\d+(\.\d)?,?/g, ''],
|
|
15
15
|
[/"format":"compact".+/gmius, ''],
|
|
16
16
|
[/%%\s*\d*-+/g, ''],
|
|
17
17
|
[/"[rR]": "\d+\.\d+\.\d+.*?"/g, ''],
|
package/package.json
CHANGED
|
@@ -92,6 +92,11 @@ export declare abstract class FlowrFile<Content extends StringableContent = Stri
|
|
|
92
92
|
get roles(): readonly FileRole[] | undefined;
|
|
93
93
|
path(): string;
|
|
94
94
|
content(): Content;
|
|
95
|
+
/**
|
|
96
|
+
* Allows to overwrite the content cache.
|
|
97
|
+
* @protected
|
|
98
|
+
*/
|
|
99
|
+
protected setContent(content: Content): void;
|
|
95
100
|
protected abstract loadContent(): Content;
|
|
96
101
|
assignRole(role: FileRole): void;
|
|
97
102
|
/**
|
|
@@ -62,6 +62,13 @@ class FlowrFile {
|
|
|
62
62
|
}
|
|
63
63
|
return this.contentCache;
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Allows to overwrite the content cache.
|
|
67
|
+
* @protected
|
|
68
|
+
*/
|
|
69
|
+
setContent(content) {
|
|
70
|
+
this.contentCache = content;
|
|
71
|
+
}
|
|
65
72
|
assignRole(role) {
|
|
66
73
|
if (this._roles === undefined) {
|
|
67
74
|
this._roles = [role];
|
|
@@ -24,6 +24,7 @@ export declare class FlowrDescriptionFile extends FlowrFile<DeepReadonly<DCF>> {
|
|
|
24
24
|
* @see {@link parseDCF} for details on the parsing logic.
|
|
25
25
|
*/
|
|
26
26
|
protected loadContent(): DCF;
|
|
27
|
+
static fromDCF(dcf: DCF, path: string, roles?: FileRole[]): FlowrDescriptionFile;
|
|
27
28
|
/**
|
|
28
29
|
* Description file lifter, this does not re-create if already a description file
|
|
29
30
|
*/
|
|
@@ -34,6 +34,11 @@ class FlowrDescriptionFile extends flowr_file_1.FlowrFile {
|
|
|
34
34
|
loadContent() {
|
|
35
35
|
return parseDCF(this.wrapped);
|
|
36
36
|
}
|
|
37
|
+
static fromDCF(dcf, path, roles) {
|
|
38
|
+
const file = new FlowrDescriptionFile(new flowr_file_1.FlowrTextFile(path, roles));
|
|
39
|
+
file.setContent(dcf);
|
|
40
|
+
return file;
|
|
41
|
+
}
|
|
37
42
|
/**
|
|
38
43
|
* Description file lifter, this does not re-create if already a description file
|
|
39
44
|
*/
|
|
@@ -32,6 +32,10 @@ export declare class FlowrNamespaceFile extends FlowrFile<NamespaceFormat> {
|
|
|
32
32
|
* and handle role assignments.
|
|
33
33
|
*/
|
|
34
34
|
constructor(file: FlowrFileProvider, ctx?: FlowrAnalyzerContext);
|
|
35
|
+
/**
|
|
36
|
+
* Creates a {@link FlowrNamespaceFile} from a given {@link NamespaceFormat}, path and optional roles. This is useful if you already have the namespace content parsed and want to create a namespace file instance without re-parsing.
|
|
37
|
+
*/
|
|
38
|
+
static fromNamespaceFormat(fmt: NamespaceFormat, path: string, roles?: FileRole[]): FlowrNamespaceFile;
|
|
35
39
|
/**
|
|
36
40
|
* Loads and parses the content of the wrapped file in the {@link NamespaceFormat}.
|
|
37
41
|
* @see {@link parseNamespaceSimple} for details on the parsing logic.
|
|
@@ -30,6 +30,14 @@ class FlowrNamespaceFile extends flowr_file_1.FlowrFile {
|
|
|
30
30
|
this.wrapped = file;
|
|
31
31
|
this.ctx = ctx;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates a {@link FlowrNamespaceFile} from a given {@link NamespaceFormat}, path and optional roles. This is useful if you already have the namespace content parsed and want to create a namespace file instance without re-parsing.
|
|
35
|
+
*/
|
|
36
|
+
static fromNamespaceFormat(fmt, path, roles) {
|
|
37
|
+
const file = new FlowrNamespaceFile(new flowr_file_1.FlowrTextFile(path, roles));
|
|
38
|
+
file.setContent(fmt);
|
|
39
|
+
return file;
|
|
40
|
+
}
|
|
33
41
|
/**
|
|
34
42
|
* Loads and parses the content of the wrapped file in the {@link NamespaceFormat}.
|
|
35
43
|
* @see {@link parseNamespaceSimple} for details on the parsing logic.
|
|
@@ -24,4 +24,8 @@ export declare class FlowrNewsFile extends FlowrFile<NewsChunk[]> {
|
|
|
24
24
|
* News file lifter, this does not re-create if already a news file
|
|
25
25
|
*/
|
|
26
26
|
static from(file: FlowrFileProvider | FlowrNewsFile, role?: FileRole): FlowrNewsFile;
|
|
27
|
+
/**
|
|
28
|
+
* Creates a FlowrNewsFile from given news chunks, path and optional roles. This is useful if you already have the news content parsed and want to create a news file instance without re-parsing.
|
|
29
|
+
*/
|
|
30
|
+
static fromNewsChunks(chunks: NewsChunk[], path: string, roles?: FileRole[]): FlowrNewsFile;
|
|
27
31
|
}
|
|
@@ -33,6 +33,14 @@ class FlowrNewsFile extends flowr_file_1.FlowrFile {
|
|
|
33
33
|
}
|
|
34
34
|
return file instanceof FlowrNewsFile ? file : new FlowrNewsFile(file);
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Creates a FlowrNewsFile from given news chunks, path and optional roles. This is useful if you already have the news content parsed and want to create a news file instance without re-parsing.
|
|
38
|
+
*/
|
|
39
|
+
static fromNewsChunks(chunks, path, roles) {
|
|
40
|
+
const file = new FlowrNewsFile(new flowr_file_1.FlowrTextFile(path, roles));
|
|
41
|
+
file.setContent(chunks);
|
|
42
|
+
return file;
|
|
43
|
+
}
|
|
36
44
|
}
|
|
37
45
|
exports.FlowrNewsFile = FlowrNewsFile;
|
|
38
46
|
function makeRversionRegex() {
|
|
@@ -27,7 +27,7 @@ exports.ControlFlowQueryDefinition = {
|
|
|
27
27
|
flattenInvolvedNodes: (queryResults) => {
|
|
28
28
|
const out = queryResults;
|
|
29
29
|
return out.controlFlow.graph.vertices(true).entries()
|
|
30
|
-
.filter(([, v]) =>
|
|
30
|
+
.filter(([, v]) => !control_flow_graph_1.CfgVertex.isBlock(v))
|
|
31
31
|
.map(v => v[0])
|
|
32
32
|
.toArray();
|
|
33
33
|
}
|
|
@@ -2,7 +2,8 @@ import type { Tree } from 'web-tree-sitter';
|
|
|
2
2
|
import type { RProject } from '../ast/model/nodes/r-project';
|
|
3
3
|
import type { ParseStepOutputSingleFile } from '../../parser';
|
|
4
4
|
export interface TreeSitterInfo {
|
|
5
|
-
|
|
5
|
+
/** id of the tree-sitter node this AST node was created from, used for traceability and linking back to tree-sitter nodes */
|
|
6
|
+
tsId: number;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* @param tree - The tree to normalize
|
|
@@ -77,7 +77,7 @@ function convertTreeNode(node) {
|
|
|
77
77
|
info: {
|
|
78
78
|
fullRange: undefined,
|
|
79
79
|
adToks: [],
|
|
80
|
-
|
|
80
|
+
tsId: -1,
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
}
|
|
@@ -90,7 +90,7 @@ function convertTreeNode(node) {
|
|
|
90
90
|
fullRange: range,
|
|
91
91
|
adToks: [],
|
|
92
92
|
fullLexeme: node.text,
|
|
93
|
-
|
|
93
|
+
tsId: node.id
|
|
94
94
|
}
|
|
95
95
|
};
|
|
96
96
|
switch (node.type) {
|
|
@@ -105,7 +105,7 @@ function convertTreeNode(node) {
|
|
|
105
105
|
lexeme: undefined,
|
|
106
106
|
info: {
|
|
107
107
|
adToks: remainingComments.map(c => c[1]),
|
|
108
|
-
|
|
108
|
+
tsId: node.id
|
|
109
109
|
}
|
|
110
110
|
};
|
|
111
111
|
}
|
|
@@ -138,7 +138,7 @@ function convertTreeNode(node) {
|
|
|
138
138
|
],
|
|
139
139
|
info: {
|
|
140
140
|
adToks: remainingComments.map(c => c[1]),
|
|
141
|
-
|
|
141
|
+
tsId: node.id
|
|
142
142
|
}
|
|
143
143
|
};
|
|
144
144
|
}
|
|
@@ -156,7 +156,7 @@ function convertTreeNode(node) {
|
|
|
156
156
|
name: undefined,
|
|
157
157
|
lexeme: lhs.lexeme,
|
|
158
158
|
info: {
|
|
159
|
-
|
|
159
|
+
tsId: lhs.info.tsId
|
|
160
160
|
}
|
|
161
161
|
};
|
|
162
162
|
if (op.type === 'special') {
|
|
@@ -170,7 +170,7 @@ function convertTreeNode(node) {
|
|
|
170
170
|
lexeme: op.text,
|
|
171
171
|
content: op.text,
|
|
172
172
|
info: {
|
|
173
|
-
|
|
173
|
+
tsId: op.id
|
|
174
174
|
}
|
|
175
175
|
},
|
|
176
176
|
arguments: [lhsAsArg, {
|
|
@@ -180,14 +180,14 @@ function convertTreeNode(node) {
|
|
|
180
180
|
name: undefined,
|
|
181
181
|
lexeme: rhs.lexeme,
|
|
182
182
|
info: {
|
|
183
|
-
|
|
183
|
+
tsId: rhs.info.tsId
|
|
184
184
|
}
|
|
185
185
|
}],
|
|
186
186
|
named: true,
|
|
187
187
|
infixSpecial: true,
|
|
188
188
|
info: {
|
|
189
189
|
adToks: comments,
|
|
190
|
-
|
|
190
|
+
tsId: node.id
|
|
191
191
|
}
|
|
192
192
|
};
|
|
193
193
|
}
|
|
@@ -203,7 +203,7 @@ function convertTreeNode(node) {
|
|
|
203
203
|
fullRange: range,
|
|
204
204
|
adToks: comments,
|
|
205
205
|
fullLexeme: node.text,
|
|
206
|
-
|
|
206
|
+
tsId: node.id
|
|
207
207
|
}
|
|
208
208
|
};
|
|
209
209
|
}
|
|
@@ -218,7 +218,7 @@ function convertTreeNode(node) {
|
|
|
218
218
|
fullRange: range,
|
|
219
219
|
adToks: comments,
|
|
220
220
|
fullLexeme: node.text,
|
|
221
|
-
|
|
221
|
+
tsId: node.id
|
|
222
222
|
}
|
|
223
223
|
};
|
|
224
224
|
}
|
|
@@ -291,7 +291,7 @@ function convertTreeNode(node) {
|
|
|
291
291
|
fullRange: undefined,
|
|
292
292
|
adToks: [],
|
|
293
293
|
fullLexeme: undefined,
|
|
294
|
-
|
|
294
|
+
tsId: variableNode.id
|
|
295
295
|
}
|
|
296
296
|
},
|
|
297
297
|
vector: convertTreeNode(sequenceNode),
|
|
@@ -302,7 +302,7 @@ function convertTreeNode(node) {
|
|
|
302
302
|
fullRange: range,
|
|
303
303
|
adToks: variableComments.concat(sequenceComments).map(c => c[1]),
|
|
304
304
|
fullLexeme: node.text,
|
|
305
|
-
|
|
305
|
+
tsId: node.id
|
|
306
306
|
}
|
|
307
307
|
};
|
|
308
308
|
}
|
|
@@ -369,7 +369,7 @@ function convertTreeNode(node) {
|
|
|
369
369
|
fullRange: range,
|
|
370
370
|
adToks: [],
|
|
371
371
|
fullLexeme: node.text,
|
|
372
|
-
|
|
372
|
+
tsId: node.id
|
|
373
373
|
}
|
|
374
374
|
},
|
|
375
375
|
named: true
|
|
@@ -480,7 +480,7 @@ function convertTreeNode(node) {
|
|
|
480
480
|
fullRange: rhsRange,
|
|
481
481
|
adToks: [],
|
|
482
482
|
fullLexeme: rhs?.text,
|
|
483
|
-
|
|
483
|
+
tsId: rhs?.id
|
|
484
484
|
}
|
|
485
485
|
}],
|
|
486
486
|
location: makeSourceRange(operator),
|
|
@@ -507,7 +507,7 @@ function convertTreeNode(node) {
|
|
|
507
507
|
fullRange: range,
|
|
508
508
|
adToks: [],
|
|
509
509
|
fullLexeme: name.text,
|
|
510
|
-
|
|
510
|
+
tsId: name.id
|
|
511
511
|
}
|
|
512
512
|
},
|
|
513
513
|
special: name.text === '...',
|
|
@@ -518,7 +518,7 @@ function convertTreeNode(node) {
|
|
|
518
518
|
fullRange: range,
|
|
519
519
|
adToks: [],
|
|
520
520
|
fullLexeme: name.text,
|
|
521
|
-
|
|
521
|
+
tsId: name.id
|
|
522
522
|
}
|
|
523
523
|
};
|
|
524
524
|
}
|
|
@@ -560,7 +560,7 @@ function convertTreeNode(node) {
|
|
|
560
560
|
fullRange: nameRange,
|
|
561
561
|
adToks: [],
|
|
562
562
|
fullLexeme: nameNode.text,
|
|
563
|
-
|
|
563
|
+
tsId: nameNode.id
|
|
564
564
|
}
|
|
565
565
|
};
|
|
566
566
|
}
|
|
@@ -595,7 +595,7 @@ function convertTreeNode(node) {
|
|
|
595
595
|
info: {
|
|
596
596
|
fullRange: undefined,
|
|
597
597
|
adToks: [],
|
|
598
|
-
|
|
598
|
+
tsId: -1,
|
|
599
599
|
}
|
|
600
600
|
};
|
|
601
601
|
}
|
|
@@ -624,7 +624,7 @@ function splitComments(nodes) {
|
|
|
624
624
|
info: {
|
|
625
625
|
adToks: [],
|
|
626
626
|
fullLexeme: node.text,
|
|
627
|
-
|
|
627
|
+
tsId: node.id
|
|
628
628
|
}
|
|
629
629
|
}]);
|
|
630
630
|
}
|
|
@@ -112,8 +112,8 @@ async function generateSyntax(input, args) {
|
|
|
112
112
|
const nodesByTreeSitterId = new Map();
|
|
113
113
|
(0, visitor_1.visitAst)((await input.normalize()).ast.files.map(f => f.root), node => {
|
|
114
114
|
const treeSitterInfo = node.info;
|
|
115
|
-
if (treeSitterInfo.
|
|
116
|
-
nodesByTreeSitterId.set(treeSitterInfo.
|
|
115
|
+
if (treeSitterInfo.tsId) {
|
|
116
|
+
nodesByTreeSitterId.set(treeSitterInfo.tsId, node);
|
|
117
117
|
}
|
|
118
118
|
else {
|
|
119
119
|
exports.searchLogger.debug(`normalized ast node ${node.lexeme} with type ${node.type} does not have a tree-sitter id`);
|
package/util/assert.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare function assertUnreachable(x: never): never;
|
|
|
26
26
|
* @see {@link isUndefined}
|
|
27
27
|
* @see {@link isNotNull}
|
|
28
28
|
*/
|
|
29
|
-
export declare function isNotUndefined<T>(x: T | undefined): x is T;
|
|
29
|
+
export declare function isNotUndefined<T>(this: void, x: T | undefined): x is T;
|
|
30
30
|
/**
|
|
31
31
|
* Verifies that the given value is undefined.
|
|
32
32
|
* This especially helps with a `.filter`
|
|
@@ -39,7 +39,7 @@ export declare function isNotUndefined<T>(x: T | undefined): x is T;
|
|
|
39
39
|
* @see {@link isNotUndefined}
|
|
40
40
|
* @see {@link isNotNull}
|
|
41
41
|
*/
|
|
42
|
-
export declare function isUndefined<T>(x: T | undefined): x is undefined;
|
|
42
|
+
export declare function isUndefined<T>(this: void, x: T | undefined): x is undefined;
|
|
43
43
|
/**
|
|
44
44
|
* Verifies that the given value is not null.
|
|
45
45
|
* This especially helps with a `.filter`
|
|
@@ -52,7 +52,7 @@ export declare function isUndefined<T>(x: T | undefined): x is undefined;
|
|
|
52
52
|
* @see {@link isUndefined}
|
|
53
53
|
* @see {@link isNotUndefined}
|
|
54
54
|
*/
|
|
55
|
-
export declare function isNotNull<T>(x: T | null): x is T;
|
|
55
|
+
export declare function isNotNull<T>(this: void, x: T | null): x is T;
|
|
56
56
|
/**
|
|
57
57
|
* Generates a GitHub issue URL for reporting guard errors
|
|
58
58
|
*/
|
package/util/mermaid/cfg.d.ts
CHANGED
|
@@ -15,13 +15,7 @@ export declare const MermaidEntryPointDefaultMarkStyle: MermaidMarkStyle['vertex
|
|
|
15
15
|
export declare const MermaidExitPointDefaultMarkStyle: MermaidMarkStyle['vertex'];
|
|
16
16
|
/**
|
|
17
17
|
* Convert the control flow graph to a mermaid string.
|
|
18
|
-
* @
|
|
19
|
-
* @param normalizedAst - The normalized AST to use for the vertex content.
|
|
20
|
-
* @param prefix - The prefix to use for the mermaid string.
|
|
21
|
-
* @param simplify - Whether to simplify the control flow graph (especially in the context of basic blocks).
|
|
22
|
-
* @param markStyle - The style to use for marked vertices and edges.
|
|
23
|
-
* @param includeOnlyIds - If provided, only include the vertices with the given IDs.
|
|
24
|
-
* @param mark - If provided, mark the given vertices and edges.
|
|
18
|
+
* @see {@link MermaidCfgGraphPrinterInfo} for additional options.
|
|
25
19
|
*/
|
|
26
20
|
export declare function cfgToMermaid(cfg: ControlFlowInformation, normalizedAst: NormalizedAst, { prefix, simplify, markStyle, entryPointStyle, exitPointStyle, includeOnlyIds, mark, includeBasicBlockLabel, basicBlockCharacterLimit }?: MermaidCfgGraphPrinterInfo): string;
|
|
27
21
|
/**
|
package/util/mermaid/cfg.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.MermaidExitPointDefaultMarkStyle = exports.MermaidEntryPointDefaultMarkS
|
|
|
4
4
|
exports.cfgToMermaid = cfgToMermaid;
|
|
5
5
|
exports.cfgToMermaidUrl = cfgToMermaidUrl;
|
|
6
6
|
const mermaid_1 = require("./mermaid");
|
|
7
|
-
const type_1 = require("../../r-bridge/lang-4.x/ast/model/type");
|
|
8
7
|
const control_flow_graph_1 = require("../../control-flow/control-flow-graph");
|
|
9
8
|
const reconstruct_1 = require("../../reconstruct/reconstruct");
|
|
10
9
|
const auto_select_defaults_1 = require("../../reconstruct/auto-select/auto-select-defaults");
|
|
@@ -17,9 +16,9 @@ function getLexeme(n) {
|
|
|
17
16
|
}
|
|
18
17
|
function cfgOfNode(vert, normalizedVertex, id, content, output) {
|
|
19
18
|
if (normalizedVertex && content !== undefined) {
|
|
20
|
-
const start =
|
|
21
|
-
const end =
|
|
22
|
-
const name = `"\`${(0, mermaid_1.escapeMarkdown)(normalizedVertex.type)} (${id})${content ? '\n' + (0, mermaid_1.escapeMarkdown)(JSON.stringify(content)) : ''}${vert
|
|
19
|
+
const start = control_flow_graph_1.CfgVertex.isExpression(vert) ? '([' : '[';
|
|
20
|
+
const end = control_flow_graph_1.CfgVertex.isExpression(vert) ? '])' : ']';
|
|
21
|
+
const name = `"\`${(0, mermaid_1.escapeMarkdown)(normalizedVertex.type)} (${id})${content ? '\n' + (0, mermaid_1.escapeMarkdown)(JSON.stringify(content)) : ''}${control_flow_graph_1.CfgVertex.getCallTargets(vert) ? '\n calls:' + (0, mermaid_1.escapeMarkdown)(JSON.stringify([...control_flow_graph_1.CfgVertex.getCallTargets(vert)])) : ''}\`"`;
|
|
23
22
|
output += ` n${id}${start}${name}${end}\n`;
|
|
24
23
|
}
|
|
25
24
|
else {
|
|
@@ -28,27 +27,25 @@ function cfgOfNode(vert, normalizedVertex, id, content, output) {
|
|
|
28
27
|
return output;
|
|
29
28
|
}
|
|
30
29
|
const getDirRegex = /flowchart\s+([A-Za-z]+)/;
|
|
31
|
-
function shouldIncludeNode(simplify,
|
|
30
|
+
function shouldIncludeNode(simplify, v, include) {
|
|
32
31
|
if (simplify) {
|
|
33
32
|
// Only basic blocks are shown, so include the BB, if at least one child is selected
|
|
34
|
-
return
|
|
33
|
+
return control_flow_graph_1.CfgVertex.isBlock(v) && control_flow_graph_1.CfgVertex.getBasicBlockElements(v)
|
|
34
|
+
.filter(elem => !control_flow_graph_1.CfgVertex.isMarker(elem))
|
|
35
|
+
.some(elem => include.has(control_flow_graph_1.CfgVertex.getId(elem)));
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
38
|
// Basic blocks and vertices are shown, include the BB, if all children are highlighted
|
|
38
|
-
return
|
|
39
|
-
?
|
|
40
|
-
|
|
39
|
+
return control_flow_graph_1.CfgVertex.isBlock(v)
|
|
40
|
+
? control_flow_graph_1.CfgVertex.getBasicBlockElements(v)
|
|
41
|
+
.filter(elem => !control_flow_graph_1.CfgVertex.isMarker(elem))
|
|
42
|
+
.every(elem => include.has(control_flow_graph_1.CfgVertex.getId(elem)))
|
|
43
|
+
: include.has(control_flow_graph_1.CfgVertex.getId(v));
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
47
|
* Convert the control flow graph to a mermaid string.
|
|
45
|
-
* @
|
|
46
|
-
* @param normalizedAst - The normalized AST to use for the vertex content.
|
|
47
|
-
* @param prefix - The prefix to use for the mermaid string.
|
|
48
|
-
* @param simplify - Whether to simplify the control flow graph (especially in the context of basic blocks).
|
|
49
|
-
* @param markStyle - The style to use for marked vertices and edges.
|
|
50
|
-
* @param includeOnlyIds - If provided, only include the vertices with the given IDs.
|
|
51
|
-
* @param mark - If provided, mark the given vertices and edges.
|
|
48
|
+
* @see {@link MermaidCfgGraphPrinterInfo} for additional options.
|
|
52
49
|
*/
|
|
53
50
|
function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify = false, markStyle = info_1.MermaidDefaultMarkStyle, entryPointStyle = exports.MermaidEntryPointDefaultMarkStyle, exitPointStyle = exports.MermaidExitPointDefaultMarkStyle, includeOnlyIds, mark, includeBasicBlockLabel = true, basicBlockCharacterLimit = 100 } = {}) {
|
|
54
51
|
const hasBbandSimplify = simplify && cfg.graph.mayHaveBasicBlocks();
|
|
@@ -68,7 +65,7 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
|
|
|
68
65
|
}
|
|
69
66
|
// if we have a filter, we automatically add all vertices in the cfg that are *markers* for these ids and
|
|
70
67
|
for (const [id, v] of cfg.graph.vertices()) {
|
|
71
|
-
if (
|
|
68
|
+
if (control_flow_graph_1.CfgVertex.isMarker(v) && completed.has(control_flow_graph_1.CfgVertex.unpackRootId(v))) {
|
|
72
69
|
completed.add(id);
|
|
73
70
|
}
|
|
74
71
|
}
|
|
@@ -79,53 +76,48 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
|
|
|
79
76
|
for (const [id, vertex] of cfg.graph.vertices(false)) {
|
|
80
77
|
const normalizedVertex = normalizedAst?.idMap.get(id);
|
|
81
78
|
const content = getLexeme(normalizedVertex);
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
output += ` direction ${dirIs}\n`;
|
|
85
|
-
}
|
|
86
|
-
if (vertex.type === control_flow_graph_1.CfgVertexType.Block) {
|
|
79
|
+
if (control_flow_graph_1.CfgVertex.isBlock(vertex)) {
|
|
80
|
+
const elems = control_flow_graph_1.CfgVertex.getBasicBlockElements(vertex);
|
|
87
81
|
if (simplify) {
|
|
88
|
-
if (includeOnlyIds && !
|
|
82
|
+
if (includeOnlyIds && !elems.some(elem => includeOnlyIds.has(control_flow_graph_1.CfgVertex.getId(elem)))) {
|
|
89
83
|
continue;
|
|
90
84
|
}
|
|
91
|
-
const ids =
|
|
85
|
+
const ids = elems?.map(control_flow_graph_1.CfgVertex.getId) ?? [];
|
|
92
86
|
const reconstruct = limitTo((0, reconstruct_1.reconstructToCode)(normalizedAst, { nodes: new Set(ids) }, auto_select_defaults_1.doNotAutoSelect).code, basicBlockCharacterLimit);
|
|
93
87
|
const name = `"\`${includeBasicBlockLabel ? `Basic Block (${id})\n` : ''}${(0, mermaid_1.escapeMarkdown)(reconstruct)}\`"`;
|
|
94
88
|
output += ` n${id}[[${name}]]\n`;
|
|
95
|
-
diagramIncludedIds.add(vertex
|
|
89
|
+
diagramIncludedIds.add(control_flow_graph_1.CfgVertex.getId(vertex));
|
|
96
90
|
}
|
|
97
91
|
else {
|
|
98
|
-
if (includeOnlyIds && !
|
|
92
|
+
if (includeOnlyIds && !elems.some(elem => includeOnlyIds.has(control_flow_graph_1.CfgVertex.getId(elem)))) {
|
|
99
93
|
continue;
|
|
100
94
|
}
|
|
101
|
-
output += ` subgraph n${
|
|
95
|
+
output += ` subgraph n${id} [Block ${normalizedVertex?.info.fullLexeme ?? id}]\n`;
|
|
102
96
|
output += ` direction ${dirIs}\n`;
|
|
103
|
-
diagramIncludedIds.add(
|
|
97
|
+
diagramIncludedIds.add(id);
|
|
104
98
|
let last = undefined;
|
|
105
|
-
for (const element of
|
|
106
|
-
if (includeOnlyIds && !includeOnlyIds.has(element
|
|
99
|
+
for (const element of elems ?? []) {
|
|
100
|
+
if (includeOnlyIds && !includeOnlyIds.has(control_flow_graph_1.CfgVertex.getId(element))) {
|
|
107
101
|
last = undefined;
|
|
108
102
|
continue;
|
|
109
103
|
}
|
|
110
|
-
const
|
|
104
|
+
const eid = control_flow_graph_1.CfgVertex.getId(element);
|
|
105
|
+
const childNormalizedVertex = normalizedAst?.idMap.get(eid);
|
|
111
106
|
const childContent = getLexeme(childNormalizedVertex);
|
|
112
|
-
output = cfgOfNode(vertex, childNormalizedVertex,
|
|
113
|
-
diagramIncludedIds.add(
|
|
107
|
+
output = cfgOfNode(vertex, childNormalizedVertex, eid, childContent, output);
|
|
108
|
+
diagramIncludedIds.add(eid);
|
|
114
109
|
// just to keep the order
|
|
115
110
|
if (last) {
|
|
116
|
-
output += ` ${last} -.-> n${
|
|
111
|
+
output += ` ${last} -.-> n${eid}\n`;
|
|
117
112
|
}
|
|
118
|
-
last = `n${
|
|
113
|
+
last = `n${eid}`;
|
|
119
114
|
}
|
|
120
115
|
output += ' end\n';
|
|
121
116
|
}
|
|
122
117
|
}
|
|
123
118
|
else if (!includeOnlyIds || includeOnlyIds.has(id)) {
|
|
124
119
|
output = cfgOfNode(vertex, normalizedVertex, id, content, output);
|
|
125
|
-
diagramIncludedIds.add(
|
|
126
|
-
}
|
|
127
|
-
if (vertex.name === type_1.RType.ExpressionList && vertex.type === control_flow_graph_1.CfgVertexType.EndMarker) {
|
|
128
|
-
output += ' end\n';
|
|
120
|
+
diagramIncludedIds.add(id);
|
|
129
121
|
}
|
|
130
122
|
}
|
|
131
123
|
for (const [from, targets] of cfg.graph.edges()) {
|
|
@@ -136,9 +128,10 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
|
|
|
136
128
|
if (!diagramIncludedIds.has(to)) {
|
|
137
129
|
continue;
|
|
138
130
|
}
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
131
|
+
const isCd = control_flow_graph_1.CfgEdge.isControlDependency(edge);
|
|
132
|
+
const edgeType = isCd ? '-->' : '-.->';
|
|
133
|
+
const edgeSuffix = isCd ? ` (${control_flow_graph_1.CfgEdge.unpackWhen(edge)})` : '';
|
|
134
|
+
output += ` n${from} ${edgeType}|"${(0, mermaid_1.escapeMarkdown)(control_flow_graph_1.CfgEdge.typeToString(edge))}${edgeSuffix}"| n${to}\n`;
|
|
142
135
|
}
|
|
143
136
|
}
|
|
144
137
|
for (const entryPoint of cfg.entryPoints) {
|
|
@@ -152,9 +145,9 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
|
|
|
152
145
|
}
|
|
153
146
|
}
|
|
154
147
|
if (mark) {
|
|
155
|
-
for (const [
|
|
148
|
+
for (const [id, vertex] of cfg.graph.vertices(true)) {
|
|
156
149
|
if (shouldIncludeNode(hasBbandSimplify, vertex, mark)) {
|
|
157
|
-
output += ` style n${
|
|
150
|
+
output += ` style n${id} ${markStyle.vertex}`;
|
|
158
151
|
}
|
|
159
152
|
}
|
|
160
153
|
}
|
package/util/version.js
CHANGED
|
@@ -6,7 +6,7 @@ exports.printVersionInformation = printVersionInformation;
|
|
|
6
6
|
const semver_1 = require("semver");
|
|
7
7
|
const assert_1 = require("./assert");
|
|
8
8
|
// this is automatically replaced with the current version by release-it
|
|
9
|
-
const version = '2.9.
|
|
9
|
+
const version = '2.9.9';
|
|
10
10
|
/**
|
|
11
11
|
* Retrieves the current flowR version as a new {@link SemVer} object.
|
|
12
12
|
*/
|