mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 20:22:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			3101 lines
		
	
	
	
		
			68 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			3101 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)
 | ||
| 	}
 | ||
| }
 |