demangle.go
func (st *state) expression() AST {
	if len(st.str) == 0 {
		st.fail("expected expression")
	}
	if st.str[0] == 'L' {
		return st.exprPrimary()
	} else if st.str[0] == 'T' {
		return st.templateParam()
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'r' {
		st.advance(2)
		if len(st.str) == 0 {
			st.fail("expected unresolved type")
		}
		switch st.str[0] {
		case 'T', 'D', 'S':
			t := st.demangleType(false)
			n := st.baseUnresolvedName()
			n = &Qualified{Scope: t, Name: n, LocalName: false}
			if len(st.str) > 0 && st.str[0] == 'I' {
				args := st.templateArgs()
				n = &Template{Name: n, Args: args}
			}
			return n
		default:
			var s AST
			if st.str[0] == 'N' {
				st.advance(1)
				s = st.demangleType(false)
			}
			for len(st.str) == 0 || st.str[0] != 'E' {
				// GCC does not seem to follow the ABI here.
				// It can emit type/name without an 'E'.
				if s != nil && len(st.str) > 0 && !isDigit(st.str[0]) {
					if q, ok := s.(*Qualified); ok {
						a := q.Scope
						if t, ok := a.(*Template); ok {
							st.subs.add(t.Name)
							st.subs.add(t)
						} else {
							st.subs.add(a)
						}
						return s
					}
				}
				n := st.sourceName()
				if len(st.str) > 0 && st.str[0] == 'I' {
					st.subs.add(n)
					args := st.templateArgs()
					n = &Template{Name: n, Args: args}
				}
				if s == nil {
					s = n
				} else {
					s = &Qualified{Scope: s, Name: n, LocalName: false}
				}
				st.subs.add(s)
			}
			if s == nil {
				st.fail("missing scope in unresolved name")
			}
			st.advance(1)
			n := st.baseUnresolvedName()
			return &Qualified{Scope: s, Name: n, LocalName: false}
		}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'p' {
		st.advance(2)
		e := st.expression()
		pack := st.findArgumentPack(e)
		return &PackExpansion{Base: e, Pack: pack}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'Z' {
		st.advance(2)
		off := st.off
		e := st.expression()
		ap := st.findArgumentPack(e)
		if ap == nil {
			st.failEarlier("missing argument pack", st.off-off)
		}
		return &SizeofPack{Pack: ap}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'P' {
		st.advance(2)
		var args []AST
		for len(st.str) == 0 || st.str[0] != 'E' {
			arg := st.templateArg()
			args = append(args, arg)
		}
		st.advance(1)
		return &SizeofArgs{Args: args}
	} else if st.str[0] == 'f' && len(st.str) > 1 && st.str[1] == 'p' {
		st.advance(2)
		if len(st.str) > 0 && st.str[0] == 'T' {
			st.advance(1)
			return &FunctionParam{Index: 0}
		} else {
			index := st.compactNumber()
			return &FunctionParam{Index: index + 1}
		}
	} else if isDigit(st.str[0]) || (st.str[0] == 'o' && len(st.str) > 1 && st.str[1] == 'n') {
		if st.str[0] == 'o' {
			// Skip operator function ID.
			st.advance(2)
		}
		n, _ := st.unqualifiedName()
		if len(st.str) > 0 && st.str[0] == 'I' {
			args := st.templateArgs()
			n = &Template{Name: n, Args: args}
		}
		return n
	} else if (st.str[0] == 'i' || st.str[0] == 't') && len(st.str) > 1 && st.str[1] == 'l' {
		// Brace-enclosed initializer list.
		c := st.str[0]
		st.advance(2)
		var t AST
		if c == 't' {
			t = st.demangleType(false)
		}
		exprs := st.exprList('E')
		return &InitializerList{Type: t, Exprs: exprs}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 't' {
		o, _ := st.operatorName(true)
		t := st.demangleType(false)
		return &Unary{Op: o, Expr: t, Suffix: false, SizeofType: true}
	} else {
		if len(st.str) < 2 {
			st.fail("missing operator code")
		}
		code := st.str[:2]
		o, args := st.operatorName(true)
		switch args {
		case 0:
			return &Nullary{Op: o}

		case 1:
			suffix := false
			if code == "pp" || code == "mm" {
				if len(st.str) > 0 && st.str[0] == '_' {
					st.advance(1)
				} else {
					suffix = true
				}
			}
			var operand AST
			if _, ok := o.(*Cast); ok && len(st.str) > 0 && st.str[0] == '_' {
				st.advance(1)
				operand = st.exprList('E')
			} else {
				operand = st.expression()
			}
			return &Unary{Op: o, Expr: operand, Suffix: suffix, SizeofType: false}

		case 2:
			var left, right AST
			if code == "sc" || code == "dc" || code == "cc" || code == "rc" {
				left = st.demangleType(false)
			} else if code[0] == 'f' {
				left, _ = st.operatorName(true)
				right = st.expression()
				return &Fold{Left: code[1] == 'l', Op: left, Arg1: right, Arg2: nil}
			} else {
				left = st.expression()
			}
			if code == "cl" {
				right = st.exprList('E')
			} else if code == "dt" || code == "pt" {
				right, _ = st.unqualifiedName()
				if len(st.str) > 0 && st.str[0] == 'I' {
					args := st.templateArgs()
					right = &Template{Name: right, Args: args}
				}
			} else {
				right = st.expression()
			}
			return &Binary{Op: o, Left: left, Right: right}

		case 3:
			if code[0] == 'n' {
				if code[1] != 'w' && code[1] != 'a' {
					panic("internal error")
				}
				place := st.exprList('_')
				if place.(*ExprList).Exprs == nil {
					place = nil
				}
				t := st.demangleType(false)
				var ini AST
				if len(st.str) > 0 && st.str[0] == 'E' {
					st.advance(1)
				} else if len(st.str) > 1 && st.str[0] == 'p' && st.str[1] == 'i' {
					// Parenthesized initializer.
					st.advance(2)
					ini = st.exprList('E')
				} else if len(st.str) > 1 && st.str[0] == 'i' && st.str[1] == 'l' {
					// Initializer list.
					ini = st.expression()
				} else {
					st.fail("unrecognized new initializer")
				}
				return &New{Op: o, Place: place, Type: t, Init: ini}
			} else if code[0] == 'f' {
				first, _ := st.operatorName(true)
				second := st.expression()
				third := st.expression()
				return &Fold{Left: code[1] == 'L', Op: first, Arg1: second, Arg2: third}
			} else {
				first := st.expression()
				second := st.expression()
				third := st.expression()
				return &Trinary{Op: o, First: first, Second: second, Third: third}
			}

		default:
			st.fail(fmt.Sprintf("unsupported number of operator arguments: %d", args))
			panic("not reached")
		}
	}
}
func (st *state) expression() AST {
	if len(st.str) == 0 {
		st.fail("expected expression")
	}
	if st.str[0] == 'L' {
		return st.exprPrimary()
	} else if st.str[0] == 'T' {
		return st.templateParam()
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'r' {
		st.advance(2)
		if len(st.str) == 0 {
			st.fail("expected unresolved type")
		}
		switch st.str[0] {
		case 'T', 'D', 'S':
			t := st.demangleType(false)
			n := st.baseUnresolvedName()
			n = &Qualified{Scope: t, Name: n, LocalName: false}
			if len(st.str) > 0 && st.str[0] == 'I' {
				args := st.templateArgs()
				n = &Template{Name: n, Args: args}
			}
			return n
		default:
			var s AST
			if st.str[0] == 'N' {
				st.advance(1)
				s = st.demangleType(false)
			}
			for len(st.str) == 0 || st.str[0] != 'E' {
				// GCC does not seem to follow the ABI here.
				// It can emit type/name without an 'E'.
				if s != nil && len(st.str) > 0 && !isDigit(st.str[0]) {
					if q, ok := s.(*Qualified); ok {
						a := q.Scope
						if t, ok := a.(*Template); ok {
							st.subs.add(t.Name)
							st.subs.add(t)
						} else {
							st.subs.add(a)
						}
						return s
					}
				}
				n := st.sourceName()
				if len(st.str) > 0 && st.str[0] == 'I' {
					st.subs.add(n)
					args := st.templateArgs()
					n = &Template{Name: n, Args: args}
				}
				if s == nil {
					s = n
				} else {
					s = &Qualified{Scope: s, Name: n, LocalName: false}
				}
				st.subs.add(s)
			}
			if s == nil {
				st.fail("missing scope in unresolved name")
			}
			st.advance(1)
			n := st.baseUnresolvedName()
			return &Qualified{Scope: s, Name: n, LocalName: false}
		}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'p' {
		st.advance(2)
		e := st.expression()
		pack := st.findArgumentPack(e)
		return &PackExpansion{Base: e, Pack: pack}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'Z' {
		st.advance(2)
		off := st.off
		e := st.expression()
		ap := st.findArgumentPack(e)
		if ap == nil {
			st.failEarlier("missing argument pack", st.off-off)
		}
		return &SizeofPack{Pack: ap}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'P' {
		st.advance(2)
		var args []AST
		for len(st.str) == 0 || st.str[0] != 'E' {
			arg := st.templateArg()
			args = append(args, arg)
		}
		st.advance(1)
		return &SizeofArgs{Args: args}
	} else if st.str[0] == 'f' && len(st.str) > 1 && st.str[1] == 'p' {
		st.advance(2)
		if len(st.str) > 0 && st.str[0] == 'T' {
			st.advance(1)
			return &FunctionParam{Index: 0}
		} else {
			index := st.compactNumber()
			return &FunctionParam{Index: index + 1}
		}
	} else if isDigit(st.str[0]) || (st.str[0] == 'o' && len(st.str) > 1 && st.str[1] == 'n') {
		if st.str[0] == 'o' {
			// Skip operator function ID.
			st.advance(2)
		}
		n, _ := st.unqualifiedName()
		if len(st.str) > 0 && st.str[0] == 'I' {
			args := st.templateArgs()
			n = &Template{Name: n, Args: args}
		}
		return n
	} else if (st.str[0] == 'i' || st.str[0] == 't') && len(st.str) > 1 && st.str[1] == 'l' {
		// Brace-enclosed initializer list.
		c := st.str[0]
		st.advance(2)
		var t AST
		if c == 't' {
			t = st.demangleType(false)
		}
		exprs := st.exprList('E')
		return &InitializerList{Type: t, Exprs: exprs}
	} else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 't' {
		o, _ := st.operatorName(true)
		t := st.demangleType(false)
		return &Unary{Op: o, Expr: t, Suffix: false, SizeofType: true}
	} else {
		if len(st.str) < 2 {
			st.fail("missing operator code")
		}
		code := st.str[:2]
		o, args := st.operatorName(true)
		switch args {
		case 0:
			return &Nullary{Op: o}

		case 1:
			suffix := false
			if code == "pp" || code == "mm" {
				if len(st.str) > 0 && st.str[0] == '_' {
					st.advance(1)
				} else {
					suffix = true
				}
			}
			var operand AST
			if _, ok := o.(*Cast); ok && len(st.str) > 0 && st.str[0] == '_' {
				st.advance(1)
				operand = st.exprList('E')
			} else {
				operand = st.expression()
			}
			return &Unary{Op: o, Expr: operand, Suffix: suffix, SizeofType: false}

		case 2:
			var left, right AST
			if code == "sc" || code == "dc" || code == "cc" || code == "rc" {
				left = st.demangleType(false)
			} else if code[0] == 'f' {
				left, _ = st.operatorName(true)
				right = st.expression()
				return &Fold{Left: code[1] == 'l', Op: left, Arg1: right, Arg2: nil}
			} else {
				left = st.expression()
			}
			if code == "cl" {
				right = st.exprList('E')
			} else if code == "dt" || code == "pt" {
				right, _ = st.unqualifiedName()
				if len(st.str) > 0 && st.str[0] == 'I' {
					args := st.templateArgs()
					right = &Template{Name: right, Args: args}
				}
			} else {
				right = st.expression()
			}
			return &Binary{Op: o, Left: left, Right: right}

		case 3:
			if code[0] == 'n' {
				if code[1] != 'w' && code[1] != 'a' {
					panic("internal error")
				}
				place := st.exprList('_')
				if place.(*ExprList).Exprs == nil {
					place = nil
				}
				t := st.demangleType(false)
				var ini AST
				if len(st.str) > 0 && st.str[0] == 'E' {
					st.advance(1)
				} else if len(st.str) > 1 && st.str[0] == 'p' && st.str[1] == 'i' {
					// Parenthesized initializer.
					st.advance(2)
					ini = st.exprList('E')
				} else if len(st.str) > 1 && st.str[0] == 'i' && st.str[1] == 'l' {
					// Initializer list.
					ini = st.expression()
				} else {
					st.fail("unrecognized new initializer")
				}
				return &New{Op: o, Place: place, Type: t, Init: ini}
			} else if code[0] == 'f' {
				first, _ := st.operatorName(true)
				second := st.expression()
				third := st.expression()
				return &Fold{Left: code[1] == 'L', Op: first, Arg1: second, Arg2: third}
			} else {
				first := st.expression()
				second := st.expression()
				third := st.expression()
				return &Trinary{Op: o, First: first, Second: second, Third: third}
			}

		default:
			st.fail(fmt.Sprintf("unsupported number of operator arguments: %d", args))
			panic("not reached")
		}
	}
}
// <expression> ::= <(unary) operator-name> <expression>
//              ::= <(binary) operator-name> <expression> <expression>
//              ::= <(trinary) operator-name> <expression> <expression> <expression>
//              ::= cl <expression>+ E
//              ::= st <type>
//              ::= <template-param>
//              ::= sr <type> <unqualified-name>
//              ::= sr <type> <unqualified-name> <template-args>
//              ::= <expr-primary>