@eagleoutice/flowr 2.7.4 → 2.7.6

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.
Files changed (38) hide show
  1. package/README.md +14 -14
  2. package/cli/repl/commands/repl-commands.d.ts +2 -0
  3. package/cli/repl/commands/repl-commands.js +2 -0
  4. package/cli/repl/commands/repl-dataflow.d.ts +2 -0
  5. package/cli/repl/commands/repl-dataflow.js +38 -1
  6. package/cli/repl/core.js +22 -0
  7. package/config.d.ts +5 -0
  8. package/config.js +6 -0
  9. package/dataflow/graph/graph.js +2 -0
  10. package/documentation/wiki-analyzer.js +12 -0
  11. package/documentation/wiki-interface.js +3 -0
  12. package/documentation/wiki-query.js +1 -1
  13. package/package.json +3 -1
  14. package/project/context/flowr-analyzer-dependencies-context.d.ts +5 -1
  15. package/project/context/flowr-analyzer-functions-context.d.ts +16 -0
  16. package/project/context/flowr-analyzer-functions-context.js +6 -0
  17. package/project/context/flowr-analyzer-loading-order-context.d.ts +4 -4
  18. package/project/context/flowr-analyzer-loading-order-context.js +4 -0
  19. package/project/plugins/file-plugins/files/flowr-description-file.d.ts +18 -1
  20. package/project/plugins/file-plugins/files/flowr-description-file.js +47 -13
  21. package/project/plugins/loading-order-plugins/flowr-analyzer-loading-order-description-file-plugin.js +8 -3
  22. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +5 -2
  23. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.js +6 -1
  24. package/project/plugins/package-version-plugins/package.js +1 -1
  25. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +12 -2
  26. package/queries/catalog/project-query/project-query-executor.js +12 -2
  27. package/queries/catalog/project-query/project-query-format.d.ts +13 -0
  28. package/queries/catalog/project-query/project-query-format.js +25 -2
  29. package/queries/query-print.js +8 -3
  30. package/util/mermaid/cfg.d.ts +3 -0
  31. package/util/mermaid/cfg.js +25 -0
  32. package/util/r-author.d.ts +39 -0
  33. package/util/r-author.js +194 -0
  34. package/util/r-license.d.ts +23 -0
  35. package/util/r-license.js +196 -0
  36. package/util/simple-df/dfg-ascii.d.ts +5 -0
  37. package/util/simple-df/dfg-ascii.js +272 -0
  38. package/util/version.js +1 -1
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRLicense = parseRLicense;
4
+ const semver_1 = require("semver");
5
+ const assert_1 = require("./assert");
6
+ const objects_1 = require("./objects");
7
+ /**
8
+ * Parses an R license string into its structured representation.
9
+ */
10
+ function parseRLicense(licenseString) {
11
+ const start = skipWhitespace({ position: 0, remInput: licenseString });
12
+ const parsed = parseLicenseInfo(start);
13
+ return parsed.element;
14
+ }
15
+ function parseLicenseInfo(info) {
16
+ info = skipWhitespace(info);
17
+ if (info.remInput.length === 0) {
18
+ return { ...info, element: { type: 'no-license' } };
19
+ }
20
+ else if (info.remInput.startsWith('(')) {
21
+ return parseParenthesizedExpression(info);
22
+ }
23
+ else {
24
+ return parseBinaryOperator(info);
25
+ }
26
+ }
27
+ function parseParenthesizedExpression(info) {
28
+ const openParen = consumeString(info, '(');
29
+ (0, assert_1.guard)(openParen.element, `Expected (, but found ${info.remInput[0]} at position ${info.position}`);
30
+ const innerInfo = parseLicenseInfo(openParen);
31
+ const closeParen = consumeString(innerInfo, ')');
32
+ (0, assert_1.guard)(closeParen.element, `Expected ), but found ${innerInfo.remInput[0]} at position ${innerInfo.position}`);
33
+ return { ...closeParen, element: innerInfo.element };
34
+ }
35
+ function skipWhitespace({ remInput, position }) {
36
+ let idx = remInput.search(/\S/);
37
+ if (idx === -1) {
38
+ idx = remInput.length;
39
+ }
40
+ position += idx;
41
+ remInput = remInput.slice(idx);
42
+ return { position, remInput };
43
+ }
44
+ function consumeLicenseName(info) {
45
+ info = skipWhitespace(info);
46
+ let licenseName = '';
47
+ let isAtWhitespace = false;
48
+ for (let i = 0; i < info.remInput.length; i++) {
49
+ const char = info.remInput[i];
50
+ if (/[()<>~!=,&|+]/.test(char)) {
51
+ break;
52
+ }
53
+ else {
54
+ isAtWhitespace = /\s/.test(char);
55
+ }
56
+ if (isAtWhitespace) {
57
+ if (/^\s*(with|and|or)\s+/i.test(info.remInput.slice(i))) {
58
+ break;
59
+ }
60
+ }
61
+ licenseName += char;
62
+ }
63
+ const newInfo = {
64
+ position: info.position + licenseName.length,
65
+ remInput: info.remInput.slice(licenseName.length)
66
+ };
67
+ // file licenses are a special case!
68
+ if (licenseName && /^file[\s-]+license$/i.test(licenseName.trim())) {
69
+ return { ...newInfo, element: 'file LICENSE' };
70
+ }
71
+ return { ...newInfo, element: licenseName?.trim() ?? '' };
72
+ }
73
+ function makeRange(rangeStr) {
74
+ try {
75
+ rangeStr = rangeStr.trim().replaceAll('==', '=');
76
+ return new semver_1.Range(rangeStr);
77
+ }
78
+ catch {
79
+ return undefined;
80
+ }
81
+ }
82
+ function parseLicenseElement(info) {
83
+ const licenseName = consumeLicenseName(info);
84
+ info = skipWhitespace(licenseName);
85
+ // may be followed by version constraint in parentheses or directly
86
+ let versionConstraint;
87
+ if (/^\(\s*?[\d<>=!~]/.test(info.remInput)) {
88
+ // version constraint
89
+ if (info.remInput.startsWith('(')) {
90
+ const openParen = consumeString(info, '(');
91
+ (0, assert_1.guard)(openParen.element, `Expected (, but found ${info.remInput[0]} at position ${info.position}`);
92
+ info = skipWhitespace(openParen);
93
+ // consume until closing parenthesis
94
+ const versionStr = consumeUntilString(info, ')');
95
+ versionConstraint = makeRange(versionStr.element);
96
+ const closeParen = consumeString(versionStr, ')');
97
+ (0, assert_1.guard)(closeParen.element, `Expected ), but found ${versionStr.remInput[0]} at position ${versionStr.position}`);
98
+ info = skipWhitespace(closeParen);
99
+ }
100
+ else {
101
+ // consume until whitespace or special character
102
+ const versionStr = consumeRegexp(info, /^[\d<>=!~.\s]+/);
103
+ versionConstraint = versionStr.element ? makeRange(versionStr.element) : undefined;
104
+ info = skipWhitespace(versionStr);
105
+ }
106
+ }
107
+ const licenseInfo = (0, objects_1.compactRecord)({
108
+ type: 'license',
109
+ license: licenseName.element,
110
+ versionConstraint
111
+ });
112
+ return { ...info, element: licenseInfo };
113
+ }
114
+ const operators = {
115
+ 'and': ['and', '+', '&'],
116
+ 'or': ['or', '|'],
117
+ 'with': ['with']
118
+ };
119
+ function parseBinaryOperator(info) {
120
+ const license = parseLicenseElement(info);
121
+ info = skipWhitespace(license);
122
+ const operator = tryConsumeOperator(info);
123
+ if (operator.element) {
124
+ info = skipWhitespace(operator);
125
+ let rightLicense = parseLicenseInfo(info);
126
+ if (operator.element === 'with' && rightLicense.element.type === 'license') {
127
+ rightLicense = {
128
+ ...rightLicense,
129
+ element: {
130
+ type: 'exception',
131
+ exception: rightLicense.element.license
132
+ }
133
+ };
134
+ }
135
+ const combination = {
136
+ type: 'combination',
137
+ combination: operator.element,
138
+ elements: [license.element, rightLicense.element]
139
+ };
140
+ return { ...rightLicense, element: combination };
141
+ }
142
+ return { ...info, element: license.element };
143
+ }
144
+ function tryConsumeOperator(info) {
145
+ for (const [opName, opSymbols] of Object.entries(operators)) {
146
+ for (const symbol of opSymbols) {
147
+ if (info.remInput.toLowerCase().startsWith(symbol)) {
148
+ const newInfo = {
149
+ position: info.position + symbol.length,
150
+ remInput: info.remInput.slice(symbol.length)
151
+ };
152
+ return { ...newInfo, element: opName };
153
+ }
154
+ }
155
+ }
156
+ return { ...info, element: undefined };
157
+ }
158
+ function consumeString(info, str) {
159
+ info = skipWhitespace(info);
160
+ if (info.remInput.startsWith(str)) {
161
+ const newInfo = {
162
+ position: info.position + str.length,
163
+ remInput: info.remInput.slice(str.length)
164
+ };
165
+ return { ...newInfo, element: true };
166
+ }
167
+ else {
168
+ return { ...info, element: false };
169
+ }
170
+ }
171
+ function consumeUntilString(info, str) {
172
+ let idx = info.remInput.indexOf(str);
173
+ if (idx === -1) {
174
+ idx = info.remInput.length;
175
+ }
176
+ const consumed = info.remInput.slice(0, idx);
177
+ const newInfo = {
178
+ position: info.position + idx,
179
+ remInput: info.remInput.slice(idx)
180
+ };
181
+ return { ...newInfo, element: consumed };
182
+ }
183
+ function consumeRegexp(info, regex) {
184
+ info = skipWhitespace(info);
185
+ const match = info.remInput.match(regex);
186
+ if (match && match.index === 0) {
187
+ const matchedStr = match[0];
188
+ const newInfo = {
189
+ position: info.position + matchedStr.length,
190
+ remInput: info.remInput.slice(matchedStr.length)
191
+ };
192
+ return { ...newInfo, element: matchedStr };
193
+ }
194
+ return { ...info, element: null };
195
+ }
196
+ //# sourceMappingURL=r-license.js.map
@@ -0,0 +1,5 @@
1
+ import type { DataflowGraph } from '../../dataflow/graph/graph';
2
+ /**
3
+ * Converts the given dataflow graph to an ASCII representation.
4
+ */
5
+ export declare function dfgToAscii(dfg: DataflowGraph): string;
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dfgToAscii = dfgToAscii;
4
+ const dagre_1 = require("dagre");
5
+ const edge_1 = require("../../dataflow/graph/edge");
6
+ const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
7
+ const vertex_1 = require("../../dataflow/graph/vertex");
8
+ function combineAscii(has, add) {
9
+ if (has === ' ' || has === add) {
10
+ return add;
11
+ }
12
+ const [a, b] = [has, add].sort();
13
+ let res = add;
14
+ if (b === edgesChar.vertical && a === edgesChar.horizontal) {
15
+ res = '┼';
16
+ }
17
+ else if ((b === edgesChar.topRight || b === edgesChar.topLeft || b === '┬') && a === edgesChar.horizontal) {
18
+ res = '┬';
19
+ }
20
+ else if ((b === edgesChar.bottomRight || b === edgesChar.bottomLeft || b === '┴') && a === edgesChar.horizontal) {
21
+ res = '┴';
22
+ }
23
+ else if ((b === edgesChar.topRight || b === edgesChar.bottomRight || b === '┤') && a === edgesChar.vertical) {
24
+ res = '┤';
25
+ }
26
+ else if ((b === edgesChar.topLeft || b === edgesChar.bottomLeft || b === '├') && a === edgesChar.vertical) {
27
+ res = '├';
28
+ }
29
+ else if ((b === '┤' || b === '├') && a === edgesChar.horizontal) {
30
+ res = '┼';
31
+ }
32
+ else if ((b === '┬' || b === '┴') && a === edgesChar.vertical) {
33
+ res = '┼';
34
+ }
35
+ return res;
36
+ }
37
+ class AsciiCanvas {
38
+ filler;
39
+ grid;
40
+ shiftX;
41
+ shiftY;
42
+ constructor(filler = ' ', shiftX = 0, shiftY = 0) {
43
+ this.grid = [];
44
+ this.filler = filler;
45
+ this.shiftX = shiftX;
46
+ this.shiftY = shiftY;
47
+ }
48
+ set(x, y, char, overwrite = false) {
49
+ x += this.shiftX;
50
+ y += this.shiftY;
51
+ if (x < 0 || y < 0 || isNaN(x) || isNaN(y)) {
52
+ return;
53
+ }
54
+ while (this.grid.length <= y) {
55
+ this.grid.push([]);
56
+ }
57
+ while (this.grid[y].length <= x) {
58
+ this.grid[y].push(this.filler);
59
+ }
60
+ this.grid[y][x] = overwrite ? char : combineAscii(this.grid[y][x], char);
61
+ }
62
+ drawText(x, y, text) {
63
+ for (let i = 0; i < text.length; i++) {
64
+ this.set(x + i, y, text[i]);
65
+ }
66
+ }
67
+ toString() {
68
+ return this.grid.map(r => r.join('')).join('\n');
69
+ }
70
+ }
71
+ /**
72
+ * Converts the given dataflow graph to an ASCII representation.
73
+ */
74
+ function dfgToAscii(dfg) {
75
+ const g = new dagre_1.graphlib.Graph();
76
+ const verts = Array.from(dfg.vertices(true));
77
+ g.setGraph({
78
+ nodesep: 1,
79
+ ranksep: 4,
80
+ edgesep: 0,
81
+ rankdir: verts.length < 15 ? 'LR' : 'TB',
82
+ ranker: 'longest-path',
83
+ });
84
+ for (const [id, v] of verts) {
85
+ let label = (0, node_id_1.recoverName)(id, dfg.idMap) ?? v.tag;
86
+ if (label.length < 3) {
87
+ label = label.padStart(2, ' ').padEnd(3, ' ');
88
+ }
89
+ g.setNode(String(id), {
90
+ label,
91
+ width: Math.max(3, label.length * 2),
92
+ height: 3,
93
+ shape: 'rectangle'
94
+ });
95
+ }
96
+ const edgesDone = new Set();
97
+ let longestId = 0;
98
+ for (const [from, edges] of dfg.edges()) {
99
+ longestId = Math.max(longestId, String(from).length);
100
+ for (const [to, { types }] of edges) {
101
+ if (!g.hasNode(String(from)) || !g.hasNode(String(to)) || edgesDone.has(`${to}-${from}`)) {
102
+ continue;
103
+ }
104
+ longestId = Math.max(longestId, String(to).length);
105
+ g.setEdge(String(from), String(to), (0, edge_1.edgeTypesToNames)(types));
106
+ edgesDone.add(`${from}-${to}`);
107
+ }
108
+ }
109
+ (0, dagre_1.layout)(g, {
110
+ minlen: 2
111
+ });
112
+ const canvas = new AsciiCanvas();
113
+ renderEdges(g, canvas);
114
+ renderVertices(dfg, g, canvas);
115
+ const lines = canvas.toString().split('\n').filter(line => line.trim() !== '');
116
+ const edgeLines = [];
117
+ // add all edges
118
+ for (const [from, edges] of dfg.edges()) {
119
+ for (const [to, { types }] of edges) {
120
+ if (!g.hasNode(String(from)) || !g.hasNode(String(to))) {
121
+ continue;
122
+ }
123
+ edgeLines.push(`${from.toString().padStart(longestId, ' ')} -> ${to.toString().padStart(longestId, ' ')}: ${Array.from((0, edge_1.edgeTypesToNames)(types)).join(', ')}`);
124
+ }
125
+ }
126
+ // always merge two edgelines with padding
127
+ if (edgeLines.length > 0) {
128
+ lines.push('Edges:');
129
+ }
130
+ const longestFirstLine = Math.max(...edgeLines.map(l => l.length));
131
+ for (let i = 0; i < edgeLines.length; i += 2) {
132
+ const line1 = edgeLines[i];
133
+ const line2 = edgeLines[i + 1];
134
+ if (line2) {
135
+ lines.push(line1.padEnd(Math.min(50, longestFirstLine), ' ') + line2);
136
+ }
137
+ else {
138
+ lines.push(line1);
139
+ }
140
+ }
141
+ return lines.join('\n');
142
+ }
143
+ const type2Edge = {
144
+ [vertex_1.VertexType.FunctionCall]: 'c',
145
+ [vertex_1.VertexType.Use]: 'u',
146
+ [vertex_1.VertexType.FunctionDefinition]: 'f',
147
+ [vertex_1.VertexType.VariableDefinition]: 'v',
148
+ [vertex_1.VertexType.Value]: '0'
149
+ };
150
+ function renderVertices(dfg, g, canvas) {
151
+ for (const nodeId of g.nodes()) {
152
+ const node = g.node(nodeId);
153
+ if (!node) {
154
+ continue;
155
+ }
156
+ const label = node.label;
157
+ const x = Math.round(node.x);
158
+ const y = Math.round(node.y);
159
+ const tag = dfg.getVertex((0, node_id_1.normalizeIdToNumberIfPossible)(nodeId))?.tag;
160
+ let e = '+';
161
+ if (tag && tag in type2Edge) {
162
+ e = type2Edge[tag];
163
+ }
164
+ canvas.drawText(x - 1, y - 1, `${e}${'-'.repeat(label.length)}${e}`);
165
+ canvas.drawText(x - 1 + Math.round(label.length / 2 - nodeId.length / 2), y - 1, `<${nodeId}>`);
166
+ canvas.drawText(x - 1, y, `|${label}|`);
167
+ canvas.drawText(x - 1, y + 1, `${e}${'-'.repeat(label.length)}${e}`);
168
+ }
169
+ }
170
+ const edgesChar = {
171
+ vertical: '│',
172
+ horizontal: '─',
173
+ topLeft: '┌',
174
+ topRight: '┐',
175
+ bottomLeft: '└',
176
+ bottomRight: '┘',
177
+ };
178
+ function determineCornerChar(lastDirection, px, py, tx, ty) {
179
+ if (px === tx) {
180
+ return edgesChar.vertical;
181
+ }
182
+ else if (py === ty) {
183
+ return edgesChar.horizontal;
184
+ }
185
+ else if (px < tx) {
186
+ if (py < ty) {
187
+ return lastDirection === 'horizontal' ? edgesChar.topRight : edgesChar.bottomLeft;
188
+ }
189
+ else {
190
+ return lastDirection === 'horizontal' ? edgesChar.bottomRight : edgesChar.topLeft;
191
+ }
192
+ }
193
+ else {
194
+ if (py < ty) {
195
+ return lastDirection === 'horizontal' ? edgesChar.topLeft : edgesChar.bottomRight;
196
+ }
197
+ else {
198
+ return lastDirection === 'horizontal' ? edgesChar.bottomLeft : edgesChar.topRight;
199
+ }
200
+ }
201
+ }
202
+ function renderEdges(g, canvas) {
203
+ const otherEdges = new Set();
204
+ for (const e of g.edges()) {
205
+ const edge = g.edge(e);
206
+ let points = edge.points;
207
+ // we rework edges into sequences of straight lines only, adding intermediate points as needed
208
+ const newPoints = [points[0]];
209
+ for (let i = 1; i < points.length; i++) {
210
+ const prev = points[i - 1];
211
+ const curr = points[i];
212
+ if (prev.x !== curr.x && prev.y !== curr.y) {
213
+ const intermediate = { x: prev.x, y: curr.y };
214
+ newPoints.push(intermediate);
215
+ }
216
+ newPoints.push(curr);
217
+ }
218
+ points = newPoints;
219
+ let lastDirection = null;
220
+ // let single edges overwrite themselves
221
+ const writtenPoints = new Set();
222
+ for (let i = 0; i < points.length - 1; i++) {
223
+ const p = points[i - 1] ?? points[i];
224
+ const a = points[i];
225
+ const b = points[i + 1];
226
+ const px = Math.round(p.x);
227
+ const py = Math.round(p.y);
228
+ const x1 = Math.round(a.x);
229
+ const y1 = Math.round(a.y);
230
+ const x2 = Math.round(b.x);
231
+ const y2 = Math.round(b.y);
232
+ if (x1 === x2) {
233
+ // vertical
234
+ const [start, end] = y1 < y2 ? [y1, y2] : [y2, y1];
235
+ for (let y = start; y <= end; y++) {
236
+ const key = `${x1},${y}`;
237
+ const overwrite = writtenPoints.has(key) && !otherEdges.has(key);
238
+ if (y === (y1 < y2 ? start : end)) {
239
+ const cornerChar = determineCornerChar(lastDirection, px, py, x2, y2);
240
+ canvas.set(x1, y, cornerChar, overwrite);
241
+ }
242
+ else {
243
+ canvas.set(x1, y, edgesChar.vertical, overwrite);
244
+ }
245
+ writtenPoints.add(`${x1},${y}`);
246
+ }
247
+ lastDirection = 'vertical';
248
+ }
249
+ else if (y1 === y2) {
250
+ // horizontal
251
+ const [start, end] = x1 < x2 ? [x1, x2] : [x2, x1];
252
+ for (let x = start; x <= end; x++) {
253
+ const key = `${x},${y1}`;
254
+ const overwrite = writtenPoints.has(key) && !otherEdges.has(key);
255
+ if (x === (x1 < x2 ? start : end)) {
256
+ const cornerChar = determineCornerChar(lastDirection, px, py, x2, y2);
257
+ canvas.set(x, y1, cornerChar, overwrite);
258
+ }
259
+ else {
260
+ canvas.set(x, y1, edgesChar.horizontal, overwrite);
261
+ }
262
+ writtenPoints.add(`${x},${y1}`);
263
+ }
264
+ lastDirection = 'horizontal';
265
+ }
266
+ }
267
+ for (const p of writtenPoints) {
268
+ otherEdges.add(p);
269
+ }
270
+ }
271
+ }
272
+ //# sourceMappingURL=dfg-ascii.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.7.4';
9
+ const version = '2.7.6';
10
10
  /**
11
11
  * Retrieves the current flowR version as a new {@link SemVer} object.
12
12
  */