modernc.org/cc@v1.0.1/cpp.go (about) 1 // Copyright 2016 The CC Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cc // import "modernc.org/cc" 6 7 import ( 8 "bytes" 9 "fmt" 10 "os" 11 "path/filepath" 12 "strconv" 13 "strings" 14 15 "modernc.org/golex/lex" 16 "modernc.org/mathutil" 17 "modernc.org/xc" 18 ) 19 20 var ( 21 _ tokenReader = (*tokenBuf)(nil) 22 _ tokenReader = (*tokenPipe)(nil) 23 ) 24 25 const ( 26 maxIncludeLevel = 100 27 sentinel = -1 28 ) 29 30 var ( 31 protectedMacros = map[int]bool{ 32 idDate: true, 33 idDefined: true, 34 idFile: true, 35 idLine: true, 36 idSTDC: true, 37 idSTDCHosted: true, 38 idSTDCMBMightNeqWc: true, 39 idSTDCVersion: true, 40 idTime: true, 41 idVAARGS: true, 42 } 43 ) 44 45 // Macro represents a C preprocessor macro. 46 type Macro struct { 47 Args []int // Numeric IDs of argument identifiers. 48 DefTok xc.Token // Macro name definition token. 49 IsFnLike bool // Whether the macro is function like. 50 Type Type // Non nil if macro expands to a constant expression. 51 Value interface{} // Non nil if macro expands to a constant expression. 52 ellipsis bool // Macro definition uses the idList, ... notation. 53 ellipsis2 bool // Macro definition uses the idList... notation. 54 nonRepl []bool // Non replaceable, due to # or ##, arguments of a fn-like macro. 55 repl PPTokenList // 56 } 57 58 // ReplacementToks returns the tokens that replace m. 59 func (m *Macro) ReplacementToks() (r []xc.Token) { return decodeTokens(m.repl, nil, false) } 60 61 func (m *Macro) findArg(nm int) int { 62 for i, v := range m.Args { 63 if v == nm { 64 return i 65 } 66 } 67 68 if m.ellipsis && nm == idVAARGS { 69 return len(m.Args) 70 } 71 72 return -1 73 } 74 75 type macros struct { 76 m map[int]*Macro 77 pp *pp 78 stack map[int][]*Macro 79 } 80 81 func newMacros() *macros { 82 return ¯os{ 83 m: map[int]*Macro{}, 84 stack: map[int][]*Macro{}, 85 } 86 } 87 88 func (m *macros) macros() map[int]*Macro { 89 p := m.pp 90 defer func(ie bool) { 91 p.report.IgnoreErrors = ie 92 }(p.report.IgnoreErrors) 93 94 p.report.IgnoreErrors = true 95 r := map[int]*Macro{} 96 for id, macro := range m.m { 97 r[id] = macro 98 99 if macro.IsFnLike { 100 continue 101 } 102 103 rl := macro.repl 104 if rl == 0 { 105 macro.Value = true // #define foo -> foo: true. 106 macro.Type = p.model.BoolType 107 continue 108 } 109 110 macro.Value, macro.Type = p.lx.parsePPConstExpr0(rl, p) 111 } 112 return r 113 } 114 115 type tokenReader interface { 116 eof(more bool) bool 117 peek() xc.Token 118 read() xc.Token 119 unget([]xc.Token) 120 } 121 122 type tokenBuf struct { 123 toks []xc.Token 124 } 125 126 // Implements tokenReader. 127 func (t *tokenBuf) eof(bool) bool { return len(t.toks) == 0 } 128 129 // Implements tokenReader. 130 func (t *tokenBuf) peek() xc.Token { return t.toks[0] } 131 132 // Implements tokenReader. 133 func (t *tokenBuf) read() xc.Token { r := t.peek(); t.toks = t.toks[1:]; return r } 134 135 // Implements tokenReader. 136 func (t *tokenBuf) unget(toks []xc.Token) { t.toks = append(toks[:len(toks):len(toks)], t.toks...) } 137 138 type tokenPipe struct { 139 ack chan struct{} 140 ackMore bool 141 closed bool 142 in []xc.Token 143 last xc.Token 144 out []xc.Token 145 r chan []xc.Token 146 w chan []xc.Token 147 } 148 149 // Implements tokenReader. 150 func (t *tokenPipe) eof(more bool) bool { 151 again: 152 if len(t.in) != 0 { 153 return false 154 } 155 156 if t.closed { 157 return true 158 } 159 160 t.flush(false) 161 if !more { 162 return true 163 } 164 165 if t.ackMore { 166 t.ack <- struct{}{} 167 } 168 var ok bool 169 if t.in, ok = <-t.r; !ok { 170 t.closed = true 171 return true 172 } 173 174 if len(t.in) != 0 && t.last.Rune == ' ' && t.in[0].Rune == ' ' { 175 t.in = t.in[1:] 176 goto again 177 } 178 179 if n := len(t.in); n > 1 && t.in[n-1].Rune == ' ' && t.in[n-2].Rune == ' ' { 180 t.in = t.in[:n-1] 181 goto again 182 } 183 184 return false 185 } 186 187 // Implements tokenReader. 188 func (t *tokenPipe) peek() xc.Token { return t.in[0] } 189 190 // Implements tokenReader. 191 func (t *tokenPipe) read() xc.Token { 192 r := t.peek() 193 t.in = t.in[1:] 194 t.last = r 195 return r 196 } 197 198 // Implements tokenReader. 199 func (t *tokenPipe) unget(toks []xc.Token) { 200 t.in = append(toks[:len(toks):len(toks)], t.in...) 201 } 202 203 func (t *tokenPipe) flush(final bool) { 204 t.out = trimSpace(t.out, false) 205 if n := len(t.out); !final && n != 0 { 206 if tok := t.out[n-1]; tok.Rune == STRINGLITERAL || tok.Rune == LONGSTRINGLITERAL { 207 // Accumulate lines b/c of possible string concatenation of preprocessing phase 6. 208 return 209 } 210 } 211 212 // Preproc phase 6. Adjacent string literal tokens are concatenated. 213 w := 0 214 for r := 0; r < len(t.out); r++ { 215 v := t.out[r] 216 switch v.Rune { 217 case IDENTIFIER_NONREPL: 218 v.Rune = IDENTIFIER 219 t.out[w] = v 220 w++ 221 case STRINGLITERAL, LONGSTRINGLITERAL: 222 to := r 223 loop: 224 for to < len(t.out)-1 { 225 switch t.out[to+1].Rune { 226 case STRINGLITERAL, LONGSTRINGLITERAL, ' ': 227 to++ 228 default: 229 break loop 230 } 231 } 232 for t.out[to].Rune == ' ' { 233 to-- 234 } 235 if to == r { 236 t.out[w] = v 237 w++ 238 break 239 } 240 241 var buf bytes.Buffer 242 s := v.S() 243 s = s[:len(s)-1] // Remove trailing " 244 buf.Write(s) 245 for i := r + 1; i <= to; i++ { 246 if t.out[i].Rune == ' ' { 247 continue 248 } 249 250 s = dict.S(t.out[i].Val) 251 s = s[1 : len(s)-1] // Remove leading and trailing " 252 buf.Write(s) 253 } 254 r = to 255 buf.WriteByte('"') 256 v.Val = dict.ID(buf.Bytes()) 257 fallthrough 258 default: 259 t.out[w] = v 260 w++ 261 } 262 } 263 t.out = t.out[:w] 264 if w == 0 { 265 return 266 } 267 268 t.w <- t.out 269 t.out = nil 270 } 271 272 type pp struct { 273 ack chan struct{} // Must be unbuffered. 274 expandingMacros map[int]int // 275 in chan []xc.Token // Must be unbuffered. 276 includeLevel int // 277 includedSearchPath string // 278 includes []string // 279 lx *lexer // 280 macros *macros // 281 model *Model // 282 ppf *PreprocessingFile // 283 protectMacros bool // 284 report *xc.Report // 285 sysIncludes []string // 286 tweaks *tweaks // 287 } 288 289 func newPP(ch chan []xc.Token, includes, sysIncludes []string, macros *macros, protectMacros bool, model *Model, report *xc.Report, tweaks *tweaks) *pp { 290 var err error 291 if includes, err = dedupAbsPaths(append(includes[:len(includes):len(includes)], sysIncludes...)); err != nil { 292 report.Err(0, "%s", err) 293 return nil 294 } 295 pp := &pp{ 296 ack: make(chan struct{}), 297 expandingMacros: map[int]int{}, 298 in: make(chan []xc.Token), 299 includes: includes, 300 lx: newSimpleLexer(nil, report, tweaks), 301 macros: macros, 302 model: model, 303 protectMacros: protectMacros, 304 report: report, 305 sysIncludes: sysIncludes, 306 tweaks: tweaks, 307 } 308 macros.pp = pp 309 pp.lx.model = model 310 model.initialize(pp.lx) 311 go pp.pp2(ch) 312 return pp 313 } 314 315 func (p *pp) pp2(ch chan []xc.Token) { 316 pipe := &tokenPipe{ack: p.ack, r: p.in, w: ch} 317 for !pipe.eof(true) { 318 pipe.ackMore = true 319 p.expand(pipe, false, func(toks []xc.Token) { pipe.out = append(pipe.out, toks...) }) 320 pipe.ackMore = false 321 p.ack <- struct{}{} 322 } 323 pipe.flush(true) 324 p.ack <- struct{}{} 325 } 326 327 func (p *pp) checkCompatibleReplacementTokenList(tok xc.Token, oldList, newList PPTokenList) { 328 ex := trimSpace(decodeTokens(oldList, nil, true), false) 329 toks := trimSpace(decodeTokens(newList, nil, true), false) 330 331 if g, e := len(toks), len(ex); g != e && len(ex) > 0 { 332 p.report.ErrTok(tok, "cannot redefine macro using a replacement list of different length") 333 return 334 } 335 336 if len(toks) == 0 || len(ex) == 0 { 337 return 338 } 339 340 if g, e := whitespace(toks), whitespace(ex); !bytes.Equal(g, e) { 341 p.report.ErrTok(tok, "cannot redefine macro, whitespace differs") 342 } 343 344 for i, g := range toks { 345 if e := ex[i]; g.Rune != e.Rune || g.Val != e.Val { 346 p.report.ErrTok(tok, "cannot redefine macro using a different replacement list") 347 return 348 } 349 } 350 } 351 352 func (p *pp) defineMacro(tok xc.Token, repl PPTokenList) { 353 nm := tok.Val 354 if protectedMacros[nm] && p.protectMacros { 355 p.report.ErrTok(tok, "cannot define protected macro") 356 return 357 } 358 359 m := p.macros.m[nm] 360 if m == nil { 361 if debugMacros { 362 toks := trimSpace(decodeTokens(repl, nil, true), false) 363 var a [][]byte 364 for _, v := range toks { 365 a = append(a, xc.Dict.S(tokVal(v))) 366 } 367 fmt.Fprintf(os.Stderr, "%s: #define %s %s\n", tok.Position(), tok.S(), bytes.Join(a, nil)) 368 } 369 p.macros.m[nm] = &Macro{DefTok: tok, repl: repl} 370 return 371 } 372 373 if m.IsFnLike { 374 p.report.ErrTok(tok, "cannot redefine a function-like macro using an object-like macro") 375 return 376 } 377 378 p.checkCompatibleReplacementTokenList(tok, m.repl, repl) 379 } 380 381 func (p *pp) defineFnMacro(tok xc.Token, il *IdentifierList, repl PPTokenList, ellipsis, ellipsis2 bool) { 382 nm0 := tok.S() 383 nm := dict.ID(nm0[:len(nm0)-1]) 384 if protectedMacros[nm] && p.protectMacros { 385 p.report.ErrTok(tok, "cannot define protected macro %s", xc.Dict.S(nm)) 386 return 387 } 388 389 var args []int 390 for ; il != nil; il = il.IdentifierList { 391 tok := il.Token2 392 if !tok.IsValid() { 393 tok = il.Token 394 } 395 args = append(args, tok.Val) 396 } 397 m := p.macros.m[nm] 398 defTok := tok 399 defTok.Rune = IDENTIFIER 400 defTok.Val = nm 401 if m == nil { 402 replToks := decodeTokens(repl, nil, false) 403 if debugMacros { 404 toks := trimSpace(replToks, false) 405 var p [][]byte 406 for _, v := range args { 407 p = append(p, xc.Dict.S(v)) 408 } 409 var a [][]byte 410 for _, v := range toks { 411 a = append(a, xc.Dict.S(tokVal(v))) 412 } 413 fmt.Fprintf(os.Stderr, "%s: #define %s%s) %s\n", tok.Position(), tok.S(), bytes.Join(p, []byte(", ")), bytes.Join(a, nil)) 414 } 415 nonRepl := make([]bool, len(args)) 416 mp := map[int]struct{}{} 417 for i, v := range replToks { 418 switch v.Rune { 419 case PPPASTE: 420 if i > 0 { 421 if tok := replToks[i-1]; tok.Rune == IDENTIFIER { 422 mp[tok.Val] = struct{}{} 423 } 424 } 425 fallthrough 426 case '#': 427 if i < len(replToks)-1 { 428 if tok := replToks[i+1]; tok.Rune == IDENTIFIER { 429 mp[tok.Val] = struct{}{} 430 } 431 } 432 } 433 } 434 m := &Macro{Args: args, DefTok: defTok, IsFnLike: true, repl: repl, ellipsis: ellipsis, ellipsis2: ellipsis2} 435 for nm := range mp { 436 if i := m.findArg(nm); i >= 0 && i < len(nonRepl) { 437 nonRepl[i] = true 438 } 439 } 440 m.nonRepl = nonRepl 441 p.macros.m[nm] = m 442 return 443 } 444 445 if !m.IsFnLike { 446 p.report.ErrTok(tok, "cannot redefine an object-like macro %s using a function-like macro", xc.Dict.S(nm)) 447 return 448 } 449 450 if g, e := len(args), len(m.Args); g != e { 451 p.report.ErrTok(tok, "cannot redefine macro %s: number of arguments differ", xc.Dict.S(nm)) 452 return 453 } 454 455 for i, g := range args { 456 if e := m.Args[i]; g != e { 457 p.report.ErrTok(tok, "cannot redefine macro %s: argument names differ", xc.Dict.S(nm)) 458 return 459 } 460 } 461 462 p.checkCompatibleReplacementTokenList(tok, m.repl, repl) 463 } 464 465 func (p *pp) expand(r tokenReader, handleDefined bool, w func([]xc.Token)) { 466 for !r.eof(false) { 467 tok := r.read() 468 switch tok.Rune { 469 case sentinel: 470 p.expandingMacros[tok.Val]-- 471 case IDENTIFIER: 472 if tok.Val == idFile { 473 tok.Rune = STRINGLITERAL 474 tok.Val = dict.SID(fmt.Sprintf("%q", tok.Position().Filename)) 475 w([]xc.Token{tok}) 476 continue 477 } 478 479 if tok.Val == idLine && !p.tweaks.disablePredefinedLineMacro { 480 tok.Rune = INTCONST 481 tok.Val = dict.SID(strconv.Itoa(position(tok.Pos()).Line)) 482 w([]xc.Token{tok}) 483 continue 484 } 485 486 if handleDefined && tok.Val == idDefined { 487 p.expandDefined(tok, r, w) 488 continue 489 } 490 491 m := p.macros.m[tok.Val] 492 if m == nil { 493 w([]xc.Token{tok}) 494 continue 495 } 496 497 p.expandMacro(tok, r, m, handleDefined, w) 498 default: 499 w([]xc.Token{tok}) 500 } 501 } 502 } 503 504 func (p *pp) expandDefined(tok xc.Token, r tokenReader, w func([]xc.Token)) { 505 again: 506 if r.eof(false) { 507 p.report.ErrTok(tok, "'defined' with no argument") 508 return 509 } 510 511 switch tok = r.read(); tok.Rune { 512 case ' ': 513 goto again 514 case '(': // defined (IDENTIFIER) 515 again2: 516 if r.eof(false) { 517 p.report.ErrTok(tok, "'defined' with no argument") 518 return 519 } 520 521 tok = r.read() 522 switch tok.Rune { 523 case IDENTIFIER: 524 v := tok 525 v.Rune = INTCONST 526 if p.macros.m[tok.Val] != nil { 527 v.Val = id1 528 } else { 529 v.Val = id0 530 } 531 532 again3: 533 if r.eof(false) { 534 p.report.ErrTok(tok, "must be followed by ')'") 535 return 536 } 537 538 tok = r.read() 539 if tok.Rune == ' ' { 540 goto again3 541 } 542 543 if tok.Rune != ')' { 544 p.report.ErrTok(tok, "expected ')'") 545 return 546 } 547 548 w([]xc.Token{v}) 549 case ' ': 550 goto again2 551 default: 552 p.report.ErrTok(tok, "expected identifier") 553 return 554 } 555 case IDENTIFIER: 556 v := tok 557 v.Rune = INTCONST 558 if p.macros.m[tok.Val] != nil { 559 v.Val = id1 560 } else { 561 v.Val = id0 562 } 563 564 w([]xc.Token{v}) 565 default: 566 panic(PrettyString(tok)) 567 } 568 } 569 570 func (p *pp) expandMacro(tok xc.Token, r tokenReader, m *Macro, handleDefined bool, w func([]xc.Token)) { 571 nm := tok.Val 572 if m.IsFnLike { 573 p.expandFnMacro(tok, r, m, handleDefined, w) 574 return 575 } 576 577 repl := trimSpace(normalizeToks(decodeTokens(m.repl, nil, true)), false) 578 repl = pasteToks(repl) 579 pos := tok.Pos() 580 for i, v := range repl { 581 repl[i].Char = lex.NewChar(pos, v.Rune) 582 } 583 tok.Rune = sentinel 584 p.expandingMacros[nm]++ 585 y := append(p.sanitize(p.expandLineNo(p.pragmas(repl))), tok) 586 r.unget(y) 587 } 588 589 func trimSpace(toks []xc.Token, removeTrailingComma bool) []xc.Token { 590 if len(toks) == 0 { 591 return nil 592 } 593 594 if removeTrailingComma { 595 if tok := toks[len(toks)-1]; tok.Rune == ',' { 596 toks = toks[:len(toks)-1] 597 } 598 } 599 for len(toks) != 0 && toks[0].Rune == ' ' { 600 toks = toks[1:] 601 } 602 for len(toks) != 0 && toks[len(toks)-1].Rune == ' ' { 603 toks = toks[:len(toks)-1] 604 } 605 return toks 606 } 607 608 func (p *pp) pragmas(toks []xc.Token) []xc.Token { 609 var r []xc.Token 610 for len(toks) != 0 { 611 switch tok := toks[0]; { 612 case tok.Rune == IDENTIFIER && tok.Val == idPragma: 613 toks = toks[1:] 614 for len(toks) != 0 && toks[0].Rune == ' ' { 615 toks = toks[1:] 616 } 617 if len(toks) == 0 { 618 p.report.ErrTok(tok, "malformed _Pragma unary operator expression.") 619 return r 620 } 621 622 if toks[0].Rune != '(' { 623 p.report.ErrTok(toks[0], "expected '('") 624 return r 625 } 626 627 toks = toks[1:] 628 for len(toks) != 0 && toks[0].Rune == ' ' { 629 toks = toks[1:] 630 } 631 if len(toks) == 0 { 632 p.report.ErrTok(tok, "malformed _Pragma unary operator expression.") 633 return r 634 } 635 636 if toks[0].Rune != STRINGLITERAL && toks[0].Rune != LONGSTRINGLITERAL { 637 p.report.ErrTok(toks[0], "expected string literal or long string literal") 638 return r 639 } 640 641 toks = toks[1:] 642 for len(toks) != 0 && toks[0].Rune == ' ' { 643 toks = toks[1:] 644 } 645 if len(toks) == 0 { 646 p.report.ErrTok(tok, "malformed _Pragma unary operator expression.") 647 return r 648 } 649 650 if toks[0].Rune != ')' { 651 p.report.ErrTok(toks[0], "expected ')'") 652 return r 653 } 654 655 toks = toks[1:] 656 default: 657 r = append(r, tok) 658 toks = toks[1:] 659 } 660 } 661 return r 662 } 663 664 func (p *pp) sanitize(toks []xc.Token) []xc.Token { 665 w := 0 666 for _, v := range toks { 667 switch v.Rune { 668 case 0: 669 // nop 670 case IDENTIFIER: 671 if p.expandingMacros[v.Val] != 0 { 672 v.Rune = IDENTIFIER_NONREPL 673 } 674 fallthrough 675 default: 676 toks[w] = v 677 w++ 678 } 679 } 680 return toks[:w] 681 } 682 683 func pasteToks(toks []xc.Token) []xc.Token { 684 for i := 0; i < len(toks); { 685 switch tok := toks[i]; tok.Rune { 686 case PPPASTE: 687 var b []byte 688 var r rune 689 var v int 690 if i > 0 { 691 i-- 692 t := toks[i] 693 r = t.Rune 694 if r == IDENTIFIER_NONREPL { 695 // testdata/gcc-6.3.0/gcc/testsuite/gcc.c-torture/compile/981001-3.c 696 r = IDENTIFIER 697 } 698 v = t.Val 699 b = append(b, xc.Dict.S(tokVal(t))...) 700 toks = append(toks[:i], toks[i+1:]...) // Remove left arg. 701 } 702 if i < len(toks)-1 { 703 i++ 704 t := toks[i] 705 switch { 706 case r == 0: 707 r = t.Rune 708 case r == IDENTIFIER && v == idL: 709 switch t.Rune { 710 case CHARCONST: 711 r = LONGCHARCONST 712 case STRINGLITERAL: 713 r = LONGSTRINGLITERAL 714 } 715 } 716 b = append(b, xc.Dict.S(tokVal(t))...) 717 toks = append(toks[:i], toks[i+1:]...) // Remove right arg. 718 i-- 719 } 720 tok.Rune = r 721 tok.Val = xc.Dict.ID(b) 722 if tok.Rune < 0x80 && tok.Val > 0x80 { 723 tok.Rune = PPOTHER 724 } 725 toks[i] = tok 726 default: 727 i++ 728 } 729 } 730 return toks 731 } 732 733 func (p *pp) expandLineNo(toks []xc.Token) []xc.Token { 734 for i, v := range toks { 735 if v.Rune == IDENTIFIER && v.Val == idLine && !p.tweaks.disablePredefinedLineMacro { 736 v.Rune = INTCONST 737 v.Val = dict.SID(strconv.Itoa(position(v.Pos()).Line)) 738 toks[i] = v 739 } 740 } 741 return toks 742 } 743 744 func normalizeToks(toks []xc.Token) []xc.Token { 745 if len(toks) == 0 { 746 return toks 747 } 748 749 for i := 0; i < len(toks); { 750 switch toks[i].Rune { 751 case PPPASTE: 752 if i > 0 && toks[i-1].Rune == ' ' { 753 i-- 754 toks = append(toks[:i], toks[i+1:]...) 755 break 756 } 757 758 fallthrough 759 case '#': 760 if i < len(toks)-1 && toks[i+1].Rune == ' ' { 761 j := i + 1 762 toks = append(toks[:j], toks[j+1:]...) 763 break 764 } 765 766 fallthrough 767 default: 768 i++ 769 } 770 } 771 return toks 772 } 773 774 func (p *pp) expandFnMacro(tok xc.Token, r tokenReader, m *Macro, handleDefined bool, w func([]xc.Token)) { 775 nm := tok.Val 776 var sentinels []xc.Token 777 again: 778 if r.eof(true) { 779 r.unget(sentinels) 780 w([]xc.Token{tok}) 781 return 782 } 783 784 switch c := r.peek().Rune; { 785 case c == ' ': 786 r.read() 787 goto again 788 case c == sentinel: 789 s := r.read() 790 sentinels = append([]xc.Token{s}, sentinels...) 791 goto again 792 case c != '(': // != name() 793 r.unget(sentinels) 794 w([]xc.Token{tok}) 795 return 796 } 797 798 args := p.parseMacroArgs(r) 799 if g, e := len(args), len(m.Args); g != e { 800 switch { 801 case g == 1 && e == 0 && len(args[0]) == 0: 802 // Spacial case: Handling of empty args to macros with 803 // one parameter makes it non distinguishable of 804 // passing no argument to a macro with no parameters. 805 806 // ok, nop. 807 case m.ellipsis: 808 if g < e { 809 p.report.ErrTok(tok, "not enough macro arguments, expected at least %v", e+1) 810 return 811 } 812 813 for i := e + 1; i < len(args); i++ { 814 args[e] = append(args[e], args[i]...) 815 } 816 args = args[:e+1] 817 case m.ellipsis2: 818 if g < e { 819 p.report.ErrTok(tok, "not enough macro arguments, expected at least %v", e) 820 return 821 } 822 823 for i := e; i < len(args); i++ { 824 args[e-1] = append(args[e-1], args[i]...) 825 } 826 args = args[:e] 827 default: 828 p.report.ErrTok(tok, "macro argument count mismatch: got %v, expected %v", g, e) 829 return 830 } 831 } 832 833 for i, arg := range args { 834 args[i] = trimSpace(arg, true) 835 } 836 for i, arg := range args { 837 args[i] = nil 838 toks := p.expandLineNo(arg) 839 if i < len(m.nonRepl) && m.nonRepl[i] { 840 if len(toks) != 0 { 841 args[i] = toks 842 } 843 continue 844 } 845 846 p.expand(&tokenBuf{toks}, handleDefined, func(toks []xc.Token) { args[i] = append(args[i], toks...) }) 847 } 848 repl := trimSpace(normalizeToks(decodeTokens(m.repl, nil, true)), false) 849 for i, v := range repl { 850 repl[i].Char = lex.NewChar(tok.Pos(), v.Rune) 851 } 852 var r0 []xc.Token 853 next: 854 for i, tok := range repl { 855 switch tok.Rune { 856 case IDENTIFIER: 857 if ia := m.findArg(tok.Val); ia >= 0 { 858 if i > 0 && repl[i-1].Rune == '#' { 859 r0 = append(r0[:len(r0)-1], stringify(args[ia])) 860 continue next 861 } 862 863 var arg []xc.Token 864 if ia < len(args) { 865 arg = args[ia] 866 } 867 if len(arg) == 0 { 868 arg = []xc.Token{{}} 869 } 870 r0 = append(r0, arg...) 871 872 continue next 873 } 874 875 r0 = append(r0, tok) 876 default: 877 r0 = append(r0, tok) 878 } 879 } 880 881 tok.Rune = sentinel 882 sentinels = append([]xc.Token{tok}, sentinels...) 883 p.expandingMacros[nm]++ 884 y := append(p.sanitize(p.pragmas(p.expandLineNo(pasteToks(r0)))), sentinels...) 885 r.unget(y) 886 } 887 888 func stringify(toks []xc.Token) xc.Token { 889 toks = trimSpace(toks, false) 890 if len(toks) == 0 || (toks[0] == xc.Token{}) { 891 return xc.Token{Char: lex.NewChar(0, STRINGLITERAL), Val: idEmptyString} 892 } 893 894 s := []byte{'"'} 895 for _, tok := range toks { 896 switch tok.Rune { 897 case CHARCONST, STRINGLITERAL, LONGSTRINGLITERAL, LONGCHARCONST: 898 for _, c := range tok.S() { 899 switch c { 900 case '"', '\\': 901 s = append(s, '\\', c) 902 default: 903 s = append(s, c) 904 } 905 } 906 default: 907 s = append(s, xc.Dict.S(tokVal(tok))...) 908 } 909 } 910 s = append(s, '"') 911 r := xc.Token{Char: lex.NewChar(toks[0].Pos(), STRINGLITERAL), Val: dict.ID(s)} 912 return r 913 } 914 915 func whitespace(toks []xc.Token) []byte { 916 if len(toks) < 2 { 917 return nil 918 } 919 920 r := make([]byte, 0, len(toks)-1) 921 ltok := toks[0] 922 for _, tok := range toks[1:] { 923 if ltok.Rune == ' ' { 924 continue 925 } 926 927 switch { 928 case tok.Rune == ' ': 929 r = append(r, 1) 930 default: 931 r = append(r, 0) 932 } 933 ltok = tok 934 } 935 return r 936 } 937 938 func (p *pp) parseMacroArgs(r tokenReader) (args [][]xc.Token) { 939 if r.eof(true) { 940 panic("internal error") 941 } 942 943 tok := r.read() 944 if tok.Rune != '(' { 945 p.report.ErrTok(tok, "expected '('") 946 return nil 947 } 948 949 for !r.eof(true) { 950 arg, more := p.parseMacroArg(r) 951 args = append(args, arg) 952 if more { 953 continue 954 } 955 956 if r.eof(true) || r.peek().Rune == ')' { 957 break 958 } 959 } 960 961 if r.eof(true) { 962 p.report.ErrTok(tok, "missing final ')'") 963 return nil 964 } 965 966 tok = r.read() 967 if tok.Rune != ')' { 968 p.report.ErrTok(tok, "expected ')'") 969 } 970 971 return args 972 } 973 974 func (p *pp) parseMacroArg(r tokenReader) (arg []xc.Token, more bool) { 975 n := 0 976 tok := r.peek() 977 for { 978 if r.eof(true) { 979 p.report.ErrTok(tok, "unexpected end of line after token") 980 return arg, false 981 } 982 983 tok = r.peek() 984 switch tok.Rune { 985 case '(': 986 arg = append(arg, r.read()) 987 n++ 988 case ')': 989 if n == 0 { 990 return arg, false 991 } 992 993 arg = append(arg, r.read()) 994 n-- 995 case ',': 996 if n == 0 { 997 arg = append(arg, r.read()) 998 return arg, true 999 } 1000 1001 arg = append(arg, r.read()) 1002 default: 1003 arg = append(arg, r.read()) 1004 } 1005 } 1006 } 1007 1008 func (p *pp) preprocessingFile(n *PreprocessingFile) { 1009 ppf := p.ppf 1010 p.ppf = n 1011 p.groupList(n.GroupList) 1012 p.ppf = ppf 1013 if p.includeLevel == 0 { 1014 close(p.in) 1015 <-p.ack 1016 } 1017 } 1018 1019 func (p *pp) groupList(n *GroupList) { 1020 for ; n != nil; n = n.GroupList { 1021 switch gp := n.GroupPart.(type) { 1022 case nil: // PPNONDIRECTIVE PPTokenList 1023 // nop 1024 case *ControlLine: 1025 p.controlLine(gp) 1026 case *IfSection: 1027 p.ifSection(gp) 1028 case PPTokenList: // TextLine 1029 if gp == 0 { 1030 break 1031 } 1032 1033 toks := decodeTokens(gp, nil, true) 1034 for _, v := range toks { 1035 if v.Rune != ' ' { 1036 p.in <- toks 1037 <-p.ack 1038 break 1039 } 1040 } 1041 case xc.Token: 1042 if p.tweaks.enableWarnings { 1043 fmt.Printf("[INFO] %s at %s\n", gp.S(), xc.FileSet.Position(gp.Pos()).String()) 1044 } 1045 default: 1046 panic("internal error") 1047 } 1048 } 1049 } 1050 1051 func (p *pp) ifSection(n *IfSection) { 1052 if p.ifGroup(n.IfGroup) || p.elifGroupListOpt(n.ElifGroupListOpt) { 1053 return 1054 } 1055 1056 p.elseGroupOpt(n.ElseGroupOpt) 1057 } 1058 1059 func (p *pp) ifGroup(n *IfGroup) bool { 1060 switch n.Case { 1061 case 0: // PPIF PPTokenList GroupListOpt 1062 if !p.lx.parsePPConstExpr(n.PPTokenList, p) { 1063 return false 1064 } 1065 case 1: // PPIFDEF IDENTIFIER '\n' GroupListOpt 1066 if m := p.macros.m[n.Token2.Val]; m == nil { 1067 return false 1068 } 1069 case 2: // PPIFNDEF IDENTIFIER '\n' GroupListOpt 1070 if m := p.macros.m[n.Token2.Val]; m != nil { 1071 return false 1072 } 1073 default: 1074 panic(n.Case) 1075 } 1076 p.groupListOpt(n.GroupListOpt) 1077 return true 1078 } 1079 1080 func (p *pp) elifGroupListOpt(n *ElifGroupListOpt) bool { 1081 if n == nil { 1082 return false 1083 } 1084 1085 return p.elifGroupList(n.ElifGroupList) 1086 } 1087 1088 func (p *pp) elifGroupList(n *ElifGroupList) bool { 1089 for ; n != nil; n = n.ElifGroupList { 1090 if p.elifGroup(n.ElifGroup) { 1091 return true 1092 } 1093 } 1094 1095 return false 1096 } 1097 1098 func (p *pp) elifGroup(n *ElifGroup) bool { 1099 if !p.lx.parsePPConstExpr(n.PPTokenList, p) { 1100 return false 1101 } 1102 1103 p.groupListOpt(n.GroupListOpt) 1104 return true 1105 } 1106 1107 func (p *pp) elseGroupOpt(n *ElseGroupOpt) { 1108 if n == nil { 1109 return 1110 } 1111 1112 p.groupListOpt(n.ElseGroup.GroupListOpt) 1113 } 1114 1115 func (p *pp) groupListOpt(n *GroupListOpt) { 1116 if n == nil { 1117 return 1118 } 1119 1120 p.groupList(n.GroupList) 1121 } 1122 1123 func (p *pp) fixInclude(toks []xc.Token) []xc.Token { 1124 again: 1125 if len(toks) == 0 { 1126 return nil 1127 } 1128 1129 switch toks[0].Rune { 1130 case ' ': 1131 toks = toks[1:] 1132 goto again 1133 case STRINGLITERAL, PPHEADER_NAME: 1134 return toks 1135 case '<': 1136 for i := 1; i < len(toks); i++ { 1137 if toks[i].Rune == '>' { 1138 r := stringify(toks[1:i]) 1139 return []xc.Token{r} 1140 } 1141 } 1142 1143 return nil 1144 default: 1145 return nil 1146 } 1147 } 1148 1149 func (p *pp) pragma1(a []xc.Token) (t xc.Token, _ bool) { 1150 if len(a) != 3 || a[0].Rune != '(' || a[1].Rune != STRINGLITERAL || a[2].Rune != ')' { 1151 return t, false 1152 } 1153 1154 return a[1], true 1155 } 1156 1157 func (p *pp) pragma(a []xc.Token) { 1158 if len(a) == 0 { 1159 return 1160 } 1161 1162 switch t := a[0]; t.Val { 1163 case idPushMacro: 1164 t, ok := p.pragma1(a[1:]) 1165 if !ok { 1166 break 1167 } 1168 1169 s := dict.S(t.Val) 1170 nm := dict.ID(s[1 : len(s)-1]) 1171 m := p.macros.m[nm] 1172 if m == nil { 1173 break 1174 } 1175 1176 p.macros.stack[nm] = append(p.macros.stack[nm], m) 1177 case idPopMacro: 1178 t, ok := p.pragma1(a[1:]) 1179 if !ok { 1180 break 1181 } 1182 1183 s := dict.S(t.Val) 1184 nm := dict.ID(s[1 : len(s)-1]) 1185 stack := p.macros.stack[nm] 1186 if len(stack) == 0 { 1187 break 1188 } 1189 1190 m := stack[0] 1191 p.macros.stack[nm] = stack[1:] 1192 p.macros.m[nm] = m 1193 } 1194 } 1195 1196 func (p *pp) controlLine(n *ControlLine) { 1197 out: 1198 switch n.Case { 1199 case 0: // PPDEFINE IDENTIFIER ReplacementList 1200 p.defineMacro(n.Token2, n.ReplacementList) 1201 case 1: // PPDEFINE IDENTIFIER_LPAREN "..." ')' ReplacementList 1202 p.defineFnMacro(n.Token2, nil, n.ReplacementList, true, false) 1203 case 2: // PPDEFINE IDENTIFIER_LPAREN IdentifierList ',' "..." ')' ReplacementList 1204 p.defineFnMacro(n.Token2, n.IdentifierList, n.ReplacementList, true, false) 1205 case 3: // PPDEFINE IDENTIFIER_LPAREN IdentifierListOpt ')' ReplacementList 1206 var l *IdentifierList 1207 if o := n.IdentifierListOpt; o != nil { 1208 l = o.IdentifierList 1209 } 1210 p.defineFnMacro(n.Token2, l, n.ReplacementList, false, false) 1211 case 5: // PPHASH_NL 1212 // nop 1213 case 4: // PPERROR PPTokenListOpt 1214 var sep string 1215 toks := decodeTokens(n.PPTokenListOpt, nil, true) 1216 s := stringify(toks) 1217 if s.Val != 0 { 1218 sep = ": " 1219 } 1220 p.report.ErrTok(n.Token, "error%s%s", sep, s.S()) 1221 case 6: // PPINCLUDE PPTokenList 1222 toks := decodeTokens(n.PPTokenList, nil, false) 1223 var exp []xc.Token 1224 p.expand(&tokenBuf{toks}, false, func(toks []xc.Token) { exp = append(exp, toks...) }) 1225 toks = p.fixInclude(exp) 1226 if len(toks) == 0 { 1227 p.report.ErrTok(n.Token, "invalid #include argument") 1228 break 1229 } 1230 1231 if p.includeLevel == maxIncludeLevel { 1232 p.report.ErrTok(toks[0], "too many include nesting levels") 1233 break 1234 } 1235 1236 currentFileDir := filepath.Dir(p.ppf.path) 1237 arg := string(toks[0].S()) 1238 var dirs []string 1239 switch { 1240 case strings.HasPrefix(arg, "<"): 1241 switch { 1242 case p.tweaks.mode99c: 1243 dirs = append([]string(nil), p.sysIncludes...) 1244 default: 1245 dirs = append(p.includes, p.sysIncludes...) 1246 } 1247 case strings.HasPrefix(arg, "\""): 1248 switch { 1249 case p.tweaks.mode99c: 1250 dirs = append([]string(nil), p.includes...) 1251 default: 1252 dirs = p.includes 1253 dirs = append([]string{filepath.Dir(p.ppf.path)}, dirs...) 1254 } 1255 default: 1256 p.report.ErrTok(n.Token, "invalid #include argument") 1257 break out 1258 } 1259 1260 // Include origin. 1261 arg = arg[1 : len(arg)-1] 1262 for i, dir := range dirs { 1263 if p.tweaks.mode99c && dir == "@" { 1264 dir = currentFileDir 1265 dirs[i] = dir 1266 } 1267 pth := arg 1268 if !filepath.IsAbs(pth) { 1269 pth = filepath.Join(dir, arg) 1270 } 1271 if _, err := os.Stat(pth); err != nil { 1272 if !os.IsNotExist(err) { 1273 p.report.ErrTok(toks[0], err.Error()) 1274 } 1275 if debugIncludes { 1276 fmt.Fprintf(os.Stderr, "include file %q not found\n", pth) 1277 } 1278 continue 1279 } 1280 1281 ppf, err := ppParse(pth, p.report, p.tweaks) 1282 if err != nil { 1283 p.report.ErrTok(toks[0], err.Error()) 1284 return 1285 } 1286 1287 p.includeLevel++ 1288 save := p.includedSearchPath 1289 p.includedSearchPath = dir 1290 p.preprocessingFile(ppf) 1291 p.includedSearchPath = save 1292 p.includeLevel-- 1293 return 1294 } 1295 1296 p.report.ErrTok(toks[0], "include file not found: %s. Search paths:\n\t%s", arg, strings.Join(clean(dirs), "\n\t")) 1297 case 7: // PPLINE PPTokenList '\n' 1298 toks := decodeTokens(n.PPTokenList, nil, false) 1299 // lineno, fname 1300 if len(toks) < 2 || toks[0].Rune != INTCONST || toks[1].Rune != STRINGLITERAL { 1301 break 1302 } 1303 1304 ln, err := strconv.ParseUint(string(toks[0].S()), 10, mathutil.IntBits-1) 1305 if err != nil { 1306 break 1307 } 1308 1309 fn := string(toks[1].S()) 1310 fn = fn[1 : len(fn)-1] // Unquote. 1311 nl := n.Token2 1312 tf := xc.FileSet.File(nl.Pos()) 1313 tf.AddLineInfo(tf.Offset(nl.Pos()+1), fn, int(ln)) 1314 case 8: // PPPRAGMA PPTokenListOpt 1315 p.pragma(decodeTokens(n.PPTokenListOpt, nil, false)) 1316 case 1317 9, // PPUNDEF IDENTIFIER '\n' 1318 12: // PPUNDEF IDENTIFIER PPTokenList '\n' 1319 nm := n.Token2.Val 1320 if protectedMacros[nm] && p.protectMacros { 1321 p.report.ErrTok(n.Token2, "cannot undefine protected macro") 1322 return 1323 } 1324 1325 if debugMacros { 1326 fmt.Fprintf(os.Stderr, "#undef %s\n", xc.Dict.S(nm)) 1327 } 1328 delete(p.macros.m, nm) 1329 case 10: // PPDEFINE IDENTIFIER_LPAREN IdentifierList "..." ')' ReplacementList 1330 p.defineFnMacro(n.Token2, n.IdentifierList, n.ReplacementList, false, true) 1331 case 13: // PPINCLUDE_NEXT PPTokenList '\n' 1332 toks := decodeTokens(n.PPTokenList, nil, false) 1333 var exp []xc.Token 1334 p.expand(&tokenBuf{toks}, false, func(toks []xc.Token) { exp = append(exp, toks...) }) 1335 toks = p.fixInclude(exp) 1336 if len(toks) == 0 { 1337 p.report.ErrTok(n.Token, "invalid #include_next argument") 1338 break 1339 } 1340 1341 if p.includeLevel == maxIncludeLevel { 1342 p.report.ErrTok(toks[0], "too many include nesting levels") 1343 break 1344 } 1345 1346 arg := string(toks[0].S()) 1347 arg = arg[1 : len(arg)-1] 1348 origin := p.includedSearchPath 1349 var dirs []string 1350 found := false 1351 for i, dir := range p.includes { 1352 if dir == origin { 1353 dirs = p.includes[i+1:] 1354 found = true 1355 break 1356 } 1357 } 1358 if !found { 1359 for i, dir := range p.sysIncludes { 1360 if dir == origin { 1361 dirs = p.sysIncludes[i+1:] 1362 found = true 1363 break 1364 } 1365 } 1366 } 1367 1368 for _, dir := range dirs { 1369 pth := filepath.Join(dir, arg) 1370 if _, err := os.Stat(pth); err != nil { 1371 if !os.IsNotExist(err) { 1372 p.report.ErrTok(toks[0], err.Error()) 1373 } 1374 if debugIncludes { 1375 fmt.Fprintf(os.Stderr, "include file %q not found\n", pth) 1376 } 1377 continue 1378 } 1379 1380 ppf, err := ppParse(pth, p.report, p.tweaks) 1381 if err != nil { 1382 p.report.ErrTok(toks[0], err.Error()) 1383 return 1384 } 1385 1386 p.includeLevel++ 1387 save := p.includedSearchPath 1388 p.includedSearchPath = dir 1389 p.preprocessingFile(ppf) 1390 p.includedSearchPath = save 1391 p.includeLevel-- 1392 return 1393 } 1394 1395 p.report.ErrTok(toks[0], "include file not found: %s", arg) 1396 default: 1397 panic(n.Case) 1398 } 1399 }