private DemangledDataType parseDataType(String fullDatatype) {

		DemangledDataType dt = createTypeInNamespace(fullDatatype);
		String datatype = dt.getDemangledName();

		if (isMemberPointerOrReference(fullDatatype, datatype)) {
			return createMemberPointer(fullDatatype);
		}

		// note: we should only encounter literals as template arguments.  Function parameters
		//       and return types should never be literals.
		if (isLiteral(fullDatatype)) {
			return createLiteral(fullDatatype);
		}

		boolean finishedName = false;
		for (int i = 0; i < datatype.length(); ++i) {
			char ch = datatype.charAt(i);

			//
			// Hack: Not sure what this is, but we have seen template parameter values that start
			// with an '&'
			//
			if (ch == '&' && i == 0) {
				continue; // for now, let the '&' through to be part of the name
			}

			if (!finishedName && isDataTypeNameCharacter(ch)) {
				continue;
			}

			if (!finishedName) {
				finishedName = true;

				if (VAR_ARGS.equals(datatype)) {
					dt.setVarArgs();
				}
				else {
					String name = datatype.substring(0, i).trim();
					dt.setName(name);
				}
			}

			if (ch == ' ') {
				continue;
			}
			if (ch == '<') {//start of template
				int contentStart = i + 1;
				int templateEnd = findTemplateEnd(datatype, i);
				if (templateEnd == -1 || templateEnd > datatype.length()) {
					throw new DemanglerParseException("Did not find ending to template");
				}

				String templateContent = datatype.substring(contentStart, templateEnd);
				DemangledTemplate template = parseTemplate(templateContent);
				dt.setTemplate(template);
				i = templateEnd;
			}
			else if (ch == '(') {// start of function pointer or array ref/pointer
				//
				// function pointer
				// 		e.g., unsigned long (*)(long const &)
				// array pointer/refs
				//  	e.g., short (&)[7]
				// lambda function
				//      e.g., {lambda(NS1::Class1 const&, int, int)#1} const&
				//            {lambda(auto:1&&)#1}<NS1::NS2>>&&
				//

				LambdaName lambdaName = getLambdaName(datatype);

				//
				// Check for array case
				//
				// remove the templates to allow us to use a simpler regex when checking for arrays
				DemangledDataType newDt = tryToParseArrayPointerOrReference(dt, datatype);
				if (newDt != null) {
					dt = newDt;
					i = datatype.length();
				}
				// lambda case, maybe an array
				else if (lambdaName != null) {

					DemangledDataType lambdaArrayDt =
						tryToParseLambdaArrayPointerOrReference(lambdaName, dt, datatype);
					if (lambdaArrayDt != null) {
						dt = lambdaArrayDt;
						i = datatype.length();
					}
					else {
						// try a non-array lambda
						String fullText = lambdaName.getFullText();
						dt.setName(fullText);
						int offset = fullText.indexOf('(');
						// to to the end of the lambda, which is its length, minus our position
						// inside the lambda
						int remaining = fullText.length() - offset;
						i = i + remaining; // end of lambda's closing '}'
						i = i - 1; // back up one space to catch optional templates on next loop pass
					}
				}
				// function pointer case
				else {
					// e.g., unsigned long (*)(long const &)
					boolean hasPointerParens = hasConsecutiveSetsOfParens(datatype.substring(i));
					if (hasPointerParens) {
						Demangled namespace = dt.getNamespace();
						DemangledFunctionPointer dfp = parseFunctionPointer(datatype);
						int paramParenEnd = datatype.lastIndexOf(')');
						if (paramParenEnd == -1) {
							throw new DemanglerParseException(
								"Did not find ending to closure: " + datatype);
						}

						dfp.getReturnType().setNamespace(namespace);
						dt = dfp;
						i = paramParenEnd + 1; // two sets of parens (normal case)
					}
					else {

						// parse as a function pointer, but display as a function
						Demangled namespace = dt.getNamespace();
						DemangledFunctionPointer dfp = parseFunction(datatype, i);
						int firstParenEnd = datatype.indexOf(')', i + 1);
						if (firstParenEnd == -1) {
							throw new DemanglerParseException(
								"Did not find ending to closure: " + datatype);
						}

						dfp.getReturnType().setNamespace(namespace);
						dt = dfp;
						i = firstParenEnd + 1;// two sets of parens (normal case)
					}
				}
			}
			else if (ch == '*') {
				dt.incrementPointerLevels();
				continue;
			}
			else if (ch == '&') {
				if (!dt.isReference()) {
					dt.setLValueReference();
				}
				else {
					dt.setRValueReference();
				}
				continue;
			}
			else if (ch == '[') {
				dt.setArray(dt.getArrayDimensions() + 1);
				i = datatype.indexOf(']', i + 1);
				continue;
			}

			String substr = datatype.substring(i);

			if (substr.startsWith("const")) {
				dt.setConst();
				i += 4;
			}
			else if (substr.startsWith("struct")) {
				dt.setStruct();
				i += 5;
			}
			else if (substr.startsWith("class")) {
				dt.setClass();
				i += 4;
			}
			else if (substr.startsWith("enum")) {
				dt.setEnum();
				i += 3;
			}
			else if (dt.getName().equals("long")) {
				if (substr.startsWith("long")) {
					dt.setName(DemangledDataType.LONG_LONG);
					i += 3;
				}
				else if (substr.startsWith("double")) {
					dt.setName(DemangledDataType.LONG_DOUBLE);
					i += 5;
				}
			}
			// unsigned can also mean unsigned long, int
			else if (dt.getName().equals("unsigned")) {
				dt.setUnsigned();
				if (substr.startsWith("long")) {
					dt.setName(DemangledDataType.LONG);
					i += 3;
				}
				else if (substr.startsWith("int")) {
					dt.setName(DemangledDataType.INT);
					i += 2;
				}
				else if (substr.startsWith("short")) {
					dt.setName(DemangledDataType.SHORT);
					i += 4;
				}
				else if (substr.startsWith("char")) {
					dt.setName(DemangledDataType.CHAR);
					i += 3;
				}
			}
		}
		return dt;
	}
This method converts each parameter string into
actual DemangledDataType objects.
Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java