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  }