Skip to content

Commit 9ad54b3

Browse files
committed
Merge branch 'main' of github.com:flowr-analysis/flowr
2 parents a4605f1 + e0d2d27 commit 9ad54b3

File tree

18 files changed

+782
-465
lines changed

18 files changed

+782
-465
lines changed

.github/workflows/broken-links-and-wiki.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ jobs:
4949
# just as a time-proven safety measure
5050
run: git lfs checkout
5151

52+
- name: "⚗️ Test Suite (full, for data)"
53+
run: bash .github/workflows/scripts/run-flowr-command.sh "test-full -- --allowOnly=false"
54+
5255
- name: "🛠️ Update the Generated Wiki Pages"
5356
run: |
5457
npm ci

src/documentation/doc-util/doc-dfg.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { RShell } from '../../r-bridge/shell';
33
import type { MermaidMarkdownMark } from '../../util/mermaid/dfg';
44
import { graphToMermaid } from '../../util/mermaid/dfg';
55
import { PipelineExecutor } from '../../core/pipeline-executor';
6-
import { DEFAULT_DATAFLOW_PIPELINE } from '../../core/steps/pipeline/default-pipelines';
6+
import { createDataflowPipeline, DEFAULT_DATAFLOW_PIPELINE } from '../../core/steps/pipeline/default-pipelines';
77
import { requestFromInput } from '../../r-bridge/retriever';
88
import { deterministicCountingIdGenerator } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
99
import { resolveDataflowGraph } from '../../dataflow/graph/resolve-graph';
@@ -12,14 +12,17 @@ import { diffOfDataflowGraphs } from '../../dataflow/graph/diff';
1212
import { guard } from '../../util/assert';
1313
import type { PipelineOutput } from '../../core/steps/pipeline/pipeline';
1414
import { printAsMs } from '../../util/time';
15+
import type { KnownParser } from '../../r-bridge/parser';
16+
import { FlowrWikiBaseRef } from './doc-files';
1517

16-
export function printDfGraph(graph: DataflowGraph, mark?: ReadonlySet<MermaidMarkdownMark>) {
18+
export function printDfGraph(graph: DataflowGraph, mark?: ReadonlySet<MermaidMarkdownMark>, simplified = false) {
1719
return `
1820
\`\`\`mermaid
1921
${graphToMermaid({
2022
graph,
2123
prefix: 'flowchart LR',
22-
mark
24+
mark,
25+
simplified
2326
}).string}
2427
\`\`\`
2528
`;
@@ -31,6 +34,7 @@ export interface PrintDataflowGraphOptions {
3134
readonly codeOpen?: boolean;
3235
readonly exposeResult?: boolean;
3336
readonly switchCodeAndGraph?: boolean;
37+
readonly simplified?: boolean;
3438
}
3539

3640
export function formatSideEffect(ef: UnknownSidEffect): string {
@@ -41,12 +45,11 @@ export function formatSideEffect(ef: UnknownSidEffect): string {
4145
}
4246
}
4347

44-
export async function printDfGraphForCode(shell: RShell, code: string, options: PrintDataflowGraphOptions & { exposeResult: true }): Promise<[string, PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>]>;
45-
export async function printDfGraphForCode(shell: RShell, code: string, options?: PrintDataflowGraphOptions & { exposeResult?: false | undefined }): Promise<string>;
46-
export async function printDfGraphForCode(shell: RShell, code: string, { mark, showCode = true, codeOpen = false, exposeResult, switchCodeAndGraph = false }: PrintDataflowGraphOptions = {}): Promise<string | [string, PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>]> {
48+
export async function printDfGraphForCode(parser: KnownParser, code: string, options: PrintDataflowGraphOptions & { exposeResult: true }): Promise<[string, PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>]>;
49+
export async function printDfGraphForCode(parser: KnownParser, code: string, options?: PrintDataflowGraphOptions & { exposeResult?: false | undefined }): Promise<string>;
50+
export async function printDfGraphForCode(parser: KnownParser, code: string, { simplified = false, mark, showCode = true, codeOpen = false, exposeResult, switchCodeAndGraph = false }: PrintDataflowGraphOptions = {}): Promise<string | [string, PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>]> {
4751
const now = performance.now();
48-
const result = await new PipelineExecutor(DEFAULT_DATAFLOW_PIPELINE, {
49-
parser: shell,
52+
const result = await createDataflowPipeline(parser, {
5053
request: requestFromInput(code)
5154
}).allRemainingSteps();
5255
const duration = performance.now() - now;
@@ -55,8 +58,9 @@ export async function printDfGraphForCode(shell: RShell, code: string, { mark, s
5558
guard(showCode, 'can not switch code and graph if code is not shown');
5659
}
5760

58-
const metaInfo = `The analysis required _${printAsMs(duration)}_ (including parse and normalize) within the generation environment.`;
59-
const dfGraph = printDfGraph(result.dataflow.graph, mark);
61+
const metaInfo = `The analysis required _${printAsMs(duration)}_ (including parse and normalize, using the [${parser.name}](${FlowrWikiBaseRef}/Engines) engine) within the generation environment.`;
62+
const dfGraph = printDfGraph(result.dataflow.graph, mark, simplified);
63+
const simplyText = simplified ? '(simplified) ' : '';
6064
let resultText = '\n\n';
6165

6266
if(showCode) {
@@ -67,7 +71,7 @@ ${code}
6771
resultText += `
6872
<details${codeOpen ? ' open' : ''}>
6973
70-
<summary style="color:gray">${switchCodeAndGraph ? 'Dataflow Graph of the R Code' : 'R Code of the Dataflow Graph'}</summary>
74+
<summary style="color:gray">${switchCodeAndGraph ? `${simplyText}Dataflow Graph of the R Code` : `R Code of the ${simplyText}Dataflow Graph`}</summary>
7175
7276
${metaInfo} ${mark ? `The following marks are used in the graph to highlight sub-parts (uses ids): {${[...mark].join(', ')}}.` : ''}
7377
We encountered ${result.dataflow.graph.unknownSideEffects.size > 0 ? 'unknown side effects (with ids: ' + [...result.dataflow.graph.unknownSideEffects].map(formatSideEffect).join(', ') + ')' : 'no unknown side effects'} during the analysis.
Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,82 @@
11
import type { FlowrCapability } from '../r-bridge/data/types';
22
import { flowrCapabilities } from '../r-bridge/data/data';
3-
import { flowrVersion } from '../util/version';
43
import { setMinLevelOfAllLogs } from '../../test/functionality/_helper/log';
54
import { LogLevel } from '../util/log';
5+
import { autoGenHeader } from './doc-util/doc-auto-gen';
6+
import { joinWithLast } from '../util/strings';
7+
import { block } from './doc-util/doc-structure';
8+
import { prefixLines } from './doc-util/doc-general';
9+
import { TreeSitterExecutor } from '../r-bridge/lang-4.x/tree-sitter/tree-sitter-executor';
10+
import type { KnownParser } from '../r-bridge/parser';
11+
import fs from 'fs';
12+
import type { SerializedTestLabel, TestLabel } from '../../test/functionality/_helper/label';
13+
14+
const detailedInfoFile = 'coverage/flowr-test-details.json';
15+
16+
interface CapabilityInformation {
17+
readonly parser: KnownParser;
18+
readonly info: Map<string, TestLabel[]> | undefined
19+
}
20+
21+
22+
function obtainDetailedInfos(): Map<string, TestLabel[]> | undefined {
23+
if(!fs.existsSync(detailedInfoFile)) {
24+
return undefined;
25+
}
26+
const content = fs.readFileSync(detailedInfoFile).toString();
27+
const base = JSON.parse(content) as [string, SerializedTestLabel[]][];
28+
const out = new Map<string, TestLabel[]>();
29+
for(const [key, values] of base) {
30+
out.set(key, values.map(v => ({
31+
id: v.id,
32+
name: v.name,
33+
capabilities: new Set(v.capabilities),
34+
context: new Set(v.context)
35+
} satisfies TestLabel)));
36+
}
37+
return out;
38+
}
639

740
const supportedSymbolMap: Map<string, string> = new Map([
8-
['not', ':red_circle:' ],
9-
['partially', ':large_orange_diamond:'],
10-
['fully', ':green_square:' ]
41+
['not', '🔴' ],
42+
['partially', '🔶' ],
43+
['fully', '🟩' ]
1144
]);
1245

13-
function printSingleCapability(depth: number, index: number, capability: FlowrCapability) {
46+
function getTestDetails(info: CapabilityInformation, capability: FlowrCapability) {
47+
if(!info.info) {
48+
return '';
49+
}
50+
const totalTests = info.info.get(capability.id);
51+
if(!totalTests || totalTests.length === 0) {
52+
return '';
53+
}
54+
const grouped = new Map<string, number>();
55+
for(const { context } of totalTests) {
56+
for(const c of context) {
57+
grouped.set(c, (grouped.get(c) ?? 0) + 1);
58+
}
59+
}
60+
if(grouped.get('desugar-tree-sitter') !== undefined && grouped.get('desugar-tree-sitter') === grouped.get('desugar-shell')) {
61+
grouped.set('desugar', (grouped.get('desugar-tree-sitter') ?? 0) * 2);
62+
grouped.delete('desugar-shell');
63+
grouped.delete('desugar-tree-sitter');
64+
}
65+
grouped.delete('other'); // opinionated view on the categories
66+
const testString: string[] = [`${totalTests.length} test${totalTests.length !== 1 ? 's' : ''}`];
67+
// sort by count
68+
const sorted = [...grouped.entries()].sort((a, b) => b[1] - a[1]);
69+
for(const [context, count] of sorted) {
70+
testString.push(`${context}: ${count}`);
71+
}
72+
return ` $\\color{gray}{\\textsf{(${testString.join(', ')})}}$`;
73+
}
74+
75+
async function printSingleCapability(info: CapabilityInformation, depth: number, index: number, capability: FlowrCapability): Promise<string> {
1476
const indent = ' '.repeat(depth);
1577
const indexStr = index.toString().padStart(2, ' ');
1678
const nextLineIndent = ' '.repeat(depth + indexStr.length);
17-
const mainLine = `${indent}${indexStr}. **${capability.name}** (<a id='${capability.id}'>\`${capability.id}\`</a>)`;
79+
const mainLine = `${indent}${indexStr}. <a id='${capability.id}'></a>**${capability.name}** <a href="#${capability.id}">🔗</a>${getTestDetails(info, capability)}`;
1880
let nextLine = '';
1981

2082
if(capability.supported) {
@@ -23,45 +85,63 @@ function printSingleCapability(depth: number, index: number, capability: FlowrCa
2385
if(capability.description) {
2486
nextLine += capability.description;
2587
}
26-
if(capability.note) {
27-
nextLine += `\\\n${nextLineIndent}_${capability.note}_`;
88+
if(capability.url) {
89+
nextLine += '\\\nSee ' + joinWithLast(capability.url.map(({ name, href }) => `[${name}](${href})`)) + ' for more info.';
90+
}
91+
nextLine += ' Internal ID: `' + capability.id + '`';
92+
if(capability.example) {
93+
nextLine += `\n${nextLineIndent}${prefixLines(block({
94+
type: 'INFO',
95+
content: typeof capability.example === 'string' ? capability.example : await capability.example(info.parser)
96+
}), nextLineIndent)}`;
2897
}
2998
return nextLine ? `${mainLine}\\\n${nextLineIndent}${nextLine}` : mainLine;
3099
}
31100

32-
function printAsMarkdown(capabilities: readonly FlowrCapability[], depth = 0, lines: string[] = []): string {
101+
async function printAsMarkdown(info: CapabilityInformation, capabilities: readonly FlowrCapability[], depth = 0, lines: string[] = []): Promise<string> {
33102
for(let i = 0; i < capabilities.length; i++) {
34103
const capability = capabilities[i];
35-
const result = printSingleCapability(depth, i + 1, capability);
104+
const result = await printSingleCapability(info, depth, i + 1, capability);
36105
lines.push(result);
37106
if(capability.capabilities) {
38-
printAsMarkdown(capability.capabilities, depth + 1, lines);
107+
await printAsMarkdown(info, capability.capabilities, depth + 1, lines);
39108
}
40109
}
41110
return lines.join('\n');
42111
}
43112

44113
function getPreamble(): string {
45-
const currentDateAndTime = new Date().toISOString().replace('T', ', ').replace(/\.\d+Z$/, ' UTC');
46-
const shortenFilename = module.filename.replace(/^.*src\//, 'src/');
47-
return `_This document was generated from '${shortenFilename}' on ${currentDateAndTime} summarizing flowR's current capabilities (v${flowrVersion().format()})._
114+
return `${autoGenHeader({ filename: module.filename, purpose: 'current capabilities' })}
48115
49116
The code-font behind each capability name is a link to the capability's id. This id can be used to reference the capability in a labeled test within flowR.
50117
Besides, we use colored bullets like this:
51118
52119
| <!-- --> | <!-- --> |
53120
| ---------------------- | ----------------------------------------------------- |
54-
| :green_square: | _flowR_ is capable of handling this feature fully |
55-
| :large_orange_diamond: | _flowR_ is capable of handling this feature partially |
56-
| :red_circle: | _flowR_ is not capable of handling this feature |
121+
| ${supportedSymbolMap.get('fully')} | _flowR_ is capable of handling this feature _fully_ |
122+
| ${supportedSymbolMap.get('partially')} | _flowR_ is capable of handling this feature _partially_ |
123+
| ${supportedSymbolMap.get('not')} | _flowR_ is _not_ capable of handling this feature |
57124
58125
:cloud: This could be a feature diagram... :cloud:
59126
60127
`;
61128
}
62129

130+
async function print(parser: KnownParser) {
131+
/* check if the detailed test data is available */
132+
if(!fs.existsSync(detailedInfoFile)) {
133+
console.warn('No detailed test data available. Run the full tests (npm run test-full) to generate it.');
134+
}
135+
return getPreamble() + await printAsMarkdown({ parser, info: obtainDetailedInfos() }, flowrCapabilities.capabilities);
136+
}
137+
63138
/** if we run this script, we want a Markdown representation of the capabilities */
64139
if(require.main === module) {
65140
setMinLevelOfAllLogs(LogLevel.Fatal);
66-
console.log(getPreamble() + printAsMarkdown(flowrCapabilities.capabilities));
141+
void TreeSitterExecutor.initTreeSitter().then(() => {
142+
const parser = new TreeSitterExecutor();
143+
void print(parser).then(str => {
144+
console.log(str);
145+
});
146+
});
67147
}

src/r-bridge/data/data.ts

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,40 @@
11
import type { FlowrCapabilities } from './types';
2+
import { FlowrGithubBaseRef } from '../../documentation/doc-util/doc-files';
3+
import { codeBlock } from '../../documentation/doc-util/doc-code';
4+
import { printDfGraphForCode } from '../../documentation/doc-util/doc-dfg';
5+
6+
const Joiner = '/';
7+
const AdvancedR = (subname: string) => 'Advanced R' + Joiner + subname;
8+
const RLang = (subname: string) => 'R Definition' + Joiner + subname;
9+
const Issue = (num: number) => `${FlowrGithubBaseRef}/flowr/issues/${num}`;
10+
const LinkTo = (id: string, label = id) => `[${label}](#${id})`;
211

312
export const flowrCapabilities = {
413
name: 'Capabilities of flowR',
514
description: 'This is an evolving representation of what started with #636 to formulate capabilities in a structured format.',
6-
version: '0.0.1',
15+
version: '0.0.2',
716
capabilities: [
817
{
9-
name: 'Names and Identifiers',
10-
id: 'names-and-identifiers',
18+
name: 'Names and Identifiers',
19+
id: 'names-and-identifiers',
20+
description: 'The recognition of syntactical and non-syntactical names, including their resolutions to corresponding definitions.',
21+
example: async parser => {
22+
const code = '"f" <- function(x) { get("x") } \n`y x` <- 2\nprint(`y x` + f(3))';
23+
return `
24+
Consider the following R code:
25+
${codeBlock('r', code)}
26+
Identifiers of interest are:
27+
28+
- The symbols \`x\` (${LinkTo('name-normal')}), \`f\` (${LinkTo('name-quoted')}), and \`\` \`y x\` \`\` (${LinkTo('name-escaped')}).
29+
- The function calls \`<-\`, \`function\`, \`{\`, \`get\`, \`+\`, and \`print\` (${LinkTo('function-calls')}, all given with ${LinkTo('name-normal')}).
30+
Especially \`{\` is identified as a ${LinkTo('grouping')} of the ${LinkTo('function-definitions', 'function-definitions\'')} body.
31+
- The quoted name created by a function call \`get\` (${LinkTo('name-created')}).
32+
33+
Besides the parameter \`x\`, which is resolved in its ${LinkTo('lexicographic-scope')}, the other identifiers are resolved in the ${LinkTo('global-scope')}.
34+
35+
${await printDfGraphForCode(parser, code, { simplified: true })}
36+
`;
37+
},
1138
capabilities: [
1239
{
1340
name: 'Form',
@@ -17,19 +44,38 @@ export const flowrCapabilities = {
1744
name: 'Normal',
1845
id: 'name-normal',
1946
supported: 'fully',
20-
description: '_Recognize constructs like `a`, `plot`, ..._'
47+
description: '_Recognize symbol uses like `a`, `plot`, ..._ (i.e., "normal variables or function calls").',
48+
url: [
49+
{ name: AdvancedR('Bindings'), href: 'https://adv-r.hadley.nz/names-values.html#binding-basics' },
50+
{ name: RLang('Identifiers'), href: 'https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Identifiers-1' }
51+
]
2152
},
2253
{
2354
name: 'Quoted',
2455
id: 'name-quoted',
2556
supported: 'fully',
26-
description: "_Recognize `\"a\"`, `'plot'`, ..._"
57+
description: "_Recognize `\"a\"`, `'plot'`, ..._ In general, R allows to envelop names in quotations to allow for special characters such as spaces in variable names. However, this only works in the context of definitions. To access these names as variables, one has to either use function such as `get` or escape the name with backticks.",
58+
url: [
59+
{ name: AdvancedR('Non-Syntactic Names'), href: 'https://adv-r.hadley.nz/names-values.html#non-syntactic' }
60+
]
2761
},
2862
{
2963
name: 'Escaped',
3064
id: 'name-escaped',
3165
supported: 'fully',
32-
description: '_Recognize `` `a` ``, `` `plot` ``, ..._'
66+
description: '_Recognize `` `a` ``, `` `plot` ``, ..._',
67+
url: [
68+
{ name: AdvancedR('Non-Syntactic Names'), href: 'https://adv-r.hadley.nz/names-values.html#non-syntactic' }
69+
]
70+
},
71+
{
72+
name: 'Created',
73+
id: 'name-created',
74+
supported: 'partially',
75+
description: '_Recognize functions which resolve strings as identifiers, such as `get`, ..._',
76+
url: [
77+
{ name: 'flowr#633', href: Issue(633) }
78+
]
3379
}
3480
]
3581
},
@@ -670,28 +716,28 @@ export const flowrCapabilities = {
670716
{
671717
name: 'S3',
672718
id: 'oop-s3',
673-
note: 'https://adv-r.hadley.nz/s3.html',
719+
example: 'https://adv-r.hadley.nz/s3.html',
674720
supported: 'not',
675721
description: '_Handle S3 classes and methods as one unit (with attributes etc.). Including Dispatch and Inheritance._ We do not support typing currently and do not handle objects of these classes "as units."'
676722
},
677723
{
678724
name: 'S4',
679725
id: 'oop-s4',
680-
note: 'https://adv-r.hadley.nz/s4.html',
726+
example: 'https://adv-r.hadley.nz/s4.html',
681727
supported: 'not',
682728
description: '_Handle S4 classes and methods as one unit. Including Dispatch and Inheritance_ We do not support typing currently and do not handle objects of these classes "as units."'
683729
},
684730
{
685731
name: 'R6',
686732
id: 'oop-r6',
687-
note: 'https://adv-r.hadley.nz/r6.html',
733+
example: 'https://adv-r.hadley.nz/r6.html',
688734
supported: 'not',
689735
description: '_Handle R6 classes and methods as one unit. Including Dispatch and Inheritance, as well as its Reference Semantics, Access Control, Finalizers, and Introspection._ We do not support typing currently and do not handle objects of these classes "as units."'
690736
},
691737
{
692738
name: 'R7/S7',
693739
id: 'r7-s7',
694-
note: 'https://www.r-bloggers.com/2022/12/what-is-r7-a-new-oop-system-for-r/, https://cran.r-project.org/web/packages/S7/index.html',
740+
example: 'https://www.r-bloggers.com/2022/12/what-is-r7-a-new-oop-system-for-r/, https://cran.r-project.org/web/packages/S7/index.html',
695741
supported: 'not',
696742
description: '_Handle R7 classes and methods as one unit. Including Dispatch and Inheritance, as well as its Reference Semantics, Validators, ..._ We do not support typing currently and do not handle objects of these classes "as units."'
697743
}

src/r-bridge/data/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { KnownParser } from '../parser';
2+
13
const enum RequiredFeature {
24
/** https://github.com/flowr-analysis/flowr/labels/typing */
35
Typing,
@@ -16,7 +18,10 @@ export interface FlowrCapability {
1618
/** A list of features that are required for the capability, extend at need. */
1719
readonly needs?: RequiredFeature[]
1820
readonly description?: string
19-
readonly note?: string
21+
/* examples may be generated on demand */
22+
readonly example?: string | ((parser: KnownParser) => Promise<string>),
23+
/** A list of URLs that provide additional information about the capability */
24+
readonly url?: { name: string, href: string }[]
2025
/** The level of support for the capability, undefined if it is a meta-capability that does not need such an attribute */
2126
readonly supported?: 'not' | 'partially' | 'fully'
2227
readonly capabilities?: readonly FlowrCapability[]

0 commit comments

Comments
 (0)