_flowR_ is a sophisticated, static dataflow analyzer for the R programming language, available for VSCode, Positron, RStudio, and Docker. It offers a wide variety of features, for example:
- 📚 dependency analysis\
Example: Dependency Analysis with flowR
The following showcases the dependency view of the Visual Studio Code extension:
!Dependency Analysis
- 🐞 code linting\
Example: Linting code with flowR
To lint your code, you can use the REPL or the Visual Studio Code extension (see vscode-flowr#283).
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
flowR repl v2.15.8, R grammar v14 (tree-sitter engine)
R> :query @linter "read.csv(\"/root/x.txt\")"Output
The linter will analyze the code and return any issues found.
Formatted more nicely, this returns:
Query: linter (17 ms)
╰ Deprecated Functions (deprecated-functions): no findings
╰ File Path Validity (file-path-validity): no findings
╰ Seeded Randomness (seeded-randomness): no findings
╰ Absolute Paths (absolute-file-paths): no findings
╰ Unused Definitions (unused-definitions): no findings
╰ Network Functions (network-functions): no findings
╰ Dataframe Access Validation (dataframe-access-validation): no findings
╰ Dead Code (dead-code): no findings
╰ Useless Loops (useless-loop): no findings
╰ Problematic inputs (problematic-inputs): no findings
╰ Stop without call.=False argument (stop-call): no findings
╰ Roxygen Arguments (roxygen-arguments): no findings
╰ No Leaked Credentials (no-leaked-credentials): no findings
╰ Undefined Symbol (undefined-symbol): no findings
╰ Unused Import (unused-import): no findings
╰ Unclosed Connection (unclosed-connection): no findings
╰ Unescaped Arguments (unescaped-arguments): no findings
╰ Namespace Access Validity (namespace-access): no findings
All queries together required ≈17 ms (1ms accuracy, total 17 ms)
(This can be shortened to [ { "type": "linter" } ]@linter when used with the REPL command :query).
_Results (prettified and summarized):_
Query: linter (24 ms)\
╰ Deprecated Functions (deprecated-functions): _no findings_\
╰ File Path Validity (file-path-validity):\
╰ certain:\
╰ Path /root/x.txt at 1.1-23\
╰ Seeded Randomness (seeded-randomness): _no findings_\
╰ Absolute Paths (absolute-file-paths):\
╰ certain:\
╰ Path /root/x.txt at 1.1-23\
╰ Unused Definitions (unused-definitions): _no findings_\
╰ Network Functions (network-functions): _no findings_\
╰ Dataframe Access Validation (dataframe-access-validation): _no findings_\
╰ Dead Code (dead-code): _no findings_\
╰ Useless Loops (useless-loop): _no findings_\
╰ Problematic inputs (problematic-inputs): _no findings_\
╰ Stop without call.=False argument (stop-call): _no findings_\
╰ Roxygen Arguments (roxygen-arguments): _no findings_\
╰ No Leaked Credentials (no-leaked-credentials): _no findings_\
╰ Undefined Symbol (undefined-symbol): _no findings_\
╰ Unused Import (unused-import): _no findings_\
╰ Unclosed Connection (unclosed-connection): _no findings_\
╰ Unescaped Arguments (unescaped-arguments): _no findings_\
╰ Namespace Access Validity (namespace-access): _no findings_\
_All queries together required ≈24 ms (1ms accuracy, total 25 ms)_
Show Detailed Results as Json
The analysis ran (including parsing and normalization and the query) within the generation environment.
In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
Please consult the Interface wiki page for more information on how to get those.
{
"linter": {
"results": {
"deprecated-functions": {"results":[],".meta":{"builtin":0,"sigdb":0}},
"file-path-validity": {
"results": [{"involvedId":3,"loc":[1,1,1,23],"filePath":"/root/x.txt","certainty":"certain"}],
".meta": {"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0}
},
"seeded-randomness": {
"results": [],
".meta": {"consumerCalls":0,"callsWithFunctionProducers":0,"callsWithAssignmentProducers":0,"callsWithNonConstantProducers":0,"callsWithOtherBranchProducers":0}
},
"absolute-file-paths": {"results":[{"certainty":"certain","filePath":"/root/x.txt","loc":[1,1,1,23]}],".meta":{"totalConsidered":1,"totalUnknown":0}},
"unused-definitions": {"results":[],".meta":{"totalConsidered":0}},
"network-functions": {"results":[],".meta":{"totalCalls":0,"totalFunctionDefinitions":0}},
"dataframe-access-validation": {"results":[],".meta":{"numOperations":0,"numAccesses":0,"totalAccessed":0}},
"dead-code": {"results":[],".meta":{}},
"useless-loop": {"results":[],".meta":{"numOfUselessLoops":0}},
"problematic-inputs": {"results":[],".meta":{}},
"stop-call": {"results":[],".meta":{"consideredNodes":0}},
"roxygen-arguments": {"results":[],".meta":{}},
"no-leaked-credentials": {"results":[],".meta":{"totalChecked":0}},
"undefined-symbol": {
"results": [],
".meta": {"totalFunctionCalls":1,"totalVariableUses":0,"suppressed":{"installed":0,"loadedPackage":0,"enclosingScope":0,"nonStandardEval":0,"subscript":0}}
},
"unused-import": {"results":[],".meta":{"totalConsidered":0,"totalUnresolved":0,"totalMultiPackage":0,"totalUnused":0}},
"unclosed-connection": {"results":[],".meta":{"totalOpened":0,"totalClosed":0}},
"unescaped-arguments": {"results":[],".meta":{"totalCriticalArguments":0,"totalEscapedArguments":0}},
"namespace-access": {"results":[],".meta":{"unresolved":0}}
},
".meta": {}
},
".meta": {}
}
- 🍕 program slicing\
Example: Slicing with flowR
The simplest way to retrieve slices is with flowR's Visual Studio Code extension.
However, you can slice using the REPL as well.
This can help you if you want to reuse specific parts of an existing analysis within another context or if you want to understand
what is happening in the code.
For this, let's have a look at the example file, located at test/testfiles/example.R:
Let's suppose we are interested only in the sum <- 0
product <- 1
w <- 7
N <- 10
for (i in 1:(N-1)) {
sum <- sum + i + w
product <- product * i
}
cat("Sum:", sum, "\n")
cat("Product:", product, "\n")sum which is printed in line 11.
To get a slice for this, you can use the following command:
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
flowR repl v2.15.8, R grammar v14 (tree-sitter engine)
R> :query @static-slice (11@sum) file://test/testfiles/example.ROutput
sum <- 0
w <- 7
N <- 10
for(i in 1:(N-1)) sum <- sum + i + w
sum
All queries together required ≈5 ms (1ms accuracy, total 5 ms)
- 🚀 fast call-graph, data-, and control-flow graphs\
Example: Generating a dataflow graph with flowR
You can investigate flowR's analyses using the REPL.
Commands like :dataflow* allow you to view a dataflow graph for a given R script.
Let's have a look at the following example:
To get the dataflow graph for this script, you can use the following command:
sum <- 0
product <- 1
w <- 7
N <- 10
for (i in 1:(N-1)) {
sum <- sum + i + w
product <- product * i
}
cat("Sum:", sum, "\n")
cat("Product:", product, "\n")$ docker run -it --rm eagleoutice/flowr # or npm run flowr
flowR repl v2.15.8, R grammar v14 (tree-sitter engine)
R> :dataflow* test/testfiles/example.ROutput
Following the link output should show the following:
'test/testfiles/example.R' looks like a path, analyzing file://test/testfiles/example.R (repl.autoUseFileProtocol is set).
https://mermaid.live/view#base64:eyJjb2RlIjoiZmxvd2NoYXJ0IFREXG4gICAgMXt7XCJgKiM5MTtSTnVtYmVyIzkzOyogKiowKipcbiAgICAgICoxLjgqICgqKmlkOiAxKiopYFwifX1cbiAgICAwW1wiYCojOTE7UlN5bWJvbCM5MzsqICoqc3VtKipcbiAgICAgICoxLjEtMyogKCoqaWQ6IDAqKiwgdjogMSlgXCJdXG4gICAgMltbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM2MDsjNDU7KipcbiAgICAgICoxLjEtOCogKCoqaWQ6IDIqKilcbiAgICBhcmc6ICgwLCAxKWBcIl1dXG4gICAgYnVpbHQtaW46Xy1bXCJgQnVpbHQtSW46XG4jNjA7IzQ1O2BcIl1cbiAgICBzdHlsZSBidWlsdC1pbjpfLSBzdHJva2U6Z3JheSxmaWxsOmdyYXksc3Ryb2tlLXdpZHRoOjJweCxvcGFjaXR5Oi44O1xuICAgIDR7e1wiYCojOTE7Uk51bWJlciM5MzsqICoqMSoqXG4gICAgICAqMi4xMiogKCoqaWQ6IDQqKilgXCJ9fVxuICAgIDNbXCJgKiM5MTtSU3ltYm9sIzkzOyogKipwcm9kdWN0KipcbiAgICAgICoyLjEtNyogKCoqaWQ6IDMqKiwgdjogNClgXCJdXG4gICAgNVtbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM2MDsjNDU7KipcbiAgICAgICoyLjEtMTIqICgqKmlkOiA1KiopXG4gICAgYXJnOiAoMywgNClgXCJdXVxuICAgIDd7e1wiYCojOTE7Uk51bWJlciM5MzsqICoqNyoqXG4gICAgICAqMy42KiAoKippZDogNyoqKWBcIn19XG4gICAgNltcImAqIzkxO1JTeW1ib2wjOTM7KiAqKncqKlxuICAgICAgKjMuMSogKCoqaWQ6IDYqKiwgdjogNylgXCJdXG4gICAgOFtbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM2MDsjNDU7KipcbiAgICAgICozLjEtNiogKCoqaWQ6IDgqKilcbiAgICBhcmc6ICg2LCA3KWBcIl1dXG4gICAgMTB7e1wiYCojOTE7Uk51bWJlciM5MzsqICoqMTAqKlxuICAgICAgKjQuNi03KiAoKippZDogMTAqKilgXCJ9fVxuICAgIDlbXCJgKiM5MTtSU3ltYm9sIzkzOyogKipOKipcbiAgICAgICo0LjEqICgqKmlkOiA5KiosIHY6IDEwKWBcIl1cbiAgICAxMVtbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM2MDsjNDU7KipcbiAgICAgICo0LjEtNyogKCoqaWQ6IDExKiopXG4gICAgYXJnOiAoOSwgMTApYFwiXV1cbiAgICAxMltcImAqIzkxO1JTeW1ib2wjOTM7KiAqKmkqKlxuICAgICAgKjYuNiogKCoqaWQ6IDEyKiosIHY6IDIwKWBcIl1cbiAgICAxM3t7XCJgKiM5MTtSTnVtYmVyIzkzOyogKioxKipcbiAgICAgICo2LjExKiAoKippZDogMTMqKilgXCJ9fVxuICAgIDE2KFtcImAqIzkxO1JTeW1ib2wjOTM7KiAqKk4qKlxuICAgICAgKjYuMTQqICgqKmlkOiAxNioqKWBcIl0pXG4gICAgMTd7e1wiYCojOTE7Uk51bWJlciM5MzsqICoqMSoqXG4gICAgICAqNi4xNiogKCoqaWQ6IDE3KiopYFwifX1cbiAgICAxOFtbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM0NTsqKlxuICAgICAgKjYuMTQtMTYqICgqKmlkOiAxOCoqKVxuICAgIGFyZzogKDE2LCAxNylgXCJdXVxuICAgIGJ1aWx0LWluOi1bXCJgQnVpbHQtSW46XG4jNDU7YFwiXVxuICAgIHN0eWxlIGJ1aWx0LWluOi0gc3Ryb2tlOmdyYXksZmlsbDpncmF5LHN0cm9rZS13aWR0aDoycHgsb3BhY2l0eTouODtcbiAgICAxOVtbXCJgKiM5MTtSRXhwcmVzc2lvbkxpc3QjOTM7KiBiYXNlIzU4OyM1ODsqKigqKlxuICAgICAgKjYuMTMqICgqKmlkOiAxOSoqKVxuICAgIGFyZzogKDE4KWBcIl1dXG4gICAgYnVpbHQtaW46X1tcImBCdWlsdC1JbjpcbihgXCJdXG4gICAgc3R5bGUgYnVpbHQtaW46XyBzdHJva2U6Z3JheSxmaWxsOmdyYXksc3Ryb2tlLXdpZHRoOjJweCxvcGFjaXR5Oi44O1xuICAgIDIwW1tcImAqIzkxO1JCaW5hcnlPcCM5MzsqIGJhc2UjNTg7IzU4OyoqIzU4OyoqXG4gICAgICAqNi4xMS0xNyogKCoqaWQ6IDIwKiopXG4gICAgYXJnOiAoMTMsIDE5KWBcIl1dXG4gICAgYnVpbHQtaW46OltcImBCdWlsdC1JbjpcbiM1ODtgXCJdXG4gICAgc3R5bGUgYnVpbHQtaW46OiBzdHJva2U6Z3JheSxmaWxsOmdyYXksc3Ryb2tlLXdpZHRoOjJweCxvcGFjaXR5Oi44O1xuICAgIDI0KFtcImAqIzkxO1JTeW1ib2wjOTM7KiAqKnN1bSoqXG4gICAgICAqNy4xMC0xMiogKCoqaWQ6IDI0KiosIDM2KylgXCJdKVxuICAgIDI1KFtcImAqIzkxO1JTeW1ib2wjOTM7KiAqKmkqKlxuICAgICAgKjcuMTYqICgqKmlkOiAyNSoqLCAzNispYFwiXSlcbiAgICAyNltbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM0MzsqKlxuICAgICAgKjcuMTAtMTYqICgqKmlkOiAyNioqLCAzNispXG4gICAgYXJnOiAoMjQsIDI1KWBcIl1dXG4gICAgMjcoW1wiYCojOTE7UlN5bWJvbCM5MzsqICoqdyoqXG4gICAgICAqNy4yMCogKCoqaWQ6IDI3KiosIDM2KylgXCJdKVxuICAgIDI4W1tcImAqIzkxO1JCaW5hcnlPcCM5MzsqIGJhc2UjNTg7IzU4OyoqIzQzOyoqXG4gICAgICAqNy4xMC0yMCogKCoqaWQ6IDI4KiosIDM2KylcbiAgICBhcmc6ICgyNiwgMjcpYFwiXV1cbiAgICAyM1tcImAqIzkxO1JTeW1ib2wjOTM7KiAqKnN1bSoqXG4gICAgICAqNy4zLTUqICgqKmlkOiAyMyoqLCAzNissIHY6IDI4KWBcIl1cbiAgICAyOVtbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM2MDsjNDU7KipcbiAgICAgICo3LjMtMjAqICgqKmlkOiAyOSoqLCAzNispXG4gICAgYXJnOiAoMjMsIDI4KWBcIl1dXG4gICAgMzEoW1wiYCojOTE7UlN5bWJvbCM5MzsqICoqcHJvZHVjdCoqXG4gICAgICAqOC4xNC0yMCogKCoqaWQ6IDMxKiosIDM2KylgXCJdKVxuICAgIDMyKFtcImAqIzkxO1JTeW1ib2wjOTM7KiAqKmkqKlxuICAgICAgKjguMjQqICgqKmlkOiAzMioqLCAzNispYFwiXSlcbiAgICAzM1tbXCJgKiM5MTtSQmluYXJ5T3AjOTM7KiBiYXNlIzU4OyM1ODsqKiM0MjsqKlxuICAgICAgKjguMTQtMjQqICgqKmlkOiAzMyoqLCAzNispXG4gICAgYXJnOiAoMzEsIDMyKWBcIl1dXG4gICAgMzBbXCJgKiM5MTtSU3ltYm9sIzkzOyogKipwcm9kdWN0KipcbiAgICAgICo4LjMtOSogKCoqaWQ6IDMwKiosIDM2KywgdjogMzMpYFwiXVxuICAgIDM0W1tcImAqIzkxO1JCaW5hcnlPcCM5MzsqIGJhc2UjNTg7IzU4OyoqIzYwOyM0NTsqKlxuICAgICAgKjguMy0yNCogKCoqaWQ6IDM0KiosIDM2KylcbiAgICBhcmc6ICgzMCwgMzMpYFwiXV1cbiAgICAzNVtbXCJgKiM5MTtSRXhwcmVzc2lvbkxpc3QjOTM7KiBiYXNlIzU4OyM1ODsqKiMxMjM7KipcbiAgICAgICo2LjIwKiAoKippZDogMzUqKiwgMzYrKVxuICAgIGFyZzogKDI5LCAzNClgXCJdXVxuICAgIDM2W1tcImAqIzkxO1JGb3JMb29wIzkzOyogYmFzZSM1ODsjNTg7Kipmb3IqKlxuICAgICAgKjYuMS05LjEqICgqKmlkOiAzNioqKVxuICAgIGFyZzogKDEyLCAyMCwgMzUpYFwiXV1cbiAgICBidWlsdC1pbjpmb3JbXCJgQnVpbHQtSW46XG5mb3JgXCJdXG4gICAgc3R5bGUgYnVpbHQtaW46Zm9yIHN0cm9rZTpncmF5LGZpbGw6Z3JheSxzdHJva2Utd2lkdGg6MnB4LG9wYWNpdHk6Ljg7XG4gICAgMzh7e1wiYCojOTE7UlN0cmluZyM5MzsqICoqIzM0O1N1bSM1ODsjMzQ7KipcbiAgICAgICoxMS41LTEwKiAoKippZDogMzgqKilgXCJ9fVxuICAgIDQwKFtcImAqIzkxO1JTeW1ib2wjOTM7KiAqKnN1bSoqXG4gICAgICAqMTEuMTMtMTUqICgqKmlkOiA0MCoqKWBcIl0pXG4gICAgYnVpbHQtaW46c3VtW1wiYEJ1aWx0LUluOlxuc3VtYFwiXVxuICAgIHN0eWxlIGJ1aWx0LWluOnN1bSBzdHJva2U6Z3JheSxmaWxsOmdyYXksc3Ryb2tlLXdpZHRoOjJweCxvcGFjaXR5Oi44O1xuICAgIDQye3tcImAqIzkxO1JTdHJpbmcjOTM7KiAqKiMzNDtcbiMzNDsqKlxuICAgICAgKjExLjE4LTIxKiAoKippZDogNDIqKilgXCJ9fVxuICAgIDQ0W1tcImAqIzkxO1JGdW5jdGlvbkNhbGwjOTM7KiBiYXNlIzU4OyM1ODsqKmNhdCoqXG4gICAgICAqMTEuMS0yMiogKCoqaWQ6IDQ0KiopXG4gICAgYXJnOiAoMzgsIDQwLCA0MilgXCJdXVxuICAgIGJ1aWx0LWluOmNhdFtcImBCdWlsdC1JbjpcbmNhdGBcIl1cbiAgICBzdHlsZSBidWlsdC1pbjpjYXQgc3Ryb2tlOmdyYXksZmlsbDpncmF5LHN0cm9rZS13aWR0aDoycHgsb3BhY2l0eTouODtcbiAgICA0Nnt7XCJgKiM5MTtSU3RyaW5nIzkzOyogKiojMzQ7UHJvZHVjdCM1ODsjMzQ7KipcbiAgICAgICoxMi41LTE0KiAoKippZDogNDYqKilgXCJ9fVxuICAgIDQ4KFtcImAqIzkxO1JTeW1ib2wjOTM7KiAqKnByb2R1Y3QqKlxuICAgICAgKjEyLjE3LTIzKiAoKippZDogNDgqKilgXCJdKVxuICAgIDUwe3tcImAqIzkxO1JTdHJpbmcjOTM7KiAqKiMzNDtcbiMzNDsqKlxuICAgICAgKjEyLjI2LTI5KiAoKippZDogNTAqKilgXCJ9fVxuICAgIDUyW1tcImAqIzkxO1JGdW5jdGlvbkNhbGwjOTM7KiBiYXNlIzU4OyM1ODsqKmNhdCoqXG4gICAgICAqMTIuMS0zMCogKCoqaWQ6IDUyKiopXG4gICAgYXJnOiAoNDYsIDQ4LCA1MClgXCJdXVxuICAgIDEgLS4tPnxcImZsb3dcInwgMFxuICAgIGxpbmtTdHlsZSAwIHN0cm9rZTpncmF5LGNvbG9yOmdyYXk7XG4gICAgMCAtLT58XCJkZWZpbmVkLWJ5LCBmbG93XCJ8IDJcbiAgICAwIC0tPnxcImRlZmluZWQtYnlcInwgMVxuICAgIDIgLS0+fFwicmVhZHMsIGFyZ1wifCAxXG4gICAgMiAtLT58XCJyZXR1cm5zLCBhcmdcInwgMFxuICAgIDIgLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjpfLVxuICAgIGxpbmtTdHlsZSA1IHN0cm9rZTpncmF5O1xuICAgIDIgLS4tPnxcImZsb3dcInwgNFxuICAgIGxpbmtTdHlsZSA2IHN0cm9rZTpncmF5LGNvbG9yOmdyYXk7XG4gICAgNCAtLi0+fFwiZmxvd1wifCAzXG4gICAgbGlua1N0eWxlIDcgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAzIC0tPnxcImRlZmluZWQtYnksIGZsb3dcInwgNVxuICAgIDMgLS0+fFwiZGVmaW5lZC1ieVwifCA0XG4gICAgNSAtLT58XCJyZWFkcywgYXJnXCJ8IDRcbiAgICA1IC0tPnxcInJldHVybnMsIGFyZ1wifCAzXG4gICAgNSAtLi0+fFwicmVhZHMsIGNhbGxzXCJ8IGJ1aWx0LWluOl8tXG4gICAgbGlua1N0eWxlIDEyIHN0cm9rZTpncmF5O1xuICAgIDUgLS4tPnxcImZsb3dcInwgN1xuICAgIGxpbmtTdHlsZSAxMyBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDcgLS4tPnxcImZsb3dcInwgNlxuICAgIGxpbmtTdHlsZSAxNCBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDYgLS0+fFwiZGVmaW5lZC1ieSwgZmxvd1wifCA4XG4gICAgNiAtLT58XCJkZWZpbmVkLWJ5XCJ8IDdcbiAgICA4IC0tPnxcInJlYWRzLCBhcmdcInwgN1xuICAgIDggLS0+fFwicmV0dXJucywgYXJnXCJ8IDZcbiAgICA4IC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46Xy1cbiAgICBsaW5rU3R5bGUgMTkgc3Ryb2tlOmdyYXk7XG4gICAgOCAtLi0+fFwiZmxvd1wifCAxMFxuICAgIGxpbmtTdHlsZSAyMCBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDEwIC0uLT58XCJmbG93XCJ8IDlcbiAgICBsaW5rU3R5bGUgMjEgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICA5IC0tPnxcImRlZmluZWQtYnksIGZsb3dcInwgMTFcbiAgICA5IC0tPnxcImRlZmluZWQtYnlcInwgMTBcbiAgICAxMSAtLT58XCJyZWFkcywgYXJnXCJ8IDEwXG4gICAgMTEgLS0+fFwicmV0dXJucywgYXJnXCJ8IDlcbiAgICAxMSAtLi0+fFwicmVhZHMsIGNhbGxzXCJ8IGJ1aWx0LWluOl8tXG4gICAgbGlua1N0eWxlIDI2IHN0cm9rZTpncmF5O1xuICAgIDExIC0uLT58XCJmbG93XCJ8IDEzXG4gICAgbGlua1N0eWxlIDI3IHN0cm9rZTpncmF5LGNvbG9yOmdyYXk7XG4gICAgMTIgLS0+fFwiZGVmaW5lZC1ieVwifCAyMFxuICAgIDEyIC0uLT58XCJicmFuY2ggKHdoZW46IHRydWUpXCJ8IDI0XG4gICAgbGlua1N0eWxlIDI5IHN0cm9rZTpncmF5LGNvbG9yOmdyYXk7XG4gICAgMTIgLS4tPnxcImJyYW5jaCAod2hlbjogZmFsc2UpXCJ8IDM2XG4gICAgbGlua1N0eWxlIDMwIHN0cm9rZTpncmF5LGNvbG9yOmdyYXk7XG4gICAgMTMgLS4tPnxcImZsb3dcInwgMTZcbiAgICBsaW5rU3R5bGUgMzEgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAxNiAtLT58XCJyZWFkc1wifCA5XG4gICAgMTYgLS4tPnxcImZsb3dcInwgMTdcbiAgICBsaW5rU3R5bGUgMzMgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAxNyAtLi0+fFwiZmxvd1wifCAxOFxuICAgIGxpbmtTdHlsZSAzNCBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDE4IC0tPnxcInJlYWRzLCBhcmdcInwgMTZcbiAgICAxOCAtLT58XCJyZWFkcywgYXJnXCJ8IDE3XG4gICAgMTggLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjotXG4gICAgbGlua1N0eWxlIDM3IHN0cm9rZTpncmF5O1xuICAgIDE4IC0uLT58XCJmbG93XCJ8IDE5XG4gICAgbGlua1N0eWxlIDM4IHN0cm9rZTpncmF5LGNvbG9yOmdyYXk7XG4gICAgMTkgLS0+fFwicmVhZHMsIHJldHVybnMsIGFyZ1wifCAxOFxuICAgIDE5IC0uLT58XCJyZWFkc1wifCBidWlsdC1pbjpfXG4gICAgbGlua1N0eWxlIDQwIHN0cm9rZTpncmF5O1xuICAgIDE5IC0uLT58XCJmbG93XCJ8IDIwXG4gICAgbGlua1N0eWxlIDQxIHN0cm9rZTpncmF5LGNvbG9yOmdyYXk7XG4gICAgMjAgLS0+fFwicmVhZHMsIGFyZ1wifCAxM1xuICAgIDIwIC0tPnxcInJlYWRzLCBhcmdcInwgMTlcbiAgICAyMCAtLi0+fFwiZmxvd1wifCAxMlxuICAgIGxpbmtTdHlsZSA0NCBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDIwIC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46OlxuICAgIGxpbmtTdHlsZSA0NSBzdHJva2U6Z3JheTtcbiAgICAyNCAtLT58XCJyZWFkc1wifCAwXG4gICAgMjQgLS4tPnxcImZsb3dcInwgMjVcbiAgICBsaW5rU3R5bGUgNDcgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAyNCAtLT58XCJyZWFkc1wifCAyM1xuICAgIDI1IC0tPnxcInJlYWRzXCJ8IDEyXG4gICAgMjUgLS4tPnxcImZsb3dcInwgMjZcbiAgICBsaW5rU3R5bGUgNTAgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAyNiAtLT58XCJyZWFkcywgYXJnXCJ8IDI0XG4gICAgMjYgLS0+fFwicmVhZHMsIGFyZ1wifCAyNVxuICAgIDI2IC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46X1xuICAgIGxpbmtTdHlsZSA1MyBzdHJva2U6Z3JheTtcbiAgICAyNiAtLi0+fFwiZmxvd1wifCAyN1xuICAgIGxpbmtTdHlsZSA1NCBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDI3IC0tPnxcInJlYWRzXCJ8IDZcbiAgICAyNyAtLi0+fFwiZmxvd1wifCAyOFxuICAgIGxpbmtTdHlsZSA1NiBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDI4IC0tPnxcInJlYWRzLCBhcmdcInwgMjZcbiAgICAyOCAtLT58XCJyZWFkcywgYXJnXCJ8IDI3XG4gICAgMjggLS4tPnxcImZsb3dcInwgMjNcbiAgICBsaW5rU3R5bGUgNTkgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAyOCAtLi0+fFwicmVhZHMsIGNhbGxzXCJ8IGJ1aWx0LWluOl9cbiAgICBsaW5rU3R5bGUgNjAgc3Ryb2tlOmdyYXk7XG4gICAgMjMgLS0+fFwiZGVmaW5lZC1ieSwgZmxvd1wifCAyOVxuICAgIDIzIC0tPnxcImRlZmluZWQtYnlcInwgMjhcbiAgICAyOSAtLT58XCJyZWFkcywgYXJnXCJ8IDI4XG4gICAgMjkgLS0+fFwicmV0dXJucywgYXJnXCJ8IDIzXG4gICAgMjkgLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjpfLVxuICAgIGxpbmtTdHlsZSA2NSBzdHJva2U6Z3JheTtcbiAgICAyOSAtLi0+fFwiZmxvd1wifCAzMVxuICAgIGxpbmtTdHlsZSA2NiBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDMxIC0tPnxcInJlYWRzXCJ8IDNcbiAgICAzMSAtLi0+fFwiZmxvd1wifCAzMlxuICAgIGxpbmtTdHlsZSA2OCBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDMxIC0tPnxcInJlYWRzXCJ8IDMwXG4gICAgMzIgLS0+fFwicmVhZHNcInwgMTJcbiAgICAzMiAtLi0+fFwiZmxvd1wifCAzM1xuICAgIGxpbmtTdHlsZSA3MSBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDMzIC0tPnxcInJlYWRzLCBhcmdcInwgMzFcbiAgICAzMyAtLT58XCJyZWFkcywgYXJnXCJ8IDMyXG4gICAgMzMgLS4tPnxcImZsb3dcInwgMzBcbiAgICBsaW5rU3R5bGUgNzQgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAzMyAtLi0+fFwicmVhZHMsIGNhbGxzXCJ8IGJ1aWx0LWluOl9cbiAgICBsaW5rU3R5bGUgNzUgc3Ryb2tlOmdyYXk7XG4gICAgMzAgLS0+fFwiZGVmaW5lZC1ieSwgZmxvd1wifCAzNFxuICAgIDMwIC0tPnxcImRlZmluZWQtYnlcInwgMzNcbiAgICAzNCAtLT58XCJyZWFkcywgYXJnXCJ8IDMzXG4gICAgMzQgLS0+fFwicmV0dXJucywgYXJnXCJ8IDMwXG4gICAgMzQgLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjpfLVxuICAgIGxpbmtTdHlsZSA4MCBzdHJva2U6Z3JheTtcbiAgICAzNCAtLi0+fFwiZmxvd1wifCAzNVxuICAgIGxpbmtTdHlsZSA4MSBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDM1IC0tPnxcImFyZ1wifCAyOVxuICAgIDM1IC0tPnxcInJldHVybnMsIGFyZ1wifCAzNFxuICAgIDM1IC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46X1xuICAgIGxpbmtTdHlsZSA4NCBzdHJva2U6Z3JheTtcbiAgICAzNSAtLi0+fFwiZmxvd1wifCAxMlxuICAgIGxpbmtTdHlsZSA4NSBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDM2IC0tPnxcImFyZ1wifCAxMlxuICAgIDM2IC0tPnxcInJlYWRzLCBhcmdcInwgMjBcbiAgICAzNiAtLT58XCJhcmcsIG5vbi1zdGFuZGFyZC1ldmFsdWF0aW9uXCJ8IDM1XG4gICAgMzYgLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjpmb3JcbiAgICBsaW5rU3R5bGUgODkgc3Ryb2tlOmdyYXk7XG4gICAgMzYgLS4tPnxcImZsb3dcInwgMzhcbiAgICBsaW5rU3R5bGUgOTAgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICAzOCAtLi0+fFwiZmxvd1wifCA0MFxuICAgIGxpbmtTdHlsZSA5MSBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDQwIC0tPnxcInJlYWRzXCJ8IDBcbiAgICA0MCAtLT58XCJyZWFkc1wifCAyM1xuICAgIDQwIC0uLT58XCJyZWFkc1wifCBidWlsdC1pbjpzdW1cbiAgICBsaW5rU3R5bGUgOTQgc3Ryb2tlOmdyYXk7XG4gICAgNDAgLS4tPnxcImZsb3dcInwgNDJcbiAgICBsaW5rU3R5bGUgOTUgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICA0MiAtLi0+fFwiZmxvd1wifCA0NFxuICAgIGxpbmtTdHlsZSA5NiBzdHJva2U6Z3JheSxjb2xvcjpncmF5O1xuICAgIDQ0IC0tPnxcInJlYWRzLCBhcmdcInwgMzhcbiAgICA0NCAtLT58XCJyZWFkcywgYXJnXCJ8IDQwXG4gICAgNDQgLS0+fFwicmVhZHMsIGFyZ1wifCA0MlxuICAgIDQ0IC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46Y2F0XG4gICAgbGlua1N0eWxlIDEwMCBzdHJva2U6Z3JheTtcbiAgICA0NCAtLi0+fFwiZmxvd1wifCA0NlxuICAgIGxpbmtTdHlsZSAxMDEgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICA0NiAtLi0+fFwiZmxvd1wifCA0OFxuICAgIGxpbmtTdHlsZSAxMDIgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICA0OCAtLT58XCJyZWFkc1wifCAzXG4gICAgNDggLS0+fFwicmVhZHNcInwgMzBcbiAgICA0OCAtLi0+fFwiZmxvd1wifCA1MFxuICAgIGxpbmtTdHlsZSAxMDUgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICA1MCAtLi0+fFwiZmxvd1wifCA1MlxuICAgIGxpbmtTdHlsZSAxMDYgc3Ryb2tlOmdyYXksY29sb3I6Z3JheTtcbiAgICA1MiAtLT58XCJyZWFkcywgYXJnXCJ8IDQ2XG4gICAgNTIgLS0+fFwicmVhZHMsIGFyZ1wifCA0OFxuICAgIDUyIC0tPnxcInJlYWRzLCBhcmdcInwgNTBcbiAgICA1MiAtLi0+fFwicmVhZHMsIGNhbGxzXCJ8IGJ1aWx0LWluOmNhdFxuICAgIGxpbmtTdHlsZSAxMTAgc3Ryb2tlOmdyYXk7IiwibWVybWFpZCI6eyJhdXRvU3luYyI6dHJ1ZX19
(The analysis ran (including parse and normalize, using the tree-sitter engine) within the generation environment. No signature database is mounted for these generated graphs, so flowchart LR
1{{"#91;RNumber#93; 0
1.8 (id: 1)"}}
0["#91;RSymbol#93; sum
1.1-3 (id: 0, v: 1)"]
2[["#91;RBinaryOp#93; base#58;#58;#60;#45;
1.1-8 (id: 2)
arg: (0, 1)"]]
built-in:_-["Built-In:
#60;#45;"]
style built-in:_- stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
4{{"#91;RNumber#93; 1
2.12 (id: 4)"}}
3["#91;RSymbol#93; product
2.1-7 (id: 3, v: 4)"]
5[["#91;RBinaryOp#93; base#58;#58;#60;#45;
2.1-12 (id: 5)
arg: (3, 4)"]]
7{{"#91;RNumber#93; 7
3.6 (id: 7)"}}
6["#91;RSymbol#93; w
3.1 (id: 6, v: 7)"]
8[["#91;RBinaryOp#93; base#58;#58;#60;#45;
3.1-6 (id: 8)
arg: (6, 7)"]]
10{{"#91;RNumber#93; 10
4.6-7 (id: 10)"}}
9["#91;RSymbol#93; N
4.1 (id: 9, v: 10)"]
11[["#91;RBinaryOp#93; base#58;#58;#60;#45;
4.1-7 (id: 11)
arg: (9, 10)"]]
12["#91;RSymbol#93; i
6.6 (id: 12, v: 20)"]
13{{"#91;RNumber#93; 1
6.11 (id: 13)"}}
16(["#91;RSymbol#93; N
6.14 (id: 16)"])
17{{"#91;RNumber#93; 1
6.16 (id: 17)"}}
18[["#91;RBinaryOp#93; base#58;#58;#45;
6.14-16 (id: 18)
arg: (16, 17)"]]
built-in:-["Built-In:
#45;"]
style built-in:- stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
19[["#91;RExpressionList#93; base#58;#58;(
6.13 (id: 19)
arg: (18)"]]
built-in:_["Built-In:
("]
style built-in:_ stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
20[["#91;RBinaryOp#93; base#58;#58;#58;
6.11-17 (id: 20)
arg: (13, 19)"]]
built-in::["Built-In:
#58;"]
style built-in:: stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
24(["#91;RSymbol#93; sum
7.10-12 (id: 24, 36+)"])
25(["#91;RSymbol#93; i
7.16 (id: 25, 36+)"])
26[["#91;RBinaryOp#93; base#58;#58;#43;
7.10-16 (id: 26, 36+)
arg: (24, 25)"]]
27(["#91;RSymbol#93; w
7.20 (id: 27, 36+)"])
28[["#91;RBinaryOp#93; base#58;#58;#43;
7.10-20 (id: 28, 36+)
arg: (26, 27)"]]
23["#91;RSymbol#93; sum
7.3-5 (id: 23, 36+, v: 28)"]
29[["#91;RBinaryOp#93; base#58;#58;#60;#45;
7.3-20 (id: 29, 36+)
arg: (23, 28)"]]
31(["#91;RSymbol#93; product
8.14-20 (id: 31, 36+)"])
32(["#91;RSymbol#93; i
8.24 (id: 32, 36+)"])
33[["#91;RBinaryOp#93; base#58;#58;#42;
8.14-24 (id: 33, 36+)
arg: (31, 32)"]]
30["#91;RSymbol#93; product
8.3-9 (id: 30, 36+, v: 33)"]
34[["#91;RBinaryOp#93; base#58;#58;#60;#45;
8.3-24 (id: 34, 36+)
arg: (30, 33)"]]
35[["#91;RExpressionList#93; base#58;#58;#123;
6.20 (id: 35, 36+)
arg: (29, 34)"]]
36[["#91;RForLoop#93; base#58;#58;for
6.1-9.1 (id: 36)
arg: (12, 20, 35)"]]
built-in:for["Built-In:
for"]
style built-in:for stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
38{{"#91;RString#93; #34;Sum#58;#34;
11.5-10 (id: 38)"}}
40(["#91;RSymbol#93; sum
11.13-15 (id: 40)"])
built-in:sum["Built-In:
sum"]
style built-in:sum stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
42{{"#91;RString#93; **#34;
#34;**
11.18-21 (id: 42)"}}
44[["#91;RFunctionCall#93; base#58;#58;cat
11.1-22 (id: 44)
arg: (38, 40, 42)"]]
built-in:cat["Built-In:
cat"]
style built-in:cat stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
46{{"#91;RString#93; #34;Product#58;#34;
12.5-14 (id: 46)"}}
48(["#91;RSymbol#93; product
12.17-23 (id: 48)"])
50{{"#91;RString#93; **#34;
#34;**
12.26-29 (id: 50)"}}
52[["#91;RFunctionCall#93; base#58;#58;cat
12.1-30 (id: 52)
arg: (46, 48, 50)"]]
1 -.->|"flow"| 0
linkStyle 0 stroke:gray,color:gray;
0 -->|"defined-by, flow"| 2
0 -->|"defined-by"| 1
2 -->|"reads, arg"| 1
2 -->|"returns, arg"| 0
2 -.->|"reads, calls"| built-in:_-
linkStyle 5 stroke:gray;
2 -.->|"flow"| 4
linkStyle 6 stroke:gray,color:gray;
4 -.->|"flow"| 3
linkStyle 7 stroke:gray,color:gray;
3 -->|"defined-by, flow"| 5
3 -->|"defined-by"| 4
5 -->|"reads, arg"| 4
5 -->|"returns, arg"| 3
5 -.->|"reads, calls"| built-in:_-
linkStyle 12 stroke:gray;
5 -.->|"flow"| 7
linkStyle 13 stroke:gray,color:gray;
7 -.->|"flow"| 6
linkStyle 14 stroke:gray,color:gray;
6 -->|"defined-by, flow"| 8
6 -->|"defined-by"| 7
8 -->|"reads, arg"| 7
8 -->|"returns, arg"| 6
8 -.->|"reads, calls"| built-in:_-
linkStyle 19 stroke:gray;
8 -.->|"flow"| 10
linkStyle 20 stroke:gray,color:gray;
10 -.->|"flow"| 9
linkStyle 21 stroke:gray,color:gray;
9 -->|"defined-by, flow"| 11
9 -->|"defined-by"| 10
11 -->|"reads, arg"| 10
11 -->|"returns, arg"| 9
11 -.->|"reads, calls"| built-in:_-
linkStyle 26 stroke:gray;
11 -.->|"flow"| 13
linkStyle 27 stroke:gray,color:gray;
12 -->|"defined-by"| 20
12 -.->|"branch (when: true)"| 24
linkStyle 29 stroke:gray,color:gray;
12 -.->|"branch (when: false)"| 36
linkStyle 30 stroke:gray,color:gray;
13 -.->|"flow"| 16
linkStyle 31 stroke:gray,color:gray;
16 -->|"reads"| 9
16 -.->|"flow"| 17
linkStyle 33 stroke:gray,color:gray;
17 -.->|"flow"| 18
linkStyle 34 stroke:gray,color:gray;
18 -->|"reads, arg"| 16
18 -->|"reads, arg"| 17
18 -.->|"reads, calls"| built-in:-
linkStyle 37 stroke:gray;
18 -.->|"flow"| 19
linkStyle 38 stroke:gray,color:gray;
19 -->|"reads, returns, arg"| 18
19 -.->|"reads"| built-in:_
linkStyle 40 stroke:gray;
19 -.->|"flow"| 20
linkStyle 41 stroke:gray,color:gray;
20 -->|"reads, arg"| 13
20 -->|"reads, arg"| 19
20 -.->|"flow"| 12
linkStyle 44 stroke:gray,color:gray;
20 -.->|"reads, calls"| built-in::
linkStyle 45 stroke:gray;
24 -->|"reads"| 0
24 -.->|"flow"| 25
linkStyle 47 stroke:gray,color:gray;
24 -->|"reads"| 23
25 -->|"reads"| 12
25 -.->|"flow"| 26
linkStyle 50 stroke:gray,color:gray;
26 -->|"reads, arg"| 24
26 -->|"reads, arg"| 25
26 -.->|"reads, calls"| built-in:_
linkStyle 53 stroke:gray;
26 -.->|"flow"| 27
linkStyle 54 stroke:gray,color:gray;
27 -->|"reads"| 6
27 -.->|"flow"| 28
linkStyle 56 stroke:gray,color:gray;
28 -->|"reads, arg"| 26
28 -->|"reads, arg"| 27
28 -.->|"flow"| 23
linkStyle 59 stroke:gray,color:gray;
28 -.->|"reads, calls"| built-in:_
linkStyle 60 stroke:gray;
23 -->|"defined-by, flow"| 29
23 -->|"defined-by"| 28
29 -->|"reads, arg"| 28
29 -->|"returns, arg"| 23
29 -.->|"reads, calls"| built-in:_-
linkStyle 65 stroke:gray;
29 -.->|"flow"| 31
linkStyle 66 stroke:gray,color:gray;
31 -->|"reads"| 3
31 -.->|"flow"| 32
linkStyle 68 stroke:gray,color:gray;
31 -->|"reads"| 30
32 -->|"reads"| 12
32 -.->|"flow"| 33
linkStyle 71 stroke:gray,color:gray;
33 -->|"reads, arg"| 31
33 -->|"reads, arg"| 32
33 -.->|"flow"| 30
linkStyle 74 stroke:gray,color:gray;
33 -.->|"reads, calls"| built-in:_
linkStyle 75 stroke:gray;
30 -->|"defined-by, flow"| 34
30 -->|"defined-by"| 33
34 -->|"reads, arg"| 33
34 -->|"returns, arg"| 30
34 -.->|"reads, calls"| built-in:_-
linkStyle 80 stroke:gray;
34 -.->|"flow"| 35
linkStyle 81 stroke:gray,color:gray;
35 -->|"arg"| 29
35 -->|"returns, arg"| 34
35 -.->|"reads, calls"| built-in:_
linkStyle 84 stroke:gray;
35 -.->|"flow"| 12
linkStyle 85 stroke:gray,color:gray;
36 -->|"arg"| 12
36 -->|"reads, arg"| 20
36 -->|"arg, non-standard-evaluation"| 35
36 -.->|"reads, calls"| built-in:for
linkStyle 89 stroke:gray;
36 -.->|"flow"| 38
linkStyle 90 stroke:gray,color:gray;
38 -.->|"flow"| 40
linkStyle 91 stroke:gray,color:gray;
40 -->|"reads"| 0
40 -->|"reads"| 23
40 -.->|"reads"| built-in:sum
linkStyle 94 stroke:gray;
40 -.->|"flow"| 42
linkStyle 95 stroke:gray,color:gray;
42 -.->|"flow"| 44
linkStyle 96 stroke:gray,color:gray;
44 -->|"reads, arg"| 38
44 -->|"reads, arg"| 40
44 -->|"reads, arg"| 42
44 -.->|"reads, calls"| built-in:cat
linkStyle 100 stroke:gray;
44 -.->|"flow"| 46
linkStyle 101 stroke:gray,color:gray;
46 -.->|"flow"| 48
linkStyle 102 stroke:gray,color:gray;
48 -->|"reads"| 3
48 -->|"reads"| 30
48 -.->|"flow"| 50
linkStyle 105 stroke:gray,color:gray;
50 -.->|"flow"| 52
linkStyle 106 stroke:gray,color:gray;
52 -->|"reads, arg"| 46
52 -->|"reads, arg"| 48
52 -->|"reads, arg"| 50
52 -.->|"reads, calls"| built-in:cat
linkStyle 110 stroke:gray;library() calls attach no package exports; base-R names are still qualified via the generated base-package store (e.g. acf as stats::acf).)
If you want to use flowR and the features it provides, feel free to check out the:
- Visual Studio Code/Positron: provides access to flowR directly in VS Code and Positron (or vscode.dev)
- RStudio Addin: integrates flowR into RStudio
- R package: use flowR in your R scripts
- Docker image: run flowR in a container, this also includes flowR's server
- NPM package: include flowR in your TypeScript and JavaScript projects
⭐ Getting Started
To get started with _flowR_ and its features, please check out the Overview wiki page. The Setup wiki page explains how you can download and setup _flowR_ on your system. With docker 🐳️, the following line should be enough (and drop you directly into the read-eval-print loop):
docker run -it --rm eagleoutice/flowr
You can enter :help to gain more information on its capabilities.
!Example of a simple REPL session
If you want to use the same commands:
- First this runs
docker run -it --rm eagleoutice/flowrin a terminal to start the REPL. - In the REPL, it runs
:slicer -c '11@prod' demo.R --diffto slice the example filedemo.Rfor the print statement in line 11.
11 refers to the 11th line number to slice for!
📜 More Information
For more details on how to use _flowR_ please refer to the wiki pages, as well as the deployed code documentation. To cite flowR, please check out the publications below. To specifically refer to the source code, please check out flowR's Zenodo archive.
📃 Publications on flowR
If you are interested in the theoretical background of _flowR_, please check out the following publications (if you find that a paper is missing here, please open a new issue):
This refers to an updated tool demonstration of the framework. Preprint available at arXiv:2604.15963.BibTeX
@article{10.1145/3803437.3806402,
author = {Sihler, Florian and Gerstl, Oliver and Pfrenger, Lars and Schubert, Julian and Tichy, Matthias},
title = {Supporting the Comprehension of Data Analysis Scripts},
year = {2026},
doi = {10.1145/3803437.3806402}
}
BibTeX
@article{10.1145/3763087,
author = {Sihler, Florian and Tichy, Matthias},
title = {Statically Analyzing the Dataflow of R Programs},
year = {2025},
issue_date = {October 2025},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
volume = {9},
number = {OOPSLA2},
url = {https://doi.org/10.1145/3763087},
doi = {10.1145/3763087},
abstract = {The R programming language is primarily designed for statistical computing and mostly used by researchers without a background in computer science. R provides a wide range of dynamic features and peculiarities that are difficult to analyze statically like dynamic scoping and lazy evaluation with dynamic side effects. At the same time, the R ecosystem lacks sophisticated analysis tools that support researchers in understanding and improving their code. In this paper, we present a novel static dataflow analysis framework for the R programming language that is capable of handling the dynamic nature of R programs and produces the dataflow graph of given R programs. This graph can be essential in a range of analyses, including program slicing, which we implement as a proof of concept. The core analysis works as a stateful fold over a normalized version of the abstract syntax tree of the R program, which tracks (re-)definitions, values, function calls, side effects, external files, and a dynamic control flow to produce one dataflow graph per program. We evaluate the correctness of our analysis using output equivalence testing on a manually curated dataset of 779 sensible slicing points from executable real-world R scripts. Additionally, we use a set of systematic test cases based on the capabilities of the R language and the implementation of the R interpreter and measure the runtimes well as the memory consumption on a set of 4,230 real-world R scripts and 20,815 packages available on R’s package manager CRAN. Furthermore, we evaluate the recall of our program slicer, its accuracy using shrinking, and its improvement over the state of the art. We correctly analyze almost all programs in our equivalence test suite, preserving the identical output for 99.7\% of the manually curated slicing points. On average, we require 576ms to analyze the dataflow and around 213kB to store the graph of a research script. This shows that our analysis is capable of analyzing real-world sources quickly and correctly. Our slicer achieves an average reduction of 84.8\% of tokens indicating its potential to improve program comprehension.},
journal = {Proc. ACM Program. Lang.},
month = oct,
articleno = {309},
numpages = {29},
keywords = {Dataflow Analysis, R Programming Language, Static Analysis}
}
BibTeX
@inproceedings{DBLP:conf/kbse/SihlerT24,
author = {Florian Sihler and
Matthias Tichy},
editor = {Vladimir Filkov and
Baishakhi Ray and
Minghui Zhou},
title = {flowR: {A} Static Program Slicer for {R}},
booktitle = {Proceedings of the 39th {IEEE/ACM} International Conference on Automated
Software Engineering, {ASE} 2024, Sacramento, CA, USA, October 27
- November 1, 2024},
pages = {2390--2393},
publisher = {{ACM}},
year = {2024},
url = {https://doi.org/10.1145/3691620.3695359},
doi = {10.1145/3691620.3695359},
timestamp = {Mon, 03 Mar 2025 21:16:51 +0100},
biburl = {https://dblp.org/rec/conf/kbse/SihlerT24.bib},
bibsource = {dblp computer science bibliography, https://dblp.org}
}
BibTeX
@inproceedings{DBLP:conf/msr/SihlerPSTDD24,
author = {Florian Sihler and
Lukas Pietzschmann and
Raphael Straub and
Matthias Tichy and
Andor Diera and
Abdelhalim Hafedh Dahou},
editor = {Diomidis Spinellis and
Alberto Bacchelli and
Eleni Constantinou},
title = {On the Anatomy of Real-World {R} Code for Static Analysis},
booktitle = {21st {IEEE/ACM} International Conference on Mining Software Repositories,
{MSR} 2024, Lisbon, Portugal, April 15-16, 2024},
pages = {619--630},
publisher = {{ACM}},
year = {2024},
url = {https://doi.org/10.1145/3643991.3644911},
doi = {10.1145/3643991.3644911},
timestamp = {Sun, 19 Jan 2025 13:31:27 +0100},
biburl = {https://dblp.org/rec/conf/msr/SihlerPSTDD24.bib},
bibsource = {dblp computer science bibliography, https://dblp.org}
}
Works using flowR include: Computational Reproducibility of R Code Supplements on OSF and Multi-View Structural Graph Summaries.
🚀 Contributing
We welcome every contribution! The developer onboarding page has everything you need to get started.
With R and Node.js installed, npm run setup:dev checks your prer
... (README truncated for length)