Inner Workings of RagConnect
¶
Please see API documentation for more details.
RagConnect
uses the relast-preprocessor to parse .relast
grammar files.
This results in an ASTNode of type Program
.
It further uses a dedicated parser for .connect
files containing port-, mapping-, and dependency-definitions.
This results in an ASTNode of type RagConnect
.
The goal is to generate an aspect file containing setters and getters of tokens referred to by port-definitions
We use mustache (currently its Java version) making use of partials to separate concerns.
The .mustache
files are located in ragconnect.base/src/main/resources
.
The generation process uses intermediates NTAs (whose types are defined in Intermediate.relast
) to ease the definition of two main generation "problems".
Those problems are differentiation on both kinds of a port (send/receive and type/token/list), and attributes depending on position in n-ary relations.
There are aspect files for Navigation
(mainly isX/asX attributes), Analysis
(static analysis attributes), Printing
, Mappings
(default mappings).
One of the main aspects is Intermediate
containing all attributes consumed by mustache
and other attributes the former depend on.
The other main aspect (which is currently not really used) is IntermediateToYAML
containing the transformation from a RagConnect
subtree to a Document
subtree defined by Mustache.relast
(located in relast-preprocessor
submodule).
This is used to generate a YAML file containing the data used by mustache.
It can be used by the default mustache implementation together with the templates.
Implementation Details¶
In the following, details for special implementation topics are discussed.
Forwarding¶
When a nonterminal is used in a send ports, it needs an implicit forwarding attribute to work, because only computed elements can be sent. Since the nonterminal itself should be sent, the generated attribute simply returns this nonterminal.
However, changing any token within the whole subtree or changing the structure of the subtree must trigger a new message, upon computation of the forwarding attribute, all tokens are "touched" (their getter is called). This way, the dependency tracking registers a dependency between structure and tokens to the attribute.
The attribute (as well as any other generated element) is prefixed with _ragconnect_
to avoid potential name conflicts with user-specified elements.
Implementation Hints¶
Debugging Tests and Finding Bugs¶
To help with finding errors/bugs when tests fail, there are several things to find the correct spot.
- Look closely. Analyze the error message closely, and possible any previous error message(s) that could have caused the test to fail.
- Focus on single error
- To only inspect one test, mark them with
@Tag("New")
and use the gradle task "newTests". - Use
Assumptions.assumeTrue(false);
to abort unneeded test cases early. - When editing RagConnect itself and force recreating source for the affected test, e.g.,
compileForwardingIncremental.outputs.upToDateWhen { false }
- Remember to undo all changes, once the bug is fixed.
- To only inspect one test, mark them with
- Activate logs. Activate logging in the
ragconnect
specification of the compile-task of the affected test:- Remember to remove those lines, once the bug is fixed.
task compile(type: RagConnectTest) {
ragconnect {
// ... other parameters ...
logReads = true
logWrites = true
logIncremental = true
}
// ... other tools ...
}
- Trace incremental events. Add a receiver right after creation of the root node (named
model
here)- This will output every event fired by the incremental evaluation engine.
- Remember to remove this line, once the bug is fixed.
model.trace().setReceiver(TestUtils::logEvent);
- Add log statements. As there will be quite some log output, add some identifying log statement (i.e., using
logger.fatal("---")
) right before the suspicious statement to inspect only the relevant log message after that.