comaniac commented on a change in pull request #6218:
URL: https://github.com/apache/incubator-tvm/pull/6218#discussion_r468900741



##########
File path: src/target/target.cc
##########
@@ -162,14 +313,149 @@ Target Target::Create(const String& target_str) {
   return CreateTarget(splits[0], {splits.begin() + 1, splits.end()});
 }
 
+ObjectRef TargetNode::ParseAttr(const ObjectRef& obj,
+                                const TargetKindNode::ValueTypeInfo& info) 
const {
+  if (info.type_index == 
Integer::ContainerType::_GetOrAllocRuntimeTypeIndex()) {
+    const auto* v = obj.as<IntImmNode>();
+    CHECK(v != nullptr) << "Expect type 'int', but get: " << obj->GetTypeKey();
+    return GetRef<Integer>(v);
+  }
+  if (info.type_index == String::ContainerType::_GetOrAllocRuntimeTypeIndex()) 
{
+    const auto* v = obj.as<StringObj>();
+    CHECK(v != nullptr) << "Expect type 'str', but get: " << obj->GetTypeKey();
+    return GetRef<String>(v);
+  }
+  if (info.type_index == Target::ContainerType::_GetOrAllocRuntimeTypeIndex()) 
{
+    CHECK(obj->IsInstance<MapNode>())
+        << "Expect type 'dict' to construct Target, but get: " << 
obj->GetTypeKey();
+    return Target::FromConfig(Downcast<Map<String, ObjectRef>>(obj));
+  }
+  if (info.type_index == ArrayNode::_GetOrAllocRuntimeTypeIndex()) {
+    CHECK(obj->IsInstance<ArrayNode>()) << "Expect type 'list', but get: " << 
obj->GetTypeKey();
+    Array<ObjectRef> array = Downcast<Array<ObjectRef>>(obj);
+    std::vector<ObjectRef> result;
+    int i = 0;
+    for (const ObjectRef& e : array) {
+      ++i;
+      try {
+        result.push_back(TargetNode::ParseAttr(e, *info.key));
+      } catch (const dmlc::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing element " << i << " of the 
array: " << array
+                   << ". Details:\n"
+                   << e.what();
+      }
+    }
+    return Array<ObjectRef>(result);
+  }
+  if (info.type_index == MapNode::_GetOrAllocRuntimeTypeIndex()) {
+    CHECK(obj->IsInstance<MapNode>()) << "Expect type 'dict', but get: " << 
obj->GetTypeKey();
+    std::unordered_map<ObjectRef, ObjectRef, ObjectHash, ObjectEqual> result;
+    for (const auto& kv : Downcast<Map<ObjectRef, ObjectRef>>(obj)) {
+      ObjectRef key, val;
+      try {
+        key = TargetNode::ParseAttr(kv.first, *info.key);
+      } catch (const tvm::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing a key of the dict: " << 
kv.first
+                   << ". Details:\n"
+                   << e.what();
+      }
+      try {
+        val = TargetNode::ParseAttr(kv.second, *info.val);
+      } catch (const tvm::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing a value of the dict: " << 
kv.second
+                   << ". Details:\n"
+                   << e.what();
+      }
+      result[key] = val;
+    }
+    return Map<ObjectRef, ObjectRef>(result);
+  }
+  LOG(FATAL) << "Unsupported type registered: \"" << info.type_key
+             << "\", and the type given is: " << obj->GetTypeKey();
+  throw;
+}
+
+Target Target::FromConfig(const Map<String, ObjectRef>& config_dict) {
+  const String kKind = "kind";
+  const String kTag = "tag";
+  const String kKeys = "keys";
+  std::unordered_map<std::string, ObjectRef> config(config_dict.begin(), 
config_dict.end());
+  ObjectPtr<TargetNode> target = make_object<TargetNode>();
+  // parse 'kind'
+  if (config.count(kKind)) {
+    const auto* kind = config[kKind].as<StringObj>();
+    CHECK(kind != nullptr) << "AttributeError: Expect type of field 'kind' is 
string, but get: "
+                           << config[kKind]->GetTypeKey();
+    target->kind = TargetKind::Get(GetRef<String>(kind));
+    config.erase(kKind);
+  } else {
+    LOG(FATAL) << "AttributeError: Field 'kind' is not found";
+  }
+  // parse "tag"
+  if (config.count(kTag)) {
+    const auto* tag = config[kTag].as<StringObj>();
+    CHECK(tag != nullptr) << "AttributeError: Expect type of field 'tag' is 
string, but get: "
+                          << config[kTag]->GetTypeKey();
+    target->tag = GetRef<String>(tag);
+    config.erase(kTag);
+  } else {
+    target->tag = "";
+  }
+  // parse "keys"
+  // TODO(@junrushao1994): add more keys according to CreateTarget
+  if (config.count(kKeys)) {
+    const auto* keys = config[kKeys].as<ArrayNode>();
+    CHECK(keys != nullptr) << "AttributeError: Expect type of field 'keys' is 
an Array, but get: "
+                           << config[kTag]->GetTypeKey();
+    target->keys = {};
+    for (const ObjectRef& e : *keys) {
+      const auto* key = e.as<StringObj>();
+      CHECK(key != nullptr) << "AttributeError: Expect 'keys' to be an array 
of strings, but it "
+                               "contains an element of type: "
+                            << e->GetTypeKey();
+      target->keys.push_back(GetRef<String>(key));
+    }
+    config.erase(kKeys);
+  } else {
+    target->keys = {};
+  }
+  // parse attrs
+  // TODO(@junrushao1994): add default values
+  std::unordered_map<String, ObjectRef> attrs;
+  const auto& key2vtype = target->kind->key2vtype_;
+  for (const auto& cfg_kv : config) {
+    const String& name = cfg_kv.first;
+    const ObjectRef& obj = cfg_kv.second;
+    if (!key2vtype.count(name)) {
+      std::ostringstream os;
+      os << "AttributeError: Invalid config option, cannot recognize \"" << 
name
+         << "\". Candidates are:";

Review comment:
       ```suggestion
         os << "AttributeError: Unrecognized config option: \"" << name << "\". 
Candidates are:";
   ```

##########
File path: src/target/target.cc
##########
@@ -30,20 +30,195 @@
 #include <algorithm>
 #include <stack>
 
+#include "../runtime/object_internal.h"
+
 namespace tvm {
 
 using runtime::PackedFunc;
 using runtime::TVMArgs;
 using runtime::TVMRetValue;
 
+TVM_REGISTER_NODE_TYPE(TargetNode);
+
+static inline size_t CountNumPrefixDashes(const std::string& s) {
+  size_t i = 0;
+  for (; i < s.length() && s[i] == '-'; ++i) {
+  }
+  return i;
+}
+
+static inline int FindUniqueSubstr(const std::string& str, const std::string& 
substr) {
+  size_t pos = str.find_first_of(substr);
+  if (pos == std::string::npos) {
+    return -1;
+  }
+  size_t next_pos = pos + substr.size();
+  CHECK(next_pos >= str.size() || str.find_first_of(substr, next_pos) == 
std::string::npos)
+      << "ValueError: At most one \"" << substr << "\" is allowed in "
+      << "the the given string \"" << str << "\"";
+  return pos;
+}
+
+static inline ObjectRef ParseAtomicType(uint32_t type_index, const 
std::string& str) {
+  std::istringstream is(str);
+  if (type_index == Integer::ContainerType::_GetOrAllocRuntimeTypeIndex()) {
+    int v;
+    is >> v;
+    return is.fail() ? ObjectRef(nullptr) : Integer(v);
+  } else if (type_index == 
String::ContainerType::_GetOrAllocRuntimeTypeIndex()) {
+    std::string v;
+    is >> v;
+    return is.fail() ? ObjectRef(nullptr) : String(v);
+  }
+  return ObjectRef(nullptr);
+}
+
+Map<String, ObjectRef> TargetNode::ParseAttrsFromRaw(
+    const std::vector<std::string>& options) const {
+  std::unordered_map<String, ObjectRef> attrs;
+  for (size_t iter = 0, end = options.size(); iter < end;) {
+    std::string s = options[iter++];
+    // remove the prefix dashes
+    size_t n_dashes = CountNumPrefixDashes(s);
+    CHECK(0 < n_dashes && n_dashes < s.size())
+        << "ValueError: Not an attribute key \"" << s << "\"";
+    s = s.substr(n_dashes);
+    // parse name-obj pair
+    std::string name;
+    std::string obj;
+    int pos;
+    if ((pos = FindUniqueSubstr(s, "=")) != -1) {
+      // case 1. --key=value
+      name = s.substr(0, pos);
+      obj = s.substr(pos + 1);
+      CHECK(!name.empty()) << "ValueError: Empty attribute key in \"" << 
options[iter - 1] << "\"";
+      CHECK(!obj.empty()) << "ValueError: Empty attribute in \"" << 
options[iter - 1] << "\"";
+    } else if (iter < end && options[iter][0] != '-') {
+      // case 2. --key value
+      name = s;
+      obj = options[iter++];
+    } else {
+      // case 3. --boolean-key
+      name = s;
+      obj = "1";
+    }
+    // check if `name` is invalid
+    auto it = this->kind->key2vtype_.find(name);
+    if (it == this->kind->key2vtype_.end()) {
+      std::ostringstream os;
+      os << "AttributeError: Invalid config option, cannot recognize \'" << 
name
+         << "\'. Candidates are:";
+      for (const auto& kv : this->kind->key2vtype_) {
+        os << "\n  " << kv.first;
+      }
+      LOG(FATAL) << os.str();
+    }
+    // check if `name` has been set once
+    CHECK(!attrs.count(name)) << "AttributeError: key \"" << name
+                              << "\" appears more than once in the target 
string";
+    // then `name` is valid, let's parse them
+    // only several types are supported when parsing raw string
+    const auto& info = it->second;
+    ObjectRef parsed_obj(nullptr);
+    if (info.type_index != ArrayNode::_type_index) {
+      parsed_obj = ParseAtomicType(info.type_index, obj);
+    } else {
+      Array<ObjectRef> array;
+      std::string item;
+      bool failed = false;
+      uint32_t type_index = info.key->type_index;
+      for (std::istringstream is(obj); std::getline(is, item, ',');) {
+        ObjectRef parsed_obj = ParseAtomicType(type_index, item);
+        if (parsed_obj.defined()) {
+          array.push_back(parsed_obj);
+        } else {
+          failed = true;
+          break;
+        }
+      }
+      if (!failed) {
+        parsed_obj = std::move(array);
+      }
+    }
+    if (!parsed_obj.defined()) {
+      LOG(FATAL) << "ValueError: Cannot parse type \"" << info.type_key << "\""
+                 << ", where attribute key is \"" << name << "\""
+                 << ", and attribute is \"" << obj << "\"";
+    }
+    attrs[name] = std::move(parsed_obj);
+  }
+  // set default attribute values if they do not exist
+  for (const auto& kv : this->kind->key2default_) {
+    if (!attrs.count(kv.first)) {
+      attrs[kv.first] = kv.second;
+    }
+  }
+  return attrs;
+}
+
+static inline Optional<String> StringifyAtomicType(const ObjectRef& obj) {
+  if (const auto* p = obj.as<IntImmNode>()) {
+    return String(std::to_string(p->value));
+  }
+  if (const auto* p = obj.as<StringObj>()) {
+    return GetRef<String>(p);
+  }
+  return NullOpt;
+}
+
+static inline Optional<String> JoinString(const std::vector<String>& array, 
char separator) {
+  if (array.empty()) {
+    return NullOpt;
+  }
+  std::ostringstream os;
+  os << array[0];
+  for (size_t i = 1; i < array.size(); ++i) {
+    os << separator << array[i];
+  }
+  return String(os.str());
+}
+
+Optional<String> TargetNode::StringifyAttrsToRaw(const Map<String, ObjectRef>& 
attrs) const {
+  std::ostringstream os;
+  std::vector<String> keys;
+  for (const auto& kv : attrs) {
+    keys.push_back(kv.first);
+  }
+  std::sort(keys.begin(), keys.end());
+  std::vector<String> result;
+  for (const auto& key : keys) {
+    const ObjectRef& obj = attrs[key];
+    Optional<String> value = NullOpt;
+    if (const auto* array = obj.as<ArrayNode>()) {
+      std::vector<String> items;
+      for (const ObjectRef& item : *array) {

Review comment:
       Per discussion in the RFC, should we sort the values?

##########
File path: src/target/target.cc
##########
@@ -162,14 +313,149 @@ Target Target::Create(const String& target_str) {
   return CreateTarget(splits[0], {splits.begin() + 1, splits.end()});
 }
 
+ObjectRef TargetNode::ParseAttr(const ObjectRef& obj,
+                                const TargetKindNode::ValueTypeInfo& info) 
const {
+  if (info.type_index == 
Integer::ContainerType::_GetOrAllocRuntimeTypeIndex()) {
+    const auto* v = obj.as<IntImmNode>();
+    CHECK(v != nullptr) << "Expect type 'int', but get: " << obj->GetTypeKey();
+    return GetRef<Integer>(v);
+  }
+  if (info.type_index == String::ContainerType::_GetOrAllocRuntimeTypeIndex()) 
{
+    const auto* v = obj.as<StringObj>();
+    CHECK(v != nullptr) << "Expect type 'str', but get: " << obj->GetTypeKey();
+    return GetRef<String>(v);
+  }
+  if (info.type_index == Target::ContainerType::_GetOrAllocRuntimeTypeIndex()) 
{
+    CHECK(obj->IsInstance<MapNode>())
+        << "Expect type 'dict' to construct Target, but get: " << 
obj->GetTypeKey();
+    return Target::FromConfig(Downcast<Map<String, ObjectRef>>(obj));
+  }
+  if (info.type_index == ArrayNode::_GetOrAllocRuntimeTypeIndex()) {
+    CHECK(obj->IsInstance<ArrayNode>()) << "Expect type 'list', but get: " << 
obj->GetTypeKey();
+    Array<ObjectRef> array = Downcast<Array<ObjectRef>>(obj);
+    std::vector<ObjectRef> result;
+    int i = 0;
+    for (const ObjectRef& e : array) {
+      ++i;
+      try {
+        result.push_back(TargetNode::ParseAttr(e, *info.key));
+      } catch (const dmlc::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing element " << i << " of the 
array: " << array
+                   << ". Details:\n"
+                   << e.what();
+      }
+    }
+    return Array<ObjectRef>(result);
+  }
+  if (info.type_index == MapNode::_GetOrAllocRuntimeTypeIndex()) {
+    CHECK(obj->IsInstance<MapNode>()) << "Expect type 'dict', but get: " << 
obj->GetTypeKey();
+    std::unordered_map<ObjectRef, ObjectRef, ObjectHash, ObjectEqual> result;
+    for (const auto& kv : Downcast<Map<ObjectRef, ObjectRef>>(obj)) {
+      ObjectRef key, val;
+      try {
+        key = TargetNode::ParseAttr(kv.first, *info.key);
+      } catch (const tvm::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing a key of the dict: " << 
kv.first
+                   << ". Details:\n"
+                   << e.what();
+      }
+      try {
+        val = TargetNode::ParseAttr(kv.second, *info.val);
+      } catch (const tvm::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing a value of the dict: " << 
kv.second
+                   << ". Details:\n"
+                   << e.what();
+      }
+      result[key] = val;
+    }
+    return Map<ObjectRef, ObjectRef>(result);
+  }
+  LOG(FATAL) << "Unsupported type registered: \"" << info.type_key
+             << "\", and the type given is: " << obj->GetTypeKey();
+  throw;
+}
+
+Target Target::FromConfig(const Map<String, ObjectRef>& config_dict) {
+  const String kKind = "kind";
+  const String kTag = "tag";
+  const String kKeys = "keys";
+  std::unordered_map<std::string, ObjectRef> config(config_dict.begin(), 
config_dict.end());
+  ObjectPtr<TargetNode> target = make_object<TargetNode>();
+  // parse 'kind'
+  if (config.count(kKind)) {
+    const auto* kind = config[kKind].as<StringObj>();
+    CHECK(kind != nullptr) << "AttributeError: Expect type of field 'kind' is 
string, but get: "
+                           << config[kKind]->GetTypeKey();
+    target->kind = TargetKind::Get(GetRef<String>(kind));
+    config.erase(kKind);
+  } else {
+    LOG(FATAL) << "AttributeError: Field 'kind' is not found";
+  }
+  // parse "tag"
+  if (config.count(kTag)) {
+    const auto* tag = config[kTag].as<StringObj>();
+    CHECK(tag != nullptr) << "AttributeError: Expect type of field 'tag' is 
string, but get: "
+                          << config[kTag]->GetTypeKey();
+    target->tag = GetRef<String>(tag);
+    config.erase(kTag);
+  } else {
+    target->tag = "";
+  }
+  // parse "keys"
+  // TODO(@junrushao1994): add more keys according to CreateTarget

Review comment:
       Didn't understand this comment. Could you elaborate?

##########
File path: src/target/target.cc
##########
@@ -30,20 +30,195 @@
 #include <algorithm>
 #include <stack>
 
+#include "../runtime/object_internal.h"
+
 namespace tvm {
 
 using runtime::PackedFunc;
 using runtime::TVMArgs;
 using runtime::TVMRetValue;
 
+TVM_REGISTER_NODE_TYPE(TargetNode);
+
+static inline size_t CountNumPrefixDashes(const std::string& s) {

Review comment:
       It should be fine to just `RemovePrefixDashes` and return a substring, 
because this utility is used by only one place.

##########
File path: src/target/target.cc
##########
@@ -162,14 +313,149 @@ Target Target::Create(const String& target_str) {
   return CreateTarget(splits[0], {splits.begin() + 1, splits.end()});
 }
 
+ObjectRef TargetNode::ParseAttr(const ObjectRef& obj,
+                                const TargetKindNode::ValueTypeInfo& info) 
const {
+  if (info.type_index == 
Integer::ContainerType::_GetOrAllocRuntimeTypeIndex()) {
+    const auto* v = obj.as<IntImmNode>();
+    CHECK(v != nullptr) << "Expect type 'int', but get: " << obj->GetTypeKey();
+    return GetRef<Integer>(v);
+  }
+  if (info.type_index == String::ContainerType::_GetOrAllocRuntimeTypeIndex()) 
{
+    const auto* v = obj.as<StringObj>();
+    CHECK(v != nullptr) << "Expect type 'str', but get: " << obj->GetTypeKey();
+    return GetRef<String>(v);
+  }
+  if (info.type_index == Target::ContainerType::_GetOrAllocRuntimeTypeIndex()) 
{
+    CHECK(obj->IsInstance<MapNode>())
+        << "Expect type 'dict' to construct Target, but get: " << 
obj->GetTypeKey();
+    return Target::FromConfig(Downcast<Map<String, ObjectRef>>(obj));
+  }
+  if (info.type_index == ArrayNode::_GetOrAllocRuntimeTypeIndex()) {
+    CHECK(obj->IsInstance<ArrayNode>()) << "Expect type 'list', but get: " << 
obj->GetTypeKey();
+    Array<ObjectRef> array = Downcast<Array<ObjectRef>>(obj);
+    std::vector<ObjectRef> result;
+    int i = 0;
+    for (const ObjectRef& e : array) {
+      ++i;
+      try {
+        result.push_back(TargetNode::ParseAttr(e, *info.key));
+      } catch (const dmlc::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing element " << i << " of the 
array: " << array
+                   << ". Details:\n"
+                   << e.what();
+      }
+    }
+    return Array<ObjectRef>(result);
+  }
+  if (info.type_index == MapNode::_GetOrAllocRuntimeTypeIndex()) {
+    CHECK(obj->IsInstance<MapNode>()) << "Expect type 'dict', but get: " << 
obj->GetTypeKey();
+    std::unordered_map<ObjectRef, ObjectRef, ObjectHash, ObjectEqual> result;
+    for (const auto& kv : Downcast<Map<ObjectRef, ObjectRef>>(obj)) {
+      ObjectRef key, val;
+      try {
+        key = TargetNode::ParseAttr(kv.first, *info.key);
+      } catch (const tvm::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing a key of the dict: " << 
kv.first
+                   << ". Details:\n"
+                   << e.what();
+      }
+      try {
+        val = TargetNode::ParseAttr(kv.second, *info.val);
+      } catch (const tvm::Error& e) {
+        LOG(FATAL) << "Error occurred when parsing a value of the dict: " << 
kv.second
+                   << ". Details:\n"
+                   << e.what();
+      }
+      result[key] = val;
+    }
+    return Map<ObjectRef, ObjectRef>(result);
+  }
+  LOG(FATAL) << "Unsupported type registered: \"" << info.type_key
+             << "\", and the type given is: " << obj->GetTypeKey();
+  throw;
+}
+
+Target Target::FromConfig(const Map<String, ObjectRef>& config_dict) {
+  const String kKind = "kind";
+  const String kTag = "tag";
+  const String kKeys = "keys";
+  std::unordered_map<std::string, ObjectRef> config(config_dict.begin(), 
config_dict.end());
+  ObjectPtr<TargetNode> target = make_object<TargetNode>();
+  // parse 'kind'
+  if (config.count(kKind)) {
+    const auto* kind = config[kKind].as<StringObj>();
+    CHECK(kind != nullptr) << "AttributeError: Expect type of field 'kind' is 
string, but get: "
+                           << config[kKind]->GetTypeKey();

Review comment:
       Can this check be a helper function, as it appears 4 times in this 
function?

##########
File path: src/target/target.cc
##########
@@ -30,20 +30,195 @@
 #include <algorithm>
 #include <stack>
 
+#include "../runtime/object_internal.h"
+
 namespace tvm {
 
 using runtime::PackedFunc;
 using runtime::TVMArgs;
 using runtime::TVMRetValue;
 
+TVM_REGISTER_NODE_TYPE(TargetNode);
+
+static inline size_t CountNumPrefixDashes(const std::string& s) {
+  size_t i = 0;
+  for (; i < s.length() && s[i] == '-'; ++i) {
+  }
+  return i;
+}
+
+static inline int FindUniqueSubstr(const std::string& str, const std::string& 
substr) {
+  size_t pos = str.find_first_of(substr);
+  if (pos == std::string::npos) {
+    return -1;
+  }
+  size_t next_pos = pos + substr.size();
+  CHECK(next_pos >= str.size() || str.find_first_of(substr, next_pos) == 
std::string::npos)
+      << "ValueError: At most one \"" << substr << "\" is allowed in "
+      << "the the given string \"" << str << "\"";
+  return pos;
+}
+
+static inline ObjectRef ParseAtomicType(uint32_t type_index, const 
std::string& str) {
+  std::istringstream is(str);
+  if (type_index == Integer::ContainerType::_GetOrAllocRuntimeTypeIndex()) {
+    int v;
+    is >> v;
+    return is.fail() ? ObjectRef(nullptr) : Integer(v);
+  } else if (type_index == 
String::ContainerType::_GetOrAllocRuntimeTypeIndex()) {
+    std::string v;
+    is >> v;
+    return is.fail() ? ObjectRef(nullptr) : String(v);
+  }
+  return ObjectRef(nullptr);
+}
+
+Map<String, ObjectRef> TargetNode::ParseAttrsFromRaw(
+    const std::vector<std::string>& options) const {
+  std::unordered_map<String, ObjectRef> attrs;
+  for (size_t iter = 0, end = options.size(); iter < end;) {
+    std::string s = options[iter++];
+    // remove the prefix dashes
+    size_t n_dashes = CountNumPrefixDashes(s);
+    CHECK(0 < n_dashes && n_dashes < s.size())
+        << "ValueError: Not an attribute key \"" << s << "\"";
+    s = s.substr(n_dashes);
+    // parse name-obj pair
+    std::string name;
+    std::string obj;
+    int pos;
+    if ((pos = FindUniqueSubstr(s, "=")) != -1) {
+      // case 1. --key=value
+      name = s.substr(0, pos);
+      obj = s.substr(pos + 1);
+      CHECK(!name.empty()) << "ValueError: Empty attribute key in \"" << 
options[iter - 1] << "\"";
+      CHECK(!obj.empty()) << "ValueError: Empty attribute in \"" << 
options[iter - 1] << "\"";
+    } else if (iter < end && options[iter][0] != '-') {
+      // case 2. --key value
+      name = s;
+      obj = options[iter++];
+    } else {
+      // case 3. --boolean-key
+      name = s;
+      obj = "1";
+    }
+    // check if `name` is invalid
+    auto it = this->kind->key2vtype_.find(name);
+    if (it == this->kind->key2vtype_.end()) {
+      std::ostringstream os;
+      os << "AttributeError: Invalid config option, cannot recognize \'" << 
name
+         << "\'. Candidates are:";
+      for (const auto& kv : this->kind->key2vtype_) {
+        os << "\n  " << kv.first;
+      }
+      LOG(FATAL) << os.str();
+    }
+    // check if `name` has been set once
+    CHECK(!attrs.count(name)) << "AttributeError: key \"" << name
+                              << "\" appears more than once in the target 
string";
+    // then `name` is valid, let's parse them
+    // only several types are supported when parsing raw string
+    const auto& info = it->second;
+    ObjectRef parsed_obj(nullptr);
+    if (info.type_index != ArrayNode::_type_index) {
+      parsed_obj = ParseAtomicType(info.type_index, obj);
+    } else {
+      Array<ObjectRef> array;
+      std::string item;
+      bool failed = false;
+      uint32_t type_index = info.key->type_index;
+      for (std::istringstream is(obj); std::getline(is, item, ',');) {
+        ObjectRef parsed_obj = ParseAtomicType(type_index, item);
+        if (parsed_obj.defined()) {
+          array.push_back(parsed_obj);
+        } else {
+          failed = true;
+          break;
+        }
+      }
+      if (!failed) {
+        parsed_obj = std::move(array);
+      }
+    }
+    if (!parsed_obj.defined()) {
+      LOG(FATAL) << "ValueError: Cannot parse type \"" << info.type_key << "\""
+                 << ", where attribute key is \"" << name << "\""
+                 << ", and attribute is \"" << obj << "\"";
+    }
+    attrs[name] = std::move(parsed_obj);

Review comment:
       It seems to me that you can reuse at least of a part of 
`TargetNode::ParseAttr` implementation here? But it's fine if we are going to 
deprecate string serialization.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to