From 2e0b5b388343fe850e75d3f0bdb8436548e42b47 Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Thu, 20 Jan 2022 17:28:07 +0100
Subject: [PATCH] WIP: begin with attribute as endpoint target

---
 .../src/main/jastadd/Analysis.jrag            |  1 +
 .../src/main/jastadd/Intermediate.jadd        |  8 ++--
 .../src/main/jastadd/Mappings.jrag            |  3 ++
 .../src/main/jastadd/Navigation.jrag          |  1 +
 .../src/main/jastadd/RagConnect.relast        |  5 +-
 .../main/jastadd/parser/ParserRewrites.jrag   | 13 +++++
 .../src/main/jastadd/parser/RagConnect.parser |  6 ++-
 .../src/main/jastadd/scanner/Keywords.flex    |  2 +
 ragconnect.tests/build.gradle                 | 26 ++++++++++
 .../src/test/01-input/attribute/README.md     |  3 ++
 .../src/test/01-input/attribute/Test.connect  | 31 ++++++++++++
 .../src/test/01-input/attribute/Test.jadd     | 39 +++++++++++++++
 .../src/test/01-input/attribute/Test.relast   | 14 ++++++
 .../ragconnect/tests/AttributeTest.java       | 48 +++++++++++++++++++
 14 files changed, 193 insertions(+), 7 deletions(-)
 create mode 100644 ragconnect.tests/src/test/01-input/attribute/README.md
 create mode 100644 ragconnect.tests/src/test/01-input/attribute/Test.connect
 create mode 100644 ragconnect.tests/src/test/01-input/attribute/Test.jadd
 create mode 100644 ragconnect.tests/src/test/01-input/attribute/Test.relast
 create mode 100644 ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java

diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index 85ff38d..b48146f 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -64,6 +64,7 @@ aspect Analysis {
   }
 
   syn boolean EndpointTarget.entityIsNormalAttribute();
+  // TODO AttributeEndpointTarget.entityIsNormalAttribute
   eq TokenEndpointTarget.entityIsNormalAttribute() = !getToken().getNTA();
   eq TypeEndpointTarget.entityIsNormalAttribute() = !getType().getNTA();
   eq ContextFreeTypeEndpointTarget.entityIsNormalAttribute() = false;
diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd
index bcd4cc4..b215f18 100644
--- a/ragconnect.base/src/main/jastadd/Intermediate.jadd
+++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd
@@ -371,6 +371,10 @@ aspect MustacheReceiveAndSendAndHandleUri {
   syn String EndpointTarget.parentTypeName();
   syn String EndpointTarget.entityName();
 
+  // TODO AttributeEndpointTarget.getterName
+  eq AttributeEndpointTarget.parentTypeName() = getParentTypeDecl().getName();
+  eq AttributeEndpointTarget.entityName() = getName();
+
   eq TokenEndpointTarget.getterMethodName() = "get" + getToken().getName();
   eq TokenEndpointTarget.parentTypeName() = getToken().containingTypeDecl().getName();
   eq TokenEndpointTarget.entityName() = getToken().getName();
@@ -456,9 +460,7 @@ aspect MustacheSendDefinition {
           getTypeDecl().getName() :
           ragconnect().configJastAddList() + "<" + getTypeDecl().getName() + ">";
 
-  syn String EndpointTarget.senderName();
-  eq TokenEndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + getToken().getName();
-  eq TypeEndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + getType().getName();
+  syn String EndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + entityName();
   eq ContextFreeTypeEndpointTarget.senderName() = null;
 
   syn String MEndpointDefinition.updateMethodName();
diff --git a/ragconnect.base/src/main/jastadd/Mappings.jrag b/ragconnect.base/src/main/jastadd/Mappings.jrag
index 738028b..fb7c296 100644
--- a/ragconnect.base/src/main/jastadd/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/Mappings.jrag
@@ -191,6 +191,7 @@ aspect Mappings {
 
   // --- suitableReceiveDefaultMapping ---
   syn DefaultMappingDefinition EndpointDefinition.suitableReceiveDefaultMapping() {
+    // TODO might be expanded to AttributeEndpointTarget
     if (getEndpointTarget().isTypeEndpointTarget()) {
       try {
         TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
@@ -236,6 +237,7 @@ aspect Mappings {
 
   // --- suitableSendDefaultMapping ---
   syn DefaultMappingDefinition EndpointDefinition.suitableSendDefaultMapping() {
+    // TODO might be expanded to AttributeEndpointTarget
     if (getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess()) {
       return ragconnect().defaultListTreeToBytesMapping();
     }
@@ -288,6 +290,7 @@ aspect Mappings {
     }
   }
   syn String EndpointTarget.targetTypeName();
+  eq AttributeEndpointTarget.targetTypeName() = getTypeName();
   eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName();
   eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName();
   eq ContextFreeTypeEndpointTarget.targetTypeName() = getTypeDecl().getName();
diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag
index a65ab72..260ed55 100644
--- a/ragconnect.base/src/main/jastadd/Navigation.jrag
+++ b/ragconnect.base/src/main/jastadd/Navigation.jrag
@@ -1,5 +1,6 @@
 aspect NewStuff {
 
+  // TODO regenerate for AttributeEndpointTarget
   /** Tests if EndpointTarget is a TokenEndpointTarget.
   *  @return 'true' if this is a TokenEndpointTarget, otherwise 'false'
   */
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index 01a0f2f..f6cf0dd 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -13,9 +13,10 @@ TypeEndpointTarget : EndpointTarget;
 rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*;
 ContextFreeTypeEndpointTarget : EndpointTarget;
 rel ContextFreeTypeEndpointTarget.TypeDecl <-> TypeDecl.ContextFreeTypeEndpointTarget*;
-UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName>;  // only used by parser
+UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName> <IsAttribute:boolean>;  // only used by parser
 // to be integrated:
-//AttributeEndpointTarget : EndpointTarget ::= <Name> ;
+AttributeEndpointTarget : EndpointTarget ::= <Name> <TypeName> ;
+rel AttributeEndpointTarget.ParentTypeDecl <-> TypeDecl.AttributeEndpointTarget*;
 //RelationEndpointTarget : EndpointTarget ;
 //rel RelationEndpointTarget.Role <-> Role.RelationEndpointTarget* ;
 
diff --git a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
index f4a8912..3de9341 100644
--- a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
+++ b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
@@ -23,6 +23,19 @@ aspect ParserRewrites {
       result.setTypeDecl(TypeDecl.createRef(getTypeName()));
       return result;
     }
+
+    when (getIsAttribute())
+    to AttributeEndpointTarget {
+      AttributeEndpointTarget result = new AttributeEndpointTarget();
+      String[] tokens = this.getChildName().split(":");
+      String attributeName = tokens[0];
+      String attributeTypeName = tokens[1];
+      result.copyOtherValuesFrom(this);
+      result.setName(attributeName);
+      result.setTypeName(attributeTypeName);
+      result.setParentTypeDecl(TypeDecl.createRef(getTypeName()));
+      return result;
+    }
   }
 
   syn String UntypedEndpointTarget.combinedName() = getTypeName() + "." + getChildName();
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index 54a388e..dcd55d2 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -62,8 +62,10 @@ EndpointDefinition endpoint_definition_type
 ;
 
 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, ""); :}
+  = ID.type_name DOT ID.child_name    {: return new UntypedEndpointTarget(type_name, child_name, false); :}
+  | ID.type_name DOT ID.child_name BRACKETS COLON ID.attribute_type_name
+     {: return new UntypedEndpointTarget(type_name, child_name + ":" + attribute_type_name, true); :}
+  | ID.type_name                      {: return new UntypedEndpointTarget(type_name, "", false); :}
 ;
 
 ArrayList string_list
diff --git a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
index 8a1eaec..ffa057b 100644
--- a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
+++ b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
@@ -8,3 +8,5 @@
 "with"       { return sym(Terminals.WITH); }
 "indexed"    { return sym(Terminals.INDEXED); }
 "add"        { return sym(Terminals.ADD); }
+"()"         { return sym(Terminals.BRACKETS); }
+":"          { return sym(Terminals.COLON); }
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 8d073b5..b545b19 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -621,3 +621,29 @@ task compileIndexedSendIncremental(type: RagConnectTest, dependsOn: ':ragconnect
         extraOptions = JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL
     }
 }
+
+// --- Test: attribute-incremental ---
+task compileAttributeIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/attributeInc')
+        inputFiles = [file('src/test/01-input/attribute/Test.relast'),
+                      file('src/test/01-input/attribute/Test.connect')]
+        rootNode = 'Root'
+        logWrites = true
+        logReads = true
+        logIncremental = true
+        extraOptions = ['--experimental-jastadd-329']
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/attributeInc/attributeInc'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'attributeInc.ast'
+        inputFiles = [file('src/test/01-input/attribute/Test.jadd')]
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL
+    }
+}
+compileAttributeIncremental.outputs.upToDateWhen { false }
diff --git a/ragconnect.tests/src/test/01-input/attribute/README.md b/ragconnect.tests/src/test/01-input/attribute/README.md
new file mode 100644
index 0000000..08481b7
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/attribute/README.md
@@ -0,0 +1,3 @@
+# Attribute
+
+Idea: Use send definitions for attributes.
diff --git a/ragconnect.tests/src/test/01-input/attribute/Test.connect b/ragconnect.tests/src/test/01-input/attribute/Test.connect
new file mode 100644
index 0000000..3e6a66e
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/attribute/Test.connect
@@ -0,0 +1,31 @@
+send SenderRoot.basic():String ;
+send SenderRoot.simple():String ;
+send SenderRoot.transformed():int ;
+send SenderRoot.toReferenceType():A ;
+send SenderRoot.toNTA():A ;
+
+AddSuffix maps A a to A {:
+  A result = new A();
+  String changedValue = a.getValue() + "post";
+  result.setValue(changedValue);
+  result.setInner(new Inner("inner" + changedValue));
+  return result;
+:}
+
+AddStringSuffix maps String s to String {:
+  return s + "post";
+:}
+
+AddPlusOne maps int i to int {:
+  return i + 1;
+:}
+
+receive ReceiverRoot.FromBasic;
+receive ReceiverRoot.FromSimpleNoMapping;
+receive ReceiverRoot.FromSimpleWithMapping using AddStringSuffix;
+receive ReceiverRoot.FromTransformedNoMapping;
+receive ReceiverRoot.FromTransformedWithMapping using AddPlusOne;
+receive ReceiverRoot.FromReferenceTypeNoMapping;
+receive ReceiverRoot.FromReferenceTypeWithMapping using AddSuffix;
+receive ReceiverRoot.FromNTANoMapping;
+receive ReceiverRoot.FromNTAWithMapping using AddSuffix;
diff --git a/ragconnect.tests/src/test/01-input/attribute/Test.jadd b/ragconnect.tests/src/test/01-input/attribute/Test.jadd
new file mode 100644
index 0000000..c5274c6
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/attribute/Test.jadd
@@ -0,0 +1,39 @@
+aspect Computation {
+  syn String SenderRoot.basic() = getInput();
+  syn String SenderRoot.simple() = getInput() + "Post";
+  syn int SenderRoot.transformed() = getInput().length();
+  syn A SenderRoot.toReferenceType() {
+    A result = new A();
+    result.setValue(getInput());
+    Inner inner = new Inner();
+    inner.setInnerValue("1");
+    result.setInner(inner);
+    return result;
+  }
+  syn nta A SenderRoot.toNTA() {
+    A result = new A();
+    result.setValue(getInput());
+    Inner inner = new Inner();
+    inner.setInnerValue("2");
+    result.setInner(inner);
+    return result;
+  }
+}
+aspect MakeCodeCompile {
+
+}
+aspect MakeCodeWork {
+
+}
+aspect NameResolution {
+  // overriding customID guarantees to produce the same JSON representation for equal lists
+  // otherwise, the value for id is different each time
+  @Override
+  protected String A.customID() {
+    return getClass().getSimpleName() + getValue();
+  }
+  @Override
+  protected String Inner.customID() {
+    return getClass().getSimpleName() + getInnerValue();
+  }
+}
diff --git a/ragconnect.tests/src/test/01-input/attribute/Test.relast b/ragconnect.tests/src/test/01-input/attribute/Test.relast
new file mode 100644
index 0000000..d7edb5c
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/attribute/Test.relast
@@ -0,0 +1,14 @@
+Root ::= SenderRoot ReceiverRoot;
+SenderRoot ::= <Input> ;
+ReceiverRoot ::=
+    <FromBasic>
+    <FromSimpleNoMapping>
+    <FromSimpleWithMapping>
+    <FromTransformedNoMapping:int>
+    <FromTransformedWithMapping:int>
+    FromReferenceTypeNoMapping:A
+    FromReferenceTypeWithMapping:A
+    FromNTANoMapping:A
+    FromNTAWithMapping:A ;
+A ::= <Value> Inner ;
+Inner ::= <InnerValue> ;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java
new file mode 100644
index 0000000..6b87744
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java
@@ -0,0 +1,48 @@
+package org.jastadd.ragconnect.tests;
+
+import attribute.ast.*;
+import org.junit.jupiter.api.Tag;
+
+import java.io.IOException;
+
+/**
+ * Test case "attribute".
+ *
+ * @author rschoene - Initial contribution
+ */
+@Tag("Incremental")
+@Tag("New")
+public class AttributeTest extends AbstractMqttTest {
+  Root model;
+  MqttHandler handler;
+
+  @Override
+  protected void createModel() {
+
+  }
+
+  @Override
+  protected void setupReceiverAndConnect() throws IOException, InterruptedException {
+
+  }
+
+  @Override
+  protected void communicateSendInitialValue() throws IOException, InterruptedException {
+
+  }
+
+  @Override
+  protected void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
+
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+}
-- 
GitLab