@eagleoutice/flowr 2.1.12 → 2.2.1

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 (107) hide show
  1. package/README.md +0 -1
  2. package/benchmark/slicer.d.ts +5 -12
  3. package/benchmark/slicer.js +46 -28
  4. package/cli/benchmark-app.d.ts +2 -0
  5. package/cli/benchmark-app.js +2 -1
  6. package/cli/benchmark-helper-app.d.ts +2 -0
  7. package/cli/benchmark-helper-app.js +2 -2
  8. package/cli/common/options.js +3 -1
  9. package/cli/flowr-main-options.js +36 -2
  10. package/cli/flowr.d.ts +6 -0
  11. package/cli/flowr.js +51 -24
  12. package/cli/repl/commands/repl-cfg.js +2 -4
  13. package/cli/repl/commands/repl-dataflow.js +2 -4
  14. package/cli/repl/commands/repl-execute.d.ts +2 -2
  15. package/cli/repl/commands/repl-execute.js +15 -5
  16. package/cli/repl/commands/repl-lineage.js +2 -4
  17. package/cli/repl/commands/repl-main.d.ts +2 -2
  18. package/cli/repl/commands/repl-normalize.js +2 -4
  19. package/cli/repl/commands/repl-parse.js +2 -4
  20. package/cli/repl/commands/repl-query.js +6 -8
  21. package/cli/repl/commands/repl-version.d.ts +5 -4
  22. package/cli/repl/commands/repl-version.js +10 -9
  23. package/cli/repl/core.d.ts +5 -5
  24. package/cli/repl/core.js +8 -12
  25. package/cli/repl/print-version.d.ts +2 -2
  26. package/cli/repl/print-version.js +3 -3
  27. package/cli/repl/server/connection.d.ts +3 -3
  28. package/cli/repl/server/connection.js +5 -7
  29. package/cli/repl/server/messages/message-hello.js +2 -1
  30. package/cli/repl/server/server.d.ts +4 -3
  31. package/cli/repl/server/server.js +7 -5
  32. package/cli/slicer-app.js +1 -1
  33. package/config.d.ts +36 -4
  34. package/config.js +30 -1
  35. package/core/pipeline-executor.d.ts +1 -1
  36. package/core/pipeline-executor.js +1 -1
  37. package/core/steps/all/core/00-parse.d.ts +4 -18
  38. package/core/steps/all/core/00-parse.js +2 -11
  39. package/core/steps/all/core/01-parse-tree-sitter.d.ts +23 -0
  40. package/core/steps/all/core/01-parse-tree-sitter.js +19 -0
  41. package/core/steps/all/core/10-normalize.d.ts +3 -2
  42. package/core/steps/all/core/10-normalize.js +1 -0
  43. package/core/steps/all/core/11-normalize-tree-sitter.d.ts +25 -0
  44. package/core/steps/all/core/11-normalize-tree-sitter.js +27 -0
  45. package/core/steps/all/core/20-dataflow.d.ts +2 -0
  46. package/core/steps/all/core/20-dataflow.js +1 -1
  47. package/core/steps/pipeline/default-pipelines.d.ts +368 -23
  48. package/core/steps/pipeline/default-pipelines.js +42 -4
  49. package/dataflow/environments/resolve-by-name.js +5 -4
  50. package/dataflow/extractor.d.ts +2 -1
  51. package/dataflow/extractor.js +2 -1
  52. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +6 -5
  53. package/dataflow/processor.d.ts +2 -0
  54. package/documentation/doc-util/doc-auto-gen.js +2 -1
  55. package/documentation/doc-util/doc-cfg.js +1 -1
  56. package/documentation/doc-util/doc-dfg.js +2 -2
  57. package/documentation/doc-util/doc-files.d.ts +1 -0
  58. package/documentation/doc-util/doc-files.js +4 -0
  59. package/documentation/doc-util/doc-normalized-ast.js +2 -3
  60. package/documentation/doc-util/doc-query.js +1 -1
  61. package/documentation/doc-util/doc-search.js +1 -1
  62. package/documentation/doc-util/doc-types.js +2 -2
  63. package/documentation/print-dataflow-graph-wiki.js +15 -15
  64. package/documentation/print-engines-wiki.d.ts +1 -0
  65. package/documentation/print-engines-wiki.js +82 -0
  66. package/documentation/print-interface-wiki.js +6 -7
  67. package/documentation/print-normalized-ast-wiki.js +1 -1
  68. package/documentation/print-query-wiki.js +24 -0
  69. package/package.json +9 -5
  70. package/queries/catalog/cluster-query/cluster-query-format.d.ts +5 -4
  71. package/queries/catalog/dataflow-query/dataflow-query-format.d.ts +5 -4
  72. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +5 -4
  73. package/queries/catalog/happens-before-query/happens-before-query-executor.d.ts +3 -0
  74. package/queries/catalog/happens-before-query/happens-before-query-executor.js +36 -0
  75. package/queries/catalog/happens-before-query/happens-before-query-format.d.ts +71 -0
  76. package/queries/catalog/happens-before-query/happens-before-query-format.js +27 -0
  77. package/queries/catalog/id-map-query/id-map-query-format.d.ts +5 -4
  78. package/queries/catalog/lineage-query/lineage-query-format.d.ts +5 -4
  79. package/queries/catalog/normalized-ast-query/normalized-ast-query-format.d.ts +5 -4
  80. package/queries/catalog/search-query/search-query-executor.js +0 -4
  81. package/queries/catalog/search-query/search-query-format.d.ts +5 -4
  82. package/queries/catalog/static-slice-query/static-slice-query-format.d.ts +5 -4
  83. package/queries/query.d.ts +100 -33
  84. package/queries/query.js +3 -1
  85. package/r-bridge/lang-4.x/ast/parser/json/parser.d.ts +4 -2
  86. package/r-bridge/lang-4.x/ast/parser/json/parser.js +5 -0
  87. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-executor.d.ts +18 -0
  88. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-executor.js +57 -0
  89. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.d.ts +3 -0
  90. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +541 -0
  91. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-types.d.ts +35 -0
  92. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-types.js +40 -0
  93. package/r-bridge/parser.d.ts +32 -0
  94. package/r-bridge/parser.js +14 -0
  95. package/r-bridge/shell-executor.d.ts +37 -1
  96. package/r-bridge/shell-executor.js +39 -0
  97. package/r-bridge/shell.d.ts +12 -6
  98. package/r-bridge/shell.js +15 -6
  99. package/search/search-optimizer/search-optimizer.js +1 -1
  100. package/statistics/statistics.js +1 -1
  101. package/util/cfg/cfg.d.ts +2 -0
  102. package/util/cfg/cfg.js +8 -8
  103. package/util/cfg/happens-before.d.ts +7 -0
  104. package/util/cfg/happens-before.js +32 -0
  105. package/util/logic.d.ts +4 -4
  106. package/util/logic.js +8 -0
  107. package/util/version.js +1 -1
package/README.md CHANGED
@@ -73,4 +73,3 @@ We welcome every contribution! Please check out the [contributing guidelines](ht
73
73
  [GPLv3 License](LICENSE).
74
74
 
75
75
  ----
76
- # abstract-interpretation-ltx
@@ -12,6 +12,7 @@ import type { SlicingCriteria } from '../slicing/criterion/parse';
12
12
  import type { RParseRequestFromFile, RParseRequestFromText } from '../r-bridge/retriever';
13
13
  import type { SlicingCriteriaFilter } from '../slicing/criterion/collect-all';
14
14
  import type { AutoSelectPredicate } from '../reconstruct/auto-select/auto-select-defaults';
15
+ import type { KnownParserName } from '../r-bridge/parser';
15
16
  /**
16
17
  * The logger to be used for benchmarking as a global object.
17
18
  */
@@ -42,29 +43,21 @@ export interface BenchmarkSingleSliceStats extends MergeableRecord {
42
43
  /** the final code, as the result of the 'reconstruct' step */
43
44
  code: ReconstructionResult;
44
45
  }
45
- /**
46
- * A slicer that can be used to slice exactly one file (multiple times).
47
- * It holds its own {@link RShell} instance, maintains a cached dataflow and keeps measurements.
48
- *
49
- * Make sure to call {@link init} to initialize the slicer, before calling {@link slice}.
50
- * After slicing, call {@link finish} to close the R session and retrieve the stats.
51
- *
52
- * @note Under the hood, the benchmark slicer maintains a {@link PipelineExecutor} using the {@link DEFAULT_SLICING_PIPELINE}.
53
- */
54
46
  export declare class BenchmarkSlicer {
55
47
  /** Measures all data recorded *once* per slicer (complete setup up to the dataflow graph creation) */
56
48
  private readonly commonMeasurements;
57
49
  private readonly perSliceMeasurements;
58
50
  private readonly deltas;
59
- private readonly shell;
51
+ private readonly parserName;
60
52
  private stats;
61
53
  private loadedXml;
62
54
  private dataflow;
63
55
  private normalizedAst;
64
56
  private totalStopwatch;
65
57
  private finished;
66
- private pipeline;
67
- constructor();
58
+ private executor;
59
+ private parser;
60
+ constructor(parserName: KnownParserName);
68
61
  /**
69
62
  * Initialize the slicer on the given request.
70
63
  * Can only be called once for each instance.
@@ -11,35 +11,27 @@ exports.BenchmarkSlicer = exports.benchmarkLogger = void 0;
11
11
  const stopwatch_1 = require("./stopwatch");
12
12
  const fs_1 = __importDefault(require("fs"));
13
13
  const log_1 = require("../util/log");
14
- const pipeline_executor_1 = require("../core/pipeline-executor");
15
14
  const assert_1 = require("../util/assert");
16
15
  const strings_1 = require("../util/strings");
17
- const shell_1 = require("../r-bridge/shell");
18
16
  const default_pipelines_1 = require("../core/steps/pipeline/default-pipelines");
19
17
  const retriever_1 = require("../r-bridge/retriever");
20
18
  const collect_all_1 = require("../slicing/criterion/collect-all");
21
19
  const type_1 = require("../r-bridge/lang-4.x/ast/model/type");
22
20
  const visitor_1 = require("../r-bridge/lang-4.x/ast/model/processing/visitor");
23
21
  const size_of_1 = require("./stats/size-of");
22
+ const shell_1 = require("../r-bridge/shell");
23
+ const tree_sitter_types_1 = require("../r-bridge/lang-4.x/tree-sitter/tree-sitter-types");
24
+ const tree_sitter_executor_1 = require("../r-bridge/lang-4.x/tree-sitter/tree-sitter-executor");
24
25
  /**
25
26
  * The logger to be used for benchmarking as a global object.
26
27
  */
27
28
  exports.benchmarkLogger = log_1.log.getSubLogger({ name: 'benchmark' });
28
- /**
29
- * A slicer that can be used to slice exactly one file (multiple times).
30
- * It holds its own {@link RShell} instance, maintains a cached dataflow and keeps measurements.
31
- *
32
- * Make sure to call {@link init} to initialize the slicer, before calling {@link slice}.
33
- * After slicing, call {@link finish} to close the R session and retrieve the stats.
34
- *
35
- * @note Under the hood, the benchmark slicer maintains a {@link PipelineExecutor} using the {@link DEFAULT_SLICING_PIPELINE}.
36
- */
37
29
  class BenchmarkSlicer {
38
30
  /** Measures all data recorded *once* per slicer (complete setup up to the dataflow graph creation) */
39
31
  commonMeasurements = new stopwatch_1.Measurements();
40
32
  perSliceMeasurements = new Map();
41
33
  deltas = new Map();
42
- shell;
34
+ parserName;
43
35
  stats;
44
36
  loadedXml;
45
37
  dataflow;
@@ -47,10 +39,11 @@ class BenchmarkSlicer {
47
39
  totalStopwatch;
48
40
  finished = false;
49
41
  // Yes, this is unclean, but we know that we assign the executor during the initialization and this saves us from having to check for nullability every time
50
- pipeline = null;
51
- constructor() {
42
+ executor = null;
43
+ parser = null;
44
+ constructor(parserName) {
52
45
  this.totalStopwatch = this.commonMeasurements.start('total');
53
- this.shell = this.commonMeasurements.measure('initialize R session', () => new shell_1.RShell());
46
+ this.parserName = parserName;
54
47
  }
55
48
  /**
56
49
  * Initialize the slicer on the given request.
@@ -58,8 +51,17 @@ class BenchmarkSlicer {
58
51
  */
59
52
  async init(request, autoSelectIf) {
60
53
  (0, assert_1.guard)(this.stats === undefined, 'cannot initialize the slicer twice');
61
- this.pipeline = new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_SLICING_PIPELINE, {
62
- shell: this.shell,
54
+ // we know these are in sync so we just cast to one of them
55
+ this.parser = await this.commonMeasurements.measure('initialize R session', async () => {
56
+ if (this.parserName === 'r-shell') {
57
+ return new shell_1.RShell();
58
+ }
59
+ else {
60
+ await tree_sitter_executor_1.TreeSitterExecutor.initTreeSitter();
61
+ return new tree_sitter_executor_1.TreeSitterExecutor();
62
+ }
63
+ });
64
+ this.executor = (0, default_pipelines_1.createSlicePipeline)(this.parser, {
63
65
  request: { ...request },
64
66
  criterion: [],
65
67
  autoSelectIf
@@ -67,14 +69,30 @@ class BenchmarkSlicer {
67
69
  this.loadedXml = (await this.measureCommonStep('parse', 'retrieve AST from R code')).parsed;
68
70
  this.normalizedAst = await this.measureCommonStep('normalize', 'normalize R AST');
69
71
  this.dataflow = await this.measureCommonStep('dataflow', 'produce dataflow information');
70
- this.pipeline.switchToRequestStage();
72
+ this.executor.switchToRequestStage();
71
73
  await this.calculateStatsAfterInit(request);
72
74
  }
73
75
  async calculateStatsAfterInit(request) {
74
76
  const loadedContent = request.request === 'text' ? request.content : fs_1.default.readFileSync(request.content, 'utf-8');
75
- // retrieve number of R tokens - flowr_parsed should still contain the last parsed code
76
- const numberOfRTokens = await (0, retriever_1.retrieveNumberOfRTokensOfLastParse)(this.shell);
77
- const numberOfRTokensNoComments = await (0, retriever_1.retrieveNumberOfRTokensOfLastParse)(this.shell, true);
77
+ let numberOfRTokens;
78
+ let numberOfRTokensNoComments;
79
+ if (this.parser.name === 'r-shell') {
80
+ // retrieve number of R tokens - flowr_parsed should still contain the last parsed code
81
+ numberOfRTokens = await (0, retriever_1.retrieveNumberOfRTokensOfLastParse)(this.parser);
82
+ numberOfRTokensNoComments = await (0, retriever_1.retrieveNumberOfRTokensOfLastParse)(this.parser, true);
83
+ }
84
+ else {
85
+ const countChildren = function (node, ignoreComments = false) {
86
+ let ret = node.type === tree_sitter_types_1.TreeSitterType.Comment && ignoreComments ? 0 : 1;
87
+ for (const child of node.children) {
88
+ ret += countChildren(child, ignoreComments);
89
+ }
90
+ return ret;
91
+ };
92
+ const root = this.loadedXml.rootNode;
93
+ numberOfRTokens = countChildren(root);
94
+ numberOfRTokensNoComments = countChildren(root, true);
95
+ }
78
96
  (0, assert_1.guard)(this.normalizedAst !== undefined, 'normalizedAst should be defined after initialization');
79
97
  (0, assert_1.guard)(this.dataflow !== undefined, 'dataflow should be defined after initialization');
80
98
  // collect dataflow graph size
@@ -165,14 +183,14 @@ class BenchmarkSlicer {
165
183
  }
166
184
  };
167
185
  this.perSliceMeasurements.set(slicingCriteria, stats);
168
- this.pipeline.updateRequest({ criterion: slicingCriteria });
186
+ this.executor.updateRequest({ criterion: slicingCriteria });
169
187
  const totalStopwatch = measurements.start('total');
170
188
  const slicedOutput = await this.measureSliceStep('slice', measurements, 'static slicing');
171
189
  stats.slicingCriteria = [...slicedOutput.decodedCriteria];
172
190
  stats.reconstructedCode = await this.measureSliceStep('reconstruct', measurements, 'reconstruct code');
173
191
  totalStopwatch.stop();
174
192
  exports.benchmarkLogger.debug(`Produced code for ${JSON.stringify(slicingCriteria)}: ${stats.reconstructedCode.code}`);
175
- const results = this.pipeline.getResults(false);
193
+ const results = this.executor.getResults(false);
176
194
  if (exports.benchmarkLogger.settings.minLevel >= 3 /* LogLevel.Info */) {
177
195
  exports.benchmarkLogger.info(`mapped slicing criteria: ${slicedOutput.decodedCriteria.map(c => {
178
196
  const node = results.normalize.idMap.get(c.id);
@@ -192,7 +210,7 @@ class BenchmarkSlicer {
192
210
  /** Bridging the gap between the new internal and the old names for the benchmarking */
193
211
  async measureCommonStep(expectedStep, keyToMeasure) {
194
212
  const memoryInit = process.memoryUsage();
195
- const { result } = await this.commonMeasurements.measureAsync(keyToMeasure, () => this.pipeline.nextStep(expectedStep));
213
+ const { result } = await this.commonMeasurements.measureAsync(keyToMeasure, () => this.executor.nextStep(expectedStep));
196
214
  const memoryEnd = process.memoryUsage();
197
215
  this.deltas.set(keyToMeasure, {
198
216
  heap: memoryEnd.heapUsed - memoryInit.heapUsed,
@@ -203,7 +221,7 @@ class BenchmarkSlicer {
203
221
  return result;
204
222
  }
205
223
  async measureSliceStep(expectedStep, measure, keyToMeasure) {
206
- const { result } = await measure.measureAsync(keyToMeasure, () => this.pipeline.nextStep(expectedStep));
224
+ const { result } = await measure.measureAsync(keyToMeasure, () => this.executor.nextStep(expectedStep));
207
225
  return result;
208
226
  }
209
227
  guardActive() {
@@ -242,7 +260,7 @@ class BenchmarkSlicer {
242
260
  finish() {
243
261
  (0, assert_1.guard)(this.stats !== undefined, 'need to call init before finish');
244
262
  if (!this.finished) {
245
- this.commonMeasurements.measure('close R session', () => this.shell.close());
263
+ this.commonMeasurements.measure('close R session', () => this.parser.close());
246
264
  this.totalStopwatch.stop();
247
265
  this.finished = true;
248
266
  }
@@ -268,7 +286,7 @@ class BenchmarkSlicer {
268
286
  };
269
287
  return {
270
288
  stats: this.stats,
271
- parse: this.loadedXml,
289
+ parse: typeof this.loadedXml === 'string' ? this.loadedXml : JSON.stringify(this.loadedXml),
272
290
  dataflow: this.dataflow,
273
291
  normalize: this.normalizedAst
274
292
  };
@@ -277,7 +295,7 @@ class BenchmarkSlicer {
277
295
  * Only call in case of an error - if the session must be closed and the benchmark itself is to be considered failed/dead.
278
296
  */
279
297
  ensureSessionClosed() {
280
- this.shell.close();
298
+ this.parser?.close();
281
299
  }
282
300
  }
283
301
  exports.BenchmarkSlicer = BenchmarkSlicer;
@@ -1,3 +1,4 @@
1
+ import type { KnownParserName } from '../r-bridge/parser';
1
2
  export interface BenchmarkCliOptions {
2
3
  verbose: boolean;
3
4
  help: boolean;
@@ -7,4 +8,5 @@ export interface BenchmarkCliOptions {
7
8
  parallel: number;
8
9
  limit?: number;
9
10
  runs?: number;
11
+ parser: KnownParserName;
10
12
  }
@@ -58,7 +58,8 @@ async function benchmark() {
58
58
  '--input', f.request.content,
59
59
  '--file-id', `${i}`,
60
60
  '--output', path_1.default.join(options.output, path_1.default.relative(f.baseDir, `${f.request.content}.json`)),
61
- '--slice', options.slice, ...verboseAdd
61
+ '--slice', options.slice, ...verboseAdd,
62
+ '--parser', options.parser
62
63
  ]);
63
64
  const runs = options.runs ?? 1;
64
65
  for (let i = 1; i <= runs; i++) {
@@ -1,3 +1,4 @@
1
+ import type { KnownParserName } from '../r-bridge/parser';
1
2
  export interface SingleBenchmarkCliOptions {
2
3
  verbose: boolean;
3
4
  help: boolean;
@@ -6,4 +7,5 @@ export interface SingleBenchmarkCliOptions {
6
7
  'run-num'?: number;
7
8
  slice: string;
8
9
  output?: string;
10
+ parser: KnownParserName;
9
11
  }
@@ -36,7 +36,7 @@ async function benchmark() {
36
36
  const fileStat = fs_1.default.statSync(options.input);
37
37
  (0, assert_1.guard)(fileStat.isFile(), `File ${options.input} does not exist or is no file`);
38
38
  const request = { request: 'file', content: options.input };
39
- const slicer = new slicer_1.BenchmarkSlicer();
39
+ const slicer = new slicer_1.BenchmarkSlicer(options.parser);
40
40
  try {
41
41
  await slicer.init(request);
42
42
  // ${escape}1F${escape}1G${escape}2K for line reset
@@ -69,7 +69,7 @@ async function benchmark() {
69
69
  catch (e) {
70
70
  if (e instanceof Error) {
71
71
  if (!e.message.includes('unable to parse R')) {
72
- console.log(`${prefix} Non R-Side error : ${e.message}`);
72
+ console.log(`${prefix} Non R-Side error : ${e.name} ${e.message} ${e.stack}`);
73
73
  }
74
74
  }
75
75
  slicer.ensureSessionClosed(); // ensure finish
@@ -19,7 +19,8 @@ exports.benchmarkOptions = [
19
19
  { name: 'input', alias: 'i', type: String, description: 'Pass a folder or file as src to read from', multiple: true, defaultOption: true, defaultValue: [], typeLabel: '{underline files/folders}' },
20
20
  { name: 'parallel', alias: 'p', type: String, description: 'Number of parallel executors (defaults to {italic max(cpu.count-1, 1)})', defaultValue: Math.max(os_1.default.cpus().length - 1, 1), typeLabel: '{underline number}' },
21
21
  { name: 'slice', alias: 's', type: String, description: 'Automatically slice for *all* variables (default) or *no* slicing and only parsing/dataflow construction. Numbers will indicate: sample X random slices from all.', defaultValue: 'all', typeLabel: '{underline all/no}' },
22
- { name: 'output', alias: 'o', type: String, description: `Directory to write all the measurements to in a per-file-basis (defaults to {italic benchmark-${StartTimeString}})`, defaultValue: `benchmark-${StartTimeString}`, typeLabel: '{underline file}' }
22
+ { name: 'output', alias: 'o', type: String, description: `Directory to write all the measurements to in a per-file-basis (defaults to {italic benchmark-${StartTimeString}})`, defaultValue: `benchmark-${StartTimeString}`, typeLabel: '{underline file}' },
23
+ { name: 'parser', type: String, description: 'The parser to use for the benchmark', defaultValue: 'r-shell', typeLabel: '{underline parser}' }
23
24
  ];
24
25
  exports.benchmarkHelperOptions = [
25
26
  { name: 'verbose', alias: 'v', type: Boolean, description: 'Run with verbose logging [do not use for the real benchmark as this affects the time measurements, but only to find errors]' },
@@ -29,6 +30,7 @@ exports.benchmarkHelperOptions = [
29
30
  { name: 'run-num', alias: 'r', type: Number, description: 'The n-th time that the file with the given file-id is being benchmarked' },
30
31
  { name: 'slice', alias: 's', type: String, description: 'Automatically slice for *all* variables (default) or *no* slicing and only parsing/dataflow construction. Numbers will indicate: sample X random slices from all.', defaultValue: 'all', typeLabel: '{underline all/no}' },
31
32
  { name: 'output', alias: 'o', type: String, description: 'File to write the measurements to (appends a single line in JSON format)', typeLabel: '{underline file}' },
33
+ { name: 'parser', type: String, description: 'The parser to use for the benchmark', defaultValue: 'r-shell', typeLabel: '{underline parser}' }
32
34
  ];
33
35
  exports.exportQuadsOptions = [
34
36
  { name: 'verbose', alias: 'v', type: Boolean, description: 'Run with verbose logging' },
@@ -47,7 +47,7 @@ exports.flowrMainOptionDefinitions = [
47
47
  {
48
48
  name: 'r-path',
49
49
  type: String,
50
- description: 'The path to the R executable to use. Defaults to your PATH.',
50
+ description: 'The path to the R executable to use. Defaults to your PATH. This option is being phased out in favor of the engine configuration option "engine.r-shell.r-path", which should be used instead.',
51
51
  multiple: false
52
52
  },
53
53
  {
@@ -82,7 +82,41 @@ exports.flowrMainOptionDefinitions = [
82
82
  type: Boolean,
83
83
  description: 'Provide information about the version of flowR as well as its underlying R system and exit.'
84
84
  },
85
- { name: 'ws', type: Boolean, description: 'If the server flag is set, use websocket for messaging' }
85
+ { name: 'ws', type: Boolean, description: 'If the server flag is set, use websocket for messaging' },
86
+ {
87
+ name: 'engine.r-shell.disabled',
88
+ type: Boolean,
89
+ description: 'Disable the R shell engine'
90
+ },
91
+ {
92
+ name: 'engine.r-shell.r-path',
93
+ type: String,
94
+ description: 'The path to the R executable to use. Defaults to your PATH.',
95
+ multiple: false
96
+ },
97
+ {
98
+ name: 'engine.tree-sitter.disabled',
99
+ type: Boolean,
100
+ description: 'Disable the tree-sitter engine'
101
+ },
102
+ {
103
+ name: 'engine.tree-sitter.wasm-path',
104
+ type: String,
105
+ description: 'The path to the tree-sitter-r WASM binary to use. Defaults to the one shipped with flowR.',
106
+ multiple: false
107
+ },
108
+ {
109
+ name: 'engine.tree-sitter.tree-sitter-wasm-path',
110
+ type: String,
111
+ description: 'The path to the tree-sitter WASM binary to use. Defaults to the path specified by the tree-sitter package.',
112
+ multiple: false
113
+ },
114
+ {
115
+ name: 'default-engine',
116
+ type: String,
117
+ description: 'The default engine to use for interacting with R code. If this is undefined, an arbitrary engine from the specified list will be used.',
118
+ multiple: false
119
+ }
86
120
  ];
87
121
  exports.defaultConfigFile = 'flowr.json';
88
122
  //# sourceMappingURL=flowr-main-options.js.map
package/cli/flowr.d.ts CHANGED
@@ -14,6 +14,12 @@ export interface FlowrCliOptions {
14
14
  verbose: boolean;
15
15
  version: boolean;
16
16
  ws: boolean;
17
+ 'default-engine': string;
18
+ 'engine.r-shell.disabled': boolean;
19
+ 'engine.r-shell.r-path': string | undefined;
20
+ 'engine.tree-sitter.disabled': boolean;
21
+ 'engine.tree-sitter.wasm-path': string | undefined;
22
+ 'engine.tree-sitter.tree-sitter-wasm-path': string | undefined;
17
23
  }
18
24
  export declare const optionHelp: ({
19
25
  header: string;
package/cli/flowr.js CHANGED
@@ -21,6 +21,7 @@ const core_1 = require("./repl/core");
21
21
  const repl_version_1 = require("./repl/commands/repl-version");
22
22
  const print_version_1 = require("./repl/print-version");
23
23
  const flowr_main_options_1 = require("./flowr-main-options");
24
+ const tree_sitter_executor_1 = require("../r-bridge/lang-4.x/tree-sitter/tree-sitter-executor");
24
25
  const fs_1 = __importDefault(require("fs"));
25
26
  exports.toolName = 'flowr';
26
27
  exports.optionHelp = [
@@ -69,20 +70,44 @@ if (!usedConfig) {
69
70
  }
70
71
  (0, config_1.setConfigFile)(options['config-file'] ?? flowr_main_options_1.defaultConfigFile, undefined, true);
71
72
  }
72
- function retrieveShell() {
73
- // we keep an active shell session to allow other parse investigations :)
74
- let opts = {
75
- revive: 2 /* RShellReviveOptions.Always */,
76
- onRevive: (code, signal) => {
77
- const signalText = signal == null ? '' : ` and signal ${signal}`;
78
- console.log(ansi_1.formatter.format(`R process exited with code ${code}${signalText}. Restarting...`, { color: 5 /* Colors.Magenta */, effect: ansi_1.ColorEffect.Foreground }));
79
- console.log((0, ansi_1.italic)(`If you want to exit, press either Ctrl+C twice, or enter ${(0, ansi_1.bold)(':quit')}`));
80
- }
81
- };
82
- if (options['r-path']) {
83
- opts = { ...opts, pathToRExecutable: options['r-path'] };
73
+ // for all options that we manually supply that have a config equivalent, set them in the config
74
+ if (!options['engine.r-shell.disabled']) {
75
+ (0, config_1.amendConfig)({ engines: [{ type: 'r-shell', rPath: options['r-path'] || options['engine.r-shell.r-path'] }] });
76
+ }
77
+ if (!options['engine.tree-sitter.disabled']) {
78
+ (0, config_1.amendConfig)({ engines: [{
79
+ type: 'tree-sitter',
80
+ wasmPath: options['engine.tree-sitter.wasm-path'],
81
+ treeSitterWasmPath: options['engine.tree-sitter.tree-sitter-wasm-path']
82
+ }] });
83
+ }
84
+ if (options['default-engine']) {
85
+ (0, config_1.amendConfig)({ defaultEngine: options['default-engine'] });
86
+ }
87
+ async function retrieveEngineInstances() {
88
+ const engines = {};
89
+ if ((0, config_1.getEngineConfig)('r-shell')) {
90
+ // we keep an active shell session to allow other parse investigations :)
91
+ engines['r-shell'] = new shell_1.RShell({
92
+ revive: 2 /* RShellReviveOptions.Always */,
93
+ onRevive: (code, signal) => {
94
+ const signalText = signal == null ? '' : ` and signal ${signal}`;
95
+ console.log(ansi_1.formatter.format(`R process exited with code ${code}${signalText}. Restarting...`, { color: 5 /* Colors.Magenta */, effect: ansi_1.ColorEffect.Foreground }));
96
+ console.log((0, ansi_1.italic)(`If you want to exit, press either Ctrl+C twice, or enter ${(0, ansi_1.bold)(':quit')}`));
97
+ }
98
+ });
99
+ }
100
+ if ((0, config_1.getEngineConfig)('tree-sitter')) {
101
+ await tree_sitter_executor_1.TreeSitterExecutor.initTreeSitter();
102
+ engines['tree-sitter'] = new tree_sitter_executor_1.TreeSitterExecutor();
84
103
  }
85
- return new shell_1.RShell(opts);
104
+ let defaultEngine = (0, config_1.getConfig)().defaultEngine;
105
+ if (!defaultEngine || !engines[defaultEngine]) {
106
+ // if a default engine isn't specified, we just take the first one we have
107
+ defaultEngine = Object.keys(engines)[0];
108
+ }
109
+ log_1.log.info(`Using engines ${Object.keys(engines).join(', ')} with default ${defaultEngine}`);
110
+ return { engines, default: defaultEngine };
86
111
  }
87
112
  async function mainRepl() {
88
113
  if (options.script) {
@@ -97,18 +122,20 @@ async function mainRepl() {
97
122
  console.log((0, command_line_usage_1.default)(exports.optionHelp));
98
123
  process.exit(0);
99
124
  }
125
+ const engines = await retrieveEngineInstances();
126
+ const defaultEngine = engines.engines[engines.default];
100
127
  if (options.version) {
101
- const shell = new shell_1.RShell();
102
- process.on('exit', () => shell.close());
103
- await (0, repl_version_1.printVersionInformation)(repl_main_1.standardReplOutput, shell);
128
+ for (const engine of Object.values(engines.engines)) {
129
+ await (0, repl_version_1.printVersionInformation)(repl_main_1.standardReplOutput, engine);
130
+ engine?.close();
131
+ }
104
132
  process.exit(0);
105
133
  }
106
- const shell = retrieveShell();
107
134
  const end = () => {
108
135
  if (options.execute === undefined) {
109
136
  console.log(`\n${(0, ansi_1.italic)('Exiting...')}`);
110
137
  }
111
- shell.close();
138
+ Object.values(engines.engines).forEach(e => e?.close());
112
139
  process.exit(0);
113
140
  };
114
141
  // hook some handlers
@@ -116,27 +143,27 @@ async function mainRepl() {
116
143
  process.on('SIGTERM', end);
117
144
  const allowRSessionAccess = options['r-session-access'] ?? false;
118
145
  if (options.execute) {
119
- await (0, core_1.replProcessAnswer)(repl_main_1.standardReplOutput, options.execute, shell, allowRSessionAccess);
146
+ await (0, core_1.replProcessAnswer)(repl_main_1.standardReplOutput, options.execute, defaultEngine, allowRSessionAccess);
120
147
  }
121
148
  else {
122
- await (0, print_version_1.printVersionRepl)(shell);
123
- await (0, core_1.repl)({ shell, allowRSessionAccess });
149
+ await (0, print_version_1.printVersionRepl)(defaultEngine);
150
+ await (0, core_1.repl)({ parser: defaultEngine, allowRSessionAccess });
124
151
  }
125
152
  process.exit(0);
126
153
  }
127
154
  async function mainServer(backend = new net_1.NetServer()) {
128
- const shell = retrieveShell();
155
+ const engines = await retrieveEngineInstances();
129
156
  const end = () => {
130
157
  if (options.execute === undefined) {
131
158
  console.log(`\n${(0, ansi_1.italic)('Exiting...')}`);
132
159
  }
133
- shell.close();
160
+ Object.values(engines.engines).forEach(e => e?.close());
134
161
  process.exit(0);
135
162
  };
136
163
  // hook some handlers
137
164
  process.on('SIGINT', end);
138
165
  process.on('SIGTERM', end);
139
- await new server_1.FlowRServer(shell, options['r-session-access'], backend).start(options.port);
166
+ await new server_1.FlowRServer(engines.engines, engines.default, options['r-session-access'], backend).start(options.port);
140
167
  }
141
168
  if (options.server) {
142
169
  void mainServer(options.ws ? new net_1.WebSocketServerWrapper() : new net_1.NetServer());
@@ -1,14 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.controlflowStarCommand = exports.controlflowCommand = void 0;
4
- const pipeline_executor_1 = require("../../../core/pipeline-executor");
5
4
  const cfg_1 = require("../../../util/cfg/cfg");
6
5
  const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
7
6
  const retriever_1 = require("../../../r-bridge/retriever");
8
7
  const cfg_2 = require("../../../util/mermaid/cfg");
9
- async function controlflow(shell, remainingLine) {
10
- return await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_NORMALIZE_PIPELINE, {
11
- shell,
8
+ async function controlflow(parser, remainingLine) {
9
+ return await (0, default_pipelines_1.createNormalizePipeline)(parser, {
12
10
  request: (0, retriever_1.requestFromInput)(remainingLine.trim())
13
11
  }).allRemainingSteps();
14
12
  }
@@ -1,13 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.dataflowStarCommand = exports.dataflowCommand = void 0;
4
- const pipeline_executor_1 = require("../../../core/pipeline-executor");
5
4
  const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
6
5
  const retriever_1 = require("../../../r-bridge/retriever");
7
6
  const dfg_1 = require("../../../util/mermaid/dfg");
8
- async function dataflow(shell, remainingLine) {
9
- return await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_DATAFLOW_PIPELINE, {
10
- shell,
7
+ async function dataflow(parser, remainingLine) {
8
+ return await (0, default_pipelines_1.createDataflowPipeline)(parser, {
11
9
  request: (0, retriever_1.requestFromInput)(remainingLine.trim())
12
10
  }).allRemainingSteps();
13
11
  }
@@ -1,4 +1,4 @@
1
1
  import type { ReplCommand, ReplOutput } from './repl-main';
2
- import type { RShell } from '../../../r-bridge/shell';
3
- export declare function executeRShellCommand(output: ReplOutput, shell: RShell, statement: string): Promise<void>;
2
+ import type { KnownParser } from '../../../r-bridge/parser';
3
+ export declare function tryExecuteRShellCommand(output: ReplOutput, parser: KnownParser, statement: string, allowRSessionAccess: boolean): Promise<void>;
4
4
  export declare const executeCommand: ReplCommand;
@@ -1,8 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.executeCommand = void 0;
4
- exports.executeRShellCommand = executeRShellCommand;
4
+ exports.tryExecuteRShellCommand = tryExecuteRShellCommand;
5
5
  const ansi_1 = require("../../../util/ansi");
6
+ const shell_1 = require("../../../r-bridge/shell");
7
+ async function tryExecuteRShellCommand(output, parser, statement, allowRSessionAccess) {
8
+ if (!allowRSessionAccess) {
9
+ output.stderr(`${output.formatter.format('You are not allowed to execute arbitrary R code.', { style: 1 /* FontStyles.Bold */, color: 1 /* Colors.Red */, effect: ansi_1.ColorEffect.Foreground })}\nIf you want to do so, please restart flowR with the ${output.formatter.format('--r-session-access', { style: 1 /* FontStyles.Bold */ })} flag. Please be careful of the security implications of this action.`);
10
+ }
11
+ else if (parser instanceof shell_1.RShell) {
12
+ await executeRShellCommand(output, parser, statement);
13
+ }
14
+ else {
15
+ output.stderr(`Executing arbitrary R code is only possible when using the r-shell engine as the default engine. Enable it using the configuration file or the ${output.formatter.format('--default-engine r-shell', { style: 1 /* FontStyles.Bold */ })} command line option.`);
16
+ }
17
+ }
6
18
  async function executeRShellCommand(output, shell, statement) {
7
19
  try {
8
20
  const result = await shell.sendCommandWithOutput(statement, {
@@ -16,12 +28,10 @@ async function executeRShellCommand(output, shell, statement) {
16
28
  }
17
29
  }
18
30
  exports.executeCommand = {
19
- description: 'Execute the given code as R code (essentially similar to using now command)',
31
+ description: 'Execute the given code as R code (essentially similar to using now command). This requires the `--r-session-access` flag to be set and requires the r-shell engine.',
20
32
  usageExample: ':execute',
21
33
  aliases: ['e', 'r'],
22
34
  script: false,
23
- fn: async (output, shell, remainingLine) => {
24
- await executeRShellCommand(output, shell, remainingLine);
25
- }
35
+ fn: tryExecuteRShellCommand
26
36
  };
27
37
  //# sourceMappingURL=repl-execute.js.map
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.lineageCommand = void 0;
4
4
  exports.getLineage = getLineage;
5
- 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");
8
7
  const parse_1 = require("../../../slicing/criterion/parse");
@@ -11,9 +10,8 @@ const assert_1 = require("../../../util/assert");
11
10
  function splitAt(str, idx) {
12
11
  return [str.slice(0, idx), str.slice(idx)];
13
12
  }
14
- async function getDfg(shell, remainingLine) {
15
- return await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_DATAFLOW_PIPELINE, {
16
- shell,
13
+ async function getDfg(parser, remainingLine) {
14
+ return await (0, default_pipelines_1.createDataflowPipeline)(parser, {
17
15
  request: (0, retriever_1.requestFromInput)(remainingLine.trim())
18
16
  }).allRemainingSteps();
19
17
  }
@@ -1,5 +1,5 @@
1
1
  import type { OutputFormatter } from '../../../util/ansi';
2
- import type { RShell } from '../../../r-bridge/shell';
2
+ import type { KnownParser } from '../../../r-bridge/parser';
3
3
  /**
4
4
  * Defines the main interface for output of the repl.
5
5
  * This allows us to redirect it (e.g., in the case of a server connection or tests).
@@ -35,5 +35,5 @@ export interface ReplCommand {
35
35
  * Function to execute when the command is invoked, it must not write to the command line but instead use the output handler.
36
36
  * Furthermore, it has to obey the formatter defined in the {@link ReplOutput}.
37
37
  */
38
- fn: (output: ReplOutput, shell: RShell, remainingLine: string) => Promise<void> | void;
38
+ fn: (output: ReplOutput, parser: KnownParser, remainingLine: string, allowRSessionAccess: boolean) => Promise<void> | void;
39
39
  }
@@ -1,13 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeStarCommand = exports.normalizeCommand = void 0;
4
- const pipeline_executor_1 = require("../../../core/pipeline-executor");
5
4
  const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
6
5
  const retriever_1 = require("../../../r-bridge/retriever");
7
6
  const ast_1 = require("../../../util/mermaid/ast");
8
- async function normalize(shell, remainingLine) {
9
- return await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_NORMALIZE_PIPELINE, {
10
- shell,
7
+ async function normalize(parser, remainingLine) {
8
+ return await (0, default_pipelines_1.createNormalizePipeline)(parser, {
11
9
  request: (0, retriever_1.requestFromInput)(remainingLine.trim())
12
10
  }).allRemainingSteps();
13
11
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseCommand = void 0;
4
- const pipeline_executor_1 = require("../../../core/pipeline-executor");
5
4
  const format_1 = require("../../../r-bridge/lang-4.x/ast/parser/json/format");
6
5
  const normalize_meta_1 = require("../../../r-bridge/lang-4.x/ast/parser/main/normalize-meta");
7
6
  const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipelines");
@@ -90,9 +89,8 @@ exports.parseCommand = {
90
89
  usageExample: ':parse',
91
90
  aliases: ['p'],
92
91
  script: false,
93
- fn: async (output, shell, remainingLine) => {
94
- const result = await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_PARSE_PIPELINE, {
95
- shell,
92
+ fn: async (output, parser, remainingLine) => {
93
+ const result = await (0, default_pipelines_1.createParsePipeline)(parser, {
96
94
  request: (0, retriever_1.requestFromInput)((0, retriever_1.removeRQuotes)(remainingLine.trim()))
97
95
  }).allRemainingSteps();
98
96
  const object = (0, format_1.convertPreparedParsedData)((0, format_1.prepareParsedData)(result.parse.parsed));