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")
		}
	}
}
func ( st * state ) expression ( ) AST { if len ( st . str ) == 0 { st . fail ( " " ) } 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 ( " " ) } 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 ( " " ) } 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 ( " " , 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 ( " " ) } code := st . str [ : 2 ] o , args := st . operatorName ( true ) switch args { case 0 : return & Nullary { Op : o } case 1 : suffix := false if code == " " || code == " " { 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 == " " || code == " " || code == " " || code == " " { 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 == " " { right = st . exprList ( 'E' ) } else if code == " " || code == " " { 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 ( " " ) } 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 ( " " ) } 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 ( " " , args ) ) panic ( " " ) } } }
// <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>
<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 >