Skip to content
Snippets Groups Projects
Commit 99122e3b authored by René Schöne's avatar René Schöne
Browse files

working on forwarding using implicit NTAs

- add some documentation, updated old examples to also contain indexedSend and forwarding
parent 7ed4e252
No related branches found
No related tags found
1 merge request!24Resolve "Feature: Send enpoint for non-terminal using implicit NTA"
Pipeline #12428 passed
......@@ -38,9 +38,6 @@ A breakdown of the parts of that syntax:
### Context-Free Endpoints
!!! attention
Context-Free endpoints are currently only supported for receiving endpoints.
An endpoint with only a non-terminal and without a target is called context-free endpoint.
Specifying such an endpoint has several consequences:
......
......@@ -19,3 +19,17 @@ One of the main aspects is `Intermediate` containing all attributes consumed by
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 endpoints, 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.
......@@ -4,14 +4,14 @@ The full example is available at <https://git-st.inf.tu-dresden.de/jastadd/ragco
## Preparation and Specification
The following examples are inspired by the real test case [read1write2](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests/-/tree/master/ragconnect.tests/src/test/01-input/read1write2)
The following examples are inspired by real [test cases](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests/-/tree/master/ragconnect.tests/src/test/01-input) read1write2 and tokenValueSend.
The idea is to have two non-terminals, where input information is received on one of them, and - after transformation - is sent out by both.
Let's use the following grammar:
```
A ::= <Input:String> /<OutputOnA:String>/ B* ;
B ::= /<OutputOnB:String>/ ;
B ::= <OutputOnB:String> ;
```
To declare receiving and sending tokens, a dedicated DSL is used:
......@@ -35,28 +35,15 @@ Such mapping definitions can be defined for receiving tokens as well.
In this case, they are applied before the value is set.
If no mapping definition is given, or if the required type (depending on the communication protocol, see later) does not match, a "default mapping definition" is used to avoid boilerplate code converting from or to primitive types.
Furthermore, let the following attribute definitions be given:
Furthermore, let the following attribute definition be given:
```java
syn String A.getOutputOnA() = "a" + getInput();
syn String B.getOutputOnB() = "b" + input();
inh String B.input();
eq A.getB().input() = getInput();
```
In other words, `OutputOnA` depends on `Input` of the same node, and `OutputOnB` depends on `Input` of its parent node.
Currently, those dependencies can be explicitly written down, or incremental evaluation can be used.
### Dependency tracking: Manually specified
This specification happens also in the DSL (dependencies have to be named to uniquely identify them):
```java
// dependency definitions
A.OutputOnA canDependOn A.Input as dependencyA ;
B.OutputOnB canDependOn A.Input as dependencyB ;
```
In other words, `OutputOnA` depends on `Input` of the same node.
This dependency is automatically inferred, if incremental evaluation is used.
Otherwise, the deprecated manual dependencies must be used.
### Dependency tracking: Automatically derived
......@@ -68,6 +55,15 @@ The value for `trace` can include other values besides `flush`.
An experimental, optimized version can be selected using `--experimental-jastadd-329` reducing the risk of conflicts between concurrent attribute evaluations.
However, this requires a version of JastAdd that resolved the [issue 329](https://bitbucket.org/jastadd/jastadd2/issues/329/add-event-for-completion-of-flush).
### Deprecated Manual Dependency Specification
Specification happens also in the DSL (dependencies have to be named to uniquely identify them):
```java
// dependency definition
A.OutputOnA canDependOn A.Input as dependencyA ;
```
## Using generated code
After specifying everything, code will be generated if [setup properly](/adding).
......@@ -83,15 +79,11 @@ a.addB(b1);
a.addB(b2);
```
If necessary, we have to set the dependencies as [described earlier](#dependency-tracking-manually-specified).
If necessary, we have to set the dependencies as [described earlier](#deprecated-manual-dependency-specification).
```java
// a.OutputOnA -> a.Input
a.addDependencyA(a);
// b1.OutputOnB -> a.Input
b1.addDependencyB(a);
// b2.OutputOnB -> a.Input
b2.addDependencyB(a);
```
Finally, we can actually _connect_ the tokens.
......@@ -144,24 +136,24 @@ Non-terminal children can also be selected as endpoints (not only tokens).
Receiving normal non-terminal children and optionals means to replace them with a new node deserialized from the received message.
Sending them involves serializing a node, and sending this representation in a message.
Suppose, the following (shortened) grammar is used (inspired from the testcase [tree](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests/-/tree/master/ragconnect.tests/src/test/01-input/tree))
Suppose, the following (shortened) grammar is used (inspired from the testcase tree and forwarding)
```
Root ::= SenderRoot ReceiverRoot ;
SenderRoot ::= <Input:int> /Alfa/ ;
ReceiverRoot ::= Alfa ;
Alfa ::= // some content ...
SenderRoot ::= <Input:int> /A/ B ;
ReceiverRoot ::= A ;
A ::= // some content ...
B ::= <Value> ;
```
Now, the complete node of type `Alfa` can be sent, and received again using the following connect specification:
Now, the complete node of types `A` and `B` can be sent, and received again using the following connect specification:
```
send tree SenderRoot.Alfa ;
receive tree ReceiverRoot.Alfa ;
send SenderRoot.A ;
send SenderRoot.B ;
receive ReceiverRoot.A ;
```
Currently, receiving and sending trees requires the explicit demarcation from tokens using the keyword `tree`.
To process non-terminals, default mappings are provided for every non-terminal type of the used grammar.
They use the JSON serialization offered by the RelAST compiler, i.e., interpret the message as a `String`, deserialize the content reading the message as JSON, or vice versa.
Additional dependencies are required to use this feature, as detailed in [the compiler section](/compiler#treelist-endpoints).
......@@ -173,82 +165,82 @@ When receiving list children, there are a few more options to match the connecti
Suppose we use a similar grammar as above, i.e.:
```
SenderRoot ::= /AlfaList:Alfa*/ /SingleAlfa:Alfa/;
ReceiverRoot ::= Alfa* ;
SenderRoot ::= /AList:A*/ /SingleA:A/;
ReceiverRoot ::= A* ;
```
Several options are possible:
Several options are possible (please also refer to the specification of the [connect DSL](/dsl):
### list
### (empty)
A message for a list endpoint can be interpreted as a complete list (a sequence of nodes of type `Alfa`) by using the `list` keyword instead of `tree`:
A message for a list endpoint can be interpreted as a complete list (a sequence of nodes of type `A`) by not specifying any special keyword:
```
receive list ReceiverRoot.Alfa ;
receive ReceiverRoot.A ;
```
### list + with add
### with add
Upon receiving the message, the deserialized list can also be appended to the existing list instead of replace the latter.
This can be achieved using the keyword `with add` in addition to the keyword `list`:
This can be achieved using the keyword `with add` :
```
receive list with add ReceiverRoot.Alfa ;
receive with add ReceiverRoot.Alfa ;
```
### tree (indexed)
### indexed
A message for a list endpoint can also be interpreted as an element of this list.
```
receive tree ReceiverRoot.Alfa ;
receive tree ReceiverRoot.A ;
```
Upon connection, the index of the deserialized element to set, has to be passed (`1` in the example below).
The list must have enough elements once a message is received.
```java
receiverRoot.connectAlfa("<some-url>", 1);
receiverRoot.connectA("<some-url>", 1);
```
### tree (wildcard)
### indexed (wildcard)
Similar to the `tree (indexed)` case above, messages are interpreted as an element of the list, but the connection can also be made using a "wildcard topic" and without an index.
Similar to the `indexed` case above, messages are interpreted as an element of the list, but the connection can also be made using a "wildcard topic" and without an index.
Then, once a message is received from a new concrete topic, the deserialized element will be appended to the list and this topic is associated with the index of the newly added element.
Any further message from that topic will replace the element at the associated index.
In the short example below, MQTT is used to with a wildcard topic, as `#` matches every sub-topic.
```java
receiverRoot.connectAlfa("mqtt://<broker>/some/topic/#");
receiverRoot.connectA("mqtt://<broker>/some/topic/#");
// list is initially empty
assertEquals(receiverRoot.getAlfaList(), list());
assertEquals(receiverRoot.getAList(), list());
// after receiving "1" on new topic "some/topic/one" (index 0)
assertEquals(receiverRoot.getAlfaList(), list("1"));
assertEquals(receiverRoot.getAList(), list("1"));
// after receiving "other" on new topic "some/topic/two" (index 1)
assertEquals(receiverRoot.getAlfaList(), list("1", "other"));
assertEquals(receiverRoot.getAList(), list("1", "other"));
// after receiving "new" on existing topic "some/topic/one" (index 0)
assertEquals(receiverRoot.getAlfaList(), list("new", "other"));
assertEquals(receiverRoot.getAList(), list("new", "other"));
```
### tree (indexed/wildcard) + with add
### indexed + with add
Combining `tree` and `with add` results in a connection, where messages are interpreted as elements of the list, and new elements are appended to the existing list.
Combining `indexed` and `with add` results in a connection, where messages are interpreted as elements of the list, and new elements are appended to the existing list.
In that case, wildcard and non-wildcard connections behave in the same way, as no index has to be passed, and the element is always append at the end.
Reusing the example from above, the following observations can be made.
```java
receiverRoot.connectAlfa("mqtt://<broker>/some/topic/#");
receiverRoot.connectA("mqtt://<broker>/some/topic/#");
// or
receiverRoot.connectAlfa("mqtt://<broker>/some/topic/one");
receiverRoot.connectAlfa("mqtt://<broker>/some/topic/two");
receiverRoot.connectA("mqtt://<broker>/some/topic/one");
receiverRoot.connectA("mqtt://<broker>/some/topic/two");
// list is initially empty
assertEquals(receiverRoot.getAlfaList(), list());
assertEquals(receiverRoot.getAList(), list());
// after receiving "1" on topic "some/topic/one"
assertEquals(receiverRoot.getAlfaList(), list("1"));
assertEquals(receiverRoot.getAList(), list("1"));
// after receiving "other" on topic "some/topic/two"
assertEquals(receiverRoot.getAlfaList(), list("1", "other"));
assertEquals(receiverRoot.getAList(), list("1", "other"));
// after receiving "new" on topic "some/topic/one"
assertEquals(receiverRoot.getAlfaList(), list("1", "other", "new"));
assertEquals(receiverRoot.getAList(), list("1", "other", "new"));
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment