diff --git a/.gitignore b/.gitignore
index 8e7d63ec40cb8b81aaa7fe19cf4f672d3fca548a..1c0e398ffb5ae6d968d97a3a6870401d9aa26676 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
 .idea/
 .gradle/
 /build/
+/public/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f212e144a859a527b8add94d68d5ce474adfe9cd..d07299bade9f50d37d4f4f7631238f18a08caeca 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,6 +4,7 @@ variables:
 stages:
   - build
   - test
+  - ragdoc
   - publish
 
 build:
@@ -12,33 +13,86 @@ build:
   before_script:
     - ls -lah *
   script:
-    - ./gradlew assemble
+    - "./gradlew assemble"
 
 test:
   image: openjdk:11
   stage: test
+  needs:
+    - build
   script:
-    - ./gradlew test
+    - "./gradlew test"
   artifacts:
     reports:
-      junit: relast2uml.tests/build/test-results/test/TEST-*.xml
+      junit: "*/build/test-results/test/TEST-*.xml"
 
-publish:
+publish_dev:
   image: openjdk:11
   stage: publish
+  needs:
+    - test
+  script:
+    - "./gradlew setDevVersionForCI"
+    - "./gradlew publish"
+  only:
+    - dev
+
+publish_master:
+  image: openjdk:11
+  stage: publish
+  needs:
+    - test
   script:
     - "./gradlew publish"
   only:
     - master
 
+ragdoc_build:
+  image:
+    name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-builder"
+    entrypoint: [""]
+  stage: ragdoc
+  needs:
+    - build
+  script:
+    - JAVA_FILES=$(find dumpAstWithPlantuml/src/ -name '*.java')
+    - /ragdoc-builder/start-builder.sh -excludeGenerated -d data/ $JAVA_FILES
+  artifacts:
+    paths:
+      - "data/"
+
+ragdoc_view:
+  image:
+    name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-view:relations"
+    entrypoint: [""]
+  stage: ragdoc
+  needs:
+    - ragdoc_build
+  script:
+    - DATA_DIR=$(pwd -P)/data
+    - mkdir -p pages/docs/ragdoc
+    - OUTPUT_DIR=$(pwd -P)/pages/docs/ragdoc
+    - cd /ragdoc-view/src/ && rm -rf data && ln -s $DATA_DIR
+    - /ragdoc-view/build-view.sh --output-path=$OUTPUT_DIR
+  only:
+    - dev
+    - main
+  artifacts:
+    paths:
+      - "pages/docs/ragdoc"
+
 pages:
-  image: python:3.7-alpine
+  image: python:3.10.0-bullseye
   stage: publish
+  needs:
+    - ragdoc_view
+    - test
+  before_script:
+    - pip install -r pages/requirements.txt
   script:
-  - pip install -U sphinx sphinx-rtd-theme recommonmark sphinxemoji sphinx-markdown-tables
-  - sphinx-build -b html pages/ public
+    - cd pages && mkdocs build
   artifacts:
     paths:
-    - public
+      - public/
   only:
-  - master
+    - main
diff --git a/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle b/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle
index 0a5d763501b81b682a8a37c30e0282ff5f378b2d..e3130c9008342d8dc2be8d80e2b29ba064cb31fa 100644
--- a/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle
+++ b/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle
@@ -46,6 +46,14 @@ task newVersion() {
   }
 }
 
+task setDevVersionForCI() {
+  doFirst {
+    def props = new Properties()
+    props['version'] = version + "-$System.env.CI_PIPELINE_IID"
+    props.store(file(versionFile).newWriter(), null)
+  }
+}
+
 //679
 publishing {
   publications {
diff --git a/dumpAst/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast
index 7a3e31f2cfc389af9e9cd14228697b59c5929dc1..2fad59a121f72fb3629dda4bdbf7733a797ae1fa 100644
--- a/dumpAst/src/main/jastadd/DumpAst.relast
+++ b/dumpAst/src/main/jastadd/DumpAst.relast
@@ -1,18 +1,25 @@
 DumpAst ::= DumpNode* <PackageName> BuildConfig PrintConfig ;
 rel DumpAst.RootNode -> DumpNode ;
 
-BuildConfig ::= GlobalPatternCollection:PatternCollection ExcludeTypePattern:TypePatternCollectionMapping* IncludeTypePattern:TypePatternCollectionMapping* <TypeIgnorePattern> <IncludeEmptyString:boolean> <Debug:boolean> ;
+BuildConfig ::= StyleInformation GlobalPatternCollection:PatternCollection
+ ExcludeTypePattern:TypePatternCollectionMapping* IncludeTypePattern:TypePatternCollectionMapping*
+ <TypeIgnorePattern> <IncludeEmptyString:boolean> <ExcludeNullNodes:boolean> <Debug:boolean>;
 TypePatternCollectionMapping ::= <TypeRegex> PatternCollection ;
 PatternCollection ::= <TokenPattern> <ChildPattern> <RelationPattern> <AttributePattern> <NonterminalAttributePattern> ;
-PrintConfig ::= <Scale:double> <Version> Header* ;
+StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod> <TextColorMethod:StyleMethod>;
+
+PrintConfig ::= <Scale:double> <Version> <OrderChildren:boolean> Header* ;
 Header ::= <Value> ;
-DumpNode ::= <Name> <Label> <Object:Object> <Invisible:boolean> DumpChildNode* DumpToken* DumpRelation* /InvisiblePath/ ;
+
+DumpNode ::=  DumpChildNode* DumpToken* DumpRelation*
+ <Name> <Label> <BackgroundColor> <TextColor> <Object:Object> <Invisible:boolean>
+ /InvisiblePath/ ;
 InnerDumpNode ;
-rel InnerDumpNode.DumpNode -> DumpNode ;
+rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ;
 
 abstract DumpChildNode ::= <Name> <Computed:boolean> ;
 DumpNormalChildNode : DumpChildNode ;
-rel DumpNormalChildNode.DumpNode -> DumpNode ;
+rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild ;
 DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
 
 abstract DumpToken ::= <Name> <Computed:boolean> ;
diff --git a/dumpAst/src/main/jastadd/GenerationBackend.jadd b/dumpAst/src/main/jastadd/GenerationBackend.jadd
index ce550ca7fc0cdc85e7326a649635b7cba8a1698f..beeb183de52a4adccbdb8413309b16c418b55511 100644
--- a/dumpAst/src/main/jastadd/GenerationBackend.jadd
+++ b/dumpAst/src/main/jastadd/GenerationBackend.jadd
@@ -26,11 +26,23 @@ aspect GenerationBackend {
   }
   protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj, Source source)
       throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
-    if (obj == null) {
+    return transform(tti, obj, source, false);
+  }
+  protected DumpNode DumpAst.transform(
+      TransformationTransferInformation tti, Object obj, Source source, boolean allowNullObj)
+      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+    if (obj == null && !allowNullObj) {
       return null;
     }
-    DumpNode node = tti.transformed.get(obj);
-    String objClassName = obj.getClass().getSimpleName();
+    DumpNode node;
+    String objClassName;
+    if (obj == null) {
+      node = null;
+      objClassName = "null";
+    } else {
+      node = tti.transformed.get(obj);
+      objClassName = obj.getClass().getSimpleName();
+    }
     if (node != null) {
       if (source == Source.RELATION) {
         return node;
@@ -40,106 +52,118 @@ aspect GenerationBackend {
     } else {
       node = new DumpNode();
       node.setObject(obj);
-      node.setLabel(objClassName + "@" + obj.hashCode());
       node.setName("node" + tti.transformed.size());
       tti.transformed.put(obj, node);
       this.addDumpNode(node);
     }
-    if (node.isAstNode()) {
-      // do not process node further if coming from a relation
-      if (source == Source.RELATION) {
-        tti.relationTargetsUnprocessed.put(node, true);
-        return node;
-      }
-      if (source == Source.INVISIBLE_PARENT || !isTypeEnabled(objClassName)) {
-        node.setInvisible(true);
+    if (!node.isAstNode()) {
+      return node;
+    }
+    applyStyle(node);
+    // do not process node further if coming from a relation
+    if (source == Source.RELATION) {
+      tti.relationTargetsUnprocessed.put(node, true);
+      return node;
+    }
+    if (source == Source.INVISIBLE_PARENT || !isTypeEnabled(objClassName)) {
+      node.setInvisible(true);
+    }
+    if (obj == null) {
+      // for a null object, we do not need any further analysis
+      return node;
+    }
+    final ClassAnalysisResult car = analyzeClass(obj.getClass());
+    // -- singleChild --
+    for (SingleChildMethod singleChildMethod : car.singleChildMethods()) {
+      Object target = singleChildMethod.getMethod().invoke(obj);
+      String childName = singleChildMethod.getName();
+      DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)), !getBuildConfig().getExcludeNullNodes());
+      if (targetNode != null) {
+        DumpNormalChildNode normalChild = new DumpNormalChildNode();
+        normalChild.setName(childName);
+        normalChild.setDumpNode(targetNode);
+        normalChild.setComputed(singleChildMethod.isNTASingleChildMethod());
+        node.addDumpChildNode(normalChild);
       }
-      final ClassAnalysisResult car = analyzeClass(obj.getClass());
-      // -- singleChild --
-      for (SingleChildMethod singleChildMethod : car.singleChildMethods()) {
-        Object target = singleChildMethod.getMethod().invoke(obj);
-        String childName = singleChildMethod.getName();
-        DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)));
+    }
+    // -- listChild --
+    for (ListChildMethod listChildMethod : car.listChildMethods()) {
+      Iterable<?> targetList = (Iterable<?>) listChildMethod.getMethod().invoke(obj);
+      DumpListChildNode listChild = new DumpListChildNode();
+      listChild.setComputed(listChildMethod.isNTAListChildMethod());
+      String childName = listChildMethod.getName();
+      boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
+      listChild.setName(childName);
+      for (Object target : targetList) {
+        DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble));
         if (target != null && targetNode != null) {
-          DumpNormalChildNode normalChild = new DumpNormalChildNode();
-          normalChild.setName(childName);
-          normalChild.setDumpNode(targetNode);
-          normalChild.setComputed(singleChildMethod.isNTASingleChildMethod());
-          node.addDumpChildNode(normalChild);
+          listChild.addInnerDumpNode(new InnerDumpNode(targetNode));
         }
       }
-      // -- listChild --
-      for (ListChildMethod listChildMethod : car.listChildMethods()) {
-        Iterable<?> targetList = (Iterable<?>) listChildMethod.getMethod().invoke(obj);
-        DumpListChildNode listChild = new DumpListChildNode();
-        listChild.setComputed(listChildMethod.isNTAListChildMethod());
-        String childName = listChildMethod.getName();
-        boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
-        listChild.setName(childName);
-        for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble));
-          if (target != null && targetNode != null) {
-            listChild.addInnerDumpNode(new InnerDumpNode(targetNode));
-          }
-        }
-        if (listChild.getNumInnerDumpNode() > 0) {
-          node.addDumpChildNode(listChild);
-        }
+      if (listChild.getNumInnerDumpNode() > 0) {
+        node.addDumpChildNode(listChild);
+      }
+    }
+    // -- singleRelation --
+    for (SingleRelationMethod singleRelationMethod : car.singleRelationMethods()) {
+      Object target = singleRelationMethod.getMethod().invoke(obj);
+      DumpNode targetNode = transform(tti, target, Source.RELATION);
+      if (target != null && targetNode != null) {
+        DumpNormalRelation normalRelation = new DumpNormalRelation();
+        normalRelation.setName(singleRelationMethod.getName());
+        normalRelation.setDumpNode(targetNode);
+        node.addDumpRelation(normalRelation);
       }
-      // -- singleRelation --
-      for (SingleRelationMethod singleRelationMethod : car.singleRelationMethods()) {
-        Object target = singleRelationMethod.getMethod().invoke(obj);
+    }
+    // -- listRelation --
+    for (ListRelationMethod listRelationMethod : car.listRelationMethods()) {
+      Iterable<?> targetList = (Iterable<?>) listRelationMethod.getMethod().invoke(obj);
+      DumpListRelation listRelation = new DumpListRelation();
+      listRelation.setName(listRelationMethod.getName());
+      for (Object target : targetList) {
         DumpNode targetNode = transform(tti, target, Source.RELATION);
         if (target != null && targetNode != null) {
-          DumpNormalRelation normalRelation = new DumpNormalRelation();
-          normalRelation.setName(singleRelationMethod.getName());
-          normalRelation.setDumpNode(targetNode);
-          node.addDumpRelation(normalRelation);
+          listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
         }
       }
-      // -- listRelation --
-      for (ListRelationMethod listRelationMethod : car.listRelationMethods()) {
-        Iterable<?> targetList = (Iterable<?>) listRelationMethod.getMethod().invoke(obj);
-        DumpListRelation listRelation = new DumpListRelation();
-        listRelation.setName(listRelationMethod.getName());
-        for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, Source.RELATION);
-          if (target != null && targetNode != null) {
-            listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
-          }
-        }
-        if (listRelation.getNumInnerDumpNode() > 0) {
-          node.addDumpRelation(listRelation);
-        }
+      if (listRelation.getNumInnerDumpNode() > 0) {
+        node.addDumpRelation(listRelation);
       }
-      // -- token --
-      for (TokenMethod tokenMethod : car.tokenMethods()) {
-        Object target = tokenMethod.getMethod().invoke(obj);
-        if (target != null) {
-          DumpNode targetNode = transform(tti, target, Source.RELATION);
-          DumpToken token = null;
-          // TODO check, if Iterable.isAssignableFrom(target.getClass()).
-          //      if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's
-          if (targetNode != null && targetNode.isAstNode()) {
-            token = new DumpReferenceToken().setValue(targetNode);
-          } else {
-            if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) {
-              DumpValueToken valueToken = new DumpValueToken();
-              valueToken.setValue(target);
-              token = valueToken;
-            }
-          }
-          if (token != null) {
-            token.setName(tokenMethod.getName());
-            token.setComputed(tokenMethod.isAttributeMethod());
-            node.addDumpToken(token);
+    }
+    // -- token --
+    for (TokenMethod tokenMethod : car.tokenMethods()) {
+      Object target = tokenMethod.getMethod().invoke(obj);
+      if (target != null) {
+        DumpNode targetNode = transform(tti, target, Source.RELATION);
+        DumpToken token = null;
+        // TODO check, if Iterable.isAssignableFrom(target.getClass()).
+        //      if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's
+        if (targetNode != null && targetNode.isAstNode()) {
+          token = new DumpReferenceToken().setValue(targetNode);
+        } else {
+          if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) {
+            DumpValueToken valueToken = new DumpValueToken();
+            valueToken.setValue(target);
+            token = valueToken;
           }
         }
+        if (token != null) {
+          token.setName(tokenMethod.getName());
+          token.setComputed(tokenMethod.isAttributeMethod());
+          node.addDumpToken(token);
+        }
       }
     }
     return node;
   }
 
+  private void DumpAst.applyStyle(DumpNode node) {
+    Object obj = node.getObject();
+    node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj));
+    node.setBackgroundColor(getBuildConfig().getStyleInformation().getBackgroundColorMethod().get(obj));
+    node.setTextColor(getBuildConfig().getStyleInformation().getTextColorMethod().get(obj));
+  }
+
   private Source DumpAst.nextSource(Source currentSource, boolean shouldBeInvisible) {
     return currentSource == Source.INVISIBLE_PARENT ? Source.INVISIBLE_PARENT :
         (shouldBeInvisible ? Source.INVISIBLE_PARENT : Source.PARENT);
@@ -412,6 +436,10 @@ aspect GenerationBackend {
 
   // --- isAstNode ---
   syn boolean DumpNode.isAstNode() {
+    if (getObject() == null) {
+      // this is only possible for normal child nodes, and they are ast nodes
+      return true;
+    }
     Class<?> clazz = getObject().getClass();
     for (java.lang.reflect.Method method : clazz.getMethods()) {
       if ("init$Children".equals(method.getName()) && method.getParameterCount() == 0) {
@@ -450,33 +478,47 @@ aspect GenerationBackend {
     return result;
   }
 
-  class TransformationTransferInformation {
-    java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>();
-    java.util.Map<DumpNode, Boolean> relationTargetsUnprocessed = new java.util.HashMap<>();
+  // --- labelAndTextColor ---
+  syn String DumpNode.labelAndTextColor() {
+    if (getTextColor().isEmpty()) {
+      return getLabel();
+    } else {
+      return "<color:" + getTextColor() + ">" + getLabel() + "</color>";
+    }
   }
 
-  syn String DumpAst.toPlantUml() {
-    StringBuilder sb = new StringBuilder();
-    com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
-      @Override
-      public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
-        return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
-          @Override
-          protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
-            com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
-            if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
-              throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
-            }
-            return wrapper;
-          }
-        };
+  // --- myChildren ---
+  syn java.util.List<DumpNode> DumpNode.myChildren() {
+    java.util.List<DumpNode> result = new java.util.ArrayList<>();
+    for (DumpChildNode childNode : getDumpChildNodeList()) {
+      for (DumpNode inner : childNode.innerNodes(true)) {
+        result.add(inner);
       }
-    };
-    com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
-    mf.setObjectHandler(roh);
-    com.github.mustachejava.Mustache m = mf.compile("dumpAst.mustache");
-    m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
-    return sb.toString();
+    }
+    return result;
+  }
+
+  // --- successor ---
+  syn DumpNode DumpNode.successor() {
+    if (container() == null) {
+      // not contained
+      return null;
+    }
+    java.util.List<DumpNode> siblingsAndMe = container().myChildren();
+    int indexOfMe = siblingsAndMe.indexOf(this);
+    if (indexOfMe == siblingsAndMe.size() - 1) {
+      // last child
+      return null;
+    }
+    return siblingsAndMe.get(indexOfMe + 1);
+  }
+
+  // --- hasSuccessor ---
+  syn boolean DumpNode.hasSuccessor() = successor() != null;
+
+  class TransformationTransferInformation {
+    java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>();
+    java.util.Map<DumpNode, Boolean> relationTargetsUnprocessed = new java.util.HashMap<>();
   }
 
   syn String DumpAst.toYaml(boolean prependCreationComment) {
@@ -558,4 +600,17 @@ aspect GenerationBackend {
     public void close() {
     }
   }
+
+  static StyleInformation StyleInformation.createDefault() {
+    StyleInformation result = new StyleInformation();
+    result.setNameMethod(n -> n == null ? "null" : n.getClass().getSimpleName() + "@" + Integer.toHexString(n.hashCode()));
+    result.setBackgroundColorMethod(n -> "");
+    result.setTextColorMethod(n -> "");
+    return result;
+  }
+
+  @FunctionalInterface
+  public interface StyleMethod<ASTNODE> {
+    String get(ASTNODE node);
+  }
 }
diff --git a/dumpAst/src/main/jastadd/GenerationFrontend.jadd b/dumpAst/src/main/jastadd/GenerationFrontend.jadd
index 26466b1cd9ae3b1aa6cb9dec45acccd8437a2dfc..97b622a1ba1d333ad23e44dabb0a8571721cf6ab 100644
--- a/dumpAst/src/main/jastadd/GenerationFrontend.jadd
+++ b/dumpAst/src/main/jastadd/GenerationFrontend.jadd
@@ -62,6 +62,7 @@ public class DumpBuilder {
       this.target = target;
       buildConfig = new BuildConfig();
       buildConfig.setGlobalPatternCollection(new PatternCollection());
+      buildConfig.setStyleInformation(StyleInformation.createDefault());
       printConfig = new PrintConfig();
       printConfig.setScale(1);
       printConfig.setVersion(readVersion());
@@ -307,6 +308,16 @@ public class DumpBuilder {
       }
     }
 
+    public DumpBuilder excludeNullNodes() {
+      buildConfig.setExcludeNullNodes(true);
+      return this;
+    }
+
+    public DumpBuilder includeNullNodes() {
+      buildConfig.setExcludeNullNodes(false);
+      return this;
+    }
+
     public DumpBuilder customPreamble(String option) {
       printConfig.addHeader(new Header(option));
       return this;
@@ -321,11 +332,31 @@ public class DumpBuilder {
       return this;
     }
 
+    public <ASTNODE> DumpBuilder setNameMethod(StyleMethod<ASTNODE> nameMethod) {
+      buildConfig.getStyleInformation().setNameMethod(nameMethod);
+      return this;
+    }
+
+    public <ASTNODE> DumpBuilder setBackgroundColorMethod(StyleMethod<ASTNODE> colorMethod) {
+      buildConfig.getStyleInformation().setBackgroundColorMethod(colorMethod);
+      return this;
+    }
+
+    public <ASTNODE> DumpBuilder setTextColorMethod(StyleMethod<ASTNODE> colorMethod) {
+      buildConfig.getStyleInformation().setTextColorMethod(colorMethod);
+      return this;
+    }
+
     public DumpBuilder setScale(double value) {
       printConfig.setScale(value);
       return this;
     }
 
+    public DumpBuilder orderChildren() {
+      printConfig.setOrderChildren(true);
+      return this;
+    }
+
     private String readVersion() {
       try {
         java.util.ResourceBundle resources = java.util.ResourceBundle.getBundle("dumpAstVersion");
diff --git a/dumpAst/src/main/jastadd/GenerationMustache.jrag b/dumpAst/src/main/jastadd/GenerationMustache.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..d69f4fba573580285101ce63946da0fda5ce52fd
--- /dev/null
+++ b/dumpAst/src/main/jastadd/GenerationMustache.jrag
@@ -0,0 +1,25 @@
+aspect GenerationMustache {
+  syn String DumpAst.toPlantUml() {
+    StringBuilder sb = new StringBuilder();
+    com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
+      @Override
+      public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
+        return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
+          @Override
+          protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
+            com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
+            if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
+              throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
+            }
+            return wrapper;
+          }
+        };
+      }
+    };
+    com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
+    mf.setObjectHandler(roh);
+    com.github.mustachejava.Mustache m = mf.compile("dumpAst.mustache");
+    m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
+    return sb.toString();
+  }
+}
diff --git a/dumpAst/src/main/jastadd/Navigation.jrag b/dumpAst/src/main/jastadd/Navigation.jrag
index 8783503bbcac55eac737c6777227a7b9bd18b70f..3f04c9c4392d6b02f4fa85b7329e9ae36fe140b9 100644
--- a/dumpAst/src/main/jastadd/Navigation.jrag
+++ b/dumpAst/src/main/jastadd/Navigation.jrag
@@ -33,6 +33,17 @@ aspect Navigation {
   eq DumpNode.getDumpRelation().containingDumpNode() = this;
   eq DumpNode.getInvisiblePath().containingDumpNode() = this;
 
+  // --- container ---
+  syn DumpNode DumpNode.container() {
+    if (getContainerOfNormalChild() != null) {
+      return getContainerOfNormalChild().containingDumpNode();
+    }
+    if (getContainerOfInner() != null) {
+      return getContainerOfInner().containingDumpNode();
+    }
+    return null;
+  }
+
   // --- innerVisibleNodes ---
   syn java.util.List<DumpNode> DumpChildNode.innerVisibleNodes() = innerNodes(true);
   syn java.util.List<DumpNode> DumpRelation.innerVisibleNodes() = innerNodes(true);
diff --git a/dumpAst/src/main/resources/dumpAst.mustache b/dumpAst/src/main/resources/dumpAst.mustache
index b0c05305e6ff1c6431d593d7628101c90b3230bc..9dd0d02e100d308c175e494648008aebb663e5b0 100644
--- a/dumpAst/src/main/resources/dumpAst.mustache
+++ b/dumpAst/src/main/resources/dumpAst.mustache
@@ -9,7 +9,7 @@ scale {{Scale}}
 {{#DumpNodes}}
   {{#isAstNode}}
     {{^Invisible}}
-object "{{label}}" as {{name}} {
+object "{{{labelAndTextColor}}}" as {{name}} {{#backgroundColor}}#{{{backgroundColor}}}{{/backgroundColor}} {
       {{#DumpTokens}}
         {{#isDumpValueToken}}
   {{label}} = {{{Value}}}
@@ -63,6 +63,13 @@ object "{{label}}" as {{name}} {
       {{/InnerDumpNodes}}
     {{/InvisiblePath}}
   {{/Invisible}}
+  {{#PrintConfig}}{{#orderChildren}}
+      {{#myChildren}}
+          {{#hasSuccessor}}
+{{name}} -[hidden]right-> {{#successor}}{{name}}{{/successor}}
+          {{/hasSuccessor}}
+      {{/myChildren}}
+  {{/orderChildren}}{{/PrintConfig}}
 {{/DumpNodes}}
 {{#BuildConfig}}
   {{#Debug}}
diff --git a/dumpAst/src/main/resources/dumpAstVersion.properties b/dumpAst/src/main/resources/dumpAstVersion.properties
index 14aef4289892896a1b5965bd9edd25a783a3ebc2..e0a9ca936f23641719ffee3bfed385ca97d946b8 100644
--- a/dumpAst/src/main/resources/dumpAstVersion.properties
+++ b/dumpAst/src/main/resources/dumpAstVersion.properties
@@ -1,2 +1,2 @@
-#Fri Jan 15 20:29:48 CET 2021
-version=0.3.5
+#Thu Feb 24 09:45:15 CET 2022
+version=0.3.6
diff --git a/grammar2uml/.gitignore b/grammar2uml/.gitignore
deleted file mode 100644
index 87b4cdd3d7c6a41502ca98703abeeb69a1d536fb..0000000000000000000000000000000000000000
--- a/grammar2uml/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-build
-src/gen-res/
-src/gen/
-out/
-*.class
diff --git a/grammar2uml/build.gradle b/grammar2uml/build.gradle
deleted file mode 100644
index 78152a3d533446575bcc0a3e5a3e950100034a61..0000000000000000000000000000000000000000
--- a/grammar2uml/build.gradle
+++ /dev/null
@@ -1,148 +0,0 @@
-buildscript {
-    repositories.mavenLocal()
-    repositories.mavenCentral()
-    dependencies {
-        classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3'
-    }
-}
-
-plugins {
-    id 'relast2uml.java-jastadd-conventions'
-    id 'relast2uml.java-application-conventions'
-    id 'relast2uml.java-publishing-conventions'
-}
-
-apply plugin: 'jastadd'
-
-application.mainClassName = 'de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler'
-
-jar {
-    manifest {
-        attributes "Main-Class": application.mainClassName
-    }
-}
-
-dependencies {
-    implementation project(':relast.preprocessor')
-
-    implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}"
-    runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4'
-}
-
-File preprocessorGrammar = file('../relast.preprocessor/src/main/jastadd/RelAst.relast')
-File grammar2umlGrammar = file('./src/main/jastadd/Grammar2Uml.relast')
-File intermediateGrammar = file('./src/main/jastadd/MustacheNodes.relast')
-
-task relast(type: JavaExec) {
-    group = 'Build'
-    main = "-jar"
-
-    doFirst {
-        delete "src/gen/jastadd/*.ast"
-        delete "src/gen/jastadd/Grammar2Uml.jadd"
-        delete "src/gen/jastadd/Grammar2UmlRefResolver.jadd"
-        delete "src/gen/jastadd/Grammar2UmlResolverStubs.jrag"
-        mkdir  "src/gen/jastadd/"
-    }
-
-    args = [
-            "../libs/relast.jar",
-            preprocessorGrammar,
-            grammar2umlGrammar,
-            intermediateGrammar,
-            "--listClass=java.util.ArrayList",
-            "--jastAddList=JastAddList",
-            "--useJastAddNames",
-            "--file",
-            "--resolverHelper",
-            "--grammarName=./src/gen/jastadd/Grammar2Uml"
-    ]
-
-    inputs.files(file("../libs/relast.jar"),
-            preprocessorGrammar,
-            grammar2umlGrammar,
-            intermediateGrammar)
-    outputs.files(file("./src/gen/jastadd/Grammar2Uml.ast"),
-            file("./src/gen/jastadd/Grammar2Uml.jadd"),
-            file("./src/gen/jastadd/Grammar2UmlRefResolver.jadd"),
-            file('./src/gen/jastadd/Grammar2UmlResolverStubs.jrag'))
-}
-
-jastadd {
-    configureModuleBuild()
-    modules {
-        //noinspection GroovyAssignabilityCheck
-        module("Grammar2Uml") {
-
-            java {
-                basedir ".."
-                include "relast.preprocessor/main/**/*.java"
-                include "relast.preprocessor/gen/**/*.java"
-                include "grammar2uml/src/main/**/*.java"
-                include "grammar2uml/src/gen/**/*.java"
-            }
-
-            jastadd {
-                basedir ".."
-                include "relast.preprocessor/src/main/jastadd/**/*.ast"
-                include "relast.preprocessor/src/main/jastadd/**/*.jadd"
-                include "relast.preprocessor/src/main/jastadd/**/*.jrag"
-                include "grammar2uml/src/main/jastadd/**/*.ast"
-                include "grammar2uml/src/main/jastadd/**/*.jadd"
-                include "grammar2uml/src/main/jastadd/**/*.jrag"
-                include "grammar2uml/src/gen/jastadd/**/*.ast"
-                include "grammar2uml/src/gen/jastadd/**/*.jadd"
-                include "grammar2uml/src/gen/jastadd/**/*.jrag"
-            }
-
-            scanner {
-                basedir ".."
-                include "grammar2uml/src/main/jastadd/scanner/Header.flex",               [-5]
-                include "relast.preprocessor/src/main/jastadd/scanner/Preamble.flex",      [-4]
-                include "relast.preprocessor/src/main/jastadd/scanner/Macros.flex",        [-3]
-                include "grammar2uml/src/main/jastadd/scanner/Macros.flex",               [-3]
-                include "relast.preprocessor/src/main/jastadd/scanner/RulesPreamble.flex", [-2]
-                include "grammar2uml/src/main/jastadd/scanner/MappingContent.flex",       [-1]
-                include "grammar2uml/src/main/jastadd/scanner/Keywords.flex"
-                include "relast.preprocessor/src/main/jastadd/scanner/Keywords.flex"
-                include "relast.preprocessor/src/main/jastadd/scanner/Symbols.flex",        [1]
-                include "relast.preprocessor/src/main/jastadd/scanner/RulesPostamble.flex", [2]
-            }
-
-            parser {
-                basedir ".."
-                include "grammar2uml/src/main/jastadd/parser/Preamble.parser"
-                include "relast.preprocessor/src/main/jastadd/parser/RelAst.parser"
-                include "grammar2uml/src/main/jastadd/parser/Grammar2Uml.parser"
-            }
-        }
-    }
-
-    cleanGen.doFirst {
-        delete "src/gen/java/de"
-        delete "src/gen-res/BuildInfo.properties"
-    }
-
-    preprocessParser.doFirst {
-
-        args += ["--no-beaver-symbol"]
-
-    }
-
-    module = "Grammar2Uml"
-
-    astPackage = 'de.tudresden.inf.st.jastadd.grammar2uml.ast'
-
-    parser.name = 'Grammar2UmlParser'
-
-    genDir = 'src/gen/java'
-
-    buildInfoDir = 'src/gen-res'
-
-    scanner.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/scanner"
-    parser.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/parser"
-
-    jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
-}
-
-generateAst.dependsOn relast
diff --git a/grammar2uml/src/main/jastadd/Analysis.jrag b/grammar2uml/src/main/jastadd/Analysis.jrag
deleted file mode 100644
index 317cf9dab8f3d0beb71e38fa7a2410b3c04090e2..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/Analysis.jrag
+++ /dev/null
@@ -1,12 +0,0 @@
-aspect Analysis {
-  syn lazy Folder GrammarFile.defaultFolder() {
-    Folder result = new Folder();
-    result.setName(new java.io.File(getFileName()).getName().replace(".relast", "").replace(".ast", ""));
-    for (Declaration decl : getDeclarationList()) {
-      if (decl.isTypeDecl() && !decl.asTypeDecl().hasSourceFolder()) {
-        result.addType(decl.asTypeDecl());
-      }
-    }
-    return result;
-  }
-}
diff --git a/grammar2uml/src/main/jastadd/Errors.jrag b/grammar2uml/src/main/jastadd/Errors.jrag
deleted file mode 100644
index 6a67912cee04483dec1f7727b97f8bdc6b19a8c1..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/Errors.jrag
+++ /dev/null
@@ -1,68 +0,0 @@
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.LinkedList;
-
-aspect Errors {
-  coll Set<ErrorMessage> Grammar2Uml.errors()
-    [new TreeSet<ErrorMessage>()]
-    root Grammar2Uml;
-}
-
-aspect ErrorMessage {
-  public class ErrorMessage implements Comparable<ErrorMessage> {
-    private final ASTNode node;
-    private final String filename;
-    private final int line;
-    private final int col;
-    private final String message;
-
-    public ErrorMessage(ASTNode node, String message) {
-      this.node = node;
-      this.filename = node.containedFileName();
-      this.line = node.getStartLine();
-      this.col = node.getStartColumn();
-      this.message = message;
-    }
-
-    public ASTNode getNode() {
-      return node;
-    }
-    public int getLine() {
-      return line;
-    }
-    public int getCol() {
-      return col;
-    }
-    public String getMessage() {
-      return message;
-    }
-
-    public String toString() {
-      return filename + " Line " + line + ", column " + col + ": " + message;
-    }
-
-    @Override
-    public int compareTo(ErrorMessage err) {
-      int n = filename.compareTo(err.filename);
-      if (n != 0) {
-        return n;
-      }
-
-      n = line - err.line;
-      if (n != 0) {
-        return n;
-      }
-
-      n = col-err.col;
-      if (n != 0) {
-        return n;
-      }
-
-      return message.compareTo(err.message);
-    }
-  }
-
-  protected ErrorMessage ASTNode.error(String message) {
-    return new ErrorMessage(this, message);
-  }
-}
diff --git a/grammar2uml/src/main/jastadd/Grammar2Uml.relast b/grammar2uml/src/main/jastadd/Grammar2Uml.relast
deleted file mode 100644
index 9eab1230a1f5abd9fffbb7784043e1a457a14248..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/Grammar2Uml.relast
+++ /dev/null
@@ -1,4 +0,0 @@
-Grammar2Uml ::= Program <FileName> Folder* ;
-
-Folder ::= <Name:String> ;
-rel Folder.Type* <-> TypeDecl.SourceFolder?;
diff --git a/grammar2uml/src/main/jastadd/MustacheNodes.relast b/grammar2uml/src/main/jastadd/MustacheNodes.relast
deleted file mode 100644
index d4b684c6a23d885c5aca80ebb54aef3f6d4aa402..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/MustacheNodes.relast
+++ /dev/null
@@ -1,21 +0,0 @@
-MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* Inheritance:MInheritance* ;
-MFolder ::= InnerTypeDecl:MTypeDecl*;
-MTypeDecl ::= InnerTokenComponent:MTokenComponent*;
-MTokenComponent;
-abstract MContainment ::= <Label:String> ;
-MSingleContainment : MContainment;
-MOptContainment : MContainment;
-MListContainment : MContainment;
-MRelation ::= <Label> <LeftModifier> <RightModifier> <Bidirectional:boolean>;
-MInheritance ;
-
-rel MGrammar2Uml.Grammar2Uml -> Grammar2Uml;
-rel MFolder.Folder -> Folder;
-rel MTypeDecl.Type -> TypeDecl;
-rel MTokenComponent.Token -> TokenComponent;
-rel MContainment.Type -> TypeDecl;
-rel MContainment.Component -> TypeDecl;
-rel MRelation.Left -> TypeDecl;
-rel MRelation.Right -> TypeDecl;
-rel MInheritance.SuperClass -> TypeDecl;
-rel MInheritance.SubClass -> TypeDecl;
diff --git a/grammar2uml/src/main/jastadd/NameResolution.jrag b/grammar2uml/src/main/jastadd/NameResolution.jrag
deleted file mode 100644
index e6450fbd126ab20cf1d3ccc792af9d3c84ac9636..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/NameResolution.jrag
+++ /dev/null
@@ -1,12 +0,0 @@
-aspect NameResolutionExtended {
-  refine NameResolution eq Program.resolveTypeDecl(String name) {
-    try {
-      return refined(name);
-    } catch (RuntimeException e) {
-      System.err.println(e.getMessage());
-      TypeDecl dummy = new TypeDecl();
-      dummy.setName("(" + name + ")");
-      return dummy;
-    }
-  }
-}
diff --git a/grammar2uml/src/main/jastadd/Navigation.jrag b/grammar2uml/src/main/jastadd/Navigation.jrag
deleted file mode 100644
index ccd0f3d49ede4fbecfafafff05b5228e42c76ed9..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/Navigation.jrag
+++ /dev/null
@@ -1,21 +0,0 @@
-aspect Navigation {
-
-  // --- program ---
-  eq Grammar2Uml.getChild().program() = getProgram();
-  eq MGrammar2Uml.getChild().program() = getGrammar2Uml().program();
-
-  // --- grammar2uml ---
-  inh Grammar2Uml ASTNode.grammar2uml();
-  eq Grammar2Uml.getChild().grammar2uml() = this;
-  eq MGrammar2Uml.getChild().grammar2uml() = getGrammar2Uml();
-
-  // --- containedFile ---
-  eq Grammar2Uml.getChild().containedFile() = null;
-  eq MGrammar2Uml.getChild().containedFile() = null;
-
-  // --- containedFileName ---
-  eq Grammar2Uml.getChild().containedFileName() = getFileName();
-  eq Program.getChild().containedFileName() = null;
-  eq Grammar.getChild().containedFileName() = null;
-  eq MGrammar2Uml.getChild().containedFileName() = null;
-}
diff --git a/grammar2uml/src/main/jastadd/backend/Configuration.jadd b/grammar2uml/src/main/jastadd/backend/Configuration.jadd
deleted file mode 100644
index 8dc259407fd5fff7e314af0875e318b0aaa288e2..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/backend/Configuration.jadd
+++ /dev/null
@@ -1,2 +0,0 @@
-aspect Configuration {
-}
diff --git a/grammar2uml/src/main/jastadd/backend/Generation.jadd b/grammar2uml/src/main/jastadd/backend/Generation.jadd
deleted file mode 100644
index 45276bd4aa3744de020a75512bc9ef62e7e130fb..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/backend/Generation.jadd
+++ /dev/null
@@ -1,215 +0,0 @@
-aspect GenerationUtils {
-  public static final String ASTNode.aspectIndent = "  ";
-
-  public String ASTNode.ind(int n) {
-    StringBuilder s = new StringBuilder();
-    for (int i = 0; i < n; i++) {
-      s.append(aspectIndent);
-    }
-    return s.toString();
-  }
-
-  // --- prettyPrint ---
-  syn String JavaTypeUse.prettyPrint() {
-    StringBuilder sb = new StringBuilder();
-    generateAbstractGrammar(sb);
-    return sb.toString();
-  }
-}
-
-aspect AttributesForMustache {
-  // --- MFolder ---
-  syn String MFolder.name() = getFolder().getName();
-
-  // --- MTypeDecl ---
-  eq MTypeDecl.getInnerTokenComponent(int i).isFirst() = i == 0;
-  eq MTypeDecl.getInnerTokenComponent(int i).isLast() = i == getNumInnerTokenComponent() - 1;
-
-  syn boolean MTypeDecl.isAbstract() = getType().getAbstract();
-  syn String MTypeDecl.name() = getType().getName();
-
-  // --- MTokenComponent ---
-  syn String MTokenComponent.name() = getToken().getName();
-  inh boolean MTokenComponent.isFirst();
-  inh boolean MTokenComponent.isLast();
-
-  // --- MContainment ---
-  syn String MContainment.typeName() = getType().getName();
-  syn String MContainment.componentName() = getComponent().getName();
-  syn String MContainment.modifier();
-  eq MSingleContainment.modifier() = "\"1\"";
-  eq MOptContainment.modifier() = "\"0 .. 1\"";
-  eq MListContainment.modifier() = "\"*\"";
-
-  // --- MRelation ---
-  syn String MRelation.leftName() = getLeft().getName();
-  syn String MRelation.rightName() = getRight().getName();
-  syn boolean MRelation.isBidirectional() = getBidirectional();
-//  syn String MRelation.modifier();
-//  eq MSingleRelation.modifier() = "\"1\"";
-//  eq MOptRelation.modifier() = "\"0 .. 1\"";
-//  eq MListRelation.modifier() = "\"*\"";
-
-  // --- MInheritance ---
-  syn String MInheritance.superClassName() = getSuperClass().getName();
-  syn String MInheritance.subClassName() = getSubClass().getName();
-
-  // --- toMContainment ---
-  syn MContainment TypeComponent.toMContainment();
-  eq NormalComponent.toMContainment() = new MSingleContainment();
-  eq ListComponent.toMContainment() = new MListContainment();
-  eq OptComponent.toMContainment() = new MOptContainment();
-
-  // --- toMRelation ---
-  syn MRelation Relation.toMRelation();
-  eq DirectedRelation.toMRelation() {
-    MRelation result = new MRelation();
-    result.setRightModifier(getSource().toMRelationModifier());
-    result.setBidirectional(false);
-    return result;
-  }
-  eq BidirectionalRelation.toMRelation() {
-    MRelation result = new MRelation();
-    result.setLeftModifier(getRight().toMRelationModifier());
-    result.setRightModifier(getLeft().toMRelationModifier());
-    result.setBidirectional(true);
-    return result;
-  }
-
-  // --- toMRelationModifier ---
-  syn String Role.toMRelationModifier();
-  eq NormalRole.toMRelationModifier() = "\"1\"";
-  eq ListRole.toMRelationModifier() = "\"0 .. 1\"";
-  eq OptRole.toMRelationModifier() = "\"*\"";
-  eq UnnamedRole.toMRelationModifier() {
-    throw new RuntimeException("UnnamedRole cannot be converted to MRelation");
-  }
-
-  // --- toMustache ---
-  syn lazy MGrammar2Uml Grammar2Uml.toMustache() {
-    MGrammar2Uml result = new MGrammar2Uml();
-    result.setGrammar2Uml(this);
-    for (Folder folder : getFolderList()) {
-      result.addFolder(folder.toMustache());
-    }
-    for (TypeDecl typeDecl : getProgram().typeDecls()) {
-      if (!typeDecl.hasSourceFolder()) {
-        result.addOtherType(typeDecl.toMustache());
-      }
-      for (Component component : typeDecl.getComponentList()) {
-        if (component.isTypeComponent()) {
-          TypeComponent typeComponent = component.asTypeComponent();
-          MContainment containment = typeComponent.toMContainment();
-          containment.setType(typeDecl);
-          containment.setComponent(component.asTypeComponent().getTypeDecl());
-          if (!component.getName().isEmpty() && !component.getName().equals(component.asTypeComponent().getTypeDecl().getName())) {
-            containment.setLabel(component.getName());
-          }
-          result.addContainment(containment);
-        }
-      }
-      if (typeDecl.hasSuperType()) {
-        MInheritance inheritance = new MInheritance();
-        inheritance.setSuperClass(typeDecl.getSuperType());
-        inheritance.setSubClass(typeDecl);
-        result.addInheritance(inheritance);
-      }
-    }
-    for (Relation relation : getProgram().relations()) {
-      if (relation.isDirectedRelation()) {
-        DirectedRelation directedRelation = relation.asDirectedRelation();
-        MRelation mRelation = directedRelation.toMRelation();
-        mRelation.setLeft(directedRelation.getSource().getType());
-        mRelation.setRight(directedRelation.getTarget().getType());
-        mRelation.setLabel(directedRelation.getSource().getName());
-        result.addRelation(mRelation);
-      } else {
-        BidirectionalRelation bidiRelation = relation.asBidirectionalRelation();
-        MRelation mRelation = bidiRelation.toMRelation();
-        mRelation.setLeft(bidiRelation.getLeft().getType());
-        mRelation.setRight(bidiRelation.getRight().getType());
-//        mRelation.setLabel(bidiRelation.getSource().getName());
-        result.addRelation(mRelation);
-      }
-    }
-    return result;
-  }
-
-  syn lazy MFolder Folder.toMustache() {
-    MFolder result = new MFolder();
-    result.setFolder(this);
-    for (TypeDecl typeDecl : getTypeList()) {
-      result.addInnerTypeDecl(typeDecl.toMustache());
-    }
-    return result;
-  }
-
-  syn lazy MTypeDecl TypeDecl.toMustache() {
-    MTypeDecl result = new MTypeDecl();
-    result.setType(this);
-    for (Component component : getComponentList()) {
-      if (component.isTokenComponent()) {
-        result.addInnerTokenComponent(component.asTokenComponent().toMustache());
-      }
-    }
-    return result;
-  }
-
-  syn lazy MTokenComponent TokenComponent.toMustache() {
-    MTokenComponent result = new MTokenComponent();
-    result.setToken(this);
-    return result;
-  }
-
-}
-
-aspect AspectGeneration {
-  syn String Grammar2Uml.generateAspect() = toMustache().generateAspect();
-
-  syn String MGrammar2Uml.generateAspect() {
-    StringBuilder sb = new StringBuilder();
-    com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
-      @Override
-      public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
-        return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
-          @Override
-          protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
-            com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
-            if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
-              throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
-            }
-            return wrapper;
-          }
-        };
-      }
-    };
-    com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
-//    mf.setObjectHandler(roh);
-    com.github.mustachejava.Mustache m = mf.compile("grammar2uml.mustache");
-    m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
-    return sb.toString();
-  }
-  public class AppendableWriter extends java.io.Writer {
-    private final StringBuilder sb;
-
-    public AppendableWriter(StringBuilder sb) {
-      this.sb = sb;
-    }
-
-    @Override
-    public void write(char[] chars, int off, int len) {
-      sb.append(chars, off, len);
-    }
-
-    @Override
-    public void write(String str) {
-      sb.append(str);
-    }
-
-    @Override
-    public void flush() {}
-
-    @Override
-    public void close() {}
-  }
-}
diff --git a/grammar2uml/src/main/jastadd/parser/Grammar2Uml.parser b/grammar2uml/src/main/jastadd/parser/Grammar2Uml.parser
deleted file mode 100644
index df2865f7f39906268d759884ec359a046d02f3ae..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/parser/Grammar2Uml.parser
+++ /dev/null
@@ -1,23 +0,0 @@
-Grammar2Uml grammar2uml
-  = folder.f grammar2uml.r  {: r.getFolderList().insertChild(f, 0); return r; :}
-  |                         {: return new Grammar2Uml(); :}
-;
-
-Folder folder
-  = FOLDER ID.name COL type_use_list.t
-    {:
-      Folder result = new Folder();
-      result.setName(name);
-      java.util.List<TypeDecl> typeDecls = ((ArrayList<Object>) t).stream().map(x -> TypeDecl.createRef(((Symbol) x).value.toString())).collect(java.util.stream.Collectors.toList());
-      java.util.Collections.reverse(typeDecls);
-      for (TypeDecl typeDecl : typeDecls) {
-        result.addType(typeDecl);
-      }
-      return result;
-    :}
-;
-
-ArrayList type_use_list
-  = ID
-  | type_use_list COMMA ID
-;
diff --git a/grammar2uml/src/main/jastadd/parser/Preamble.parser b/grammar2uml/src/main/jastadd/parser/Preamble.parser
deleted file mode 100644
index b65eedbcac607a2495d8d514d48ba2eb7d415ed2..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/parser/Preamble.parser
+++ /dev/null
@@ -1,7 +0,0 @@
-%header {:
-package de.tudresden.inf.st.jastadd.grammar2uml.parser;
-import de.tudresden.inf.st.jastadd.grammar2uml.ast.*;
-:};
-
-%goal goal;
-%goal grammar2uml;
diff --git a/grammar2uml/src/main/jastadd/scanner/Header.flex b/grammar2uml/src/main/jastadd/scanner/Header.flex
deleted file mode 100644
index 1fa63001776ed524713f182197ccb097127394a8..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/scanner/Header.flex
+++ /dev/null
@@ -1,20 +0,0 @@
-package de.tudresden.inf.st.jastadd.grammar2uml.scanner;
-
-import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser.Terminals;
-%%
-
-%public
-%final
-%class Grammar2UmlScanner
-%extends beaver.Scanner
-
-%type beaver.Symbol
-%function nextToken
-%yylexthrow beaver.Scanner.Exception
-%scanerror Grammar2UmlScanner.ScannerError
-
-%x COMMENT
-%s DECLARATION
-
-%line
-%column
diff --git a/grammar2uml/src/main/jastadd/scanner/Keywords.flex b/grammar2uml/src/main/jastadd/scanner/Keywords.flex
deleted file mode 100644
index da23f5cac26860abdda8b61003d41a04022d44a5..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/scanner/Keywords.flex
+++ /dev/null
@@ -1 +0,0 @@
-"folder"       { yybegin(DECLARATION); return sym(Terminals.FOLDER); }
diff --git a/grammar2uml/src/main/jastadd/scanner/Macros.flex b/grammar2uml/src/main/jastadd/scanner/Macros.flex
deleted file mode 100644
index eac10a8da1b6ed7c8b01243996e99d25ab119bbd..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/scanner/Macros.flex
+++ /dev/null
@@ -1,2 +0,0 @@
-//MappingContent = [{][:][^:]*[:]+([^:}][^:]*[:]+)*[}]
-ID = [:jletter:][:jletterdigit:]*
diff --git a/grammar2uml/src/main/jastadd/scanner/MappingContent.flex b/grammar2uml/src/main/jastadd/scanner/MappingContent.flex
deleted file mode 100644
index fc2e9c220346d3193ff8c973de951df4a25789c1..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/jastadd/scanner/MappingContent.flex
+++ /dev/null
@@ -1 +0,0 @@
-//{MappingContent} { return sym(Terminals.MAPPING_CONTENT); }
diff --git a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
deleted file mode 100644
index f318da191014d5df7410c0f5d0f4c5edf06029f6..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
+++ /dev/null
@@ -1,195 +0,0 @@
-package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
-
-import beaver.Parser;
-import org.jastadd.option.BooleanOption;
-import org.jastadd.option.ValueOption;
-import org.jastadd.relast.compiler.AbstractCompiler;
-import org.jastadd.relast.compiler.CompilerException;
-import de.tudresden.inf.st.jastadd.grammar2uml.ast.ErrorMessage;
-import de.tudresden.inf.st.jastadd.grammar2uml.ast.GrammarFile;
-import de.tudresden.inf.st.jastadd.grammar2uml.ast.Program;
-import de.tudresden.inf.st.jastadd.grammar2uml.ast.Grammar2Uml;
-import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
-import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-
-public class Compiler extends AbstractCompiler {
-
-  private ValueOption optionOutputFile;
-  private ValueOption optionInputGrammar2Uml;
-  private BooleanOption optionDefaultFolders;
-  private BooleanOption optionHelp;
-  private BooleanOption optionVersion;
-  private BooleanOption optionVerbose;
-
-  public Compiler() {
-    super("grammar2uml", false);
-  }
-
-  /**
-   * Reads the version string.
-   *
-   * The version string is read from the property file
-   * src/main/resources/Version.properties. This
-   * file should be generated during the build process. If it is missing
-   * then there is some problem in the build script.
-   *
-   * @author Jesper Öqvist <jesper.oqvist@cs.lth.se>
-   * @return the read version string, or <code>version ?</code>
-   */
-  private String readVersion() {
-    try {
-      return ResourceBundle.getBundle("grammar2umlVersion").getString("version");
-    } catch (MissingResourceException e) {
-      return "version ?";
-    }
-  }
-
-  public static void main(String[] args) {
-    System.setProperty("mustache.debug", "true");
-    try {
-      new Compiler().run(args);
-    } catch (CompilerException e) {
-      System.err.println(e.getMessage());
-      System.exit(-1);
-    }
-  }
-
-  private void printMessage(String message) {
-    System.out.println(message);
-  }
-
-  private void writeToFile(String filename, String str) throws CompilerException {
-    try {
-      PrintWriter writer = new PrintWriter(filename);
-      writer.print(str);
-      writer.close();
-    } catch (Exception e) {
-      throw new CompilerException("Could not write to file " + filename, e);
-    }
-  }
-
-  protected void initOptions() {
-    optionOutputFile = addOption(
-        new ValueOption("output", "target file to be generated.")
-            .defaultValue("uml.md")
-            .acceptAnyValue()
-            .needsValue(false));
-    optionInputGrammar2Uml = addOption(
-        new ValueOption("inputGrammar2Uml", "grammar2uml definition file.")
-            .needsValue(true));
-    optionDefaultFolders = addOption(
-        new BooleanOption("defaultFolders",
-            "Creates a default folder per grammar file.")
-        .defaultValue(false));
-    optionHelp = addOption(
-        new BooleanOption("help", "Print usage and exit.")
-        .defaultValue(false));
-    optionVersion = addOption(
-        new BooleanOption("version", "Print version and exit.")
-        .defaultValue(false));
-    optionVerbose = addOption(
-        new BooleanOption("verbose", "Print more messages while compiling.")
-        .defaultValue(false));
-  }
-
-  private Grammar2Uml parseProgram() throws CompilerException {
-    Program program = new Program();
-    Grammar2Uml grammar2Uml;
-
-    for (String inputGrammarFileName : getConfiguration().getFiles()) {
-      printMessage("Parsing " + inputGrammarFileName);
-      GrammarFile inputGrammar;
-      try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammarFileName))) {
-        Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
-        Grammar2UmlParser parser = new Grammar2UmlParser();
-        inputGrammar = (GrammarFile) parser.parse(scanner);
-        if (optionVerbose.value()) {
-          inputGrammar.dumpTree(System.out);
-        }
-        program.addGrammarFile(inputGrammar);
-        inputGrammar.setFileName(inputGrammarFileName);
-      } catch (IOException | Parser.Exception e) {
-        throw new CompilerException("Could not parse grammar file " + inputGrammarFileName, e);
-      }
-    }
-
-    if (optionInputGrammar2Uml.isMatched()) {
-      String inputGrammar2UmlFileName = optionInputGrammar2Uml.value();
-      try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammar2UmlFileName))) {
-        Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
-        Grammar2UmlParser parser = new Grammar2UmlParser();
-        grammar2Uml = (Grammar2Uml) parser.parse(scanner, Grammar2UmlParser.AltGoals.grammar2uml);
-        grammar2Uml.setFileName(inputGrammar2UmlFileName);
-      } catch (IOException | Parser.Exception e) {
-        throw new CompilerException("Could not parse grammar2uml file " + inputGrammar2UmlFileName, e);
-      }
-    } else {
-      // no special setting given
-      grammar2Uml = new Grammar2Uml();
-      grammar2Uml.setFileName("<none>");
-    }
-    grammar2Uml.setProgram(program);
-    grammar2Uml.treeResolveAll();
-    if (optionDefaultFolders.value()) {
-      for (GrammarFile grammarFile : program.getGrammarFileList()) {
-        grammar2Uml.addFolder(grammarFile.defaultFolder());
-      }
-    }
-    return grammar2Uml;
-  }
-
-  @Override
-  protected int compile() throws CompilerException {
-    if (optionVersion.value()) {
-      System.out.println(readVersion());
-      return 0;
-    }
-    if (optionHelp.value()) {
-      getConfiguration().printHelp(System.out);
-      return 0;
-    }
-
-    printMessage("Running grammar2uml " + readVersion());
-
-    String output;
-    if (optionOutputFile.isMatched()) {
-      output = optionOutputFile.value();
-    } else {
-      output = "uml.md";
-      System.out.println("No output output file is set. Assuming '" + output + "'.");
-    }
-    Path parent = Paths.get(optionOutputFile.value()).toAbsolutePath().getParent();
-    try {
-      Files.createDirectories(parent);
-    } catch (IOException e) {
-      throw new CompilerException("Error creating output dir " + parent, e);
-    }
-
-    if (getConfiguration().getFiles().isEmpty()) {
-      throw new CompilerException("No input grammars specified!");
-    }
-
-    Grammar2Uml grammar2uml = parseProgram();
-
-    if (!grammar2uml.errors().isEmpty()) {
-      System.err.println("Errors:");
-      for (ErrorMessage e : grammar2uml.errors()) {
-        System.err.println(e);
-      }
-      System.exit(1);
-    }
-
-    printMessage("Writing output file " + output);
-    writeToFile(output, grammar2uml.generateAspect());
-    return 0;
-  }
-}
diff --git a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlMain.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlMain.java
deleted file mode 100644
index e552d5548514363c535a2943ac97a1f35788bd8d..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlMain.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
-
-import beaver.Parser;
-import de.tudresden.inf.st.jastadd.grammar2uml.ast.*;
-import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
-import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-/**
- * Testing Grammar2Uml without parser.
- *
- * @author rschoene - Initial contribution
- */
-public class Grammar2UmlMain {
-
-  public static void main(String[] args) {
-//    testing();
-    processManualAST();
-  }
-
-  public static Grammar2Uml createManualAST() {
-    System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
-    System.setProperty("mustache.debug", "true");
-    Grammar2Uml model = new Grammar2Uml();
-    Path path = Paths.get("grammar2uml.base", "src", "test", "resources", "Example.relast");
-    System.out.println("path.toFile().getAbsolutePath() = " + path.toFile().getAbsolutePath());
-    Program program = parseProgram(path);
-    model.setProgram(program);
-
-    Folder folder1 = new Folder();
-    folder1.setName("Folder1");
-    folder1.addType(program.resolveTypeDecl("Wert"));
-    folder1.addType(program.resolveTypeDecl("Quelle"));
-    model.addFolder(folder1);
-
-    return model;
-  }
-
-  private static void processManualAST() {
-    Grammar2Uml model = createManualAST();
-    model.treeResolveAll();
-    for (Folder f : model.getFolderList()) {
-      System.out.println(f.getName() + ":");
-      for (TypeDecl typeDecl : f.getTypeList()) {
-        System.out.println(typeDecl.getName());
-      }
-    }
-
-    System.out.println(model.generateAspect());
-  }
-
-  public static Program parseProgram(Path path) {
-    try (BufferedReader reader = Files.newBufferedReader(path)) {
-      Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
-      Grammar2UmlParser parser = new Grammar2UmlParser();
-      GrammarFile grammarFile = (GrammarFile) parser.parse(scanner);
-      Program program = new Program();
-      program.addGrammarFile(grammarFile);
-      return program;
-    } catch (IOException | Parser.Exception e) {
-      e.printStackTrace();
-    }
-    return null;
-  }
-}
diff --git a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java
deleted file mode 100644
index 496daa3bbe6f1745e2b69c6b0b412e0b1b62d29b..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
-
-import java.util.*;
-import java.util.function.Predicate;
-
-import static java.util.stream.Collectors.toList;
-
-public class Utils {
-  public static <T> List<T> filterToList(Collection<T> collection, Predicate<T> predicate) {
-    return collection.stream().filter(predicate).collect(toList());
-  }
-
-  public static <T> Set<T> asSet(T... t) {
-    return new HashSet<T>(Arrays.asList(t));
-  }
-}
diff --git a/grammar2uml/src/main/resources/Containment.mustache b/grammar2uml/src/main/resources/Containment.mustache
deleted file mode 100644
index 48dbc880dc31026777fbd8e281b75b409bcbac4c..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/Containment.mustache
+++ /dev/null
@@ -1 +0,0 @@
-{{typeName}} *-- {{{modifier}}} {{componentName}} {{#Label}}: {{Label}}{{/Label}}
diff --git a/grammar2uml/src/main/resources/Folder.mustache b/grammar2uml/src/main/resources/Folder.mustache
deleted file mode 100644
index e9890e1ba8c128f89db6a45107c2ce99746cbd7a..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/Folder.mustache
+++ /dev/null
@@ -1,3 +0,0 @@
-package {{name}} <<Folder>> {
-{{#InnerTypeDecls}}  {{> TypeDecl}}{{/InnerTypeDecls}}
-}
diff --git a/grammar2uml/src/main/resources/Inheritance.mustache b/grammar2uml/src/main/resources/Inheritance.mustache
deleted file mode 100644
index a8f10c07b587f926b60a45646de70e422e68caf0..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/Inheritance.mustache
+++ /dev/null
@@ -1 +0,0 @@
-{{superClassName}} <|-- {{subClassName}}
diff --git a/grammar2uml/src/main/resources/Relation.mustache b/grammar2uml/src/main/resources/Relation.mustache
deleted file mode 100644
index 7f23515c96462c8e86a24172d77cea66c81eaac7..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/Relation.mustache
+++ /dev/null
@@ -1 +0,0 @@
-{{leftName}} {{{leftModifier}}} {{#isBidirectional}}<{{/isBidirectional}}--> {{{rightModifier}}} {{rightName}} {{#Label}}: {{Label}}{{/Label}}
diff --git a/grammar2uml/src/main/resources/TypeDecl.mustache b/grammar2uml/src/main/resources/TypeDecl.mustache
deleted file mode 100644
index a72b5dcfcdc2b8fe11ca240402d98cc787340d83..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/TypeDecl.mustache
+++ /dev/null
@@ -1,5 +0,0 @@
-{{=<% %>=}}<%#isAbstract%>abstract <%/isAbstract%>class <%name%><%#InnerTokenComponents%><%#first%> {
-<%/first%>
-  <%name%>
-<%#last%>
-}<%/last%><%/InnerTokenComponents%><%={{ }}=%>
diff --git a/grammar2uml/src/main/resources/grammar2uml.mustache b/grammar2uml/src/main/resources/grammar2uml.mustache
deleted file mode 100644
index 6fe27d0d1b3f4e39170b693153c5675f1912c7a1..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/grammar2uml.mustache
+++ /dev/null
@@ -1,11 +0,0 @@
-```plantuml
-@startuml
-hide circle
-hide methods
-{{#Folders}}{{> Folder}}{{/Folders}}
-{{#OtherTypes}}{{> TypeDecl}}{{/OtherTypes}}
-{{#Containments}}{{> Containment}}{{/Containments}}
-{{#Relations}}{{> Relation}}{{/Relations}}
-{{#Inheritances}}{{> Inheritance}}{{/Inheritances}}
-@enduml
-```
diff --git a/grammar2uml/src/main/resources/grammar2umlVersion.properties b/grammar2uml/src/main/resources/grammar2umlVersion.properties
deleted file mode 100644
index 3a408660377b15e06ef7d0249b1da178b5ea006d..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/grammar2umlVersion.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#Fri Jan 15 20:11:10 CET 2021
-version=0.1.1
diff --git a/grammar2uml/src/main/resources/log4j2.xml b/grammar2uml/src/main/resources/log4j2.xml
deleted file mode 100644
index 98cfd73c75df58d8598521bc10b043e214ec4ad8..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/resources/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="INFO">
-    <Appenders>
-        <Console name="Console" target="SYSTEM_OUT">
-            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
-        </Console>
-    </Appenders>
-    <Loggers>
-        <Root level="info">
-            <AppenderRef ref="Console"/>
-        </Root>
-    </Loggers>
-</Configuration>
\ No newline at end of file
diff --git a/grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java b/grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java
deleted file mode 100644
index 5d43d2bd2f22a0d72eef263fbd1e467e9a39c942..0000000000000000000000000000000000000000
--- a/grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package de.tudresden.inf.st.jastadd.grammar2uml.test;
-
-import org.jastadd.relast.compiler.CompilerException;
-import de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler;
-import org.junit.jupiter.api.Test;
-
-import java.io.File;
-import java.nio.file.Paths;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * TODO: Add description.
- *
- * @author rschoene - Initial contribution
- */
-public class CompilerTest {
-
-  @SuppressWarnings("SameParameterValue")
-  void transform(String inputGrammar, String inputGrammar2Uml, String output) throws CompilerException {
-
-    System.out.println("Running test in directory '" + Paths.get(".").toAbsolutePath() + "'.");
-    assertTrue(Paths.get(inputGrammar).toFile().exists(), "input grammar does not exist");
-    assertTrue(Paths.get(inputGrammar2Uml).toFile().exists(), "input grammar2uml does not exist");
-    assertFalse(Paths.get(inputGrammar).toFile().isDirectory(), "input grammar is a directory");
-    assertFalse(Paths.get(inputGrammar2Uml).toFile().isDirectory(), "input grammar2uml is a directory");
-
-    File outputFile = Paths.get(output).toFile();
-    File outputDirFile = outputFile.getParentFile();
-    if (!outputDirFile.exists()) {
-      assertTrue(outputDirFile.mkdir());
-    }
-
-    String[] args = {
-        "--output=" + output,
-        "--inputGrammar2Uml=" + inputGrammar2Uml,
-        "--defaultFolders",
-        inputGrammar
-    };
-
-    new Compiler().run(args);
-  }
-
-  @Test
-  void transformMinimalExample() throws CompilerException {
-    transform("src/test/resources/Example.relast", "src/test/resources/Example.grammar2uml", "src/test/resources/uml.md");
-  }
-}
diff --git a/grammar2uml/src/test/resources/.gitignore b/grammar2uml/src/test/resources/.gitignore
deleted file mode 100644
index 9749ac39920e0a1fb55067777edd7d9372108858..0000000000000000000000000000000000000000
--- a/grammar2uml/src/test/resources/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-uml.md
diff --git a/grammar2uml/src/test/resources/Example.grammar2uml b/grammar2uml/src/test/resources/Example.grammar2uml
deleted file mode 100644
index 6665ad50fad2535f1596393ca59672827d69d99b..0000000000000000000000000000000000000000
--- a/grammar2uml/src/test/resources/Example.grammar2uml
+++ /dev/null
@@ -1,3 +0,0 @@
-folder Datenfluss: Prüfmethode, Funktionsprinzip, Datenausgangswert, Verarbeitungsschritt, Dateneingangswert, Wert, Datenquelle, Quelle
-folder Darstellung: DarstellungsElement, Dezimalwert, Auffälligkeitsmarkierung, Text
-
diff --git a/grammar2uml/src/test/resources/Example.relast b/grammar2uml/src/test/resources/Example.relast
deleted file mode 100644
index 0eb3846f541bdf720d7adf3687defa446c8c30bd..0000000000000000000000000000000000000000
--- a/grammar2uml/src/test/resources/Example.relast
+++ /dev/null
@@ -1,37 +0,0 @@
-Root ::= Prüfmethode* Wert*;
-
-// Beispiel für Prüfmethode: ReLiAbweichung
-Prüfmethode ::= Funktionsprinzip Zielobjekt*;
-Zielobjekt;
-
-rel Prüfmethode.Funktionsprinzip* -> Funktionsprinzip;
-
-// Beispiel Funktionsprinzip: Berechnungen innerhalb von ReLi-Abweichung für ist_diff und ist_diff < max_diff
-Funktionsprinzip ::= Dateneingangswert* Verarbeitungsschritt* Datenausgangswert*;
-
-abstract Datenquelle;
-// Beispiel für Wert: FB_links, ist_diff, max_diff
-Wert : Datenquelle ::= <Datentyp> <A> <B>;
-
-// Ein Wert, der bei "Dateneingang" steht, z.B. für ReLi: max_Diff oder FB,links
-// Beispiel für Dateneingangswert: Zuordnung, aus welcher Quelle der Wert ausgelesen werden soll
-Dateneingangswert ::= [Quelle];
-rel Dateneingangswert.Wert -> Wert;
-
-// Beispiel für Verarbeitungsschritt: ist_Diff = ..., oder ist_diff < max_diff
-Verarbeitungsschritt : Datenquelle ::= <Berechnungsvorschrift>;
-rel Verarbeitungsschritt.EingabeWert* -> Wert;
-
-// Ein Wert, der bei "Datenausgang" steht, z.B. für ReLi $ist_{Diff,Achse n}$
-Datenausgangswert ::= DarstellungsElement;
-rel Datenausgangswert.Wert -> Wert;
-
-// Beispiel für Quelle: Rollenbremsprüfstand
-Quelle;
-
-abstract DarstellungsElement ;
-Dezimalwert : DarstellungsElement;
-Auffälligkeitsmarkierung : DarstellungsElement;
-Text : DarstellungsElement;
-
-rel Auffälligkeitsmarkierung.Element <-> DarstellungsElement.Auffälligkeitsmarkierung;
diff --git a/grammar2uml/src/test/resources/log4j2.xml b/grammar2uml/src/test/resources/log4j2.xml
deleted file mode 100644
index 98cfd73c75df58d8598521bc10b043e214ec4ad8..0000000000000000000000000000000000000000
--- a/grammar2uml/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="INFO">
-    <Appenders>
-        <Console name="Console" target="SYSTEM_OUT">
-            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
-        </Console>
-    </Appenders>
-    <Loggers>
-        <Root level="info">
-            <AppenderRef ref="Console"/>
-        </Root>
-    </Loggers>
-</Configuration>
\ No newline at end of file
diff --git a/pages/.gitignore b/pages/.gitignore
index 87174b686c0670d3fb6a66535729af2a8a249d72..14338f39f89e2a45e3473a6597e640aa9458f9a8 100644
--- a/pages/.gitignore
+++ b/pages/.gitignore
@@ -1 +1,2 @@
-/public/
+/docs/ragdoc/
+__pycache__
diff --git a/pages/Makefile b/pages/Makefile
deleted file mode 100644
index 7878ee5305af7d6c04860113aa9ef008991d07af..0000000000000000000000000000000000000000
--- a/pages/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line, and also
-# from the environment for the first two.
-SPHINXOPTS    ?=
-SPHINXBUILD   ?= sphinx-build
-SOURCEDIR     = .
-BUILDDIR      = public
-
-# Put it first so that "make" without argument is like "make help".
-help:
-	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
-	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/pages/_static/grammar2uml.png b/pages/_static/grammar2uml.png
deleted file mode 100644
index f7002d330f635abc5be675fdd32d8d9421dd97cd..0000000000000000000000000000000000000000
Binary files a/pages/_static/grammar2uml.png and /dev/null differ
diff --git a/pages/_static/minimal-example.png b/pages/_static/minimal-example.png
deleted file mode 100644
index 6582a4eda68b936051b45991d04a3bba58963c5e..0000000000000000000000000000000000000000
Binary files a/pages/_static/minimal-example.png and /dev/null differ
diff --git a/pages/conf.py b/pages/conf.py
deleted file mode 100644
index 7483de1d681697c751dc1d215aa6dc35a7177d83..0000000000000000000000000000000000000000
--- a/pages/conf.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Configuration file for the Sphinx documentation builder.
-#
-# This file only contains a selection of the most common options. For a full
-# list see the documentation:
-# https://www.sphinx-doc.org/en/master/usage/configuration.html
-
-# -- Path setup --------------------------------------------------------------
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#
-# import os
-# import sys
-# sys.path.insert(0, os.path.abspath('.'))
-import sphinx_rtd_theme
-
-
-# -- Project information -----------------------------------------------------
-
-project = 'Relast2Uml'
-copyright = '2021, René Schöne'
-author = 'René Schöne'
-
-
-# -- General configuration ---------------------------------------------------
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = [
-    'sphinx_rtd_theme',
-    'recommonmark',
-    'sphinxemoji.sphinxemoji',
-    'sphinx_markdown_tables'
-]
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-# This pattern also affects html_static_path and html_extra_path.
-exclude_patterns = ['public', 'Thumbs.db', '.DS_Store']
-
-
-# -- Options for HTML output -------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-#
-html_theme = 'sphinx_rtd_theme'
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-sphinxemoji_style = 'twemoji'
diff --git a/pages/custom_theme/footer.html b/pages/custom_theme/footer.html
new file mode 100644
index 0000000000000000000000000000000000000000..2d6c164695b3a1ed19711d3497caf142a8416ebb
--- /dev/null
+++ b/pages/custom_theme/footer.html
@@ -0,0 +1,11 @@
+{% block footer %}
+<p>{% if config.copyright %}
+<small>{{ config.copyright }}<br></small>
+{% endif %}
+<hr>
+Built with <a href="https://www.mkdocs.org/">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
+{% if page and page.meta and page.meta.git_revision_date_localized %}
+<small><br><i>Last updated {{ page.meta.git_revision_date_localized }}</i></small>
+{% endif %}
+</p>
+{% endblock %}
diff --git a/pages/adding.md b/pages/docs/adding.md
similarity index 73%
rename from pages/adding.md
rename to pages/docs/adding.md
index ef101c862dc944f9ff73184071d53cb99fb0888a..2822997b946d501554f3644f016452e95a1ee0a7 100644
--- a/pages/adding.md
+++ b/pages/docs/adding.md
@@ -66,36 +66,22 @@ If you want to build the tools of `Relast2Uml` from source, first build the jar
 git clone https://git-st.inf.tu-dresden.de/jastadd/relast2uml.git
 cd relast2uml
 ./gradlew jar
-ls grammar2uml/build/libs/ dumpAst/build/libs/ dumpAstWithPlantuml/build/libs/
+ls dumpAst/build/libs/ dumpAstWithPlantuml/build/libs/
 ```
 
-Those JARs can then be copied to your project, e.g. for grammar2uml.
+Those JARs can then be copied to your project.
 
 ```bash
-cp grammar2uml/build/libs/grammar2uml-<version>.jar ../your-project/libs/grammar2uml.jar
+cp dumpAst/build/libs/dumpAst-<version>.jar ../your-project/libs/dumpAst.jar
 cd ../your-project/
 ```
 
-Finally, this JAR has to be integrated into your build process. In case, [Gradle](https://gradle.org/) is used, the JAR file needs to be added as dependency using:
+Finally, this JAR has to be integrated into your build process. In case [Gradle](https://gradle.org/) is used, the JAR file needs to be added as dependency using:
 
 ```groovy
 dependencies {
-    implementation fileTree(include: ['grammar2uml.jar'], dir: 'libs')
     implementation fileTree(include: ['dumpAst.jar'], dir: 'libs')
 }
 ```
 
 The path to the JAR file may need to be changed according to your project structure.
-In case of `grammar2uml` a task needs to be created, similar to the one defined [above](#grammar2uml).
-
-```groovy
-task grammar2uml(type: JavaExec) {
-    main = '-jar'
-
-    args([
-            '../libs/grammar2uml.jar',
-            '--verbose',
-            'src/main/jastadd/GoalModel.relast'
-    ])
-}
-```
diff --git a/pages/dumpAst.md b/pages/docs/dumpAst.md
similarity index 99%
rename from pages/dumpAst.md
rename to pages/docs/dumpAst.md
index 06eaaced0a2b639466a26eb8b39ce0a308dd6f09..5b23549bcabfa62a26ef097cac6ed95f0f3ef3fd 100644
--- a/pages/dumpAst.md
+++ b/pages/docs/dumpAst.md
@@ -2,7 +2,7 @@
 
 The tool called `DumpAst` ([see in repo](https://git-st.inf.tu-dresden.de/jastadd/relast2uml/-/tree/master/dumpAst)) is used to create a snapshot of an AST and visualize it.
 
-![](_static/dumpAst.png)
+![](img/dumpAst.png)
 
 It has to be used within your application code, and comes with a fluent interface to add and/or filter parts of the AST.
 First, import the entry point class `Dumper`
diff --git a/pages/_static/dumpAst.png b/pages/docs/img/dumpAst.png
similarity index 100%
rename from pages/_static/dumpAst.png
rename to pages/docs/img/dumpAst.png
diff --git a/pages/grammar2uml.md b/pages/grammar2uml.md
deleted file mode 100644
index bd261d30575ca4e07ea89943226a7fc3abaa04a7..0000000000000000000000000000000000000000
--- a/pages/grammar2uml.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Grammar2Uml
-
-The tool called `Grammar2Uml` ([see in repo](https://git-st.inf.tu-dresden.de/jastadd/relast2uml/-/tree/master/grammar2uml)) takes a set of grammar specifications and creates a visualization similar to UML class diagrams.
-
-![](_static/grammar2uml.png)
-![](_static/minimal-example.png)
-
-Every nonterminal will be shown as a rectangular box with its terminal children listed within it. Children are shown as containment relations between parent and child, whereas relations and intrinsic references are shown using arrows.
-
-Furthermore, some options may be specified.
-
-|         Name         | Required (Default) |                               Description                               |
-|----------------------|--------------------|-------------------------------------------------------------------------|
-| `--output`           | No (`uml.md`)      | target file to be generated.                                            |
-| `--inputGrammar2Uml` | No                 | grammar2uml definition file, see [below](#grammar2uml-definition-file). |
-| `--defaultFolders`   | No (`false`)       | Creates a default folder per grammar file.                              |
-| `--help`             | No                 | Print usage and exit.                                                   |
-| `--version`          | No                 | Print version and exit.                                                 |
-| `--verbose`          | No                 | Print more messages while compiling.                                    |
-
-## Grammar2uml definition file
-
-To structure the generated visualization, any number of nonterminals can be grouped with folders.
-Either specify the option `--defaultFolders` to use one folder per input grammar file, or specify a grammar2uml definition file.
-The definition file has a simple syntax, it is a list of folder specifications. One specifications looks like
-
-```
-folder FOLDER_NAME : NT1, NT2, ..., NT_n
-```
-
-It produces one folder named `FOLDER_NAME` containing all nonterminals (`NT1`, `NT2`, ..., `NT_n`) following it.
diff --git a/pages/index.rst b/pages/index.rst
deleted file mode 100644
index 7497cb04536e3ebe867a5c22d9337653fc0bc5e5..0000000000000000000000000000000000000000
--- a/pages/index.rst
+++ /dev/null
@@ -1,25 +0,0 @@
-.. Relast2Uml documentation master file, created by
-   sphinx-quickstart on Thu Jan 14 17:56:50 2021.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
-
-Relast2Uml Documentation
-========================
-
-`Relast2Uml <https://git-st.inf.tu-dresden.de/jastadd/relast2uml>`_ is a collection of tools to visualize specifications and programs using models based on `Reference Attribute Grammars <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.108.8792&rep=rep1&type=pdf>`_ and `Relation Reference Attribute Grammars <https://doi.org/10.1016/j.cola.2019.100940>`_ built with `JastAdd <http://jastadd.org/>`_.
-
-.. toctree::
-   :maxdepth: 2
-   :caption: Contents:
-
-   dumpAst.md
-   grammar2uml.md
-   adding.md
-
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`search`
diff --git a/pages/main.py b/pages/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..d10da843c522e18f0c8d40a9b5672b7ec2094a22
--- /dev/null
+++ b/pages/main.py
@@ -0,0 +1,29 @@
+import os
+
+dumpAstVersionFileName = '../dumpAst/src/main/resources/dumpAstVersion.properties'
+
+
+def get_version():
+    if os.environ.get('CI_COMMIT_BRANCH', 'unknown') == 'dev':
+        return 'dev'
+    with open(dumpAstVersionFileName) as dumpAstVersionFile:
+        versionFileContent = dumpAstVersionFile.read()
+    return versionFileContent[versionFileContent.rindex('version=') + 8:].strip()
+
+
+def define_env(env):
+    """
+    This is the hook for defining variables, macros and filters
+
+    - variables: the dictionary that contains the environment variables
+    - macro: a decorator function, to declare a macro.
+    """
+    env.conf['site_name'] = 'dumpAst ' + get_version()
+
+    @env.macro
+    def dumpAst_version():
+        return get_version()
+
+
+if __name__ == '__main__':
+    print(get_version())
diff --git a/pages/make.bat b/pages/make.bat
deleted file mode 100644
index 922152e96a04a242e6fc40f124261d74890617d8..0000000000000000000000000000000000000000
--- a/pages/make.bat
+++ /dev/null
@@ -1,35 +0,0 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
-	set SPHINXBUILD=sphinx-build
-)
-set SOURCEDIR=.
-set BUILDDIR=_build
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
-	echo.
-	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
-	echo.installed, then set the SPHINXBUILD environment variable to point
-	echo.to the full path of the 'sphinx-build' executable. Alternatively you
-	echo.may add the Sphinx directory to PATH.
-	echo.
-	echo.If you don't have Sphinx installed, grab it from
-	echo.http://sphinx-doc.org/
-	exit /b 1
-)
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-goto end
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-
-:end
-popd
diff --git a/pages/mkdocs.yml b/pages/mkdocs.yml
new file mode 100644
index 0000000000000000000000000000000000000000..86bada7ed5d5f3762b08bab9667c48e4f5b34e24
--- /dev/null
+++ b/pages/mkdocs.yml
@@ -0,0 +1,26 @@
+site_name: DumpAst
+repo_url: https://git-st.inf.tu-dresden.de/jastadd/relast2uml
+site_dir: ../public
+
+nav:
+  - "DumpAst": dumpAst.md
+  - "Add to your project": adding.md
+  - "API documentation": ragdoc/index.html
+
+theme:
+  name: readthedocs
+  custom_dir: custom_theme/
+
+markdown_extensions:
+  - toc:
+      permalink: 
+  - admonition
+
+plugins:
+  - search
+  - git-revision-date-localized:
+      type: datetime
+      timezone: Europe/Berlin
+      locale: en
+      fallback_to_build_date: True
+  - macros
diff --git a/pages/requirements.txt b/pages/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a5b1bbf825def6db5490bd6d87103c08357d879d
--- /dev/null
+++ b/pages/requirements.txt
@@ -0,0 +1,3 @@
+mkdocs==1.2.2
+mkdocs-git-revision-date-localized-plugin==0.10.3
+mkdocs-macros-plugin==0.6.3
diff --git a/settings.gradle b/settings.gradle
index ddebe309588e3be93657248ddae60011ca7767a9..631fcc974ebb903bd09ad6b13cd6dd5fbd875f3d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,7 +1,6 @@
 rootProject.name = 'relast2uml'
 
 include 'relast.preprocessor'
-include 'grammar2uml'
 include 'dumpAst'
 include 'dumpAstWithPlantuml'
 include 'testDumper'
diff --git a/testDumper/src/main/jastadd/testDumper.jrag b/testDumper/src/main/jastadd/testDumper.jrag
index a0ef867b1fe9da0ef2b7f5c91cb9cfc9877800ea..4cedce8cfa75749d2fb90adcb0ef5766e36e0ca1 100644
--- a/testDumper/src/main/jastadd/testDumper.jrag
+++ b/testDumper/src/main/jastadd/testDumper.jrag
@@ -29,6 +29,9 @@ aspect GrammarGlobal {
 
   syn int Root.simpleAttr() = 42;
   syn A Root.referenceAttr() = getA();
+
+  syn boolean ASTNode.isA() = false;
+  eq A.isA() = true;
 }
 
 aspect GrammarTypeLevel {
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java
index fc3d4c80558d64cf5dc882522e8afd11db04258a..d7b14047c0b144339dead510f89f8ea510e60464 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java
@@ -1,10 +1,14 @@
 package de.tudresden.inf.st.jastadd.testDumper;
 
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpAst;
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
 import org.jastadd.testDumper.ast.A;
 import org.jastadd.testDumper.ast.B;
 import org.jastadd.testDumper.ast.C;
 import org.jastadd.testDumper.ast.Root;
 
+import java.util.stream.Collectors;
+
 public class TestDumperMain {
   public static void main(String[] args) {
     Root root = new Root();
@@ -29,13 +33,20 @@ public class TestDumperMain {
 
     TestUtils.ExposingDumpBuilder builder = new TestUtils.ExposingDumpBuilder(root);
     builder.includeAttributes("simpleAttr")
-        .includeNonterminalAttributes("getCalculated");
+        .orderChildren()
+        .includeNonterminalAttributes("getCalculated")
+        .setNameMethod(n -> n == null ? "null" : n.getClass().getSimpleName());
 
     System.out.println(">> PlantUml");
-    System.out.println(builder.build().toPlantUml());
+    DumpAst dumpAst = builder.build();
+    System.out.println(dumpAst.toPlantUml());
+
+    DumpNode node = dumpAst.getDumpNode(0);
+    System.out.println(node.getName());
+    System.out.println(node.myChildren().stream().map(DumpNode::getName).collect(Collectors.joining(", ")));
 
-    System.out.println(">> YAML begin");
-    System.out.println(builder.build().toYaml(true));
-    System.out.println(">> YAML end");
+//    System.out.println(">> YAML begin");
+//    System.out.println(builder.build().toYaml(true));
+//    System.out.println(">> YAML end");
   }
 }
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
index 507250f1e1dc2b74c794f8b1980a550d105913d5..1ccba6cc518d0ec487b937f0efd81068013ba2af 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
@@ -1,10 +1,9 @@
 package de.tudresden.inf.st.jastadd.testDumper;
 
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpAst;
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpBuilder;
 import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
-import org.jastadd.testDumper.ast.C;
-import org.jastadd.testDumper.ast.D;
-import org.jastadd.testDumper.ast.Root;
-import org.jastadd.testDumper.ast.SubC;
+import org.jastadd.testDumper.ast.*;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
@@ -32,9 +31,9 @@ public class TestSimple {
   @Test void testChildlessNonterminal() {
     Root root = createRoot(createA(A_NAME, a -> a.setD(new D())), null);
     List<DumpNode> nodes = TestUtils.dumpModel(root);
-    Optional<DumpNode> optionalCDumpNode = nodes.stream().filter(n -> n.getObject() instanceof D).findFirst();
-    assertTrue(optionalCDumpNode.isPresent());
-    assertTrue(optionalCDumpNode.get().isAstNode());
+    Optional<DumpNode> actualD = nodes.stream().filter(n -> n.getObject() instanceof D).findFirst();
+    assertTrue(actualD.isPresent());
+    assertTrue(actualD.get().isAstNode());
   }
 
   @Test
@@ -50,6 +49,22 @@ public class TestSimple {
     assertThatMapOf(normalChildren(actualRoot)).containsExactlyInAnyOrder(tuple("A", A_NAME));
   }
 
+  @Test
+  public void testOneNormalChildIncludeNullNodes() {
+    Root root = createRoot(createA(A_NAME), null);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, DumpBuilder::includeNullNodes);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
+        ROOT_NAME, A_NAME, null, null, null, null);
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals(1, actualRoot.getNumDumpToken());
+    assertEquals(2, actualRoot.getNumDumpChildNode());
+    assertEquals(0, actualRoot.getNumDumpRelation());
+
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals(3, actualA.getNumDumpChildNode());
+  }
+
   @Test
   public void testListChildren() {
     Root root = createRoot(null, null, createB(B1_NAME), createB(B2_NAME), createB(B3_NAME));
@@ -63,6 +78,22 @@ public class TestSimple {
     assertThatMapOf(listChildren(actualRoot), "B").containsExactlyInAnyOrder(B1_NAME, B2_NAME, B3_NAME);
   }
 
+  @Test
+  public void testOrderedListChildren() {
+    Root root = createRoot(null, null, createB(B1_NAME), createB(B2_NAME), createB(B3_NAME));
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, DumpBuilder::orderChildren);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B1_NAME, B2_NAME, B3_NAME);
+
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    // in grammar: DumpAst ::= [...] DumpNode* [...];
+    // use getParent() twice as first returns JastAddList for DumpNode list
+    assertTrue(((DumpAst) actualRoot.getParent().getParent()).getPrintConfig().getOrderChildren());
+
+    List<DumpNode> children = actualRoot.myChildren();
+    assertThat(children).extracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(B1_NAME, B2_NAME, B3_NAME);
+  }
+
   @Test
   public void testOneOptChild() {
     Root root = createRoot(null, createC(C_NAME));
@@ -183,4 +214,43 @@ public class TestSimple {
         entry(TOKEN_LABEL_UNWANTED, 5));
   }
 
+  @Test
+  public void testLabel() {
+    Root root = createRoot(createA(A_NAME), null);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        builder -> builder.<ASTNode<?>>setNameMethod(n -> n.isA() ? "A" : "Not A"));
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals("Not A", actualRoot.getLabel());
+
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals("A", actualA.getLabel());
+  }
+
+  @Test
+  public void testTextColor() {
+    Root root = createRoot(createA(A_NAME), null);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        builder -> builder.<ASTNode<?>>setTextColorMethod(n -> n.isA() ? "red" : ""));
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals("", actualRoot.getTextColor());
+
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals("red", actualA.getTextColor());
+  }
+
+  @Test
+  public void testBackgroundColor() {
+    Root root = createRoot(createA(A_NAME), null);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        builder -> builder.<ASTNode<?>>setBackgroundColorMethod(n -> n.isA() ? "green" : ""));
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals("", actualRoot.getBackgroundColor());
+
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals("green", actualA.getBackgroundColor());
+  }
+
 }
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
index dafc29d8b58b71679d806ed3e70db467b10a9de5..10a0859b6dec99ff2b7797c0ea382c774722bdfd 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
@@ -156,6 +156,7 @@ public class TestUtils {
 
   public static List<DumpNode> dumpModel(Object target, Consumer<DumpBuilder> options) {
     ExposingDumpBuilder builder = new ExposingDumpBuilder(target);
+    builder.excludeNullNodes();
     options.accept(builder);
     DumpAst dumpAst = builder.build();
     // let mustache run, but ignore result