diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9729a314e03c730a3f31025a68ae8233c46d0a62..c2af79478a37ba64e52390b8a447292600cfdabb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,8 +1,21 @@
+variables:
+  GIT_SUBMODULE_STRATEGY: none
+
 stages:
   - build
 
 test:
   image: openjdk:8
   stage: build
+  before_script:
+    - git submodule init
+    - git submodule sync
+    - cat .gitmodules
+    - cat .git/config
+    - ls -lah *
+    - rm -rf relast.preprocessor/*
+    - ls -lah *
+    - git submodule update --remote
+    - ls -lah *
   script:
     - ./gradlew --no-daemon build
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..d26b5a22d5a7098f78d4bb4cbb21b00b09ace7d0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "relast-preprocessor"]
+	path = relast.preprocessor
+	url = ../relast-preprocessor.git
diff --git a/relast.preprocessor b/relast.preprocessor
new file mode 160000
index 0000000000000000000000000000000000000000..54af6737685fab0be452f446b70b9842aba89e42
--- /dev/null
+++ b/relast.preprocessor
@@ -0,0 +1 @@
+Subproject commit 54af6737685fab0be452f446b70b9842aba89e42
diff --git a/ros2rag.base/build.gradle b/ros2rag.base/build.gradle
index c08852a8a6c787c3ebcc53c8a9d6bb0eaf51c20f..d6b7e06c97a59d3011c6e92cf6b6f09181c6d484 100644
--- a/ros2rag.base/build.gradle
+++ b/ros2rag.base/build.gradle
@@ -66,7 +66,7 @@ task relast(type: JavaExec) {
 
     args = [
             "../libs/relast.jar",
-            "./src/main/jastadd/RelAst.relast",
+            "../relast.preprocessor/src/main/jastadd/RelAst.relast",
             "./src/main/jastadd/Ros2Rag.relast",
             "--listClass=java.util.ArrayList",
             "--jastAddList=JastAddList",
@@ -77,7 +77,7 @@ task relast(type: JavaExec) {
     ]
 
     inputs.files file("../libs/relast.jar"),
-            file("src/main/jastadd/RelAST.relast"),
+            file("../relast.preprocessor/src/main/jastadd/RelAST.relast"),
             file("src/main/jastadd/Ros2Rag.relast")
     outputs.files file("./src/gen/jastadd/Ros2Rag.ast"),
             file("src/gen/jastadd/Ros2Rag.jadd"),
@@ -92,29 +92,45 @@ jastadd {
         module("Ros2Rag") {
 
             java {
-                basedir "."
-                include "src/main/**/*.java"
-                include "src/gen/**/*.java"
+                basedir ".."
+                include "relast.preprocessor/main/**/*.java"
+                include "relast.preprocessor/gen/**/*.java"
+                include "ros2rag.base/src/main/**/*.java"
+                include "ros2rag.base/src/gen/**/*.java"
             }
 
             jastadd {
-                basedir "."
-                include "src/main/jastadd/**/*.ast"
-                include "src/main/jastadd/**/*.jadd"
-                include "src/main/jastadd/**/*.jrag"
-                include "src/gen/jastadd/**/*.ast"
-                include "src/gen/jastadd/**/*.jadd"
-                include "src/gen/jastadd/**/*.jrag"
+                basedir ".."
+                include "relast.preprocessor/src/main/jastadd/**/*.ast"
+                include "relast.preprocessor/src/main/jastadd/**/*.jadd"
+                include "relast.preprocessor/src/main/jastadd/**/*.jrag"
+                include "ros2rag.base/src/main/jastadd/**/*.ast"
+                include "ros2rag.base/src/main/jastadd/**/*.jadd"
+                include "ros2rag.base/src/main/jastadd/**/*.jrag"
+                include "ros2rag.base/src/gen/jastadd/**/*.ast"
+                include "ros2rag.base/src/gen/jastadd/**/*.jadd"
+                include "ros2rag.base/src/gen/jastadd/**/*.jrag"
             }
 
             scanner {
-                include "src/main/jastadd/Ros2Rag.flex"
+                basedir ".."
+                include "ros2rag.base/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 "ros2rag.base/src/main/jastadd/scanner/Macros.flex",               [-3]
+                include "relast.preprocessor/src/main/jastadd/scanner/RulesPreamble.flex", [-2]
+                include "ros2rag.base/src/main/jastadd/scanner/MappingContent.flex",       [-1]
+                include "ros2rag.base/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 {
-                include "src/main/jastadd/Preamble.parser"
-                include "src/main/jastadd/RelAst.parser"
-                include "src/main/jastadd/Ros2Rag.parser"
+                basedir ".."
+                include "ros2rag.base/src/main/jastadd/parser/Preamble.parser"
+                include "ros2rag.base/src/main/jastadd/parser/Ros2Rag.parser"
+                include "relast.preprocessor/src/main/jastadd/parser/RelAst.parser"
             }
         }
     }
diff --git a/ros2rag.base/src/main/jastadd/DumpTree.jrag b/ros2rag.base/src/main/jastadd/DumpTree.jrag
deleted file mode 100644
index 4dd3985dc725e1f901499589ed1d471ca7ffa0ac..0000000000000000000000000000000000000000
--- a/ros2rag.base/src/main/jastadd/DumpTree.jrag
+++ /dev/null
@@ -1,53 +0,0 @@
-aspect DumpTree {
-  private static final String ASTNode.DUMP_TREE_INDENT = "  ";
-
-  public String ASTNode.dumpTree() {
-    java.io.ByteArrayOutputStream bytes = new java.io.ByteArrayOutputStream();
-    dumpTree(new java.io.PrintStream(bytes));
-    return bytes.toString();
-  }
-
-  public void ASTNode.dumpTree(java.io.PrintStream out) {
-    dumpTree(out, "");
-    out.flush();
-  }
-
-  public void ASTNode.dumpTree(java.io.PrintStream out, String indent) {
-    out.print(indent + getClass().getSimpleName());
-    out.print(getTokens());
-    String extra = extraDumpInfo();
-    if (!extra.isEmpty()) {
-      out.print(" " + extra);
-    }
-    out.println();
-    String childIndent = indent + DUMP_TREE_INDENT;
-    for (ASTNode<?> child : astChildren()) {
-      if (child == null) {
-        out.println(childIndent + "null");
-      } else {
-        child.dumpTree(out, childIndent);
-      }
-    }
-  }
-
-  public String ASTNode.extraDumpInfo() { return ""; }
-
-  public String ASTNode.getTokens() {
-    java.util.TreeSet<java.lang.reflect.Method> methods = new java.util.TreeSet<>(java.util.Comparator.comparing(java.lang.reflect.Method::getName));
-
-    methods.addAll(java.util.Arrays.asList(getClass().getMethods()));
-
-    StringBuilder result = new StringBuilder();
-    for (java.lang.reflect.Method method : methods) {
-      ASTNodeAnnotation.Token token = method.getAnnotation(ASTNodeAnnotation.Token.class);
-      if (token != null) {
-        try {
-          result.append(String.format(" %s=\"%s\"", token.name(), method.invoke(this)));
-        } catch (IllegalAccessException  ignored) {
-        } catch (java.lang.reflect.InvocationTargetException ignored) {
-        }
-      }
-    }
-    return result.toString();
-  }
-}
diff --git a/ros2rag.base/src/main/jastadd/NameResolution.jrag b/ros2rag.base/src/main/jastadd/NameResolution.jrag
index 314d9d5fe3474dbab9026593c8bfe8dd57758c1d..fc4485858d199f352c18fcd188914020666ef619 100644
--- a/ros2rag.base/src/main/jastadd/NameResolution.jrag
+++ b/ros2rag.base/src/main/jastadd/NameResolution.jrag
@@ -1,32 +1,5 @@
 aspect NameResolution {
 
-  refine RefResolverStubs eq ASTNode.globallyResolveTypeDeclByToken(String id) = program().resolveTypeDecl(id);
-
-  syn TypeDecl Program.resolveTypeDecl(String name) {
-    for (TypeDecl decl : typeDecls()) {
-      if (decl.getName().equals(name)) {
-        return decl;
-      }
-    }
-    throw new RuntimeException("TypeDecl " + name + " could not be resolved.");
-  }
-
-  refine RefResolverStubs eq ASTNode.globallyResolveTokenComponentByToken(String id) {
-    // return a TokenComponent. id is of the form 'type_name + "." + token_name'
-    int dotIndex = id.indexOf(".");
-    String typeName = id.substring(0, dotIndex);
-    String tokenName = id.substring(dotIndex + 1);
-    TypeDecl type = program().resolveTypeDecl(typeName);
-    // iterate over components and find the matching tokenComponent
-    for (Component comp : type.getComponentList()) {
-      if (comp.isTokenComponent() && comp.getName().equals(tokenName)) {
-        return comp.asTokenComponent();
-      }
-    }
-    System.err.println("Could not resolve TokenComponent '" + id + "'.");
-    return null;
-  }
-
   refine RefResolverStubs eq UpdateDefinition.resolveMappingByToken(String id, int position) {
     // return a MappingDefinition
     for (MappingDefinition mappingDefinition : ros2rag().getMappingDefinitionList()) {
diff --git a/ros2rag.base/src/main/jastadd/Navigation.jrag b/ros2rag.base/src/main/jastadd/Navigation.jrag
index b4011120a6cdb25d9ce70ae4f05d080fd799cf59..a43beba54597e06cae95d577e610b5bba5b1e2c9 100644
--- a/ros2rag.base/src/main/jastadd/Navigation.jrag
+++ b/ros2rag.base/src/main/jastadd/Navigation.jrag
@@ -1,55 +1,20 @@
 aspect Navigation {
 
   // --- program ---
-  inh Program ASTNode.program();
-  eq Program.getChild().program() = this;
   eq Ros2Rag.getChild().program() = getProgram();
 
   // --- ros2rag
   inh Ros2Rag ASTNode.ros2rag();
   eq Ros2Rag.getChild().ros2rag() = this;
 
-  // --- typeDecls ---
-  coll java.util.Set<TypeDecl> Program.typeDecls() [new java.util.HashSet<>()] root Program;
-  TypeDecl contributes this
-    to Program.typeDecls()
-    for program();
-
-  // --- relations ---
-  coll java.util.Set<Relation> Program.relations() [new java.util.HashSet<>()] root Program;
-  Relation contributes this
-    to Program.relations()
-    for program();
-
-  // --- containingTypeDecl ---
-  inh TypeDecl Component.containingTypeDecl();
-  eq TypeDecl.getChild().containingTypeDecl() = this;
-
-//  syn boolean RelationComponent.multiplicityOne() = false;
-//  eq OneRelationComponent.multiplicityOne() = true;
-//  syn boolean RelationComponent.multiplicityOpt() = false;
-//  eq OptionalRelationComponent.multiplicityOpt() = true;
-//  syn boolean RelationComponent.multiplicityMany() = false;
-//  eq ManyRelationComponent.multiplicityMany() = true;
-
-  // --- containedFile ---
-  inh GrammarFile ASTNode.containedFile();
-  eq GrammarFile.getChild().containedFile() = this;
+  // --- containedFile (first equation should be in preprocessor) ---
   eq Program.getChild().containedFile() = null;
   eq Ros2Rag.getChild().containedFile() = null;
 
-  // --- isTypeComponent ---
+  // --- isTypeComponent (should be in preprocessor) ---
   syn boolean Component.isTypeComponent() = false;
   eq TypeComponent.isTypeComponent() = true;
 
-  // --- isTokenComponent ---
-  syn boolean Component.isTokenComponent() = false;
-  eq TokenComponent.isTokenComponent() = true;
-
-  // --- asTokenComponent ---
-  syn TokenComponent Component.asTokenComponent() = null;
-  eq TokenComponent.asTokenComponent() = this;
-
   // --- isWriteToMqttDefinition ---
   syn boolean UpdateDefinition.isWriteToMqttDefinition() = false;
   eq WriteToMqttDefinition.isWriteToMqttDefinition() = true;
diff --git a/ros2rag.base/src/main/jastadd/RelAst.parser b/ros2rag.base/src/main/jastadd/RelAst.parser
deleted file mode 100644
index f94fbef474825bbd669f89ce070b2ce8fc406c72..0000000000000000000000000000000000000000
--- a/ros2rag.base/src/main/jastadd/RelAst.parser
+++ /dev/null
@@ -1,218 +0,0 @@
-GrammarFile goal
-  = declaration.d goal.p     {: p.getDeclarationList().insertChild(d, 0); return p; :}
-  |                          {: return new GrammarFile(); :}
-;
-
-Declaration declaration
-  = type_decl
-  | relation
-  | comment
-;
-
-Comment comment
-  = COMMENT  {: return new Comment(COMMENT); :};
-
-TypeDecl type_decl
-  = ID components_opt.c SCOL
-    {:
-      TypeDecl result = new TypeDecl();
-      result.setName(ID);
-      result.setAbstract(false);
-      result.setComponentList(c);
-      return result;
-    :}
-  | ID COL ID.s components_opt.c SCOL
-    {:
-      TypeDecl result = new TypeDecl();
-      result.setName(ID);
-      result.setAbstract(false);
-      result.setSuperType(TypeDecl.createRef(s));
-      result.setComponentList(c);
-      return result;
-    :}
-  | ABSTRACT ID components_opt.c SCOL
-    {:
-      TypeDecl result = new TypeDecl();
-      result.setName(ID);
-      result.setAbstract(true);
-      result.setComponentList(c);
-      return result;
-    :}
-  | ABSTRACT ID COL ID.s components_opt.c SCOL
-    {:
-      TypeDecl result = new TypeDecl();
-      result.setName(ID);
-      result.setAbstract(true);
-      result.setSuperType(TypeDecl.createRef(s));
-      result.setComponentList(c);
-      return result;
-    :}
-;
-
-JastAddList components_opt
-  = /* empty */             {: return new JastAddList(); :}
-  | ASSIGN components.l     {: return l; :}
-;
-
-JastAddList components
-  = /* empty */                 {: return new JastAddList(); :}
-  | components.l component.c    {: return l.add(c); :}
-;
-
-Component component
-  = SLASH actual_component.c SLASH
-      {:
-        c.setNTA(true);
-        return c;
-      :}
-  | actual_component
-;
-
-Component actual_component
-  = ID.name COL ID.type STAR
-    {:
-      ListComponent result = new ListComponent();
-      result.setName(name);
-      result.setTypeDecl(TypeDecl.createRef(type));
-      return result;
-    :}
-  | ID.name COL ID.type
-    {:
-      NormalComponent result = new NormalComponent();
-      result.setName(name);
-      result.setTypeDecl(TypeDecl.createRef(type));
-      return result;
-    :}
-  | ID.type STAR
-    {:
-      ListComponent result = new ListComponent();
-      result.setName(type);
-      result.setTypeDecl(TypeDecl.createRef(type));
-      return result;
-    :}
-  | ID.type
-    {:
-      NormalComponent result = new NormalComponent();
-      result.setName(type);
-      result.setTypeDecl(TypeDecl.createRef(type));
-      return result;
-    :}
-  | LBRACKET ID.name COL ID.type RBRACKET
-    {:
-      OptComponent result = new OptComponent();
-      result.setName(name);
-      result.setTypeDecl(TypeDecl.createRef(type));
-      return result;
-    :}
-  | LBRACKET ID.type RBRACKET
-    {:
-      OptComponent result = new OptComponent();
-      result.setName(type);
-      result.setTypeDecl(TypeDecl.createRef(type));
-      return result;
-    :}
-  | LT ID.name COL java_type_use.type GT
-    {:
-      TokenComponent result = new TokenComponent();
-      result.setName(name);
-      result.setJavaTypeUse(type);
-      return result;
-    :}
-  | LT ID.type GT
-    {:
-      TokenComponent result = new TokenComponent();
-      result.setName(type);
-      result.setJavaTypeUse(new SimpleJavaTypeUse(type));
-      return result;
-    :}
-;
-
-
-JavaTypeUse java_type_use
-  = parameterized_java_type_use
-  | simple_java_type_use
-;
-
-ArrayList qualified_name
-  = ID
-  | qualified_name DOT ID
-;
-
-
-JastAddList java_type_use_list
-  = java_type_use.u                                 {: return new JastAddList().add(u); :}
-  | java_type_use_list.l COMMA java_type_use.u      {: return l.add(u); :}
-;
-
-SimpleJavaTypeUse simple_java_type_use
-  = qualified_name.n
-    {:
-      return new SimpleJavaTypeUse((String)n.stream().map( x -> ((Symbol)x).value.toString()).collect(java.util.stream.Collectors.joining(".")));
-    :}
-;
-
-ParameterizedJavaTypeUse parameterized_java_type_use
-  = simple_java_type_use.i LT java_type_use_list.l GT
-    {:
-      return new ParameterizedJavaTypeUse(i.getName(), l);
-    :}
-;
-
-Relation relation
-  = RELATION unnamed_role.a LEFT navigable_role.b SCOL
-    {:
-       LeftDirectedRelation result = new LeftDirectedRelation();
-       result.setSource(b);
-       result.setTarget(a);
-       return result;
-    :}
-  | RELATION navigable_role.a RIGHT unnamed_role.b SCOL
-    {:
-       RightDirectedRelation result = new RightDirectedRelation();
-       result.setSource(a);
-       result.setTarget(b);
-       return result;
-    :}
-  | RELATION navigable_role.a BIDIRECTIONAL navigable_role.b SCOL
-    {:
-       BidirectionalRelation result = new BidirectionalRelation();
-       result.setLeft(a);
-       result.setRight(b);
-       return result;
-    :}
-;
-
-
-UnnamedRole unnamed_role
-  = ID.type
-    {:
-      UnnamedRole result = new UnnamedRole();
-      result.setType(TypeDecl.createRef(type));
-      return result;
-    :}
-  | navigable_role
-;
-
-NavigableRole navigable_role
-  = ID.type DOT ID.name
-    {:
-      NormalRole result = new NormalRole();
-      result.setName(name);
-      result.setType(TypeDecl.createRef(type));
-      return result;
-    :}
-  | ID.type DOT ID.name STAR
-    {:
-      ListRole result = new ListRole();
-      result.setName(name);
-      result.setType(TypeDecl.createRef(type));
-      return result;
-    :}
-  | ID.type DOT ID.name QUESTION_MARK
-    {:
-      OptRole result = new OptRole();
-      result.setName(name);
-      result.setType(TypeDecl.createRef(type));
-      return result;
-    :}
-;
diff --git a/ros2rag.base/src/main/jastadd/RelAst.relast b/ros2rag.base/src/main/jastadd/RelAst.relast
deleted file mode 100644
index 2168413aef3510a889a1a2ea3f71ea4624938f1c..0000000000000000000000000000000000000000
--- a/ros2rag.base/src/main/jastadd/RelAst.relast
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-Program ::= GrammarFile *;
-
-abstract Grammar ::= Declaration*;
-GrammarFile : Grammar ::= <FileName> ;
-
-abstract Declaration;
-
-TypeDecl:Declaration ::= <Name> <Abstract:boolean>  Component*;
-
-rel TypeDecl.SuperType? <-> TypeDecl.SubType*;
-
-abstract Component ::= <Name> <NTA:boolean>;
-abstract TypeComponent : Component;
-
-rel TypeComponent.TypeDecl <-> TypeDecl.PotentialParent*;
-
-NormalComponent : TypeComponent;
-ListComponent : TypeComponent;
-OptComponent : TypeComponent;
-
-TokenComponent : Component ::= JavaTypeUse;
-
-abstract JavaTypeUse ::= <Name>;
-SimpleJavaTypeUse : JavaTypeUse;
-ParameterizedJavaTypeUse : JavaTypeUse ::= JavaTypeUse*;
-
-abstract Relation : Declaration;
-// rel Source.Name -> Target;
-// rel Target <- Source.Name;
-abstract DirectedRelation : Relation ::= Source:NavigableRole Target:UnnamedRole ;
-LeftDirectedRelation : DirectedRelation;
-RightDirectedRelation : DirectedRelation;
-// rel Left.LeftName <-> Right.RightName;
-BidirectionalRelation : Relation ::= Left:NavigableRole Right:NavigableRole ;
-
-abstract Role;
-abstract NavigableRole : Role ::= <Name>;
-NormalRole : NavigableRole;
-ListRole : NavigableRole;
-OptRole : NavigableRole;
-
-UnnamedRole : Role ;
-
-rel Role.Type <-> TypeDecl.Role*;
-
-// comments
-Comment : Declaration ::= <Text>;
diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.flex b/ros2rag.base/src/main/jastadd/Ros2Rag.flex
deleted file mode 100644
index 2d44774c3a4e775b4306dca783688ef27b498648..0000000000000000000000000000000000000000
--- a/ros2rag.base/src/main/jastadd/Ros2Rag.flex
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.jastadd.ros2rag.scanner;
-
-import org.jastadd.ros2rag.parser.Ros2RagParser.Terminals;
-
-%%
-
-%public
-%final
-%class Ros2RagScanner
-%extends beaver.Scanner
-
-%type beaver.Symbol
-%function nextToken
-%yylexthrow beaver.Scanner.Exception
-%scanerror Ros2RagScanner.ScannerError
-
-%line
-%column
-%{
-  private StringBuilder stringLitSb = new StringBuilder();
-
-  private beaver.Symbol sym(short id) {
-    return new beaver.Symbol(id, yyline + 1, yycolumn + 1, yylength(), yytext());
-  }
-
-  private beaver.Symbol sym(short id, String text) {
-    return new beaver.Symbol(id, yyline + 1, yycolumn + 1, yylength(), text);
-  }
-
-
-  public static class ScannerError extends Error {
-    public ScannerError(String message) {
-      super(message);
-    }
-  }
-%}
-
-WhiteSpace = [ ] | \t | \f | \n | \r | \r\n
-TraditionalComment   = [/][*][^*]*[*]+([^*/][^*]*[*]+)*[/]
-EndOfLineComment = "//" [^\n\r]*
-Comment = {TraditionalComment} | {EndOfLineComment}
-
-MappingContent = [{][:][^:]*[:]+([^:}][^:]*[:]+)*[}]
-
-ID = [a-zA-Z$_][a-zA-Z0-9$_]*
-
-%%
-{WhiteSpace} { /* ignore */ }
-{Comment}    { return sym(Terminals.COMMENT); }
-{MappingContent} { return sym(Terminals.MAPPING_CONTENT); }
-
-"abstract"   { return sym(Terminals.ABSTRACT); }
-"rel"        { return sym(Terminals.RELATION); }
-
-"read"       { return sym(Terminals.READ); }
-"write"      { return sym(Terminals.WRITE); }
-"using"      { return sym(Terminals.USING); }
-"canDependOn" { return sym(Terminals.CAN_DEPEND_ON); }
-"maps"       { return sym(Terminals.MAPS); }
-"to"         { return sym(Terminals.TO); }
-"as"         { return sym(Terminals.AS); }
-
-";"          { return sym(Terminals.SCOL); }
-":"          { return sym(Terminals.COL); }
-"::="        { return sym(Terminals.ASSIGN); }
-"*"          { return sym(Terminals.STAR); }
-"."          { return sym(Terminals.DOT); }
-","          { return sym(Terminals.COMMA); }
-"<"          { return sym(Terminals.LT); }
-">"          { return sym(Terminals.GT); }
-"["          { return sym(Terminals.LBRACKET); }
-"]"          { return sym(Terminals.RBRACKET); }
-"/"          { return sym(Terminals.SLASH); }
-"?"          { return sym(Terminals.QUESTION_MARK); }
-"->"         { return sym(Terminals.RIGHT); }
-"<-"         { return sym(Terminals.LEFT); }
-"<->"        { return sym(Terminals.BIDIRECTIONAL); }
-
-// ID
-{ID}         { return sym(Terminals.ID); }
-<<EOF>>      { return sym(Terminals.EOF); }
-
-[^]            { throw new ScannerError((yyline+1) +"," + (yycolumn+1) + ": Illegal character <"+yytext()+">"); }
diff --git a/ros2rag.base/src/main/jastadd/backend/AbstractGrammar.jadd b/ros2rag.base/src/main/jastadd/backend/AbstractGrammar.jadd
deleted file mode 100644
index 606be6ea329fe361b1784e88489f6e34ac81ed1b..0000000000000000000000000000000000000000
--- a/ros2rag.base/src/main/jastadd/backend/AbstractGrammar.jadd
+++ /dev/null
@@ -1,171 +0,0 @@
-aspect BackendAbstractGrammar {
-
-  public static String ASTNode.listInterface = "java.util.List";
-  public static String ASTNode.listClass = "java.util.ArrayList";
-  public static String ASTNode.jastAddListType = "List";
-  public static String ASTNode.jastAddOptType = "Opt";
-
-  public String Program.generateAbstractGrammar() {
-    StringBuilder sb = new StringBuilder();
-    generateAbstractGrammar(sb);
-    return sb.toString();
-  }
-
-  public void Program.generateAbstractGrammar(StringBuilder b) {
-    for (GrammarFile file : getGrammarFileList()) {
-      file.generateAbstractGrammar(b);
-    }
-  }
-
-  public void GrammarFile.generateAbstractGrammar(StringBuilder b) {
-    b.append("// Grammar for file ").append(getFileName()).append("\n");
-    super.generateAbstractGrammar(b);
-    b.append("\n");
-  }
-
-  public void Grammar.generateAbstractGrammar(StringBuilder b) {
-
-    for (Declaration decl : getDeclarationList()) {
-      decl.generateAbstractGrammar(b);
-    }
-  }
-
-  abstract public void Declaration.generateAbstractGrammar(StringBuilder b);
-
-  public void Comment.generateAbstractGrammar(StringBuilder b) {
-    b.append(getText()).append("\n");
-  }
-
-  public void TypeDecl.generateAbstractGrammar(StringBuilder b) {
-    if (getAbstract()) {
-      b.append("abstract ");
-    }
-    b.append(getName()).append(" ");
-    if (hasSuperType()) {
-      b.append(": ").append(getSuperType().getName()).append(" ");
-    }
-    b.append("::=");
-    for (Component component : getComponentList()) {
-      b.append(" ");
-      component.generateAbstractGrammar(b);
-    }
-    b.append(";\n");
-  }
-
-  public abstract void Component.generateAbstractGrammar(StringBuilder b);
-
-  public void NormalComponent.generateAbstractGrammar(StringBuilder b) {
-    if (getNTA()) {
-      b.append("/");
-    }
-
-    if (!getName().equals("")) {
-      b.append(getName()).append(":");
-    }
-    b.append(getTypeDecl().getName());
-    if (getNTA()) {
-      b.append("/");
-    }
-  }
-
-  public void ListComponent.generateAbstractGrammar(StringBuilder b) {
-    if (getNTA()) {
-      b.append("/");
-    }
-
-    if (!getName().equals("")) {
-      b.append(getName()).append(":");
-    }
-    b.append(getTypeDecl().getName()).append("*");
-    if (getNTA()) {
-      b.append("/");
-    }
-  }
-
-  public void OptComponent.generateAbstractGrammar(StringBuilder b) {
-    if (getNTA()) {
-      b.append("/");
-    }
-    b.append("[");
-    if (!getName().equals("")) {
-      b.append(getName()).append(":");
-    }
-    b.append(getTypeDecl().getName()).append("]");
-    if (getNTA()) {
-      b.append("/");
-    }
-  }
-
-  public void TokenComponent.generateAbstractGrammar(StringBuilder b) {
-    if (getNTA()) {
-      b.append("/");
-    }
-    b.append("<");
-    if (!getName().equals("")) {
-      b.append(getName()).append(":");
-    }
-    getJavaTypeUse().generateAbstractGrammar(b);
-    b.append(">");
-    if (getNTA()) {
-      b.append("/");
-    }
-  }
-
-  abstract public void JavaTypeUse.generateAbstractGrammar(StringBuilder b);
-
-  public void SimpleJavaTypeUse.generateAbstractGrammar(StringBuilder b) {
-    b.append(getName());
-  }
-
-  public void ParameterizedJavaTypeUse.generateAbstractGrammar(StringBuilder b) {
-    b.append("<");
-    boolean first = true;
-    for (JavaTypeUse javaTypeUse : getJavaTypeUseList()) {
-      if (first) {
-        first = false;
-      } else {
-        b.append(", ");
-      }
-      javaTypeUse.generateAbstractGrammar(b);
-    }
-    b.append(">");
-  }
-
-  abstract public void Relation.generateAbstractGrammar(StringBuilder b);
-
-  public void DirectedRelation.generateAbstractGrammar(StringBuilder b) {
-    b.append("rel ");
-    getSource().generateAbstractGrammar(b);
-    b.append(" -> ");
-    getTarget().generateAbstractGrammar(b);
-    b.append(";\n");
-  }
-
-  public void BidirectionalRelation.generateAbstractGrammar(StringBuilder b) {
-    b.append("rel ");
-    getLeft().generateAbstractGrammar(b);
-    b.append(" <-> ");
-    getRight().generateAbstractGrammar(b);
-    b.append(";\n");
-  }
-
-  abstract public void Role.generateAbstractGrammar(StringBuilder b);
-
-  public void NormalRole.generateAbstractGrammar(StringBuilder b) {
-    b.append(getType().getName()).append(".").append(getName());
-  }
-
-  public void ListRole.generateAbstractGrammar(StringBuilder b) {
-    b.append(getType().getName()).append(".").append(getName()).append("*");
-  }
-
-  public void OptRole.generateAbstractGrammar(StringBuilder b) {
-    b.append(getType().getName()).append(".").append(getName()).append("?");
-  }
-
-  public void UnnamedRole.generateAbstractGrammar(StringBuilder b) {
-    b.append(getType().getName());
-  }
-
-
-}
diff --git a/ros2rag.base/src/main/jastadd/Preamble.parser b/ros2rag.base/src/main/jastadd/parser/Preamble.parser
similarity index 100%
rename from ros2rag.base/src/main/jastadd/Preamble.parser
rename to ros2rag.base/src/main/jastadd/parser/Preamble.parser
diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.parser b/ros2rag.base/src/main/jastadd/parser/Ros2Rag.parser
similarity index 100%
rename from ros2rag.base/src/main/jastadd/Ros2Rag.parser
rename to ros2rag.base/src/main/jastadd/parser/Ros2Rag.parser
diff --git a/ros2rag.base/src/main/jastadd/scanner/Header.flex b/ros2rag.base/src/main/jastadd/scanner/Header.flex
new file mode 100644
index 0000000000000000000000000000000000000000..974aa01fe70d15edbd4c80ac67169a18fe95f9e8
--- /dev/null
+++ b/ros2rag.base/src/main/jastadd/scanner/Header.flex
@@ -0,0 +1,17 @@
+package org.jastadd.ros2rag.scanner;
+
+import org.jastadd.ros2rag.parser.Ros2RagParser.Terminals;
+%%
+
+%public
+%final
+%class Ros2RagScanner
+%extends beaver.Scanner
+
+%type beaver.Symbol
+%function nextToken
+%yylexthrow beaver.Scanner.Exception
+%scanerror Ros2RagScanner.ScannerError
+
+%line
+%column
diff --git a/ros2rag.base/src/main/jastadd/scanner/Keywords.flex b/ros2rag.base/src/main/jastadd/scanner/Keywords.flex
new file mode 100644
index 0000000000000000000000000000000000000000..3f546823fd8e034f821c8e867d8777ef697effdd
--- /dev/null
+++ b/ros2rag.base/src/main/jastadd/scanner/Keywords.flex
@@ -0,0 +1,7 @@
+"read"       { return sym(Terminals.READ); }
+"write"      { return sym(Terminals.WRITE); }
+"using"      { return sym(Terminals.USING); }
+"canDependOn" { return sym(Terminals.CAN_DEPEND_ON); }
+"maps"       { return sym(Terminals.MAPS); }
+"to"         { return sym(Terminals.TO); }
+"as"         { return sym(Terminals.AS); }
diff --git a/ros2rag.base/src/main/jastadd/scanner/Macros.flex b/ros2rag.base/src/main/jastadd/scanner/Macros.flex
new file mode 100644
index 0000000000000000000000000000000000000000..08bb629fe2f9c10db42ab6e4587918447e330a63
--- /dev/null
+++ b/ros2rag.base/src/main/jastadd/scanner/Macros.flex
@@ -0,0 +1 @@
+MappingContent = [{][:][^:]*[:]+([^:}][^:]*[:]+)*[}]
diff --git a/ros2rag.base/src/main/jastadd/scanner/MappingContent.flex b/ros2rag.base/src/main/jastadd/scanner/MappingContent.flex
new file mode 100644
index 0000000000000000000000000000000000000000..fdf3f205113e79a007bc7f5bb9d5f28433f7f387
--- /dev/null
+++ b/ros2rag.base/src/main/jastadd/scanner/MappingContent.flex
@@ -0,0 +1 @@
+{MappingContent} { return sym(Terminals.MAPPING_CONTENT); }
diff --git a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java
index f9b6f5f9153c2c07b6cb669a6be2e0b13d1dc5ac..afae43aacba3080c023adecaa8de95b12efd451e 100644
--- a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java
+++ b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java
@@ -83,12 +83,12 @@ public class Compiler {
   }
 
 
-  public static int main(String[] args) {
+  public static void main(String[] args) {
     try {
-      return new Compiler().run(args);
+      new Compiler().run(args);
     } catch (CommandLineException | CompilerException e) {
       System.err.println(e.getMessage());
-      return 1;
+      System.exit(-1);
     }
   }
 
diff --git a/settings.gradle b/settings.gradle
index 22db44e3b12b9129e0206f5e6a313da08e49e368..558a492cc410a682b33c2a452474c8e9eade2bed 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -4,3 +4,4 @@ include 'ros2rag.base'
 include 'ros2rag.example'
 include 'ros2rag.senderstub'
 include 'ros2rag.receiverstub'
+include 'relast.preprocessor'