diff --git a/dumpAst2uml/src/main/jastadd/DumpAst.relast b/dumpAst2uml/src/main/jastadd/DumpAst.relast
index 1d94dc6875215d74e9167d54a36154d4461bbbd3..91f4d16f74b37f33f4200aa9f91a79ac0f4a7947 100644
--- a/dumpAst2uml/src/main/jastadd/DumpAst.relast
+++ b/dumpAst2uml/src/main/jastadd/DumpAst.relast
@@ -1,5 +1,5 @@
-DumpAst ::= DumpNode* <PackageName> ;
-DumpNode ::= <Name> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ;
+DumpAst ::= DumpNode* <PackageName> <Scale:double> ;
+DumpNode ::= <Name> <Label> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ;
 InnerDumpNode ;
 rel InnerDumpNode.DumpNode -> DumpNode ;
 
diff --git a/dumpAst2uml/src/main/jastadd/Generation.jadd b/dumpAst2uml/src/main/jastadd/Generation.jadd
index 7dd3c516032df66b344676cd2cde3afa572ed1c0..68de1bf3c40db41a370deb9dfad5c74b23c48aef 100644
--- a/dumpAst2uml/src/main/jastadd/Generation.jadd
+++ b/dumpAst2uml/src/main/jastadd/Generation.jadd
@@ -4,10 +4,12 @@ aspect Generation {
       return new DumpBuilder(obj);
     }
   }
-  class DumpBuilder {
+  public class DumpBuilder {
     private Object target;
     private String packageName;
     private DumpAst result;
+    private java.util.List<String> header = new java.util.ArrayList<>();
+
     protected DumpBuilder(Object target) {
       this.target = target;
     }
@@ -29,11 +31,14 @@ aspect Generation {
           throw new RuntimeException("Could not transform :(", e);
         } catch (IllegalAccessException e) {
           throw new RuntimeException("Could not transform :(", e);
+        } catch (NoSuchMethodException e) {
+          throw new RuntimeException("Could not transform :(", e);
         }
       }
       return result;
     }
     public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException {
+      build().setScale(0.1);
       String content = build().toPlantUml();
       try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
         writer.write(content);
@@ -41,15 +46,25 @@ aspect Generation {
       return this;
     }
     public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException {
+      build().setScale(0.1);
       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;
     }
+    public DumpBuilder dumpAsSVG(java.nio.file.Path destination) throws java.io.IOException {
+      build().setScale(1);
+      String content = build().toPlantUml();
+      net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content);
+      reader.outputImage(java.nio.file.Files.newOutputStream(destination),
+          new net.sourceforge.plantuml.FileFormatOption(net.sourceforge.plantuml.FileFormat.SVG));
+      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 {
+  protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj)
+      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
     if (obj == null) {
       return null;
     }
@@ -58,11 +73,20 @@ aspect Generation {
     }
     DumpNode node = new DumpNode();
     node.setObject(obj);
+    node.setLabel(obj.getClass().getSimpleName() + "@" + obj.hashCode());
+    node.setName("node" + tti.transformed.size());
     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());
+      final ClassAnalysisResult car;
+      Class<?> clazz = obj.getClass();
+      if (tti.classAnalysisResults.containsKey(clazz)) {
+        car = tti.classAnalysisResults.get(clazz);
+      } else {
+        car = node.analyzeClass();
+        tti.classAnalysisResults.put(clazz, car);
+      }
       // -- singleChild --
       for (java.lang.reflect.Method method : car.singleChildMethods) {
         Object target = method.invoke(obj);
@@ -76,7 +100,7 @@ aspect Generation {
       // -- listChild --
       for (java.lang.reflect.Method method : car.listChildMethods) {
         Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
-        DumpListChildNode listChild = new DumpNormalChildNode();
+        DumpListChildNode listChild = new DumpListChildNode();
         listChild.setName(car.names.get(method));
         for (Object target : targetList) {
           if (target != null) {
@@ -89,23 +113,23 @@ aspect Generation {
       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);
+          DumpNormalRelation normalRelation = new DumpNormalRelation();
+          normalRelation.setName(car.names.get(method));
+          normalRelation.setDumpNode(transform(tti, target));
+          node.addDumpRelation(normalRelation);
         }
       }
-      // -- listChild --
-      for (java.lang.reflect.Method method : car.listChildMethods) {
+      // -- listRelation --
+      for (java.lang.reflect.Method method : car.listRelationMethods) {
         Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
-        DumpListRelation listChild = new DumpNormalRelation();
-        listChild.setName(car.names.get(method));
+        DumpListRelation listRelation = new DumpListRelation();
+        listRelation.setName(car.names.get(method));
         for (Object target : targetList) {
           if (target != null) {
-            listChild.addInnerDumpNode(new InnerDumpNode(transform(tti, target)));
+            listRelation.addInnerDumpNode(new InnerDumpNode(transform(tti, target)));
           }
         }
-        node.addDumpRelation(listChild);
+        node.addDumpRelation(listRelation);
       }
       // -- token --
       for (java.lang.reflect.Method method : car.tokenMethods) {
@@ -114,12 +138,12 @@ aspect Generation {
           DumpNode targetNode = transform(tti, target);
           DumpToken token;
           if (targetNode.isAstNode()) {
-            token = new DumpReferenceToken();
-            token.setValue(targetNode);
+            token = new DumpReferenceToken().setValue(targetNode);
           } else {
             // maybe ignore empty string values here
-            token = new DumpValueToken();
-            token.setValue(target);
+            DumpValueToken valueToken = new DumpValueToken();
+            valueToken.setValue(target);
+            token = valueToken;
           }
           token.setName(car.names.get(method));
           node.addDumpToken(token);
@@ -129,28 +153,93 @@ aspect Generation {
     return node;
   }
 
-  syn ClassAnalysisResult DumpNode.analyseClass() {
-    // TODO
-    return null;
+  ClassAnalysisResult DumpNode.analyzeClass()
+      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+    ClassAnalysisResult result = new ClassAnalysisResult();
+    Class<?> clazz = getObject().getClass();
+    for (java.lang.reflect.Method method : clazz.getMethods()) {
+      for (java.lang.annotation.Annotation annotation : method.getAnnotations()) {
+        String canonicalName = annotation.annotationType().getCanonicalName();
+        if (canonicalName.startsWith(astNodeAnnotationPrefix())) {
+          String simpleName = annotation.annotationType().getSimpleName();
+          switch (simpleName) {
+            case "Child":
+            case "OptChild":
+              result.singleChildMethods.add(method);
+              break;
+            case "ListChild":
+              result.listChildMethods.add(method);
+              break;
+            case "Token":
+              // heuristic for relations
+              String tokenName = invokeName(annotation);
+              if (tokenName.startsWith("_impl_")) {
+                String relationName = tokenName.substring(6);
+                try {
+                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
+                  // normal get + token-name -> singleRelation
+                  result.singleRelationMethods.add(relationMethod);
+                  result.names.put(relationMethod, relationName);
+                  continue;
+                } catch (NoSuchMethodException e) {
+                  // ignore, but we know this is probably not a single relation
+                }
+                // try list-relation next
+                try {
+                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
+                  // normal get + token-name + "List" -> listRelation
+                  result.listRelationMethods.add(relationMethod);
+                  result.names.put(relationMethod, relationName);
+                  continue;
+                } catch (NoSuchMethodException e) {
+                  // ignore, but we know this is probably not a relation at all
+                }
+              }
+              result.tokenMethods.add(method);
+              break;
+            default:
+              continue;
+          }
+          result.names.put(method, invokeName(annotation));
+        }
+      }
+    }
+    return result;
+  }
+
+  private static String DumpNode.invokeName(java.lang.annotation.Annotation annotation)
+      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+    return (String) annotation.annotationType().getMethod("name").invoke(annotation);
   }
 
   syn boolean DumpNode.isAstNode() {
-    // TODO
+    Class<?> clazz = getObject().getClass();
+    for (java.lang.reflect.Constructor<?> constructor : clazz.getConstructors()) {
+      for (java.lang.annotation.Annotation annotation : constructor.getAnnotations()) {
+        if (annotation.annotationType().getCanonicalName().startsWith(astNodeAnnotationPrefix()) &&
+              annotation.annotationType().getSimpleName().equals("Constructor")) {
+          return true;
+        }
+      }
+    }
     return false;
   }
 
+  inh String DumpNode.astNodeAnnotationPrefix();
+  eq DumpAst.getDumpNode().astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation";
+
   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;
+    java.util.List<java.lang.reflect.Method> singleChildMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> listChildMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> singleRelationMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> listRelationMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> tokenMethods = new java.util.ArrayList<>();
+    java.util.Map<java.lang.reflect.Method, String> names = new java.util.HashMap<>();
   }
 
   syn String DumpAst.toPlantUml() {
diff --git a/dumpAst2uml/src/main/jastadd/Printing.jrag b/dumpAst2uml/src/main/jastadd/Printing.jrag
index 9a399ba6bfdfd9ec6f5c8e279d6cf6fb39e933c1..c41f02b1052560ba2bba70e84943d98a16f7c40b 100644
--- a/dumpAst2uml/src/main/jastadd/Printing.jrag
+++ b/dumpAst2uml/src/main/jastadd/Printing.jrag
@@ -19,5 +19,5 @@ aspect Printing {
   syn String DumpChildNode.label() = getName();
   syn String DumpRelation.label() = getName();
   syn String DumpToken.label() = getName();
-
+  syn String DumpNode.label() = getLabel();
 }
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 f6e83dbf0441b9dc2a55a232c9e26ddfccfdd816..b0b6e02dffa8e7b4a9deb4e6f4ba6b1b25fa0eec 100644
--- a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
+++ b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
@@ -25,11 +25,13 @@ import static org.jastadd.dumpAst2uml.compiler.SimpleMain.Kind.*;
  *
  * @author rschoene - Initial contribution
  */
+@SuppressWarnings("unused")
 public class SimpleMain {
 
-  public static void main(String[] args) {
-//    printing();
-    createManualAST();
+  public static void main(String[] args) throws Exception {
+    printing();
+//    createManualAST();
+//    small();
   }
 
   private static void createManualAST() {
@@ -93,7 +95,7 @@ public class SimpleMain {
     System.out.println(content);
   }
 
-  private static void printing() {
+  private static void printing() throws IOException {
     System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
     System.setProperty("mustache.debug", "true");
     Grammar2Uml model = new Grammar2Uml();
@@ -109,7 +111,34 @@ public class SimpleMain {
     model.addFolder(folder1);
 
     model.treeResolveAll();
-    traverseInitial(model.toMustache());
+//    traverseInitial(model.toMustache());
+    Dumper.read(model.toMustache())
+        .dumpAsSource(Paths.get("temp.plantuml"))
+        .dumpAsSVG(Paths.get("temp.svg"));
+  }
+
+  private static void small() throws IOException {
+    Program program = new Program();
+    GrammarFile grammar = new GrammarFile();
+    grammar.setFileName("foo.relast");
+    program.addGrammarFile(grammar);
+
+    TypeDecl typeDeclA = new TypeDecl();
+    typeDeclA.setName("A");
+    grammar.addDeclaration(typeDeclA);
+
+    TypeDecl typeDeclB = new TypeDecl();
+    typeDeclB.setName("B");
+    grammar.addDeclaration(typeDeclB);
+
+    LeftDirectedRelation relation = new LeftDirectedRelation();
+    relation.setSource(new NormalRole(typeDeclA, "myB"));
+    relation.setTarget(new UnnamedRole(typeDeclB));
+    grammar.addDeclaration(relation);
+
+    Dumper.read(program)
+        .dumpAsSource(Paths.get("grammar.plantuml"))
+        .dumpAsSVG(Paths.get("grammar.svg"));
   }
 
   static final String MORE_INDENT = "| ";
diff --git a/dumpAst2uml/src/main/resources/dumpAst.mustache b/dumpAst2uml/src/main/resources/dumpAst.mustache
index ddbc1b11a0448d516319e8ddd9c0ce08a2b38bd0..fba11038ec059e56a2ee633d789595a1b216064f 100644
--- a/dumpAst2uml/src/main/resources/dumpAst.mustache
+++ b/dumpAst2uml/src/main/resources/dumpAst.mustache
@@ -1,19 +1,23 @@
 @startuml
+scale {{Scale}}
 {{#DumpNodes}}
-object {{name}} {
-{{! tokens }}
+{{#isAstNode}}
+object "{{label}}" as {{name}} {
 {{#DumpTokens}}
 {{#isDumpValueToken}}
-  {{label}} = {{Value}}
+  {{label}} = {{{Value}}}
 {{/isDumpValueToken}}
 {{/DumpTokens}}
 }
+{{/isAstNode}}
+{{/DumpNodes}}
+
+{{#DumpNodes}}
 {{#DumpTokens}}
 {{^isDumpValueToken}}
 {{outerNodeName}} ..> {{innerNodeName}} : {{label}}
 {{/isDumpValueToken}}
 {{/DumpTokens}}
-{{! child nodes }}
 {{#DumpChildNodes}}
 {{#isList}}
 {{#InnerDumpNodes}}
@@ -24,7 +28,6 @@ object {{name}} {
 {{outerNodeName}} *-- {{innerNodeName}} : {{label}}
 {{/isList}}
 {{/DumpChildNodes}}
-{{! relations }}
 {{#DumpRelations}}
 {{#isList}}
 {{#InnerDumpNodes}}