mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 05:42:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			3102 lines
		
	
	
	
		
			68 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			3102 lines
		
	
	
	
		
			68 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright 2019 The CC Authors. All rights reserved. | |||
|  | // Use of this source code is governed by a BSD-style | |||
|  | // license that can be found in the LICENSE file. | |||
|  | 
 | |||
|  | package cc // import "modernc.org/cc/v3" | |||
|  | 
 | |||
|  | import ( | |||
|  | 	"bytes" | |||
|  | 	"fmt" | |||
|  | 	gotoken "go/token" | |||
|  | 	"math" | |||
|  | 	"os" | |||
|  | 	"path/filepath" | |||
|  | 	"strconv" | |||
|  | 	"strings" | |||
|  | 	"sync" | |||
|  | 	"time" | |||
|  | 	"unicode/utf8" | |||
|  | 
 | |||
|  | 	"modernc.org/token" | |||
|  | ) | |||
|  | 
 | |||
|  | const ( | |||
|  | 	maxIncludeLevel = 200 // gcc, std is at least 15. | |||
|  | ) | |||
|  | 
 | |||
|  | var ( | |||
|  | 	_ tokenReader = (*cpp)(nil) | |||
|  | 	_ tokenWriter = (*cpp)(nil) | |||
|  | 
 | |||
|  | 	idCOUNTER                  = dict.sid("__COUNTER__") | |||
|  | 	idCxLimitedRange           = dict.sid("CX_LIMITED_RANGE") | |||
|  | 	idDATE                     = dict.sid("__DATE__") | |||
|  | 	idDefault                  = dict.sid("DEFAULT") | |||
|  | 	idDefined                  = dict.sid("defined") | |||
|  | 	idEmptyString              = dict.sid(`""`) | |||
|  | 	idFILE                     = dict.sid("__FILE__") | |||
|  | 	idFPContract               = dict.sid("FP_CONTRACT") | |||
|  | 	idFdZero                   = dict.sid("FD_ZERO") | |||
|  | 	idFenvAccess               = dict.sid("FENV_ACCESS") | |||
|  | 	idGNUC                     = dict.sid("__GNUC__") | |||
|  | 	idHasIncludeImpl           = dict.sid("__has_include_impl") | |||
|  | 	idIntMaxWidth              = dict.sid("__INTMAX_WIDTH__") | |||
|  | 	idL                        = dict.sid("L") | |||
|  | 	idLINE                     = dict.sid("__LINE__") | |||
|  | 	idNL                       = dict.sid("\n") | |||
|  | 	idOff                      = dict.sid("OFF") | |||
|  | 	idOn                       = dict.sid("ON") | |||
|  | 	idOne                      = dict.sid("1") | |||
|  | 	idPragmaSTDC               = dict.sid("__pragma_stdc") | |||
|  | 	idSTDC                     = dict.sid("STDC") | |||
|  | 	idTIME                     = dict.sid("__TIME__") | |||
|  | 	idTclDefaultDoubleRounding = dict.sid("TCL_DEFAULT_DOUBLE_ROUNDING") | |||
|  | 	idTclIeeeDoubleRounding    = dict.sid("TCL_IEEE_DOUBLE_ROUNDING") | |||
|  | 	idVaArgs                   = dict.sid("__VA_ARGS__") | |||
|  | 	idZero                     = dict.sid("0") | |||
|  | 
 | |||
|  | 	cppTokensPool = sync.Pool{New: func() interface{} { r := []cppToken{}; return &r }} | |||
|  | 
 | |||
|  | 	protectedMacros = hideSet{ // [0], 6.10.8, 4 | |||
|  | 		dict.sid("__STDC_HOSTED__"):          {}, | |||
|  | 		dict.sid("__STDC_IEC_559_COMPLEX__"): {}, | |||
|  | 		dict.sid("__STDC_IEC_559__"):         {}, | |||
|  | 		dict.sid("__STDC_ISO_10646__"):       {}, | |||
|  | 		dict.sid("__STDC_MB_MIGHT_NEQ_WC__"): {}, | |||
|  | 		dict.sid("__STDC_VERSION__"):         {}, | |||
|  | 		dict.sid("__STDC__"):                 {}, | |||
|  | 		idCOUNTER:                            {}, | |||
|  | 		idDATE:                               {}, | |||
|  | 		idFILE:                               {}, | |||
|  | 		idLINE:                               {}, | |||
|  | 		idTIME:                               {}, | |||
|  | 	} | |||
|  | ) | |||
|  | 
 | |||
|  | type tokenReader interface { | |||
|  | 	read() (cppToken, bool) | |||
|  | 	unget(cppToken) | |||
|  | 	ungets([]cppToken) | |||
|  | } | |||
|  | 
 | |||
|  | type tokenWriter interface { | |||
|  | 	write(cppToken) | |||
|  | 	writes([]cppToken) | |||
|  | } | |||
|  | 
 | |||
|  | // token4 is produced by translation phase 4. | |||
|  | type token4 struct { | |||
|  | 	file *tokenFile //TODO sort fields | |||
|  | 	token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (t *token4) Position() (r token.Position) { | |||
|  | 	if t.pos != 0 && t.file != nil { | |||
|  | 		r = t.file.PositionFor(token.Pos(t.pos), true) | |||
|  | 	} | |||
|  | 	return r | |||
|  | } | |||
|  | 
 | |||
|  | type hideSet map[StringID]struct{} | |||
|  | 
 | |||
|  | type cppToken struct { | |||
|  | 	token4 | |||
|  | 	hs hideSet | |||
|  | } | |||
|  | 
 | |||
|  | func (t *cppToken) has(nm StringID) bool { _, ok := t.hs[nm]; return ok } | |||
|  | 
 | |||
|  | type cppWriter struct { | |||
|  | 	toks []cppToken | |||
|  | } | |||
|  | 
 | |||
|  | func (w *cppWriter) write(tok cppToken)     { w.toks = append(w.toks, tok) } | |||
|  | func (w *cppWriter) writes(toks []cppToken) { w.toks = append(w.toks, toks...) } | |||
|  | 
 | |||
|  | type ungetBuf []cppToken | |||
|  | 
 | |||
|  | func (u *ungetBuf) unget(t cppToken) { *u = append(*u, t) } | |||
|  | 
 | |||
|  | func (u *ungetBuf) read() (t cppToken) { | |||
|  | 	s := *u | |||
|  | 	n := len(s) - 1 | |||
|  | 	t = s[n] | |||
|  | 	*u = s[:n] | |||
|  | 	return t | |||
|  | } | |||
|  | func (u *ungetBuf) ungets(toks []cppToken) { | |||
|  | 	s := *u | |||
|  | 	for i := len(toks) - 1; i >= 0; i-- { | |||
|  | 		s = append(s, toks[i]) | |||
|  | 	} | |||
|  | 	*u = s | |||
|  | } | |||
|  | 
 | |||
|  | func cppToksStr(toks []cppToken, sep string) string { | |||
|  | 	var b strings.Builder | |||
|  | 	for i, v := range toks { | |||
|  | 		if i != 0 { | |||
|  | 			b.WriteString(sep) | |||
|  | 		} | |||
|  | 		b.WriteString(v.String()) | |||
|  | 	} | |||
|  | 	return b.String() | |||
|  | } | |||
|  | 
 | |||
|  | func cppToksStr2(toks [][]cppToken) string { | |||
|  | 	panic(todo("")) | |||
|  | 	var a []string | |||
|  | 	for _, v := range toks { | |||
|  | 		a = append(a, fmt.Sprintf("%q", cppToksStr(v, "|"))) | |||
|  | 	} | |||
|  | 	return fmt.Sprint(a) | |||
|  | } | |||
|  | 
 | |||
|  | type cppReader struct { | |||
|  | 	buf []cppToken | |||
|  | 	ungetBuf | |||
|  | } | |||
|  | 
 | |||
|  | func (r *cppReader) read() (tok cppToken, ok bool) { | |||
|  | 	if len(r.ungetBuf) != 0 { | |||
|  | 		return r.ungetBuf.read(), true | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if len(r.buf) == 0 { | |||
|  | 		return tok, false | |||
|  | 	} | |||
|  | 
 | |||
|  | 	tok = r.buf[0] | |||
|  | 	r.buf = r.buf[1:] | |||
|  | 	return tok, true | |||
|  | } | |||
|  | 
 | |||
|  | type cppScanner []cppToken | |||
|  | 
 | |||
|  | func (s *cppScanner) peek() (r cppToken) { | |||
|  | 	r.char = -1 | |||
|  | 	if len(*s) == 0 { | |||
|  | 		return r | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return (*s)[0] | |||
|  | } | |||
|  | 
 | |||
|  | func (s *cppScanner) next() (r cppToken) { | |||
|  | 	r.char = -1 | |||
|  | 	if len(*s) == 0 { | |||
|  | 		return r | |||
|  | 	} | |||
|  | 
 | |||
|  | 	*s = (*s)[1:] | |||
|  | 	return s.peek() | |||
|  | } | |||
|  | 
 | |||
|  | func (s *cppScanner) Pos() token.Pos { | |||
|  | 	if len(*s) == 0 { | |||
|  | 		return 0 | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return (*s)[0].Pos() | |||
|  | } | |||
|  | 
 | |||
|  | // Macro represents a preprocessor macro definition. | |||
|  | type Macro struct { | |||
|  | 	fp    []StringID | |||
|  | 	repl  []token3 | |||
|  | 	repl2 []Token | |||
|  | 
 | |||
|  | 	name token4 | |||
|  | 	pos  int32 | |||
|  | 
 | |||
|  | 	isFnLike      bool | |||
|  | 	namedVariadic bool // foo..., note no comma before ellipsis. | |||
|  | 	variadic      bool | |||
|  | } | |||
|  | 
 | |||
|  | // Position reports the position of the macro definition. | |||
|  | func (m *Macro) Position() token.Position { | |||
|  | 	if m.pos != 0 && m.name.file != nil { | |||
|  | 		return m.name.file.PositionFor(token.Pos(m.pos), true) | |||
|  | 	} | |||
|  | 	return token.Position{} | |||
|  | } | |||
|  | 
 | |||
|  | // Parameters return the list of function-like macro parameters. | |||
|  | func (m *Macro) Parameters() []StringID { return m.fp } | |||
|  | 
 | |||
|  | // ReplacementTokens return the list of tokens m is replaced with. Tokens in | |||
|  | // the returned list have only the Rune and Value fields valid. | |||
|  | func (m *Macro) ReplacementTokens() []Token { | |||
|  | 	if m.repl2 != nil { | |||
|  | 		return m.repl2 | |||
|  | 	} | |||
|  | 
 | |||
|  | 	m.repl2 = make([]Token, len(m.repl)) | |||
|  | 	for i, v := range m.repl { | |||
|  | 		m.repl2[i] = Token{Rune: v.char, Value: v.value, Src: v.src} | |||
|  | 	} | |||
|  | 	return m.repl2 | |||
|  | } | |||
|  | 
 | |||
|  | // IsFnLike reports whether m is a function-like macro. | |||
|  | func (m *Macro) IsFnLike() bool { return m.isFnLike } | |||
|  | 
 | |||
|  | func (m *Macro) isNamedVariadicParam(nm StringID) bool { | |||
|  | 	return m.namedVariadic && nm == m.fp[len(m.fp)-1] | |||
|  | } | |||
|  | 
 | |||
|  | func (m *Macro) param2(varArgs []cppToken, ap [][]cppToken, nm StringID, out *[]cppToken, argIndex *int) bool { | |||
|  | 	*out = nil | |||
|  | 	if nm == idVaArgs || m.isNamedVariadicParam(nm) { | |||
|  | 		if !m.variadic { | |||
|  | 			return false | |||
|  | 		} | |||
|  | 
 | |||
|  | 		*out = append([]cppToken(nil), varArgs...) | |||
|  | 		return true | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for i, v := range m.fp { | |||
|  | 		if v == nm { | |||
|  | 			if i < len(ap) { | |||
|  | 				a := ap[i] | |||
|  | 				for len(a) != 0 && a[0].char == ' ' { | |||
|  | 					a = a[1:] | |||
|  | 				} | |||
|  | 				*out = a | |||
|  | 			} | |||
|  | 			if argIndex != nil { | |||
|  | 				*argIndex = i | |||
|  | 			} | |||
|  | 			return true | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return false | |||
|  | } | |||
|  | 
 | |||
|  | func (m *Macro) param(varArgs []cppToken, ap [][]cppToken, nm StringID, out *[]cppToken) bool { | |||
|  | 	// trc("select (A) varArgs %q, ap %v, nm %q, out %q", cppToksStr(varArgs, "|"), cppToksStr2(ap), nm, cppToksStr(*out, "|")) | |||
|  | 	// defer func() { | |||
|  | 	// 	trc("select (A) varArgs %q, ap %v, nm %q, out %q", cppToksStr(varArgs, "|"), cppToksStr2(ap), nm, cppToksStr(*out, "|")) | |||
|  | 	// }() | |||
|  | 	return m.param2(varArgs, ap, nm, out, nil) | |||
|  | } | |||
|  | 
 | |||
|  | // --------------------------------------------------------------- Preprocessor | |||
|  | 
 | |||
|  | type cpp struct { | |||
|  | 	counter      int | |||
|  | 	counterMacro Macro | |||
|  | 	ctx          *context | |||
|  | 	dateMacro    Macro | |||
|  | 	file         *tokenFile | |||
|  | 	fileMacro    Macro | |||
|  | 	in           chan []token3 | |||
|  | 	inBuf        []token3 | |||
|  | 	includeLevel int | |||
|  | 	lineMacro    Macro | |||
|  | 	macroStack   map[StringID][]*Macro | |||
|  | 	macros       map[StringID]*Macro | |||
|  | 	out          chan *[]token4 | |||
|  | 	outBuf       *[]token4 | |||
|  | 	pragmaOpBuf  []token4 | |||
|  | 	rq           chan struct{} | |||
|  | 	timeMacro    Macro | |||
|  | 	ungetBuf | |||
|  | 
 | |||
|  | 	last rune | |||
|  | 
 | |||
|  | 	intmaxChecked bool | |||
|  | 	nonFirstRead  bool | |||
|  | 	seenEOF       bool | |||
|  | 	inPragmaOp    bool | |||
|  | } | |||
|  | 
 | |||
|  | func newCPP(ctx *context) *cpp { | |||
|  | 	b := token4Pool.Get().(*[]token4) | |||
|  | 	*b = (*b)[:0] | |||
|  | 	r := &cpp{ | |||
|  | 		ctx:        ctx, | |||
|  | 		macroStack: map[StringID][]*Macro{}, | |||
|  | 		macros:     map[StringID]*Macro{}, | |||
|  | 		outBuf:     b, | |||
|  | 	} | |||
|  | 	r.counterMacro = Macro{repl: []token3{{char: PPNUMBER}}} | |||
|  | 	r.dateMacro = Macro{repl: []token3{{char: STRINGLITERAL}}} | |||
|  | 	r.timeMacro = Macro{repl: []token3{{char: STRINGLITERAL}}} | |||
|  | 	r.fileMacro = Macro{repl: []token3{{char: STRINGLITERAL}}} | |||
|  | 	r.lineMacro = Macro{repl: []token3{{char: PPNUMBER}}} | |||
|  | 	r.macros = map[StringID]*Macro{ | |||
|  | 		idCOUNTER: &r.counterMacro, | |||
|  | 		idDATE:    &r.dateMacro, | |||
|  | 		idFILE:    &r.fileMacro, | |||
|  | 		idLINE:    &r.lineMacro, | |||
|  | 		idTIME:    &r.timeMacro, | |||
|  | 	} | |||
|  | 	t := time.Now() | |||
|  | 	// This macro expands to a string constant that describes the date on which the | |||
|  | 	// preprocessor is being run. The string constant contains eleven characters | |||
|  | 	// and looks like "Feb 12 1996". If the day of the month is less than 10, it is | |||
|  | 	// padded with a space on the left. | |||
|  | 	r.dateMacro.repl[0].value = dict.sid(t.Format("\"Jan _2 2006\"")) | |||
|  | 	// This macro expands to a string constant that describes the time at which the | |||
|  | 	// preprocessor is being run. The string constant contains eight characters and | |||
|  | 	// looks like "23:59:01". | |||
|  | 	r.timeMacro.repl[0].value = dict.sid(t.Format("\"15:04:05\"")) | |||
|  | 	return r | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) cppToks(toks []token3) (r []cppToken) { | |||
|  | 	r = make([]cppToken, len(toks)) | |||
|  | 	for i, v := range toks { | |||
|  | 		r[i].token4.token3 = v | |||
|  | 		r[i].token4.file = c.file | |||
|  | 	} | |||
|  | 	return r | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) err(n node, msg string, args ...interface{}) (stop bool) { | |||
|  | 	var position token.Position | |||
|  | 	switch x := n.(type) { | |||
|  | 	case nil: | |||
|  | 	case token4: | |||
|  | 		position = x.Position() | |||
|  | 	default: | |||
|  | 		if p := n.Pos(); p.IsValid() { | |||
|  | 			position = c.file.PositionFor(p, true) | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return c.ctx.err(position, msg, args...) | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) read() (cppToken, bool) { | |||
|  | 	if len(c.ungetBuf) != 0 { | |||
|  | 		return c.ungetBuf.read(), true | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if len(c.inBuf) == 0 { | |||
|  | 		if c.seenEOF { | |||
|  | 			return cppToken{}, false | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if c.nonFirstRead { | |||
|  | 			c.rq <- struct{}{} | |||
|  | 		} | |||
|  | 		c.nonFirstRead = true | |||
|  | 
 | |||
|  | 		var ok bool | |||
|  | 		if c.inBuf, ok = <-c.in; !ok { | |||
|  | 			c.seenEOF = true | |||
|  | 			return cppToken{}, false | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	tok := c.inBuf[0] | |||
|  | 	c.inBuf = c.inBuf[1:] | |||
|  | 	return cppToken{token4{token3: tok, file: c.file}, nil}, true | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) write(tok cppToken) { | |||
|  | 	if tok.char == ' ' && c.last == ' ' { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if c.ctx.cfg.PreprocessOnly { | |||
|  | 		switch { | |||
|  | 		case | |||
|  | 			//TODO cover ALL the bad combinations | |||
|  | 			c.last == '+' && tok.char == '+', | |||
|  | 			c.last == '+' && tok.char == INC, | |||
|  | 			c.last == '-' && tok.char == '-', | |||
|  | 			c.last == '-' && tok.char == DEC, | |||
|  | 			c.last == IDENTIFIER && tok.char == IDENTIFIER, | |||
|  | 			c.last == PPNUMBER && tok.char == '+', //TODO not when ends in a digit | |||
|  | 			c.last == PPNUMBER && tok.char == '-': //TODO not when ends in a digit | |||
|  | 
 | |||
|  | 			sp := tok | |||
|  | 			sp.char = ' ' | |||
|  | 			sp.value = idSpace | |||
|  | 			*c.outBuf = append(*c.outBuf, sp.token4) | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	//dbg("%T.write %q", c, tok) | |||
|  | 	c.last = tok.char | |||
|  | 	switch { | |||
|  | 	case c.inPragmaOp: | |||
|  | 	out: | |||
|  | 		switch tok.char { | |||
|  | 		case ')': | |||
|  | 			c.inPragmaOp = false | |||
|  | 			b := c.pragmaOpBuf | |||
|  | 			if len(b) == 0 || b[0].char != '(' { | |||
|  | 				c.err(b[0], "expected (") | |||
|  | 				break | |||
|  | 			} | |||
|  | 
 | |||
|  | 			var a []string | |||
|  | 			for _, v := range b[1:] { | |||
|  | 				if v.char != STRINGLITERAL { | |||
|  | 					c.err(v, "expected string literal") | |||
|  | 					break out | |||
|  | 				} | |||
|  | 
 | |||
|  | 				a = append(a, v.String()) | |||
|  | 			} | |||
|  | 
 | |||
|  | 			if len(a) == 0 { | |||
|  | 				break | |||
|  | 			} | |||
|  | 
 | |||
|  | 			for i, v := range a { | |||
|  | 				// [0], 6.10.9, 1 | |||
|  | 				if v[0] == 'L' { | |||
|  | 					v = v[1:] | |||
|  | 				} | |||
|  | 				v = v[1 : len(v)-1] | |||
|  | 				v = strings.ReplaceAll(v, `\"`, `"`) | |||
|  | 				a[i] = "#pragma " + strings.ReplaceAll(v, `\\`, `\`) + "\n" | |||
|  | 			} | |||
|  | 			src := strings.Join(a, "") | |||
|  | 			s := newScanner0(c.ctx, strings.NewReader(src), tokenNewFile("", len(src)), 4096) | |||
|  | 			if ppf := s.translationPhase3(); ppf != nil { | |||
|  | 				ppf.translationPhase4(c) | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			c.pragmaOpBuf = append(c.pragmaOpBuf, tok.token4) | |||
|  | 		} | |||
|  | 	default: | |||
|  | 		switch { | |||
|  | 		case tok.char == '\n': | |||
|  | 			*c.outBuf = append(*c.outBuf, tok.token4) | |||
|  | 			c.out <- c.outBuf | |||
|  | 			b := token4Pool.Get().(*[]token4) | |||
|  | 			*b = (*b)[:0] | |||
|  | 			c.outBuf = b | |||
|  | 		case tok.char == IDENTIFIER && tok.value == idPragmaOp: | |||
|  | 			if len(*c.outBuf) != 0 { | |||
|  | 				tok.char = '\n' | |||
|  | 				tok.value = 0 | |||
|  | 				*c.outBuf = append(*c.outBuf, tok.token4) | |||
|  | 				c.out <- c.outBuf | |||
|  | 				b := token4Pool.Get().(*[]token4) | |||
|  | 				*b = (*b)[:0] | |||
|  | 				c.outBuf = b | |||
|  | 			} | |||
|  | 			c.inPragmaOp = true | |||
|  | 			c.pragmaOpBuf = c.pragmaOpBuf[:0] | |||
|  | 		default: | |||
|  | 			*c.outBuf = append(*c.outBuf, tok.token4) | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | func ltrim4(toks []token4) []token4 { | |||
|  | 	for len(toks) != 0 && toks[0].char == ' ' { | |||
|  | 		toks = toks[1:] | |||
|  | 	} | |||
|  | 	return toks | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) writes(toks []cppToken) { | |||
|  | 	for _, v := range toks { | |||
|  | 		c.write(v) | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [1]pg 1. | |||
|  | // | |||
|  | // expand(TS) /* recur, substitute, pushback, rescan */ | |||
|  | // { | |||
|  | // 	if TS is {} then | |||
|  | //		// ---------------------------------------------------------- A | |||
|  | // 		return {}; | |||
|  | // | |||
|  | // 	else if TS is T^HS • TS’ and T is in HS then | |||
|  | //		//----------------------------------------------------------- B | |||
|  | // 		return T^HS • expand(TS’); | |||
|  | // | |||
|  | // 	else if TS is T^HS • TS’ and T is a "()-less macro" then | |||
|  | //		// ---------------------------------------------------------- C | |||
|  | // 		return expand(subst(ts(T), {}, {}, HS \cup {T}, {}) • TS’ ); | |||
|  | // | |||
|  | // 	else if TS is T^HS •(•TS’ and T is a "()’d macro" then | |||
|  | //		// ---------------------------------------------------------- D | |||
|  | // 		check TS’ is actuals • )^HS’ • TS’’ and actuals are "correct for T" | |||
|  | // 		return expand(subst(ts(T), fp(T), actuals,(HS \cap HS’) \cup {T }, {}) • TS’’); | |||
|  | // | |||
|  | //	// ------------------------------------------------------------------ E | |||
|  | // 	note TS must be T^HS • TS’ | |||
|  | // 	return T^HS • expand(TS’); | |||
|  | // } | |||
|  | func (c *cpp) expand(ts tokenReader, w tokenWriter, expandDefined bool) { | |||
|  | 	// trc("==== expand enter") | |||
|  | start: | |||
|  | 	tok, ok := ts.read() | |||
|  | 	tok.file = c.file | |||
|  | 	// First, if TS is the empty set, the result is the empty set. | |||
|  | 	if !ok { | |||
|  | 		// ---------------------------------------------------------- A | |||
|  | 		// return {}; | |||
|  | 		// trc("---- expand A") | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// dbg("expand start %q", tok) | |||
|  | 	if tok.char == IDENTIFIER { | |||
|  | 		nm := tok.value | |||
|  | 		if nm == idDefined && expandDefined { | |||
|  | 			c.parseDefined(tok, ts, w) | |||
|  | 			goto start | |||
|  | 		} | |||
|  | 
 | |||
|  | 		// Otherwise, if the token sequence begins with a token whose | |||
|  | 		// hide set contains that token, then the result is the token | |||
|  | 		// sequence beginning with that token (including its hide set) | |||
|  | 		// followed by the result of expand on the rest of the token | |||
|  | 		// sequence. | |||
|  | 		if tok.has(nm) { | |||
|  | 			// -------------------------------------------------- B | |||
|  | 			// return T^HS • expand(TS’); | |||
|  | 			// trc("---- expand B") | |||
|  | 			// trc("expand write %q", tok) | |||
|  | 			w.write(tok) | |||
|  | 			goto start | |||
|  | 		} | |||
|  | 
 | |||
|  | 		m := c.macros[nm] | |||
|  | 		if m != nil && !m.isFnLike { | |||
|  | 			// Otherwise, if the token sequence begins with an | |||
|  | 			// object-like macro, the result is the expansion of | |||
|  | 			// the rest of the token sequence beginning with the | |||
|  | 			// sequence returned by subst invoked with the | |||
|  | 			// replacement token sequence for the macro, two empty | |||
|  | 			// sets, the union of the macro’s hide set and the | |||
|  | 			// macro itself, and an empty set. | |||
|  | 			switch nm { | |||
|  | 			case idLINE: | |||
|  | 				c.lineMacro.repl[0].value = dict.sid(fmt.Sprint(tok.Position().Line)) | |||
|  | 			case idCOUNTER: | |||
|  | 				c.counterMacro.repl[0].value = dict.sid(fmt.Sprint(c.counter)) | |||
|  | 				c.counter++ | |||
|  | 			case idTclDefaultDoubleRounding: | |||
|  | 				if c.ctx.cfg.ReplaceMacroTclDefaultDoubleRounding != "" { | |||
|  | 					m = c.macros[dict.sid(c.ctx.cfg.ReplaceMacroTclDefaultDoubleRounding)] | |||
|  | 				} | |||
|  | 			case idTclIeeeDoubleRounding: | |||
|  | 				if c.ctx.cfg.ReplaceMacroTclIeeeDoubleRounding != "" { | |||
|  | 					m = c.macros[dict.sid(c.ctx.cfg.ReplaceMacroTclIeeeDoubleRounding)] | |||
|  | 				} | |||
|  | 			} | |||
|  | 			if m != nil { | |||
|  | 				// -------------------------------------------------- C | |||
|  | 				// return expand(subst(ts(T), {}, {}, HS \cup {T}, {}) • TS’ ); | |||
|  | 				// trc("---- expand C") | |||
|  | 				hs := hideSet{nm: {}} | |||
|  | 				for k, v := range tok.hs { | |||
|  | 					hs[k] = v | |||
|  | 				} | |||
|  | 				os := cppTokensPool.Get().(*[]cppToken) | |||
|  | 				toks := c.subst(m, c.cppToks(m.repl), nil, nil, nil, hs, os, expandDefined) | |||
|  | 				for i := range toks { | |||
|  | 					toks[i].pos = tok.pos | |||
|  | 				} | |||
|  | 				if len(toks) == 1 { | |||
|  | 					toks[0].macro = nm | |||
|  | 				} | |||
|  | 				ts.ungets(toks) | |||
|  | 				(*os) = (*os)[:0] | |||
|  | 				cppTokensPool.Put(os) | |||
|  | 				goto start | |||
|  | 			} | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if m != nil && m.isFnLike { | |||
|  | 			switch nm { | |||
|  | 			case idFdZero: | |||
|  | 				if c.ctx.cfg.ReplaceMacroFdZero != "" { | |||
|  | 					m = c.macros[dict.sid(c.ctx.cfg.ReplaceMacroFdZero)] | |||
|  | 				} | |||
|  | 			} | |||
|  | 			if m != nil { | |||
|  | 				// -------------------------------------------------- D | |||
|  | 				// check TS’ is actuals • )^HS’ • TS’’ and actuals are "correct for T" | |||
|  | 				// return expand(subst(ts(T), fp(T), actuals,(HS \cap HS’) \cup {T }, {}) • TS’’); | |||
|  | 				// trc("---- expand D") | |||
|  | 				hs := tok.hs | |||
|  | 				var skip []cppToken | |||
|  | 			again: | |||
|  | 				t2, ok := ts.read() | |||
|  | 				if !ok { | |||
|  | 					// dbg("expand write %q", tok) | |||
|  | 					w.write(tok) | |||
|  | 					ts.ungets(skip) | |||
|  | 					goto start | |||
|  | 				} | |||
|  | 
 | |||
|  | 				skip = append(skip, t2) | |||
|  | 				switch t2.char { | |||
|  | 				case '\n', ' ': | |||
|  | 					goto again | |||
|  | 				case '(': | |||
|  | 					// ok | |||
|  | 				default: | |||
|  | 					w.write(tok) | |||
|  | 					ts.ungets(skip) | |||
|  | 					goto start | |||
|  | 				} | |||
|  | 
 | |||
|  | 				varArgs, ap, hs2 := c.actuals(m, ts) | |||
|  | 				if nm == idHasIncludeImpl { //TODO- | |||
|  | 					if len(ap) != 1 || len(ap[0]) != 1 { | |||
|  | 						panic(todo("internal error")) | |||
|  | 					} | |||
|  | 
 | |||
|  | 					arg := ap[0][0].value.String() | |||
|  | 					switch { | |||
|  | 					case strings.HasPrefix(arg, `"\"`): // `"\"stdio.h\""` | |||
|  | 						arg = arg[2:len(arg)-3] + `"` // -> `"stdio.h"` | |||
|  | 					case strings.HasPrefix(arg, `"<`): // `"<stdio.h>"` | |||
|  | 						arg = arg[1 : len(arg)-1] // -> `<stdio.h>` | |||
|  | 					default: | |||
|  | 						arg = "" | |||
|  | 					} | |||
|  | 					var tok3 token3 | |||
|  | 					tok3.char = PPNUMBER | |||
|  | 					tok3.value = idZero | |||
|  | 					if arg != "" { | |||
|  | 						if _, err := c.hasInclude(&tok, arg); err == nil { | |||
|  | 							tok3.value = idOne | |||
|  | 						} | |||
|  | 					} | |||
|  | 					tok := cppToken{token4{token3: tok3, file: c.file}, nil} | |||
|  | 					ts.ungets([]cppToken{tok}) | |||
|  | 					goto start | |||
|  | 				} | |||
|  | 
 | |||
|  | 				switch { | |||
|  | 				case len(hs2) == 0: | |||
|  | 					hs2 = hideSet{nm: {}} | |||
|  | 				default: | |||
|  | 					nhs := hideSet{} | |||
|  | 					for k := range hs { | |||
|  | 						if _, ok := hs2[k]; ok { | |||
|  | 							nhs[k] = struct{}{} | |||
|  | 						} | |||
|  | 					} | |||
|  | 					nhs[nm] = struct{}{} | |||
|  | 					hs2 = nhs | |||
|  | 				} | |||
|  | 				os := cppTokensPool.Get().(*[]cppToken) | |||
|  | 				toks := c.subst(m, c.cppToks(m.repl), m.fp, varArgs, ap, hs2, os, expandDefined) | |||
|  | 				for i := range toks { | |||
|  | 					toks[i].pos = tok.pos | |||
|  | 				} | |||
|  | 				ts.ungets(toks) | |||
|  | 				(*os) = (*os)[:0] | |||
|  | 				cppTokensPool.Put(os) | |||
|  | 				goto start | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// ------------------------------------------------------------------ E | |||
|  | 	// note TS must be T^HS • TS’ | |||
|  | 	// return T^HS • expand(TS’); | |||
|  | 	// trc("---- expand E") | |||
|  | 	// trc("expand write %q", tok) | |||
|  | 	w.write(tok) | |||
|  | 	goto start | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) hasInclude(n Node, nm string) (rs string, err error) { | |||
|  | 	// nm0 := nm | |||
|  | 	// defer func() { //TODO- | |||
|  | 	// 	trc("nm0 %q nm %q rs %q err %v", nm0, nm, rs, err) | |||
|  | 	// }() | |||
|  | 	var ( | |||
|  | 		b     byte | |||
|  | 		paths []string | |||
|  | 		sys   bool | |||
|  | 	) | |||
|  | 	switch { | |||
|  | 	case nm != "" && nm[0] == '"': | |||
|  | 		paths = c.ctx.includePaths | |||
|  | 		b = '"' | |||
|  | 	case nm != "" && nm[0] == '<': | |||
|  | 		paths = c.ctx.sysIncludePaths | |||
|  | 		sys = true | |||
|  | 		b = '>' | |||
|  | 	case nm == "": | |||
|  | 		return "", fmt.Errorf("%v: invalid empty include argument", n.Position()) | |||
|  | 	default: | |||
|  | 		return "", fmt.Errorf("%v: invalid include argument %s", n.Position(), nm) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	x := strings.IndexByte(nm[1:], b) | |||
|  | 	if x < 0 { | |||
|  | 		return "", fmt.Errorf("%v: invalid include argument %s", n.Position(), nm) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	nm = filepath.FromSlash(nm[1 : x+1]) | |||
|  | 	switch { | |||
|  | 	case filepath.IsAbs(nm): | |||
|  | 		fi, err := c.ctx.statFile(nm, sys) | |||
|  | 		if err != nil { | |||
|  | 			return "", fmt.Errorf("%v: %s", n.Position(), err) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if fi.IsDir() { | |||
|  | 			return "", fmt.Errorf("%v: %s is a directory, not a file", n.Position(), nm) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		return nm, nil | |||
|  | 	default: | |||
|  | 		dir := filepath.Dir(c.file.Name()) | |||
|  | 		for _, v := range paths { | |||
|  | 			if v == "@" { | |||
|  | 				v = dir | |||
|  | 			} | |||
|  | 
 | |||
|  | 			var p string | |||
|  | 			switch { | |||
|  | 			case strings.HasPrefix(nm, "./"): | |||
|  | 				wd := c.ctx.cfg.WorkingDir | |||
|  | 				if wd == "" { | |||
|  | 					var err error | |||
|  | 					if wd, err = os.Getwd(); err != nil { | |||
|  | 						return "", fmt.Errorf("%v: cannot determine working dir: %v", n.Position(), err) | |||
|  | 					} | |||
|  | 				} | |||
|  | 				p = filepath.Join(wd, nm) | |||
|  | 			default: | |||
|  | 				p = filepath.Join(v, nm) | |||
|  | 			} | |||
|  | 			fi, err := c.ctx.statFile(p, sys) | |||
|  | 			if err != nil || fi.IsDir() { | |||
|  | 				continue | |||
|  | 			} | |||
|  | 
 | |||
|  | 			return p, nil | |||
|  | 		} | |||
|  | 		wd, _ := os.Getwd() | |||
|  | 		return "", fmt.Errorf("include file not found: %s (wd %s)\nsearch paths:\n\t%s", nm, wd, strings.Join(paths, "\n\t")) | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) actuals(m *Macro, r tokenReader) (varArgs []cppToken, ap [][]cppToken, hs hideSet) { | |||
|  | 	var lvl, n int | |||
|  | 	varx := len(m.fp) | |||
|  | 	if m.namedVariadic { | |||
|  | 		varx-- | |||
|  | 	} | |||
|  | 	var last rune | |||
|  | 	for { | |||
|  | 		t, ok := r.read() | |||
|  | 		if !ok { | |||
|  | 			c.err(t, "unexpected EOF") | |||
|  | 			return nil, nil, nil | |||
|  | 		} | |||
|  | 
 | |||
|  | 		// 6.10.3, 10 | |||
|  | 		// | |||
|  | 		// Within the sequence of preprocessing tokens making up an | |||
|  | 		// invocation of a function-like macro, new-line is considered | |||
|  | 		// a normal white-space character. | |||
|  | 		if t.char == '\n' { | |||
|  | 			t.char = ' ' | |||
|  | 			t.value = idSpace | |||
|  | 		} | |||
|  | 		if t.char == ' ' && last == ' ' { | |||
|  | 			continue | |||
|  | 		} | |||
|  | 
 | |||
|  | 		last = t.char | |||
|  | 		switch t.char { | |||
|  | 		case ',': | |||
|  | 			if lvl == 0 { | |||
|  | 				if n >= varx && (len(varArgs) != 0 || !isWhite(t.char)) { | |||
|  | 					varArgs = append(varArgs, t) | |||
|  | 				} | |||
|  | 				n++ | |||
|  | 				continue | |||
|  | 			} | |||
|  | 		case ')': | |||
|  | 			if lvl == 0 { | |||
|  | 				for len(ap) < len(m.fp) { | |||
|  | 					ap = append(ap, nil) | |||
|  | 				} | |||
|  | 				for i, v := range ap { | |||
|  | 					ap[i] = c.trim(v) | |||
|  | 				} | |||
|  | 				// for i, v := range ap { | |||
|  | 				// 	dbg("%T.actuals %v/%v %q", c, i, len(ap), tokStr(v, "|")) | |||
|  | 				// } | |||
|  | 				return c.trim(varArgs), ap, t.hs | |||
|  | 			} | |||
|  | 			lvl-- | |||
|  | 		case '(': | |||
|  | 			lvl++ | |||
|  | 		} | |||
|  | 		if n >= varx && (len(varArgs) != 0 || !isWhite(t.char)) { | |||
|  | 			varArgs = append(varArgs, t) | |||
|  | 		} | |||
|  | 		for len(ap) <= n { | |||
|  | 			ap = append(ap, []cppToken{}) | |||
|  | 		} | |||
|  | 		ap[n] = append(ap[n], t) | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [1]pg 2. | |||
|  | // | |||
|  | // subst(IS, FP, AP, HS, OS) /* substitute args, handle stringize and paste */ | |||
|  | // { | |||
|  | // 	if IS is {} then | |||
|  | //		// ---------------------------------------------------------- A | |||
|  | // 		return hsadd(HS, OS); | |||
|  | // | |||
|  | // 	else if IS is # • T • IS’ and T is FP[i] then | |||
|  | //		// ---------------------------------------------------------- B | |||
|  | // 		return subst(IS’, FP, AP, HS, OS • stringize(select(i, AP))); | |||
|  | // | |||
|  | // 	else if IS is ## • T • IS’ and T is FP[i] then | |||
|  | //	{ | |||
|  | //		// ---------------------------------------------------------- C | |||
|  | // 		if select(i, AP) is {} then /* only if actuals can be empty */ | |||
|  | //			// -------------------------------------------------- D | |||
|  | // 			return subst(IS’, FP, AP, HS, OS); | |||
|  | // 		else | |||
|  | //			// -------------------------------------------------- E | |||
|  | // 			return subst(IS’, FP, AP, HS, glue(OS, select(i, AP))); | |||
|  | // 	} | |||
|  | // | |||
|  | // 	else if IS is ## • T^HS’ • IS’ then | |||
|  | //		// ---------------------------------------------------------- F | |||
|  | // 		return subst(IS’, FP, AP, HS, glue(OS, T^HS’)); | |||
|  | // | |||
|  | // 	else if IS is T • ##^HS’ • IS’ and T is FP[i] then | |||
|  | //	{ | |||
|  | //		// ---------------------------------------------------------- G | |||
|  | // 		if select(i, AP) is {} then /* only if actuals can be empty */ | |||
|  | //		{ | |||
|  | //			// -------------------------------------------------- H | |||
|  | // 			if IS’ is T’ • IS’’ and T’ is FP[j] then | |||
|  | //				// ------------------------------------------ I | |||
|  | // 				return subst(IS’’, FP, AP, HS, OS • select(j, AP)); | |||
|  | // 			else | |||
|  | //				// ------------------------------------------ J | |||
|  | // 				return subst(IS’, FP, AP, HS, OS); | |||
|  | // 		} | |||
|  | //		else | |||
|  | //			// -------------------------------------------------- K | |||
|  | // 			return subst(##^HS’ • IS’, FP, AP, HS, OS • select(i, AP)); | |||
|  | // | |||
|  | //	} | |||
|  | // | |||
|  | // 	else if IS is T • IS’ and T is FP[i] then | |||
|  | //		// ---------------------------------------------------------- L | |||
|  | // 		return subst(IS’, FP, AP, HS, OS • expand(select(i, AP))); | |||
|  | // | |||
|  | //	// ------------------------------------------------------------------ M | |||
|  | // 	note IS must be T^HS’ • IS’ | |||
|  | // 	return subst(IS’, FP, AP, HS, OS • T^HS’); | |||
|  | // } | |||
|  | // | |||
|  | // A quick overview of subst is that it walks through the input sequence, IS, | |||
|  | // building up an output sequence, OS, by handling each token from left to | |||
|  | // right. (The order that this operation takes is left to the implementation | |||
|  | // also, walking from left to right is more natural since the rest of the | |||
|  | // algorithm is constrained to this ordering.) Stringizing is easy, pasting | |||
|  | // requires trickier handling because the operation has a bunch of | |||
|  | // combinations. After the entire input sequence is finished, the updated hide | |||
|  | // set is applied to the output sequence, and that is the result of subst. | |||
|  | func (c *cpp) subst(m *Macro, is []cppToken, fp []StringID, varArgs []cppToken, ap [][]cppToken, hs hideSet, os *[]cppToken, expandDefined bool) (r []cppToken) { | |||
|  | 	var ap0 [][]cppToken | |||
|  | 	for _, v := range ap { | |||
|  | 		ap0 = append(ap0, append([]cppToken(nil), v...)) | |||
|  | 	} | |||
|  | 	// trc("==== subst: is %q, fp, %v ap@%p %v", cppToksStr(is, "|"), fp, &ap, cppToksStr2(ap)) | |||
|  | start: | |||
|  | 	// trc("start: is %q, fp %v, ap@%p %v, os %q", cppToksStr(is, "|"), fp, &ap, cppToksStr2(ap), cppToksStr(*os, "|")) | |||
|  | 	if len(is) == 0 { | |||
|  | 		// ---------------------------------------------------------- A | |||
|  | 		// return hsadd(HS, OS); | |||
|  | 		// trc("---- A") | |||
|  | 		// trc("subst RETURNS %q", cppToksStr(*os, "|")) | |||
|  | 		return c.hsAdd(hs, os) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	tok := is[0] | |||
|  | 	var arg []cppToken | |||
|  | 	if tok.char == '#' { | |||
|  | 		if len(is) > 1 && is[1].char == IDENTIFIER && m.param(varArgs, ap0, is[1].value, &arg) { | |||
|  | 			// -------------------------------------------------- B | |||
|  | 			// return subst(IS’, FP, AP, HS, OS • stringize(select(i, AP))); | |||
|  | 			// trc("---- subst B") | |||
|  | 			*os = append(*os, c.stringize(arg)) | |||
|  | 			is = is[2:] | |||
|  | 			goto start | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if tok.char == PPPASTE { | |||
|  | 		if len(is) > 1 && is[1].char == IDENTIFIER && m.param(varArgs, ap0, is[1].value, &arg) { | |||
|  | 			// -------------------------------------------------- C | |||
|  | 			// trc("---- subst C") | |||
|  | 			if len(arg) == 0 { | |||
|  | 				// TODO "only if actuals can be empty" | |||
|  | 				// ------------------------------------------ D | |||
|  | 				// return subst(IS’, FP, AP, HS, OS); | |||
|  | 				// trc("---- D") | |||
|  | 				if c := len(*os); c != 0 && (*os)[c-1].char == ',' { | |||
|  | 					*os = (*os)[:c-1] | |||
|  | 				} | |||
|  | 				is = is[2:] | |||
|  | 				goto start | |||
|  | 			} | |||
|  | 
 | |||
|  | 			// -------------------------------------------------- E | |||
|  | 			// return subst(IS’, FP, AP, HS, glue(OS, select(i, AP))); | |||
|  | 			// trc("---- subst E, arg %q", cppToksStr(arg, "|")) | |||
|  | 			*os = c.glue(*os, arg) | |||
|  | 			is = is[2:] | |||
|  | 			goto start | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if len(is) > 1 { | |||
|  | 			// -------------------------------------------------- F | |||
|  | 			// return subst(IS’, FP, AP, HS, glue(OS, T^HS’)); | |||
|  | 			// trc("---- subst F") | |||
|  | 			*os = c.glue(*os, is[1:2]) | |||
|  | 			is = is[2:] | |||
|  | 			goto start | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if tok.char == IDENTIFIER && (len(is) > 1 && is[1].char == PPPASTE) && m.param(varArgs, ap0, tok.value, &arg) { | |||
|  | 		// ---------------------------------------------------------- G | |||
|  | 		// trc("---- subst G") | |||
|  | 		if len(arg) == 0 { | |||
|  | 			// TODO "only if actuals can be empty" | |||
|  | 			// -------------------------------------------------- H | |||
|  | 			// trc("---- subst H") | |||
|  | 			is = is[2:] // skip T## | |||
|  | 			if len(is) > 0 && is[0].char == IDENTIFIER && m.param(varArgs, ap, is[0].value, &arg) { | |||
|  | 				// -------------------------------------------------- I | |||
|  | 				// return subst(IS’’, FP, AP, HS, OS • select(j, AP)); | |||
|  | 				// trc("---- subst I") | |||
|  | 				*os = append(*os, arg...) | |||
|  | 				is = is[1:] | |||
|  | 				goto start | |||
|  | 			} else { | |||
|  | 				// -------------------------------------------------- J | |||
|  | 				// return subst(IS’, FP, AP, HS, OS); | |||
|  | 				// trc("---- subst J") | |||
|  | 				goto start | |||
|  | 			} | |||
|  | 		} | |||
|  | 
 | |||
|  | 		// ---------------------------------------------------------- K | |||
|  | 		// return subst(##^HS’ • IS’, FP, AP, HS, OS • select(i, AP)); | |||
|  | 		// trc("---- subst K") | |||
|  | 		*os = append(*os, arg...) | |||
|  | 		is = is[1:] | |||
|  | 		goto start | |||
|  | 	} | |||
|  | 
 | |||
|  | 	ax := -1 | |||
|  | 	if tok.char == IDENTIFIER && m.param2(varArgs, ap, tok.value, &arg, &ax) { | |||
|  | 		// ------------------------------------------ L | |||
|  | 		// return subst(IS’, FP, AP, HS, OS • expand(select(i, AP))); | |||
|  | 		// trc("---- subst L") | |||
|  | 		// if toks, ok := cache[tok.value]; ok { | |||
|  | 		// 	os = append(os, toks...) | |||
|  | 		// 	is = is[1:] | |||
|  | 		// 	goto start | |||
|  | 		// } | |||
|  | 
 | |||
|  | 		sel := cppReader{buf: arg} | |||
|  | 		var w cppWriter | |||
|  | 		// trc("---- L(1) ap@%p %v", &ap, cppToksStr2(ap)) | |||
|  | 		c.expand(&sel, &w, expandDefined) | |||
|  | 		// trc("---- L(2) ap@%p %v", &ap, cppToksStr2(ap)) | |||
|  | 		*os = append(*os, w.toks...) | |||
|  | 		if ax >= 0 { | |||
|  | 			ap[ax] = w.toks | |||
|  | 		} | |||
|  | 		is = is[1:] | |||
|  | 		goto start | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// ------------------------------------------------------------------ M | |||
|  | 	// note IS must be T^HS’ • IS’ | |||
|  | 	// return subst(IS’, FP, AP, HS, OS • T^HS’); | |||
|  | 	*os = append(*os, tok) | |||
|  | 	is = is[1:] | |||
|  | 	// trc("---- subst M: is %q, os %q", cppToksStr(is, "|"), cppToksStr(*os, "|")) | |||
|  | 	goto start | |||
|  | } | |||
|  | 
 | |||
|  | // paste last of left side with first of right side | |||
|  | // | |||
|  | // [1] pg. 3 | |||
|  | // | |||
|  | //TODO implement properly [0], 6.10.3.3, 2. Must rescan the resulting token(s). | |||
|  | // | |||
|  | // $ cat main.c | |||
|  | // #include <stdio.h> | |||
|  | // | |||
|  | // #define foo(a, b) a ## b | |||
|  | // | |||
|  | // int main() { | |||
|  | // 	int i = 42; | |||
|  | // 	i foo(+, +); | |||
|  | // 	printf("%i\n", i); | |||
|  | // 	return 0; | |||
|  | // } | |||
|  | // $ rm -f a.out ; gcc -Wall main.c && ./a.out ; echo $? | |||
|  | // 43 | |||
|  | // 0 | |||
|  | // $ | |||
|  | // | |||
|  | // ---------------------------------------------------------------------------- | |||
|  | //	glue(LS,RS ) /* paste last of left side with first of right side */ | |||
|  | //	{ | |||
|  | //		if LS is L^HS and RS is R^HS’ • RS’ then | |||
|  | //			return L&R^(HS∩HS’) • RS’;	/* undefined if L&R is invalid */ | |||
|  | 
 | |||
|  | //		// note LS must be L HS • LS’ | |||
|  | //		return L^HS • glue(LS’,RS ); | |||
|  | //	} | |||
|  | func (c *cpp) glue(ls, rs []cppToken) (out []cppToken) { | |||
|  | 	// trc("ls %q, rs %q", cppToksStr(ls, "|"), cppToksStr(rs, "|")) | |||
|  | 	if len(rs) == 0 { | |||
|  | 		return ls | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if len(ls) == 0 { | |||
|  | 		return rs | |||
|  | 	} | |||
|  | 
 | |||
|  | 	l := ls[len(ls)-1] | |||
|  | 	ls = ls[:len(ls)-1] | |||
|  | 	r := rs[0] | |||
|  | 	rs = rs[1:] | |||
|  | 
 | |||
|  | 	if l.char == IDENTIFIER && l.value == idL && r.char == STRINGLITERAL { | |||
|  | 		l.char = LONGSTRINGLITERAL | |||
|  | 	} | |||
|  | 	l.value = dict.sid(l.String() + r.String()) | |||
|  | 	return append(append(ls, l), rs...) | |||
|  | } | |||
|  | 
 | |||
|  | // Given a token sequence, stringize returns a single string literal token | |||
|  | // containing the concatenated spellings of the tokens. | |||
|  | // | |||
|  | // [1] pg. 3 | |||
|  | func (c *cpp) stringize(s0 []cppToken) (r cppToken) { | |||
|  | 	// 6.10.3.2 | |||
|  | 	// | |||
|  | 	// Each occurrence of white space between the argument’s preprocessing | |||
|  | 	// tokens becomes a single space character in the character string | |||
|  | 	// literal. | |||
|  | 	s := make([]cppToken, 0, len(s0)) | |||
|  | 	var last rune | |||
|  | 	for i := range s0 { | |||
|  | 		t := s0[i] | |||
|  | 		if isWhite(t.char) { | |||
|  | 			t.char = ' ' | |||
|  | 			t.value = idSpace | |||
|  | 			if last == ' ' { | |||
|  | 				continue | |||
|  | 			} | |||
|  | 		} | |||
|  | 
 | |||
|  | 		last = t.char | |||
|  | 		s = append(s, t) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// White space before the first preprocessing token and after the last | |||
|  | 	// preprocessing token composing the argument is deleted. | |||
|  | 	s = c.trim(s) | |||
|  | 
 | |||
|  | 	// The character string literal corresponding to an empty argument is | |||
|  | 	// "" | |||
|  | 	if len(s) == 0 { | |||
|  | 		r.hs = nil | |||
|  | 		r.char = STRINGLITERAL | |||
|  | 		r.value = idEmptyString | |||
|  | 		return r | |||
|  | 	} | |||
|  | 
 | |||
|  | 	var a []string | |||
|  | 	// Otherwise, the original spelling of each preprocessing token in the | |||
|  | 	// argument is retained in the character string literal, except for | |||
|  | 	// special handling for producing the spelling of string literals and | |||
|  | 	// character constants: a \ character is inserted before each " and \ | |||
|  | 	// character of a character constant or string literal (including the | |||
|  | 	// delimiting " characters), except that it is implementation-defined | |||
|  | 	// whether a \ character is inserted before the \ character beginning a | |||
|  | 	// universal character name. | |||
|  | 	for _, v := range s { | |||
|  | 		s := v.String() | |||
|  | 		switch v.char { | |||
|  | 		case CHARCONST, STRINGLITERAL: | |||
|  | 			s = strings.ReplaceAll(s, `\`, `\\`) | |||
|  | 			s = strings.ReplaceAll(s, `"`, `\"`) | |||
|  | 		case LONGCHARCONST, LONGSTRINGLITERAL: | |||
|  | 			panic("TODO") | |||
|  | 		} | |||
|  | 		a = append(a, s) | |||
|  | 	} | |||
|  | 	r = s[0] | |||
|  | 	r.hs = nil | |||
|  | 	r.char = STRINGLITERAL | |||
|  | 	r.value = dict.sid(`"` + strings.Join(a, "") + `"`) | |||
|  | 	return r | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) trim(toks []cppToken) []cppToken { | |||
|  | 	for len(toks) != 0 && isWhite(toks[0].char) { | |||
|  | 		toks = toks[1:] | |||
|  | 	} | |||
|  | 	for len(toks) != 0 && isWhite(toks[len(toks)-1].char) { | |||
|  | 		toks = toks[:len(toks)-1] | |||
|  | 	} | |||
|  | 	return toks | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) hsAdd(hs hideSet, toks *[]cppToken) []cppToken { | |||
|  | 	for i, v := range *toks { | |||
|  | 		if v.hs == nil { | |||
|  | 			v.hs = hideSet{} | |||
|  | 		} | |||
|  | 		for k, w := range hs { | |||
|  | 			v.hs[k] = w | |||
|  | 		} | |||
|  | 		v.file = c.file | |||
|  | 		(*toks)[i] = v | |||
|  | 	} | |||
|  | 	return *toks | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) parseDefined(tok cppToken, r tokenReader, w tokenWriter) { | |||
|  | 	toks := []cppToken{tok} | |||
|  | 	if tok = c.scanToNonBlankToken(&toks, r, w); tok.char < 0 { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	switch tok.char { | |||
|  | 	case IDENTIFIER: | |||
|  | 		// ok | |||
|  | 	case '(': | |||
|  | 		if tok = c.scanToNonBlankToken(&toks, r, w); tok.char < 0 { | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if tok.char != IDENTIFIER { | |||
|  | 			w.writes(toks) | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		tok2 := c.scanToNonBlankToken(&toks, r, w) | |||
|  | 		if tok2.char < 0 { | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if tok2.char != ')' { | |||
|  | 			w.writes(toks) | |||
|  | 			return | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	tok.char = PPNUMBER | |||
|  | 	switch _, ok := c.macros[tok.value]; { | |||
|  | 	case ok: | |||
|  | 		tok.value = idOne | |||
|  | 	default: | |||
|  | 		tok.value = idZero | |||
|  | 	} | |||
|  | 	w.write(tok) | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) scanToNonBlankToken(toks *[]cppToken, r tokenReader, w tokenWriter) cppToken { | |||
|  | 	tok, ok := r.read() | |||
|  | 	if !ok { | |||
|  | 		w.writes(*toks) | |||
|  | 		tok.char = -1 | |||
|  | 		return tok | |||
|  | 	} | |||
|  | 
 | |||
|  | 	*toks = append(*toks, tok) | |||
|  | 	if tok.char == ' ' || tok.char == '\n' { | |||
|  | 		if tok, ok = r.read(); !ok { | |||
|  | 			w.writes(*toks) | |||
|  | 			tok.char = -1 | |||
|  | 			return tok | |||
|  | 		} | |||
|  | 
 | |||
|  | 		*toks = append(*toks, tok) | |||
|  | 	} | |||
|  | 	return (*toks)[len(*toks)-1] | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.10.1 | |||
|  | func (c *cpp) evalInclusionCondition(expr []token3) (r bool) { | |||
|  | 	if !c.intmaxChecked { | |||
|  | 		if m := c.macros[idIntMaxWidth]; m != nil && len(m.repl) != 0 { | |||
|  | 			if val := c.intMaxWidth(); val != 0 && val != 64 { | |||
|  | 				c.err(m.name, "%s is %v, but only 64 is supported", idIntMaxWidth, val) | |||
|  | 			} | |||
|  | 		} | |||
|  | 		c.intmaxChecked = true | |||
|  | 	} | |||
|  | 
 | |||
|  | 	val := c.eval(expr) | |||
|  | 	return val != nil && c.isNonZero(val) | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) intMaxWidth() int64 { | |||
|  | 	if m := c.macros[idIntMaxWidth]; m != nil && len(m.repl) != 0 { | |||
|  | 		switch x := c.eval(m.repl).(type) { | |||
|  | 		case nil: | |||
|  | 			return 0 | |||
|  | 		case int64: | |||
|  | 			return x | |||
|  | 		case uint64: | |||
|  | 			return int64(x) | |||
|  | 		default: | |||
|  | 			panic(internalError()) | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return 0 | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) eval(expr []token3) interface{} { | |||
|  | 	toks := make([]cppToken, len(expr)) | |||
|  | 	for i, v := range expr { | |||
|  | 		toks[i] = cppToken{token4{token3: v}, nil} | |||
|  | 	} | |||
|  | 	var w cppWriter | |||
|  | 	c.expand(&cppReader{buf: toks}, &w, true) | |||
|  | 	toks = w.toks | |||
|  | 	p := 0 | |||
|  | 	for _, v := range toks { | |||
|  | 		switch v.char { | |||
|  | 		case ' ', '\n': | |||
|  | 			// nop | |||
|  | 		default: | |||
|  | 			toks[p] = v | |||
|  | 			p++ | |||
|  | 		} | |||
|  | 	} | |||
|  | 	toks = toks[:p] | |||
|  | 	s := cppScanner(toks) | |||
|  | 	val := c.expression(&s, true) | |||
|  | 	switch s.peek().char { | |||
|  | 	case -1, '#': | |||
|  | 		// ok | |||
|  | 	default: | |||
|  | 		t := s.peek() | |||
|  | 		c.err(t, "unexpected %s", tokName(t.char)) | |||
|  | 		return nil | |||
|  | 	} | |||
|  | 	return val | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.17 Comma operator | |||
|  | // | |||
|  | //  expression: | |||
|  | // 	assignment-expression | |||
|  | // 	expression , assignment-expression | |||
|  | func (c *cpp) expression(s *cppScanner, eval bool) interface{} { | |||
|  | 	for { | |||
|  | 		r := c.assignmentExpression(s, eval) | |||
|  | 		if s.peek().char != ',' { | |||
|  | 			return r | |||
|  | 		} | |||
|  | 
 | |||
|  | 		s.next() | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.16 Assignment operators | |||
|  | // | |||
|  | //  assignment-expression: | |||
|  | // 	conditional-expression | |||
|  | // 	unary-expression assignment-operator assignment-expression | |||
|  | // | |||
|  | //  assignment-operator: one of | |||
|  | // 	= *= /= %= += -= <<= >>= &= ^= |= | |||
|  | func (c *cpp) assignmentExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	return c.conditionalExpression(s, eval) | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.15 Conditional operator | |||
|  | // | |||
|  | //  conditional-expression: | |||
|  | //		logical-OR-expression | |||
|  | //		logical-OR-expression ? expression : conditional-expression | |||
|  | func (c *cpp) conditionalExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	expr := c.logicalOrExpression(s, eval) | |||
|  | 	if s.peek().char == '?' { | |||
|  | 		s.next() | |||
|  | 		exprIsNonZero := c.isNonZero(expr) | |||
|  | 		expr2 := c.conditionalExpression(s, exprIsNonZero) | |||
|  | 		if tok := s.peek(); tok.char != ':' { | |||
|  | 			c.err(tok, "expected ':'") | |||
|  | 			return expr | |||
|  | 		} | |||
|  | 
 | |||
|  | 		s.next() | |||
|  | 		expr3 := c.conditionalExpression(s, !exprIsNonZero) | |||
|  | 
 | |||
|  | 		// [0] 6.5.15 | |||
|  | 		// | |||
|  | 		// 5. If both the second and third operands have arithmetic type, the result | |||
|  | 		// type that would be determined by the usual arithmetic conversions, were they | |||
|  | 		// applied to those two operands, is the type of the result. | |||
|  | 		x := c.operand(expr2) | |||
|  | 		y := c.operand(expr3) | |||
|  | 		if x != nil && y != nil { | |||
|  | 			x, y = usualArithmeticConversions(c.ctx, nil, x, y, false) | |||
|  | 			expr2 = c.fromOperand(x) | |||
|  | 			expr3 = c.fromOperand(y) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		switch { | |||
|  | 		case exprIsNonZero: | |||
|  | 			expr = expr2 | |||
|  | 		default: | |||
|  | 			expr = expr3 | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return expr | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) operand(v interface{}) Operand { | |||
|  | 	switch x := v.(type) { | |||
|  | 	case int64: | |||
|  | 		return &operand{typ: &typeBase{size: 8, kind: byte(LongLong), flags: fSigned}, value: Int64Value(x)} | |||
|  | 	case uint64: | |||
|  | 		return &operand{typ: &typeBase{size: 8, kind: byte(ULongLong)}, value: Uint64Value(x)} | |||
|  | 	default: | |||
|  | 		return nil | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) fromOperand(op Operand) interface{} { | |||
|  | 	switch x := op.Value().(type) { | |||
|  | 	case Int64Value: | |||
|  | 		return int64(x) | |||
|  | 	case Uint64Value: | |||
|  | 		return uint64(x) | |||
|  | 	default: | |||
|  | 		return nil | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.14 Logical OR operator | |||
|  | // | |||
|  | //  logical-OR-expression: | |||
|  | //		logical-AND-expression | |||
|  | //		logical-OR-expression || logical-AND-expression | |||
|  | func (c *cpp) logicalOrExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.logicalAndExpression(s, eval) | |||
|  | 	for s.peek().char == OROR { | |||
|  | 		s.next() | |||
|  | 		if c.isNonZero(lhs) { | |||
|  | 			eval = false | |||
|  | 		} | |||
|  | 		rhs := c.logicalAndExpression(s, eval) | |||
|  | 		if c.isNonZero(lhs) || c.isNonZero(rhs) { | |||
|  | 			lhs = int64(1) | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return lhs | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.13 Logical AND operator | |||
|  | // | |||
|  | //  logical-AND-expression: | |||
|  | //		inclusive-OR-expression | |||
|  | //		logical-AND-expression && inclusive-OR-expression | |||
|  | func (c *cpp) logicalAndExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.inclusiveOrExpression(s, eval) | |||
|  | 	for s.peek().char == ANDAND { | |||
|  | 		s.next() | |||
|  | 		if c.isZero(lhs) { | |||
|  | 			eval = false | |||
|  | 		} | |||
|  | 		rhs := c.inclusiveOrExpression(s, eval) | |||
|  | 		if c.isZero(lhs) || c.isZero(rhs) { | |||
|  | 			lhs = int64(0) | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return lhs | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) isZero(val interface{}) bool { | |||
|  | 	switch x := val.(type) { | |||
|  | 	case int64: | |||
|  | 		return x == 0 | |||
|  | 	case uint64: | |||
|  | 		return x == 0 | |||
|  | 	} | |||
|  | 	panic(internalError()) | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.12 Bitwise inclusive OR operator | |||
|  | // | |||
|  | //  inclusive-OR-expression: | |||
|  | //		exclusive-OR-expression | |||
|  | //		inclusive-OR-expression | exclusive-OR-expression | |||
|  | func (c *cpp) inclusiveOrExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.exclusiveOrExpression(s, eval) | |||
|  | 	for s.peek().char == '|' { | |||
|  | 		s.next() | |||
|  | 		rhs := c.exclusiveOrExpression(s, eval) | |||
|  | 		if eval { | |||
|  | 			switch x := lhs.(type) { | |||
|  | 			case int64: | |||
|  | 				switch y := rhs.(type) { | |||
|  | 				case int64: | |||
|  | 					lhs = x | y | |||
|  | 				case uint64: | |||
|  | 					lhs = uint64(x) | y | |||
|  | 				} | |||
|  | 			case uint64: | |||
|  | 				switch y := rhs.(type) { | |||
|  | 				case int64: | |||
|  | 					lhs = x | uint64(y) | |||
|  | 				case uint64: | |||
|  | 					lhs = x | y | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return lhs | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.11 Bitwise exclusive OR operator | |||
|  | // | |||
|  | //  exclusive-OR-expression: | |||
|  | //		AND-expression | |||
|  | //		exclusive-OR-expression ^ AND-expression | |||
|  | func (c *cpp) exclusiveOrExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.andExpression(s, eval) | |||
|  | 	for s.peek().char == '^' { | |||
|  | 		s.next() | |||
|  | 		rhs := c.andExpression(s, eval) | |||
|  | 		if eval { | |||
|  | 			switch x := lhs.(type) { | |||
|  | 			case int64: | |||
|  | 				switch y := rhs.(type) { | |||
|  | 				case int64: | |||
|  | 					lhs = x ^ y | |||
|  | 				case uint64: | |||
|  | 					lhs = uint64(x) ^ y | |||
|  | 				} | |||
|  | 			case uint64: | |||
|  | 				switch y := rhs.(type) { | |||
|  | 				case int64: | |||
|  | 					lhs = x ^ uint64(y) | |||
|  | 				case uint64: | |||
|  | 					lhs = x ^ y | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return lhs | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.10 Bitwise AND operator | |||
|  | // | |||
|  | //  AND-expression: | |||
|  | // 		equality-expression | |||
|  | // 		AND-expression & equality-expression | |||
|  | func (c *cpp) andExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.equalityExpression(s, eval) | |||
|  | 	for s.peek().char == '&' { | |||
|  | 		s.next() | |||
|  | 		rhs := c.equalityExpression(s, eval) | |||
|  | 		if eval { | |||
|  | 			switch x := lhs.(type) { | |||
|  | 			case int64: | |||
|  | 				switch y := rhs.(type) { | |||
|  | 				case int64: | |||
|  | 					lhs = x & y | |||
|  | 				case uint64: | |||
|  | 					lhs = uint64(x) & y | |||
|  | 				} | |||
|  | 			case uint64: | |||
|  | 				switch y := rhs.(type) { | |||
|  | 				case int64: | |||
|  | 					lhs = x & uint64(y) | |||
|  | 				case uint64: | |||
|  | 					lhs = x & y | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return lhs | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.9 Equality operators | |||
|  | // | |||
|  | //  equality-expression: | |||
|  | //		relational-expression | |||
|  | //		equality-expression == relational-expression | |||
|  | //		equality-expression != relational-expression | |||
|  | func (c *cpp) equalityExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.relationalExpression(s, eval) | |||
|  | 	for { | |||
|  | 		var v bool | |||
|  | 		switch s.peek().char { | |||
|  | 		case EQ: | |||
|  | 			s.next() | |||
|  | 			rhs := c.relationalExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x == y | |||
|  | 					case uint64: | |||
|  | 						v = uint64(x) == y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x == uint64(y) | |||
|  | 					case uint64: | |||
|  | 						v = x == y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case NEQ: | |||
|  | 			s.next() | |||
|  | 			rhs := c.relationalExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x != y | |||
|  | 					case uint64: | |||
|  | 						v = uint64(x) != y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x != uint64(y) | |||
|  | 					case uint64: | |||
|  | 						v = x != y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			return lhs | |||
|  | 		} | |||
|  | 		switch { | |||
|  | 		case v: | |||
|  | 			lhs = int64(1) | |||
|  | 		default: | |||
|  | 			lhs = int64(0) | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.8 Relational operators | |||
|  | // | |||
|  | //  relational-expression: | |||
|  | //		shift-expression | |||
|  | //		relational-expression <  shift-expression | |||
|  | //		relational-expression >  shift-expression | |||
|  | //		relational-expression <= shift-expression | |||
|  | //		relational-expression >= shift-expression | |||
|  | func (c *cpp) relationalExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.shiftExpression(s, eval) | |||
|  | 	for { | |||
|  | 		var v bool | |||
|  | 		switch s.peek().char { | |||
|  | 		case '<': | |||
|  | 			s.next() | |||
|  | 			rhs := c.shiftExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x < y | |||
|  | 					case uint64: | |||
|  | 						v = uint64(x) < y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x < uint64(y) | |||
|  | 					case uint64: | |||
|  | 						v = x < y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case '>': | |||
|  | 			s.next() | |||
|  | 			rhs := c.shiftExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x > y | |||
|  | 					case uint64: | |||
|  | 						v = uint64(x) > y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x > uint64(y) | |||
|  | 					case uint64: | |||
|  | 						v = x > y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case LEQ: | |||
|  | 			s.next() | |||
|  | 			rhs := c.shiftExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x <= y | |||
|  | 					case uint64: | |||
|  | 						v = uint64(x) <= y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x <= uint64(y) | |||
|  | 					case uint64: | |||
|  | 						v = x <= y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case GEQ: | |||
|  | 			s.next() | |||
|  | 			rhs := c.shiftExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x >= y | |||
|  | 					case uint64: | |||
|  | 						v = uint64(x) >= y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						v = x >= uint64(y) | |||
|  | 					case uint64: | |||
|  | 						v = x >= y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			return lhs | |||
|  | 		} | |||
|  | 		switch { | |||
|  | 		case v: | |||
|  | 			lhs = int64(1) | |||
|  | 		default: | |||
|  | 			lhs = int64(0) | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.7 Bitwise shift operators | |||
|  | // | |||
|  | //  shift-expression: | |||
|  | //		additive-expression | |||
|  | //		shift-expression << additive-expression | |||
|  | //		shift-expression >> additive-expression | |||
|  | func (c *cpp) shiftExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.additiveExpression(s, eval) | |||
|  | 	for { | |||
|  | 		switch s.peek().char { | |||
|  | 		case LSH: | |||
|  | 			s.next() | |||
|  | 			rhs := c.additiveExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x << uint(y) | |||
|  | 					case uint64: | |||
|  | 						lhs = uint64(x) << uint(y) | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x << uint(y) | |||
|  | 					case uint64: | |||
|  | 						lhs = x << uint(y) | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case RSH: | |||
|  | 			s.next() | |||
|  | 			rhs := c.additiveExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x >> uint(y) | |||
|  | 					case uint64: | |||
|  | 						lhs = uint64(x) >> uint(y) | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x >> uint(y) | |||
|  | 					case uint64: | |||
|  | 						lhs = x >> uint(y) | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			return lhs | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.6 Additive operators | |||
|  | // | |||
|  | //  additive-expression: | |||
|  | //		multiplicative-expression | |||
|  | //		additive-expression + multiplicative-expression | |||
|  | //		additive-expression - multiplicative-expression | |||
|  | func (c *cpp) additiveExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.multiplicativeExpression(s, eval) | |||
|  | 	for { | |||
|  | 		switch s.peek().char { | |||
|  | 		case '+': | |||
|  | 			s.next() | |||
|  | 			rhs := c.multiplicativeExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x + y | |||
|  | 					case uint64: | |||
|  | 						lhs = uint64(x) + y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x + uint64(y) | |||
|  | 					case uint64: | |||
|  | 						lhs = x + y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case '-': | |||
|  | 			s.next() | |||
|  | 			rhs := c.multiplicativeExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x - y | |||
|  | 					case uint64: | |||
|  | 						lhs = uint64(x) - y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x - uint64(y) | |||
|  | 					case uint64: | |||
|  | 						lhs = x - y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			return lhs | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.5 Multiplicative operators | |||
|  | // | |||
|  | //  multiplicative-expression: | |||
|  | //		unary-expression // [0], 6.10.1, 1. | |||
|  | //		multiplicative-expression * unary-expression | |||
|  | //		multiplicative-expression / unary-expression | |||
|  | //		multiplicative-expression % unary-expression | |||
|  | func (c *cpp) multiplicativeExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	lhs := c.unaryExpression(s, eval) | |||
|  | 	for { | |||
|  | 		switch s.peek().char { | |||
|  | 		case '*': | |||
|  | 			s.next() | |||
|  | 			rhs := c.unaryExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x * y | |||
|  | 					case uint64: | |||
|  | 						lhs = uint64(x) * y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						lhs = x * uint64(y) | |||
|  | 					case uint64: | |||
|  | 						lhs = x * y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case '/': | |||
|  | 			tok := s.next() | |||
|  | 			rhs := c.unaryExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = x / y | |||
|  | 					case uint64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = uint64(x) / y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = x / uint64(y) | |||
|  | 					case uint64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = x / y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		case '%': | |||
|  | 			tok := s.next() | |||
|  | 			rhs := c.unaryExpression(s, eval) | |||
|  | 			if eval { | |||
|  | 				switch x := lhs.(type) { | |||
|  | 				case int64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = x % y | |||
|  | 					case uint64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = uint64(x) % y | |||
|  | 					} | |||
|  | 				case uint64: | |||
|  | 					switch y := rhs.(type) { | |||
|  | 					case int64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = x % uint64(y) | |||
|  | 					case uint64: | |||
|  | 						if y == 0 { | |||
|  | 							c.err(tok, "division by zero") | |||
|  | 							break | |||
|  | 						} | |||
|  | 
 | |||
|  | 						lhs = x % y | |||
|  | 					} | |||
|  | 				} | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			return lhs | |||
|  | 		} | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.3 Unary operators | |||
|  | // | |||
|  | //  unary-expression: | |||
|  | //		primary-expression | |||
|  | //		unary-operator unary-expression | |||
|  | // | |||
|  | //  unary-operator: one of | |||
|  | //		+ - ~ ! | |||
|  | func (c *cpp) unaryExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	switch s.peek().char { | |||
|  | 	case '+': | |||
|  | 		s.next() | |||
|  | 		return c.unaryExpression(s, eval) | |||
|  | 	case '-': | |||
|  | 		s.next() | |||
|  | 		expr := c.unaryExpression(s, eval) | |||
|  | 		if eval { | |||
|  | 			switch x := expr.(type) { | |||
|  | 			case int64: | |||
|  | 				expr = -x | |||
|  | 			case uint64: | |||
|  | 				expr = -x | |||
|  | 			} | |||
|  | 		} | |||
|  | 		return expr | |||
|  | 	case '~': | |||
|  | 		s.next() | |||
|  | 		expr := c.unaryExpression(s, eval) | |||
|  | 		if eval { | |||
|  | 			switch x := expr.(type) { | |||
|  | 			case int64: | |||
|  | 				expr = ^x | |||
|  | 			case uint64: | |||
|  | 				expr = ^x | |||
|  | 			} | |||
|  | 		} | |||
|  | 		return expr | |||
|  | 	case '!': | |||
|  | 		s.next() | |||
|  | 		expr := c.unaryExpression(s, eval) | |||
|  | 		if eval { | |||
|  | 			var v bool | |||
|  | 			switch x := expr.(type) { | |||
|  | 			case int64: | |||
|  | 				v = x == 0 | |||
|  | 			case uint64: | |||
|  | 				v = x == 0 | |||
|  | 			} | |||
|  | 			switch { | |||
|  | 			case v: | |||
|  | 				expr = int64(1) | |||
|  | 			default: | |||
|  | 				expr = int64(0) | |||
|  | 			} | |||
|  | 		} | |||
|  | 		return expr | |||
|  | 	default: | |||
|  | 		return c.primaryExpression(s, eval) | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.5.1 Primary expressions | |||
|  | // | |||
|  | //  primary-expression: | |||
|  | //		identifier | |||
|  | //		constant | |||
|  | //		( expression ) | |||
|  | func (c *cpp) primaryExpression(s *cppScanner, eval bool) interface{} { | |||
|  | 	switch tok := s.peek(); tok.char { | |||
|  | 	case CHARCONST, LONGCHARCONST: | |||
|  | 		s.next() | |||
|  | 		r := charConst(c.ctx, tok) | |||
|  | 		return int64(r) | |||
|  | 	case IDENTIFIER: | |||
|  | 		if c.ctx.evalIdentError { | |||
|  | 			panic("cannot evaluate identifier") | |||
|  | 		} | |||
|  | 
 | |||
|  | 		s.next() | |||
|  | 		if s.peek().char == '(' { | |||
|  | 			s.next() | |||
|  | 			n := 1 | |||
|  | 		loop: | |||
|  | 			for n != 0 { | |||
|  | 				switch s.peek().char { | |||
|  | 				case '(': | |||
|  | 					n++ | |||
|  | 				case ')': | |||
|  | 					n-- | |||
|  | 				case -1: | |||
|  | 					c.err(s.peek(), "expected )") | |||
|  | 					break loop | |||
|  | 				} | |||
|  | 				s.next() | |||
|  | 			} | |||
|  | 		} | |||
|  | 		return int64(0) | |||
|  | 	case PPNUMBER: | |||
|  | 		s.next() | |||
|  | 		return c.intConst(tok) | |||
|  | 	case '(': | |||
|  | 		s.next() | |||
|  | 		expr := c.expression(s, eval) | |||
|  | 		if s.peek().char == ')' { | |||
|  | 			s.next() | |||
|  | 		} | |||
|  | 		return expr | |||
|  | 	default: | |||
|  | 		return int64(0) | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.4.4.1 Integer constants | |||
|  | // | |||
|  | //  integer-constant: | |||
|  | //		decimal-constant integer-suffix_opt | |||
|  | //		octal-constant integer-suffix_opt | |||
|  | //		hexadecimal-constant integer-suffix_opt | |||
|  | // | |||
|  | //  decimal-constant: | |||
|  | //		nonzero-digit | |||
|  | //		decimal-constant digit | |||
|  | // | |||
|  | //  octal-constant: | |||
|  | //		0 | |||
|  | //		octal-constant octal-digit | |||
|  | // | |||
|  | //  hexadecimal-prefix: one of | |||
|  | //		0x 0X | |||
|  | // | |||
|  | //  integer-suffix_opt: one of | |||
|  | //		u ul ull l lu ll llu | |||
|  | func (c *cpp) intConst(tok cppToken) (r interface{}) { | |||
|  | 	var n uint64 | |||
|  | 	s0 := tok.String() | |||
|  | 	s := strings.TrimRight(s0, "uUlL") | |||
|  | 	switch { | |||
|  | 	case strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X"): | |||
|  | 		var err error | |||
|  | 		if n, err = strconv.ParseUint(s[2:], 16, 64); err != nil { | |||
|  | 			c.err(tok, "%v", err) | |||
|  | 			return int64(0) | |||
|  | 		} | |||
|  | 	case strings.HasPrefix(s, "0"): | |||
|  | 		var err error | |||
|  | 		if n, err = strconv.ParseUint(s, 8, 64); err != nil { | |||
|  | 			c.err(tok, "%v", err) | |||
|  | 			return int64(0) | |||
|  | 		} | |||
|  | 	default: | |||
|  | 		var err error | |||
|  | 		if n, err = strconv.ParseUint(s, 10, 64); err != nil { | |||
|  | 			c.err(tok, "%v", err) | |||
|  | 			return int64(0) | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	suffix := s0[len(s):] | |||
|  | 	if suffix == "" { | |||
|  | 		if n > math.MaxInt64 { | |||
|  | 			return n | |||
|  | 		} | |||
|  | 
 | |||
|  | 		return int64(n) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	switch suffix = strings.ToLower(suffix); suffix { | |||
|  | 	default: | |||
|  | 		c.err(tok, "invalid suffix: %v", s0) | |||
|  | 		fallthrough | |||
|  | 	case | |||
|  | 		"l", | |||
|  | 		"ll": | |||
|  | 
 | |||
|  | 		if n > math.MaxInt64 { | |||
|  | 			return n | |||
|  | 		} | |||
|  | 
 | |||
|  | 		return int64(n) | |||
|  | 	case | |||
|  | 		"llu", | |||
|  | 		"lu", | |||
|  | 		"u", | |||
|  | 		"ul", | |||
|  | 		"ull": | |||
|  | 
 | |||
|  | 		return n | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | func charConst(ctx *context, tok cppToken) rune { | |||
|  | 	s := tok.String() | |||
|  | 	switch tok.char { | |||
|  | 	case LONGCHARCONST: | |||
|  | 		s = s[1:] // Remove leading 'L'. | |||
|  | 		fallthrough | |||
|  | 	case CHARCONST: | |||
|  | 		s = s[1 : len(s)-1] // Remove outer 's. | |||
|  | 		if len(s) == 1 { | |||
|  | 			return rune(s[0]) | |||
|  | 		} | |||
|  | 
 | |||
|  | 		var r rune | |||
|  | 		var n int | |||
|  | 		switch s[0] { | |||
|  | 		case '\\': | |||
|  | 			r, n = decodeEscapeSequence(ctx, tok, s) | |||
|  | 			if r < 0 { | |||
|  | 				r = -r | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			r, n = utf8.DecodeRuneInString(s) | |||
|  | 		} | |||
|  | 		if n != len(s) { | |||
|  | 			ctx.errNode(&tok, "invalid character constant") | |||
|  | 		} | |||
|  | 		return r | |||
|  | 	} | |||
|  | 	panic(internalError()) | |||
|  | } | |||
|  | 
 | |||
|  | // escape-sequence		{simple-sequence}|{octal-escape-sequence}|{hexadecimal-escape-sequence}|{universal-character-name} | |||
|  | // simple-sequence		\\['\x22?\\abfnrtv] | |||
|  | // octal-escape-sequence	\\{octal-digit}{octal-digit}?{octal-digit}? | |||
|  | // hexadecimal-escape-sequence	\\x{hexadecimal-digit}+ | |||
|  | func decodeEscapeSequence(ctx *context, tok cppToken, s string) (rune, int) { | |||
|  | 	if s[0] != '\\' { | |||
|  | 		panic(internalError()) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if len(s) == 1 { | |||
|  | 		return rune(s[0]), 1 | |||
|  | 	} | |||
|  | 
 | |||
|  | 	r := rune(s[1]) | |||
|  | 	switch r { | |||
|  | 	case '\'', '"', '?', '\\': | |||
|  | 		return r, 2 | |||
|  | 	case 'a': | |||
|  | 		return 7, 2 | |||
|  | 	case 'b': | |||
|  | 		return 8, 2 | |||
|  | 	case 'e': | |||
|  | 		return 0x1b, 2 | |||
|  | 	case 'f': | |||
|  | 		return 12, 2 | |||
|  | 	case 'n': | |||
|  | 		return 10, 2 | |||
|  | 	case 'r': | |||
|  | 		return 13, 2 | |||
|  | 	case 't': | |||
|  | 		return 9, 2 | |||
|  | 	case 'v': | |||
|  | 		return 11, 2 | |||
|  | 	case 'x': | |||
|  | 		v, n := 0, 2 | |||
|  | 	loop2: | |||
|  | 		for i := 2; i < len(s); i++ { | |||
|  | 			r := s[i] | |||
|  | 			switch { | |||
|  | 			case r >= '0' && r <= '9', r >= 'a' && r <= 'f', r >= 'A' && r <= 'F': | |||
|  | 				v = v<<4 | decodeHex(r) | |||
|  | 				n++ | |||
|  | 			default: | |||
|  | 				break loop2 | |||
|  | 			} | |||
|  | 		} | |||
|  | 		return -rune(v & 0xff), n | |||
|  | 	case 'u', 'U': | |||
|  | 		return decodeUCN(s) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if r < '0' || r > '7' { | |||
|  | 		panic(internalError()) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	v, n := 0, 1 | |||
|  | 	ok := false | |||
|  | loop: | |||
|  | 	for i := 1; i < len(s); i++ { | |||
|  | 		r := s[i] | |||
|  | 		switch { | |||
|  | 		case i < 4 && r >= '0' && r <= '7': | |||
|  | 			ok = true | |||
|  | 			v = v<<3 | (int(r) - '0') | |||
|  | 			n++ | |||
|  | 		default: | |||
|  | 			break loop | |||
|  | 		} | |||
|  | 	} | |||
|  | 	if !ok { | |||
|  | 		ctx.errNode(&tok, "invalid octal sequence") | |||
|  | 	} | |||
|  | 	return -rune(v), n | |||
|  | } | |||
|  | 
 | |||
|  | // universal-character-name	\\u{hex-quad}|\\U{hex-quad}{hex-quad} | |||
|  | func decodeUCN(s string) (rune, int) { | |||
|  | 	if s[0] != '\\' { | |||
|  | 		panic(internalError()) | |||
|  | 	} | |||
|  | 
 | |||
|  | 	s = s[1:] | |||
|  | 	switch s[0] { | |||
|  | 	case 'u': | |||
|  | 		return rune(decodeHexQuad(s[1:])), 6 | |||
|  | 	case 'U': | |||
|  | 		return rune(decodeHexQuad(s[1:])<<16 | decodeHexQuad(s[5:])), 10 | |||
|  | 	} | |||
|  | 	panic(internalError()) | |||
|  | } | |||
|  | 
 | |||
|  | // hex-quad	{hexadecimal-digit}{hexadecimal-digit}{hexadecimal-digit}{hexadecimal-digit} | |||
|  | func decodeHexQuad(s string) int { | |||
|  | 	n := 0 | |||
|  | 	for i := 0; i < 4; i++ { | |||
|  | 		n = n<<4 | decodeHex(s[i]) | |||
|  | 	} | |||
|  | 	return n | |||
|  | } | |||
|  | 
 | |||
|  | func decodeHex(r byte) int { | |||
|  | 	switch { | |||
|  | 	case r >= '0' && r <= '9': | |||
|  | 		return int(r) - '0' | |||
|  | 	default: | |||
|  | 		x := int(r) &^ 0x20 | |||
|  | 		return x - 'A' + 10 | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) isNonZero(val interface{}) bool { | |||
|  | 	switch x := val.(type) { | |||
|  | 	case int64: | |||
|  | 		return x != 0 | |||
|  | 	case uint64: | |||
|  | 		return x != 0 | |||
|  | 	} | |||
|  | 	panic(internalError()) | |||
|  | } | |||
|  | 
 | |||
|  | type ppLine interface { | |||
|  | 	getToks() []token3 | |||
|  | } | |||
|  | 
 | |||
|  | type ppIfGroupDirective interface { | |||
|  | 	evalInclusionCondition(*cpp) bool | |||
|  | } | |||
|  | 
 | |||
|  | type ppElifDirective struct { | |||
|  | 	toks []token3 | |||
|  | 	expr []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppElifDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | type ppElseDirective struct { | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppElseDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | type ppEndifDirective struct { | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppEndifDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | type ppEmptyDirective struct { | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppEmptyDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppEmptyDirective) translationPhase4(c *cpp) { | |||
|  | 	// nop | |||
|  | } | |||
|  | 
 | |||
|  | type ppIncludeDirective struct { | |||
|  | 	arg  []token3 | |||
|  | 	toks []token3 | |||
|  | 
 | |||
|  | 	includeNext bool // false: #include, true: #include_next | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppIncludeDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppIncludeDirective) translationPhase4(c *cpp) { | |||
|  | 	if c.ctx.cfg.ignoreIncludes { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	args := make([]cppToken, 0, len(n.arg)) | |||
|  | 	for _, v := range n.arg { | |||
|  | 		switch v.char { | |||
|  | 		case ' ', '\t', '\v', '\f': | |||
|  | 			// nop | |||
|  | 		default: | |||
|  | 			args = append(args, cppToken{token4{token3: v}, nil}) | |||
|  | 		} | |||
|  | 	} | |||
|  | 	var sb strings.Builder | |||
|  | 	for _, v := range args { | |||
|  | 		sb.WriteString(v.String()) | |||
|  | 	} | |||
|  | 	nm := strings.TrimSpace(sb.String()) | |||
|  | 	if nm == "" { | |||
|  | 		c.err(n.toks[0], "invalid empty include argument") | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	switch nm[0] { | |||
|  | 	case '"', '<': | |||
|  | 		// ok | |||
|  | 	default: | |||
|  | 		var w cppWriter | |||
|  | 		c.expand(&cppReader{buf: args}, &w, false) | |||
|  | 		x := 0 | |||
|  | 		for _, v := range w.toks { | |||
|  | 			switch v.char { | |||
|  | 			case ' ', '\t', '\v', '\f': | |||
|  | 				// nop | |||
|  | 			default: | |||
|  | 				w.toks[x] = v | |||
|  | 				x++ | |||
|  | 			} | |||
|  | 		} | |||
|  | 		w.toks = w.toks[:x] | |||
|  | 		nm = strings.TrimSpace(cppToksStr(w.toks, "")) | |||
|  | 	} | |||
|  | 	toks := n.toks | |||
|  | 	if c.ctx.cfg.RejectIncludeNext { | |||
|  | 		c.err(toks[0], "#include_next is a GCC extension") | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if c.ctx.cfg.fakeIncludes { | |||
|  | 		c.send([]token3{{char: STRINGLITERAL, value: dict.sid(nm), src: dict.sid(nm)}, {char: '\n', value: idNL}}) | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if re := c.ctx.cfg.IgnoreInclude; re != nil && re.MatchString(nm) { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if c.includeLevel == maxIncludeLevel { | |||
|  | 		c.err(toks[0], "too many include levels") | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	c.includeLevel++ | |||
|  | 
 | |||
|  | 	defer func() { c.includeLevel-- }() | |||
|  | 
 | |||
|  | 	var ( | |||
|  | 		b     byte | |||
|  | 		paths []string | |||
|  | 		sys   bool | |||
|  | 	) | |||
|  | 	switch { | |||
|  | 	case nm != "" && nm[0] == '"': | |||
|  | 		paths = c.ctx.includePaths | |||
|  | 		b = '"' | |||
|  | 	case nm != "" && nm[0] == '<': | |||
|  | 		paths = c.ctx.sysIncludePaths | |||
|  | 		sys = true | |||
|  | 		b = '>' | |||
|  | 	case nm == "": | |||
|  | 		c.err(toks[0], "invalid empty include argument") | |||
|  | 		return | |||
|  | 	default: | |||
|  | 		c.err(toks[0], "invalid include argument %s", nm) | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	x := strings.IndexByte(nm[1:], b) | |||
|  | 	if x < 0 { | |||
|  | 		c.err(toks[0], "invalid include argument %s", nm) | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	nm = filepath.FromSlash(nm[1 : x+1]) | |||
|  | 	var path string | |||
|  | 	switch { | |||
|  | 	case filepath.IsAbs(nm): | |||
|  | 		path = nm | |||
|  | 	default: | |||
|  | 		dir := filepath.Dir(c.file.Name()) | |||
|  | 		if n.includeNext { | |||
|  | 			nmDir, _ := filepath.Split(nm) | |||
|  | 			for i, v := range paths { | |||
|  | 				if w, err := filepath.Abs(v); err == nil { | |||
|  | 					v = w | |||
|  | 				} | |||
|  | 				v = filepath.Join(v, nmDir) | |||
|  | 				if v == dir { | |||
|  | 					paths = paths[i+1:] | |||
|  | 					break | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 		for _, v := range paths { | |||
|  | 			if v == "@" { | |||
|  | 				v = dir | |||
|  | 			} | |||
|  | 
 | |||
|  | 			p := filepath.Join(v, nm) | |||
|  | 			fi, err := c.ctx.statFile(p, sys) | |||
|  | 			if err != nil || fi.IsDir() { | |||
|  | 				continue | |||
|  | 			} | |||
|  | 
 | |||
|  | 			path = p | |||
|  | 			break | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if path == "" { | |||
|  | 		wd, _ := os.Getwd() | |||
|  | 		c.err(toks[0], "include file not found: %s (wd %s)\nsearch paths:\n\t%s", nm, wd, strings.Join(paths, "\n\t")) | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if h := c.ctx.cfg.IncludeFileHandler; h != nil { | |||
|  | 		var position gotoken.Position | |||
|  | 		if p := toks[0].Pos(); p.IsValid() { | |||
|  | 			position = gotoken.Position(c.file.PositionFor(p, true)) | |||
|  | 		} | |||
|  | 		apath, err := filepath.Abs(path) | |||
|  | 		if err != nil { | |||
|  | 			c.err(toks[0], "%s: cannot compute absolute path: %v", path, err) | |||
|  | 		} | |||
|  | 		h(position, apath) | |||
|  | 	} | |||
|  | 	cf, err := cache.getFile(c.ctx, path, sys, false) | |||
|  | 	if err != nil { | |||
|  | 		c.err(toks[0], "%s: %v", path, err) | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	pf, err := cf.ppFile() | |||
|  | 	if err != nil { | |||
|  | 		c.err(toks[0], "%s: %v", path, err) | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	saveFile := c.file | |||
|  | 	saveFileMacro := c.fileMacro.repl[0].value | |||
|  | 
 | |||
|  | 	c.file = pf.file | |||
|  | 	c.fileMacro.repl[0].value = dict.sid(fmt.Sprintf("%q", c.file.Name())) | |||
|  | 
 | |||
|  | 	defer func() { | |||
|  | 		c.file = saveFile | |||
|  | 		c.fileMacro.repl[0].value = saveFileMacro | |||
|  | 	}() | |||
|  | 
 | |||
|  | 	pf.translationPhase4(c) | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) send(toks []token3) { | |||
|  | 	c.in <- toks | |||
|  | 	<-c.rq | |||
|  | } | |||
|  | 
 | |||
|  | func (c *cpp) identicalReplacementLists(a, b []token3) bool { | |||
|  | 	for len(a) != 0 && a[0].char == ' ' { | |||
|  | 		a = a[1:] | |||
|  | 	} | |||
|  | 	for len(b) != 0 && b[0].char == ' ' { | |||
|  | 		b = b[1:] | |||
|  | 	} | |||
|  | 	for len(a) != 0 && a[len(a)-1].char == ' ' { | |||
|  | 		a = a[:len(a)-1] | |||
|  | 	} | |||
|  | 	for len(b) != 0 && b[len(b)-1].char == ' ' { | |||
|  | 		b = b[:len(b)-1] | |||
|  | 	} | |||
|  | 	if len(a) != len(b) { | |||
|  | 		return false | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for i, v := range a { | |||
|  | 		w := b[i] | |||
|  | 		if v.char != w.char || v.value != w.value { | |||
|  | 			return false | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return true | |||
|  | } | |||
|  | 
 | |||
|  | func stringConst(ctx *context, t cppToken) string { | |||
|  | 	s := t.String() | |||
|  | 	switch t.char { | |||
|  | 	case LONGSTRINGLITERAL: | |||
|  | 		s = s[1:] // Remove leading 'L'. | |||
|  | 		fallthrough | |||
|  | 	case STRINGLITERAL: | |||
|  | 		var buf bytes.Buffer | |||
|  | 		for i := 1; i < len(s)-1; { | |||
|  | 			switch c := s[i]; c { | |||
|  | 			case '\\': | |||
|  | 				r, n := decodeEscapeSequence(ctx, t, s[i:]) | |||
|  | 				switch { | |||
|  | 				case r < 0: | |||
|  | 					buf.WriteByte(byte(-r)) | |||
|  | 				default: | |||
|  | 					buf.WriteRune(r) | |||
|  | 				} | |||
|  | 				i += n | |||
|  | 			default: | |||
|  | 				buf.WriteByte(c) | |||
|  | 				i++ | |||
|  | 			} | |||
|  | 		} | |||
|  | 		return buf.String() | |||
|  | 	} | |||
|  | 	panic(internalError()) | |||
|  | } | |||
|  | 
 | |||
|  | // -------------------------------------------------------- Translation phase 4 | |||
|  | 
 | |||
|  | // [0], 5.1.1.2, 4 | |||
|  | // | |||
|  | // Preprocessing directives are executed, macro invocations are expanded, and | |||
|  | // _Pragma unary operator expressions are executed. If a character sequence | |||
|  | // that matches the syntax of a universal character name is produced by token | |||
|  | // concatenation (6.10.3.3), the behavior is undefined. A #include | |||
|  | // preprocessing directive causes the named header or source file to be | |||
|  | // processed from phase 1 through phase 4, recursively. All preprocessing | |||
|  | // directives are then deleted. | |||
|  | func (c *cpp) translationPhase4(in []source) chan *[]token4 { | |||
|  | 	c.rq = make(chan struct{})       // Must be unbufferred | |||
|  | 	c.in = make(chan []token3)       // Must be unbufferred | |||
|  | 	c.out = make(chan *[]token4, 10) //DONE benchmark tuned | |||
|  | 
 | |||
|  | 	go func() { | |||
|  | 		defer close(c.out) | |||
|  | 
 | |||
|  | 		c.expand(c, c, false) | |||
|  | 	}() | |||
|  | 
 | |||
|  | 	go func() { | |||
|  | 		defer close(c.in) | |||
|  | 
 | |||
|  | 		for _, v := range in { | |||
|  | 			pf, err := v.ppFile() | |||
|  | 			if err != nil { | |||
|  | 				c.err(nil, "%s", err) | |||
|  | 				break | |||
|  | 			} | |||
|  | 
 | |||
|  | 			c.file = pf.file | |||
|  | 			c.fileMacro.repl[0].value = dict.sid(fmt.Sprintf("%q", c.file.Name())) | |||
|  | 			pf.translationPhase4(c) | |||
|  | 		} | |||
|  | 	}() | |||
|  | 
 | |||
|  | 	return c.out | |||
|  | } | |||
|  | 
 | |||
|  | type ppErrorDirective struct { | |||
|  | 	toks []token3 | |||
|  | 	msg  []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppErrorDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppErrorDirective) translationPhase4(c *cpp) { | |||
|  | 	var b strings.Builder | |||
|  | 	for _, v := range n.msg { | |||
|  | 		b.WriteString(v.String()) | |||
|  | 	} | |||
|  | 	c.err(n.toks[0], "%s", strings.TrimSpace(b.String())) | |||
|  | } | |||
|  | 
 | |||
|  | type ppPragmaDirective struct { | |||
|  | 	toks []token3 | |||
|  | 	args []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppPragmaDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppPragmaDirective) translationPhase4(c *cpp) { parsePragma(c, n.args) } | |||
|  | 
 | |||
|  | func parsePragma(c *cpp, args0 []token3) { | |||
|  | 	if len(args0) == 1 { // \n | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if t := args0[0]; t.char == IDENTIFIER && t.value == idSTDC { | |||
|  | 		p := t | |||
|  | 		p.char = PRAGMASTDC | |||
|  | 		p.value = idPragmaSTDC | |||
|  | 		send := []token3{p, {char: ' ', value: idSpace, src: idSpace, pos: t.pos}} | |||
|  | 		args := ltrim3(args0[1:]) | |||
|  | 		if len(args) == 0 { | |||
|  | 			c.err(args[0], "expected argument of STDC") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if t = args[0]; t.char != IDENTIFIER { | |||
|  | 			c.err(t, "expected identifier") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		switch t.value { | |||
|  | 		case idFPContract, idFenvAccess, idCxLimitedRange: | |||
|  | 			// ok | |||
|  | 		default: | |||
|  | 			c.err(t, "expected FP_CONTRACT or FENV_ACCESS or CX_LIMITED_RANGE") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		args = ltrim3(args[1:]) | |||
|  | 		if len(args) == 0 { | |||
|  | 			c.err(args[0], "expected ON or OFF or DEFAULT") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if t = args[0]; t.char != IDENTIFIER { | |||
|  | 			c.err(t, "expected identifier") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		switch t.value { | |||
|  | 		case idOn, idOff, idDefault: | |||
|  | 			c.writes(c.cppToks(append(send, args0...))) | |||
|  | 		default: | |||
|  | 			c.err(t, "expected ON or OFF or DEFAULT") | |||
|  | 			return | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if c.ctx.cfg.PragmaHandler == nil { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	var toks []cppToken | |||
|  | 	for _, v := range args0[:len(args0)-1] { | |||
|  | 		toks = append(toks, cppToken{token4: token4{file: c.file, token3: v}}) | |||
|  | 	} | |||
|  | 	if len(toks) == 0 { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	var toks2 []Token | |||
|  | 	var sep StringID | |||
|  | 	for _, tok := range toks { | |||
|  | 		switch tok.char { | |||
|  | 		case ' ', '\n': | |||
|  | 			if c.ctx.cfg.PreserveOnlyLastNonBlankSeparator { | |||
|  | 				if strings.TrimSpace(tok.value.String()) != "" { | |||
|  | 					sep = tok.value | |||
|  | 				} | |||
|  | 				break | |||
|  | 			} | |||
|  | 
 | |||
|  | 			switch { | |||
|  | 			case sep != 0: | |||
|  | 				sep = dict.sid(sep.String() + tok.String()) //TODO quadratic | |||
|  | 			default: | |||
|  | 				sep = tok.value | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			var t Token | |||
|  | 			t.Rune = tok.char | |||
|  | 			t.Sep = sep | |||
|  | 			t.Value = tok.value | |||
|  | 			t.file = tok.file | |||
|  | 			t.pos = tok.pos | |||
|  | 			toks2 = append(toks2, t) | |||
|  | 			sep = 0 | |||
|  | 		} | |||
|  | 	} | |||
|  | 	if len(toks2) == 0 { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// dbg("%v: %q", c.file.PositionFor(args0[0].Pos(), true), tokStr(toks2, "|")) | |||
|  | 	c.ctx.cfg.PragmaHandler(&pragma{tok: toks[0], c: c}, toks2) | |||
|  | } | |||
|  | 
 | |||
|  | type ppNonDirective struct { | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppNonDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppNonDirective) translationPhase4(c *cpp) { | |||
|  | 	// nop | |||
|  | } | |||
|  | 
 | |||
|  | type ppTextLine struct { | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppTextLine) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppTextLine) translationPhase4(c *cpp) { c.send(n.toks) } | |||
|  | 
 | |||
|  | type ppLineDirective struct { | |||
|  | 	toks    []token3 | |||
|  | 	args    []token3 | |||
|  | 	nextPos int | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppLineDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppLineDirective) translationPhase4(c *cpp) { | |||
|  | 	toks := expandArgs(c, n.args) | |||
|  | 	if len(toks) == 0 { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	switch t := toks[0]; t.char { | |||
|  | 	case PPNUMBER: | |||
|  | 		ln, err := strconv.ParseInt(t.String(), 10, 31) | |||
|  | 		if err != nil || ln < 1 { | |||
|  | 			c.err(t, "expected positive integer less or equal 2147483647") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		for len(toks) != 0 && toks[0].char == ' ' { | |||
|  | 			toks = toks[1:] | |||
|  | 		} | |||
|  | 		if len(toks) == 1 { | |||
|  | 			c.file.AddLineInfo(int(n.nextPos)-1, c.file.Name(), int(ln)) | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		toks = toks[1:] | |||
|  | 		for len(toks) != 0 && toks[0].char == ' ' { | |||
|  | 			toks = toks[1:] | |||
|  | 		} | |||
|  | 		if len(toks) == 0 { | |||
|  | 			c.file.AddLineInfo(int(n.nextPos)-1, c.file.Name(), int(ln)) | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		switch t := toks[0]; t.char { | |||
|  | 		case STRINGLITERAL: | |||
|  | 			s := t.String() | |||
|  | 			s = s[1 : len(s)-1] | |||
|  | 			c.file.AddLineInfo(int(n.nextPos)-1, s, int(ln)) | |||
|  | 			c.fileMacro.repl[0].value = t.value | |||
|  | 			for len(toks) != 0 && toks[0].char == ' ' { | |||
|  | 				toks = toks[1:] | |||
|  | 			} | |||
|  | 			if len(toks) != 0 && c.ctx.cfg.RejectLineExtraTokens { | |||
|  | 				c.err(toks[0], "expected new-line") | |||
|  | 			} | |||
|  | 		default: | |||
|  | 			c.err(t, "expected string literal") | |||
|  | 			return | |||
|  | 		} | |||
|  | 	default: | |||
|  | 		c.err(toks[0], "expected integer literal") | |||
|  | 		return | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | func expandArgs(c *cpp, args []token3) []cppToken { | |||
|  | 	var w cppWriter | |||
|  | 	var toks []cppToken | |||
|  | 	for _, v := range args { | |||
|  | 		toks = append(toks, cppToken{token4: token4{file: c.file, token3: v}}) | |||
|  | 	} | |||
|  | 	c.expand(&cppReader{buf: toks}, &w, true) | |||
|  | 	return w.toks | |||
|  | } | |||
|  | 
 | |||
|  | type ppUndefDirective struct { | |||
|  | 	name token3 | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppUndefDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppUndefDirective) translationPhase4(c *cpp) { | |||
|  | 	nm := n.name.value | |||
|  | 	if _, ok := protectedMacros[nm]; ok || nm == idDefined { | |||
|  | 		c.err(n.name, "cannot undefine a protected name") | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// dbg("#undef %s", nm) | |||
|  | 	delete(c.macros, nm) | |||
|  | } | |||
|  | 
 | |||
|  | type ppIfdefDirective struct { | |||
|  | 	name StringID | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppIfdefDirective) evalInclusionCondition(c *cpp) bool { _, ok := c.macros[n.name]; return ok } | |||
|  | 
 | |||
|  | func (n *ppIfdefDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | type ppIfndefDirective struct { | |||
|  | 	name StringID | |||
|  | 	toks []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppIfndefDirective) evalInclusionCondition(c *cpp) bool { | |||
|  | 	_, ok := c.macros[n.name] | |||
|  | 	return !ok | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppIfndefDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | type ppIfDirective struct { | |||
|  | 	toks []token3 | |||
|  | 	expr []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppIfDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppIfDirective) evalInclusionCondition(c *cpp) bool { | |||
|  | 	return c.evalInclusionCondition(n.expr) | |||
|  | } | |||
|  | 
 | |||
|  | type ppDefineObjectMacroDirective struct { | |||
|  | 	name            token3 | |||
|  | 	toks            []token3 | |||
|  | 	replacementList []token3 | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppDefineObjectMacroDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppDefineObjectMacroDirective) translationPhase4(c *cpp) { | |||
|  | 	nm := n.name.value | |||
|  | 	m := c.macros[nm] | |||
|  | 	if m != nil { | |||
|  | 		if _, ok := protectedMacros[nm]; ok || nm == idDefined { | |||
|  | 			c.err(n.name, "cannot define protected name") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if m.isFnLike { | |||
|  | 			c.err(n.name, "redefinition of a function-like macro with an object-like one") | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if !c.identicalReplacementLists(n.replacementList, m.repl) && c.ctx.cfg.RejectIncompatibleMacroRedef { | |||
|  | 			c.err(n.name, "redefinition with different replacement list") | |||
|  | 			return | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// find first non-blank token to claim as our location | |||
|  | 	var pos int32 | |||
|  | 	for _, t := range n.toks { | |||
|  | 		if t.char != ' ' { | |||
|  | 			pos = t.pos | |||
|  | 			break | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// dbg("#define %s %s // %v", n.name, tokStr(n.replacementList, " "), c.file.PositionFor(n.name.Pos(), true)) | |||
|  | 	c.macros[nm] = &Macro{pos: pos, name: token4{token3: n.name, file: c.file}, repl: n.replacementList} | |||
|  | 	if nm != idGNUC { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	c.ctx.keywords = gccKeywords | |||
|  | } | |||
|  | 
 | |||
|  | type ppDefineFunctionMacroDirective struct { | |||
|  | 	identifierList  []token3 | |||
|  | 	toks            []token3 | |||
|  | 	replacementList []token3 | |||
|  | 
 | |||
|  | 	name token3 | |||
|  | 
 | |||
|  | 	namedVariadic bool // foo..., note no comma before ellipsis. | |||
|  | 	variadic      bool | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppDefineFunctionMacroDirective) getToks() []token3 { return n.toks } | |||
|  | 
 | |||
|  | func (n *ppDefineFunctionMacroDirective) translationPhase4(c *cpp) { | |||
|  | 	nm := n.name.value | |||
|  | 	m := c.macros[nm] | |||
|  | 	if m != nil { | |||
|  | 		if _, ok := protectedMacros[nm]; ok || nm == idDefined { | |||
|  | 			c.err(n.name, "cannot define protected name") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if !m.isFnLike && c.ctx.cfg.RejectIncompatibleMacroRedef { | |||
|  | 			c.err(n.name, "redefinition of an object-like macro with a function-like one") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		ok := len(m.fp) == len(n.identifierList) | |||
|  | 		if ok { | |||
|  | 			for i, v := range m.fp { | |||
|  | 				if v != n.identifierList[i].value { | |||
|  | 					ok = false | |||
|  | 					break | |||
|  | 				} | |||
|  | 			} | |||
|  | 		} | |||
|  | 		if !ok && (len(n.replacementList) != 0 || len(m.repl) != 0) && c.ctx.cfg.RejectIncompatibleMacroRedef { | |||
|  | 			c.err(n.name, "redefinition with different formal parameters") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if !c.identicalReplacementLists(n.replacementList, m.repl) && c.ctx.cfg.RejectIncompatibleMacroRedef { | |||
|  | 			c.err(n.name, "redefinition with different replacement list") | |||
|  | 			return | |||
|  | 		} | |||
|  | 
 | |||
|  | 		if m.variadic != n.variadic && c.ctx.cfg.RejectIncompatibleMacroRedef { | |||
|  | 			c.err(n.name, "redefinition differs in being variadic") | |||
|  | 			return | |||
|  | 		} | |||
|  | 	} | |||
|  | 	nms := map[StringID]struct{}{} | |||
|  | 	for _, v := range n.identifierList { | |||
|  | 		if _, ok := nms[v.value]; ok { | |||
|  | 			c.err(v, "duplicate identifier %s", v.value) | |||
|  | 		} | |||
|  | 	} | |||
|  | 	var fp []StringID | |||
|  | 	for _, v := range n.identifierList { | |||
|  | 		fp = append(fp, v.value) | |||
|  | 	} | |||
|  | 	// dbg("#define %s %s // %v", n.name, tokStr(n.replacementList, " "), c.file.PositionFor(n.name.Pos(), true)) | |||
|  | 	c.macros[nm] = &Macro{fp: fp, isFnLike: true, name: token4{token3: n.name, file: c.file}, repl: n.replacementList, variadic: n.variadic, namedVariadic: n.namedVariadic} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.10.1 | |||
|  | // | |||
|  | //  elif-group: | |||
|  | //  		# elif constant-expression new-line group_opt | |||
|  | type ppElifGroup struct { | |||
|  | 	elif   *ppElifDirective | |||
|  | 	groups []ppGroup | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppElifGroup) evalInclusionCondition(c *cpp) bool { | |||
|  | 	if !c.evalInclusionCondition(n.elif.expr) { | |||
|  | 		return false | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, v := range n.groups { | |||
|  | 		v.translationPhase4(c) | |||
|  | 	} | |||
|  | 	return true | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.10.1 | |||
|  | // | |||
|  | //  else-group: | |||
|  | //  		# else new-line group_opt | |||
|  | type ppElseGroup struct { | |||
|  | 	elseLine *ppElseDirective | |||
|  | 	groups   []ppGroup | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppElseGroup) translationPhase4(c *cpp) { | |||
|  | 	if n == nil { | |||
|  | 		return | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, v := range n.groups { | |||
|  | 		v.translationPhase4(c) | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.10.1 | |||
|  | // | |||
|  | //  PreprocessingFile: | |||
|  | //  		GroupOpt | |||
|  | type ppFile struct { | |||
|  | 	file   *tokenFile | |||
|  | 	groups []ppGroup | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppFile) translationPhase4(c *cpp) { | |||
|  | 	c.ctx.tuSourcesAdd(1) | |||
|  | 	if f := n.file; f != nil { | |||
|  | 		c.ctx.tuSizeAdd(int64(f.Size())) | |||
|  | 	} | |||
|  | 	for _, v := range n.groups { | |||
|  | 		v.translationPhase4(c) | |||
|  | 	} | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.10.1 | |||
|  | // | |||
|  | //  group-part: | |||
|  | //  		if-section | |||
|  | //  		control-line | |||
|  | //  		text-line | |||
|  | //  		# non-directive | |||
|  | type ppGroup interface { | |||
|  | 	translationPhase4(*cpp) | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.10.1 | |||
|  | // | |||
|  | //  if-group: | |||
|  | //  		# if constant-expression new-line group opt | |||
|  | //  		# ifdef identifier new-line group opt | |||
|  | //  		# ifndef identifier new-line group opt | |||
|  | type ppIfGroup struct { | |||
|  | 	directive ppIfGroupDirective | |||
|  | 	groups    []ppGroup | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppIfGroup) evalInclusionCondition(c *cpp) bool { | |||
|  | 	if !n.directive.evalInclusionCondition(c) { | |||
|  | 		return false | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, v := range n.groups { | |||
|  | 		v.translationPhase4(c) | |||
|  | 	} | |||
|  | 	return true | |||
|  | } | |||
|  | 
 | |||
|  | // [0], 6.10.1 | |||
|  | // | |||
|  | // if-section: | |||
|  | // 		if-group elif-groups_opt else-group_opt endif-line | |||
|  | type ppIfSection struct { | |||
|  | 	ifGroup    *ppIfGroup | |||
|  | 	elifGroups []*ppElifGroup | |||
|  | 	elseGroup  *ppElseGroup | |||
|  | 	endifLine  *ppEndifDirective | |||
|  | } | |||
|  | 
 | |||
|  | func (n *ppIfSection) translationPhase4(c *cpp) { | |||
|  | 	if !n.ifGroup.evalInclusionCondition(c) { | |||
|  | 		for _, v := range n.elifGroups { | |||
|  | 			if v.evalInclusionCondition(c) { | |||
|  | 				return | |||
|  | 			} | |||
|  | 		} | |||
|  | 
 | |||
|  | 		n.elseGroup.translationPhase4(c) | |||
|  | 	} | |||
|  | } |