public ArrayList<TypeInfo> parseTypeInfos() {
      typeInfos = new ArrayList<TypeInfo>();
      iToken = 0;
      while (iToken < typeInfoTokens.size()) {
        typeInfos.add(parseType());
        if (iToken < typeInfoTokens.size()) {
          Token separator = typeInfoTokens.get(iToken);
          if (",".equals(separator.text) || ";".equals(separator.text)
              || ":".equals(separator.text)) {
            iToken++;
          } else {
            throw new IllegalArgumentException(
                "Error: ',', ':', or ';' expected at position "
                + separator.position + " from '" + typeInfoString + "' "
                + typeInfoTokens);
          }
        }
      }
      return typeInfos;
    }
Tokenize the typeInfoString. The rule is simple: all consecutive
alphadigits and '_', '.' are in one token, and all other characters are
one character per token.

tokenize("map<int,string>") should return
["map","<","int",",","string",">"]

Note that we add '$' in new Calcite return path. As '$' will not appear
in any type in Hive, it is safe to do so.
serde/src/java/org/apache/hadoop/hive/serde2/typeinfo/TypeInfoUtils.java