@Override
	protected void parseInternal() throws MDException {
		boolean argsDone = false;
		boolean needsComma = false; // For "delimiters" MSFT bug
		while (!argsDone) {
			char code = dmang.peek();
			MDDataType dt;
			switch (code) {
				case '@':
					dmang.increment();
					argsDone = true;
					break;
				case MDMang.DONE:
					argsDone = true;
					break;
				case 'X':
					dmang.increment();
					dt = new MDVoidDataType(dmang);
					dt.parse();
					commaDelimiter.add(needsComma);
					needsComma = true;
					args.add(dt);
					// Unlike an argument of a function, when "void" is first argument of a
					//  template, it does not terminate the arguments list.
					// Do not put into backreference parameter list.
					break;
				case 'Z':
					throw new MDException("Varargs not allowed as template parameter");
				case '?': {
					dmang.increment();
					// TODO: make this the new MDTemplateParameter
					MDEncodedNumber x = new MDEncodedNumber(dmang);
					x.parse();
					MDDataType datatype = new MDDataType(dmang);
					datatype.setTypeName("`template-parameter-" + x + "'");
					dt = datatype;
					commaDelimiter.add(needsComma);
					needsComma = true;
					args.add(dt);
					dmang.addBackrefTemplateParameterMDDataType(dt);
				}
					break;
				case '$':
					// "$$$V" and "$$V" (latter is MSVC15 version):  case of ignore as argument.
					if ((dmang.peek(1) == '$') && (dmang.peek(2) == '$') &&
						(dmang.peek(3) == 'V')) {
						dmang.increment();
						dmang.increment();
						dmang.increment();
						dmang.increment();
						if (args.isEmpty()) {
							// MDMANG SPECIALIZATION USED.
							// For "delimiters" MSFT bug: setting true even though we are
							//  skipping parameter.
							needsComma = dmang.emptyFirstArgComma(this);
						}
						continue;
					}
					if ((dmang.peek(1) == '$') && (dmang.peek(2) == 'V')) {
						dmang.increment();
						dmang.increment();
						dmang.increment();
						if (args.isEmpty()) {
							// MDMANG SPECIALIZATION USED.
							// For "delimiters" MSFT bug: setting true even though we are
							//  skipping parameter.
							needsComma = dmang.emptyFirstArgComma(this);
						}
						continue;
					}
					if (dmang.peek(1) == '$') { // This is the same as the "default" case below
						dt = MDDataTypeParser.parsePrimaryDataType(dmang, true);
						dt.parse();
						if (dt instanceof MDEmptyParameterType ||
							dt instanceof MDEndParameterType) {
							// These two do not get added to the arguments list and they don't
							//  count as or change the indices of backreferences.
							break;
						}
						commaDelimiter.add(needsComma);
						needsComma = true;
						args.add(dt);
						// Only MDModifierType hits here (because $$ will only be that type
						// (decision AT THE MOMENT is that they extend MDModifierType)"
						// We are keeping the others here for now as this $$ case is the same
						// as the "default" case below.
						if ((dt instanceof MDModifierType) || (dt instanceof MDExtendedType) ||
							(dt instanceof MDComplexType)) {
							dmang.addBackrefTemplateParameterMDDataType(dt);
						}
					}
					else {
						MDTemplateConstant tp = new MDTemplateConstant(dmang);
						tp.parse();
						StringBuilder tmpBuilder = new StringBuilder();
						tp.insert(tmpBuilder);
						String tpName = tmpBuilder.toString();
						if (!tpName.isEmpty()) {
							// MSFT does not put it on the arguments list or create a
							// backreference for it, BUT... MSFT will note that it needs a
							// comma in the arguments list, which is WRONG.
							MDDataType datatype = new MDDataType(dmang);
							datatype.setTypeName(tpName);
							dt = datatype;
							commaDelimiter.add(needsComma);
							needsComma = true;
							args.add(dt);
							dmang.addBackrefTemplateParameterMDDataType(dt);
						}
						else if (args.isEmpty()) { // See note above.
							// To overcome MSFT comma bug
							// See//TODO: for MSFT output (when we do it), need to note the need
							//  need for a comma before our "real" first parameter.
							// MDMANG SPECIALIZATION USED.
							// For "delimiters" MSFT bug: setting true even though we are
							// skipping parameter.
							needsComma = dmang.emptyFirstArgComma(this);
						}
					}
					break;
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					dmang.increment();
					int index = code - '0';
					dt = dmang.getBackreferenceTemplateParameterMDDataType(index);
					// MDMANG SPECIALIZATION USED.
					commaDelimiter.add(dmang.templateBackrefComma(this));
					// For "delimiters" MSFT bug: setting false here for backreference.
					needsComma = true;
					args.add(dt);
					break;
				default:
					dt = MDDataTypeParser.parsePrimaryDataType(dmang, true);
					dt.parse();
					commaDelimiter.add(needsComma);
					needsComma = true;
					args.add(dt);
					if ((dt instanceof MDModifierType) || (dt instanceof MDExtendedType) ||
						(dt instanceof MDComplexType)) {
						dmang.addBackrefTemplateParameterMDDataType(dt);
					}
					break;
			}
		}
	}
This class represents the template arguments list portion of a
Microsoft mangled symbol.
Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/template/MDTemplateArgumentsList.java