@eagleoutice/flowr 2.9.9 → 2.9.10

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 (55) hide show
  1. package/README.md +34 -29
  2. package/abstract-interpretation/absint-visitor.d.ts +16 -14
  3. package/abstract-interpretation/absint-visitor.js +91 -46
  4. package/abstract-interpretation/data-frame/shape-inference.d.ts +2 -5
  5. package/abstract-interpretation/data-frame/shape-inference.js +4 -5
  6. package/abstract-interpretation/domains/abstract-domain.d.ts +4 -4
  7. package/abstract-interpretation/domains/abstract-domain.js +8 -8
  8. package/abstract-interpretation/domains/mapped-abstract-domain.d.ts +12 -5
  9. package/abstract-interpretation/domains/mapped-abstract-domain.js +47 -23
  10. package/abstract-interpretation/domains/state-abstract-domain.d.ts +30 -1
  11. package/abstract-interpretation/domains/state-abstract-domain.js +130 -4
  12. package/control-flow/cfg-simplification.d.ts +1 -0
  13. package/control-flow/cfg-simplification.js +1 -0
  14. package/control-flow/extract-cfg.js +33 -15
  15. package/control-flow/semantic-cfg-guided-visitor.js +1 -0
  16. package/dataflow/environments/built-in.d.ts +4 -0
  17. package/dataflow/environments/built-in.js +17 -5
  18. package/dataflow/environments/default-builtin-config.d.ts +4 -8
  19. package/dataflow/environments/default-builtin-config.js +8 -5
  20. package/dataflow/environments/reference-to-maybe.d.ts +8 -0
  21. package/dataflow/environments/reference-to-maybe.js +46 -3
  22. package/dataflow/eval/resolve/alias-tracking.d.ts +1 -1
  23. package/dataflow/eval/resolve/alias-tracking.js +4 -5
  24. package/dataflow/eval/resolve/resolve.js +8 -7
  25. package/dataflow/graph/graph.d.ts +1 -1
  26. package/dataflow/graph/graph.js +9 -10
  27. package/dataflow/graph/unknown-side-effect.js +3 -1
  28. package/dataflow/info.d.ts +4 -0
  29. package/dataflow/info.js +2 -2
  30. package/dataflow/internal/linker.d.ts +2 -2
  31. package/dataflow/internal/linker.js +52 -27
  32. package/dataflow/internal/process/functions/call/argument/make-argument.js +2 -1
  33. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +15 -6
  34. package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +2 -2
  35. package/dataflow/internal/process/functions/call/built-in/built-in-s-seven-new-generic.js +4 -4
  36. package/dataflow/internal/process/functions/call/built-in/built-in-source.js +22 -11
  37. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +22 -19
  38. package/dataflow/internal/process/functions/call/known-call-handling.js +0 -2
  39. package/documentation/wiki-absint.js +7 -8
  40. package/linter/rules/dead-code.js +1 -1
  41. package/linter/rules/seeded-randomness.js +1 -1
  42. package/linter/rules/unused-definition.js +1 -1
  43. package/package.json +7 -7
  44. package/project/plugins/file-plugins/files/flowr-description-file.d.ts +4 -0
  45. package/project/plugins/file-plugins/files/flowr-description-file.js +4 -0
  46. package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
  47. package/queries/catalog/df-shape-query/df-shape-query-format.d.ts +1 -1
  48. package/queries/catalog/df-shape-query/df-shape-query-format.js +3 -3
  49. package/r-bridge/lang-4.x/ast/model/processing/decorate.js +23 -17
  50. package/r-bridge/lang-4.x/ast/model/processing/role.d.ts +18 -17
  51. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +31 -13
  52. package/statistics/features/supported/data-access/data-access.js +2 -2
  53. package/util/mermaid/ast.js +1 -1
  54. package/util/mermaid/dfg.js +6 -5
  55. package/util/version.js +1 -1
@@ -14,6 +14,7 @@ const general_1 = require("../../../../../eval/values/general");
14
14
  const alias_tracking_1 = require("../../../../../eval/resolve/alias-tracking");
15
15
  const reference_to_maybe_1 = require("../../../../../environments/reference-to-maybe");
16
16
  const built_in_1 = require("../../../../../environments/built-in");
17
+ const append_1 = require("../../../../../environments/append");
17
18
  /**
18
19
  * Process a while loop like `while(cond) { ... }`.
19
20
  */
@@ -27,6 +28,8 @@ function processWhileLoop(name, args, rootId, data) {
27
28
  logger_1.dataflowLogger.warn(`While-Loop ${identifier_1.Identifier.toString(name.content)} has empty arguments in ${JSON.stringify(args)}, skipping`);
28
29
  return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information;
29
30
  }
31
+ const nameId = name.info.id;
32
+ const origEnv = data.environment;
30
33
  // we should defer this to the abstract interpretation
31
34
  const values = (0, alias_tracking_1.resolveIdToValue)(unpackedArgs[0]?.info.id, { environment: data.environment, idMap: data.completeAst.idMap, resolve: data.ctx.config.solver.variables, ctx: data.ctx });
32
35
  const conditionIsAlwaysFalse = (0, general_1.valueSetGuard)(values)?.elements.every(d => d.type === 'logical' && d.value === false) ?? false;
@@ -41,49 +44,49 @@ function processWhileLoop(name, args, rootId, data) {
41
44
  rootId,
42
45
  data,
43
46
  markAsNSE: [1],
44
- patchData: (d, i) => {
45
- if (i === 1) {
46
- return { ...d, cds: [...d.cds ?? [], { id: name.info.id, when: true }] };
47
- }
48
- return d;
49
- }, origin: built_in_1.BuiltInProcName.WhileLoop
47
+ origin: built_in_1.BuiltInProcName.WhileLoop
50
48
  });
51
49
  const [condition, body] = processedArguments;
52
50
  // If the condition is always false, we don't include the body
53
51
  if (condition !== undefined && conditionIsAlwaysFalse) {
54
- information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
52
+ information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
55
53
  return {
56
54
  unknownReferences: [],
57
- in: [{ nodeId: name.info.id, name: name.lexeme, cds: data.cds, type: identifier_1.ReferenceType.Function }],
55
+ in: [{ nodeId: nameId, name: name.lexeme, cds: data.cds, type: identifier_1.ReferenceType.Function }],
58
56
  out: condition.out,
59
- entryPoint: name.info.id,
57
+ entryPoint: nameId,
60
58
  exitPoints: [],
61
59
  graph: information.graph,
62
60
  environment: information.environment,
63
61
  hooks: condition.hooks
64
62
  };
65
63
  }
64
+ const conditionIsAlwaysTrue = (0, general_1.valueSetGuard)(values)?.elements.every(d => d.type === 'logical' && d.value === true) ?? false;
66
65
  (0, assert_1.guard)(condition !== undefined && body !== undefined, () => `While-Loop ${identifier_1.Identifier.toString(name.content)} has no condition or body, impossible!`);
67
66
  const originalDependency = data.cds;
68
67
  if ((0, info_1.alwaysExits)(condition)) {
69
68
  logger_1.dataflowLogger.warn(`While-Loop ${rootId} forces exit in condition, skipping rest`);
70
- information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
69
+ information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
71
70
  return condition;
72
71
  }
73
- const cdTrue = [{ id: name.info.id, when: true }];
74
- const remainingInputs = (0, linker_1.linkInputs)((0, reference_to_maybe_1.makeAllMaybe)(body.unknownReferences, information.graph, information.environment, false, cdTrue).concat((0, reference_to_maybe_1.makeAllMaybe)(body.in, information.graph, information.environment, false, cdTrue)), information.environment, condition.in.concat(condition.unknownReferences), information.graph, true);
75
- (0, linker_1.linkCircularRedefinitionsWithinALoop)(information.graph, (0, linker_1.produceNameSharedIdMap)((0, linker_1.findNonLocalReads)(information.graph, condition.in)), body.out);
76
- (0, linker_1.reapplyLoopExitPoints)(body.exitPoints, body.in.concat(body.out, body.unknownReferences));
72
+ const cdTrue = [{ id: nameId, when: true }];
73
+ const bodyRead = body.in.concat(body.unknownReferences);
74
+ (0, reference_to_maybe_1.applyCdsToAllInGraphButConstants)(body.graph, bodyRead, cdTrue);
75
+ const remainingInputs = (0, linker_1.linkInputs)(bodyRead, information.environment, condition.in.concat(condition.unknownReferences), information.graph, true);
76
+ (0, reference_to_maybe_1.applyCdToReferences)(body.out, cdTrue);
77
+ (0, linker_1.linkCircularRedefinitionsWithinALoop)(information.graph, (0, linker_1.produceNameSharedIdMap)((0, linker_1.findNonLocalReads)(information.graph, new Set(condition.in.map(i => i.nodeId)))), body.out);
78
+ (0, linker_1.reapplyLoopExitPoints)(body.exitPoints, body.in.concat(body.out, body.unknownReferences), information.graph);
77
79
  // as the while-loop always evaluates its condition
78
- information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
80
+ information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
79
81
  return {
80
82
  unknownReferences: [],
81
- in: [{ nodeId: name.info.id, name: name.lexeme, cds: originalDependency, type: identifier_1.ReferenceType.Function }, ...remainingInputs],
82
- out: condition.out.concat((0, reference_to_maybe_1.makeAllMaybe)(body.out, information.graph, information.environment, true, cdTrue)),
83
- entryPoint: name.info.id,
83
+ in: [{ nodeId: nameId, name: name.lexeme, cds: originalDependency, type: identifier_1.ReferenceType.Function }, ...remainingInputs],
84
+ out: condition.out.concat(body.out),
85
+ entryPoint: nameId,
84
86
  exitPoints: (0, info_1.filterOutLoopExitPoints)(body.exitPoints),
85
87
  graph: information.graph,
86
- environment: information.environment,
88
+ // as we do not know whether the loop executes at all, we have to merge the environments of the condition and the body, as both may be relevant
89
+ environment: conditionIsAlwaysTrue ? information.environment : (0, append_1.appendEnvironment)(origEnv, information.environment),
87
90
  hooks: condition.hooks.concat(body.hooks)
88
91
  };
89
92
  }
@@ -9,7 +9,6 @@ const graph_1 = require("../../../../graph/graph");
9
9
  const edge_1 = require("../../../../graph/edge");
10
10
  const logger_1 = require("../../../../logger");
11
11
  const vertex_1 = require("../../../../graph/vertex");
12
- const log_1 = require("../../../../../util/log");
13
12
  const unknown_side_effect_1 = require("../../../../graph/unknown-side-effect");
14
13
  const built_in_1 = require("../../../../environments/built-in");
15
14
  /**
@@ -36,7 +35,6 @@ function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = fal
36
35
  const functionName = (0, processor_1.processDataflowFor)(name, data);
37
36
  const finalGraph = new graph_1.DataflowGraph(data.completeAst.idMap);
38
37
  const functionCallName = name.content;
39
- (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Processing known function call ${identifier_1.Identifier.toString(functionCallName)} with ${args.length} arguments`);
40
38
  const processArgs = reverseOrder ? args.toReversed() : args;
41
39
  const { finalEnv, callArgs, remainingReadInArgs, processedArguments } = (0, common_1.processAllArguments)({ functionName, args: processArgs, data, finalGraph, functionRootId: rootId, patchData, forceArgs });
42
40
  if (markAsNSE) {
@@ -17,7 +17,7 @@ class IntervalInferenceVisitor extends absint_visitor_1.AbstractInterpretationVi
17
17
  onNumberConstant({ vertex, node }) {
18
18
  super.onNumberConstant({ vertex, node });
19
19
  const interval = new interval_domain_1.IntervalDomain([node.content.num, node.content.num]);
20
- this.currentState.set(node.info.id, interval);
20
+ this.updateState(node.info.id, interval);
21
21
  }
22
22
  onFunctionCall({ call }) {
23
23
  super.onFunctionCall({ call });
@@ -31,9 +31,9 @@ class IntervalInferenceVisitor extends absint_visitor_1.AbstractInterpretationVi
31
31
  // We map the numerical operation to the resulting interval after applying the abstract semantics of the operation
32
32
  switch (identifier_1.Identifier.getName(call.name)) {
33
33
  case '+':
34
- return this.currentState.set(call.id, left.add(right));
34
+ return this.updateState(call.id, left.add(right));
35
35
  case '-':
36
- return this.currentState.set(call.id, left.subtract(right));
36
+ return this.updateState(call.id, left.subtract(right));
37
37
  }
38
38
  }
39
39
  }
@@ -51,8 +51,7 @@ async function inferIntervals() {
51
51
  const dfg = (await analyzer.dataflow()).graph;
52
52
  const cfg = await analyzer.controlflow(undefined, cfg_kind_1.CfgKind.NoFunctionDefs);
53
53
  const ctx = analyzer.inspectContext();
54
- const domain = new state_abstract_domain_1.StateAbstractDomain(new Map());
55
- const inference = new IntervalInferenceVisitor({ controlFlow: cfg, dfg: dfg, normalizedAst: ast, ctx: ctx, domain: domain });
54
+ const inference = new IntervalInferenceVisitor({ controlFlow: cfg, dfg: dfg, normalizedAst: ast, ctx: ctx });
56
55
  inference.start();
57
56
  const result = inference.getEndState();
58
57
  return result.value.entries().toArray()
@@ -128,15 +127,15 @@ For example, if we want to perform a (very basic) interval analysis using abstra
128
127
 
129
128
  ${ctx.code(IntervalInferenceVisitor)}
130
129
 
131
- The interval inference visitor first overrides the ${ctx.link(`${semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor.name}:::onNumberConstant`)} function to infer intervals for visited control flow vertices that represent numeric constants. For numeric constants, the resulting interval consists just of the number value of the constant. We then update the current abstract state of the visitor by setting the inferred abstract value of the currently visited control flow vertex to the new interval.
130
+ The interval inference visitor first overrides the ${ctx.link(`${semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor.name}:::onNumberConstant`)} function to infer intervals for visited control flow vertices that represent numeric constants. For numeric constants, the resulting interval consists just of the number value of the constant. We then update the current abstract state of the visitor via ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'updateState', { hideClass: true })} by setting the inferred abstract value of the currently visited control flow vertex to the new interval.
132
131
 
133
- In this simple example, we only want to support the addition and subtraction of numeric values. Therefore, we override the ${ctx.link(`${absint_visitor_1.AbstractInterpretationVisitor.name}:::onFunctionCall`)} function to apply the abstract semantics of additions and subtraction with resprect to the interval domain. For the addition and subtraction, we are only interested in function calls with exactly two non-empty arguments. We first resolve the currently inferred abstract value for the left and right operand of the function call. If we have no inferred value for one of the operands, this function call might not be a numeric function call and we ignore it. Otherwise, we check whether the function call represents an addition or subtraction and apply the abstract semantics of the operation to the left and right operand. We then again update the current abstract state of the visitor by setting the inferred abstract value of the currently visited function call vertex to the abstract value resulting from applying the abstract semantics of the operation to the operands.
132
+ In this simple example, we only want to support the addition and subtraction of numeric values. Therefore, we override the ${ctx.link(`${absint_visitor_1.AbstractInterpretationVisitor.name}:::onFunctionCall`)} function to apply the abstract semantics of additions and subtraction with resprect to the interval domain. For the addition and subtraction, we are only interested in function calls with exactly two non-empty arguments. We first resolve the currently inferred abstract value for the left and right operand of the function call. If we have no inferred value for one of the operands, this function call might not be a numeric function call and we ignore it. Otherwise, we check whether the function call represents an addition or subtraction and apply the abstract semantics of the operation to the left and right operand. We then again update the current abstract state of the visitor via ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'updateState', { hideClass: true })} by setting the inferred abstract value of the currently visited function call vertex to the abstract value resulting from applying the abstract semantics of the operation to the operands.
134
133
 
135
134
  If we now want to run the interval inference, we can write the following code:
136
135
 
137
136
  ${ctx.code(inferIntervals, { dropLinesStart: 1, dropLinesEnd: 5 })}
138
137
 
139
- We first need a ${ctx.linkPage('wiki/Analyzer', 'flowR analyzer')} (in this case, using the ${ctx.linkPage('wiki/Engines', 'tree-sitter engine')}). In this example, we want to analyze a small example code that assigns \`42\` to the variable \`x\`, randomly assigns \`6\` or \`12\` to the variable \`y\`, and assignes the sum of \`x\` and \`y\` to the variable \`z\`. For the abstract interpretation visitor, we need to retrieve the ${ctx.linkPage('wiki/Normalized AST', 'normalized AST')}, ${ctx.linkPage('wiki/Dataflow Graph', 'dataflow graph')}, ${ctx.linkPage('wiki/Control Flow Graph', 'control flow graph')}, and context of the flowR anaylzer. Additionally, we need to provide a state abstract domain for the visitor -- in this case, a state abstract domain for the interval domain. We then construct a new ${ctx.link(IntervalInferenceVisitor)} using the control flow graph, dataflow graph, normalized AST, analyzer context, and state abstract domain, and start the visitor using ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'start', { hideClass: true })}. After the visitor is finished, we retrieve the inferred abstract state at the end of the program using ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'getEndState', { hideClass: true })}.
138
+ We first need a ${ctx.linkPage('wiki/Analyzer', 'flowR analyzer')} (in this case, using the ${ctx.linkPage('wiki/Engines', 'tree-sitter engine')}). In this example, we want to analyze a small example code that assigns \`42\` to the variable \`x\`, randomly assigns \`6\` or \`12\` to the variable \`y\`, and assignes the sum of \`x\` and \`y\` to the variable \`z\`. For the abstract interpretation visitor, we need to retrieve the ${ctx.linkPage('wiki/Normalized AST', 'normalized AST')}, ${ctx.linkPage('wiki/Dataflow Graph', 'dataflow graph')}, ${ctx.linkPage('wiki/Control Flow Graph', 'control flow graph')}, and context of the flowR anaylzer. For performance reasons, we construct the control flow graph without simplification passes, data flow information, and function definitions. We then create a new ${ctx.link(IntervalInferenceVisitor)} using the control flow graph, dataflow graph, normalized AST, and analyzer context, and start the visitor using ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'start', { hideClass: true })}. After the visitor is finished, we retrieve the inferred abstract state at the end of the program using ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'getEndState', { hideClass: true })}.
140
139
 
141
140
  If we now print the inferred abstract state at the end of the program, we get the following output:
142
141
 
@@ -22,7 +22,7 @@ exports.DEAD_CODE = {
22
22
  .filter(element => {
23
23
  meta.consideredNodes++;
24
24
  const cfgInformation = (0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.CfgInformation);
25
- return cfgInformation.isRoot && !cfgInformation.isReachable;
25
+ return element.node.info.role !== "el-g" /* RoleInParent.ExpressionListGrouping */ && !cfgInformation.isReachable;
26
26
  })
27
27
  .map(element => ({
28
28
  certainty: linter_format_1.LintingResultCertainty.Certain,
@@ -35,7 +35,7 @@ exports.SEEDED_RANDOMNESS = {
35
35
  { callName: getDefaultAssignments().flatMap(b => b.names).map(identifier_1.Identifier.getName), cascadeIf: () => cascade_action_1.CascadeAction.Continue }
36
36
  ]),
37
37
  processSearchResult: (elements, config, { dataflow, analyzer }) => {
38
- const assignmentProducers = new Set(config.randomnessProducers.filter(p => p.type == 'assignment').map(p => p.name));
38
+ const assignmentProducers = new Set(config.randomnessProducers.filter(p => p.type === 'assignment').map(p => p.name));
39
39
  const assignmentArgIndexes = new Map(getDefaultAssignments().flatMap(a => a.names.map(n => ([identifier_1.Identifier.getName(n), a.config?.swapSourceAndTarget ? 1 : 0]))));
40
40
  const metadata = {
41
41
  consumerCalls: 0,
@@ -19,7 +19,7 @@ function getDefinitionArguments(def, dfg) {
19
19
  function buildQuickFix(variable, dfg, ast) {
20
20
  // first we check whether any of the 'Defined by' targets have any obligations - if so, we can not remove the definition
21
21
  // otherwise we can automatically remove the full definition!
22
- if (variable.info.role === "accessed" /* RoleInParent.Accessed */ || variable.info.role === "for-var" /* RoleInParent.ForVariable */) {
22
+ if (variable.info.role === "acc" /* RoleInParent.Accessed */ || variable.info.role === "for-var" /* RoleInParent.ForVariable */) {
23
23
  // this is an access or a for variable, we can not remove it currently
24
24
  return undefined;
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.9.9",
3
+ "version": "2.9.10",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -199,7 +199,7 @@
199
199
  },
200
200
  "dependencies": {
201
201
  "@eagleoutice/tree-sitter-r": "^1.1.2",
202
- "@jupyterlab/nbformat": "^4.5.0",
202
+ "@jupyterlab/nbformat": "^4.5.4",
203
203
  "@xmldom/xmldom": "^0.9.7",
204
204
  "clipboardy": "^4.0.0",
205
205
  "command-line-args": "^6.0.1",
@@ -209,17 +209,17 @@
209
209
  "gray-matter": "^4.0.3",
210
210
  "joi": "^18.0.1",
211
211
  "lz-string": "^1.5.0",
212
- "n-readlines": "^1.0.1",
213
- "n3": "^1.23.1",
212
+ "n-readlines": "^1.0.3",
213
+ "n3": "^1.26.0",
214
214
  "object-hash": "^3.0.0",
215
215
  "object-sizeof": "^2.6.5",
216
- "rotating-file-stream": "^3.2.6",
216
+ "rotating-file-stream": "^3.2.8",
217
217
  "seedrandom": "^3.0.5",
218
- "semver": "^7.7.1",
218
+ "semver": "^7.7.4",
219
219
  "tar": "^7.4.3",
220
220
  "tmp": "^0.2.3",
221
221
  "ts-essentials": "^10.1.1",
222
- "tslog": "^4.9.3",
222
+ "tslog": "^4.10.2",
223
223
  "web-tree-sitter": "^0.24.7",
224
224
  "ws": "^8.18.0",
225
225
  "xpath-ts2": "^1.4.2"
@@ -24,6 +24,10 @@ export declare class FlowrDescriptionFile extends FlowrFile<DeepReadonly<DCF>> {
24
24
  * @see {@link parseDCF} for details on the parsing logic.
25
25
  */
26
26
  protected loadContent(): DCF;
27
+ /**
28
+ * Creates a FlowrDescriptionFile from given DCF content, path and optional roles.
29
+ * This is useful if you already have the DCF content parsed and want to create a description file instance without re-parsing.
30
+ */
27
31
  static fromDCF(dcf: DCF, path: string, roles?: FileRole[]): FlowrDescriptionFile;
28
32
  /**
29
33
  * Description file lifter, this does not re-create if already a description file
@@ -34,6 +34,10 @@ class FlowrDescriptionFile extends flowr_file_1.FlowrFile {
34
34
  loadContent() {
35
35
  return parseDCF(this.wrapped);
36
36
  }
37
+ /**
38
+ * Creates a FlowrDescriptionFile from given DCF content, path and optional roles.
39
+ * This is useful if you already have the DCF content parsed and want to create a description file instance without re-parsing.
40
+ */
37
41
  static fromDCF(dcf, path, roles) {
38
42
  const file = new FlowrDescriptionFile(new flowr_file_1.FlowrTextFile(path, roles));
39
43
  file.setContent(dcf);
@@ -173,7 +173,7 @@ function doesFilepathMatch(file, filter) {
173
173
  function isParameterDefaultValue(nodeId, ast) {
174
174
  let node = ast.idMap.get(nodeId);
175
175
  while (node !== undefined) {
176
- if (node.info.role === "param-value" /* RoleInParent.ParameterDefaultValue */) {
176
+ if (node.info.role === "param-v" /* RoleInParent.ParameterDefaultValue */) {
177
177
  return true;
178
178
  }
179
179
  const nip = node.info.parent;
@@ -1,6 +1,6 @@
1
1
  import Joi from 'joi';
2
2
  import type { DataFrameDomain } from '../../../abstract-interpretation/data-frame/dataframe-domain';
3
- import { StateAbstractDomain } from '../../../abstract-interpretation/domains/state-abstract-domain';
3
+ import type { StateAbstractDomain } from '../../../abstract-interpretation/domains/state-abstract-domain';
4
4
  import type { ReplOutput } from '../../../cli/repl/commands/repl-main';
5
5
  import type { FlowrConfigOptions } from '../../../config';
6
6
  import type { SingleSlicingCriterion } from '../../../slicing/criterion/parse';
@@ -5,11 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DfShapeQueryDefinition = void 0;
7
7
  const joi_1 = __importDefault(require("joi"));
8
- const state_abstract_domain_1 = require("../../../abstract-interpretation/domains/state-abstract-domain");
9
8
  const slice_query_parser_1 = require("../../../cli/repl/parser/slice-query-parser");
10
9
  const ansi_1 = require("../../../util/text/ansi");
11
10
  const time_1 = require("../../../util/text/time");
12
11
  const df_shape_query_executor_1 = require("./df-shape-query-executor");
12
+ const abstract_domain_1 = require("../../../abstract-interpretation/domains/abstract-domain");
13
13
  function dfShapeQueryLineParser(_output, line, _config) {
14
14
  const criterion = (0, slice_query_parser_1.sliceCriterionParser)(line[0]);
15
15
  return {
@@ -24,7 +24,7 @@ exports.DfShapeQueryDefinition = {
24
24
  executor: df_shape_query_executor_1.executeDfShapeQuery,
25
25
  asciiSummarizer: (formatter, _analyzer, queryResults, result) => {
26
26
  const out = queryResults;
27
- const domains = out.domains instanceof state_abstract_domain_1.StateAbstractDomain ? out.domains.value : out.domains;
27
+ const domains = out.domains instanceof abstract_domain_1.AbstractDomain ? out.domains.value : out.domains;
28
28
  result.push(`Query: ${(0, ansi_1.bold)('df-shape', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
29
29
  result.push(...domains.entries().take(20).map(([key, domain]) => {
30
30
  return ` ╰ ${key}: ${domain?.toString()}`;
@@ -36,7 +36,7 @@ exports.DfShapeQueryDefinition = {
36
36
  },
37
37
  jsonFormatter: (queryResults) => {
38
38
  const { domains, ...out } = queryResults;
39
- const state = domains instanceof state_abstract_domain_1.StateAbstractDomain ? domains.value : domains;
39
+ const state = domains instanceof abstract_domain_1.AbstractDomain ? domains.value : domains;
40
40
  const json = Object.fromEntries(state.entries().map(([key, domain]) => [key, domain?.toJson() ?? null]));
41
41
  const result = { domains: json, ...out };
42
42
  return result;
@@ -173,12 +173,12 @@ function createFoldForBinaryOp(info) {
173
173
  rhsInfo.parent = id;
174
174
  rhsInfo.index = 1;
175
175
  if (data.type === type_1.RType.Pipe) {
176
- lhsInfo.role = "pipe-lhs" /* RoleInParent.PipeLhs */;
177
- rhsInfo.role = "pipe-rhs" /* RoleInParent.PipeRhs */;
176
+ lhsInfo.role = "pipe-l" /* RoleInParent.PipeLhs */;
177
+ rhsInfo.role = "pipe-r" /* RoleInParent.PipeRhs */;
178
178
  }
179
179
  else {
180
- lhsInfo.role = "binop-lhs" /* RoleInParent.BinaryOperationLhs */;
181
- rhsInfo.role = "binop-rhs" /* RoleInParent.BinaryOperationRhs */;
180
+ lhsInfo.role = "bin-l" /* RoleInParent.BinaryOperationLhs */;
181
+ rhsInfo.role = "bin-r" /* RoleInParent.BinaryOperationRhs */;
182
182
  }
183
183
  decorated.info.file = info.file;
184
184
  return decorated;
@@ -203,7 +203,7 @@ function createFoldForAccess(info) {
203
203
  info.idMap.set(id, decorated);
204
204
  const accessedInfo = accessed.info;
205
205
  accessedInfo.parent = id;
206
- accessedInfo.role = "accessed" /* RoleInParent.Accessed */;
206
+ accessedInfo.role = "acc" /* RoleInParent.Accessed */;
207
207
  if (typeof access !== 'string') {
208
208
  let idx = 0; // the first oe will be skipped in the first iter
209
209
  for (const acc of access) {
@@ -212,7 +212,7 @@ function createFoldForAccess(info) {
212
212
  const curInfo = acc.info;
213
213
  curInfo.parent = id;
214
214
  curInfo.index = idx;
215
- curInfo.role = "index-acc" /* RoleInParent.IndexAccess */;
215
+ curInfo.role = "idx-acc" /* RoleInParent.IndexAccess */;
216
216
  }
217
217
  }
218
218
  }
@@ -235,7 +235,7 @@ function createFoldForForLoop(info) {
235
235
  const bodyInfo = body.info;
236
236
  bodyInfo.parent = id;
237
237
  bodyInfo.index = 2;
238
- bodyInfo.role = "for-body" /* RoleInParent.ForBody */;
238
+ bodyInfo.role = "for-b" /* RoleInParent.ForBody */;
239
239
  decorated.info.file = info.file;
240
240
  return decorated;
241
241
  };
@@ -247,7 +247,7 @@ function createFoldForRepeatLoop(info) {
247
247
  info.idMap.set(id, decorated);
248
248
  const bodyInfo = body.info;
249
249
  bodyInfo.parent = id;
250
- bodyInfo.role = "repeat-body" /* RoleInParent.RepeatBody */;
250
+ bodyInfo.role = "repeat-b" /* RoleInParent.RepeatBody */;
251
251
  decorated.info.file = info.file;
252
252
  return decorated;
253
253
  };
@@ -259,11 +259,11 @@ function createFoldForWhileLoop(info) {
259
259
  info.idMap.set(id, decorated);
260
260
  const condInfo = condition.info;
261
261
  condInfo.parent = id;
262
- condInfo.role = "while-cond" /* RoleInParent.WhileCondition */;
262
+ condInfo.role = "while-c" /* RoleInParent.WhileCondition */;
263
263
  const bodyInfo = body.info;
264
264
  bodyInfo.parent = id;
265
265
  bodyInfo.index = 1;
266
- bodyInfo.role = "while-body" /* RoleInParent.WhileBody */;
266
+ bodyInfo.role = "while-b" /* RoleInParent.WhileBody */;
267
267
  decorated.info.file = info.file;
268
268
  return decorated;
269
269
  };
@@ -275,7 +275,7 @@ function createFoldForIfThenElse(info) {
275
275
  info.idMap.set(id, decorated);
276
276
  const condInfo = condition.info;
277
277
  condInfo.parent = id;
278
- condInfo.role = "if-cond" /* RoleInParent.IfCondition */;
278
+ condInfo.role = "if-c" /* RoleInParent.IfCondition */;
279
279
  const thenInfo = then.info;
280
280
  thenInfo.parent = id;
281
281
  thenInfo.index = 1;
@@ -300,7 +300,13 @@ function createFoldForExprList(info) {
300
300
  const childInfo = child.info;
301
301
  childInfo.parent = id;
302
302
  childInfo.index = i++;
303
- childInfo.role = "expr-list-child" /* RoleInParent.ExpressionListChild */;
303
+ childInfo.role = "el-c" /* RoleInParent.ExpressionListChild */;
304
+ }
305
+ // assign role for grouping
306
+ if (grouping) {
307
+ const [open, close] = grouping;
308
+ open.info.role = "el-g" /* RoleInParent.ExpressionListGrouping */;
309
+ close.info.role = "el-g" /* RoleInParent.ExpressionListGrouping */;
304
310
  }
305
311
  decorated.info.file = info.file;
306
312
  return decorated;
@@ -349,7 +355,7 @@ function createFoldForFunctionDefinition(info) {
349
355
  const bodyInfo = body.info;
350
356
  bodyInfo.parent = id;
351
357
  bodyInfo.index = idx;
352
- bodyInfo.role = "fun-body" /* RoleInParent.FunctionDefinitionBody */;
358
+ bodyInfo.role = "fun-b" /* RoleInParent.FunctionDefinitionBody */;
353
359
  decorated.info.file = info.file;
354
360
  return decorated;
355
361
  };
@@ -361,12 +367,12 @@ function createFoldForFunctionParameter(info) {
361
367
  info.idMap.set(id, decorated);
362
368
  const nameInfo = name.info;
363
369
  nameInfo.parent = id;
364
- nameInfo.role = "param-name" /* RoleInParent.ParameterName */;
370
+ nameInfo.role = "param-n" /* RoleInParent.ParameterName */;
365
371
  if (defaultValue) {
366
372
  const defaultInfo = defaultValue.info;
367
373
  defaultInfo.parent = id;
368
374
  defaultInfo.index = 1;
369
- defaultInfo.role = "param-value" /* RoleInParent.ParameterDefaultValue */;
375
+ defaultInfo.role = "param-v" /* RoleInParent.ParameterDefaultValue */;
370
376
  }
371
377
  decorated.info.file = info.file;
372
378
  return decorated;
@@ -381,14 +387,14 @@ function createFoldForFunctionArgument(info) {
381
387
  if (name) {
382
388
  const nameInfo = name.info;
383
389
  nameInfo.parent = id;
384
- nameInfo.role = "arg-name" /* RoleInParent.ArgumentName */;
390
+ nameInfo.role = "arg-n" /* RoleInParent.ArgumentName */;
385
391
  idx++; // adaptive, 0 for the value if there is no name!
386
392
  }
387
393
  if (value) {
388
394
  const valueInfo = value.info;
389
395
  valueInfo.parent = id;
390
396
  valueInfo.index = idx;
391
- valueInfo.role = "arg-value" /* RoleInParent.ArgumentValue */;
397
+ valueInfo.role = "arg-v" /* RoleInParent.ArgumentValue */;
392
398
  }
393
399
  decorated.info.file = info.file;
394
400
  return decorated;
@@ -8,31 +8,32 @@ import type { AstIdMap, RNodeWithParent } from './decorate';
8
8
  export declare const enum RoleInParent {
9
9
  /** has no parent */
10
10
  Root = "root",
11
- IfCondition = "if-cond",
11
+ IfCondition = "if-c",
12
12
  IfThen = "if-then",
13
13
  IfOtherwise = "if-other",
14
- WhileCondition = "while-cond",
15
- WhileBody = "while-body",
16
- RepeatBody = "repeat-body",
14
+ WhileCondition = "while-c",
15
+ WhileBody = "while-b",
16
+ RepeatBody = "repeat-b",
17
17
  ForVariable = "for-var",
18
18
  ForVector = "for-vec",
19
- ForBody = "for-body",
19
+ ForBody = "for-b",
20
20
  FunctionCallName = "call-name",
21
21
  FunctionCallArgument = "call-arg",
22
- FunctionDefinitionBody = "fun-body",
22
+ FunctionDefinitionBody = "fun-b",
23
23
  FunctionDefinitionParameter = "param",
24
- ExpressionListChild = "expr-list-child",
25
- BinaryOperationLhs = "binop-lhs",
26
- BinaryOperationRhs = "binop-rhs",
27
- PipeLhs = "pipe-lhs",
28
- PipeRhs = "pipe-rhs",
24
+ ExpressionListChild = "el-c",
25
+ ExpressionListGrouping = "el-g",
26
+ BinaryOperationLhs = "bin-l",
27
+ BinaryOperationRhs = "bin-r",
28
+ PipeLhs = "pipe-l",
29
+ PipeRhs = "pipe-r",
29
30
  UnaryOperand = "unary-op",
30
- ParameterName = "param-name",
31
- ParameterDefaultValue = "param-value",
32
- ArgumentName = "arg-name",
33
- ArgumentValue = "arg-value",
34
- Accessed = "accessed",
35
- IndexAccess = "index-acc"
31
+ ParameterName = "param-n",
32
+ ParameterDefaultValue = "param-v",
33
+ ArgumentName = "arg-n",
34
+ ArgumentValue = "arg-v",
35
+ Accessed = "acc",
36
+ IndexAccess = "idx-acc"
36
37
  }
37
38
  /**
38
39
  * Returns the roles of the parents of the given node, starting with the parent-role of the node itself.
@@ -260,7 +260,8 @@ function convertTreeNode(node) {
260
260
  ...defaultInfo
261
261
  };
262
262
  case tree_sitter_types_1.TreeSitterType.IfStatement: {
263
- const [ifNode, /* ( */ , condition, /* ) */ , then, /* else */ , ...otherwise] = nonErrorChildren(node);
263
+ const [comments, children] = splitComments(nonErrorChildren(node));
264
+ const [ifNode, /* ( */ , condition, /* ) */ , then, /* else */ , ...otherwise] = children;
264
265
  const filteredOtherwise = otherwise.filter(n => n.type !== tree_sitter_types_1.TreeSitterType.ElseStatement);
265
266
  return {
266
267
  type: type_1.RType.IfThenElse,
@@ -269,11 +270,14 @@ function convertTreeNode(node) {
269
270
  otherwise: filteredOtherwise.length > 0 ? (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(filteredOtherwise[0])) : undefined,
270
271
  location: makeSourceRange(ifNode),
271
272
  lexeme: ifNode.text,
272
- ...defaultInfo
273
+ info: {
274
+ ...defaultInfo.info,
275
+ adToks: comments.map(c => c[1]),
276
+ }
273
277
  };
274
278
  }
275
279
  case tree_sitter_types_1.TreeSitterType.ForStatement: {
276
- const children = nonErrorChildren(node);
280
+ const [comments, children] = splitComments(nonErrorChildren(node));
277
281
  const forNode = children[0]; // we follow with a (
278
282
  const variable = getNodesUntil(children, 'in', 2); // we follow with the "in"
279
283
  const sequence = getNodesUntil(children, ')', 2 + variable.length + 1); // we follow with a (
@@ -300,31 +304,38 @@ function convertTreeNode(node) {
300
304
  lexeme: forNode.text,
301
305
  info: {
302
306
  fullRange: range,
303
- adToks: variableComments.concat(sequenceComments).map(c => c[1]),
307
+ adToks: variableComments.concat(comments, sequenceComments).map(c => c[1]),
304
308
  fullLexeme: node.text,
305
309
  tsId: node.id
306
310
  }
307
311
  };
308
312
  }
309
313
  case tree_sitter_types_1.TreeSitterType.WhileStatement: {
310
- const [whileNode, /* ( */ , condition, /* ) */ , body] = nonErrorChildren(node);
314
+ const [comments, children] = splitComments(nonErrorChildren(node));
315
+ const [whileNode, /* ( */ , condition, /* ) */ , body] = children;
311
316
  return {
312
317
  type: type_1.RType.WhileLoop,
313
318
  condition: convertTreeNode(condition),
314
319
  body: (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(body)),
315
320
  location: makeSourceRange(whileNode),
316
321
  lexeme: whileNode.text,
317
- ...defaultInfo
322
+ info: {
323
+ ...defaultInfo.info,
324
+ adToks: comments.map(c => c[1]),
325
+ }
318
326
  };
319
327
  }
320
328
  case tree_sitter_types_1.TreeSitterType.RepeatStatement: {
321
- const [repeatNode, body] = nonErrorChildren(node);
329
+ const [comments, [repeatNode, body]] = splitComments(nonErrorChildren(node));
322
330
  return {
323
331
  type: type_1.RType.RepeatLoop,
324
332
  body: (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(body)),
325
333
  location: makeSourceRange(repeatNode),
326
334
  lexeme: repeatNode.text,
327
- ...defaultInfo
335
+ info: {
336
+ ...defaultInfo.info,
337
+ adToks: comments.map(c => c[1]),
338
+ }
328
339
  };
329
340
  }
330
341
  case tree_sitter_types_1.TreeSitterType.Call: {
@@ -449,7 +460,8 @@ function convertTreeNode(node) {
449
460
  const [func, content] = nonErrorChildren(node);
450
461
  // bracket is now [ or [[ and argsClosing is x] or x]]
451
462
  const [bracket, ...argsClosing] = nonErrorChildren(content);
452
- const args = (0, arrays_1.splitArrayOn)(argsClosing.slice(0, -1), x => x.type === 'comma');
463
+ const [argsComments, argsNoComments] = splitComments(argsClosing.slice(0, -1));
464
+ const args = (0, arrays_1.splitArrayOn)(argsNoComments, x => x.type === 'comma');
453
465
  return {
454
466
  type: type_1.RType.Access,
455
467
  operator: bracket.text,
@@ -457,7 +469,10 @@ function convertTreeNode(node) {
457
469
  access: args.map(n => n.length === 0 ? r_function_call_1.EmptyArgument : convertTreeNode(n[0])),
458
470
  location: makeSourceRange(bracket),
459
471
  lexeme: bracket.text,
460
- ...defaultInfo
472
+ info: {
473
+ ...defaultInfo.info,
474
+ adToks: argsComments.map(c => c[1]),
475
+ }
461
476
  };
462
477
  }
463
478
  case tree_sitter_types_1.TreeSitterType.ExtractOperator: {
@@ -523,7 +538,7 @@ function convertTreeNode(node) {
523
538
  };
524
539
  }
525
540
  case tree_sitter_types_1.TreeSitterType.Argument: {
526
- const children = nonErrorChildren(node);
541
+ const [commentChildren, children] = splitComments(nonErrorChildren(node));
527
542
  if (children.length === 1) {
528
543
  const [arg] = children;
529
544
  return {
@@ -532,7 +547,10 @@ function convertTreeNode(node) {
532
547
  value: convertTreeNode(arg),
533
548
  location: range,
534
549
  lexeme: node.text,
535
- ...defaultInfo
550
+ info: {
551
+ ...defaultInfo.info,
552
+ adToks: commentChildren.map(c => c[1]),
553
+ }
536
554
  };
537
555
  }
538
556
  else {
@@ -558,7 +576,7 @@ function convertTreeNode(node) {
558
576
  lexeme: nameNode.text,
559
577
  info: {
560
578
  fullRange: nameRange,
561
- adToks: [],
579
+ adToks: commentChildren.map(c => c[1]),
562
580
  fullLexeme: nameNode.text,
563
581
  tsId: nameNode.id
564
582
  }
@@ -54,11 +54,11 @@ function visitAccess(info, input) {
54
54
  let acc = false;
55
55
  let idxAcc = false;
56
56
  for (const role of roles) {
57
- if (role === "accessed" /* RoleInParent.Accessed */) {
57
+ if (role === "acc" /* RoleInParent.Accessed */) {
58
58
  acc = true;
59
59
  break; // we only account for the first one
60
60
  }
61
- else if (role === "index-acc" /* RoleInParent.IndexAccess */) {
61
+ else if (role === "idx-acc" /* RoleInParent.IndexAccess */) {
62
62
  idxAcc = true;
63
63
  break;
64
64
  }