func (st *state) demangleType(isCast bool) AST {
	if len(st.str) == 0 {
		st.fail("expected type")
	}

	addSubst := true

	q := st.cvQualifiers()
	if len(q) > 0 {
		if len(st.str) == 0 {
			st.fail("expected type")
		}

		// CV-qualifiers before a function type apply to
		// 'this', so avoid adding the unqualified function
		// type to the substitution list.
		if st.str[0] == 'F' {
			addSubst = false
		}
	}

	var ret AST

	// Use correct substitution for a template parameter.
	var sub AST

	if btype, ok := builtinTypes[st.str[0]]; ok {
		ret = &BuiltinType{Name: btype}
		st.advance(1)
		if len(q) > 0 {
			ret = &TypeWithQualifiers{Base: ret, Qualifiers: q}
			st.subs.add(ret)
		}
		return ret
	}
	c := st.str[0]
	switch c {
	case 'u':
		st.advance(1)
		ret = st.sourceName()
	case 'F':
		ret = st.functionType()
	case 'N', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		ret = st.name()
	case 'A':
		ret = st.arrayType(isCast)
	case 'M':
		ret = st.pointerToMemberType(isCast)
	case 'T':
		ret = st.templateParam()
		if len(st.str) > 0 && st.str[0] == 'I' {
			// See the function comment to explain this.
			if !isCast {
				st.subs.add(ret)
				args := st.templateArgs()
				ret = &Template{Name: ret, Args: args}
			} else {
				ret = st.demangleCastTemplateArgs(ret, true)
			}
		}
	case 'S':
		// If this is a special substitution, then it
		// is the start of <class-enum-type>.
		var c2 byte
		if len(st.str) > 1 {
			c2 = st.str[1]
		}
		if isDigit(c2) || c2 == '_' || isUpper(c2) {
			ret = st.substitution(false)
			if len(st.str) == 0 || st.str[0] != 'I' {
				addSubst = false
			} else {
				// See the function comment to explain this.
				if _, ok := ret.(*TemplateParam); !ok || !isCast {
					args := st.templateArgs()
					ret = &Template{Name: ret, Args: args}
				} else {
					next := st.demangleCastTemplateArgs(ret, false)
					if next == ret {
						addSubst = false
					}
					ret = next
				}
			}
		} else {
			ret = st.name()
			// This substitution is not itself a
			// substitution candidate, unless template
			// arguments were added.
			if ret == subAST[c2] || ret == verboseAST[c2] {
				addSubst = false
			}
		}
	case 'O', 'P', 'R', 'C', 'G':
		st.advance(1)
		t := st.demangleType(isCast)
		switch c {
		case 'O':
			ret = &RvalueReferenceType{Base: t}
		case 'P':
			ret = &PointerType{Base: t}
		case 'R':
			ret = &ReferenceType{Base: t}
		case 'C':
			ret = &ComplexType{Base: t}
		case 'G':
			ret = &ImaginaryType{Base: t}
		}
	case 'U':
		if len(st.str) < 2 {
			st.fail("expected source name or unnamed type")
		}
		switch st.str[1] {
		case 'l':
			ret = st.closureTypeName()
			addSubst = false
		case 't':
			ret = st.unnamedTypeName()
			addSubst = false
		default:
			st.advance(1)
			n := st.sourceName()
			if len(st.str) > 0 && st.str[0] == 'I' {
				args := st.templateArgs()
				n = &Template{Name: n, Args: args}
			}
			t := st.demangleType(isCast)
			ret = &VendorQualifier{Qualifier: n, Type: t}
		}
	case 'D':
		st.advance(1)
		if len(st.str) == 0 {
			st.fail("expected D code for type")
		}
		addSubst = false
		c2 := st.str[0]
		st.advance(1)
		switch c2 {
		case 'T', 't':
			// decltype(expression)
			ret = st.expression()
			if len(st.str) == 0 || st.str[0] != 'E' {
				st.fail("expected E after expression in type")
			}
			st.advance(1)
			ret = &Decltype{Expr: ret}
			addSubst = true

		case 'p':
			t := st.demangleType(isCast)
			pack := st.findArgumentPack(t)
			ret = &PackExpansion{Base: t, Pack: pack}
			addSubst = true

		case 'a':
			ret = &Name{Name: "auto"}

		case 'f':
			ret = &BuiltinType{Name: "decimal32"}
		case 'd':
			ret = &BuiltinType{Name: "decimal64"}
		case 'e':
			ret = &BuiltinType{Name: "decimal128"}
		case 'h':
			ret = &BuiltinType{Name: "half"}
		case 's':
			ret = &BuiltinType{Name: "char16_t"}
		case 'i':
			ret = &BuiltinType{Name: "char32_t"}
		case 'n':
			ret = &BuiltinType{Name: "decltype(nullptr)"}

		case 'F':
			accum := false
			if len(st.str) > 0 && isDigit(st.str[0]) {
				accum = true
				// We don't care about the bits.
				_ = st.number()
			}
			base := st.demangleType(isCast)
			if len(st.str) > 0 && isDigit(st.str[0]) {
				// We don't care about the bits.
				st.number()
			}
			sat := false
			if len(st.str) > 0 {
				if st.str[0] == 's' {
					sat = true
				}
				st.advance(1)
			}
			ret = &FixedType{Base: base, Accum: accum, Sat: sat}

		case 'v':
			ret = st.vectorType(isCast)
			addSubst = true

		default:
			st.fail("unrecognized D code in type")
		}

	default:
		st.fail("unrecognized type code")
	}

	if addSubst {
		if sub != nil {
			st.subs.add(sub)
		} else {
			st.subs.add(ret)
		}
	}

	if len(q) > 0 {
		if _, ok := ret.(*FunctionType); ok {
			ret = &MethodWithQualifiers{Method: ret, Qualifiers: q, RefQualifier: ""}
		} else if mwq, ok := ret.(*MethodWithQualifiers); ok {
			// Merge adjacent qualifiers.  This case
			// happens with a function with a trailing
			// ref-qualifier.
			mwq.Qualifiers = mergeQualifiers(q, mwq.Qualifiers)
		} else {
			// Merge adjacent qualifiers.  This case
			// happens with multi-dimensional array types.
			if qsub, ok := ret.(*TypeWithQualifiers); ok {
				q = mergeQualifiers(q, qsub.Qualifiers)
				ret = qsub.Base
			}
			ret = &TypeWithQualifiers{Base: ret, Qualifiers: q}
		}
		st.subs.add(ret)
	}

	return ret
}
func (st *state) demangleType(isCast bool) AST {
	if len(st.str) == 0 {
		st.fail("expected type")
	}

	addSubst := true

	q := st.cvQualifiers()
	if len(q) > 0 {
		if len(st.str) == 0 {
			st.fail("expected type")
		}

		
		
		
		if st.str[0] == 'F' {
			addSubst = false
		}
	}

	var ret AST

	
	var sub AST

	if btype, ok := builtinTypes[st.str[0]]; ok {
		ret = &BuiltinType{Name: btype}
		st.advance(1)
		if len(q) > 0 {
			ret = &TypeWithQualifiers{Base: ret, Qualifiers: q}
			st.subs.add(ret)
		}
		return ret
	}
	c := st.str[0]
	switch c {
	case 'u':
		st.advance(1)
		ret = st.sourceName()
	case 'F':
		ret = st.functionType()
	case 'N', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		ret = st.name()
	case 'A':
		ret = st.arrayType(isCast)
	case 'M':
		ret = st.pointerToMemberType(isCast)
	case 'T':
		ret = st.templateParam()
		if len(st.str) > 0 && st.str[0] == 'I' {
			
			if !isCast {
				st.subs.add(ret)
				args := st.templateArgs()
				ret = &Template{Name: ret, Args: args}
			} else {
				ret = st.demangleCastTemplateArgs(ret, true)
			}
		}
	case 'S':
		
		
		var c2 byte
		if len(st.str) > 1 {
			c2 = st.str[1]
		}
		if isDigit(c2) || c2 == '_' || isUpper(c2) {
			ret = st.substitution(false)
			if len(st.str) == 0 || st.str[0] != 'I' {
				addSubst = false
			} else {
				
				if _, ok := ret.(*TemplateParam); !ok || !isCast {
					args := st.templateArgs()
					ret = &Template{Name: ret, Args: args}
				} else {
					next := st.demangleCastTemplateArgs(ret, false)
					if next == ret {
						addSubst = false
					}
					ret = next
				}
			}
		} else {
			ret = st.name()
			
			
			
			if ret == subAST[c2] || ret == verboseAST[c2] {
				addSubst = false
			}
		}
	case 'O', 'P', 'R', 'C', 'G':
		st.advance(1)
		t := st.demangleType(isCast)
		switch c {
		case 'O':
			ret = &RvalueReferenceType{Base: t}
		case 'P':
			ret = &PointerType{Base: t}
		case 'R':
			ret = &ReferenceType{Base: t}
		case 'C':
			ret = &ComplexType{Base: t}
		case 'G':
			ret = &ImaginaryType{Base: t}
		}
	case 'U':
		if len(st.str) < 2 {
			st.fail("expected source name or unnamed type")
		}
		switch st.str[1] {
		case 'l':
			ret = st.closureTypeName()
			addSubst = false
		case 't':
			ret = st.unnamedTypeName()
			addSubst = false
		default:
			st.advance(1)
			n := st.sourceName()
			if len(st.str) > 0 && st.str[0] == 'I' {
				args := st.templateArgs()
				n = &Template{Name: n, Args: args}
			}
			t := st.demangleType(isCast)
			ret = &VendorQualifier{Qualifier: n, Type: t}
		}
	case 'D':
		st.advance(1)
		if len(st.str) == 0 {
			st.fail("expected D code for type")
		}
		addSubst = false
		c2 := st.str[0]
		st.advance(1)
		switch c2 {
		case 'T', 't':
			
			ret = st.expression()
			if len(st.str) == 0 || st.str[0] != 'E' {
				st.fail("expected E after expression in type")
			}
			st.advance(1)
			ret = &Decltype{Expr: ret}
			addSubst = true

		case 'p':
			t := st.demangleType(isCast)
			pack := st.findArgumentPack(t)
			ret = &PackExpansion{Base: t, Pack: pack}
			addSubst = true

		case 'a':
			ret = &Name{Name: "auto"}

		case 'f':
			ret = &BuiltinType{Name: "decimal32"}
		case 'd':
			ret = &BuiltinType{Name: "decimal64"}
		case 'e':
			ret = &BuiltinType{Name: "decimal128"}
		case 'h':
			ret = &BuiltinType{Name: "half"}
		case 's':
			ret = &BuiltinType{Name: "char16_t"}
		case 'i':
			ret = &BuiltinType{Name: "char32_t"}
		case 'n':
			ret = &BuiltinType{Name: "decltype(nullptr)"}

		case 'F':
			accum := false
			if len(st.str) > 0 && isDigit(st.str[0]) {
				accum = true
				
				_ = st.number()
			}
			base := st.demangleType(isCast)
			if len(st.str) > 0 && isDigit(st.str[0]) {
				
				st.number()
			}
			sat := false
			if len(st.str) > 0 {
				if st.str[0] == 's' {
					sat = true
				}
				st.advance(1)
			}
			ret = &FixedType{Base: base, Accum: accum, Sat: sat}

		case 'v':
			ret = st.vectorType(isCast)
			addSubst = true

		default:
			st.fail("unrecognized D code in type")
		}

	default:
		st.fail("unrecognized type code")
	}

	if addSubst {
		if sub != nil {
			st.subs.add(sub)
		} else {
			st.subs.add(ret)
		}
	}

	if len(q) > 0 {
		if _, ok := ret.(*FunctionType); ok {
			ret = &MethodWithQualifiers{Method: ret, Qualifiers: q, RefQualifier: ""}
		} else if mwq, ok := ret.(*MethodWithQualifiers); ok {
			
			
			
			mwq.Qualifiers = mergeQualifiers(q, mwq.Qualifiers)
		} else {
			
			
			if qsub, ok := ret.(*TypeWithQualifiers); ok {
				q = mergeQualifiers(q, qsub.Qualifiers)
				ret = qsub.Base
			}
			ret = &TypeWithQualifiers{Base: ret, Qualifiers: q}
		}
		st.subs.add(ret)
	}

	return ret
}
func ( st * state ) demangleType ( isCast bool ) AST { if len ( st . str ) == 0 { st . fail ( " " ) } addSubst := true q := st . cvQualifiers ( ) if len ( q ) > 0 { if len ( st . str ) == 0 { st . fail ( " " ) } // CV-qualifiers before a function type apply to // 'this', so avoid adding the unqualified function // type to the substitution list. if st . str [ 0 ] == 'F' { addSubst = false } } var ret AST // Use correct substitution for a template parameter. var sub AST if btype , ok := builtinTypes [ st . str [ 0 ] ] ; ok { ret = & BuiltinType { Name : btype } st . advance ( 1 ) if len ( q ) > 0 { ret = & TypeWithQualifiers { Base : ret , Qualifiers : q } st . subs . add ( ret ) } return ret } c := st . str [ 0 ] switch c { case 'u' : st . advance ( 1 ) ret = st . sourceName ( ) case 'F' : ret = st . functionType ( ) case 'N' , 'Z' , '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' : ret = st . name ( ) case 'A' : ret = st . arrayType ( isCast ) case 'M' : ret = st . pointerToMemberType ( isCast ) case 'T' : ret = st . templateParam ( ) if len ( st . str ) > 0 && st . str [ 0 ] == 'I' { // See the function comment to explain this. if ! isCast { st . subs . add ( ret ) args := st . templateArgs ( ) ret = & Template { Name : ret , Args : args } } else { ret = st . demangleCastTemplateArgs ( ret , true ) } } case 'S' : // If this is a special substitution, then it // is the start of <class-enum-type>. var c2 byte if len ( st . str ) > 1 { c2 = st . str [ 1 ] } if isDigit ( c2 ) || c2 == '_' || isUpper ( c2 ) { ret = st . substitution ( false ) if len ( st . str ) == 0 || st . str [ 0 ] != 'I' { addSubst = false } else { // See the function comment to explain this. if _ , ok := ret . ( * TemplateParam ) ; ! ok || ! isCast { args := st . templateArgs ( ) ret = & Template { Name : ret , Args : args } } else { next := st . demangleCastTemplateArgs ( ret , false ) if next == ret { addSubst = false } ret = next } } } else { ret = st . name ( ) // This substitution is not itself a // substitution candidate, unless template // arguments were added. if ret == subAST [ c2 ] || ret == verboseAST [ c2 ] { addSubst = false } } case 'O' , 'P' , 'R' , 'C' , 'G' : st . advance ( 1 ) t := st . demangleType ( isCast ) switch c { case 'O' : ret = & RvalueReferenceType { Base : t } case 'P' : ret = & PointerType { Base : t } case 'R' : ret = & ReferenceType { Base : t } case 'C' : ret = & ComplexType { Base : t } case 'G' : ret = & ImaginaryType { Base : t } } case 'U' : if len ( st . str ) < 2 { st . fail ( " " ) } switch st . str [ 1 ] { case 'l' : ret = st . closureTypeName ( ) addSubst = false case 't' : ret = st . unnamedTypeName ( ) addSubst = false default : st . advance ( 1 ) n := st . sourceName ( ) if len ( st . str ) > 0 && st . str [ 0 ] == 'I' { args := st . templateArgs ( ) n = & Template { Name : n , Args : args } } t := st . demangleType ( isCast ) ret = & VendorQualifier { Qualifier : n , Type : t } } case 'D' : st . advance ( 1 ) if len ( st . str ) == 0 { st . fail ( " " ) } addSubst = false c2 := st . str [ 0 ] st . advance ( 1 ) switch c2 { case 'T' , 't' : // decltype(expression) ret = st . expression ( ) if len ( st . str ) == 0 || st . str [ 0 ] != 'E' { st . fail ( " " ) } st . advance ( 1 ) ret = & Decltype { Expr : ret } addSubst = true case 'p' : t := st . demangleType ( isCast ) pack := st . findArgumentPack ( t ) ret = & PackExpansion { Base : t , Pack : pack } addSubst = true case 'a' : ret = & Name { Name : " " } case 'f' : ret = & BuiltinType { Name : " " } case 'd' : ret = & BuiltinType { Name : " " } case 'e' : ret = & BuiltinType { Name : " " } case 'h' : ret = & BuiltinType { Name : " " } case 's' : ret = & BuiltinType { Name : " " } case 'i' : ret = & BuiltinType { Name : " " } case 'n' : ret = & BuiltinType { Name : " " } case 'F' : accum := false if len ( st . str ) > 0 && isDigit ( st . str [ 0 ] ) { accum = true // We don't care about the bits. _ = st . number ( ) } base := st . demangleType ( isCast ) if len ( st . str ) > 0 && isDigit ( st . str [ 0 ] ) { // We don't care about the bits. st . number ( ) } sat := false if len ( st . str ) > 0 { if st . str [ 0 ] == 's' { sat = true } st . advance ( 1 ) } ret = & FixedType { Base : base , Accum : accum , Sat : sat } case 'v' : ret = st . vectorType ( isCast ) addSubst = true default : st . fail ( " " ) } default : st . fail ( " " ) } if addSubst { if sub != nil { st . subs . add ( sub ) } else { st . subs . add ( ret ) } } if len ( q ) > 0 { if _ , ok := ret . ( * FunctionType ) ; ok { ret = & MethodWithQualifiers { Method : ret , Qualifiers : q , RefQualifier : " " } } else if mwq , ok := ret . ( * MethodWithQualifiers ) ; ok { // Merge adjacent qualifiers.  This case // happens with a function with a trailing // ref-qualifier. mwq . Qualifiers = mergeQualifiers ( q , mwq . Qualifiers ) } else { // Merge adjacent qualifiers.  This case // happens with multi-dimensional array types. if qsub , ok := ret . ( * TypeWithQualifiers ) ; ok { q = mergeQualifiers ( q , qsub . Qualifiers ) ret = qsub . Base } ret = & TypeWithQualifiers { Base : ret , Qualifiers : q } } st . subs . add ( ret ) } return ret }
// <type> ::= <builtin-type>
//        ::= <function-type>
//        ::= <class-enum-type>
//        ::= <array-type>
//        ::= <pointer-to-member-type>
//        ::= <template-param>
//        ::= <template-template-param> <template-args>
//        ::= <substitution>
//        ::= <CV-qualifiers> <type>
//        ::= P <type>
//        ::= R <type>
//        ::= O <type> (C++0x)
//        ::= C <type>
//        ::= G <type>
//        ::= U <source-name> <type>
//
// <builtin-type> ::= various one letter codes
//                ::= u <source-name>
<type > :: = <builtin - type > :: = <function - type > :: = <class - enum - type > :: = <array - type > :: = <pointer - to - member - type > :: = <template - param > :: = <template - template - param > <template - args > :: = <substitution > :: = <CV - qualifiers > <type > :: = P <type > :: = R <type > :: = O <type > ( C ++ 0x ) :: = C <type > :: = G <type > :: = U <source - name > <type > <builtin - type > :: = various one letter codes :: = u <source - name >