UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

543 lines (486 loc) 26.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.documentAllServerMessages = documentAllServerMessages; const doc_server_message_1 = require("../../doc-util/doc-server-message"); const message_hello_1 = require("../../../cli/repl/server/messages/message-hello"); const shell_1 = require("../../../r-bridge/shell"); const doc_docker_1 = require("../../doc-util/doc-docker"); const doc_cli_option_1 = require("../../doc-util/doc-cli-option"); const doc_code_1 = require("../../doc-util/doc-code"); const message_analysis_1 = require("../../../cli/repl/server/messages/message-analysis"); const doc_files_1 = require("../../doc-util/doc-files"); const cfg_1 = require("../../../util/mermaid/cfg"); const doc_cfg_1 = require("../../doc-util/doc-cfg"); const doc_issue_1 = require("../../doc-util/doc-issue"); const message_slice_1 = require("../../../cli/repl/server/messages/message-slice"); const message_repl_1 = require("../../../cli/repl/server/messages/message-repl"); const message_query_1 = require("../../../cli/repl/server/messages/message-query"); const example_query_code_1 = require("../query/example-query-code"); const message_lineage_1 = require("../../../cli/repl/server/messages/message-lineage"); const identify_link_to_last_call_relation_1 = require("../../../queries/catalog/call-context-query/identify-link-to-last-call-relation"); function documentAllServerMessages() { (0, doc_server_message_1.documentServerMessage)({ title: 'Hello', type: 'response', definitionPath: '../cli/repl/server/messages/message-hello.ts', defResponse: message_hello_1.helloMessageDefinition, mermaidSequenceDiagram: ` Client-->Server: connects Server->>Client: hello `, shortDescription: 'The server informs the client about the successful connection and provides Meta-Information.', text: async (shell) => { return ` After launching _flowR_, for example, with <code>docker run -it --rm ${doc_docker_1.DockerName} ${(0, doc_cli_option_1.getCliLongOptionOf)('flowr', 'server', false, false)}</code>&nbsp;(🐳️), simply connecting should present you with a \`${message_hello_1.helloMessageDefinition.type}\` message, that amongst others should reveal the versions of&nbsp;_flowR_ and&nbsp;R, using the [semver 2.0](https://semver.org/spec/v2.0.0.html) versioning scheme. The message looks like this: ${(0, doc_code_1.codeBlock)('json', await (0, doc_server_message_1.inServerContext)(shell, socket => { const [hello] = socket.getMessages(['hello']); return JSON.stringify(hello, null, 2); }))} There are currently a few messages that you can send after the hello message. If you want to _slice_ a piece of R code you first have to send an [analysis request](#message-request-file-analysis), so that you can send one or multiple slice requests afterward. Requests for the [REPL](#message-request-repl) are independent of that. `; } }); const cfgSample = 'if(unknown > 0) { x <- 2 } else { x <- 5 }\nfor(i in 1:x) { print(x); print(i) }'; (0, doc_server_message_1.documentServerMessage)({ title: 'Analysis', type: 'request', definitionPath: '../cli/repl/server/messages/message-analysis.ts', defRequest: message_analysis_1.requestAnalysisMessage, defResponse: message_analysis_1.analysisResponseMessage, mermaidSequenceDiagram: ` Client->>+Server: request-file-analysis alt Server-->>Client: response-file-analysis else Server-->>Client: error end deactivate Server `, shortDescription: 'The server builds the dataflow graph for a given input file (or a set of files).', text: async (shell) => { return ` The request allows the server to analyze a file and prepare it for slicing. The message can contain a \`filetoken\`, which is used to identify the file in later slice or lineage requests (if you do not add one, the request will not be stored and therefore, it is not available for subsequent requests). > **Please note!**\\ > If you want to send and process a lot of analysis requests, but do not want to slice them, please do not pass the \`filetoken\` field. This will save the server a lot of memory allocation. Furthermore, the request must contain either a \`content\` field to directly pass the file's content or a \`filepath\` field which contains the path to the file (this path must be accessible for the server to be useful). If you add the \`id\` field, the answer will use the same \`id\` so you can match requests and the corresponding answers. See the implementation of the request-file-analysis message for more information. ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, messageType: 'request-file-analysis', messages: [ { type: 'request', description: `Let' suppose you simply want to analyze the following script:\n ${(0, doc_code_1.codeBlock)('r', 'x <- 1\nx + 1')}\n For this, you can send the following request:`, message: { type: 'request-file-analysis', id: '1', filetoken: 'x', content: 'x <- 1\nx + 1' }, mark: true }, { type: 'response', expectedType: 'response-file-analysis', description: ` The \`results\` field of the response effectively contains three keys of importance: - \`parse\`: which contains 1:1 the parse result in CSV format that we received from the \`${shell_1.RShell.name}\` (i.e., the AST produced by the parser of the R interpreter). - \`normalize\`: which contains the normalized AST, including ids (see the \`info\` field and the [Normalized AST](${doc_files_1.FlowrWikiBaseRef}/Normalized%20AST) wiki page). - \`dataflow\`: especially important is the \`graph\` field which contains the dataflow graph as a set of root vertices (see the [Dataflow Graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph) wiki page). ` } ] })} You receive an error if, for whatever reason, the analysis fails (e.g., the message or code you sent contained syntax errors). It contains a human-readable description *why* the analysis failed (see the error message implementation for more details). ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, title: 'Example Error Message', messages: [{ type: 'request', message: { type: 'request-file-analysis', id: '1', filename: 'sample.R', content: 'x <-' } }, { type: 'response', expectedType: 'error', mark: true }] })} &nbsp; <a id="analysis-include-cfg"></a> **Including the Control Flow Graph** While _flowR_ does (for the time being) not use an explicit control flow graph but instead relies on control-dependency edges within the dataflow graph, the respective structure can still be exposed using the server (note that, as this feature is not needed within _flowR_, it is tested significantly less - so please create a [new issue](${doc_issue_1.NewIssueUrl}) for any bug you may encounter). For this, the analysis request may add \`cfg: true\` to its list of options. ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, title: 'Requesting a Control Flow Graph', messages: [{ type: 'request', message: { type: 'request-file-analysis', id: '1', filetoken: 'x', content: cfgSample, cfg: true }, mark: true }, { type: 'response', expectedType: 'response-file-analysis', description: ` The response looks basically the same as a response sent without the \`cfg\` flag. However, additionally it contains a \`cfg\` field. If you are interested in a visual representation of the control flow graph, see the [visualization with mermaid](${await (async () => { const res = await (0, doc_cfg_1.getCfg)(shell, cfgSample); return (0, cfg_1.cfgToMermaidUrl)(res.info, res.ast); })()}). ` }] })} &nbsp; <a id="analysis-format-n-quads"></a> **Retrieve the Output as RDF N-Quads** The default response is formatted as JSON. However, by specifying \`format: "n-quads"\`, you can retrieve the individual results (e.g., the [Normalized AST](${doc_files_1.FlowrWikiBaseRef}/Normalized%20AST)), as [RDF N-Quads](https://www.w3.org/TR/n-quads/). This works with and without the control flow graph as described [above](#analysis-include-cfg). ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, title: 'Requesting RDF N-Quads', messages: [{ type: 'request', message: { type: 'request-file-analysis', id: '1', filetoken: 'x', content: 'x <- 1\nx + 1', format: 'n-quads', cfg: true }, mark: true }, { type: 'response', expectedType: 'response-file-analysis', description: ` Please note, that the base message format is still JSON. Only the individual results get converted. While the context is derived from the \`filename\`, we currently offer no way to customize other parts of the quads (please open a [new issue](${doc_issue_1.NewIssueUrl}) if you require this). ` }] })} <a id="analysis-format-compact"></a> **Retrieve the Output in a Compacted Form** The default response is formatted as JSON. But this can get very big quickly. By specifying \`format: "compact"\`, you can retrieve the results heavily compacted (using [lz-string](https://www.npmjs.com/package/lz-string)). This works with and without the control flow graph as described [above](#analysis-include-cfg). ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, title: 'Requesting Compacted Results', messages: [{ type: 'request', message: { type: 'request-file-analysis', id: '1', filetoken: 'x', content: 'x <- 1\nx + 1', format: 'compact', cfg: true }, mark: true }, { type: 'response', expectedType: 'response-file-analysis', description: ` Please note, that the base message format is still JSON. Only the individual results are printed as binary objects. ` }] })} `; } }); const deprecatedByQuery = `(<a href="${doc_files_1.FlowrWikiBaseRef}/Query%20API">deprecated</a>)`; (0, doc_server_message_1.documentServerMessage)({ title: 'Slice', type: 'request', definitionPath: '../cli/repl/server/messages/message-slice.ts', defRequest: message_slice_1.requestSliceMessage, defResponse: message_slice_1.responseSliceMessage, mermaidSequenceDiagram: ` Client->>+Server: request-slice alt Server-->>Client: response-slice else Server-->>Client: error end deactivate Server `, shortDescription: `${deprecatedByQuery} The server slices a file based on the given criteria.`, text: async (shell) => { return ` **We deprecated the slice request in favor of the \`static-slice\` [Query](${doc_files_1.FlowrWikiBaseRef}/Query%20API).** To slice, you have to send a file analysis request first. The \`filetoken\` you assign is of use here as you can re-use it to repeatedly slice the same file. Besides that, you only need to add an array of slicing criteria, using one of the formats described on the [terminology wiki page](${doc_files_1.FlowrWikiBaseRef}/Terminology#slicing-criterion) (however, instead of using \`;\`, you can simply pass separate array elements). See the implementation of the request-slice message for more information. Additionally, you may pass \`"noMagicComments": true\` to disable the automatic selection of elements based on magic comments (see below). ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, messageType: 'request-slice', messages: [{ type: 'request', description: `Let's assume you want to slice the following script:\n${(0, doc_code_1.codeBlock)('r', 'x <- 1\nx + 1')}\n\nFor this we first request the analysis, using a \`filetoken\` of \`x\` to slice the file in the next request.`, message: { type: 'request-file-analysis', id: '1', filetoken: 'x', content: 'x <- 1\nx + 1' } }, { type: 'response', expectedType: 'response-file-analysis', description: ` See [above](#message-request-file-analysis) for the general structure of the response. ` }, { type: 'request', description: 'Of course, the second slice criterion `2:1` is redundant for the input, as they refer to the same variable. It is only for demonstration purposes.', message: { type: 'request-slice', id: '2', filetoken: 'x', criterion: ['2@x', '2:1'] }, mark: true }, { type: 'response', expectedType: 'response-slice', description: ` The \`results\` field of the response contains two keys of importance: - \`slice\`: which contains the result of the slicing (e.g., the ids included in the slice in \`result\`). - \`reconstruct\`: contains the reconstructed code, as well as additional meta information. The automatically selected lines correspond to additional filters (e.g., magic comments) which force the unconditiojnal inclusion of certain elements. ` }] })} The semantics of the error message are similar. If, for example, the slicing criterion is invalid or the \`filetoken\` is unknown, _flowR_ will respond with an error. &nbsp; <a id="slice-magic-comments"></a> **Magic Comments** Within a document that is to be sliced, you can use magic comments to influence the slicing process: - \`# flowr@include_next_line\` will cause the next line to be included, independent of if it is important for the slice. - \`# flowr@include_this_line\` will cause the current line to be included, independent of if it is important for the slice. - \`# flowr@include_start\` and \`# flowr@include_end\` will cause the lines between them to be included, independent of if they are important for the slice. These magic comments can be nested but should appear on a separate line. `; } }); (0, doc_server_message_1.documentServerMessage)({ title: 'REPL', type: 'request', definitionPath: '../cli/repl/server/messages/message-repl.ts', defRequest: message_repl_1.requestExecuteReplExpressionMessage, defResponse: message_repl_1.responseExecuteReplIntermediateMessage, additionalDefs: [message_repl_1.responseExecuteReplEndMessage], mermaidSequenceDiagram: ` Client->>+Server: request-repl-execution alt Server-->>Client: error else loop Server-->>Client: response-repl-execution end Server-->>Client: end-repl-execution end deactivate Server `, shortDescription: 'Access the read evaluate print loop of flowR.', text: async (shell) => { return ` > [!WARNING] > To execute arbitrary R commands with a request, the server has to be started explicitly with ${(0, doc_cli_option_1.getCliLongOptionOf)('flowr', 'r-session-access')}. > Please be aware that this introduces a security risk. The REPL execution message allows to send a REPL command to receive its output. For more on the REPL, see the [introduction](${doc_files_1.FlowrWikiBaseRef}/Overview#the-read-eval-print-loop-repl), or the [description below](#using-the-repl). You only have to pass the command you want to execute in the \`expression\` field. Furthermore, you can set the \`ansi\` field to \`true\` if you are interested in output formatted using [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). We strongly recommend you to make use of the \`id\` field to link answers with requests as you can theoretically request the execution of multiple scripts at the same time, which then happens in parallel. > [!WARNING] > There is currently no automatic sandboxing or safeguarding against such requests. They simply execute the respective&nbsp;R code on your machine. > Please be very careful (and do not use ${(0, doc_cli_option_1.getCliLongOptionOf)('flowr', 'r-session-access')} if you are unsure). The answer on such a request is different from the other messages as the \`response-repl-execution\` message may be sent multiple times. This allows to better handle requests that require more time but already output intermediate results. You can detect the end of the execution by receiving the \`end-repl-execution\` message. The semantics of the error message are similar to that of the other messages. ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, messageType: 'request-slice', messages: [{ type: 'request', message: { type: 'request-repl-execution', id: '1', expression: ':help', }, mark: true }, { type: 'response', expectedType: 'response-repl-execution', description: msg => { return ` The \`stream\` field (either \`stdout\` or \`stderr\`) informs you of the output's origin: either the standard output or the standard error channel. After this message follows the end marker. <details> <summary>Pretty-Printed Result</summary> ${(0, doc_code_1.codeBlock)('text', msg.result)} </details> `; } }, { type: 'response', expectedType: 'end-repl-execution', }] })} `; } }); (0, doc_server_message_1.documentServerMessage)({ title: 'Query', type: 'request', definitionPath: '../cli/repl/server/messages/message-query.ts', defRequest: message_query_1.requestQueryMessage, defResponse: message_query_1.responseQueryMessage, mermaidSequenceDiagram: ` Client->>+Server: request-query alt Server-->>Client: response-query else Server-->>Client: error end deactivate Server `, shortDescription: 'Query an analysis result for specific information.', text: async (shell) => { return ` To send queries, you have to send an [analysis request](#message-request-file-analysis) first. The \`filetoken\` you assign is of use here as you can re-use it to repeatedly query the same file. This message provides direct access to _flowR_'s Query API. Please consult the [Query API documentation](${doc_files_1.FlowrWikiBaseRef}/Query%20API) for more information. ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, messageType: 'request-query', messages: [{ type: 'request', description: `Let's assume you want to query the following script:\n${(0, doc_code_1.codeBlock)('r', example_query_code_1.exampleQueryCode)}.\n\nFor this we first request the analysis, using a dummy \`filetoken\` of \`x\` to slice the file in the next request.`, message: { type: 'request-file-analysis', id: '1', filetoken: 'x', content: example_query_code_1.exampleQueryCode } }, { type: 'response', expectedType: 'response-file-analysis', description: ` See [above](#message-request-file-analysis) for the general structure of the response. ` }, { type: 'request', message: { type: 'request-query', id: '2', filetoken: 'x', query: [ { type: 'compound', query: 'call-context', commonArguments: { kind: 'visualize', subkind: 'text', callTargets: identify_link_to_last_call_relation_1.CallTargets.OnlyGlobal, }, arguments: [ { callName: '^mean$' }, { callName: '^print$', callTargets: identify_link_to_last_call_relation_1.CallTargets.OnlyLocal } ] } ] }, mark: true }, { type: 'response', expectedType: 'response-query' }] })} `; } }); (0, doc_server_message_1.documentServerMessage)({ title: 'Lineage', type: 'request', definitionPath: '../cli/repl/server/messages/message-lineage.ts', defRequest: message_lineage_1.requestLineageMessage, defResponse: message_lineage_1.responseLineageMessage, mermaidSequenceDiagram: ` Client->>+Server: request-lineage alt Server-->>Client: response-lineage else Server-->>Client: error end deactivate Server `, shortDescription: `${deprecatedByQuery} Obtain the lineage of a given slicing criterion.`, text: async (shell) => { return ` **We deprecated the lineage request in favor of the \`lineage\` [Query](${doc_files_1.FlowrWikiBaseRef}/Query%20API).** In order to retrieve the lineage of an object, you have to send a file analysis request first. The \`filetoken\` you assign is of use here as you can re-use it to repeatedly retrieve the lineage of the same file. Besides that, you will need to add a [criterion](${doc_files_1.FlowrWikiBaseRef}/Terminology#slicing-criterion) that specifies the object whose lineage you're interested in. ${await (0, doc_server_message_1.documentServerMessageResponse)({ shell, messageType: 'request-query', messages: [{ type: 'request', message: { type: 'request-file-analysis', id: '1', filetoken: 'x', content: 'x <- 1\nx + 1' } }, { type: 'response', expectedType: 'response-file-analysis', description: ` See [above](#message-request-file-analysis) for the general structure of the response. ` }, { type: 'request', message: { type: 'request-lineage', id: '2', filetoken: 'x', criterion: '2@x' }, mark: true }, { type: 'response', expectedType: 'response-lineage', description: 'The response contains the lineage of the desired object in form of an array of IDs (as the representation of a set).' }] })} `; } }); } //# sourceMappingURL=doc-data-server-messages.js.map