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

Merge branch '34-feature-add-context-free-connect' into 'dev'

Resolve "Feature: Add context-free connect"

Closes #34

See merge request !22
parents c1cecf6a d760fa32
Branches
No related tags found
3 merge requests!39Version 1.1.0,!35Version 1.0.0,!22Resolve "Feature: Add context-free connect"
Pipeline #12155 passed
Showing
with 386 additions and 96 deletions
......@@ -29,13 +29,53 @@ A breakdown of the parts of that syntax:
As described above, it can be combined with `indexed`.
If used on its own, the incoming data is interpreted as a complete list and its elements will be appended to the current list.
- The `<Non-Terminal>[.<Target>["()"]]` notation describes the actual affected node.
- If the target is omitted, all nodes of that non-terminal type can be connected, irrespective of their context.
- If the target is omitted, all nodes of that non-terminal type can be connected, irrespective of their context. This is a context-free endpoint definition.
- The target can be any child on the right-hand side of a production rule, a role of a relation, or an attribute.
The brackets `()` after the target must be used in case of an attribute, and only then.
- Optionally, an endpoint can use one or more [mappings](#mappings).
They will be applied before sending, or after receiving a message.
Mappings will always be applied in the order they are listed after `using`.
### 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:
- The given non-terminal can be connected to in all contexts it occurs as if there were endpoints for all those contexts.
- There is a special method available on the given non-terminal to connect itself, which selects the correct connect-method depending on its context.
- Context-sensitive endpoints for this non-terminal can still be specified to modify mappings in this context. If the context is a list, the endpoint must use `indexed` and cannot use `with add`.
**Example**:
```java
// grammar
Root ::= A SingleA:A [OptA:A] ListA:A* ;
A ::= <Value> ;
// connect
receive A;
receive Root.SingleA using MyMapping; // specialized endpoint
```
Implied, additional connect specifications:
```java
receive Root.A;
receive Root.OptA;
receive indexed Root.ListA;
```
Application code:
```java
A a = root.getOptA();
// new method on A:
a.connect("<some-uri-to-connect>");
// equivalent to (implicitly generated):
root.connectOptA("<some-uri-to-connect>");
```
## Mappings
A mapping is a side effect-free function with one argument (the value that will be transformed) and one result (the transformed value), that will be applied on a value to be sent for a sending endpoint, a received value for a receiving endpoint, or the result of another mapping.
......
......@@ -12,6 +12,11 @@ aspect Analysis {
.filter(containingEndpointDefinition()::matchesType)
.count() > 1;
}
eq ContextFreeTypeEndpointTarget.isAlreadyDefined() {
return lookupContextFreeTypeEndpointDefinitions(getTypeDecl()).stream()
.filter(containingEndpointDefinition()::matchesType)
.count() > 1;
}
syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
// --- matchesType ---
......@@ -61,6 +66,7 @@ aspect Analysis {
syn boolean EndpointTarget.entityIsNormalAttribute();
eq TokenEndpointTarget.entityIsNormalAttribute() = !getToken().getNTA();
eq TypeEndpointTarget.entityIsNormalAttribute() = !getType().getNTA();
eq ContextFreeTypeEndpointTarget.entityIsNormalAttribute() = false;
// --- needProxyToken ---
syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointTargetList().stream().map(EndpointTarget::containingEndpointDefinition).anyMatch(EndpointDefinition::shouldSendValue);
......
......@@ -27,6 +27,16 @@ aspect Errors {
token().effectiveJavaTypeUse())
to RagConnect.errors();
ContextFreeTypeEndpointTarget contributes error("Context-Free endpoint not allowed for root node " +
getTypeDecl().getName() + "!")
when getTypeDecl().occurencesInProductionRules().isEmpty()
to RagConnect.errors();
EndpointDefinition contributes error("Clash with implied, indexed endpoint definition of context-free endpoint in line " +
clashingContextFreeEndpointDefinition().getStartLine() + "!")
when !getSend() && clashingContextFreeEndpointDefinition() != null
to RagConnect.errors();
DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
when isAlreadyDefined()
to RagConnect.errors();
......@@ -45,6 +55,18 @@ aspect ErrorHelpers {
}
return false;
}
syn EndpointDefinition EndpointDefinition.clashingContextFreeEndpointDefinition() {
if (getSend() || !typeIsList() || getIndexBasedListAccess()) {
return null;
}
List<EndpointDefinition> contextFreeEndpointsWithSameType = lookupContextFreeTypeEndpointDefinitions(
getEndpointTarget().asTypeEndpointTarget().getType().getTypeDecl());
if (!contextFreeEndpointsWithSameType.isEmpty()) {
return contextFreeEndpointsWithSameType.get(0);
}
return null;
}
}
aspect ErrorMessage {
......
......@@ -157,6 +157,14 @@ aspect MustacheMappingApplicationAndDefinition {
eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue();
eq MTypeSendDefinition.preemptiveReturn() = "return false;";
eq MContextFreeTypeReceiveDefinition.firstInputVarName() = "message";
eq MContextFreeTypeReceiveDefinition.preemptiveExpectedValue() = "this";
eq MContextFreeTypeReceiveDefinition.preemptiveReturn() = "return;";
eq MContextFreeTypeSendDefinition.firstInputVarName() = null;
eq MContextFreeTypeSendDefinition.preemptiveExpectedValue() = null;
eq MContextFreeTypeSendDefinition.preemptiveReturn() = null;
syn String MEndpointDefinition.parentTypeName() = getEndpointDefinition().parentTypeName();
syn String MEndpointDefinition.getterMethodName() = getEndpointDefinition().getterMethodName();
......@@ -174,9 +182,17 @@ aspect MustacheRagConnect {
syn List<EndpointDefinition> RagConnect.allEndpointDefinitionList() {
List<EndpointDefinition> result = new ArrayList<>();
for (ConnectSpecification spec : getConnectSpecificationFileList()) {
spec.getEndpointDefinitionList().forEach(result::add);
// first gather all user-defined endpoint definitions, that are not context-free
for (EndpointDefinition def : givenEndpointDefinitionList()) {
if (!def.hasContextFreeTypeEndpointTarget()) {
result.add(def);
}
}
// then check for additional endpoints, and add if no conflict with existing definitions exists
for (EndpointDefinition def : givenEndpointDefinitionList()) {
def.getEndpointTarget().impliedEndpointDefinitions().iterator().forEachRemaining(result::add);
}
return result;
}
......@@ -192,8 +208,54 @@ aspect MustacheRagConnect {
return result;
}
syn List<TypeDecl> RagConnect.typeDeclsOfContextFreeEndpointTargets() {
List<TypeDecl> result = new ArrayList<>();
for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isContextFreeTypeEndpointTarget()) {
result.add(target.asContextFreeTypeEndpointTarget().getTypeDecl());
}
}
return result;
}
// === MappingDefinition ===
syn boolean MappingDefinition.isUsed() = !effectiveUsedAt().isEmpty();
// === attributes needed for computing above ones ===
syn List<EndpointDefinition> RagConnect.givenEndpointDefinitionList() {
List<EndpointDefinition> result = new ArrayList<>();
for (ConnectSpecification spec : getConnectSpecificationFileList()) {
spec.getEndpointDefinitionList().forEach(result::add);
}
return result;
}
syn nta JastAddList<EndpointDefinition> EndpointTarget.impliedEndpointDefinitions() = new JastAddList<>();
eq ContextFreeTypeEndpointTarget.impliedEndpointDefinitions() {
JastAddList<EndpointDefinition> result = super.impliedEndpointDefinitions();
EndpointDefinition containingDef = containingEndpointDefinition();
for (TypeComponent typeComponent : getTypeDecl().occurencesInProductionRules()) {
List<EndpointDefinition> defsForTypeComponent = lookupTypeEndpointDefinitions(typeComponent);
if (!defsForTypeComponent.stream().anyMatch(containingDef::matchesType)) {
// there is no user-defined endpoint definition for this typeComponent yet
// -> create a new endpoint definition with the same options and mappings as the context-free def
// (except indexed-based for list-types)
EndpointDefinition newDef = new EndpointDefinition();
newDef.setAlwaysApply(containingDef.getAlwaysApply());
newDef.setIndexBasedListAccess(typeComponent.isListComponent());
newDef.setSend(containingDef.getSend());
containingDef.getMappings().forEach(newDef::addMapping);
TypeEndpointTarget target = new TypeEndpointTarget();
target.setType(typeComponent);
newDef.setEndpointTarget(target);
result.add(newDef);
}
}
return result;
}
}
aspect MustacheReceiveAndSendAndHandleUri {
......@@ -234,6 +296,10 @@ aspect MustacheReceiveAndSendAndHandleUri {
eq TypeEndpointTarget.parentTypeName() = getType().containingTypeDecl().getName();
eq TypeEndpointTarget.entityName() = getType().getName() + (typeIsList() && !containingEndpointDefinition().getIndexBasedListAccess() ? "List" : "");
eq ContextFreeTypeEndpointTarget.getterMethodName() = null;
eq ContextFreeTypeEndpointTarget.parentTypeName() = getTypeDecl().getName();
eq ContextFreeTypeEndpointTarget.entityName() = "";
}
aspect MustacheReceiveDefinition {
......@@ -241,6 +307,8 @@ aspect MustacheReceiveDefinition {
syn boolean RagConnect.configLoggingEnabledForReads() = getConfiguration().getLoggingEnabledForReads();
// === EndpointDefinition ===
syn boolean EndpointDefinition.hasContextFreeTypeEndpointTarget() = getEndpointTarget().isContextFreeTypeEndpointTarget();
syn boolean EndpointDefinition.hasTypeEndpointTarget() = getEndpointTarget().isTypeEndpointTarget();
syn String EndpointDefinition.idTokenName() = "InternalRagconnectTopicInList";
......@@ -281,26 +349,29 @@ aspect MustacheSendDefinition {
syn String EndpointTarget.senderName();
eq TokenEndpointTarget.senderName() = "_sender_" + getToken().getName();
eq TypeEndpointTarget.senderName() = "_sender_" + getType().getName();
eq ContextFreeTypeEndpointTarget.senderName() = null;
syn String MEndpointDefinition.updateMethodName();
syn String MEndpointDefinition.writeMethodName();
// MTokenReceiveDefinition
eq MTokenReceiveDefinition.updateMethodName() = null;
eq MTokenReceiveDefinition.writeMethodName() = null;
// MTokenSendDefinition
eq MTokenSendDefinition.updateMethodName() = "_update_" + tokenName();
eq MTokenSendDefinition.writeMethodName() = "_writeLastValue_" + tokenName();
// MTypeReceiveDefinition
eq MTypeReceiveDefinition.updateMethodName() = null;
eq MTypeReceiveDefinition.writeMethodName() = null;
// MTypeSendDefinition
eq MTypeSendDefinition.updateMethodName() = "_update_" + typeName();
eq MTypeSendDefinition.writeMethodName() = "_writeLastValue_" + typeName();
eq MContextFreeTypeReceiveDefinition.updateMethodName() = null;
eq MContextFreeTypeReceiveDefinition.writeMethodName() = null;
eq MContextFreeTypeSendDefinition.updateMethodName() = null;
eq MContextFreeTypeSendDefinition.writeMethodName() = null;
syn String EndpointDefinition.tokenName() = token().getName();
syn String MEndpointDefinition.tokenName() = getEndpointDefinition().tokenName();
......@@ -350,6 +421,25 @@ aspect MustacheTokenComponent {
// > see MustacheSend for updateMethodName, writeMethodName
}
aspect MustacheTypeDecl {
// === TypeComponent ===
syn String TypeComponent.parentTypeName() = containingTypeDecl().getName();
// === TypeDecl ===
syn List<TypeComponent> TypeDecl.occurencesInProductionRules() {
List<TypeComponent> result = new ArrayList<>();
for (TypeDecl typeDecl : program().typeDecls()) {
for (Component comp : typeDecl.getComponentList()) {
if (comp.isTypeComponent() && comp.asTypeComponent().getTypeDecl().equals(this)) {
result.add(comp.asTypeComponent());
}
}
}
return result;
}
}
aspect AttributesForMustache {
syn String MEndpointDefinition.lastValue() = getEndpointDefinition().lastValue();
......@@ -361,22 +451,7 @@ aspect AttributesForMustache {
syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
syn nta MEndpointDefinition EndpointDefinition.toMustache() {
final MEndpointDefinition result;
if (getEndpointTarget().isTokenEndpointTarget()) {
if (getSend()) {
result = new MTokenSendDefinition();
} else {
result = new MTokenReceiveDefinition();
}
} else if (getEndpointTarget().isTypeEndpointTarget()) {
if (getSend()) {
result = new MTypeSendDefinition();
} else {
result = new MTypeReceiveDefinition();
}
} else {
throw new RuntimeException("Unknown endpoint target type: " + getEndpointTarget());
}
final MEndpointDefinition result = getEndpointTarget().createMEndpointDefinition(getSend());
result.setEndpointDefinition(this);
for (MappingDefinition def : effectiveMappings()) {
MInnerMappingDefinition inner = new MInnerMappingDefinition();
......@@ -385,6 +460,20 @@ aspect AttributesForMustache {
}
return result;
}
abstract MEndpointDefinition EndpointTarget.createMEndpointDefinition(boolean isSend);
MEndpointDefinition TokenEndpointTarget.createMEndpointDefinition(boolean isSend) {
return isSend ? new MTokenSendDefinition() : new MTokenReceiveDefinition();
}
MEndpointDefinition TypeEndpointTarget.createMEndpointDefinition(boolean isSend) {
return isSend ? new MTypeSendDefinition() : new MTypeReceiveDefinition();
}
MEndpointDefinition ContextFreeTypeEndpointTarget.createMEndpointDefinition(boolean isSend) {
return isSend ? new MContextFreeTypeSendDefinition() : new MContextFreeTypeReceiveDefinition();
}
MEndpointDefinition UntypedEndpointTarget.createMEndpointDefinition(boolean isSend) {
throw new RuntimeException("Untyped endpoint target type, typeName= " +
getTypeName() + ", childName=" + getChildName());
}
}
aspect GrammarGeneration {
......
......@@ -7,6 +7,9 @@ MTokenSendDefinition : MTokenEndpointDefinition;
abstract MTypeEndpointDefinition : MEndpointDefinition;
MTypeReceiveDefinition : MTypeEndpointDefinition;
MTypeSendDefinition : MTypeEndpointDefinition;
abstract MContextFreeTypeEndpointDefinition : MEndpointDefinition;
MContextFreeTypeReceiveDefinition : MContextFreeTypeEndpointDefinition;
MContextFreeTypeSendDefinition : MContextFreeTypeEndpointDefinition;
MInnerMappingDefinition;
rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
......@@ -294,6 +294,7 @@ aspect Mappings {
syn String EndpointTarget.targetTypeName();
eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName();
eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName();
eq ContextFreeTypeEndpointTarget.targetTypeName() = getTypeDecl().getName();
// eq ReceiveFromRestDefinition.suitableDefaultMapping() {
// String typeName = getMappingList().isEmpty() ?
......
......@@ -5,7 +5,7 @@ aspect RagConnectNameResolution {
eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
for (EndpointTarget target : allEndpointTargetList()) {
for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) {
result.add(target.containingEndpointDefinition());
}
......@@ -19,7 +19,7 @@ aspect RagConnectNameResolution {
eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
for (EndpointTarget target : allEndpointTargetList()) {
for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) {
result.add(target.containingEndpointDefinition());
}
......@@ -27,6 +27,20 @@ aspect RagConnectNameResolution {
return result;
}
// --- lookupContextFreeTypeEndpointDefinition ---
inh java.util.List<EndpointDefinition> EndpointDefinition.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl);
inh java.util.List<EndpointDefinition> EndpointTarget.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl);
eq RagConnect.getConnectSpecificationFile().lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl) = lookupContextFreeTypeEndpointDefinitions(typeDecl);
syn java.util.List<EndpointDefinition> RagConnect.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl) {
java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isContextFreeTypeEndpointTarget() && target.asContextFreeTypeEndpointTarget().getTypeDecl().equals(typeDecl)) {
result.add(target.containingEndpointDefinition());
}
}
return result;
}
// --- lookupDependencyDefinition ---
inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id);
eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) {
......
......@@ -12,6 +12,12 @@ aspect NewStuff {
syn boolean EndpointTarget.isTypeEndpointTarget() = false;
eq TypeEndpointTarget.isTypeEndpointTarget() = true;
/** Tests if EndpointTarget is a ContextFreeTypeEndpointTarget.
* @return 'true' if this is a ContextFreeTypeEndpointTarget, otherwise 'false'
*/
syn boolean EndpointTarget.isContextFreeTypeEndpointTarget() = false;
eq ContextFreeTypeEndpointTarget.isContextFreeTypeEndpointTarget() = true;
/** Tests if EndpointTarget is a UntypedEndpointTarget.
* @return 'true' if this is a UntypedEndpointTarget, otherwise 'false'
*/
......@@ -32,6 +38,13 @@ aspect NewStuff {
eq EndpointTarget.asTypeEndpointTarget() = null;
eq TypeEndpointTarget.asTypeEndpointTarget() = this;
/** casts a EndpointTarget into a ContextFreeTypeEndpointTarget if possible.
* @return 'this' cast to a ContextFreeTypeEndpointTarget or 'null'
*/
syn ContextFreeTypeEndpointTarget EndpointTarget.asContextFreeTypeEndpointTarget();
eq EndpointTarget.asContextFreeTypeEndpointTarget() = null;
eq ContextFreeTypeEndpointTarget.asContextFreeTypeEndpointTarget() = this;
/** casts a EndpointTarget into a UntypedEndpointTarget if possible.
* @return 'this' cast to a UntypedEndpointTarget or 'null'
*/
......@@ -73,8 +86,8 @@ aspect RagConnectNavigation {
}
//--- allEndpointTargetList ---
syn List<EndpointTarget> RagConnect.allEndpointTargetList() {
//--- givenEndpointTargetList ---
syn List<EndpointTarget> RagConnect.givenEndpointTargetList() {
List<EndpointTarget> result = new ArrayList<>();
for (ConnectSpecification spec : getConnectSpecificationFileList()) {
spec.getEndpointDefinitionList().forEach(endpointDef -> result.add(endpointDef.getEndpointTarget()));
......
......@@ -11,7 +11,9 @@ TokenEndpointTarget : EndpointTarget;
rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*;
TypeEndpointTarget : EndpointTarget;
rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*;
UntypedEndpointTarget : EndpointTarget ::= <TokenOrType>; // only used by parser
ContextFreeTypeEndpointTarget : EndpointTarget;
rel ContextFreeTypeEndpointTarget.TypeDecl <-> TypeDecl.ContextFreeTypeEndpointTarget?;
UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName>; // only used by parser
// to be integrated:
//AttributeEndpointTarget : EndpointTarget ::= <Name> ;
//RelationEndpointTarget : EndpointTarget ;
......
aspect ParserRewrites {
rewrite UntypedEndpointTarget {
when (tryGloballyResolveTypeComponentByToken(getTokenOrType()) != null)
when (getChildName() != null && tryGloballyResolveTypeComponentByToken(combinedName()) != null)
to TypeEndpointTarget {
TypeEndpointTarget result = new TypeEndpointTarget();
result.setType(TypeComponent.createRef(this.getTokenOrType()));
result.copyOtherValuesFrom(this);
result.setType(TypeComponent.createRef(this.combinedName()));
return result;
}
when (tryGloballyResolveTokenComponentByToken(getTokenOrType()) != null)
when (getChildName() != null && tryGloballyResolveTokenComponentByToken(combinedName()) != null)
to TokenEndpointTarget {
TokenEndpointTarget result = new TokenEndpointTarget();
result.setToken(TokenComponent.createRef(this.getTokenOrType()));
result.copyOtherValuesFrom(this);
result.setToken(TokenComponent.createRef(this.combinedName()));
return result;
}
when (getChildName() == "")
to ContextFreeTypeEndpointTarget {
ContextFreeTypeEndpointTarget result = new ContextFreeTypeEndpointTarget();
result.copyOtherValuesFrom(this);
result.setTypeDecl(TypeDecl.createRef(getTypeName()));
return result;
}
}
syn String UntypedEndpointTarget.combinedName() = getTypeName() + "." + getChildName();
protected void EndpointTarget.copyOtherValuesFrom(EndpointTarget source) {
this.setStart(source.getStartLine(), source.getStartColumn());
this.setEnd(source.getEndLine(), source.getEndColumn());
}
eq UntypedEndpointTarget.senderName() = "<untyped.senderName>";
eq UntypedEndpointTarget.getterMethodName() = "<untyped.getterMethodName>";
eq UntypedEndpointTarget.parentTypeName() = "<untyped.parentTypeName>";
......
......@@ -27,13 +27,13 @@ ConnectSpecificationFile connect_specification_file
// return def;
// }
private EndpointDefinition createEndpointDefinition(
String type_name, String child_name, boolean send,
EndpointTarget endpointTarget, boolean send,
boolean indexBasedListAccess, boolean withAdd) {
EndpointDefinition result = new EndpointDefinition();
result.setSend(send);
result.setIndexBasedListAccess(indexBasedListAccess);
result.setWithAdd(withAdd);
result.setEndpointTarget(new UntypedEndpointTarget(type_name + "." + child_name));
result.setEndpointTarget(endpointTarget);
return result;
}
:} ;
......@@ -53,30 +53,16 @@ EndpointDefinition endpoint_definition
;
EndpointDefinition endpoint_definition_type
= SEND ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, true, false, false);
:}
// | SEND INDEXED ID.type_name DOT ID.child_name
// {:
// return createEndpointDefinition(type_name, child_name, true, true, false);
// :}
| RECEIVE ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, false, false, false);
:}
| RECEIVE INDEXED ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, false, true, false);
:}
| RECEIVE WITH ADD ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, false, false, true);
:}
| RECEIVE INDEXED WITH ADD ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, false, true, true);
:}
= SEND endpoint_target.t {: return createEndpointDefinition(t, true, false, false); :}
| RECEIVE endpoint_target.t {: return createEndpointDefinition(t, false, false, false); :}
| RECEIVE INDEXED endpoint_target.t {: return createEndpointDefinition(t, false, true, false); :}
| RECEIVE WITH ADD endpoint_target.t {: return createEndpointDefinition(t, false, false, true ); :}
| RECEIVE INDEXED WITH ADD endpoint_target.t {: return createEndpointDefinition(t, false, true, true ); :}
;
EndpointTarget endpoint_target
= ID.type_name DOT ID.child_name {: return new UntypedEndpointTarget(type_name, child_name); :}
| ID.type_name {: return new UntypedEndpointTarget(type_name, ""); :}
;
ArrayList string_list
......
......@@ -28,6 +28,10 @@ aspect RagConnect {
{{> tokenComponent}}
{{/tokenComponentsThatNeedProxy}}
{{#typeDeclsOfContextFreeEndpointTargets}}
{{> typeDecl}}
{{/typeDeclsOfContextFreeEndpointTargets}}
{{> ListAspect}}
public void {{rootNodeName}}.ragconnectCheckIncremental() {
......
uncache {{Name}}._ragconnect_myContext();
inh String {{Name}}._ragconnect_myContext();
inh ASTNode {{Name}}._ragconnect_myParent();
uncache {{Name}}._ragconnect_myIndexInList();
inh int {{Name}}._ragconnect_myIndexInList();
{{#occurencesInProductionRules}}
eq {{parentTypeName}}.get{{Name}}()._ragconnect_myContext() = "{{parentTypeName}}.{{Name}}";
eq {{parentTypeName}}.get{{Name}}()._ragconnect_myParent() = this;
{{^isListComponent}}
eq {{parentTypeName}}.get{{Name}}()._ragconnect_myIndexInList() = -1;
{{/isListComponent}}
{{#isListComponent}}
eq {{parentTypeName}}.get{{Name}}(int i)._ragconnect_myIndexInList() = i;
{{/isListComponent}}
{{/occurencesInProductionRules}}
{{#ContextFreeTypeEndpointTarget}}{{#containingEndpointDefinition}}
public boolean {{Name}}.{{connectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
switch (_ragconnect_myContext()) {
{{#occurencesInProductionRules}}
{{!only using "connectMethodName" is not correct, since the actual name might be different, e.g., if both send and receive are defined. need a reference to the actual endpoint-definition here}}
{{^isListComponent}}
case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}});
{{/isListComponent}}
{{#isListComponent}}
case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}}, _ragconnect_myIndexInList());
{{/isListComponent}}
{{/occurencesInProductionRules}}
default:
System.err.println("No matching context while connecting " + this + " to " + {{connectParameterName}});
return false;
}
}
public boolean {{Name}}.{{disconnectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
switch (_ragconnect_myContext()) {
{{#occurencesInProductionRules}}
case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{disconnectMethodName}}{{Name}}({{connectParameterName}});
{{/occurencesInProductionRules}}
default:
System.err.println("No matching context while disconnecting for " + this + " from " + {{connectParameterName}});
return false;
}
}
{{/containingEndpointDefinition}}{{/ContextFreeTypeEndpointTarget}}
......@@ -112,6 +112,15 @@ task specificTest(type: Test, dependsOn: testClasses) {
}
}
task newTests(type: Test, dependsOn: testClasses) {
description = 'Run test tagged with tag "New"'
group = 'verification'
useJUnitPlatform {
includeTags 'New'
}
}
preprocessorTesting {
//noinspection GroovyAssignabilityCheck
relastCompilerLocation = '../libs/relast.jar'
......@@ -556,21 +565,27 @@ task compileSingleListVariantIncremental(type: RagConnectTest, dependsOn: ':ragc
}
}
task cleanCurrentManualTest(type: Delete) {
// delete "src/test/02-after-ragconnect/singleListVariant"
// delete "src/test/03-after-relast/singleListVariant"
// delete "src/test/java-gen/singleListVariant/ast"
delete "src/test/02-after-ragconnect/singleList"
delete "src/test/03-after-relast/singleList"
delete "src/test/java-gen/singleList/ast"
// --- Test: contextFreeSimple-incremental ---
task compileContextFreeSimpleIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') {
ragconnect {
outputDir = file('src/test/02-after-ragconnect/contextFreeSimpleInc')
inputFiles = [file('src/test/01-input/contextFreeSimple/Test.relast'),
file('src/test/01-input/contextFreeSimple/Test.connect')]
rootNode = 'Root'
extraOptions = ['--experimental-jastadd-329']
}
relast {
useJastAddNames = true
grammarName = 'src/test/03-after-relast/contextFreeSimpleInc/contextFreeSimpleInc'
serializer = 'jackson'
}
jastadd {
jastAddList = 'JastAddList'
packageName = 'contextFreeSimpleInc.ast'
extraOptions = ['--tracing=cache,flush',
'--incremental=param',
'--cache=all',
'--rewrite=cnta',
'--flush=full']
}
task cleanCurrentIncrementalTest(type: Delete) {
// delete "src/test/02-after-ragconnect/singleListVariantInc"
// delete "src/test/03-after-relast/singleListVariantInc"
// delete "src/test/java-gen/singleListVariantInc/ast"
delete "src/test/02-after-ragconnect/singleListInc"
delete "src/test/03-after-relast/singleListInc"
delete "src/test/java-gen/singleListInc/ast"
}
compileSingleListManual.dependsOn cleanCurrentManualTest
compileSingleListIncremental.dependsOn cleanCurrentIncrementalTest
# ContextFree-Simple
Idea: Use only context-free context, in simple situation (non-recursive)
receive A;
//"receive Root.A;" is implied
receive Root.SingleA using PrependPrefix;
receive Root.OptA using AddSuffix;
//"receive Root.ListA;" would clash as "receive indexed Root.ListA" is implied
PrependPrefix maps A a to A {:
A result = new A();
result.setValue("pre" + a.getValue());
return result;
:}
AddSuffix maps A a to A {:
A result = new A();
result.setValue(a.getValue() + "post");
return result;
:}
Root ::= A SingleA:A [OptA:A] ListA:A* ;
A ::= <Value> ;
A ::= B C D ;
A ::= B C D E* ;
// read definitions
B ::= /<ErrorNTA:String>/ <ErrorTypeOfFirstMapping:String> <ErrorTypeOfLastMapping:String> <DoubledValue:int> <ErrorTypeMismatch:String> ;
// write definitions
C ::= <ErrorNotNTA:String> /<ErrorTypeOfFirstMapping:String>/ /<ErrorTypeOfLastMapping1:String>/ /<ErrorTypeOfLastMapping2:List<String>>/ /<ErrorTypeMismatch:String>/ /<DoubledValue:int>/ ;
C ::= /<ErrorTypeOfFirstMapping:String>/ /<ErrorTypeOfLastMapping1:String>/ /<ErrorTypeOfLastMapping2:List<String>>/ /<ErrorTypeMismatch:String>/ /<DoubledValue:int>/ ;
// dependency definitions
D ::= <SourceNonExistingTarget>
......@@ -13,3 +13,6 @@ D ::= <SourceNonExistingTarget>
<SourceSameAsListNode> /<TargetSameAsListNode>/
<SourceDoubledValue> /<TargetDoubledValue>/
MyList:D* ;
// context-free endpoints
E ::= ;
// --- update receive definitions ---
// --- receive definitions ---
// Error: there must not be two receive definitions for the same token
receive B.DoubledValue ;
receive B.DoubledValue using IntToInt ;
......@@ -18,22 +18,26 @@ receive B.ErrorTypeOfLastMapping using StringToList ;
// Error: types of mappings must match (modulo inheritance)
receive B.ErrorTypeMismatch using StringToList, IntToInt ;
// --- update send definitions ---
// Error: Context-Free endpoint not allowed for root nodes
receive A;
// Error: Clash with implied endpoint definition of context-free endpoint
receive E;
receive A.E;
// --- send definitions ---
// NOT HANDLED \\ Error: the token must be resolvable within the parent type
// NOT HANDLED \\ receive C.NonExisting ;
// Error: Token must be a TokenNTA (i.e., check for Token.getNTA())
send C.ErrorNotNTA ;
// NOT HANDLED \\ // Error: from-type of first mapping must be type of Token
// NOT HANDLED \\ send C.ErrorTypeOfFirstMapping using IntToInt ;
// Error: from-type of first mapping must be type of Token
send C.ErrorTypeOfFirstMapping using IntToInt ;
// NOT HANDLED \\ // Error: to-type of last mapping must be byte[] or a supported primitive type
// NOT HANDLED \\ send C.ErrorTypeOfLastMapping1 using StringToList ;
// NOT HANDLED \\ send C.ErrorTypeOfLastMapping2 ;
// Error: to-type of last mapping must be byte[] or a supported primitive type
send C.ErrorTypeOfLastMapping1 using StringToList ;
send C.ErrorTypeOfLastMapping2 ;
// Error: types of mappings must match (modulo inheritance)
send C.ErrorTypeMismatch using StringToList, IntToInt ;
// NOT HANDLED \\ // Error: types of mappings must match (modulo inheritance)
// NOT HANDLED \\ send C.ErrorTypeMismatch using StringToList, IntToInt ;
// Error: no more than one send mapping for each TokenComponent
send C.DoubledValue ;
......@@ -44,8 +48,8 @@ send C.DoubledValue using IntToInt ;
// NOT HANDLED \\ D.SourceNonExistingTarget canDependOn D.NonExisting as NonExistingTarget ;
// NOT HANDLED \\ D.NonExisting canDependOn D.TargetNonExistingSource as NonExistingSource ;
// Error: There must be a send update definition for the target token
D.SourceNoWriteDef canDependOn D.TargetNoWriteDef as NoWriteDef ;
// NOT HANDLED \\ // Error: There must be a send update definition for the target token
// NOT HANDLED \\ D.SourceNoWriteDef canDependOn D.TargetNoWriteDef as NoWriteDef ;
// Error: The name of a dependency definition must not be equal to a list-node on the source
D.SourceSameAsListNode canDependOn D.TargetSameAsListNode as MyList ;
......
......@@ -5,7 +5,9 @@ Standard.connect Line 13, column 1: No suitable default mapping found for type j
Standard.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the token (String)!
Standard.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the token (String)!
Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
Standard.connect Line 39, column 1: Endpoint definition already defined for C.DoubledValue
Standard.connect Line 40, column 1: Endpoint definition already defined for C.DoubledValue
Standard.connect Line 51, column 1: The name of a dependency definition must not be equal to a list-node on the source
Standard.connect Line 56, column 1: Dependency definition already defined for D with name DoubledValue
Standard.connect Line 22, column 9: Context-Free endpoint not allowed for root node A!
Standard.connect Line 26, column 1: Clash with implied, indexed endpoint definition of context-free endpoint in line 25!
Standard.connect Line 43, column 1: Endpoint definition already defined for C.DoubledValue
Standard.connect Line 44, column 1: Endpoint definition already defined for C.DoubledValue
Standard.connect Line 55, column 1: The name of a dependency definition must not be equal to a list-node on the source
Standard.connect Line 60, column 1: Dependency definition already defined for D with name DoubledValue
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment