[Commands] Tidy up Ackermann example implementation.

 - This introduces a wrapper Key and Value type to make the core algorithm more
   clear.
diff --git a/lib/Commands/BuildEngineCommand.cpp b/lib/Commands/BuildEngineCommand.cpp
index 481ab66..8f9ad1a 100644
--- a/lib/Commands/BuildEngineCommand.cpp
+++ b/lib/Commands/BuildEngineCommand.cpp
@@ -77,28 +77,79 @@
   return result;
 }
 
+/// Key representation used in Ackermann build.
+struct AckermannKey {
+  /// The Ackermann number this key represents.
+  int m, n;
+
+  /// Create a key representing the given Ackermann number.
+  AckermannKey(int m, int n) : m(m), n(n) {}
+
+  /// Create an Ackermann key from the encoded representation.
+  AckermannKey(const core::KeyType& key) {
+      auto keyString = StringRef(key);
+      assert(keyString.startswith("ack(") && keyString.endswith(")"));
+      auto arguments = keyString.split("(").second.split(")").first.split(",");
+      m = 0;
+      n = 0;
+      (void)arguments.first.getAsInteger(10, m);
+      (void)arguments.second.getAsInteger(10, n);
+      assert(m >= 0 && m < 4);
+      assert(n >= 0);
+  }
+
+  /// Convert an Ackermann key to its encoded representation.
+  operator core::KeyType() const {
+    char inputKey[32];
+    sprintf(inputKey, "ack(%d,%d)", m, n);
+    return inputKey;
+  }
+};
+
+/// Value representation used in Ackermann build.
+struct AckermannValue {
+  int value;
+
+  /// Create a value for 0.
+  AckermannValue() : value(0) { }
+
+  /// Create a value from an integer.
+  AckermannValue(int value) : value(value) { }
+
+  /// Create a value from the encoded representation.
+  AckermannValue(const core::ValueType& value) : value(intFromValue(value)) { }
+
+  /// Convert a value to its encoded representation.
+  operator core::ValueType() const {
+    return intToValue(value);
+  }
+
+  /// Convert a wrapped value to its actual value.
+  operator int() const {
+    return value;
+  }
+};
+    
 static core::Task* buildAck(core::BuildEngine& engine, int m, int n) {
   struct AckermannTask : core::Task {
     int m, n;
-    int recursiveResultA = 0;
-    int recursiveResultB = 0;
+    AckermannValue recursiveResultA = {};
+    AckermannValue recursiveResultB = {};
 
     AckermannTask(int m, int n) : m(m), n(n) { }
 
     virtual void provideValue(core::BuildEngine& engine, uintptr_t inputID,
                               const core::ValueType& value) override {
       if (inputID == 0) {
-        recursiveResultA = intFromValue(value);
+        recursiveResultA = value;
 
         // Request the second recursive result, if needed.
         if (m != 0 && n != 0) {
-          char inputKey[32];
-          sprintf(inputKey, "ack(%d,%d)", m - 1, recursiveResultA);
-          engine.taskNeedsInput(this, inputKey, 1);
+          engine.taskNeedsInput(this, AckermannKey(m-1, recursiveResultA), 1);
         }
       } else {
         assert(inputID == 1 && "invalid input ID");
-        recursiveResultB = intFromValue(value);
+        recursiveResultB = value;
       }
     }
 
@@ -107,30 +158,26 @@
       if (m == 0) {
         ;
       } else if (n == 0) {
-        char inputKey[32];
-        sprintf(inputKey, "ack(%d,%d)", m-1, 1);
-        engine.taskNeedsInput(this, inputKey, 0);
+        engine.taskNeedsInput(this, AckermannKey(m-1, 1), 0);
       } else {
-        char inputKey[32];
-        sprintf(inputKey, "ack(%d,%d)", m, n-1);
-        engine.taskNeedsInput(this, inputKey, 0);
+        engine.taskNeedsInput(this, AckermannKey(m, n-1), 0);
       }
     }
 
     virtual void inputsAvailable(core::BuildEngine& engine) override {
       if (m == 0) {
-        engine.taskIsComplete(this, intToValue(n + 1));
+        engine.taskIsComplete(this, AckermannValue(n + 1));
         return;
       }
 
       assert(recursiveResultA != 0);
       if (n == 0) {
-        engine.taskIsComplete(this, intToValue(recursiveResultA));
+        engine.taskIsComplete(this, AckermannValue(recursiveResultA));
         return;
       }
 
       assert(recursiveResultB != 0);
-      engine.taskIsComplete(this, intToValue(recursiveResultB));
+      engine.taskIsComplete(this, AckermannValue(recursiveResultB));
     }
   };
 
@@ -151,24 +198,11 @@
   public:
     int numRules = 0;
 
-    virtual core::Rule lookupRule(const core::KeyType& key) override {
-      // Extract the Ackermann parameters.
-      //
-      // FIXME: Need generalized key type.
-      assert(key[0] == 'a' && key[1] == 'c' && key[2] == 'k' &&
-             key[3] == '(');
-      const char* mStart = &key[4];
-      const char* mEnd = strchr(mStart, ',');
-      assert(mEnd != nullptr && mEnd[0] == ',');
-      const char* nStart = &mEnd[1];
-      int m = strtol(mStart, nullptr, 10);
-      int n = strtol(nStart, nullptr, 10);
-      assert(m >= 0 && m < 4);
-      assert(n >= 0);
-
+    virtual core::Rule lookupRule(const core::KeyType& keyData) override {
       ++numRules;
-      return core::Rule{key, [m, n] (core::BuildEngine& engine) {
-          return buildAck(engine, m, n); } };
+      auto key = AckermannKey(keyData);
+      return core::Rule{key, [key] (core::BuildEngine& engine) {
+          return buildAck(engine, key.m, key.n); } };
     }
 
     virtual void cycleDetected(const std::vector<core::Rule*>& items) override {
@@ -188,9 +222,8 @@
     }
   }
 
-  char key[32];
-  sprintf(key, "ack(%d,%d)", m, n);
-  int result = intFromValue(engine.build(key));
+  auto key = AckermannKey(m, n);
+  auto result = AckermannValue(engine.build(key));
   std::cout << "ack(" << m << ", " << n << ") = " << result << "\n";
   if (n < 10) {
 #ifndef NDEBUG
@@ -206,7 +239,7 @@
 
   // Recompute the result as many times as requested.
   for (int i = 0; i != recomputeCount; ++i) {
-    int recomputedResult = intFromValue(engine.build(key));
+    auto recomputedResult = AckermannValue(engine.build(key));
     if (recomputedResult != result)
       abort();
   }