@eagleoutice/flowr 2.9.5 → 2.9.7
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 +32 -30
- package/abstract-interpretation/data-frame/dataframe-domain.js +1 -2
- package/benchmark/slicer.d.ts +0 -1
- package/benchmark/slicer.js +3 -9
- package/benchmark/stats/print.js +1 -0
- package/benchmark/summarizer/data.d.ts +2 -0
- package/benchmark/summarizer/first-phase/process.js +3 -1
- package/benchmark/summarizer/second-phase/process.js +4 -0
- package/cli/repl/commands/repl-commands.d.ts +1 -0
- package/cli/repl/commands/repl-commands.js +1 -0
- package/cli/repl/commands/repl-normalize.d.ts +1 -0
- package/cli/repl/commands/repl-normalize.js +28 -1
- package/control-flow/basic-cfg-guided-visitor.js +1 -3
- package/control-flow/cfg-dead-code.js +15 -10
- package/control-flow/control-flow-graph.d.ts +3 -1
- package/control-flow/control-flow-graph.js +26 -19
- package/control-flow/extract-cfg.d.ts +2 -1
- package/control-flow/extract-cfg.js +11 -2
- package/control-flow/simple-visitor.d.ts +1 -2
- package/control-flow/simple-visitor.js +16 -13
- package/dataflow/fn/recursive-function.js +1 -1
- package/documentation/doc-readme.js +11 -9
- package/documentation/wiki-absint.js +4 -3
- package/documentation/wiki-mk/doc-context.d.ts +3 -0
- package/documentation/wiki-mk/doc-context.js +4 -1
- package/linter/linter-rules.d.ts +2 -2
- package/linter/rules/dataframe-access-validation.d.ts +2 -2
- package/linter/rules/dataframe-access-validation.js +3 -3
- package/package.json +1 -1
- package/project/cache/flowr-analyzer-controlflow-cache.js +3 -0
- package/project/cfg-kind.d.ts +5 -1
- package/project/cfg-kind.js +5 -1
- package/project/plugins/file-plugins/files/flowr-namespace-file.js +3 -2
- package/project/plugins/file-plugins/files/flowr-rmarkdown-file.d.ts +3 -0
- package/project/plugins/file-plugins/files/flowr-rmarkdown-file.js +13 -2
- package/project/plugins/file-plugins/files/flowr-sweave-file.d.ts +70 -0
- package/project/plugins/file-plugins/files/flowr-sweave-file.js +163 -0
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-sweave-file-plugin.d.ts +22 -0
- package/project/plugins/file-plugins/notebooks/flowr-analyzer-sweave-file-plugin.js +33 -0
- package/project/plugins/flowr-analyzer-plugin-defaults.js +2 -0
- package/project/plugins/plugin-registry.d.ts +2 -1
- package/project/plugins/plugin-registry.js +2 -0
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +1 -1
- package/queries/catalog/call-context-query/call-context-query-executor.d.ts +3 -2
- package/queries/catalog/call-context-query/call-context-query-executor.js +16 -11
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +1 -1
- package/queries/catalog/call-context-query/identify-link-to-nested-call-relation.js +7 -4
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +99 -81
- package/queries/catalog/df-shape-query/df-shape-query-executor.js +12 -5
- package/queries/query.js +4 -3
- package/r-bridge/data/data.d.ts +20 -0
- package/r-bridge/data/data.js +24 -0
- package/r-bridge/lang-4.x/convert-values.js +1 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +64 -34
- package/util/r-regex.d.ts +4 -0
- package/util/r-regex.js +35 -3
- package/util/version.js +1 -1
- package/control-flow/invert-cfg.d.ts +0 -5
- package/control-flow/invert-cfg.js +0 -20
|
@@ -4,26 +4,39 @@ exports.executeDependenciesQuery = executeDependenciesQuery;
|
|
|
4
4
|
const query_1 = require("../../query");
|
|
5
5
|
const dependencies_query_format_1 = require("./dependencies-query-format");
|
|
6
6
|
const vertex_1 = require("../../../dataflow/graph/vertex");
|
|
7
|
-
const log_1 = require("../../../util/log");
|
|
8
7
|
const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
|
|
9
8
|
const objects_1 = require("../../../util/objects");
|
|
10
9
|
const function_info_1 = require("./function-info/function-info");
|
|
11
10
|
const identify_link_to_last_call_relation_1 = require("../call-context-query/identify-link-to-last-call-relation");
|
|
12
11
|
const resolve_argument_1 = require("../../../dataflow/eval/resolve/resolve-argument");
|
|
13
12
|
const assert_1 = require("../../../util/assert");
|
|
13
|
+
const log_1 = require("../../../util/log");
|
|
14
14
|
/**
|
|
15
15
|
* Executes a dependencies query.
|
|
16
16
|
*/
|
|
17
17
|
async function executeDependenciesQuery({ analyzer, }, queries) {
|
|
18
|
+
let query = queries[0];
|
|
18
19
|
if (queries.length !== 1) {
|
|
19
|
-
|
|
20
|
+
// merge
|
|
21
|
+
for (let i = 1; i < queries.length; i++) {
|
|
22
|
+
const q = queries[i];
|
|
23
|
+
query = {
|
|
24
|
+
...query,
|
|
25
|
+
enabledCategories: query.enabledCategories === undefined && q.enabledCategories === undefined ? undefined : [...(query.enabledCategories ?? []), ...(q.enabledCategories ?? [])],
|
|
26
|
+
ignoreDefaultFunctions: query.ignoreDefaultFunctions || q.ignoreDefaultFunctions,
|
|
27
|
+
additionalCategories: {
|
|
28
|
+
...query.additionalCategories,
|
|
29
|
+
...q.additionalCategories
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
log_1.log.info('Merged multiple dependencies queries into one:', query);
|
|
20
34
|
}
|
|
21
35
|
const data = { analyzer };
|
|
22
36
|
const normalize = await analyzer.normalize();
|
|
23
37
|
const dataflow = await analyzer.dataflow();
|
|
24
38
|
const config = analyzer.flowrConfig;
|
|
25
39
|
const now = Date.now();
|
|
26
|
-
const [query] = queries;
|
|
27
40
|
const ignoreDefault = query.ignoreDefaultFunctions ?? false;
|
|
28
41
|
const functions = new Map(Object.entries(dependencies_query_format_1.DefaultDependencyCategories).map(([c, v]) => {
|
|
29
42
|
return [c, getFunctionsToCheck(query[`${c}Functions`], c, query.enabledCategories, ignoreDefault, v.functions)];
|
|
@@ -35,12 +48,12 @@ async function executeDependenciesQuery({ analyzer, }, queries) {
|
|
|
35
48
|
}
|
|
36
49
|
}
|
|
37
50
|
const queryResults = functions.values().toArray().flat().length === 0 ? { kinds: {}, '.meta': { timing: 0 } } :
|
|
38
|
-
await (0, query_1.executeQueriesOfSameType)(data, functions.entries().
|
|
51
|
+
await (0, query_1.executeQueriesOfSameType)(data, functions.entries().flatMap(makeCallContextQuery).toArray());
|
|
39
52
|
const g = (0, dependencies_query_format_1.getAllCategories)(queries);
|
|
53
|
+
const enabled = query.enabledCategories;
|
|
40
54
|
const results = Object.fromEntries(await Promise.all(functions.entries().map(async ([c, f]) => {
|
|
41
55
|
const results = getResults(queries, { dataflow, config, normalize }, queryResults, c, f, data);
|
|
42
56
|
// only default categories allow additional analyses, so we null-coalesce here!
|
|
43
|
-
const enabled = query.enabledCategories;
|
|
44
57
|
if (enabled === undefined || (enabled?.length > 0 && enabled.includes(c))) {
|
|
45
58
|
await g[c]?.additionalAnalysis?.(data, ignoreDefault, f, queryResults, results);
|
|
46
59
|
}
|
|
@@ -53,7 +66,7 @@ async function executeDependenciesQuery({ analyzer, }, queries) {
|
|
|
53
66
|
...results,
|
|
54
67
|
};
|
|
55
68
|
}
|
|
56
|
-
function makeCallContextQuery(
|
|
69
|
+
function makeCallContextQuery([kind, functions]) {
|
|
57
70
|
return functions.map(f => ({
|
|
58
71
|
type: 'call-context',
|
|
59
72
|
callName: f.name,
|
|
@@ -75,90 +88,95 @@ const readOnlyModes = new Set(['r', 'rt', 'rb']);
|
|
|
75
88
|
const writeOnlyModes = new Set(['w', 'wt', 'wb', 'a', 'at', 'ab']);
|
|
76
89
|
function getResults(queries, { dataflow, config, normalize }, results, kind, functions, data) {
|
|
77
90
|
const defaultValue = (0, dependencies_query_format_1.getAllCategories)(queries)[kind].defaultValue;
|
|
91
|
+
const vars = config.solver.variables;
|
|
78
92
|
const functionMap = new Map(functions.map(f => [f.name, f]));
|
|
79
93
|
const kindEntries = Object.entries(results?.kinds[kind]?.subkinds ?? {});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
+
const finalResults = [];
|
|
95
|
+
const ictx = data.analyzer.inspectContext();
|
|
96
|
+
const d = ictx.deps;
|
|
97
|
+
const dfg = dataflow.graph;
|
|
98
|
+
for (const [name, results] of kindEntries) {
|
|
99
|
+
for (const { id, linkedIds } of results) {
|
|
100
|
+
const vertex = dfg.getVertex(id);
|
|
101
|
+
const info = functionMap.get(name);
|
|
102
|
+
const args = (0, resolve_argument_1.getArgumentStringValue)(vars, dfg, vertex, info.argIdx, info.argName, info.resolveValue, ictx);
|
|
103
|
+
const linkedArgs = collectValuesFromLinks(args, { dataflow, config, ctx: ictx }, linkedIds);
|
|
104
|
+
const linked = dropInfoOnLinkedIds(linkedIds);
|
|
105
|
+
function ignoreOnArgVal() {
|
|
106
|
+
if (info.ignoreIf === 'arg-true' || info.ignoreIf === 'arg-false') {
|
|
107
|
+
const margs = info.additionalArgs?.val;
|
|
108
|
+
(0, assert_1.guard)(margs, 'Need additional argument val when checking for arg-true');
|
|
109
|
+
const valArgs = (0, resolve_argument_1.getArgumentStringValue)(vars, dfg, vertex, margs.argIdx, margs.argName, margs.resolveValue, data?.analyzer.inspectContext());
|
|
110
|
+
const valValues = valArgs?.values().flatMap(v => Array.from(v)).toArray() ?? [];
|
|
111
|
+
if (valValues.length === 0) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (info.ignoreIf === 'arg-true' && valValues.every(v => v === 'TRUE')) {
|
|
115
|
+
// all values are TRUE, so we can ignore this
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
else if (info.ignoreIf === 'arg-false' && valValues.every(v => v === 'FALSE')) {
|
|
119
|
+
// all values are FALSE, so we can ignore this
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
94
122
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const foundValues = linkedArgs ?? args;
|
|
126
|
+
if (!foundValues) {
|
|
127
|
+
if (info.ignoreIf === 'arg-missing') {
|
|
128
|
+
continue;
|
|
98
129
|
}
|
|
99
|
-
else if (
|
|
100
|
-
|
|
101
|
-
return true;
|
|
130
|
+
else if (ignoreOnArgVal()) {
|
|
131
|
+
continue;
|
|
102
132
|
}
|
|
103
|
-
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
const foundValues = linkedArgs ?? args;
|
|
107
|
-
if (!foundValues) {
|
|
108
|
-
if (info.ignoreIf === 'arg-missing') {
|
|
109
|
-
return [];
|
|
110
|
-
}
|
|
111
|
-
else if (ignoreOnArgVal()) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
const record = (0, objects_1.compactRecord)({
|
|
115
|
-
nodeId: id,
|
|
116
|
-
functionName: vertex.name,
|
|
117
|
-
lexemeOfArgument: undefined,
|
|
118
|
-
linkedIds: linked?.length ? linked : undefined,
|
|
119
|
-
value: info.defaultValue ?? defaultValue
|
|
120
|
-
});
|
|
121
|
-
return record ? [record] : [];
|
|
122
|
-
}
|
|
123
|
-
else if (info.ignoreIf === 'mode-only-read' || info.ignoreIf === 'mode-only-write') {
|
|
124
|
-
(0, assert_1.guard)('mode' in (info.additionalArgs ?? {}), 'Need additional argument mode when checking for mode');
|
|
125
|
-
const margs = info.additionalArgs?.mode;
|
|
126
|
-
(0, assert_1.guard)(margs, 'Need additional argument mode when checking for mode');
|
|
127
|
-
const modeArgs = (0, resolve_argument_1.getArgumentStringValue)(config.solver.variables, dataflow.graph, vertex, margs.argIdx, margs.argName, margs.resolveValue, data?.analyzer.inspectContext());
|
|
128
|
-
const modeValues = modeArgs?.values().flatMap(v => Array.from(v)) ?? [];
|
|
129
|
-
if (info.ignoreIf === 'mode-only-read' && modeValues.every(m => m && readOnlyModes.has(m))) {
|
|
130
|
-
// all modes are read-only, so we can ignore this
|
|
131
|
-
return [];
|
|
132
|
-
}
|
|
133
|
-
else if (info.ignoreIf === 'mode-only-write' && modeValues.every(m => m && writeOnlyModes.has(m))) {
|
|
134
|
-
// all modes are write-only, so we can ignore this
|
|
135
|
-
return [];
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else if (ignoreOnArgVal()) {
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
const results = [];
|
|
142
|
-
for (const [arg, values] of foundValues.entries()) {
|
|
143
|
-
for (const value of values) {
|
|
144
|
-
const dep = value ? data?.analyzer.inspectContext().deps.getDependency(value) ?? undefined : undefined;
|
|
145
|
-
const result = (0, objects_1.compactRecord)({
|
|
133
|
+
const record = (0, objects_1.compactRecord)({
|
|
146
134
|
nodeId: id,
|
|
147
135
|
functionName: vertex.name,
|
|
148
|
-
lexemeOfArgument:
|
|
136
|
+
lexemeOfArgument: undefined,
|
|
149
137
|
linkedIds: linked?.length ? linked : undefined,
|
|
150
|
-
value:
|
|
151
|
-
versionConstraints: dep?.versionConstraints,
|
|
152
|
-
derivedVersion: dep?.derivedVersion,
|
|
153
|
-
namespaceInfo: dep?.namespaceInfo
|
|
138
|
+
value: info.defaultValue ?? defaultValue
|
|
154
139
|
});
|
|
155
|
-
if (
|
|
156
|
-
|
|
140
|
+
if (record) {
|
|
141
|
+
finalResults.push(record);
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
else if (info.ignoreIf === 'mode-only-read' || info.ignoreIf === 'mode-only-write') {
|
|
146
|
+
const margs = info.additionalArgs?.mode;
|
|
147
|
+
(0, assert_1.guard)(margs, 'Need additional argument mode when checking for mode');
|
|
148
|
+
const modeArgs = (0, resolve_argument_1.getArgumentStringValue)(vars, dfg, vertex, margs.argIdx, margs.argName, margs.resolveValue, data?.analyzer.inspectContext());
|
|
149
|
+
const modeValues = modeArgs?.values().flatMap(v => Array.from(v)) ?? [];
|
|
150
|
+
if (info.ignoreIf === 'mode-only-read' && modeValues.every(m => m && readOnlyModes.has(m))) {
|
|
151
|
+
// all modes are read-only, so we can ignore this
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
else if (info.ignoreIf === 'mode-only-write' && modeValues.every(m => m && writeOnlyModes.has(m))) {
|
|
155
|
+
// all modes are write-only, so we can ignore this
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (ignoreOnArgVal()) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
for (const [arg, values] of foundValues.entries()) {
|
|
163
|
+
for (const value of values) {
|
|
164
|
+
const dep = value ? d.getDependency(value) ?? undefined : undefined;
|
|
165
|
+
finalResults.push((0, objects_1.compactRecord)({
|
|
166
|
+
nodeId: id,
|
|
167
|
+
functionName: vertex.name,
|
|
168
|
+
lexemeOfArgument: getLexeme(value, arg),
|
|
169
|
+
linkedIds: linked?.length ? linked : undefined,
|
|
170
|
+
value: value ?? info.defaultValue ?? defaultValue,
|
|
171
|
+
versionConstraints: dep?.versionConstraints,
|
|
172
|
+
derivedVersion: dep?.derivedVersion,
|
|
173
|
+
namespaceInfo: dep?.namespaceInfo
|
|
174
|
+
}));
|
|
157
175
|
}
|
|
158
176
|
}
|
|
159
177
|
}
|
|
160
|
-
|
|
161
|
-
|
|
178
|
+
}
|
|
179
|
+
return finalResults;
|
|
162
180
|
function getLexeme(argument, id) {
|
|
163
181
|
if ((argument && argument !== dependencies_query_format_1.Unknown) || !id) {
|
|
164
182
|
return undefined;
|
|
@@ -174,7 +192,7 @@ function collectValuesFromLinks(args, data, linkedIds) {
|
|
|
174
192
|
if (!linkedIds || linkedIds.length === 0) {
|
|
175
193
|
return undefined;
|
|
176
194
|
}
|
|
177
|
-
const hasAtLeastAValue = args !== undefined &&
|
|
195
|
+
const hasAtLeastAValue = args !== undefined && args.values().flatMap(x => Array.from(x)).toArray().some(v => v !== dependencies_query_format_1.Unknown && v !== undefined);
|
|
178
196
|
const map = new Map();
|
|
179
197
|
for (const linkedId of linkedIds) {
|
|
180
198
|
if (typeof linkedId !== 'object' || !linkedId.info) {
|
|
@@ -187,7 +205,7 @@ function collectValuesFromLinks(args, data, linkedIds) {
|
|
|
187
205
|
}
|
|
188
206
|
// collect this one!
|
|
189
207
|
const vertex = data.dataflow.graph.getVertex(linkedId.id);
|
|
190
|
-
if (vertex
|
|
208
|
+
if (vertex?.tag !== vertex_1.VertexType.FunctionCall) {
|
|
191
209
|
continue;
|
|
192
210
|
}
|
|
193
211
|
const args = (0, resolve_argument_1.getArgumentStringValue)(data.config.solver.variables, data.dataflow.graph, vertex, info.argIdx, info.argName, info.resolveValue, data.ctx);
|
|
@@ -209,7 +227,7 @@ function getFunctionsToCheck(customFunctions, functionFlag, enabled, ignoreDefau
|
|
|
209
227
|
if (enabled !== undefined && (enabled?.length === 0 || enabled.indexOf(functionFlag) < 0)) {
|
|
210
228
|
return [];
|
|
211
229
|
}
|
|
212
|
-
let functions = ignoreDefaultFunctions ? [] :
|
|
230
|
+
let functions = ignoreDefaultFunctions ? [] : defaultFunctions.slice();
|
|
213
231
|
if (customFunctions) {
|
|
214
232
|
functions = functions.concat(customFunctions);
|
|
215
233
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.executeDfShapeQuery = executeDfShapeQuery;
|
|
4
4
|
const shape_inference_1 = require("../../../abstract-interpretation/data-frame/shape-inference");
|
|
5
|
+
const cfg_kind_1 = require("../../../project/cfg-kind");
|
|
5
6
|
const parse_1 = require("../../../slicing/criterion/parse");
|
|
6
7
|
const log_1 = require("../../../util/log");
|
|
7
8
|
/**
|
|
@@ -14,7 +15,7 @@ async function executeDfShapeQuery({ analyzer }, queries) {
|
|
|
14
15
|
}
|
|
15
16
|
const ast = await analyzer.normalize();
|
|
16
17
|
const dfg = (await analyzer.dataflow()).graph;
|
|
17
|
-
const cfg = await analyzer.controlflow();
|
|
18
|
+
const cfg = await analyzer.controlflow(undefined, cfg_kind_1.CfgKind.NoFunctionDefs);
|
|
18
19
|
const start = Date.now();
|
|
19
20
|
const inference = new shape_inference_1.DataFrameShapeInferenceVisitor({ controlFlow: cfg, dfg, normalizedAst: ast, ctx: analyzer.inspectContext() });
|
|
20
21
|
inference.start();
|
|
@@ -37,10 +38,16 @@ async function executeDfShapeQuery({ analyzer }, queries) {
|
|
|
37
38
|
log_1.log.warn('Duplicate criterion in dataframe shape query:', query.criterion);
|
|
38
39
|
continue;
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
try {
|
|
42
|
+
const nodeId = (0, parse_1.slicingCriterionToId)(query.criterion, ast.idMap);
|
|
43
|
+
const node = ast.idMap.get(nodeId);
|
|
44
|
+
const value = inference.getAbstractValue(node?.info.id);
|
|
45
|
+
result.set(query.criterion, value);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.error(e instanceof Error ? e.message : e);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
52
|
return {
|
|
46
53
|
'.meta': {
|
package/queries/query.js
CHANGED
|
@@ -73,10 +73,11 @@ exports.SupportedQueries = {
|
|
|
73
73
|
*/
|
|
74
74
|
async function executeQueriesOfSameType(data, queries) {
|
|
75
75
|
(0, assert_1.guard)(queries.length > 0, 'At least one query must be provided');
|
|
76
|
+
const qzt = queries[0].type;
|
|
76
77
|
/* every query must have the same type */
|
|
77
|
-
(0, assert_1.guard)(queries.every(q => q.type ===
|
|
78
|
-
const query = exports.SupportedQueries[
|
|
79
|
-
(0, assert_1.guard)(query !== undefined, `Unsupported query type: ${
|
|
78
|
+
(0, assert_1.guard)(queries.every(q => q.type === qzt), 'All queries must have the same type');
|
|
79
|
+
const query = exports.SupportedQueries[qzt];
|
|
80
|
+
(0, assert_1.guard)(query !== undefined, `Unsupported query type: ${qzt}`);
|
|
80
81
|
return query.executor(data, queries);
|
|
81
82
|
}
|
|
82
83
|
function isVirtualQuery(query) {
|
package/r-bridge/data/data.d.ts
CHANGED
|
@@ -664,6 +664,26 @@ export declare const flowrCapabilities: {
|
|
|
664
664
|
readonly id: "system-calls";
|
|
665
665
|
readonly supported: "not";
|
|
666
666
|
readonly description: "_Handle [`system`](https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/system), `system.*`, ..._ We do not support system calls but treat them as unknown function calls.";
|
|
667
|
+
}, {
|
|
668
|
+
readonly name: "R-Markdown files";
|
|
669
|
+
readonly id: "file:rmd";
|
|
670
|
+
readonly supported: "fully";
|
|
671
|
+
readonly description: "Support R-Markdown files as R sources.";
|
|
672
|
+
}, {
|
|
673
|
+
readonly name: "Jupyter Notebook";
|
|
674
|
+
readonly id: "file:ipynb";
|
|
675
|
+
readonly supported: "partially";
|
|
676
|
+
readonly description: "Support Jupyter Notebooks as R sources.";
|
|
677
|
+
}, {
|
|
678
|
+
readonly name: "Quarto";
|
|
679
|
+
readonly id: "file:qmd";
|
|
680
|
+
readonly supported: "partially";
|
|
681
|
+
readonly description: "Support Quarto files as R sources.";
|
|
682
|
+
}, {
|
|
683
|
+
readonly name: "Sweave";
|
|
684
|
+
readonly id: "file:rnw";
|
|
685
|
+
readonly supported: "partially";
|
|
686
|
+
readonly description: "Support for Sweave files as R sources.";
|
|
667
687
|
}];
|
|
668
688
|
}, {
|
|
669
689
|
readonly name: "Pre-Processors/external Tooling";
|
package/r-bridge/data/data.js
CHANGED
|
@@ -829,6 +829,30 @@ ${await (0, doc_dfg_1.printDfGraphForCode)(parser, code, { simplified: true })}
|
|
|
829
829
|
id: 'system-calls',
|
|
830
830
|
supported: 'not',
|
|
831
831
|
description: '_Handle [`system`](https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/system), `system.*`, ..._ We do not support system calls but treat them as unknown function calls.'
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
name: 'R-Markdown files',
|
|
835
|
+
id: 'file:rmd',
|
|
836
|
+
supported: 'fully',
|
|
837
|
+
description: 'Support R-Markdown files as R sources.'
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
name: 'Jupyter Notebook',
|
|
841
|
+
id: 'file:ipynb',
|
|
842
|
+
supported: 'partially',
|
|
843
|
+
description: 'Support Jupyter Notebooks as R sources.'
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
name: 'Quarto',
|
|
847
|
+
id: 'file:qmd',
|
|
848
|
+
supported: 'partially',
|
|
849
|
+
description: 'Support Quarto files as R sources.'
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
name: 'Sweave',
|
|
853
|
+
id: 'file:rnw',
|
|
854
|
+
supported: 'partially',
|
|
855
|
+
description: 'Support for Sweave files as R sources.'
|
|
832
856
|
}
|
|
833
857
|
]
|
|
834
858
|
},
|
|
@@ -86,7 +86,7 @@ function number2ts(value) {
|
|
|
86
86
|
// check for hexadecimal number with floating point addon which is supported by R but not by JS :/
|
|
87
87
|
let lcValue = value.toLowerCase();
|
|
88
88
|
/* both checks are case-sensitive! */
|
|
89
|
-
const last = value
|
|
89
|
+
const last = value.at(-1);
|
|
90
90
|
const markedAsInt = last === exports.RIntegerMarker;
|
|
91
91
|
const complexNumber = last === exports.RImaginaryMarker;
|
|
92
92
|
if (markedAsInt || complexNumber) {
|
|
@@ -27,16 +27,17 @@ function normalizeTreeSitterTreeToAst(tree, lax) {
|
|
|
27
27
|
else {
|
|
28
28
|
makeTreeSitterStrict();
|
|
29
29
|
}
|
|
30
|
-
const files =
|
|
30
|
+
const files = [];
|
|
31
|
+
for (const t of tree) {
|
|
31
32
|
const root = convertTreeNode(t.parsed.rootNode);
|
|
32
33
|
if (root.type !== type_1.RType.ExpressionList) {
|
|
33
34
|
throw new normalizer_data_1.ParseError(`expected root to resolve to an expression list, got a ${root.type}`);
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
files.push({
|
|
36
37
|
filePath: t.filePath,
|
|
37
38
|
root: root
|
|
38
|
-
};
|
|
39
|
-
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
40
41
|
return {
|
|
41
42
|
type: type_1.RType.Project,
|
|
42
43
|
files
|
|
@@ -158,7 +159,7 @@ function convertTreeNode(node) {
|
|
|
158
159
|
treeSitterId: lhs.info.treeSitterId
|
|
159
160
|
}
|
|
160
161
|
};
|
|
161
|
-
if (op.type
|
|
162
|
+
if (op.type === 'special') {
|
|
162
163
|
return {
|
|
163
164
|
type: type_1.RType.FunctionCall,
|
|
164
165
|
location: opSource,
|
|
@@ -329,7 +330,7 @@ function convertTreeNode(node) {
|
|
|
329
330
|
case tree_sitter_types_1.TreeSitterType.Call: {
|
|
330
331
|
const [func, argsParentheses] = nonErrorChildren(node);
|
|
331
332
|
// tree-sitter wraps next and break in a function call, but we don't, so unwrap
|
|
332
|
-
if (func.type === tree_sitter_types_1.TreeSitterType.Next || func.type
|
|
333
|
+
if (func.type === tree_sitter_types_1.TreeSitterType.Next || func.type === tree_sitter_types_1.TreeSitterType.Break) {
|
|
333
334
|
return {
|
|
334
335
|
...convertTreeNode(func),
|
|
335
336
|
...defaultInfo
|
|
@@ -339,7 +340,7 @@ function convertTreeNode(node) {
|
|
|
339
340
|
const [comments, noCommentrawArgs] = splitComments(rawArgs);
|
|
340
341
|
const args = (0, arrays_1.splitArrayOn)(noCommentrawArgs.slice(1, -1), x => x.type === 'comma');
|
|
341
342
|
const funcRange = makeSourceRange(func);
|
|
342
|
-
const mappedArgs = args.map(n => n.length
|
|
343
|
+
const mappedArgs = args.map(n => n.length === 0 ? r_function_call_1.EmptyArgument : convertTreeNode(n[0]));
|
|
343
344
|
const call = {
|
|
344
345
|
arguments: mappedArgs,
|
|
345
346
|
location: funcRange,
|
|
@@ -424,14 +425,20 @@ function convertTreeNode(node) {
|
|
|
424
425
|
return {
|
|
425
426
|
type: type_1.RType.Logical,
|
|
426
427
|
location: range,
|
|
427
|
-
content:
|
|
428
|
+
content: node.text === convert_values_1.RTrue,
|
|
428
429
|
lexeme: node.text,
|
|
429
430
|
...defaultInfo
|
|
430
431
|
};
|
|
431
432
|
case tree_sitter_types_1.TreeSitterType.Break:
|
|
433
|
+
return {
|
|
434
|
+
type: type_1.RType.Break,
|
|
435
|
+
location: range,
|
|
436
|
+
lexeme: node.text,
|
|
437
|
+
...defaultInfo
|
|
438
|
+
};
|
|
432
439
|
case tree_sitter_types_1.TreeSitterType.Next:
|
|
433
440
|
return {
|
|
434
|
-
type:
|
|
441
|
+
type: type_1.RType.Next,
|
|
435
442
|
location: range,
|
|
436
443
|
lexeme: node.text,
|
|
437
444
|
...defaultInfo
|
|
@@ -447,7 +454,7 @@ function convertTreeNode(node) {
|
|
|
447
454
|
type: type_1.RType.Access,
|
|
448
455
|
operator: bracket.text,
|
|
449
456
|
accessed: convertTreeNode(func),
|
|
450
|
-
access: args.map(n => n.length
|
|
457
|
+
access: args.map(n => n.length === 0 ? r_function_call_1.EmptyArgument : convertTreeNode(n[0])),
|
|
451
458
|
location: makeSourceRange(bracket),
|
|
452
459
|
lexeme: bracket.text,
|
|
453
460
|
...defaultInfo
|
|
@@ -486,7 +493,7 @@ function convertTreeNode(node) {
|
|
|
486
493
|
const name = children[0];
|
|
487
494
|
const nameRange = makeSourceRange(name);
|
|
488
495
|
let defaultValue = undefined;
|
|
489
|
-
if (children.length
|
|
496
|
+
if (children.length === 3) {
|
|
490
497
|
defaultValue = convertTreeNode(children[2]);
|
|
491
498
|
}
|
|
492
499
|
return {
|
|
@@ -517,7 +524,7 @@ function convertTreeNode(node) {
|
|
|
517
524
|
}
|
|
518
525
|
case tree_sitter_types_1.TreeSitterType.Argument: {
|
|
519
526
|
const children = nonErrorChildren(node);
|
|
520
|
-
if (children.length
|
|
527
|
+
if (children.length === 1) {
|
|
521
528
|
const [arg] = children;
|
|
522
529
|
return {
|
|
523
530
|
type: type_1.RType.Argument,
|
|
@@ -596,21 +603,14 @@ function makeSourceRange(node) {
|
|
|
596
603
|
if (!node) {
|
|
597
604
|
return range_1.SourceRange.invalid();
|
|
598
605
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
else {
|
|
608
|
-
return [
|
|
609
|
-
(node.startPosition?.row ?? -2) + 1, (node.startPosition?.column ?? -2) + 1,
|
|
610
|
-
// tree-sitter's end position is one off from ours, so we don't add 1 here
|
|
611
|
-
(node.endPosition?.row ?? -2) + 1, node.endPosition?.column ?? -1
|
|
612
|
-
];
|
|
613
|
-
}
|
|
606
|
+
const s = node.startPosition;
|
|
607
|
+
const e = node.endPosition;
|
|
608
|
+
return [
|
|
609
|
+
// tree-sitter is 0-based but we want 1-based
|
|
610
|
+
(s?.row ?? -2) + 1, (s?.column ?? -2) + 1,
|
|
611
|
+
// tree-sitter's end position is one off from ours, so we don't add 1 here
|
|
612
|
+
(e?.row ?? -2) + 1, e?.column ?? -1
|
|
613
|
+
];
|
|
614
614
|
}
|
|
615
615
|
function splitComments(nodes) {
|
|
616
616
|
const comments = [];
|
|
@@ -634,22 +634,52 @@ function splitComments(nodes) {
|
|
|
634
634
|
}
|
|
635
635
|
return [comments, others];
|
|
636
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* Find the first sibling of the given node that is not a comment, starting from the given node and going to the right.
|
|
639
|
+
* @param snode - the node for which to find the first non-comment sibling
|
|
640
|
+
* @param knownNexts - cache map from node id to the id of the first non-comment sibling
|
|
641
|
+
*/
|
|
642
|
+
function findFirstNonCommentSibling(snode, knownNexts) {
|
|
643
|
+
const cache = knownNexts.get(snode.id);
|
|
644
|
+
if (cache !== undefined) {
|
|
645
|
+
return cache;
|
|
646
|
+
}
|
|
647
|
+
const cursor = snode.parent?.walk();
|
|
648
|
+
if (!cursor) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
const linkCaches = [snode.id];
|
|
652
|
+
cursor.gotoFirstChild();
|
|
653
|
+
while (cursor.nodeId !== snode.id && cursor.gotoNextSibling()) {
|
|
654
|
+
/* skip */
|
|
655
|
+
}
|
|
656
|
+
cursor.gotoNextSibling();
|
|
657
|
+
while (cursor.nodeType === tree_sitter_types_1.TreeSitterType.Comment && cursor.gotoNextSibling()) {
|
|
658
|
+
/* skip */
|
|
659
|
+
linkCaches.push(cursor.nodeId);
|
|
660
|
+
}
|
|
661
|
+
const cur = cursor.currentNode;
|
|
662
|
+
for (const id of linkCaches) {
|
|
663
|
+
knownNexts.set(id, cur);
|
|
664
|
+
}
|
|
665
|
+
cursor.delete();
|
|
666
|
+
return cur;
|
|
667
|
+
}
|
|
637
668
|
function linkCommentsToNextNodes(nodes, comments) {
|
|
638
669
|
const remain = [];
|
|
670
|
+
const cacheMap = new Map();
|
|
639
671
|
for (const [commentSyntaxNode, commentNode] of comments) {
|
|
640
672
|
let sibling;
|
|
641
|
-
|
|
673
|
+
const prev = commentSyntaxNode.previousSibling;
|
|
674
|
+
if (prev?.endIndex === commentSyntaxNode.startIndex) {
|
|
642
675
|
// if there is a sibling on the same line, we link the comment to that node
|
|
643
|
-
sibling =
|
|
676
|
+
sibling = prev;
|
|
644
677
|
}
|
|
645
678
|
else {
|
|
646
|
-
sibling = commentSyntaxNode
|
|
647
|
-
while (sibling && sibling.type === tree_sitter_types_1.TreeSitterType.Comment) {
|
|
648
|
-
sibling = sibling.nextSibling;
|
|
649
|
-
}
|
|
679
|
+
sibling = findFirstNonCommentSibling(commentSyntaxNode, cacheMap);
|
|
650
680
|
}
|
|
651
681
|
// if there is no valid sibling, we just link the comment to the first node (see normalize-expressions.ts)
|
|
652
|
-
const [, node] = (sibling ? nodes.find(([s]) => s.
|
|
682
|
+
const [, node] = (sibling ? nodes.find(([s]) => s.id === sibling.id) : undefined) ?? nodes[0] ?? [];
|
|
653
683
|
if (node) {
|
|
654
684
|
node.info.adToks ??= [];
|
|
655
685
|
node.info.adToks.push(commentNode);
|
package/util/r-regex.d.ts
CHANGED
|
@@ -19,3 +19,7 @@ export declare const RStandardRegexp: {
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export declare const RPunctuationChars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
|
22
|
+
/**
|
|
23
|
+
* Converts an R regex pattern (which may include POSIX character classes) into a JavaScript RegExp.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseRRegexPattern(pattern: string): RegExp;
|
package/util/r-regex.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RPunctuationChars = exports.RStandardRegexp = void 0;
|
|
4
|
+
exports.parseRRegexPattern = parseRRegexPattern;
|
|
4
5
|
/**
|
|
5
6
|
* Represents R's `.standard_regexps` definitions.
|
|
6
7
|
* @see https://github.com/r-devel/r-svn/blob/44474af03ae77fd3b9a340279fa10cb698d106c3/src/library/base/R/utils.R#L52-L53
|
|
@@ -9,11 +10,11 @@ exports.RStandardRegexp = {
|
|
|
9
10
|
/** `[[:alpha:]][[:alnum:].]*[[:alnum:]]` */
|
|
10
11
|
ValidPackageName: (/[A-Za-z][A-Za-z0-9._]*[A-Za-z0-9]/),
|
|
11
12
|
/** `([[:digit:]]+[.-]){1,}[[:digit:]]+` */
|
|
12
|
-
ValidPackageVersion: (/(
|
|
13
|
+
ValidPackageVersion: (/(\d+[.-])+\d+/),
|
|
13
14
|
/** `[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+` */
|
|
14
|
-
ValidRSystemVersion: (
|
|
15
|
+
ValidRSystemVersion: (/\d+\.\d+\.\d+/),
|
|
15
16
|
/** `([[:digit:]]+[.-])*[[:digit:]]+` */
|
|
16
|
-
ValidNumericVersion: /(
|
|
17
|
+
ValidNumericVersion: /(\d+[.-])*\d+/
|
|
17
18
|
};
|
|
18
19
|
/**
|
|
19
20
|
* Based on the C-definition:
|
|
@@ -22,4 +23,35 @@ exports.RStandardRegexp = {
|
|
|
22
23
|
* ```
|
|
23
24
|
*/
|
|
24
25
|
exports.RPunctuationChars = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
|
|
26
|
+
const PosixClassMap = {
|
|
27
|
+
digit: '0-9',
|
|
28
|
+
lower: 'a-z',
|
|
29
|
+
upper: 'A-Z',
|
|
30
|
+
alpha: 'A-Za-z',
|
|
31
|
+
alnum: 'A-Za-z0-9',
|
|
32
|
+
space: String.raw `\s`,
|
|
33
|
+
blank: String.raw ` \t`,
|
|
34
|
+
punct: "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~",
|
|
35
|
+
xdigit: 'A-Fa-f0-9',
|
|
36
|
+
ascii: '\x00-\x7F',
|
|
37
|
+
cntrl: '\x00-\x1F\x7F',
|
|
38
|
+
graph: '\x21-\x7E',
|
|
39
|
+
word: 'A-Za-z0-9_',
|
|
40
|
+
print: '\x20-\x7E'
|
|
41
|
+
};
|
|
42
|
+
const posixClassRegex = /\[:([a-z]+):]/g;
|
|
43
|
+
/**
|
|
44
|
+
* Converts an R regex pattern (which may include POSIX character classes) into a JavaScript RegExp.
|
|
45
|
+
*/
|
|
46
|
+
function parseRRegexPattern(pattern) {
|
|
47
|
+
let convertedPattern = pattern.replaceAll(posixClassRegex, (s, className) => {
|
|
48
|
+
const charClass = PosixClassMap[className];
|
|
49
|
+
return charClass ?? s;
|
|
50
|
+
});
|
|
51
|
+
// we also want to support a glob '*' without any prefix:
|
|
52
|
+
if (convertedPattern.startsWith('*') || convertedPattern.startsWith('+')) {
|
|
53
|
+
convertedPattern = '.' + convertedPattern;
|
|
54
|
+
}
|
|
55
|
+
return new RegExp(convertedPattern);
|
|
56
|
+
}
|
|
25
57
|
//# sourceMappingURL=r-regex.js.map
|
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.7';
|
|
10
10
|
/**
|
|
11
11
|
* Retrieves the current flowR version as a new {@link SemVer} object.
|
|
12
12
|
*/
|