modernc.org/cc@v1.0.1/v2/cc.go (about) 1 // Copyright 2017 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 //go:generate rm -f scanner.go trigraphs.go 6 //go:generate golex -o trigraphs.go trigraphs.l 7 //go:generate golex -o scanner.go scanner.l 8 9 //go:generate rm -f ast.go 10 //go:generate yy -kind Case -o parser.y -astImport "\"modernc.org/xc\";\"go/token\";\"fmt\"" -prettyString PrettyString parser.yy 11 12 //go:generate rm -f parser.go 13 //go:generate goyacc -o /dev/null -xegen xegen parser.y 14 //go:generate goyacc -o parser.go -pool -fs -xe xegen -dlvalf "%v" -dlval "PrettyString(lval.Token)" parser.y 15 //go:generate rm -f xegen 16 17 //go:generate stringer -output stringer.go -type=cond,Linkage,StorageDuration enum.go 18 //go:generate sh -c "go test -run ^Example |fe" 19 //go:generate gofmt -l -s -w . 20 21 // Package cc is a C99 compiler front end. Work In Progress. API unstable. 22 // 23 // This package is no longer maintained. Please see the v3 version at 24 // 25 // https://modernc.org/cc/v3 26 package cc // import "modernc.org/cc/v2" 27 28 import ( 29 "bufio" 30 "bytes" 31 "fmt" 32 "go/scanner" 33 "go/token" 34 "io" 35 "io/ioutil" 36 "os" 37 "os/exec" 38 "path/filepath" 39 "runtime" 40 "runtime/debug" 41 "sort" 42 "strings" 43 "sync" 44 45 "modernc.org/ir" 46 "modernc.org/strutil" 47 "modernc.org/xc" 48 ) 49 50 const ( 51 // CRT0Source is the source code of the C startup code. 52 CRT0Source = `int main(); 53 54 __FILE_TYPE__ __stdfiles[3]; 55 char **environ; 56 void *stdin = &__stdfiles[0], *stdout = &__stdfiles[1], *stderr = &__stdfiles[2]; 57 58 void _start(int argc, char **argv) 59 { 60 __register_stdfiles(stdin, stdout, stderr, &environ); 61 __builtin_exit(((int (*)(int, char **))main) (argc, argv)); 62 } 63 ` 64 cacheSize = 500 65 ) 66 67 var ( 68 _ Source = (*FileSource)(nil) 69 _ Source = (*StringSource)(nil) 70 71 _ debug.GCStats 72 73 // YYDebug points to parser's yyDebug variable. 74 YYDebug = &yyDebug 75 traceMacroDefs bool 76 77 cache = make(map[cacheKey][]uint32, cacheSize) 78 cacheMu sync.Mutex // Guards cache, fset 79 fset = token.NewFileSet() 80 81 packageDir string 82 headers string 83 selfImportPath string 84 ) 85 86 func init() { 87 ip, err := strutil.ImportPath() 88 if err != nil { 89 panic(err) 90 } 91 92 selfImportPath = ip 93 if packageDir, err = findRepo(ip); err != nil { 94 panic(err) 95 } 96 97 headers = filepath.Join(packageDir, "headers", fmt.Sprintf("%v_%v", env("GOOS", runtime.GOOS), env("GOARCH", runtime.GOARCH))) 98 } 99 100 func findRepo(s string) (string, error) { 101 s = filepath.FromSlash(s) 102 for _, v := range strings.Split(strutil.Gopath(), string(os.PathListSeparator)) { 103 p := filepath.Join(v, "src", s) 104 fi, err := os.Lstat(p) 105 if err != nil { 106 continue 107 } 108 109 if fi.IsDir() { 110 wd, err := os.Getwd() 111 if err != nil { 112 return "", err 113 } 114 115 if p, err = filepath.Rel(wd, p); err != nil { 116 return "", err 117 } 118 119 if p, err = filepath.Abs(p); err != nil { 120 return "", err 121 } 122 123 return p, nil 124 } 125 } 126 return "", fmt.Errorf("%q: cannot find repository", s) 127 } 128 129 // ImportPath reports the import path of this package. 130 func ImportPath() string { return selfImportPath } 131 132 // Builtin returns the Source for built-in and predefined stuff or an error, if any. 133 func Builtin() (Source, error) { return NewFileSource(filepath.Join(headers, "builtin.h")) } 134 135 // Crt0 returns the Source for program initialization code. 136 func Crt0() (Source, error) { return NewStringSource("crt0.c", CRT0Source), nil } //TODO mv to ccgo 137 138 // MustBuiltin is like Builtin but panics on error. 139 func MustBuiltin() Source { return MustFileSource(filepath.Join(headers, "builtin.h")) } 140 141 // MustCrt0 is like Crt0 but panics on error. 142 func MustCrt0() Source { //TODO mv to ccgo 143 s, err := Crt0() 144 if err != nil { 145 panic(err) 146 } 147 148 return s 149 } 150 151 // MustFileSource is like NewFileSource but panics on error. 152 func MustFileSource(nm string) *FileSource { return MustFileSource2(nm, true) } 153 154 // MustFileSource2 is like NewFileSource but panics on error. 155 func MustFileSource2(nm string, cacheable bool) *FileSource { 156 src, err := NewFileSource2(nm, cacheable) 157 if err != nil { 158 wd, _ := os.Getwd() 159 panic(fmt.Errorf("%v: %v (wd %v)", nm, err, wd)) 160 } 161 162 return src 163 } 164 165 // Paths returns the system header search paths, or an error, if any. If local 166 // is true the package-local, cached header search paths are returned. 167 func Paths(local bool) ([]string, error) { 168 p := filepath.Join(headers, "paths") 169 b, err := ioutil.ReadFile(p) 170 if err != nil { 171 return nil, err 172 } 173 174 a := strings.Split(string(b), "\n") 175 for i, v := range a { 176 switch { 177 case local: 178 a[i] = filepath.Join(headers, strings.TrimSpace(v)) 179 default: 180 a[i] = strings.TrimSpace(v) 181 } 182 } 183 return a, nil 184 } 185 186 // HostConfig executes HostCppConfig with the cpp argument set to "cpp". For 187 // more info please see the documentation of HostCppConfig. 188 func HostConfig(opts ...string) (predefined string, includePaths, sysIncludePaths []string, err error) { 189 return HostCppConfig("cpp", opts...) 190 } 191 192 // HostCppConfig returns the system C preprocessor configuration, or an error, 193 // if any. The configuration is obtained by running the cpp command. For the 194 // predefined macros list the '-dM' options is added. For the include paths 195 // lists, the option '-v' is added and the output is parsed to extract the 196 // "..." include and <...> include paths. To add any other options to cpp, list 197 // them in opts. 198 // 199 // The function relies on a POSIX compatible C preprocessor installed. 200 // Execution of HostConfig is not free, so caching the results is recommended 201 // whenever possible. 202 func HostCppConfig(cpp string, opts ...string) (predefined string, includePaths, sysIncludePaths []string, err error) { 203 args := append(append([]string{"-dM"}, opts...), os.DevNull) 204 // cross-compile e.g. win64 -> win32 205 if env("GOARCH", runtime.GOARCH) == "386" { 206 args = append(args, "-m32") 207 } 208 pre, err := exec.Command(cpp, args...).Output() 209 if err != nil { 210 return "", nil, nil, err 211 } 212 213 args = append(append([]string{"-v"}, opts...), os.DevNull) 214 out, err := exec.Command(cpp, args...).CombinedOutput() 215 if err != nil { 216 return "", nil, nil, err 217 } 218 219 sep := "\n" 220 if env("GOOS", runtime.GOOS) == "windows" { 221 sep = "\r\n" 222 } 223 224 a := strings.Split(string(out), sep) 225 for i := 0; i < len(a); { 226 switch a[i] { 227 case "#include \"...\" search starts here:": 228 loop: 229 for i = i + 1; i < len(a); { 230 switch v := a[i]; { 231 case strings.HasPrefix(v, "#") || v == "End of search list.": 232 break loop 233 default: 234 includePaths = append(includePaths, strings.TrimSpace(v)) 235 i++ 236 } 237 } 238 case "#include <...> search starts here:": 239 for i = i + 1; i < len(a); { 240 switch v := a[i]; { 241 case strings.HasPrefix(v, "#") || v == "End of search list.": 242 return string(pre), includePaths, sysIncludePaths, nil 243 default: 244 sysIncludePaths = append(sysIncludePaths, strings.TrimSpace(v)) 245 i++ 246 } 247 } 248 default: 249 i++ 250 } 251 } 252 return "", nil, nil, fmt.Errorf("failed parsing %s -v output", cpp) 253 } 254 255 type cacheKey struct { 256 name string 257 mtime int64 258 } 259 260 // FlushCache removes all items in the file cache used by instances of FileSource. 261 func FlushCache() { //TODO- 262 cacheMu.Lock() 263 cache = make(map[cacheKey][]uint32, cacheSize) 264 fset = token.NewFileSet() 265 cacheMu.Unlock() 266 } 267 268 // TranslationUnit represents a translation unit, see [0]6.9. 269 type TranslationUnit struct { 270 ExternalDeclarationList *ExternalDeclarationList 271 FileScope *Scope 272 FileSet *token.FileSet 273 IncludePaths []string 274 Macros map[int]*Macro 275 Model Model 276 SysIncludePaths []string 277 } 278 279 // Tweaks amend the behavior of the parser. 280 type Tweaks struct { //TODO- remove all options 281 TrackExpand func(string) 282 TrackIncludes func(string) 283 284 DefinesOnly bool // like in CC -E -dM foo.c 285 EnableAnonymousStructFields bool // struct{int;} 286 EnableBinaryLiterals bool // 0b101010 == 42 287 EnableEmptyStructs bool // struct{} 288 EnableImplicitBuiltins bool // Undefined printf becomes __builtin_printf. 289 EnableImplicitDeclarations bool // eg. using exit(1) w/o #include <stdlib.h> 290 EnableOmitFuncDeclSpec bool // foo() { ... } == int foo() { ... } 291 EnablePointerCompatibility bool // All pointers are assignment compatible. 292 EnableReturnExprInVoidFunc bool // void f() { return 1; } 293 EnableTrigraphs bool 294 EnableUnionCasts bool // (union foo)0 295 IgnoreUnknownPragmas bool // #pragma 296 InjectFinalNL bool // Specs want the source to always end in a newline. 297 PreprocessOnly bool // like in CC -E foo.c 298 cppExpandTest bool // Fake includes 299 } 300 301 // Translate preprocesses, parses and type checks a translation unit using 302 // includePaths and sysIncludePaths for looking for "foo.h" and <foo.h> files. 303 // A special path "@" is interpretted as 'the same directory as where the file 304 // with the #include is'. The input consists of sources which must include any 305 // predefined/builtin stuff. 306 func Translate(tweaks *Tweaks, includePaths, sysIncludePaths []string, sources ...Source) (tu *TranslationUnit, err error) { 307 returned := false 308 309 defer func() { 310 e := recover() 311 if !returned && err == nil { 312 if e != nil { 313 err = fmt.Errorf("%v\n%s", e, debugStack()) 314 return 315 } 316 317 err = fmt.Errorf("PANIC: %v\n%s", e, debugStack()) 318 } 319 }() 320 321 model, err := NewModel() 322 if err != nil { 323 return nil, err 324 } 325 326 ctx, err := newContext(tweaks) 327 if err != nil { 328 return nil, err 329 } 330 331 ctx.model = model 332 ctx.includePaths = append([]string(nil), includePaths...) 333 ctx.sysIncludePaths = append([]string(nil), sysIncludePaths...) 334 if tu, err = ctx.parse(sources); err != nil { 335 return nil, err 336 } 337 338 if tweaks.PreprocessOnly { 339 returned = true 340 return nil, nil 341 } 342 343 if err := tu.ExternalDeclarationList.check(ctx); err != nil { 344 return nil, err 345 } 346 347 if err := ctx.error(); err != nil { 348 return nil, err 349 } 350 351 tu.IncludePaths = append([]string(nil), includePaths...) 352 tu.SysIncludePaths = append([]string(nil), sysIncludePaths...) 353 returned = true 354 return tu, nil 355 } 356 357 // Translation unit context. 358 type context struct { 359 errors scanner.ErrorList 360 exampleAST interface{} 361 exampleRule int 362 includePaths []string 363 model Model 364 scope *Scope 365 sync.Mutex 366 sysIncludePaths []string 367 tweaks *Tweaks 368 } 369 370 func newContext(t *Tweaks) (*context, error) { 371 return &context{ 372 scope: newScope(nil), 373 tweaks: t, 374 }, nil 375 } 376 377 func (c *context) err(n Node, msg string, args ...interface{}) { c.errPos(n.Pos(), msg, args...) } 378 func (c *context) newScope() *Scope { c.scope = newScope(c.scope); return c.scope } 379 func (c *context) newStructScope() { c.scope = newScope(c.scope); c.scope.structScope = true } 380 381 func (c *context) position(n Node) (r token.Position) { 382 if n != nil { 383 return fset.PositionFor(n.Pos(), true) 384 } 385 386 return r 387 } 388 389 func (c *context) errPos(pos token.Pos, msg string, args ...interface{}) { 390 c.Lock() 391 s := fmt.Sprintf(msg, args...) 392 //s = fmt.Sprintf("%s\n====%s\n----", s, debug.Stack()) 393 c.errors.Add(fset.PositionFor(pos, true), s) 394 c.Unlock() 395 } 396 397 func (c *context) error() error { 398 c.Lock() 399 400 defer c.Unlock() 401 402 if len(c.errors) == 0 { 403 return nil 404 } 405 406 c.errors.Sort() 407 err := append(scanner.ErrorList(nil), c.errors...) 408 return err 409 } 410 411 func (c *context) parse(in []Source) (_ *TranslationUnit, err error) { 412 defer func() { c.scope.typedefs = nil }() 413 414 cpp := newCPP(c) 415 r, err := cpp.parse(in...) 416 if err != nil { 417 return nil, err 418 } 419 420 lx, err := newLexer(c, "", 0, nil) 421 if err != nil { 422 return nil, err 423 } 424 425 p := newTokenPipe(1024) 426 if c.tweaks.PreprocessOnly { 427 p.emitWhiteSpace = true 428 } 429 lx.tc = p 430 431 var cppErr error 432 ch := make(chan struct{}) 433 go func() { 434 returned := false 435 436 defer func() { 437 p.close() 438 e := recover() 439 if !returned && cppErr == nil { 440 cppErr = fmt.Errorf("PANIC: %v\n%s", e, debugStack()) 441 c.err(nopos, "%v", cppErr) 442 } 443 ch <- struct{}{} 444 }() 445 446 if cppErr = cpp.eval(r, p); cppErr != nil { 447 c.err(nopos, "%v", cppErr) 448 } 449 returned = true 450 }() 451 452 if c.tweaks.PreprocessOnly { 453 for { 454 t := p.read() 455 if t.Rune == ccEOF { 456 break 457 } 458 459 if f := c.tweaks.TrackExpand; f != nil { 460 if p := c.position(t); filepath.Base(p.Filename) != "builtin.h" { 461 f(TokSrc(t.Token)) 462 } 463 } 464 } 465 if err := c.error(); err != nil { 466 return nil, err 467 } 468 469 return nil, cppErr 470 } 471 472 ok := lx.parse(TRANSLATION_UNIT) 473 if err := c.error(); err != nil || !ok { 474 go func() { // drain 475 for range p.ch { 476 } 477 }() 478 return nil, err 479 } 480 481 if c.scope.Parent != nil { 482 panic("internal error") 483 } 484 485 <-ch 486 if cppErr != nil { 487 return nil, cppErr 488 } 489 490 tu := lx.ast.(*TranslationUnit) 491 tu.Macros = cpp.macros 492 return tu, nil 493 } 494 495 func (c *context) popScope() (old, new *Scope) { 496 old = c.scope 497 c.scope = c.scope.Parent 498 return old, c.scope 499 } 500 501 func (c *context) ptrDiff() Type { 502 d, ok := c.scope.LookupIdent(idPtrdiffT).(*Declarator) 503 if !ok { 504 psz := c.model[Ptr].Size 505 for _, v := range []TypeKind{Int, Long, LongLong} { 506 if c.model[v].Size >= psz { 507 return v 508 } 509 } 510 panic("internal error") 511 } 512 513 if !d.DeclarationSpecifier.IsTypedef() { 514 panic(d.Type) 515 } 516 517 return d.Type 518 } 519 520 func (c *context) wideChar() Type { 521 d, ok := c.scope.LookupIdent(idWcharT).(*Declarator) 522 if !ok { 523 var sz int 524 switch goos := env("GOOS", ""); goos { 525 case "windows": 526 sz = 2 527 case "linux": 528 sz = 4 529 default: 530 panic(goos) 531 } 532 for _, v := range []TypeKind{SChar, Short, Int, Long, LongLong} { 533 if c.model[v].Size >= sz { 534 return v 535 } 536 } 537 panic("internal error") 538 } 539 540 if !d.DeclarationSpecifier.IsTypedef() { 541 panic(d.Type) 542 } 543 544 return d.Type 545 } 546 547 func (c *context) charConst(t xc.Token) Operand { 548 switch t.Rune { 549 case CHARCONST: 550 s := string(t.S()) 551 s = s[1 : len(s)-1] // Remove outer 's. 552 if len(s) == 1 { 553 return Operand{Type: Int, Value: &ir.Int64Value{Value: int64(s[0])}} 554 } 555 556 runes := []rune(s) 557 var r rune 558 switch runes[0] { 559 case '\\': 560 r, _ = decodeEscapeSequence(runes) 561 if r < 0 { 562 r = -r 563 } 564 default: 565 r = runes[0] 566 } 567 return Operand{Type: Int, Value: &ir.Int64Value{Value: int64(r)}} 568 case LONGCHARCONST: 569 s := t.S() 570 var buf bytes.Buffer 571 s = s[2 : len(s)-1] 572 runes := []rune(string(s)) 573 for i := 0; i < len(runes); { 574 switch r := runes[i]; { 575 case r == '\\': 576 r, n := decodeEscapeSequence(runes[i:]) 577 switch { 578 case r < 0: 579 buf.WriteByte(byte(-r)) 580 default: 581 buf.WriteRune(r) 582 } 583 i += n 584 default: 585 buf.WriteByte(byte(r)) 586 i++ 587 } 588 } 589 s = buf.Bytes() 590 runes = []rune(string(s)) 591 if len(runes) != 1 { 592 panic("TODO") 593 } 594 595 return Operand{Type: Long, Value: &ir.Int64Value{Value: int64(runes[0])}} 596 default: 597 panic("internal error") 598 } 599 } 600 601 func (c *context) strConst(t xc.Token) Operand { 602 s := t.S() 603 switch t.Rune { 604 case LONGSTRINGLITERAL: 605 s = s[1:] // Remove leading 'L'. 606 fallthrough 607 case STRINGLITERAL: 608 var buf bytes.Buffer 609 s = s[1 : len(s)-1] // Remove outer "s. 610 runes := []rune(string(s)) 611 for i := 0; i < len(runes); { 612 switch r := runes[i]; { 613 case r == '\\': 614 r, n := decodeEscapeSequence(runes[i:]) 615 switch { 616 case r < 0: 617 buf.WriteByte(byte(-r)) 618 default: 619 buf.WriteRune(r) 620 } 621 i += n 622 default: 623 buf.WriteByte(byte(r)) 624 i++ 625 } 626 } 627 switch t.Rune { 628 case LONGSTRINGLITERAL: 629 runes := []rune(buf.String()) 630 typ := c.wideChar() 631 return Operand{ 632 Type: &ArrayType{Item: typ, Size: Operand{Type: Int, Value: &ir.Int64Value{Value: c.model.Sizeof(typ) * int64(len(runes)+1)}}}, 633 Value: &ir.WideStringValue{Value: runes}, 634 } 635 case STRINGLITERAL: 636 return Operand{ 637 Type: &ArrayType{Item: Char, Size: Operand{Type: Int, Value: &ir.Int64Value{Value: int64(len(buf.Bytes()) + 1)}}}, 638 Value: &ir.StringValue{StringID: ir.StringID(dict.ID(buf.Bytes()))}, 639 } 640 } 641 } 642 panic("internal error") 643 } 644 645 func (c *context) sizeof(t Type) Operand { 646 sz := c.model.Sizeof(t) 647 d, ok := c.scope.LookupIdent(idSizeT).(*Declarator) 648 if !ok { 649 psz := c.model[Ptr].Size 650 for _, v := range []TypeKind{UInt, ULong, ULongLong} { 651 if c.model[v].Size >= psz { 652 return newIntConst(c, nopos, uint64(sz), v) 653 } 654 } 655 panic("internal error") 656 } 657 658 if !d.DeclarationSpecifier.IsTypedef() { 659 panic(d.Type) 660 } 661 662 r := Operand{Type: d.Type, Value: &ir.Int64Value{Value: sz}} 663 if x, ok := underlyingType(t, false).(*ArrayType); ok && x.Size.Value == nil { 664 r.Value = nil 665 } 666 return r 667 } 668 669 func (c *context) toC(ch rune, val int) rune { 670 if ch != IDENTIFIER { 671 return ch 672 } 673 674 if x, ok := keywords[val]; ok { 675 return x 676 } 677 678 return ch 679 } 680 681 // Source represents parser's input. 682 type Source interface { 683 Cache([]uint32) // Optionally cache the encoded source. Can be a no-operation. 684 Cached() []uint32 // Return nil or the optionally encoded source cached by earlier call to Cache. 685 Name() string // Result will be used in reporting source code positions. 686 ReadCloser() (io.ReadCloser, error) // Where to read the source from 687 Size() (int64, error) // Report the size of the source in bytes. 688 String() string 689 } 690 691 // FileSource is a Source reading from a named file. 692 type FileSource struct { 693 *bufio.Reader 694 f *os.File 695 path string 696 key cacheKey 697 size int64 698 699 cacheable bool 700 } 701 702 // NewFileSource returns a newly created *FileSource reading from name. 703 func NewFileSource(name string) (*FileSource, error) { return NewFileSource2(name, true) } 704 705 // NewFileSource2 returns a newly created *FileSource reading from name. 706 func NewFileSource2(name string, cacheable bool) (*FileSource, error) { //TODO-? 707 f, err := os.Open(name) 708 if err != nil { 709 return nil, err 710 } 711 712 r := &FileSource{f: f, path: name, cacheable: cacheable} 713 fi, err := f.Stat() 714 if err != nil { 715 return nil, err 716 } 717 718 r.size = fi.Size() 719 r.key = cacheKey{name, fi.ModTime().UnixNano()} 720 return r, nil 721 } 722 723 func (s *FileSource) String() string { return s.Name() } 724 725 // Cache implements Source. 726 func (s *FileSource) Cache(a []uint32) { 727 if !s.cacheable { 728 return 729 } 730 731 cacheMu.Lock() 732 if len(cache) > cacheSize { 733 for k := range cache { 734 delete(cache, k) 735 break 736 } 737 } 738 cache[s.key] = a 739 cacheMu.Unlock() 740 } 741 742 // Cached implements Source. 743 func (s *FileSource) Cached() (r []uint32) { 744 if !s.cacheable { 745 return nil 746 } 747 748 cacheMu.Lock() 749 var ok bool 750 r, ok = cache[s.key] 751 cacheMu.Unlock() 752 if ok { 753 s.Close() 754 } 755 return r 756 } 757 758 // Close implements io.ReadCloser. 759 func (s *FileSource) Close() error { 760 if f := s.f; f != nil { 761 s.f = nil 762 return f.Close() 763 } 764 765 return nil 766 } 767 768 // Name implements Source. 769 func (s *FileSource) Name() string { return s.path } 770 771 // ReadCloser implements Source. 772 func (s *FileSource) ReadCloser() (io.ReadCloser, error) { 773 s.Reader = bufio.NewReader(s.f) 774 return s, nil 775 } 776 777 // Size implements Source. 778 func (s *FileSource) Size() (int64, error) { return s.size, nil } 779 780 // StringSource is a Source reading from a string. 781 type StringSource struct { 782 *strings.Reader 783 name string 784 src string 785 } 786 787 // NewStringSource returns a newly created *StringSource reading from src and 788 // having the presumed name. 789 func NewStringSource(name, src string) *StringSource { return &StringSource{name: name, src: src} } 790 791 func (s *StringSource) String() string { return s.Name() } 792 793 // Cache implements Source. 794 func (s *StringSource) Cache(a []uint32) { 795 cacheMu.Lock() 796 if len(cache) > cacheSize { 797 for k := range cache { 798 delete(cache, k) 799 break 800 } 801 } 802 cache[cacheKey{mtime: -1, name: s.src}] = a 803 cacheMu.Unlock() 804 } 805 806 // Cached implements Source. 807 func (s *StringSource) Cached() (r []uint32) { 808 cacheMu.Lock() 809 r = cache[cacheKey{mtime: -1, name: s.src}] 810 cacheMu.Unlock() 811 return r 812 } 813 814 // Close implements io.ReadCloser. 815 func (s *StringSource) Close() error { return nil } 816 817 // Name implements Source. 818 func (s *StringSource) Name() string { return s.name } 819 820 // Size implements Source. 821 func (s *StringSource) Size() (int64, error) { return int64(len(s.src)), nil } 822 823 // ReadCloser implements Source. 824 func (s *StringSource) ReadCloser() (io.ReadCloser, error) { 825 s.Reader = strings.NewReader(s.src) 826 return s, nil 827 } 828 829 // Scope binds names to declarations. 830 type Scope struct { 831 EnumTags map[int]*EnumSpecifier // name ID: *EnumSpecifier 832 Idents map[int]Node // name ID: Node in {*Declarator, EnumerationConstant} 833 Labels map[int]*LabeledStmt // name ID: label 834 Parent *Scope 835 StructTags map[int]*StructOrUnionSpecifier // name ID: *StructOrUnionSpecifier 836 837 // parser support 838 typedefs map[int]bool // name: isTypedef 839 fixDecl int 840 841 forStmtEndScope *Scope 842 structScope bool 843 typedef bool 844 } 845 846 func newScope(parent *Scope) *Scope { return &Scope{Parent: parent} } 847 848 func (s *Scope) insertLabel(ctx *context, st *LabeledStmt) { 849 for s.Parent != nil && s.Parent.Parent != nil { 850 s = s.Parent 851 } 852 if s.Labels == nil { 853 s.Labels = map[int]*LabeledStmt{} 854 } 855 if ex := s.Labels[st.Token.Val]; ex != nil { 856 panic("TODO") 857 } 858 859 s.Labels[st.Token.Val] = st 860 } 861 862 func (s *Scope) insertEnumTag(ctx *context, nm int, es *EnumSpecifier) { 863 for s.structScope { 864 s = s.Parent 865 } 866 if s.EnumTags == nil { 867 s.EnumTags = map[int]*EnumSpecifier{} 868 } 869 if ex := s.EnumTags[nm]; ex != nil { 870 if ex == es || ex.isCompatible(es) { 871 return 872 } 873 874 panic(fmt.Errorf("%s\n----\n%s", ex, es)) 875 } 876 877 s.EnumTags[nm] = es 878 } 879 880 func (s *Scope) insertDeclarator(ctx *context, d *Declarator) { 881 if s.Idents == nil { 882 s.Idents = map[int]Node{} 883 } 884 nm := d.Name() 885 if ex := s.Idents[nm]; ex != nil { 886 panic("internal error 8") 887 } 888 889 s.Idents[nm] = d 890 } 891 892 func (s *Scope) insertEnumerationConstant(ctx *context, c *EnumerationConstant) { 893 for s.structScope { 894 s = s.Parent 895 } 896 if s.Idents == nil { 897 s.Idents = map[int]Node{} 898 } 899 nm := c.Token.Val 900 if ex := s.Idents[nm]; ex != nil { 901 if ex == c { 902 return 903 } 904 905 if x, ok := ex.(*EnumerationConstant); ok && x.equal(c) { 906 return 907 } 908 909 panic(fmt.Errorf("%v: %v, %v", ctx.position(c), ex, c)) 910 } 911 912 s.Idents[nm] = c 913 } 914 915 func (s *Scope) insertStructTag(ctx *context, ss *StructOrUnionSpecifier) { 916 for s.structScope { 917 s = s.Parent 918 } 919 if s.StructTags == nil { 920 s.StructTags = map[int]*StructOrUnionSpecifier{} 921 } 922 nm := ss.IdentifierOpt.Token.Val 923 if ex := s.StructTags[nm]; ex != nil && !ex.typ.IsCompatible(ss.typ) { 924 panic(fmt.Errorf("%v: %v, %v", ctx.position(ss), ex.typ, ss.typ)) 925 } 926 927 s.StructTags[nm] = ss 928 } 929 930 func (s *Scope) insertTypedef(ctx *context, nm int, isTypedef bool) { 931 //dbg("%p(parent %p).insertTypedef(%q, %v)", s, s.Parent, dict.S(nm), isTypedef) 932 if s.typedefs == nil { 933 s.typedefs = map[int]bool{} 934 } 935 // Redefinitions, if any, are ignored during parsing, but checked later in insertDeclarator. 936 s.typedefs[nm] = isTypedef 937 } 938 939 func (s *Scope) isTypedef(nm int) bool { 940 //dbg("==== %p(parent %p).isTypedef(%q)", s, s.Parent, dict.S(nm)) 941 for s != nil { 942 //dbg("%p", s) 943 if v, ok := s.typedefs[nm]; ok { 944 if s.structScope && !v { 945 s = s.Parent 946 continue 947 } 948 949 //dbg("%p -> %v %v", s, v, ok) 950 return v 951 } 952 953 s = s.Parent 954 } 955 return false 956 } 957 958 // LookupIdent will return the Node associated with name ID nm. 959 func (s *Scope) LookupIdent(nm int) Node { 960 for s != nil { 961 if n := s.Idents[nm]; n != nil { 962 return n 963 } 964 965 s = s.Parent 966 } 967 return nil 968 } 969 970 // LookupLabel will return the Node associated with label ID nm. 971 func (s *Scope) LookupLabel(nm int) Node { 972 for s != nil { 973 if n := s.Labels[nm]; n != nil { 974 if s.Parent == nil && s.Parent.Parent != nil { 975 panic("internal error") 976 } 977 978 return n 979 } 980 981 s = s.Parent 982 } 983 return nil 984 } 985 986 func (s *Scope) lookupEnumTag(nm int) *EnumSpecifier { 987 for s != nil { 988 if n := s.EnumTags[nm]; n != nil { 989 return n 990 } 991 992 s = s.Parent 993 } 994 return nil 995 } 996 997 func (s *Scope) lookupStructTag(nm int) *StructOrUnionSpecifier { 998 for s != nil { 999 if n := s.StructTags[nm]; n != nil { 1000 return n 1001 } 1002 1003 s = s.Parent 1004 } 1005 return nil 1006 } 1007 1008 func (s *Scope) String() string { 1009 var a []string 1010 for _, v := range s.Idents { 1011 switch x := v.(type) { 1012 case *Declarator: 1013 a = append(a, string(dict.S(x.Name()))) 1014 default: 1015 panic(fmt.Errorf("%T", x)) 1016 } 1017 } 1018 sort.Strings(a) 1019 return "{" + strings.Join(a, ", ") + "}" 1020 }