@eagleoutice/flowr 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/benchmark/slicer.d.ts +1 -0
  2. package/benchmark/slicer.js +69 -8
  3. package/benchmark/stats/print.d.ts +1 -0
  4. package/benchmark/stats/print.js +94 -31
  5. package/benchmark/stats/size-of.d.ts +3 -0
  6. package/benchmark/stats/size-of.js +68 -0
  7. package/benchmark/stats/stats.d.ts +23 -0
  8. package/benchmark/summarizer/data.d.ts +24 -1
  9. package/benchmark/summarizer/first-phase/input.d.ts +2 -2
  10. package/benchmark/summarizer/first-phase/input.js +21 -21
  11. package/benchmark/summarizer/first-phase/process.d.ts +4 -2
  12. package/benchmark/summarizer/first-phase/process.js +120 -33
  13. package/benchmark/summarizer/second-phase/graph.js +7 -0
  14. package/benchmark/summarizer/second-phase/process.js +65 -27
  15. package/benchmark/summarizer/summarizer.d.ts +1 -0
  16. package/benchmark/summarizer/summarizer.js +23 -10
  17. package/cli/repl/commands/commands.js +19 -1
  18. package/cli/slicer-app.js +1 -1
  19. package/dataflow/environments/append.js +1 -2
  20. package/dataflow/environments/built-in.js +2 -1
  21. package/dataflow/environments/clone.js +1 -1
  22. package/dataflow/environments/diff.d.ts +1 -1
  23. package/dataflow/environments/diff.js +16 -18
  24. package/dataflow/environments/environment.d.ts +6 -8
  25. package/dataflow/environments/environment.js +5 -8
  26. package/dataflow/environments/identifier.d.ts +2 -1
  27. package/dataflow/environments/overwrite.js +1 -2
  28. package/dataflow/environments/scoping.js +1 -1
  29. package/dataflow/graph/diff.js +11 -6
  30. package/dataflow/graph/edge.d.ts +2 -3
  31. package/dataflow/graph/edge.js +2 -2
  32. package/dataflow/graph/graph.d.ts +6 -2
  33. package/dataflow/graph/graph.js +16 -9
  34. package/dataflow/graph/vertex.d.ts +2 -1
  35. package/dataflow/info.d.ts +10 -1
  36. package/dataflow/info.js +54 -2
  37. package/dataflow/internal/linker.d.ts +1 -1
  38. package/dataflow/internal/linker.js +1 -2
  39. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -5
  40. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
  41. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +21 -25
  42. package/dataflow/internal/process/functions/call/built-in/built-in-get.js +6 -1
  43. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +10 -8
  44. package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.d.ts +1 -0
  45. package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.js +1 -2
  46. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +1 -1
  47. package/dataflow/internal/process/functions/call/default-call-handling.js +1 -1
  48. package/dataflow/internal/process/functions/call/unnamed-call-handling.js +1 -1
  49. package/dataflow/internal/process/process-value.js +0 -1
  50. package/dataflow/processor.d.ts +2 -3
  51. package/package.json +5 -2
  52. package/r-bridge/data/data.d.ts +1 -1
  53. package/r-bridge/data/data.js +1 -1
  54. package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +2 -2
  55. package/r-bridge/lang-4.x/ast/model/operators.js +1 -1
  56. package/r-bridge/lang-4.x/ast/model/processing/decorate.js +1 -1
  57. package/r-bridge/lang-4.x/ast/model/processing/stateful-fold.js +1 -1
  58. package/r-bridge/lang-4.x/ast/model/processing/visitor.js +2 -2
  59. package/r-bridge/lang-4.x/ast/parser/xml/internal/functions/normalize-call.js +2 -2
  60. package/r-bridge/lang-4.x/ast/parser/xml/internal/operators/normalize-binary.js +1 -1
  61. package/r-bridge/retriever.d.ts +1 -1
  62. package/r-bridge/retriever.js +3 -2
  63. package/r-bridge/shell.js +2 -1
  64. package/reconstruct/reconstruct.d.ts +3 -3
  65. package/reconstruct/reconstruct.js +40 -41
  66. package/slicing/criterion/filters/all-variables.js +1 -1
  67. package/slicing/static/static-slicer.js +2 -2
  68. package/statistics/features/common-syntax-probability.js +1 -1
  69. package/statistics/features/supported/control-flow/control-flow.js +1 -1
  70. package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
  71. package/statistics/features/supported/loops/loops.js +1 -1
  72. package/statistics/features/supported/used-functions/used-functions.js +1 -1
  73. package/util/assert.d.ts +1 -1
  74. package/util/mermaid/ast.js +4 -0
  75. package/util/mermaid/dfg.d.ts +0 -1
  76. package/util/mermaid/dfg.js +16 -13
  77. package/util/mermaid/mermaid.js +21 -1
  78. package/util/version.js +1 -1
@@ -129,31 +129,36 @@ function reconstructForLoop(loop, variable, vector, body, config) {
129
129
  ];
130
130
  }
131
131
  }
132
- function reconstructRepeatLoop(loop, body, configuration) {
133
- const sel = isSelected(configuration, loop);
134
- if (!sel) {
135
- return body;
132
+ function reconstructBodyWithHeader(header, body, onEmpty) {
133
+ if (body.length === 0) {
134
+ return [{ line: `${header.line}${onEmpty}`, indent: header.indent }];
136
135
  }
137
- else if (body.length <= 1) {
138
- // 'inline'
139
- return [{ line: `repeat ${body.length === 0 ? '{}' : body[0].line}`, indent: 0 }];
136
+ else if (body.length === 1) {
137
+ return [
138
+ { line: `${header.line} ${body[0].line}`, indent: header.indent }
139
+ ];
140
140
  }
141
141
  else if (body[0].line === '{' && body[body.length - 1].line === '}') {
142
- // 'block'
143
142
  return [
144
- { line: 'repeat {', indent: 0 },
143
+ { line: `${header.line} {`, indent: header.indent },
145
144
  ...body.slice(1, body.length - 1),
146
- { line: '}', indent: 0 }
145
+ { line: '}', indent: header.indent }
147
146
  ];
148
147
  }
149
148
  else {
150
- // unknown
151
149
  return [
152
- { line: 'repeat', indent: 0 },
150
+ header,
153
151
  ...indentBy(body, 1)
154
152
  ];
155
153
  }
156
154
  }
155
+ function reconstructRepeatLoop(loop, body, configuration) {
156
+ const sel = isSelected(configuration, loop);
157
+ if (!sel) {
158
+ return body;
159
+ }
160
+ return reconstructBodyWithHeader({ line: 'repeat', indent: 0 }, body, '{}');
161
+ }
157
162
  function reconstructIfThenElse(ifThenElse, condition, then, otherwise, config) {
158
163
  otherwise ??= [];
159
164
  if (then.length === 0 && otherwise.length === 0) {
@@ -169,10 +174,7 @@ function reconstructIfThenElse(ifThenElse, condition, then, otherwise, config) {
169
174
  }
170
175
  else if (otherwise.length === 0) {
171
176
  if (isSelected(config, ifThenElse)) {
172
- return [
173
- { line: `if(${getLexeme(ifThenElse.condition)}) ${then[0].line}`, indent: 0 },
174
- ...indentBy(then.splice(1), 1)
175
- ];
177
+ return reconstructBodyWithHeader({ line: `if(${getLexeme(ifThenElse.condition)})`, indent: 0 }, then, '{}');
176
178
  }
177
179
  else {
178
180
  return then;
@@ -180,10 +182,7 @@ function reconstructIfThenElse(ifThenElse, condition, then, otherwise, config) {
180
182
  }
181
183
  else if (then.length === 0) {
182
184
  if (isSelected(config, ifThenElse)) {
183
- return [
184
- { line: `if(${getLexeme(ifThenElse.condition)}) { } else ${otherwise[0].line}`, indent: 0 },
185
- ...indentBy(otherwise.splice(1), 1)
186
- ];
185
+ return reconstructBodyWithHeader({ line: `if(${getLexeme(ifThenElse.condition)}) { } else`, indent: 0 }, then, '{}');
187
186
  }
188
187
  else {
189
188
  return otherwise;
@@ -283,7 +282,7 @@ function reconstructFunctionDefinition(definition, functionParameters, body, con
283
282
  const empty = body === undefined || body.length === 0;
284
283
  const selected = isSelected(config, definition);
285
284
  if (empty && selected) { // give function stub
286
- return plain(`function(${reconstructParameters(definition.parameters).join(', ')}) { }`);
285
+ return plain(`${definition.lexeme}(${reconstructParameters(definition.parameters).join(', ')}) { }`);
287
286
  }
288
287
  else if (!selected) { // do not require function
289
288
  return body;
@@ -294,25 +293,25 @@ function reconstructFunctionDefinition(definition, functionParameters, body, con
294
293
  // 'inline'
295
294
  const bodyStr = body.length === 0 ? '{ }' : `${body[0].line}`;
296
295
  // we keep the braces in every case because I do not like no-brace functions
297
- return [{ line: `function(${parameters}) ${bodyStr}`, indent: 0 }];
296
+ return [{ line: `${definition.lexeme}(${parameters}) ${bodyStr}`, indent: 0 }];
298
297
  }
299
298
  else {
300
299
  // 'block'
301
300
  return [
302
- { line: `function(${parameters}) ${body[0].line}`, indent: 0 },
301
+ { line: `${definition.lexeme}(${parameters}) ${body[0].line}`, indent: 0 },
303
302
  ...body.slice(1),
304
303
  ];
305
304
  }
306
305
  }
307
306
  function reconstructSpecialInfixFunctionCall(args, call) {
308
307
  (0, assert_1.guard)(args.length === 2, () => `infix special call must have exactly two arguments, got: ${args.length} (${JSON.stringify(args)})`);
309
- (0, assert_1.guard)(call.flavor === 'named', `infix special call must be named, got: ${call.flavor}`);
308
+ (0, assert_1.guard)(call.named, `infix special call must be named, got: ${call.named}`);
310
309
  const [lhs, rhs] = args;
311
310
  if ((lhs === undefined || lhs.length === 0) && (rhs === undefined || rhs.length === 0)) {
312
311
  return [];
313
312
  }
314
313
  // else if (rhs === undefined || rhs.length === 0) {
315
- // if rhs is undefined we still have to keep both now, but reconstruct manually :/
314
+ // if rhs is undefined we still have to keep both now, but reconstruct manually :/
316
315
  if (lhs !== r_function_call_1.EmptyArgument && lhs.length > 0) {
317
316
  const lhsText = lhs.map(l => `${getIndentString(l.indent)}${l.line}`).join('\n');
318
317
  if (rhs !== r_function_call_1.EmptyArgument && rhs.length > 0) {
@@ -339,7 +338,7 @@ function reconstructFunctionCall(call, functionName, args, configuration) {
339
338
  if (call.infixSpecial === true) {
340
339
  return reconstructSpecialInfixFunctionCall(args, call);
341
340
  }
342
- if (call.flavor === 'named' && selected) {
341
+ if (call.named && selected) {
343
342
  return plain(getLexeme(call));
344
343
  }
345
344
  const filteredArgs = args.filter(a => a !== undefined && a.length > 0);
@@ -349,14 +348,12 @@ function reconstructFunctionCall(call, functionName, args, configuration) {
349
348
  if (args.length === 0) {
350
349
  (0, assert_1.guard)(functionName.length > 0, `without args, we need the function name to be present! got: ${JSON.stringify(functionName)}`);
351
350
  const last = functionName[functionName.length - 1];
352
- if (call.flavor === 'unnamed' && !last.line.endsWith(')')) {
351
+ if (!call.named && !last.line.endsWith(')')) {
353
352
  functionName[0].line = `(${functionName[0].line}`;
354
353
  last.line += ')';
355
354
  }
356
- if (!last.line.endsWith('()')) {
357
- // add empty call braces if not present
358
- last.line += '()';
359
- }
355
+ // add empty call braces if not present
356
+ last.line += '()';
360
357
  return functionName;
361
358
  }
362
359
  else {
@@ -369,7 +366,7 @@ function doNotAutoSelect(_node) {
369
366
  exports.doNotAutoSelect = doNotAutoSelect;
370
367
  const libraryFunctionCall = /^(library|require|((require|load|attach)Namespace))$/;
371
368
  function autoSelectLibrary(node) {
372
- if (node.type !== "RFunctionCall" /* RType.FunctionCall */ || node.flavor !== 'named') {
369
+ if (node.type !== "RFunctionCall" /* RType.FunctionCall */ || !node.named) {
373
370
  return false;
374
371
  }
375
372
  return libraryFunctionCall.test(node.functionName.content);
@@ -416,13 +413,13 @@ function getIndentString(indent) {
416
413
  function prettyPrintCodeToString(code, lf = '\n') {
417
414
  return code.map(({ line, indent }) => `${getIndentString(indent)}${line}`).join(lf);
418
415
  }
419
- function removeOuterExpressionListIfApplicable(result, autoSelected) {
416
+ function removeOuterExpressionListIfApplicable(result, linesWithAutoSelected) {
420
417
  if (result.length > 1 && result[0].line === '{' && result[result.length - 1].line === '}') {
421
418
  // remove outer block
422
- return { code: prettyPrintCodeToString(indentBy(result.slice(1, result.length - 1), -1)), autoSelected };
419
+ return { code: prettyPrintCodeToString(indentBy(result.slice(1, result.length - 1), -1)), linesWithAutoSelected };
423
420
  }
424
421
  else {
425
- return { code: prettyPrintCodeToString(result), autoSelected };
422
+ return { code: prettyPrintCodeToString(result), linesWithAutoSelected };
426
423
  }
427
424
  }
428
425
  /**
@@ -432,25 +429,27 @@ function removeOuterExpressionListIfApplicable(result, autoSelected) {
432
429
  * @param selection - The selection of nodes to be reconstructed (probably the {@link NodeId|NodeIds} identified by the slicer)
433
430
  * @param autoSelectIf - A predicate that can be used to force the reconstruction of a node (for example to reconstruct library call statements, see {@link autoSelectLibrary}, {@link doNotAutoSelect})
434
431
  *
435
- * @returns The number of times `autoSelectIf` triggered, as well as the reconstructed code itself.
432
+ * @returns The number of lines for which `autoSelectIf` triggered, as well as the reconstructed code itself.
436
433
  */
437
434
  function reconstructToCode(ast, selection, autoSelectIf = autoSelectLibrary) {
438
435
  if (exports.reconstructLogger.settings.minLevel <= 1 /* LogLevel.Trace */) {
439
436
  exports.reconstructLogger.trace(`reconstruct ast with ids: ${JSON.stringify([...selection])}`);
440
437
  }
441
- // we use a wrapper to count the number of times the autoSelectIf predicate triggered
442
- let autoSelected = 0;
438
+ // we use a wrapper to count the number of lines for which the autoSelectIf predicate triggered
439
+ const linesWithAutoSelected = new Set();
443
440
  const autoSelectIfWrapper = (node) => {
444
441
  const result = autoSelectIf(node);
445
- if (result) {
446
- autoSelected++;
442
+ if (result && node.location) {
443
+ for (let i = node.location[0]; i <= node.location[2]; i++) {
444
+ linesWithAutoSelected.add(i);
445
+ }
447
446
  }
448
447
  return result;
449
448
  };
450
449
  // fold of the normalized ast
451
450
  const result = (0, stateful_fold_1.foldAstStateful)(ast.ast, { selection, autoSelectIf: autoSelectIfWrapper }, reconstructAstFolds);
452
451
  (0, log_1.expensiveTrace)(exports.reconstructLogger, () => `reconstructed ast before string conversion: ${JSON.stringify(result)}`);
453
- return removeOuterExpressionListIfApplicable(result, autoSelected);
452
+ return removeOuterExpressionListIfApplicable(result, linesWithAutoSelected.size);
454
453
  }
455
454
  exports.reconstructToCode = reconstructToCode;
456
455
  //# sourceMappingURL=reconstruct.js.map
@@ -38,7 +38,7 @@ const defaultAllVariablesCollectorFolds = {
38
38
  foldFunctionDefinition: (_, a, b) => [...a.flat(), ...b],
39
39
  foldFunctionCall: (c, a, b) => {
40
40
  const args = b.flatMap(b => b !== r_function_call_1.EmptyArgument ? b.flat() : []);
41
- if (c.flavor === 'named') {
41
+ if (c.named) {
42
42
  return c.functionName.content === 'library' ? args.slice(1) : args;
43
43
  }
44
44
  else {
@@ -48,8 +48,8 @@ function staticSlicing(graph, ast, criteria, threshold = 75) {
48
48
  if (currentVertex.controlDependencies) {
49
49
  const topLevel = graph.isRoot(id) || sliceSeedIds.has(id);
50
50
  for (const cd of currentVertex.controlDependencies) {
51
- if (!topLevel || (ast.idMap.get(cd)?.info.depth ?? 0) <= minDepth) {
52
- queue.add(cd, baseEnvironment, baseEnvFingerprint, false);
51
+ if (!topLevel || (ast.idMap.get(cd.id)?.info.depth ?? 0) <= minDepth) {
52
+ queue.add(cd.id, baseEnvironment, baseEnvFingerprint, false);
53
53
  }
54
54
  }
55
55
  }
@@ -79,7 +79,7 @@ function updateCommonSyntaxTypeCounts(current, ...nodes) {
79
79
  }
80
80
  break;
81
81
  case "RFunctionCall" /* RType.FunctionCall */:
82
- if (node.flavor === 'unnamed') {
82
+ if (!node.named) {
83
83
  current.unnamedCall++;
84
84
  }
85
85
  else {
@@ -21,7 +21,7 @@ function visitIfThenElse(info, input) {
21
21
  const ifThenElseStack = [];
22
22
  (0, visitor_1.visitAst)(input.normalizedRAst.ast, node => {
23
23
  if (node.type !== "RIfThenElse" /* RType.IfThenElse */) {
24
- if (node.type === "RFunctionCall" /* RType.FunctionCall */ && node.flavor === 'named' && node.functionName.content === 'switch') {
24
+ if (node.type === "RFunctionCall" /* RType.FunctionCall */ && node.named && node.functionName.content === 'switch') {
25
25
  const initialArg = (0, unpack_argument_1.unpackArgument)(node.arguments[0]);
26
26
  if (initialArg) {
27
27
  info.switchCase = (0, common_syntax_probability_1.updateCommonSyntaxTypeCounts)(info.switchCase, initialArg);
@@ -100,7 +100,7 @@ function visitDefinitions(info, input) {
100
100
  // track all calls with the same name that do not already have a bound calls edge, superfluous if recursive tracking is explicit
101
101
  const recursiveCalls = [];
102
102
  (0, visitor_1.visitAst)(node.body, n => {
103
- if (n.type === "RFunctionCall" /* RType.FunctionCall */ && n.flavor === 'named' && assigned.has(n.functionName.lexeme)) {
103
+ if (n.type === "RFunctionCall" /* RType.FunctionCall */ && n.named && assigned.has(n.functionName.lexeme)) {
104
104
  recursiveCalls.push(n);
105
105
  }
106
106
  });
@@ -33,7 +33,7 @@ function visitLoops(info, input) {
33
33
  info.breakStatements++;
34
34
  return;
35
35
  case "RFunctionCall" /* RType.FunctionCall */:
36
- if (node.flavor === 'named' && isImplicitLoop.test(node.functionName.lexeme)) {
36
+ if (node.named && isImplicitLoop.test(node.functionName.lexeme)) {
37
37
  info.implicitLoops++;
38
38
  (0, statistics_file_1.appendStatisticsFile)(exports.loops.name, 'implicit-loop', [node.functionName.info.fullLexeme ?? node.functionName.lexeme], input.filepath);
39
39
  }
@@ -63,7 +63,7 @@ function visitCalls(info, input) {
63
63
  if (dataflowNode) {
64
64
  hasCallsEdge = [...dataflowNode[1].values()].some(e => (0, edge_1.edgeIncludesType)(e.types, 4 /* EdgeType.Calls */));
65
65
  }
66
- if (node.flavor === 'unnamed') {
66
+ if (!node.named) {
67
67
  info.unnamedCalls++;
68
68
  (0, statistics_file_1.appendStatisticsFile)(exports.usedFunctions.name, 'unnamed-calls', [node.lexeme], input.filepath);
69
69
  allCalls.push([
package/util/assert.d.ts CHANGED
@@ -9,4 +9,4 @@ export type GuardMessage = string | (() => string);
9
9
  * @param message - if a string, will be used as error message, if a function, will be called to produce the error message (can be used to avoid costly message generations)
10
10
  * @throws GuardError - if the assertion fails
11
11
  */
12
- export declare function guard(assertion: boolean, message?: GuardMessage): asserts assertion;
12
+ export declare function guard(assertion: boolean | undefined, message?: GuardMessage): asserts assertion;
@@ -13,6 +13,10 @@ function normalizedAstToMermaid(ast, prefix = '') {
13
13
  const roleSuffix = context.role === "expr-list-child" /* RoleInParent.ExpressionListChild */ || context.role === "call-argument" /* RoleInParent.FunctionCallArgument */ || context.role === "function-def-param" /* RoleInParent.FunctionDefinitionParameter */ ? `-${context.index}` : '';
14
14
  output += ` n${n.info.parent} -->|"${context.role}${roleSuffix}"| n${n.info.id}\n`;
15
15
  }
16
+ if (n.type === "RExpressionList" /* RType.ExpressionList */ && n.grouping !== undefined) {
17
+ output += ` n${n.info.id} -.-|"group-open"| n${n.grouping[0].info.id}\n`;
18
+ output += ` n${n.info.id} -.-|"group-close"| n${n.grouping[1].info.id}\n`;
19
+ }
16
20
  return false;
17
21
  });
18
22
  return output;
@@ -7,7 +7,6 @@ type Mark = MarkVertex | MarkEdge;
7
7
  interface MermaidGraph {
8
8
  nodeLines: string[];
9
9
  edgeLines: string[];
10
- hasBuiltIn: boolean;
11
10
  includeEnvironments: boolean;
12
11
  mark: ReadonlySet<Mark> | undefined;
13
12
  /** in the form of from-\>to because I am lazy, see {@link encodeEdge} */
@@ -7,11 +7,18 @@ const graph_1 = require("../../dataflow/graph/graph");
7
7
  const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
8
8
  const edge_1 = require("../../dataflow/graph/edge");
9
9
  const environment_1 = require("../../dataflow/environments/environment");
10
- const built_in_1 = require("../../dataflow/environments/built-in");
11
10
  function formatRange(range) {
12
11
  if (range === undefined) {
13
12
  return '??-??';
14
13
  }
14
+ else if (range[0] === range[2]) {
15
+ if (range[1] === range[3]) {
16
+ return `${range[0]}.${range[1]}`;
17
+ }
18
+ else {
19
+ return `${range[0]}.${range[1]}-${range[3]}`;
20
+ }
21
+ }
15
22
  return `${range[0]}.${range[1]}-${range[2]}.${range[3]}`;
16
23
  }
17
24
  exports.formatRange = formatRange;
@@ -102,7 +109,7 @@ function printEnvironmentToLines(env) {
102
109
  else if (env.id === environment_1.BuiltInEnvironment.id) {
103
110
  return ['Built-in'];
104
111
  }
105
- const lines = [...printEnvironmentToLines(env.parent), `${env.id}--${env.name}${'-'.repeat(40)}`];
112
+ const lines = [...printEnvironmentToLines(env.parent), `${env.id}${'-'.repeat(40)}`];
106
113
  const longestName = Math.max(...[...env.memory.keys()].map(x => x.length));
107
114
  for (const [name, defs] of env.memory.entries()) {
108
115
  const printName = `${name}:`;
@@ -119,15 +126,17 @@ function vertexToMermaid(info, mermaid, id, idPrefix, mark) {
119
126
  }
120
127
  }
121
128
  const node = mermaid.rootGraph.idMap?.get(info.id);
122
- const escapedName = (0, mermaid_1.escapeMarkdown)(node ? `[${node.type}] ${node.lexeme ?? '??'}` : '??');
129
+ const lexeme = node?.lexeme ?? (node?.type === "RExpressionList" /* RType.ExpressionList */ ? node?.grouping?.[0]?.lexeme : '') ?? '??';
130
+ const escapedName = (0, mermaid_1.escapeMarkdown)(node ? `[${node.type}] ${lexeme}` : '??');
123
131
  const deps = info.controlDependencies ? ', :maybe:' + info.controlDependencies.join(',') : '';
124
- mermaid.nodeLines.push(` ${idPrefix}${id}${open}"\`${escapedName}${escapedName.length > 10 ? '\n ' : ' '}(${id}${deps})\n *${formatRange(mermaid.rootGraph.idMap?.get(id)?.location)}*${fCall ? displayFunctionArgMapping(info.args) : ''}\`"${close}`);
132
+ const n = node?.info.fullRange ?? node?.location ?? (node?.type === "RExpressionList" /* RType.ExpressionList */ ? node?.grouping?.[0].location : undefined);
133
+ mermaid.nodeLines.push(` ${idPrefix}${id}${open}"\`${escapedName}${escapedName.length > 10 ? '\n ' : ' '}(${id}${deps})\n *${formatRange(n)}*${fCall ? displayFunctionArgMapping(info.args) : ''}\`"${close}`);
125
134
  if (mark?.has(id)) {
126
135
  mermaid.nodeLines.push(` style ${idPrefix}${id} stroke:black,stroke-width:7px; `);
127
136
  }
128
137
  const edges = mermaid.rootGraph.get(id, true);
129
138
  (0, assert_1.guard)(edges !== undefined, `node ${id} must be found`);
130
- const artificialCdEdges = (info.controlDependencies ?? []).map(x => [x, { types: new Set(['CD']) }]);
139
+ const artificialCdEdges = (info.controlDependencies ?? []).map(x => [x.id, { types: new Set([x.when ? 'CD-True' : 'CD-False']) }]);
131
140
  for (const [target, edge] of [...edges[1], ...artificialCdEdges]) {
132
141
  const edgeTypes = typeof edge.types == 'number' ? new Set((0, edge_1.splitEdgeTypes)(edge.types)) : edge.types;
133
142
  const edgeId = encodeEdge(idPrefix + id, idPrefix + target, edgeTypes);
@@ -138,12 +147,9 @@ function vertexToMermaid(info, mermaid, id, idPrefix, mark) {
138
147
  // who invented this syntax?!
139
148
  mermaid.edgeLines.push(` linkStyle ${mermaid.presentEdges.size - 1} stroke:red,color:red,stroke-width:4px;`);
140
149
  }
141
- if (edgeTypes.has('CD')) {
150
+ if (edgeTypes.has('CD-True')) {
142
151
  mermaid.edgeLines.push(` linkStyle ${mermaid.presentEdges.size - 1} stroke:gray,color:gray;`);
143
152
  }
144
- if (target === built_in_1.BuiltIn) {
145
- mermaid.hasBuiltIn = true;
146
- }
147
153
  }
148
154
  }
149
155
  if (info.tag === 'function-definition') {
@@ -152,15 +158,12 @@ function vertexToMermaid(info, mermaid, id, idPrefix, mark) {
152
158
  }
153
159
  // make the passing of root ids more performant again
154
160
  function graphToMermaidGraph(rootIds, { graph, prefix = 'flowchart TD', idPrefix = '', includeEnvironments = true, mark, rootGraph, presentEdges = new Set() }) {
155
- const mermaid = { nodeLines: prefix === null ? [] : [prefix], edgeLines: [], presentEdges, hasBuiltIn: false, mark, rootGraph: rootGraph ?? graph, includeEnvironments };
161
+ const mermaid = { nodeLines: prefix === null ? [] : [prefix], edgeLines: [], presentEdges, mark, rootGraph: rootGraph ?? graph, includeEnvironments };
156
162
  for (const [id, info] of graph.vertices(true)) {
157
163
  if (rootIds.has(id)) {
158
164
  vertexToMermaid(info, mermaid, id, idPrefix, mark);
159
165
  }
160
166
  }
161
- if (mermaid.hasBuiltIn) {
162
- mermaid.nodeLines.push(` ${idPrefix}${built_in_1.BuiltIn}["Built-in"]`);
163
- }
164
167
  return mermaid;
165
168
  }
166
169
  function graphToMermaid(config) {
@@ -1,8 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.mermaidCodeToUrl = exports.escapeMarkdown = void 0;
4
+ const replacements = {
5
+ // keep newlines
6
+ '\\n': '\n',
7
+ '`': '#96;',
8
+ '[': '#91;',
9
+ ']': '#93;',
10
+ '<': '#60;',
11
+ '>': '#62;',
12
+ '*': '#42;',
13
+ '+': '#43;',
14
+ '-': '#45;',
15
+ '"': '#34;',
16
+ '\\': '#92;',
17
+ '_': '#95;',
18
+ '{': '#123;',
19
+ '}': '#125;'
20
+ };
4
21
  function escapeMarkdown(text) {
5
- return text.replaceAll(/([+*<>-])/g, '\\$1').replaceAll('"', '\'\'');
22
+ for (const [key, value] of Object.entries(replacements)) {
23
+ text = text.replaceAll(key, value);
24
+ }
25
+ return text;
6
26
  }
7
27
  exports.escapeMarkdown = escapeMarkdown;
8
28
  /**
package/util/version.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.flowrVersion = void 0;
4
4
  const semver_1 = require("semver");
5
5
  // this is automatically replaced with the current version by release-it
6
- const version = '2.0.1';
6
+ const version = '2.0.3';
7
7
  function flowrVersion() {
8
8
  return new semver_1.SemVer(version);
9
9
  }