source: code/trunk/vendor/modernc.org/cc/v3/cc.go@ 824

Last change on this file since 824 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 28.9 KB
Line 
1// Copyright 2019 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//TODO https://todo.sr.ht/~mcf/cc-issues/34
6//TODO http://mcpp.sourceforge.net/ "Provides a validation suite to test C/C++ preprocessor's conformance and quality comprehensively."
7
8//go:generate rm -f lexer.go
9//go:generate golex -o lexer.go lexer.l
10
11//go:generate rm -f ast.go
12//go:generate yy -o /dev/null -position -astImport "\"fmt\"\n\n\"modernc.org/token\"" -prettyString PrettyString -kind Case -noListKind -noPrivateHelpers -forceOptPos parser.yy
13
14//go:generate stringer -output stringer.go -linecomment -type=Kind,Linkage
15
16//go:generate sh -c "go test -run ^Example |fe"
17
18// Package cc is a C99 compiler front end (Work in progress).
19//
20// Installation
21//
22// To install/update cc/v3 invoke:
23//
24// $ go get [-u] modernc.org/cc/v3
25//
26// Online documentation
27//
28// See https://godoc.org/modernc.org/cc/v3.
29//
30// Status
31//
32// Most of the functionality is now working.
33//
34// Supported platforms
35//
36// The code is known to work on Darwin, Linux and Windows, but the supported
37// features may vary.
38//
39// Links
40//
41// Referenced from elsewhere:
42//
43// [0]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
44// [1]: https://www.spinellis.gr/blog/20060626/cpp.algo.pdf
45// [2]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
46// [3]: http://gallium.inria.fr/~fpottier/publis/jourdan-fpottier-2016.pdf
47// [4]: https://gcc.gnu.org/onlinedocs/gcc-8.3.0/gcc/Attribute-Syntax.html#Attribute-Syntax
48package cc // import "modernc.org/cc/v3"
49
50import (
51 "fmt"
52 goscanner "go/scanner"
53 gotoken "go/token"
54 "hash/maphash"
55 "io"
56 "math"
57 "os"
58 "os/exec"
59 "reflect"
60 "regexp"
61 "runtime"
62 "sort"
63 "strconv"
64 "strings"
65 "sync"
66 "sync/atomic"
67
68 "modernc.org/strutil"
69 "modernc.org/token"
70)
71
72const (
73 scopeParent StringID = -iota - 1
74 scopeSkip
75)
76
77var (
78 _ Pragma = (*pragma)(nil)
79
80 cache = newPPCache()
81 dict = newDictionary()
82 dictStrings [math.MaxUint8 + 1]string
83 noPos token.Position
84
85 debugIncludePaths bool
86 debugWorkingDir bool
87 isTesting bool
88 isTestingMingw bool
89
90 idPtrdiffT = dict.sid("ptrdiff_t")
91 idSizeT = dict.sid("size_t")
92 idWCharT = dict.sid("wchar_t")
93
94 token4Pool = sync.Pool{New: func() interface{} { r := make([]token4, 0); return &r }} //DONE benchmrk tuned capacity
95 tokenPool = sync.Pool{New: func() interface{} { r := make([]Token, 0); return &r }} //DONE benchmrk tuned capacity
96
97 printHooks = strutil.PrettyPrintHooks{
98 reflect.TypeOf(Token{}): func(f strutil.Formatter, v interface{}, prefix, suffix string) {
99 t := v.(Token)
100 if (t == Token{}) {
101 return
102 }
103
104 f.Format(prefix)
105 r := t.Rune
106 if p := t.Position(); p.IsValid() {
107 f.Format("%v: ", p)
108 }
109 s := tokName(r)
110 if x := s[0]; x >= '0' && x <= '9' {
111 s = strconv.QuoteRune(r)
112 }
113 f.Format("%s", s)
114 if s := t.Value.String(); len(s) != 0 {
115 f.Format(" %q", s)
116 }
117 f.Format(suffix)
118 },
119 reflect.TypeOf((*operand)(nil)): func(f strutil.Formatter, v interface{}, prefix, suffix string) {
120 op := v.(*operand)
121 f.Format(prefix)
122 f.Format("[%v %T(%[2]v)]", op.Type(), op.Value())
123 f.Format(suffix)
124 },
125 }
126)
127
128func todo(s string, args ...interface{}) string { //TODO-
129 switch {
130 case s == "":
131 s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
132 default:
133 s = fmt.Sprintf(s, args...)
134 }
135 pc, fn, fl, _ := runtime.Caller(1)
136 f := runtime.FuncForPC(pc)
137 var fns string
138 if f != nil {
139 fns = f.Name()
140 if x := strings.LastIndex(fns, "."); x > 0 {
141 fns = fns[x+1:]
142 }
143 }
144 r := fmt.Sprintf("%s:%d:%s: TODOTODO %s", fn, fl, fns, s) //TODOOK
145 fmt.Fprintf(os.Stdout, "%s\n", r)
146 os.Stdout.Sync()
147 return r
148}
149
150func trc(s string, args ...interface{}) string { //TODO-
151 switch {
152 case s == "":
153 s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
154 default:
155 s = fmt.Sprintf(s, args...)
156 }
157 pc, fn, fl, _ := runtime.Caller(1)
158 f := runtime.FuncForPC(pc)
159 var fns string
160 if f != nil {
161 fns = f.Name()
162 if x := strings.LastIndex(fns, "."); x > 0 {
163 fns = fns[x+1:]
164 }
165 }
166 r := fmt.Sprintf("%s:%d:%s: TRC %s", fn, fl, fns, s)
167 fmt.Fprintf(os.Stdout, "%s\n", r)
168 os.Stdout.Sync()
169 return r
170}
171
172func origin(skip int) string {
173 pc, fn, fl, _ := runtime.Caller(skip)
174 f := runtime.FuncForPC(pc)
175 var fns string
176 if f != nil {
177 fns = f.Name()
178 if x := strings.LastIndex(fns, "."); x > 0 {
179 fns = fns[x+1:]
180 }
181 }
182 return fmt.Sprintf("%s:%d:%s", fn, fl, fns)
183}
184
185// String returns a StringID for a given value.
186func String(s string) StringID {
187 return dict.sid(s)
188}
189
190// Linkage represents identifier linkage.
191//
192// [0]6.2.2: An identifier declared in different scopes or in the same scope
193// more than once can be made to refer to the same object or function by a
194// process called linkage. There are three kinds of linkage: External,
195// Internal, and None.
196type Linkage int
197
198// StorageClass determines storage duration.
199//
200// [0]6.2.4: An object has a storage duration that determines its lifetime.
201// There are three storage durations: Static, Automatic, and Allocated.
202type StorageClass int
203
204// Pragma defines behavior of the object passed to Config.PragmaHandler.
205type Pragma interface {
206 Error(msg string, args ...interface{}) // Report error.
207 MaxAligment() int // Returns the current maximum alignment. May return zero.
208 MaxInitialAligment() int // Support #pragma pack(). Returns the maximum alignment in effect at start. May return zero.
209 PopMacro(string)
210 PushMacro(string)
211 SetAlignment(n int) // Support #pragma pack(n)
212}
213
214type pragma struct {
215 tok cppToken
216 c *cpp
217}
218
219func (p *pragma) Error(msg string, args ...interface{}) { p.c.err(p.tok, msg, args...) }
220
221func (p *pragma) MaxAligment() int { return p.c.ctx.maxAlign }
222
223func (p *pragma) MaxInitialAligment() int { return p.c.ctx.maxAlign0 }
224
225func (p *pragma) SetAlignment(n int) {
226 if n <= 0 {
227 p.Error("%T.SetAlignment(%d): invalid argument", p, n)
228 return
229 }
230
231 p.c.ctx.maxAlign = n
232}
233
234func (p *pragma) PushMacro(nm string) {
235 id := dict.sid(nm)
236 if p.c.macroStack == nil {
237 p.c.macroStack = map[StringID][]*Macro{}
238 }
239 if m := p.c.macros[id]; m != nil {
240 p.c.macroStack[id] = append(p.c.macroStack[id], p.c.macros[id])
241 }
242}
243
244func (p *pragma) PopMacro(nm string) {
245 id := dict.sid(nm)
246 a := p.c.macroStack[id]
247 if n := len(a); n != 0 {
248 p.c.macros[id] = a[n-1]
249 p.c.macroStack[id] = a[:n-1]
250 }
251}
252
253// PrettyString returns a formatted representation of things produced by this package.
254func PrettyString(v interface{}) string {
255 return strutil.PrettyString(v, "", "", printHooks)
256}
257
258// StringID is a process-unique string numeric identifier. Its zero value
259// represents an empty string.
260type StringID int32
261
262// String implements fmt.Stringer.
263func (n StringID) String() (r string) {
264 if n < 256 {
265 return dictStrings[byte(n)]
266 }
267
268 dict.mu.RLock()
269 r = dict.strings[n]
270 dict.mu.RUnlock()
271 return r
272}
273
274// Node is implemented by Token and all AST nodes.
275type Node interface {
276 Position() token.Position
277}
278
279type noder struct{}
280
281func (noder) Position() token.Position { panic(internalError()) }
282
283// Scope maps identifiers to definitions.
284type Scope map[StringID][]Node
285
286func (s *Scope) new() (r Scope) {
287 if *s == nil {
288 *s = Scope{}
289 }
290 r = Scope{scopeParent: []Node{struct {
291 noder
292 Scope
293 }{Scope: *s}}}
294 return r
295}
296
297func (s *Scope) declare(nm StringID, n Node) {
298 sc := *s
299 if sc == nil {
300 *s = map[StringID][]Node{nm: {n}}
301 // t := ""
302 // if x, ok := n.(*Declarator); ok && x.IsTypedefName {
303 // t = ", typedefname"
304 // }
305 // dbg("declared %s%s at %v in scope %p", nm, t, n.Position(), *s)
306 return
307 }
308
309 switch x := n.(type) {
310 case *Declarator, *StructDeclarator, *LabeledStatement, *BlockItem:
311 // nop
312 case *StructOrUnionSpecifier, *EnumSpecifier, *Enumerator:
313 for {
314 if _, ok := sc[scopeSkip]; !ok {
315 break
316 }
317
318 sc = sc.Parent()
319 }
320 default:
321 panic(todo("%T", x))
322 }
323
324 sc[nm] = append(sc[nm], n)
325 // t := ""
326 // if x, ok := n.(*Declarator); ok && x.IsTypedefName {
327 // t = ", typedefname"
328 // }
329 // dbg("declared %s%s at %v in scope %p", nm, t, n.Position(), sc)
330}
331
332// Parent returns s's outer scope, if any.
333func (s Scope) Parent() Scope {
334 if s == nil {
335 return nil
336 }
337
338 if x, ok := s[scopeParent]; ok {
339 return x[0].(struct {
340 noder
341 Scope
342 }).Scope
343 }
344
345 return nil
346}
347
348func (s *Scope) typedef(nm StringID, tok Token) *Declarator {
349 seq := tok.seq
350 for s := *s; s != nil; s = s.Parent() {
351 for _, v := range s[nm] {
352 switch x := v.(type) {
353 case *Declarator:
354 if !x.isVisible(seq) {
355 continue
356 }
357
358 if x.IsTypedefName {
359 return x
360 }
361
362 return nil
363 case *Enumerator:
364 return nil
365 case *EnumSpecifier, *StructOrUnionSpecifier, *StructDeclarator:
366 // nop
367 default:
368 panic(internalError())
369 }
370 }
371 }
372 return nil
373}
374
375func (s *Scope) declarator(nm StringID, tok Token) *Declarator {
376 seq := tok.seq
377 for s := *s; s != nil; s = s.Parent() {
378 defs := s[nm]
379 for _, v := range defs {
380 switch x := v.(type) {
381 case *Declarator:
382 if !x.isVisible(seq) {
383 continue
384 }
385
386 for _, v := range defs {
387 if x, ok := v.(*Declarator); ok {
388 t := x.Type()
389 if t != nil && t.Kind() == Function {
390 if x.fnDef {
391 return x
392 }
393
394 continue
395 }
396
397 if t != nil && !x.Type().IsIncomplete() {
398 return x
399 }
400 }
401
402 }
403 return x
404 case *Enumerator:
405 return nil
406 case *EnumSpecifier, *StructOrUnionSpecifier, *StructDeclarator:
407 // nop
408 default:
409 panic(internalError())
410 }
411 }
412 }
413 return nil
414}
415
416func (s *Scope) enumerator(nm StringID, tok Token) *Enumerator {
417 seq := tok.seq
418 for s := *s; s != nil; s = s.Parent() {
419 for _, v := range s[nm] {
420 switch x := v.(type) {
421 case *Declarator:
422 if !x.isVisible(seq) {
423 continue
424 }
425
426 return nil
427 case *Enumerator:
428 return x
429 case *EnumSpecifier, *StructOrUnionSpecifier, *StructDeclarator:
430 // nop
431 default:
432 panic(internalError())
433 }
434 }
435 }
436 return nil
437}
438
439// Config3 amends behavior of translation phases 1 to 3.
440type Config3 struct {
441 // If IgnoreInclude is not nil, its MatchString method will be called by the
442 // preprocessor with the argument any include directive expands to. If the call
443 // evaluates to is true the include directive will be ignored completely.
444 IgnoreInclude *regexp.Regexp
445
446 // Name of a macro to use instead of FD_ZERO.
447 //
448 // Note: Temporary solution will be removed/replaced
449 ReplaceMacroFdZero string
450 // Name of a macro to use instead of TCL_DEFAULT_DOUBLE_ROUNDING.
451 //
452 // Note: Temporary solution will be removed/replaced
453 ReplaceMacroTclDefaultDoubleRounding string // Name of a macro to use instead of TCL_DEFAULT_DOUBLE_ROUNDING. Note: Temporrary solution will be removed/replaced
454 // Name of a macro to use instead of TCL_IEEE_DOUBLE_ROUNDING.
455 //
456 // Note: Temporary solution will be removed/replaced
457 ReplaceMacroTclIeeeDoubleRounding string
458
459 WorkingDir string // Overrides os.Getwd if non empty.
460 Filesystem Filesystem // Overrides filesystem access if not empty.
461
462 MaxSourceLine int // Zero: Scanner will use default buffer. Non zero: Scanner will use max(default buffer size, MaxSourceLine).
463
464 // DisableBuiltinResolution disables resolution of undefined identifiers such
465 // that eg. abort, becomes the same as __builtin_abort, prototype of which is
466 // expected to be provided by one of the sources passed to Parse, Preprocess or
467 // Translate.
468 DisableBuiltinResolution bool
469
470 DisableTrigraphs bool // GCC ignores them unless -trigraphs is used: https://gcc.gnu.org/onlinedocs/cpp/Initial-processing.html
471 GCCStructs bool // Assume __attribute__(gcc_struct) applied to structs by default.
472 //TODO MSStructs bool // Assume __attribute__(ms_struct) applied to structs by default.
473 NoFieldAndBitfieldOverlap bool // Only bitfields can be grouped together.
474 PreserveOnlyLastNonBlankSeparator bool // If PreserveWhiteSpace is true, keep only the last white space, do not combine
475 PreserveWhiteSpace bool // Including also comments.
476 RejectElseExtraTokens bool // Pedantic: do not silently accept "#else foo".
477 RejectEndifExtraTokens bool // Pedantic: do not silently accept "#endif foo".
478 RejectFinalBackslash bool // Pedantic: do not silently accept "foo\\\n".
479 RejectFunctionMacroEmptyReplacementList bool // Pedantic: do not silently accept "#define foo(bar)\n".
480 RejectIfdefExtraTokens bool // Pedantic: do not silently accept "#ifdef foo bar".
481 RejectIfndefExtraTokens bool // Pedantic: do not silently accept "#ifndef foo bar".
482 RejectIncludeNext bool // Pedantic: do not silently accept "#include_next".
483 RejectInvalidVariadicMacros bool // Pedantic: do not silently accept "#define foo(bar...)". Standard allows only #define foo(bar, ...)
484 RejectLineExtraTokens bool // Pedantic: do not silently accept "#line 1234 \"foo.c\" bar".
485 RejectMissingFinalNewline bool // Pedantic: do not silently accept "foo\nbar".
486 RejectUndefExtraTokens bool // Pedantic: do not silently accept "#undef foo bar".
487 UnsignedEnums bool // GCC compatibility: enums with no negative values will have unsigned type.
488}
489
490type SharedFunctionDefinitions struct {
491 M map[*FunctionDefinition]struct{}
492 m map[sharedFunctionDefinitionKey]*FunctionDefinition //TODO
493 hash maphash.Hash
494}
495
496type sharedFunctionDefinitionKey struct {
497 pos StringID
498 nm StringID
499 hash uint64
500}
501
502// Config amends behavior of translation phase 4 and above. Instances of Config
503// are not mutated by this package and it's safe to share/reuse them.
504//
505// The *Config passed to Parse or Translate should not be mutated afterwards.
506type Config struct {
507 Config3
508 ABI ABI
509
510 PragmaHandler func(Pragma, []Token) // Called on pragmas, other than #pragma STDC ..., if non nil
511
512 // SharedFunctionDefinitions collects function definitions having the same
513 // position and definition. This can happen, for example, when a function is
514 // defined in a header file included multiple times. Either within a single
515 // translation unit or across translation units. In the later case just supply
516 // the same SharedFunctionDefinitions in Config when translating/parsing each
517 // translation unit.
518 SharedFunctionDefinitions *SharedFunctionDefinitions
519
520 // IncludeFileHandler, when non nil, is called by the preprocessor for every
521 // successfully included file.
522 IncludeFileHandler func(pos gotoken.Position, includePath string)
523
524 MaxErrors int // 0: default (10), < 0: unlimited, n: n.
525
526 CheckExternInlineFnBodies bool // Translate will consider extern inline function bodies.
527 DebugIncludePaths bool // Output to stderr.
528 DebugWorkingDir bool // Output to stderr.
529 DoNotTypecheckAsm bool
530 EnableAssignmentCompatibilityChecking bool // No such checks performed up to v3.31.0. Currently only partially implemented.
531 FixBitfieldPadding bool // Fix a bug in calculating field positions after a bitfield.
532 InjectTracingCode bool // Output to stderr.
533 LongDoubleIsDouble bool
534 PreprocessOnly bool
535 RejectAnonymousFields bool // Pedantic: do not silently accept "struct{int;}".
536 RejectCaseRange bool // Pedantic: do not silently accept "case 'a'...'z':".
537 RejectEmptyCompositeLiterals bool // Pedantic: do not silently accept "foo = (T){}".
538 RejectEmptyDeclarations bool // Pedantic: do not silently accept "int foo(){};".
539 RejectEmptyFields bool // Pedantic: do not silently accept "struct {int a;;} foo;".
540 RejectEmptyInitializerList bool // Pedantic: do not silently accept "foo f = {};".
541 RejectEmptyStructDeclaration bool // Pedantic: do not silently accept "struct{; int i}".
542 RejectEmptyStructs bool // Pedantic: do not silently accept "struct foo {};".
543 RejectIncompatibleMacroRedef bool // Pedantic: do not silently accept "#define MIN(A,B) ...\n#define MIN(a,b) ...\n" etc.
544 RejectLabelValues bool // Pedantic: do not silently accept "foo: bar(); void *ptr = &&foo;" or "goto *ptr".
545 RejectLateBinding bool // Pedantic: do not silently accept void f() { g(); } void g() {}
546 RejectMissingConditionalExpr bool // Pedantic: do not silently accept "foo = bar ? : baz;".
547 RejectMissingDeclarationSpecifiers bool // Pedantic: do not silently accept "main() {}".
548 RejectMissingFinalStructFieldSemicolon bool // Pedantic: do not silently accept "struct{int i; int j}".
549 RejectNestedFunctionDefinitions bool // Pedantic: do not silently accept nested function definitons.
550 RejectParamSemicolon bool // Pedantic: do not silently accept "int f(int a; int b)".
551 RejectStatementExpressions bool // Pedantic: do not silently accept "i = ({foo();})".
552 RejectTypeof bool // Pedantic: do not silently accept "typeof foo" or "typeof(bar*)".
553 RejectUninitializedDeclarators bool // Reject int f() { int j; return j; }
554 TrackAssignments bool // Collect a list of LHS declarators a declarator is used in RHS or as an function argument.
555 doNotSanityCheckComplexTypes bool // Testing only
556 fakeIncludes bool // Testing only.
557 ignoreErrors bool // Testing only.
558 ignoreIncludes bool // Testing only.
559 ignoreUndefinedIdentifiers bool // Testing only.
560}
561
562type context struct {
563 ast *AST
564 breakCtx Node
565 breaks int
566 casePromote Type
567 cases []*LabeledStatement // switch
568 cfg *Config
569 checkFn *FunctionDefinition
570 closure map[StringID]struct{}
571 continues int
572 enums map[StringID]Operand //TODO putting this in alphabetical order within the struct causes crashes in VirtualBox/386 ???
573 goscanner.ErrorList
574 includePaths []string
575 intBits int
576 intMaxWidth int64 // Set if the preprocessor saw __INTMAX_WIDTH__.
577 keywords map[StringID]rune
578 maxAlign int // If non zero: maximum alignment of members of structures (other than zero-width bitfields).
579 maxAlign0 int
580 maxErrors int
581 mode mode
582 modes []mode
583 mu sync.Mutex
584 ptrdiffT Type
585 readDelta int
586 sizeT Type
587 structTypes map[StringID]Type
588 structs map[StructInfo]struct{}
589 switches int
590 sysIncludePaths []string
591 tuSize0 int64 // Sum of sizes of processed inputs
592 tuSources0 int32 // Number of processed inputs
593 wcharT Type
594
595 capture bool
596 evalIdentError bool
597}
598
599func newContext(cfg *Config) *context {
600 maxErrors := cfg.MaxErrors
601 if maxErrors == 0 {
602 maxErrors = 10
603 }
604 return &context{
605 cfg: cfg,
606 enums: map[StringID]Operand{},
607 keywords: keywords,
608 maxErrors: maxErrors,
609 structTypes: map[StringID]Type{},
610 structs: map[StructInfo]struct{}{},
611 }
612}
613
614func (c *context) tuSizeAdd(n int64) { atomic.AddInt64(&c.tuSize0, n) }
615func (c *context) tuSize() int64 { return atomic.LoadInt64(&c.tuSize0) }
616func (c *context) tuSourcesAdd(n int32) { atomic.AddInt32(&c.tuSources0, n) }
617func (c *context) tuSources() int { return int(atomic.LoadInt32(&c.tuSources0)) }
618
619func (c *context) stddef(nm StringID, s Scope, tok Token) Type {
620 if d := s.typedef(nm, tok); d != nil {
621 if t := d.Type(); t != nil && t.Kind() != Invalid {
622 return t
623 }
624 }
625
626 c.errNode(&tok, "front-end: undefined: %s", nm)
627 return noType
628}
629
630func (c *context) assignmentCompatibilityErrorCond(n Node, a, b Type) (stop bool) {
631 if !c.cfg.EnableAssignmentCompatibilityChecking {
632 return
633 }
634
635 return c.errNode(n, "invalid type combination of conditional operator: %v and %v", a, b)
636}
637
638func (c *context) assignmentCompatibilityError(n Node, lhs, rhs Type) (stop bool) {
639 if !c.cfg.EnableAssignmentCompatibilityChecking {
640 return
641 }
642
643 return c.errNode(n, "cannot use %v as type %v in assignment", rhs, lhs)
644}
645
646func (c *context) errNode(n Node, msg string, args ...interface{}) (stop bool) {
647 return c.err(n.Position(), msg, args...)
648}
649
650func (c *context) err(pos token.Position, msg string, args ...interface{}) (stop bool) {
651 // dbg("FAIL "+msg, args...)
652 //fmt.Printf("FAIL "+msg+"\n", args...)
653 if c.cfg.ignoreErrors {
654 return false
655 }
656
657 s := fmt.Sprintf(msg, args...)
658 c.mu.Lock()
659 max := c.maxErrors
660 switch {
661 case max < 0 || max > len(c.ErrorList):
662 c.ErrorList.Add(gotoken.Position(pos), s)
663 default:
664 stop = true
665 }
666 c.mu.Unlock()
667 return stop
668}
669
670func (c *context) errs(list goscanner.ErrorList) (stop bool) {
671 c.mu.Lock()
672
673 defer c.mu.Unlock()
674
675 max := c.maxErrors
676 for _, v := range list {
677 switch {
678 case max < 0 || max > len(c.ErrorList):
679 c.ErrorList = append(c.ErrorList, v)
680 default:
681 return true
682 }
683 }
684 return false
685}
686
687func (c *context) Err() error {
688 c.mu.Lock()
689 switch x := c.ErrorList.Err().(type) {
690 case goscanner.ErrorList:
691 x = append(goscanner.ErrorList(nil), x...)
692 c.mu.Unlock()
693 var lpos gotoken.Position
694 w := 0
695 for _, v := range x {
696 if lpos.Filename != "" {
697 if v.Pos.Filename == lpos.Filename && v.Pos.Line == lpos.Line {
698 continue
699 }
700 }
701
702 x[w] = v
703 w++
704 lpos = v.Pos
705 }
706 x = x[:w]
707 sort.Slice(x, func(i, j int) bool {
708 a := x[i]
709 b := x[j]
710 if !a.Pos.IsValid() && b.Pos.IsValid() {
711 return true
712 }
713
714 if a.Pos.IsValid() && !b.Pos.IsValid() {
715 return false
716 }
717
718 if a.Pos.Filename < b.Pos.Filename {
719 return true
720 }
721
722 if a.Pos.Filename > b.Pos.Filename {
723 return false
724 }
725
726 if a.Pos.Line < b.Pos.Line {
727 return true
728 }
729
730 if a.Pos.Line > b.Pos.Line {
731 return false
732 }
733
734 return a.Pos.Column < b.Pos.Column
735 })
736 a := make([]string, 0, len(x))
737 for _, v := range x {
738 a = append(a, v.Error())
739 }
740 return fmt.Errorf("%s", strings.Join(a, "\n"))
741 default:
742 c.mu.Unlock()
743 return x
744 }
745}
746
747func (c *context) not(n Node, mode mode) {
748 if c.mode&mode != 0 {
749 switch mode {
750 case mIntConstExpr:
751 c.errNode(n, "invalid integer constant expression")
752 default:
753 panic(internalError())
754 }
755 }
756}
757
758func (c *context) push(mode mode) {
759 c.modes = append(c.modes, c.mode)
760 c.mode = mode
761}
762
763func (c *context) pop() {
764 n := len(c.modes)
765 c.mode = c.modes[n-1]
766 c.modes = c.modes[:n-1]
767}
768
769func (c *context) statFile(name string, sys bool) (os.FileInfo, error) {
770 fs := c.cfg.Config3.Filesystem
771 if fs == nil {
772 fs = LocalFS()
773 }
774 return fs.Stat(name, sys)
775}
776
777func (c *context) openFile(name string, sys bool) (io.ReadCloser, error) {
778 fs := c.cfg.Config3.Filesystem
779 if fs == nil {
780 fs = LocalFS()
781 }
782 return fs.Open(name, sys)
783}
784
785// HostConfig returns the system C preprocessor/compiler configuration, or an
786// error, if any. The configuration is obtained by running the command named
787// by the cpp argumnent or "cpp" when it's empty. For the predefined macros
788// list the '-dM' options is added. For the include paths lists, the option
789// '-v' is added and the output is parsed to extract the "..." include and
790// <...> include paths. To add any other options to cpp, list them in opts.
791//
792// The function relies on a POSIX/GCC compatible C preprocessor installed.
793// Execution of HostConfig is not free, so caching of the results is
794// recommended.
795func HostConfig(cpp string, opts ...string) (predefined string, includePaths, sysIncludePaths []string, err error) {
796 if predefined, includePaths, sysIncludePaths, err = hostConfigv3(cpp, opts...); err == nil {
797 return predefined, includePaths, sysIncludePaths, nil
798 }
799
800 return hostConfigv4(opts)
801}
802
803func hostConfigv3(cpp string, opts ...string) (predefined string, includePaths, sysIncludePaths []string, err error) {
804 if cpp == "" {
805 cpp = "cpp"
806 }
807 args := append(append([]string{"-dM"}, opts...), os.DevNull)
808 pre, err := exec.Command(cpp, args...).Output()
809 if err != nil {
810 return "", nil, nil, err
811 }
812
813 args = append(append([]string{"-v"}, opts...), os.DevNull)
814 out, err := exec.Command(cpp, args...).CombinedOutput()
815 if err != nil {
816 return "", nil, nil, err
817 }
818
819 sep := "\n"
820 if env("GOOS", runtime.GOOS) == "windows" {
821 sep = "\r\n"
822 }
823
824 a := strings.Split(string(out), sep)
825 for i := 0; i < len(a); {
826 switch a[i] {
827 case "#include \"...\" search starts here:":
828 loop:
829 for i = i + 1; i < len(a); {
830 switch v := a[i]; {
831 case strings.HasPrefix(v, "#") || v == "End of search list.":
832 break loop
833 default:
834 includePaths = append(includePaths, strings.TrimSpace(v))
835 i++
836 }
837 }
838 case "#include <...> search starts here:":
839 for i = i + 1; i < len(a); {
840 switch v := a[i]; {
841 case strings.HasPrefix(v, "#") || v == "End of search list.":
842 return string(pre), includePaths, sysIncludePaths, nil
843 default:
844 sysIncludePaths = append(sysIncludePaths, strings.TrimSpace(v))
845 i++
846 }
847 }
848 default:
849 i++
850 }
851 }
852 return "", nil, nil, fmt.Errorf("failed parsing %s -v output", cpp)
853}
854
855func hostConfigv4(opts []string) (predefined string, includePaths, sysIncludePaths []string, err error) {
856 for _, cc := range []string{os.Getenv("CC"), "cc", "gcc"} {
857 if cc == "" {
858 continue
859 }
860
861 cc, err = exec.LookPath(cc)
862 if err != nil {
863 continue
864 }
865
866 args := append(opts, "-dM", "-E", "-")
867 pre, err := exec.Command(cc, args...).CombinedOutput()
868 if err != nil {
869 continue
870 }
871
872 sep := "\n"
873 if env("GOOS", runtime.GOOS) == "windows" {
874 sep = "\r\n"
875 }
876 a := strings.Split(string(pre), sep)
877 w := 0
878 for _, v := range a {
879 if strings.HasPrefix(v, "#") {
880 a[w] = v
881 w++
882 }
883 }
884 predefined = strings.Join(a[:w], "\n")
885 args = append(opts, "-v", "-E", "-")
886 out, err := exec.Command(cc, args...).CombinedOutput()
887 if err != nil {
888 continue
889 }
890
891 a = strings.Split(string(out), sep)
892 for i := 0; i < len(a); {
893 switch a[i] {
894 case "#include \"...\" search starts here:":
895 loop:
896 for i = i + 1; i < len(a); {
897 switch v := a[i]; {
898 case strings.HasPrefix(v, "#") || v == "End of search list.":
899 break loop
900 default:
901 includePaths = append(includePaths, strings.TrimSpace(v))
902 i++
903 }
904 }
905 case "#include <...> search starts here:":
906 for i = i + 1; i < len(a); {
907 switch v := a[i]; {
908 case strings.HasPrefix(v, "#") || v == "End of search list.":
909 return predefined, includePaths, sysIncludePaths, nil
910 default:
911 sysIncludePaths = append(sysIncludePaths, strings.TrimSpace(v))
912 i++
913 }
914 }
915 default:
916 i++
917 }
918 }
919 }
920 return "", nil, nil, fmt.Errorf("cannot determine C compiler configuration")
921}
922
923func env(key, val string) string {
924 if s := os.Getenv(key); s != "" {
925 return s
926 }
927
928 return val
929}
930
931// Token is a grammar terminal.
932type Token struct {
933 Rune rune // ';' or IDENTIFIER etc.
934 Sep StringID // If Config3.PreserveWhiteSpace is in effect: All preceding white space combined, including comments.
935 Value StringID // ";" or "foo" etc.
936 Src StringID
937 file *tokenFile
938 macro StringID
939 pos int32
940 seq int32
941}
942
943// Seq returns t's sequential number.
944//
945// Comparing positions as in 'before', 'after' is complicated as tokens in a
946// translation unit usually come from more than one source file. Macro
947// expansion further complicates that. The solution is sequentially numbering
948// the tokens as they are finally seen by the parser, so the usual arithmetic
949// '<', '>' operators can be used for that purpose.
950func (t Token) Seq() int { return int(t.seq) }
951
952// Macro returns the name of a macro that expanded to this token, if any.
953func (t *Token) Macro() StringID { return t.macro }
954
955// String implements fmt.Stringer.
956func (t Token) String() string { return t.Value.String() }
957
958// Position implements Node.
959func (t *Token) Position() (r token.Position) {
960 if t.pos != 0 && t.file != nil {
961 r = t.file.PositionFor(token.Pos(t.pos), true)
962 }
963 return r
964}
965
966func tokStr(toks interface{}, sep string) string {
967 var b strings.Builder
968 switch x := toks.(type) {
969 case []token3:
970 for i, v := range x {
971 if i != 0 {
972 b.WriteString(sep)
973 }
974 b.WriteString(v.String())
975 }
976 case []token4:
977 for i, v := range x {
978 if i != 0 {
979 b.WriteString(sep)
980 }
981 b.WriteString(v.String())
982 }
983 case []cppToken:
984 for i, v := range x {
985 if i != 0 {
986 b.WriteString(sep)
987 }
988 b.WriteString(v.String())
989 }
990 case []Token:
991 for i, v := range x {
992 if i != 0 {
993 b.WriteString(sep)
994 }
995 b.WriteString(v.String())
996 }
997 default:
998 panic(internalError())
999 }
1000 return b.String()
1001}
1002
1003func internalError() int {
1004 panic(fmt.Errorf("%v: internal error", origin(2)))
1005}
1006
1007func internalErrorf(s string, args ...interface{}) int {
1008 s = fmt.Sprintf(s, args)
1009 panic(fmt.Errorf("%v: %s", origin(2), s))
1010}
1011
1012func detectMingw(s string) bool {
1013 return strings.Contains(s, "#define __MINGW")
1014}
1015
1016func nodeSource(n ...Node) (r string) {
1017 if len(n) == 0 {
1018 return ""
1019 }
1020
1021 var a []*Token
1022 for _, v := range n {
1023 Inspect(v, func(n Node, _ bool) bool {
1024 if x, ok := n.(*Token); ok && x.Seq() != 0 {
1025 a = append(a, x)
1026 }
1027 return true
1028 })
1029 }
1030 sort.Slice(a, func(i, j int) bool {
1031 return a[i].Seq() < a[j].Seq()
1032 })
1033 w := 0
1034 seq := -1
1035 for _, v := range a {
1036 if n := v.Seq(); n != seq {
1037 seq = n
1038 a[w] = v
1039 w++
1040 }
1041 }
1042 a = a[:w]
1043 var b strings.Builder
1044 for _, v := range a {
1045 b.WriteString(v.Sep.String())
1046 b.WriteString(v.Src.String())
1047 }
1048 return b.String()
1049}
Note: See TracBrowser for help on using the repository browser.