@eagleoutice/flowr 2.1.2 → 2.1.3
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/cli/repl/commands/repl-query.d.ts +0 -5
- package/cli/repl/commands/repl-query.js +2 -142
- package/documentation/data/server/doc-data-server-messages.js +6 -1
- package/documentation/doc-util/doc-query.d.ts +5 -1
- package/documentation/doc-util/doc-query.js +149 -2
- package/documentation/print-query-wiki.js +32 -0
- package/package.json +3 -2
- package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
- package/queries/catalog/lineage-query/lineage-query-executor.d.ts +3 -0
- package/queries/catalog/lineage-query/lineage-query-executor.js +22 -0
- package/queries/catalog/lineage-query/lineage-query-format.d.ts +14 -0
- package/queries/catalog/lineage-query/lineage-query-format.js +3 -0
- package/queries/query-schema.d.ts +1 -0
- package/queries/query-schema.js +6 -2
- package/queries/query.d.ts +4 -1
- package/queries/query.js +3 -1
- package/util/version.js +1 -1
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import { DEFAULT_DATAFLOW_PIPELINE } from '../../../core/steps/pipeline/default-pipelines';
|
|
2
1
|
import type { ReplCommand } from './repl-main';
|
|
3
|
-
import type { OutputFormatter } from '../../../util/ansi';
|
|
4
|
-
import type { QueryResults, SupportedQueryTypes } from '../../../queries/query';
|
|
5
|
-
import type { PipelineOutput } from '../../../core/steps/pipeline/pipeline';
|
|
6
|
-
export declare function asciiSummaryOfQueryResult(formatter: OutputFormatter, totalInMs: number, results: QueryResults<SupportedQueryTypes>, processed: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>): string;
|
|
7
2
|
export declare const queryCommand: ReplCommand;
|
|
8
3
|
export declare const queryStarCommand: ReplCommand;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.queryStarCommand = exports.queryCommand = void 0;
|
|
4
|
-
exports.asciiSummaryOfQueryResult = asciiSummaryOfQueryResult;
|
|
5
4
|
const pipeline_executor_1 = require("../../../core/pipeline-executor");
|
|
6
5
|
const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
|
|
7
6
|
const retriever_1 = require("../../../r-bridge/retriever");
|
|
@@ -11,11 +10,6 @@ const schema_1 = require("../../../util/schema");
|
|
|
11
10
|
const query_1 = require("../../../queries/query");
|
|
12
11
|
const json_1 = require("../../../util/json");
|
|
13
12
|
const query_schema_1 = require("../../../queries/query-schema");
|
|
14
|
-
const built_in_1 = require("../../../dataflow/environments/built-in");
|
|
15
|
-
const dfg_1 = require("../../../util/mermaid/dfg");
|
|
16
|
-
const ast_1 = require("../../../util/mermaid/ast");
|
|
17
|
-
const time_1 = require("../../../util/time");
|
|
18
|
-
const doc_hover_over_1 = require("../../../documentation/doc-util/doc-hover-over");
|
|
19
13
|
async function getDataflow(shell, remainingLine) {
|
|
20
14
|
return await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_DATAFLOW_PIPELINE, {
|
|
21
15
|
shell,
|
|
@@ -60,141 +54,6 @@ async function processQueryArgs(line, shell, output) {
|
|
|
60
54
|
processed
|
|
61
55
|
};
|
|
62
56
|
}
|
|
63
|
-
function nodeString(id, formatter, processed) {
|
|
64
|
-
if (id === built_in_1.BuiltIn) {
|
|
65
|
-
return (0, ansi_1.italic)('built-in', formatter);
|
|
66
|
-
}
|
|
67
|
-
const node = processed.normalize.idMap.get(id);
|
|
68
|
-
if (node === undefined) {
|
|
69
|
-
return `UNKNOWN: ${id}`;
|
|
70
|
-
}
|
|
71
|
-
return `${(0, ansi_1.italic)('`' + (node.lexeme ?? node.info.fullLexeme ?? 'UNKNOWN') + '`', formatter)} (L.${node.location?.[0]})`;
|
|
72
|
-
}
|
|
73
|
-
function asciiCallContextSubHit(formatter, results, processed) {
|
|
74
|
-
const result = [];
|
|
75
|
-
for (const { id, calls = [], linkedIds = [], aliasRoots = [] } of results) {
|
|
76
|
-
const node = processed.normalize.idMap.get(id);
|
|
77
|
-
if (node === undefined) {
|
|
78
|
-
result.push(` ${(0, ansi_1.bold)('UNKNOWN: ' + JSON.stringify({ calls, linkedIds }))}`);
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
let line = nodeString(id, formatter, processed);
|
|
82
|
-
if (calls.length > 0) {
|
|
83
|
-
line += ` with ${calls.length} call${calls.length > 1 ? 's' : ''} (${calls.map(c => nodeString(c, formatter, processed)).join(', ')})`;
|
|
84
|
-
}
|
|
85
|
-
if (linkedIds.length > 0) {
|
|
86
|
-
line += ` with ${linkedIds.length} link${linkedIds.length > 1 ? 's' : ''} (${linkedIds.map(c => nodeString(c, formatter, processed)).join(', ')})`;
|
|
87
|
-
}
|
|
88
|
-
if (aliasRoots.length > 0) {
|
|
89
|
-
line += ` with ${aliasRoots.length} alias root${aliasRoots.length > 1 ? 's' : ''} (${aliasRoots.map(c => nodeString(c, formatter, processed)).join(', ')})`;
|
|
90
|
-
}
|
|
91
|
-
result.push(line);
|
|
92
|
-
}
|
|
93
|
-
return result.join(', ');
|
|
94
|
-
}
|
|
95
|
-
function asciiCallContext(formatter, results, processed) {
|
|
96
|
-
/* traverse over 'kinds' and within them 'subkinds' */
|
|
97
|
-
const result = [];
|
|
98
|
-
for (const [kind, { subkinds }] of Object.entries(results['kinds'])) {
|
|
99
|
-
result.push(` ╰ ${(0, ansi_1.bold)(kind, formatter)}`);
|
|
100
|
-
for (const [subkind, values] of Object.entries(subkinds)) {
|
|
101
|
-
result.push(` ╰ ${(0, ansi_1.bold)(subkind, formatter)}: ${asciiCallContextSubHit(formatter, values, processed)}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return result.join('\n');
|
|
105
|
-
}
|
|
106
|
-
function summarizeIdsIfTooLong(ids) {
|
|
107
|
-
const naive = ids.join(', ');
|
|
108
|
-
if (naive.length <= 20) {
|
|
109
|
-
return naive;
|
|
110
|
-
}
|
|
111
|
-
let acc = '';
|
|
112
|
-
let i = 0;
|
|
113
|
-
while (acc.length <= 20) {
|
|
114
|
-
acc += ids[i++] + ', ';
|
|
115
|
-
}
|
|
116
|
-
if (i < ids.length) {
|
|
117
|
-
acc += '... (see JSON below)';
|
|
118
|
-
}
|
|
119
|
-
return (0, doc_hover_over_1.textWithTooltip)(acc, JSON.stringify(ids));
|
|
120
|
-
}
|
|
121
|
-
function asciiSummaryOfQueryResult(formatter, totalInMs, results, processed) {
|
|
122
|
-
const result = [];
|
|
123
|
-
for (const [query, queryResults] of Object.entries(results)) {
|
|
124
|
-
if (query === '.meta') {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
if (query === 'call-context') {
|
|
128
|
-
const out = queryResults;
|
|
129
|
-
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
130
|
-
result.push(asciiCallContext(formatter, out, processed));
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
else if (query === 'dataflow') {
|
|
134
|
-
const out = queryResults;
|
|
135
|
-
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
136
|
-
result.push(` ╰ [Dataflow Graph](${(0, dfg_1.graphToMermaidUrl)(out.graph)})`);
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
else if (query === 'id-map') {
|
|
140
|
-
const out = queryResults;
|
|
141
|
-
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
142
|
-
result.push(` ╰ Id List: {${summarizeIdsIfTooLong([...out.idMap.keys()])}}`);
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
else if (query === 'normalized-ast') {
|
|
146
|
-
const out = queryResults;
|
|
147
|
-
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
148
|
-
result.push(` ╰ [Normalized AST](${(0, ast_1.normalizedAstToMermaidUrl)(out.normalized.ast)})`);
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
else if (query === 'static-slice') {
|
|
152
|
-
const out = queryResults;
|
|
153
|
-
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
154
|
-
for (const [fingerprint, obj] of Object.entries(out.results)) {
|
|
155
|
-
const { criteria, noMagicComments, noReconstruction } = JSON.parse(fingerprint);
|
|
156
|
-
const addons = [];
|
|
157
|
-
if (noReconstruction) {
|
|
158
|
-
addons.push('no reconstruction');
|
|
159
|
-
}
|
|
160
|
-
if (noMagicComments) {
|
|
161
|
-
addons.push('no magic comments');
|
|
162
|
-
}
|
|
163
|
-
result.push(` ╰ Slice for {${criteria.join(', ')}} ${addons.join(', ')}`);
|
|
164
|
-
if ('reconstruct' in obj) {
|
|
165
|
-
result.push(' ╰ Code (newline as <code>\n</code>): <code>' + obj.reconstruct.code.split('\n').join('\\n') + '</code>');
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
result.push(` ╰ Id List: {${summarizeIdsIfTooLong([...obj.slice.result])}}`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
else if (query === 'dataflow-cluster') {
|
|
174
|
-
const out = queryResults;
|
|
175
|
-
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
|
|
176
|
-
result.push(` ╰ Found ${out.clusters.length} cluster${out.clusters.length === 1 ? '' : 's'}`);
|
|
177
|
-
for (const cluster of out.clusters) {
|
|
178
|
-
const unknownSideEffects = cluster.hasUnknownSideEffects ? '(has unknown side effect)' : '';
|
|
179
|
-
result.push(` ╰ ${unknownSideEffects} {${summarizeIdsIfTooLong(cluster.members)}} ([marked](${(0, dfg_1.graphToMermaidUrl)(processed.dataflow.graph, false, new Set(cluster.members))}))`);
|
|
180
|
-
}
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)}`);
|
|
184
|
-
let timing = -1;
|
|
185
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
186
|
-
for (const [key, value] of Object.entries(queryResults)) {
|
|
187
|
-
if (key === '.meta') {
|
|
188
|
-
timing = value.timing;
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
result.push(` ╰ ${key}: ${JSON.stringify(value)}`);
|
|
192
|
-
}
|
|
193
|
-
result.push(` - Took ${(0, time_1.printAsMs)(timing, 0)}`);
|
|
194
|
-
}
|
|
195
|
-
result.push((0, ansi_1.italic)(`All queries together required ≈${(0, time_1.printAsMs)(results['.meta'].timing, 0)} (1ms accuracy, total ${(0, time_1.printAsMs)(totalInMs, 0)})`, formatter));
|
|
196
|
-
return formatter.format(result.join('\n'));
|
|
197
|
-
}
|
|
198
57
|
exports.queryCommand = {
|
|
199
58
|
description: `Query the given R code, start with '${retriever_1.fileProtocol}' to indicate a file. The query is to be a valid query in json format (use 'help' to get more information).`,
|
|
200
59
|
usageExample: ':query "<query>" <code>',
|
|
@@ -205,7 +64,8 @@ exports.queryCommand = {
|
|
|
205
64
|
const results = await processQueryArgs(remainingLine, shell, output);
|
|
206
65
|
const totalEnd = Date.now();
|
|
207
66
|
if (results) {
|
|
208
|
-
output.stdout(
|
|
67
|
+
output.stdout(JSON.stringify(results));
|
|
68
|
+
output.stdout('Total time: ' + (totalEnd - totalStart) + 'ms');
|
|
209
69
|
}
|
|
210
70
|
}
|
|
211
71
|
};
|
|
@@ -467,10 +467,15 @@ See [above](#message-request-file-analysis) for the general structure of the res
|
|
|
467
467
|
end
|
|
468
468
|
deactivate Server
|
|
469
469
|
`,
|
|
470
|
-
shortDescription: 'Obtain the lineage of a given slicing criterion.',
|
|
470
|
+
shortDescription: '([DEPRECATED](${FlowrWikiBaseRef}/Query%20API)) Obtain the lineage of a given slicing criterion.',
|
|
471
471
|
text: async (shell) => {
|
|
472
472
|
return `
|
|
473
473
|
|
|
474
|
+
${(0, doc_structure_1.block)({
|
|
475
|
+
type: 'WARNING',
|
|
476
|
+
content: `We deprecated the lineage request in favor of the \`lineage\` [Query](${doc_files_1.FlowrWikiBaseRef}/Query%20API).`
|
|
477
|
+
})}
|
|
478
|
+
|
|
474
479
|
In order to retrieve the lineage of an object, you have to send a file analysis request first. The \`filetoken\` you assign is of use here as you can re-use it to repeatedly retrieve the lineage of the same file.
|
|
475
480
|
Besides that, you will need to add a [criterion](${doc_files_1.FlowrWikiBaseRef}/Terminology#slicing-criterion) that specifies the object whose lineage you're interested in.
|
|
476
481
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { RShell } from '../../r-bridge/shell';
|
|
2
|
-
import type { Queries, SupportedQueryTypes } from '../../queries/query';
|
|
2
|
+
import type { Queries, QueryResults, SupportedQueryTypes } from '../../queries/query';
|
|
3
|
+
import { DEFAULT_DATAFLOW_PIPELINE } from '../../core/steps/pipeline/default-pipelines';
|
|
4
|
+
import { type OutputFormatter } from '../../util/ansi';
|
|
3
5
|
import type { SupportedVirtualQueryTypes } from '../../queries/virtual-query/virtual-queries';
|
|
4
6
|
import type { VirtualCompoundConstraint } from '../../queries/virtual-query/compound-query';
|
|
7
|
+
import type { PipelineOutput } from '../../core/steps/pipeline/pipeline';
|
|
5
8
|
export interface ShowQueryOptions {
|
|
6
9
|
readonly showCode?: boolean;
|
|
7
10
|
readonly collapseResult?: boolean;
|
|
8
11
|
}
|
|
12
|
+
export declare function asciiSummaryOfQueryResult(formatter: OutputFormatter, totalInMs: number, results: QueryResults<SupportedQueryTypes>, processed: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>): string;
|
|
9
13
|
export declare function showQuery<Base extends SupportedQueryTypes, VirtualArguments extends VirtualCompoundConstraint<Base> = VirtualCompoundConstraint<Base>>(shell: RShell, code: string, queries: Queries<Base, VirtualArguments>, { showCode, collapseResult }?: ShowQueryOptions): Promise<string>;
|
|
10
14
|
export interface QueryDocumentation {
|
|
11
15
|
readonly name: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RegisteredQueries = void 0;
|
|
4
|
+
exports.asciiSummaryOfQueryResult = asciiSummaryOfQueryResult;
|
|
4
5
|
exports.showQuery = showQuery;
|
|
5
6
|
exports.registerQueryDocumentation = registerQueryDocumentation;
|
|
6
7
|
exports.tocForQueryType = tocForQueryType;
|
|
@@ -11,11 +12,157 @@ const default_pipelines_1 = require("../../core/steps/pipeline/default-pipelines
|
|
|
11
12
|
const retriever_1 = require("../../r-bridge/retriever");
|
|
12
13
|
const json_1 = require("../../util/json");
|
|
13
14
|
const ansi_1 = require("../../util/ansi");
|
|
14
|
-
const repl_query_1 = require("../../cli/repl/commands/repl-query");
|
|
15
15
|
const doc_files_1 = require("./doc-files");
|
|
16
16
|
const doc_dfg_1 = require("./doc-dfg");
|
|
17
17
|
const doc_code_1 = require("./doc-code");
|
|
18
18
|
const time_1 = require("../../util/time");
|
|
19
|
+
const built_in_1 = require("../../dataflow/environments/built-in");
|
|
20
|
+
const dfg_1 = require("../../util/mermaid/dfg");
|
|
21
|
+
const ast_1 = require("../../util/mermaid/ast");
|
|
22
|
+
const doc_hover_over_1 = require("./doc-hover-over");
|
|
23
|
+
function nodeString(id, formatter, processed) {
|
|
24
|
+
if (id === built_in_1.BuiltIn) {
|
|
25
|
+
return (0, ansi_1.italic)('built-in', formatter);
|
|
26
|
+
}
|
|
27
|
+
const node = processed.normalize.idMap.get(id);
|
|
28
|
+
if (node === undefined) {
|
|
29
|
+
return `UNKNOWN: ${id}`;
|
|
30
|
+
}
|
|
31
|
+
return `${(0, ansi_1.italic)('`' + (node.lexeme ?? node.info.fullLexeme ?? 'UNKNOWN') + '`', formatter)} (L.${node.location?.[0]})`;
|
|
32
|
+
}
|
|
33
|
+
function asciiCallContextSubHit(formatter, results, processed) {
|
|
34
|
+
const result = [];
|
|
35
|
+
for (const { id, calls = [], linkedIds = [], aliasRoots = [] } of results) {
|
|
36
|
+
const node = processed.normalize.idMap.get(id);
|
|
37
|
+
if (node === undefined) {
|
|
38
|
+
result.push(` ${(0, ansi_1.bold)('UNKNOWN: ' + JSON.stringify({ calls, linkedIds }))}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
let line = nodeString(id, formatter, processed);
|
|
42
|
+
if (calls.length > 0) {
|
|
43
|
+
line += ` with ${calls.length} call${calls.length > 1 ? 's' : ''} (${calls.map(c => nodeString(c, formatter, processed)).join(', ')})`;
|
|
44
|
+
}
|
|
45
|
+
if (linkedIds.length > 0) {
|
|
46
|
+
line += ` with ${linkedIds.length} link${linkedIds.length > 1 ? 's' : ''} (${linkedIds.map(c => nodeString(c, formatter, processed)).join(', ')})`;
|
|
47
|
+
}
|
|
48
|
+
if (aliasRoots.length > 0) {
|
|
49
|
+
line += ` with ${aliasRoots.length} alias root${aliasRoots.length > 1 ? 's' : ''} (${aliasRoots.map(c => nodeString(c, formatter, processed)).join(', ')})`;
|
|
50
|
+
}
|
|
51
|
+
result.push(line);
|
|
52
|
+
}
|
|
53
|
+
return result.join(', ');
|
|
54
|
+
}
|
|
55
|
+
function asciiCallContext(formatter, results, processed) {
|
|
56
|
+
/* traverse over 'kinds' and within them 'subkinds' */
|
|
57
|
+
const result = [];
|
|
58
|
+
for (const [kind, { subkinds }] of Object.entries(results['kinds'])) {
|
|
59
|
+
result.push(` ╰ ${(0, ansi_1.bold)(kind, formatter)}`);
|
|
60
|
+
for (const [subkind, values] of Object.entries(subkinds)) {
|
|
61
|
+
result.push(` ╰ ${(0, ansi_1.bold)(subkind, formatter)}: ${asciiCallContextSubHit(formatter, values, processed)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return result.join('\n');
|
|
65
|
+
}
|
|
66
|
+
function summarizeIdsIfTooLong(ids) {
|
|
67
|
+
const naive = ids.join(', ');
|
|
68
|
+
if (naive.length <= 20) {
|
|
69
|
+
return naive;
|
|
70
|
+
}
|
|
71
|
+
let acc = '';
|
|
72
|
+
let i = 0;
|
|
73
|
+
while (acc.length <= 20) {
|
|
74
|
+
acc += ids[i++] + ', ';
|
|
75
|
+
}
|
|
76
|
+
if (i < ids.length) {
|
|
77
|
+
acc += '... (see JSON below)';
|
|
78
|
+
}
|
|
79
|
+
return (0, doc_hover_over_1.textWithTooltip)(acc, JSON.stringify(ids));
|
|
80
|
+
}
|
|
81
|
+
function asciiSummaryOfQueryResult(formatter, totalInMs, results, processed) {
|
|
82
|
+
const result = [];
|
|
83
|
+
for (const [query, queryResults] of Object.entries(results)) {
|
|
84
|
+
if (query === '.meta') {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (query === 'call-context') {
|
|
88
|
+
const out = queryResults;
|
|
89
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
90
|
+
result.push(asciiCallContext(formatter, out, processed));
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
else if (query === 'dataflow') {
|
|
94
|
+
const out = queryResults;
|
|
95
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
96
|
+
result.push(` ╰ [Dataflow Graph](${(0, dfg_1.graphToMermaidUrl)(out.graph)})`);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
else if (query === 'id-map') {
|
|
100
|
+
const out = queryResults;
|
|
101
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
102
|
+
result.push(` ╰ Id List: {${summarizeIdsIfTooLong([...out.idMap.keys()])}}`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
else if (query === 'normalized-ast') {
|
|
106
|
+
const out = queryResults;
|
|
107
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
108
|
+
result.push(` ╰ [Normalized AST](${(0, ast_1.normalizedAstToMermaidUrl)(out.normalized.ast)})`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
else if (query === 'static-slice') {
|
|
112
|
+
const out = queryResults;
|
|
113
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
114
|
+
for (const [fingerprint, obj] of Object.entries(out.results)) {
|
|
115
|
+
const { criteria, noMagicComments, noReconstruction } = JSON.parse(fingerprint);
|
|
116
|
+
const addons = [];
|
|
117
|
+
if (noReconstruction) {
|
|
118
|
+
addons.push('no reconstruction');
|
|
119
|
+
}
|
|
120
|
+
if (noMagicComments) {
|
|
121
|
+
addons.push('no magic comments');
|
|
122
|
+
}
|
|
123
|
+
result.push(` ╰ Slice for {${criteria.join(', ')}} ${addons.join(', ')}`);
|
|
124
|
+
if ('reconstruct' in obj) {
|
|
125
|
+
result.push(' ╰ Code (newline as <code>\n</code>): <code>' + obj.reconstruct.code.split('\n').join('\\n') + '</code>');
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
result.push(` ╰ Id List: {${summarizeIdsIfTooLong([...obj.slice.result])}}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
else if (query === 'dataflow-cluster') {
|
|
134
|
+
const out = queryResults;
|
|
135
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
|
|
136
|
+
result.push(` ╰ Found ${out.clusters.length} cluster${out.clusters.length === 1 ? '' : 's'}`);
|
|
137
|
+
for (const cluster of out.clusters) {
|
|
138
|
+
const unknownSideEffects = cluster.hasUnknownSideEffects ? '(has unknown side effect)' : '';
|
|
139
|
+
result.push(` ╰ ${unknownSideEffects} {${summarizeIdsIfTooLong(cluster.members)}} ([marked](${(0, dfg_1.graphToMermaidUrl)(processed.dataflow.graph, false, new Set(cluster.members))}))`);
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
else if (query === 'lineage') {
|
|
144
|
+
const out = queryResults;
|
|
145
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
146
|
+
for (const [criteria, lineage] of Object.entries(out.lineages)) {
|
|
147
|
+
result.push(` ╰ ${criteria}: {${summarizeIdsIfTooLong([...lineage])}}`);
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
result.push(`Query: ${(0, ansi_1.bold)(query, formatter)}`);
|
|
152
|
+
let timing = -1;
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
154
|
+
for (const [key, value] of Object.entries(queryResults)) {
|
|
155
|
+
if (key === '.meta') {
|
|
156
|
+
timing = value.timing;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
result.push(` ╰ ${key}: ${JSON.stringify(value)}`);
|
|
160
|
+
}
|
|
161
|
+
result.push(` - Took ${(0, time_1.printAsMs)(timing, 0)}`);
|
|
162
|
+
}
|
|
163
|
+
result.push((0, ansi_1.italic)(`All queries together required ≈${(0, time_1.printAsMs)(results['.meta'].timing, 0)} (1ms accuracy, total ${(0, time_1.printAsMs)(totalInMs, 0)})`, formatter));
|
|
164
|
+
return formatter.format(result.join('\n'));
|
|
165
|
+
}
|
|
19
166
|
async function showQuery(shell, code, queries, { showCode, collapseResult } = {}) {
|
|
20
167
|
const now = performance.now();
|
|
21
168
|
const analysis = await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_DATAFLOW_PIPELINE, {
|
|
@@ -37,7 +184,7 @@ ${collapseResult ? ' <details> <summary style="color:gray">Show Results</summary
|
|
|
37
184
|
|
|
38
185
|
_Results (prettified and summarized):_
|
|
39
186
|
|
|
40
|
-
${
|
|
187
|
+
${asciiSummaryOfQueryResult(ansi_1.markdownFormatter, duration, results, analysis)}
|
|
41
188
|
|
|
42
189
|
<details> <summary style="color:gray">Show Detailed Results as Json</summary>
|
|
43
190
|
|
|
@@ -21,6 +21,7 @@ const id_map_query_executor_1 = require("../queries/catalog/id-map-query/id-map-
|
|
|
21
21
|
const normalized_ast_query_executor_1 = require("../queries/catalog/normalized-ast-query/normalized-ast-query-executor");
|
|
22
22
|
const cluster_query_executor_1 = require("../queries/catalog/cluster-query/cluster-query-executor");
|
|
23
23
|
const static_slice_query_executor_1 = require("../queries/catalog/static-slice-query/static-slice-query-executor");
|
|
24
|
+
const lineage_query_executor_1 = require("../queries/catalog/lineage-query/lineage-query-executor");
|
|
24
25
|
(0, doc_query_1.registerQueryDocumentation)('call-context', {
|
|
25
26
|
name: 'Call-Context Query',
|
|
26
27
|
type: 'active',
|
|
@@ -121,6 +122,37 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
|
121
122
|
`;
|
|
122
123
|
}
|
|
123
124
|
});
|
|
125
|
+
(0, doc_query_1.registerQueryDocumentation)('lineage', {
|
|
126
|
+
name: 'Lineage Query',
|
|
127
|
+
type: 'active',
|
|
128
|
+
shortDescription: 'Returns lineage of a criteria.',
|
|
129
|
+
functionName: lineage_query_executor_1.executeLineageQuery.name,
|
|
130
|
+
functionFile: '../queries/catalog/lineage-query/lineage-query-executor.ts',
|
|
131
|
+
buildExplanation: async (shell) => {
|
|
132
|
+
const exampleCode = 'x <- 1\nx';
|
|
133
|
+
return `
|
|
134
|
+
This query calculates the _lineage_ of a given slicing criterion. The lineage traces back all parts that the
|
|
135
|
+
respective variables stems from given the reads, definitions, and returns in the dataflow graph.
|
|
136
|
+
|
|
137
|
+
To understand this, let's start with a simple example query, to get the lineage of the second use of \`x\` in the following code:
|
|
138
|
+
${(0, doc_code_1.codeBlock)('r', exampleCode)}
|
|
139
|
+
|
|
140
|
+
For this, we use the criterion \`2@x\` (which is the first use of \`x\` in the second line).
|
|
141
|
+
|
|
142
|
+
${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
143
|
+
type: 'lineage',
|
|
144
|
+
criterion: '2@x'
|
|
145
|
+
}], { showCode: false })}
|
|
146
|
+
|
|
147
|
+
In this simple scenario, the _lineage_ is equivalent to the slice (and in-fact the complete code).
|
|
148
|
+
In general the lineage is smaller and makes no executability guarantees.
|
|
149
|
+
It is just a quick and neither complete nor sound way to get information on where the variable originates from.
|
|
150
|
+
|
|
151
|
+
This query replaces the old [\`request-lineage\`](${doc_files_1.FlowrWikiBaseRef}/Interface#message-request-lineage) message.
|
|
152
|
+
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
124
156
|
(0, doc_query_1.registerQueryDocumentation)('dataflow-cluster', {
|
|
125
157
|
name: 'Dataflow Cluster Query',
|
|
126
158
|
type: 'active',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
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": {
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"test": "nyc --no-clean mocha",
|
|
39
39
|
"performance-test": "func() { cd test/performance/ && bash run-all-suites.sh $1 $2 $3; cd ../../; }; func",
|
|
40
40
|
"test-full": "npm run test -- --test-installation",
|
|
41
|
-
"detect-circular-deps": "npx madge --extensions ts,tsx --circular src/"
|
|
41
|
+
"detect-circular-deps": "npx madge --extensions ts,tsx --circular src/",
|
|
42
|
+
"checkup": "npm run flowr -- --execute \":version\" && npm run lint && npm run test-full -- --forbid-only && docker build -t test-flowr -f scripts/Dockerfile . && npm run doc && npm-run-all wiki:*"
|
|
42
43
|
},
|
|
43
44
|
"keywords": [
|
|
44
45
|
"static code analysis",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeLineageQuery = executeLineageQuery;
|
|
4
|
+
const log_1 = require("../../../util/log");
|
|
5
|
+
const repl_lineage_1 = require("../../../cli/repl/commands/repl-lineage");
|
|
6
|
+
function executeLineageQuery({ graph, ast }, queries) {
|
|
7
|
+
const start = Date.now();
|
|
8
|
+
const result = {};
|
|
9
|
+
for (const { criterion } of queries) {
|
|
10
|
+
if (result[criterion]) {
|
|
11
|
+
log_1.log.warn('Duplicate criterion in lineage query:', criterion);
|
|
12
|
+
}
|
|
13
|
+
result[criterion] = (0, repl_lineage_1.getLineage)(criterion, graph, ast.idMap);
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
'.meta': {
|
|
17
|
+
timing: Date.now() - start
|
|
18
|
+
},
|
|
19
|
+
lineages: result
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=lineage-query-executor.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BaseQueryFormat, BaseQueryResult } from '../../base-query-format';
|
|
2
|
+
import type { SingleSlicingCriterion } from '../../../slicing/criterion/parse';
|
|
3
|
+
import type { NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
4
|
+
/**
|
|
5
|
+
* Calculates the lineage of the given criterion.
|
|
6
|
+
*/
|
|
7
|
+
export interface LineageQuery extends BaseQueryFormat {
|
|
8
|
+
readonly type: 'lineage';
|
|
9
|
+
readonly criterion: SingleSlicingCriterion;
|
|
10
|
+
}
|
|
11
|
+
export interface LineageQueryResult extends BaseQueryResult {
|
|
12
|
+
/** Maps each criterion to the found lineage, duplicates are ignored. */
|
|
13
|
+
readonly lineages: Record<SingleSlicingCriterion, Set<NodeId>>;
|
|
14
|
+
}
|
|
@@ -5,6 +5,7 @@ export declare const IdMapQuerySchema: Joi.ObjectSchema<any>;
|
|
|
5
5
|
export declare const NormalizedAstQuerySchema: Joi.ObjectSchema<any>;
|
|
6
6
|
export declare const DataflowClusterQuerySchema: Joi.ObjectSchema<any>;
|
|
7
7
|
export declare const StaticSliceQuerySchema: Joi.ObjectSchema<any>;
|
|
8
|
+
export declare const LineageQuerySchema: Joi.ObjectSchema<any>;
|
|
8
9
|
export declare const SupportedQueriesSchema: Joi.AlternativesSchema<any>;
|
|
9
10
|
export declare const CompoundQuerySchema: Joi.ObjectSchema<any>;
|
|
10
11
|
export declare const VirtualQuerySchema: Joi.AlternativesSchema<any>;
|
package/queries/query-schema.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.QueriesSchema = exports.AnyQuerySchema = exports.VirtualQuerySchema = exports.CompoundQuerySchema = exports.SupportedQueriesSchema = exports.StaticSliceQuerySchema = exports.DataflowClusterQuerySchema = exports.NormalizedAstQuerySchema = exports.IdMapQuerySchema = exports.DataflowQuerySchema = exports.CallContextQuerySchema = void 0;
|
|
6
|
+
exports.QueriesSchema = exports.AnyQuerySchema = exports.VirtualQuerySchema = exports.CompoundQuerySchema = exports.SupportedQueriesSchema = exports.LineageQuerySchema = exports.StaticSliceQuerySchema = exports.DataflowClusterQuerySchema = exports.NormalizedAstQuerySchema = exports.IdMapQuerySchema = exports.DataflowQuerySchema = exports.CallContextQuerySchema = void 0;
|
|
7
7
|
const joi_1 = __importDefault(require("joi"));
|
|
8
8
|
const call_context_query_format_1 = require("./catalog/call-context-query/call-context-query-format");
|
|
9
9
|
exports.CallContextQuerySchema = joi_1.default.object({
|
|
@@ -37,7 +37,11 @@ exports.StaticSliceQuerySchema = joi_1.default.object({
|
|
|
37
37
|
noReconstruction: joi_1.default.boolean().optional().description('Do not reconstruct the slice into readable code.'),
|
|
38
38
|
noMagicComments: joi_1.default.boolean().optional().description('Should the magic comments (force-including lines within the slice) be ignored?')
|
|
39
39
|
}).description('Slice query used to slice the dataflow graph');
|
|
40
|
-
exports.
|
|
40
|
+
exports.LineageQuerySchema = joi_1.default.object({
|
|
41
|
+
type: joi_1.default.string().valid('lineage').required().description('The type of the query.'),
|
|
42
|
+
id: joi_1.default.string().required().description('The ID of the node to get the lineage of.')
|
|
43
|
+
}).description('Lineage query used to find the lineage of a node in the dataflow graph');
|
|
44
|
+
exports.SupportedQueriesSchema = joi_1.default.alternatives(exports.CallContextQuerySchema, exports.DataflowQuerySchema, exports.IdMapQuerySchema, exports.NormalizedAstQuerySchema, exports.DataflowClusterQuerySchema, exports.StaticSliceQuerySchema, exports.LineageQuerySchema).description('Supported queries');
|
|
41
45
|
exports.CompoundQuerySchema = joi_1.default.object({
|
|
42
46
|
type: joi_1.default.string().valid('compound').required().description('The type of the query.'),
|
|
43
47
|
query: joi_1.default.string().required().description('The query to run on the file analysis information.'),
|
package/queries/query.d.ts
CHANGED
|
@@ -15,7 +15,9 @@ import type { DataflowClusterQuery } from './catalog/cluster-query/cluster-query
|
|
|
15
15
|
import { executeDataflowClusterQuery } from './catalog/cluster-query/cluster-query-executor';
|
|
16
16
|
import type { StaticSliceQuery } from './catalog/static-slice-query/static-slice-query-format';
|
|
17
17
|
import { executeStaticSliceClusterQuery } from './catalog/static-slice-query/static-slice-query-executor';
|
|
18
|
-
|
|
18
|
+
import type { LineageQuery } from './catalog/lineage-query/lineage-query-format';
|
|
19
|
+
import { executeLineageQuery } from './catalog/lineage-query/lineage-query-executor';
|
|
20
|
+
export type Query = CallContextQuery | DataflowQuery | NormalizedAstQuery | IdMapQuery | DataflowClusterQuery | StaticSliceQuery | LineageQuery;
|
|
19
21
|
export type QueryArgumentsWithType<QueryType extends BaseQueryFormat['type']> = Query & {
|
|
20
22
|
type: QueryType;
|
|
21
23
|
};
|
|
@@ -34,6 +36,7 @@ export declare const SupportedQueries: {
|
|
|
34
36
|
readonly 'normalized-ast': typeof executeNormalizedAstQuery;
|
|
35
37
|
readonly 'dataflow-cluster': typeof executeDataflowClusterQuery;
|
|
36
38
|
readonly 'static-slice': typeof executeStaticSliceClusterQuery;
|
|
39
|
+
readonly lineage: typeof executeLineageQuery;
|
|
37
40
|
};
|
|
38
41
|
export type SupportedQueryTypes = keyof typeof SupportedQueries;
|
|
39
42
|
export type QueryResult<Type extends Query['type']> = ReturnType<typeof SupportedQueries[Type]>;
|
package/queries/query.js
CHANGED
|
@@ -11,13 +11,15 @@ const id_map_query_executor_1 = require("./catalog/id-map-query/id-map-query-exe
|
|
|
11
11
|
const normalized_ast_query_executor_1 = require("./catalog/normalized-ast-query/normalized-ast-query-executor");
|
|
12
12
|
const cluster_query_executor_1 = require("./catalog/cluster-query/cluster-query-executor");
|
|
13
13
|
const static_slice_query_executor_1 = require("./catalog/static-slice-query/static-slice-query-executor");
|
|
14
|
+
const lineage_query_executor_1 = require("./catalog/lineage-query/lineage-query-executor");
|
|
14
15
|
exports.SupportedQueries = {
|
|
15
16
|
'call-context': call_context_query_executor_1.executeCallContextQueries,
|
|
16
17
|
'dataflow': dataflow_query_executor_1.executeDataflowQuery,
|
|
17
18
|
'id-map': id_map_query_executor_1.executeIdMapQuery,
|
|
18
19
|
'normalized-ast': normalized_ast_query_executor_1.executeNormalizedAstQuery,
|
|
19
20
|
'dataflow-cluster': cluster_query_executor_1.executeDataflowClusterQuery,
|
|
20
|
-
'static-slice': static_slice_query_executor_1.executeStaticSliceClusterQuery
|
|
21
|
+
'static-slice': static_slice_query_executor_1.executeStaticSliceClusterQuery,
|
|
22
|
+
'lineage': lineage_query_executor_1.executeLineageQuery
|
|
21
23
|
};
|
|
22
24
|
function executeQueriesOfSameType(data, ...queries) {
|
|
23
25
|
(0, assert_1.guard)(queries.length > 0, 'At least one query must be provided');
|
package/util/version.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.flowrVersion = flowrVersion;
|
|
4
4
|
const semver_1 = require("semver");
|
|
5
5
|
// this is automatically replaced with the current version by release-it
|
|
6
|
-
const version = '2.1.
|
|
6
|
+
const version = '2.1.3';
|
|
7
7
|
function flowrVersion() {
|
|
8
8
|
return new semver_1.SemVer(version);
|
|
9
9
|
}
|