diff --git a/src/main/java/org/jastadd/relast/compiler/Compiler.java b/src/main/java/org/jastadd/relast/compiler/Compiler.java
index efdda319429884637beac1ff15b655a91d4cd4c6..f423ad941cb4cd4a7caf9227a31902401b9ca647 100644
--- a/src/main/java/org/jastadd/relast/compiler/Compiler.java
+++ b/src/main/java/org/jastadd/relast/compiler/Compiler.java
@@ -1,12 +1,11 @@
 package org.jastadd.relast.compiler;
 
 import beaver.Parser;
+import org.jastadd.option.ArgumentParser;
+import org.jastadd.option.Option;
+import org.jastadd.option.ValueOption;
 import org.jastadd.relast.ast.GrammarFile;
 import org.jastadd.relast.ast.Program;
-import org.jastadd.relast.compiler.options.CommandLine;
-import org.jastadd.relast.compiler.options.CommandLine.CommandLineException;
-import org.jastadd.relast.compiler.options.Option;
-import org.jastadd.relast.compiler.options.StringOption;
 import org.jastadd.relast.parser.RelAstParser;
 import org.jastadd.relast.scanner.RelAstScanner;
 
@@ -18,15 +17,16 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 public class Compiler {
 
-  private StringOption optionOutputDir;
-  private StringOption optionInputDir;
+  private ValueOption optionOutputDir;
+  private ValueOption optionInputDir;
 
   private ArrayList<Option<?>> options;
-  private CommandLine commandLine;
+  private ArgumentParser commandLine;
 
   public Compiler() {
     options = new ArrayList<>();
@@ -36,21 +36,22 @@ public class Compiler {
   public static void main(String[] args) {
     try {
       new Compiler().run(args);
-    } catch (CommandLineException | CompilerException e) {
+    } catch (CompilerException e) {
       System.err.println(e.getMessage());
       System.exit(-1);
     }
   }
 
-  public int run(String[] args) throws CommandLineException, CompilerException {
+  public int run(String[] args) throws CompilerException {
     options = new ArrayList<>();
     addOptions();
-    commandLine = new CommandLine(options);
-    commandLine.parse(args);
+    commandLine = new ArgumentParser();
+    commandLine.addOptions(options);
+    commandLine.parseArgs(args, System.err);
 
     Path inputPath;
-    if (optionInputDir.isSet()) {
-      inputPath = Paths.get(optionInputDir.getValue());
+    if (optionInputDir.isMatched()) {
+      inputPath = Paths.get(optionInputDir.value());
     } else {
       inputPath = Paths.get(".");
       printMessage("No input dir is set. Assuming current directory '" + inputPath.toAbsolutePath().toString() + "'.");
@@ -65,8 +66,8 @@ public class Compiler {
     }
 
     Path outputPath; // should not be used, but otherwise there is a compiler warning
-    if (optionOutputDir.isSet()) {
-      outputPath = Paths.get(optionOutputDir.getValue());
+    if (optionOutputDir.isMatched()) {
+      outputPath = Paths.get(optionOutputDir.value());
     } else {
       outputPath = Paths.get("./gen/");
       printMessage("No output dir is set. Assuming '" + outputPath.toAbsolutePath().toString() + "'.");
@@ -78,7 +79,8 @@ public class Compiler {
 
     printMessage("Running RelAST Preprocessor");
 
-    List<String> otherArgs = commandLine.getArguments();
+    // TODO treat unknown options appropriately
+    List<String> otherArgs = Collections.emptyList();
     if (!otherArgs.isEmpty()) {
       printMessage("Unsupported arguments will be ignored: " + otherArgs);
     }
@@ -111,8 +113,8 @@ public class Compiler {
   }
 
   private void addOptions() {
-    optionOutputDir = addOption(new StringOption("outputDir", "target directory for the generated files."));
-    optionInputDir = addOption(new StringOption("inputDir", "input directory."));
+    optionOutputDir = addOption(new ValueOption("outputDir", "target directory for the generated files."));
+    optionInputDir = addOption(new ValueOption("inputDir", "input directory."));
   }
 
   private <OptionType extends Option<?>> OptionType addOption(OptionType option) {
@@ -152,7 +154,7 @@ public class Compiler {
     System.err.println();
     System.err.println("Usage: java -jar relast.jar [--option1] [--option2=value] ...  <filename1> <filename2> ... ");
     System.err.println("Options:");
-    System.err.print(commandLine.printOptionHelp());
+    commandLine.printHelp(System.err);
     return 1;
   }
 
diff --git a/src/main/java/org/jastadd/relast/compiler/options/CommandLine.java b/src/main/java/org/jastadd/relast/compiler/options/CommandLine.java
deleted file mode 100644
index c3f02bf56d4fb4c779e489cfaa7fb150c6ea68cb..0000000000000000000000000000000000000000
--- a/src/main/java/org/jastadd/relast/compiler/options/CommandLine.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package org.jastadd.relast.compiler.options;
-
-import java.util.*;
-
-public class CommandLine {
-  private final Collection<Option<?>> options;
-  private final Map<String, Option<?>> mapping;
-  private final List<String> arguments;
-
-  public CommandLine(Collection<Option<?>> options) {
-    this.options = options;
-    this.mapping = new HashMap<>();
-    for (Option<?> option : options) {
-      mapping.put(option.getName(), option);
-    }
-    this.arguments = new ArrayList<>();
-  }
-
-  public void parse(String[] args) throws CommandLineException {
-    int i = 0;
-    while (i < args.length) {
-      if (args[i].startsWith(Option.PREFIX)) {
-        int argumentIndex = args[i].indexOf("=");
-        String name;
-        String argument = null;
-        if (argumentIndex > 0) {
-          name = args[i].substring(2, argumentIndex);
-          argument = args[i].substring(argumentIndex + 1);
-        } else {
-          name = args[i].substring(2);
-        }
-        Option<?> option = mapping.get(name);
-        if (option == null) {
-          throw new CommandLineException("Option " + Option.PREFIX + name + " not found");
-        }
-        match(option, argument);
-      } else {
-        arguments.add(args[i]);
-      }
-      i++;
-    }
-  }
-
-  public void match(Option<?> option, String argument) throws CommandLineException {
-    try {
-      switch (option.hasArgument()) {
-        case NO:
-          if (argument == null) {
-            option.match(null);
-          } else {
-            throw new CommandLineException("Option " + option + " is not allowed to have an argument");
-          }
-          break;
-        case OPTIONAL:
-          option.match(argument);
-          break;
-        case YES:
-          if (argument != null) {
-            option.match(argument);
-          } else {
-            throw new CommandLineException("Option " + option + " requires an argument");
-          }
-          break;
-      }
-    } catch (Option.IllegalMatchException e) {
-      throw new CommandLineException("Invalid value for option " + option + ": " + e.getMessage());
-    }
-  }
-
-  public List<String> getArguments() {
-    return arguments;
-  }
-
-  public String printOptionHelp() {
-    StringBuilder sb = new StringBuilder();
-    int longestOption = 0;
-    for (Option<?> option : options) {
-      if (longestOption < option.getName().length()) {
-        longestOption = option.getName().length();
-      }
-    }
-    for (Option<?> option : new TreeSet<>(options)) {
-      String s = String.format("  %s%-" + (longestOption + 6) + "s %s%n",
-          Option.PREFIX, option.getName(), option.getDescription());
-      sb.append(s);
-    }
-    return sb.toString();
-  }
-
-  public static class CommandLineException extends Exception {
-    private static final long serialVersionUID = 1L;
-
-    public CommandLineException(String message) {
-      super(message);
-    }
-  }
-}
diff --git a/src/main/java/org/jastadd/relast/compiler/options/EnumOption.java b/src/main/java/org/jastadd/relast/compiler/options/EnumOption.java
deleted file mode 100644
index 92b5149a9c3d36cf2053f0a7d0a2cadfd52129ec..0000000000000000000000000000000000000000
--- a/src/main/java/org/jastadd/relast/compiler/options/EnumOption.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.jastadd.relast.compiler.options;
-
-import java.util.Collection;
-import java.util.TreeSet;
-
-public class EnumOption extends Option<String> {
-  private final TreeSet<String> allowedValues;
-  private final String defaultValue;
-  private String value;
-  private boolean isSet;
-
-  public EnumOption(String name, String description, Collection<String> allowedValues, String defaultValue) {
-    super(name, description);
-    this.allowedValues = new TreeSet<>(allowedValues);
-    this.defaultValue = defaultValue;
-    this.value = defaultValue;
-    this.isSet = false;
-  }
-
-  public boolean addAllowedValue(String allowedValue) {
-    return allowedValues.add(allowedValue);
-  }
-
-  @Override
-  public String getValue() {
-    return value;
-  }
-
-  @Override
-  public Option.HasArgument hasArgument() {
-    return Option.HasArgument.OPTIONAL;
-  }
-
-  @Override
-  public void match(String argument) throws IllegalMatchException {
-    if (argument == null) {
-      isSet = true;
-      value = defaultValue;
-    } else if (allowedValues.contains(argument)) {
-      isSet = true;
-      value = argument;
-    } else {
-      throw new IllegalMatchException(argument
-          + " is not allowed, allowed values are " + allowedValues);
-    }
-  }
-
-  @Override
-  public boolean isSet() {
-    return isSet;
-  }
-
-  @Override
-  public String getDescription() {
-    String allowedValuesStr = allowedValues.toString();
-    allowedValuesStr = allowedValuesStr.substring(1);
-    allowedValuesStr = allowedValuesStr.substring(0, allowedValuesStr.length() - 1);
-    return super.getDescription() + " (allowed values: " + allowedValuesStr + ")";
-  }
-}
diff --git a/src/main/java/org/jastadd/relast/compiler/options/FlagOption.java b/src/main/java/org/jastadd/relast/compiler/options/FlagOption.java
deleted file mode 100644
index 80c2f0cb0313cc05ca9b05795fc3acc3ebd5d849..0000000000000000000000000000000000000000
--- a/src/main/java/org/jastadd/relast/compiler/options/FlagOption.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.jastadd.relast.compiler.options;
-
-public class FlagOption extends Option<Boolean> {
-  private boolean value;
-
-  public FlagOption(String name, String description) {
-    super(name, description);
-    value = false;
-  }
-
-  @Override
-  public Boolean getValue() {
-    return value;
-  }
-
-  @Override
-  public Option.HasArgument hasArgument() {
-    return Option.HasArgument.NO;
-  }
-
-  @Override
-  public void match(String string) throws IllegalMatchException {
-    value = true;
-  }
-
-  @Override
-  public boolean isSet() {
-    return getValue();
-  }
-}
diff --git a/src/main/java/org/jastadd/relast/compiler/options/Option.java b/src/main/java/org/jastadd/relast/compiler/options/Option.java
deleted file mode 100644
index e20354e973e5ec11568041291242c2699ec0ab4d..0000000000000000000000000000000000000000
--- a/src/main/java/org/jastadd/relast/compiler/options/Option.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.jastadd.relast.compiler.options;
-
-abstract public class Option<ValueType> implements Comparable<Option<?>> {
-  public final static String PREFIX = "--";
-  private final String name;
-  private final String description;
-
-  public Option(String name, String description) {
-    this.name = name;
-    this.description = description;
-  }
-
-  public String getName() {
-    return name;
-  }
-
-  public String getDescription() {
-    return description;
-  }
-
-  @Override
-  public int compareTo(Option<?> o) {
-    return name.compareTo(o.name);
-  }
-
-  @Override
-  public boolean equals(Object other) {
-    if (other instanceof Option) {
-      return compareTo((Option<?>) other) == 0;
-    }
-    return false;
-  }
-
-  @Override
-  public String toString() {
-    return PREFIX + name;
-  }
-
-  abstract public boolean isSet();
-
-  abstract public ValueType getValue();
-
-  abstract public HasArgument hasArgument();
-
-  abstract public void match(String input) throws IllegalMatchException;
-
-  public enum HasArgument {
-    NO,
-    OPTIONAL,
-    YES
-  }
-
-  public static class IllegalMatchException extends Exception {
-    private static final long serialVersionUID = 1L;
-
-    public IllegalMatchException(String message) {
-      super(message);
-    }
-  }
-}
diff --git a/src/main/java/org/jastadd/relast/compiler/options/StringOption.java b/src/main/java/org/jastadd/relast/compiler/options/StringOption.java
deleted file mode 100644
index a1298aeaadc259e5ecca3f1ffbade77de9a8315b..0000000000000000000000000000000000000000
--- a/src/main/java/org/jastadd/relast/compiler/options/StringOption.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.jastadd.relast.compiler.options;
-
-public class StringOption extends Option<String> {
-  private String value;
-  private boolean isSet;
-
-  public StringOption(String name, String description) {
-    this(name, description, "");
-  }
-
-  public StringOption(String name, String description, String defaultValue) {
-    super(name, description);
-    value = defaultValue;
-    isSet = false;
-  }
-
-  @Override
-  public String getValue() {
-    return value;
-  }
-
-  @Override
-  public Option.HasArgument hasArgument() {
-    return Option.HasArgument.YES;
-  }
-
-  @Override
-  public void match(String value) {
-    this.value = value;
-    isSet = true;
-  }
-
-  @Override
-  public boolean isSet() {
-    return isSet;
-  }
-}
diff --git a/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java b/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java
index 85fc911d779fef34f56e6cb0802a55072e9d7fdd..becda3fbdfb3152aa380b942a9cd1e76e32e4cc9 100644
--- a/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java
+++ b/src/test/java/org/jastadd/ros2rag/tests/RelAstTest.java
@@ -1,7 +1,6 @@
 package org.jastadd.ros2rag.tests;
 
 import org.jastadd.relast.compiler.Compiler;
-import org.jastadd.relast.compiler.options.CommandLine;
 import org.junit.jupiter.api.Test;
 
 import java.io.File;
@@ -12,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class RelAstTest {
 
-  void transform(String inputDir, String outputDir) throws CommandLine.CommandLineException, Compiler.CompilerException {
+  void transform(String inputDir, String outputDir) throws Compiler.CompilerException {
 
     System.out.println("Running test in directory '" + Paths.get(".").toAbsolutePath() + "'.");
     assertTrue(Paths.get(inputDir).toFile().exists(), "input directory does not exist");
@@ -37,7 +36,7 @@ public class RelAstTest {
   }
 
   @Test
-  void transformMinimalExample() throws CommandLine.CommandLineException, Compiler.CompilerException {
+  void transformMinimalExample() throws Compiler.CompilerException {
     transform("src/test/resources/in", "src/test/resources/out");
   }
 }