diff --git a/dumpAst2uml/build.gradle b/dumpAst2uml/build.gradle
index 7690a02f55330f0f0c199bbf94cdfcd285912e72..048b27dc3cb0d93cdfc2d580683197fa4e68bde3 100644
--- a/dumpAst2uml/build.gradle
+++ b/dumpAst2uml/build.gradle
@@ -19,7 +19,8 @@ buildscript {
 
 dependencies {
     implementation project(':grammar2uml')  // just to test SimpleMain
-    implementation project(':relast.preprocessor')
+    implementation fileTree(include: ['plantuml.jar'], dir: '../libs')
+//    implementation project(':relast.preprocessor')
 
     implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: '0.9.6'
     implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '2.11.2'
@@ -39,7 +40,7 @@ try {
     version = oldProps['version']
 } catch (e) {
     // this happens, if either the properties file is not present, or cannot be read from
-    throw new GradleException("File ${versionFile} not found or unreadable. Aborting.")
+    throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e)
 }
 
 task newVersion() {
@@ -73,116 +74,80 @@ test {
     maxHeapSize = '1G'
 }
 
-//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",
-//            "../relast.preprocessor/src/main/jastadd/RelAst.relast",
-//            "./src/main/jastadd/Grammar2Uml.relast",
+File dumpAstGrammar = file('./src/main/jastadd/DumpAst.relast')
+
+task relast(type: JavaExec) {
+    group = 'Build'
+    main = "-jar"
+
+    doFirst {
+        delete "src/gen/jastadd/*.ast"
+        delete "src/gen/jastadd/DumpAst2.jadd"
+        delete "src/gen/jastadd/DumpAstlRefResolver.jadd"
+        delete "src/gen/jastadd/DumpAstlResolverStubs.jrag"
+        mkdir  "src/gen/jastadd/"
+    }
+
+    args = [
+            "../libs/relast.jar",
+            dumpAstGrammar,
 //            "./src/main/jastadd/MustacheNodes.relast",
-//            "--listClass=java.util.ArrayList",
-//            "--jastAddList=JastAddList",
-//            "--useJastAddNames",
-//            "--file",
-//            "--resolverHelper",
-//            "--grammarName=./src/gen/jastadd/Grammar2Uml"
-//    ]
-//
-////    inputs.files file("../libs/relast.jar"),
-////            file("../relast.preprocessor/src/main/jastadd/RelAST.relast"),
-////            file("./src/main/jastadd/Grammar2Uml.relast")
-////            file("./src/main/jastadd/MustacheNodes.relast")
-////    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 "dumpAst2uml/src/main/**/*.java"
-//                include "dumpAst2uml/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 "dumpAst2uml/src/main/jastadd/**/*.ast"
-//                include "dumpAst2uml/src/main/jastadd/**/*.jadd"
-//                include "dumpAst2uml/src/main/jastadd/**/*.jrag"
-//                include "dumpAst2uml/src/gen/jastadd/**/*.ast"
-//                include "dumpAst2uml/src/gen/jastadd/**/*.jadd"
-//                include "dumpAst2uml/src/gen/jastadd/**/*.jrag"
-//            }
-//
-//            scanner {
-//                basedir ".."
-//                include "dumpAst2uml/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 "dumpAst2uml/src/main/jastadd/scanner/Macros.flex",               [-3]
-//                include "relast.preprocessor/src/main/jastadd/scanner/RulesPreamble.flex", [-2]
-//                include "dumpAst2uml/src/main/jastadd/scanner/MappingContent.flex",       [-1]
-//                include "dumpAst2uml/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 "dumpAst2uml/src/main/jastadd/parser/Preamble.parser"
-//                include "relast.preprocessor/src/main/jastadd/parser/RelAst.parser"
-//                include "dumpAst2uml/src/main/jastadd/parser/Grammar2Uml.parser"
-//            }
-//        }
-//    }
-//
-//    cleanGen.doFirst {
-//        delete "src/gen/java/org"
-//        delete "src/gen-res/BuildInfo.properties"
-//    }
-//
-//    preprocessParser.doFirst {
-//
-//        args += ["--no-beaver-symbol"]
-//
-//    }
-//
-//    module = "Grammar2Uml"
-//
-//    astPackage = 'org.jastadd.dumpAst2uml.ast'
-//
-//    parser.name = 'Grammar2UmlParser'
-//
-//    genDir = 'src/gen/java'
-//
-//    buildInfoDir = 'src/gen-res'
-//
-//    scanner.genDir = "src/gen/java/org/jastadd/dumpAst2uml/scanner"
-//    parser.genDir = "src/gen/java/org/jastadd/dumpAst2uml/parser"
-//
-//    jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
-//}
-//
-//generateAst.dependsOn relast
+            "--listClass=java.util.ArrayList",
+            "--jastAddList=JastAddList",
+            "--useJastAddNames",
+            "--file",
+            "--resolverHelper",
+            "--grammarName=./src/gen/jastadd/DumpAst2"
+    ]
+
+    inputs.files(file("../libs/relast.jar"),
+            dumpAstGrammar)
+    outputs.files(file("./src/gen/jastadd/DumpAst2.ast"),
+            file("./src/gen/jastadd/DumpAst.jadd"),
+            file("./src/gen/jastadd/DumpAstRefResolver.jadd"),
+            file('./src/gen/jastadd/DumpAstResolverStubs.jrag'))
+}
+
+jastadd {
+    configureModuleBuild()
+    modules {
+        //noinspection GroovyAssignabilityCheck
+        module("DumpAst2Uml") {
+
+            java {
+                basedir ".."
+                include "dumpAst2uml/src/main/**/*.java"
+                include "dumpAst2uml/src/gen/**/*.java"
+            }
+
+            jastadd {
+                basedir ".."
+                include "dumpAst2uml/src/main/jastadd/**/*.ast"
+                include "dumpAst2uml/src/main/jastadd/**/*.jadd"
+                include "dumpAst2uml/src/main/jastadd/**/*.jrag"
+                include "dumpAst2uml/src/gen/jastadd/**/*.ast"
+                include "dumpAst2uml/src/gen/jastadd/**/*.jadd"
+                include "dumpAst2uml/src/gen/jastadd/**/*.jrag"
+            }
+        }
+    }
+
+    cleanGen.doFirst {
+        delete "src/gen/java/org"
+        delete "src/gen-res/BuildInfo.properties"
+    }
+
+    preprocessParser.doFirst {
+
+        args += ["--no-beaver-symbol"]
+
+    }
+
+    module = "DumpAst2Uml"
+    astPackage = 'org.jastadd.dumpAst2uml.ast'
+    genDir = 'src/gen/java'
+    buildInfoDir = 'src/gen-res'
+    jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
+}
+
+generateAst.dependsOn relast
diff --git a/dumpAst2uml/src/main/jastadd/DumpAst.relast b/dumpAst2uml/src/main/jastadd/DumpAst.relast
new file mode 100644
index 0000000000000000000000000000000000000000..1d94dc6875215d74e9167d54a36154d4461bbbd3
--- /dev/null
+++ b/dumpAst2uml/src/main/jastadd/DumpAst.relast
@@ -0,0 +1,23 @@
+DumpAst ::= DumpNode* <PackageName> ;
+DumpNode ::= <Name> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ;
+InnerDumpNode ;
+rel InnerDumpNode.DumpNode -> DumpNode ;
+
+abstract DumpChildNode ::= <Name> ;
+DumpNormalChildNode : DumpChildNode ;
+rel DumpNormalChildNode.DumpNode -> DumpNode ;
+DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
+//DumpOptChildNode : DumpChildNode ;
+//rel DumpOptChildNode.DumpNode? -> DumpNode ;
+
+abstract DumpToken ::= <Name> ;
+DumpReferenceToken : DumpToken ;
+rel DumpReferenceToken.Value -> DumpNode ;
+DumpValueToken : DumpToken ::= <Value:Object> ;
+
+abstract DumpRelation ::= <Name> <Bidirectional:boolean> ;
+DumpNormalRelation : DumpRelation ;
+rel DumpNormalRelation.DumpNode -> DumpNode ;
+DumpListRelation : DumpRelation ::= InnerDumpNode* ;
+//DumpOptRelation : DumpRelation ;
+//rel DumpOptRelation.DumpNode? -> DumpNode ;
diff --git a/dumpAst2uml/src/main/jastadd/Generation.jadd b/dumpAst2uml/src/main/jastadd/Generation.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..7dd3c516032df66b344676cd2cde3afa572ed1c0
--- /dev/null
+++ b/dumpAst2uml/src/main/jastadd/Generation.jadd
@@ -0,0 +1,203 @@
+aspect Generation {
+  public class Dumper {
+    public static DumpBuilder read(Object obj) {
+      return new DumpBuilder(obj);
+    }
+  }
+  class DumpBuilder {
+    private Object target;
+    private String packageName;
+    private DumpAst result;
+    protected DumpBuilder(Object target) {
+      this.target = target;
+    }
+    public DumpBuilder ignoreTypes(String... regexes) {
+      // TODO
+      return this;
+    }
+    public DumpBuilder skinParam(String option) {
+      // TODO
+      return this;
+    }
+    protected DumpAst build() {
+      if (result == null) {
+        result = new DumpAst();
+        result.setPackageName(this.packageName == null ? this.target.getClass().getPackage().getName() : this.packageName);
+        try {
+          result.transform(new TransformationTransferInformation(), this.target);
+        } catch (java.lang.reflect.InvocationTargetException e) {
+          throw new RuntimeException("Could not transform :(", e);
+        } catch (IllegalAccessException e) {
+          throw new RuntimeException("Could not transform :(", e);
+        }
+      }
+      return result;
+    }
+    public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException {
+      String content = build().toPlantUml();
+      try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
+        writer.write(content);
+      }
+      return this;
+    }
+    public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException {
+      String content = build().toPlantUml();
+      net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content);
+      reader.outputImage(java.nio.file.Files.newOutputStream(destination));
+      return this;
+    }
+  }
+  // --- transform --- (need to be a method, because it alters the AST while traversing the object structure)
+  // maybe return type is unncessary
+  protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj) throws java.lang.reflect.InvocationTargetException, IllegalAccessException {
+    if (obj == null) {
+      return null;
+    }
+    if (tti.transformed.containsKey(obj)) {
+      return tti.transformed.get(obj);
+    }
+    DumpNode node = new DumpNode();
+    node.setObject(obj);
+    tti.transformed.put(obj, node);
+    this.addDumpNode(node);
+    if (node.isAstNode()) {
+      // only caching node.analyseClass does not help, since we want to do this only once per class of a node
+      ClassAnalysisResult car = tti.classAnalysisResults.computeIfAbsent(node.getClass(), clz -> node.analyseClass());
+      // -- singleChild --
+      for (java.lang.reflect.Method method : car.singleChildMethods) {
+        Object target = method.invoke(obj);
+        if (target != null) {
+          DumpNormalChildNode normalChild = new DumpNormalChildNode();
+          normalChild.setName(car.names.get(method));
+          normalChild.setDumpNode(transform(tti, target));
+          node.addDumpChildNode(normalChild);
+        }
+      }
+      // -- listChild --
+      for (java.lang.reflect.Method method : car.listChildMethods) {
+        Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
+        DumpListChildNode listChild = new DumpNormalChildNode();
+        listChild.setName(car.names.get(method));
+        for (Object target : targetList) {
+          if (target != null) {
+            listChild.addInnerDumpNode(new InnerDumpNode(transform(tti, target)));
+          }
+        }
+        node.addDumpChildNode(listChild);
+      }
+      // -- singleRelation --
+      for (java.lang.reflect.Method method : car.singleRelationMethods) {
+        Object target = method.invoke(obj);
+        if (target != null) {
+          DumpNormalRelation normalChild = new DumpNormalRelation();
+          normalChild.setName(car.names.get(method));
+          normalChild.setDumpNode(transform(tti, target));
+          node.addDumpRelation(normalChild);
+        }
+      }
+      // -- listChild --
+      for (java.lang.reflect.Method method : car.listChildMethods) {
+        Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
+        DumpListRelation listChild = new DumpNormalRelation();
+        listChild.setName(car.names.get(method));
+        for (Object target : targetList) {
+          if (target != null) {
+            listChild.addInnerDumpNode(new InnerDumpNode(transform(tti, target)));
+          }
+        }
+        node.addDumpRelation(listChild);
+      }
+      // -- token --
+      for (java.lang.reflect.Method method : car.tokenMethods) {
+        Object target = method.invoke(obj);
+        if (target != null) {
+          DumpNode targetNode = transform(tti, target);
+          DumpToken token;
+          if (targetNode.isAstNode()) {
+            token = new DumpReferenceToken();
+            token.setValue(targetNode);
+          } else {
+            // maybe ignore empty string values here
+            token = new DumpValueToken();
+            token.setValue(target);
+          }
+          token.setName(car.names.get(method));
+          node.addDumpToken(token);
+        }
+      }
+    }
+    return node;
+  }
+
+  syn ClassAnalysisResult DumpNode.analyseClass() {
+    // TODO
+    return null;
+  }
+
+  syn boolean DumpNode.isAstNode() {
+    // TODO
+    return false;
+  }
+
+  class TransformationTransferInformation {
+    java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>();
+    java.util.Map<Class<?>, ClassAnalysisResult> classAnalysisResults = new java.util.HashMap<>();
+  }
+
+  class ClassAnalysisResult {
+    java.lang.reflect.Method[] singleChildMethods;
+    java.lang.reflect.Method[] listChildMethods;
+    java.lang.reflect.Method[] singleRelationMethods;
+    java.lang.reflect.Method[] listRelationMethods;
+    java.lang.reflect.Method[] tokenMethods;
+    java.util.Map<java.lang.reflect.Method, String> names;
+  }
+
+  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();
+  }
+
+  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() {}
+  }
+}
\ No newline at end of file
diff --git a/dumpAst2uml/src/main/jastadd/Navigation.jrag b/dumpAst2uml/src/main/jastadd/Navigation.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..708d016d515fa4f95b46a7662adffb38c28067d6
--- /dev/null
+++ b/dumpAst2uml/src/main/jastadd/Navigation.jrag
@@ -0,0 +1,11 @@
+aspect Navigation {
+  // --- isList ---
+  syn boolean DumpChildNode.isList() = false;
+  eq DumpListChildNode.isList() = true;
+  syn boolean DumpRelation.isList() = false;
+  eq DumpListRelation.isList() = true;
+
+  // --- isDumpValueToken ---
+  syn boolean DumpToken.isDumpValueToken() = false;
+  eq DumpValueToken.isDumpValueToken() = true;
+}
\ No newline at end of file
diff --git a/dumpAst2uml/src/main/jastadd/Printing.jrag b/dumpAst2uml/src/main/jastadd/Printing.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..9a399ba6bfdfd9ec6f5c8e279d6cf6fb39e933c1
--- /dev/null
+++ b/dumpAst2uml/src/main/jastadd/Printing.jrag
@@ -0,0 +1,23 @@
+aspect Printing {
+  // --- outerNodeName ---
+  inh String InnerDumpNode.outerNodeName();
+  inh String DumpChildNode.outerNodeName();
+  inh String DumpRelation.outerNodeName();
+  inh String DumpReferenceToken.outerNodeName();
+  eq DumpNode.getChild().outerNodeName() = name();
+
+  // --- innerNodeName ---
+  syn String InnerDumpNode.innerNodeName() = getDumpNode().name();
+  syn String DumpNormalChildNode.innerNodeName() = getDumpNode().name();
+  syn String DumpNormalRelation.innerNodeName() = getDumpNode().name();
+  syn String DumpReferenceToken.innerNodeName() = getValue().name();
+
+  // --- name ---
+  syn String DumpNode.name() = getName();  // might change in the future
+
+  // --- label ---
+  syn String DumpChildNode.label() = getName();
+  syn String DumpRelation.label() = getName();
+  syn String DumpToken.label() = getName();
+
+}
diff --git a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
index 1ccafbacb5e000da51f5fffb936ef63c6574116b..f6e83dbf0441b9dc2a55a232c9e26ddfccfdd816 100644
--- a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
+++ b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
@@ -1,21 +1,25 @@
 package org.jastadd.dumpAst2uml.compiler;
 
 import beaver.Parser;
+import org.jastadd.dumpAst2uml.ast.*;
 import org.jastadd.grammar2uml.ast.*;
 import org.jastadd.grammar2uml.parser.Grammar2UmlParser;
 import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
 
-import java.io.BufferedReader;
-import java.io.IOException;
+import java.io.*;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
 
+import static org.jastadd.dumpAst2uml.compiler.SimpleMain.Kind.*;
+
 /**
  * Testing Relast2Uml without parser.
  *
@@ -24,11 +28,72 @@ import java.util.Set;
 public class SimpleMain {
 
   public static void main(String[] args) {
-//    testing();
+//    printing();
     createManualAST();
   }
 
   private static void createManualAST() {
+    DumpAst dumpAst = new DumpAst();
+    dumpAst.setPackageName("foo");
+    DumpNode a = new DumpNode();
+    a.setName("a");
+    dumpAst.addDumpNode(a);
+    DumpNode b = new DumpNode();
+    b.setName("b");
+    dumpAst.addDumpNode(b);
+    DumpNode c = new DumpNode();
+    c.setName("c");
+    dumpAst.addDumpNode(c);
+    DumpNode d = new DumpNode();
+    d.setName("d");
+    dumpAst.addDumpNode(d);
+    DumpNode e = new DumpNode();
+    e.setName("e");
+    dumpAst.addDumpNode(e);
+    DumpNode f = new DumpNode();
+    f.setName("f");
+    dumpAst.addDumpNode(f);
+    DumpNode g = new DumpNode();
+    g.setName("g");
+    dumpAst.addDumpNode(g);
+    DumpNode h = new DumpNode();
+    h.setName("h");
+    dumpAst.addDumpNode(h);
+
+    DumpListChildNode listNode = new DumpListChildNode();
+    listNode.setName("list");
+    listNode.addInnerDumpNode(new InnerDumpNode(b));
+    listNode.addInnerDumpNode(new InnerDumpNode(c));
+    a.addDumpChildNode(listNode);
+    a.addDumpChildNode(new DumpNormalChildNode("opt", d));
+    a.addDumpChildNode(new DumpNormalChildNode("normal", e));
+
+    DumpListRelation listRelation = new DumpListRelation();
+    listRelation.setName("listRel");
+    listRelation.addInnerDumpNode(new InnerDumpNode(f));
+    listRelation.addInnerDumpNode(new InnerDumpNode(g));
+    b.addDumpRelation(listRelation);
+
+    c.addDumpRelation(new DumpNormalRelation("relOpt", false, g));
+    c.addDumpRelation(new DumpNormalRelation("biRelOpt", true, h));
+
+    d.addDumpRelation(new DumpNormalRelation("relNormal", false, c));
+
+    e.addDumpToken(new DumpReferenceToken("ref", h));
+
+    h.addDumpToken(new DumpValueToken("intValue", 4));
+    DumpListRelation biListRelation = new DumpListRelation();
+    biListRelation.setName("biRelList");
+    biListRelation.setBidirectional(true);
+    biListRelation.addInnerDumpNode(new InnerDumpNode(f));
+    biListRelation.addInnerDumpNode(new InnerDumpNode(g));
+    h.addDumpRelation(biListRelation);
+
+    String content = dumpAst.toPlantUml();
+    System.out.println(content);
+  }
+
+  private static void printing() {
     System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
     System.setProperty("mustache.debug", "true");
     Grammar2Uml model = new Grammar2Uml();
@@ -48,6 +113,7 @@ public class SimpleMain {
   }
 
   static final String MORE_INDENT = "| ";
+  static final String AST_NODE_ANNOTATION = "org.jastadd.grammar2uml.ast" + ".ASTNodeAnnotation";
 
   private static void traverseInitial(Object obj) {
     traverse(obj, new HashSet<>(), "");
@@ -61,75 +127,93 @@ public class SimpleMain {
     System.out.println(indent + "|> " + obj.toString());
     System.out.println(indent + "| isAstNode(obj) = " + isAstNode(obj));
     printChildren(obj, seen, indent);
-    printTokens(obj, seen, indent);
+//    printTokens(obj, seen, indent);
   }
 
   private static boolean isAstNode(Object obj) {
     Class<?> clazz = obj.getClass();
     for (Constructor<?> constructor : clazz.getConstructors()) {
-      if (constructor.isAnnotationPresent(ASTNodeAnnotation.Constructor.class)) {
+      if (hasConstructionAnnotationClass(constructor)) {
         return true;
       }
     }
     return false;
   }
 
-  static void printChildren(Object obj, Set<Object> seen, String indent) {
-    Class<?> clazz = obj.getClass();
-    for (Method method : clazz.getMethods()) {
-      final String name;
-      final String kind;
-      if (method.isAnnotationPresent(ASTNodeAnnotation.Child.class)) {
-        name = method.getAnnotation(ASTNodeAnnotation.Child.class).name();
-        kind = "";
-      } else if (method.isAnnotationPresent(ASTNodeAnnotation.ListChild.class)) {
-        name = method.getAnnotation(ASTNodeAnnotation.ListChild.class).name();
-        kind = "list-";
-      } else if (method.isAnnotationPresent(ASTNodeAnnotation.OptChild.class)) {
-        name = method.getAnnotation(ASTNodeAnnotation.OptChild.class).name();
-        kind = "opt-";
-      } else {
-        name = null;
-        kind = null;
+  private static boolean hasConstructionAnnotationClass(Constructor<?> constructor) {
+//    ASTNodeAnnotation.Constructor.class;
+    for (Annotation annotation : constructor.getAnnotations()) {
+      String canonicalName = annotation.annotationType().getCanonicalName();
+      if (canonicalName.startsWith(AST_NODE_ANNOTATION) && annotation.annotationType().getSimpleName().equals("Constructor")) {
+        return true;
       }
-      if (kind != null) {
-        try {
-          Object child = method.invoke(obj);
-          System.out.println(indent + "| " + kind + "child (" + name + ") = " + child);
-          traverse(child, seen, indent + MORE_INDENT);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-          e.printStackTrace();
+    }
+    return false;
+  }
+
+  enum Kind {child, list, opt, tokenOrRelation, token, relation, other}
+  static class NameAndKind {
+    String name;
+    Kind kind;
+    static NameAndKind of(String name, Kind kind) {
+      NameAndKind result = new NameAndKind();
+      result.name = name;
+      result.kind = kind;
+      return result;
+    }
+  }
+
+  private static NameAndKind analyzeMethod(Method method) {
+    for (Annotation annotation : method.getAnnotations()) {
+      String canonicalName = annotation.annotationType().getCanonicalName();
+      if (canonicalName.startsWith(AST_NODE_ANNOTATION)) {
+        String simpleName = annotation.annotationType().getSimpleName();
+        switch (simpleName) {
+          case "Child": return NameAndKind.of(invokeName(annotation), Kind.child);
+          case "ListChild": return NameAndKind.of(invokeName(annotation), Kind.list);
+          case "OptChild": return NameAndKind.of(invokeName(annotation), Kind.opt);
+          case "Token": return NameAndKind.of(invokeName(annotation), tokenOrRelation);
         }
       }
     }
+    return NameAndKind.of(null, Kind.other);
+  }
+
+  private static String invokeName(Annotation annotation) {
+    try {
+      return (String) annotation.annotationType().getMethod("name").invoke(annotation);
+    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+      return "<error>";
+    }
   }
 
-  static void printTokens(Object obj, Set<Object> seen, String indent) {
+  static void printChildren(Object obj, Set<Object> seen, String indent) {
     Class<?> clazz = obj.getClass();
     for (Method method : clazz.getMethods()) {
-      if (method.isAnnotationPresent(ASTNodeAnnotation.Token.class)) {
-        Method methodToInvoke = null;
-        String name = method.getAnnotation(ASTNodeAnnotation.Token.class).name();
+      NameAndKind nameAndKind = analyzeMethod(method);
+      String name = nameAndKind.name;
+      Kind kind = nameAndKind.kind;
+      Method methodToInvoke = method;
+      if (kind == tokenOrRelation) {
         // heuristic for relations
         if (name.startsWith("_impl_")) {
           try {
             methodToInvoke = clazz.getMethod("get" + name.substring(6));
             name = name.substring(6);
+            kind = relation;
           } catch (NoSuchMethodException e) {
             // this is probably not a relation
           }
         }
-        final String kind;
-        if (methodToInvoke == null) {
-          methodToInvoke = method;
-          kind = "token";
-        } else {
-          kind = "relation";
+        if (kind == tokenOrRelation) {
+          kind = token;
         }
+      }
+      if (kind != other) {
         try {
-          Object value = methodToInvoke.invoke(obj);
-          System.out.println(indent + "| " + kind + " (" + name + ") = " + value);
-          traverse(value, seen, indent + MORE_INDENT);
+          Object child = methodToInvoke.invoke(obj);
+          System.out.println(indent + "| " + kind + " child (" + name + ") = " + child);
+          traverse(child, seen, indent + MORE_INDENT);
         } catch (IllegalAccessException | InvocationTargetException e) {
           e.printStackTrace();
         }
@@ -137,6 +221,39 @@ public class SimpleMain {
     }
   }
 
+//  static void printTokens(Object obj, Set<Object> seen, String indent) {
+//    Class<?> clazz = obj.getClass();
+//    for (Method method : clazz.getMethods()) {
+//      if (method.isAnnotationPresent(ASTNodeAnnotation.Token.class)) {
+//        Method methodToInvoke = null;
+//        String name = method.getAnnotation(ASTNodeAnnotation.Token.class).name();
+//        // heuristic for relations
+//        if (name.startsWith("_impl_")) {
+//          try {
+//            methodToInvoke = clazz.getMethod("get" + name.substring(6));
+//            name = name.substring(6);
+//          } catch (NoSuchMethodException e) {
+//            // this is probably not a relation
+//          }
+//        }
+//        final String kind;
+//        if (methodToInvoke == null) {
+//          methodToInvoke = method;
+//          kind = "token";
+//        } else {
+//          kind = "relation";
+//        }
+//        try {
+//          Object value = methodToInvoke.invoke(obj);
+//          System.out.println(indent + "| " + kind + " (" + name + ") = " + value);
+//          traverse(value, seen, indent + MORE_INDENT);
+//        } catch (IllegalAccessException | InvocationTargetException e) {
+//          e.printStackTrace();
+//        }
+//      }
+//    }
+//  }
+
   private static Program parseProgram(Path path) {
     try (BufferedReader reader = Files.newBufferedReader(path)) {
       Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
diff --git a/dumpAst2uml/src/main/resources/dumpAst.mustache b/dumpAst2uml/src/main/resources/dumpAst.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..ddbc1b11a0448d516319e8ddd9c0ce08a2b38bd0
--- /dev/null
+++ b/dumpAst2uml/src/main/resources/dumpAst.mustache
@@ -0,0 +1,39 @@
+@startuml
+{{#DumpNodes}}
+object {{name}} {
+{{! tokens }}
+{{#DumpTokens}}
+{{#isDumpValueToken}}
+  {{label}} = {{Value}}
+{{/isDumpValueToken}}
+{{/DumpTokens}}
+}
+{{#DumpTokens}}
+{{^isDumpValueToken}}
+{{outerNodeName}} ..> {{innerNodeName}} : {{label}}
+{{/isDumpValueToken}}
+{{/DumpTokens}}
+{{! child nodes }}
+{{#DumpChildNodes}}
+{{#isList}}
+{{#InnerDumpNodes}}
+{{outerNodeName}} *-- {{innerNodeName}} : {{label}}
+{{/InnerDumpNodes}}
+{{/isList}}
+{{^isList}}
+{{outerNodeName}} *-- {{innerNodeName}} : {{label}}
+{{/isList}}
+{{/DumpChildNodes}}
+{{! relations }}
+{{#DumpRelations}}
+{{#isList}}
+{{#InnerDumpNodes}}
+{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}}
+{{/InnerDumpNodes}}
+{{/isList}}
+{{^isList}}
+{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}}
+{{/isList}}
+{{/DumpRelations}}
+{{/DumpNodes}}
+@enduml
diff --git a/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties b/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties
new file mode 100644
index 0000000000000000000000000000000000000000..9e9347a71a9cec4a1fb0a4118eae4835e757cb73
--- /dev/null
+++ b/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties
@@ -0,0 +1,2 @@
+#Sat Sep 05 18:50:58 CEST 2020
+version=0.2
diff --git a/grammar2uml/build.gradle b/grammar2uml/build.gradle
index 44378a3b612dff552da7c45bafdf0a00b5976f65..578aaa1664d74c94a9d2adecb49cd5a78ae24370 100644
--- a/grammar2uml/build.gradle
+++ b/grammar2uml/build.gradle
@@ -38,7 +38,7 @@ try {
     version = oldProps['version']
 } catch (e) {
     // this happens, if either the properties file is not present, or cannot be read from
-    throw new GradleException("File ${versionFile} not found or unreadable. Aborting.")
+    throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e)
 }
 
 task newVersion() {
@@ -72,6 +72,10 @@ test {
     maxHeapSize = '1G'
 }
 
+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"
@@ -86,9 +90,9 @@ task relast(type: JavaExec) {
 
     args = [
             "../libs/relast.jar",
-            "../relast.preprocessor/src/main/jastadd/RelAst.relast",
-            "./src/main/jastadd/Grammar2Uml.relast",
-            "./src/main/jastadd/MustacheNodes.relast",
+            preprocessorGrammar,
+            grammar2umlGrammar,
+            intermediateGrammar,
             "--listClass=java.util.ArrayList",
             "--jastAddList=JastAddList",
             "--useJastAddNames",
@@ -97,14 +101,14 @@ task relast(type: JavaExec) {
             "--grammarName=./src/gen/jastadd/Grammar2Uml"
     ]
 
-//    inputs.files file("../libs/relast.jar"),
-//            file("../relast.preprocessor/src/main/jastadd/RelAST.relast"),
-//            file("./src/main/jastadd/Grammar2Uml.relast")
-//            file("./src/main/jastadd/MustacheNodes.relast")
-//    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')
+    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 {
diff --git a/grammar2uml/src/main/jastadd/Navigation.jrag b/grammar2uml/src/main/jastadd/Navigation.jrag
index 980904b34bb59d293da2512d5fea1d8375f457c0..ccd0f3d49ede4fbecfafafff05b5228e42c76ed9 100644
--- a/grammar2uml/src/main/jastadd/Navigation.jrag
+++ b/grammar2uml/src/main/jastadd/Navigation.jrag
@@ -16,5 +16,6 @@ aspect Navigation {
   // --- containedFileName ---
   eq Grammar2Uml.getChild().containedFileName() = getFileName();
   eq Program.getChild().containedFileName() = null;
+  eq Grammar.getChild().containedFileName() = null;
   eq MGrammar2Uml.getChild().containedFileName() = null;
 }
diff --git a/libs/plantuml.jar b/libs/plantuml.jar
new file mode 100644
index 0000000000000000000000000000000000000000..f3a204fc9cb7297951950e05c34f4bd270c5ebb6
Binary files /dev/null and b/libs/plantuml.jar differ