summaryrefslogtreecommitdiff
path: root/vendor/modernc.org/cc/v3/cpp.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/modernc.org/cc/v3/cpp.go')
-rw-r--r--vendor/modernc.org/cc/v3/cpp.go3101
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)
- }
-}