private String[] parseParams() {
      List<String> params = new LinkedList<String>();

      Token t = peek();
      if (t != null && t.text.equals("(")) {
        expect("(");

        // checking for null in the for-loop condition prevents null-ptr exception
        // and allows us to fail more gracefully with a parsing error.
        for(t = peek(); (t == null) || !t.text.equals(")"); t = expect(",",")")) {
          params.add(expect("name").text);
        }
        if (params.size() == 0) {
          throw new IllegalArgumentException(
              "type parameters expected for type string " + typeInfoString);
        }
      }

      return params.toArray(new String[params.size()]);
    }
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