diff options
Diffstat (limited to 'vendor/modernc.org/cc/v3/cpp.go')
| -rw-r--r-- | vendor/modernc.org/cc/v3/cpp.go | 3101 |
1 files changed, 0 insertions, 3101 deletions
diff --git a/vendor/modernc.org/cc/v3/cpp.go b/vendor/modernc.org/cc/v3/cpp.go deleted file mode 100644 index db4cee0ff..000000000 --- a/vendor/modernc.org/cc/v3/cpp.go +++ /dev/null @@ -1,3101 +0,0 @@ -// 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) - } -} |
