diff options
author | Wim <wim@42.be> | 2022-01-31 00:27:37 +0100 |
---|---|---|
committer | Wim <wim@42.be> | 2022-03-20 14:57:48 +0100 |
commit | e3cafeaf9292f67459ff1d186f68283bfaedf2ae (patch) | |
tree | b69c39620aa91dba695b3b935c6651c0fb37ce75 /vendor/modernc.org/cc/v3/cpp.go | |
parent | e7b193788a56ee7cdb02a87a9db0ad6724ef66d5 (diff) | |
download | matterbridge-msglm-e3cafeaf9292f67459ff1d186f68283bfaedf2ae.tar.gz matterbridge-msglm-e3cafeaf9292f67459ff1d186f68283bfaedf2ae.tar.bz2 matterbridge-msglm-e3cafeaf9292f67459ff1d186f68283bfaedf2ae.zip |
Add dependencies/vendor (whatsapp)
Diffstat (limited to 'vendor/modernc.org/cc/v3/cpp.go')
-rw-r--r-- | vendor/modernc.org/cc/v3/cpp.go | 3101 |
1 files changed, 3101 insertions, 0 deletions
diff --git a/vendor/modernc.org/cc/v3/cpp.go b/vendor/modernc.org/cc/v3/cpp.go new file mode 100644 index 00000000..db4cee0f --- /dev/null +++ b/vendor/modernc.org/cc/v3/cpp.go @@ -0,0 +1,3101 @@ +// 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) + } +} |