mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:02:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			1161 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			1161 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright 2019 The CC Authors. All rights reserved. | ||
|  | // Use of this source code is governed by a BSD-style | ||
|  | // license that can be found in the LICENSE file. | ||
|  | 
 | ||
|  | package cc // import "modernc.org/cc/v3" | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"fmt" | ||
|  | 	"io" | ||
|  | 	"os" | ||
|  | 	"path/filepath" | ||
|  | 	"sort" | ||
|  | 	"strings" | ||
|  | ) | ||
|  | 
 | ||
|  | // Source is a named part of a translation unit. If Value is empty, Name is | ||
|  | // interpreted as a path to file containing the source code. | ||
|  | type Source struct { | ||
|  | 	Name       string | ||
|  | 	Value      string | ||
|  | 	DoNotCache bool // Disable caching of this source | ||
|  | } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of a binary operation are promoted to | ||
|  | // or the type and argument passed in a function call is promoted. | ||
|  | func (n *AssignmentExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | type StructInfo struct { | ||
|  | 	Size uintptr | ||
|  | 
 | ||
|  | 	Align int | ||
|  | } | ||
|  | 
 | ||
|  | // AST represents a translation unit and its related data. | ||
|  | type AST struct { | ||
|  | 	Enums       map[StringID]Operand // Enumeration constants declared in file scope. | ||
|  | 	Macros      map[StringID]*Macro  // Macros as defined after parsing. | ||
|  | 	PtrdiffType Type | ||
|  | 	Scope       Scope // File scope. | ||
|  | 	SizeType    Type | ||
|  | 	StructTypes map[StringID]Type // Tagged struct/union types declared in file scope. | ||
|  | 	// Alignment and size of every struct/union defined in the translation | ||
|  | 	// unit. Valid only after Translate. | ||
|  | 	Structs map[StructInfo]struct{} | ||
|  | 	// TLD contains pruned file scope declarators, ie. either the first one | ||
|  | 	// or the first one that has an initializer. | ||
|  | 	TLD               map[*Declarator]struct{} | ||
|  | 	TrailingSeperator StringID // White space and/or comments preceding EOF. | ||
|  | 	TranslationUnit   *TranslationUnit | ||
|  | 	WideCharType      Type | ||
|  | 	cfg               *Config | ||
|  | 	cpp               *cpp | ||
|  | } | ||
|  | 
 | ||
|  | // Eval returns the operand that represents the value of m, if it expands to a | ||
|  | // valid constant expression other than an identifier, or an error, if any. | ||
|  | func (n *AST) Eval(m *Macro) (o Operand, err error) { | ||
|  | 	defer func() { | ||
|  | 		if e := recover(); e != nil { | ||
|  | 			o = nil | ||
|  | 			err = fmt.Errorf("%v", e) | ||
|  | 		} | ||
|  | 	}() | ||
|  | 
 | ||
|  | 	if m.IsFnLike() { | ||
|  | 		return nil, fmt.Errorf("cannot evaluate function-like macro") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	n.cpp.ctx.cfg.ignoreErrors = true | ||
|  | 	n.cpp.ctx.evalIdentError = true | ||
|  | 	v := n.cpp.eval(m.repl) | ||
|  | 	switch x := v.(type) { | ||
|  | 	case int64: | ||
|  | 		return &operand{abi: &n.cfg.ABI, typ: n.cfg.ABI.Type(LongLong), value: Int64Value(x)}, nil | ||
|  | 	case uint64: | ||
|  | 		return &operand{abi: &n.cfg.ABI, typ: n.cfg.ABI.Type(ULongLong), value: Uint64Value(x)}, nil | ||
|  | 	default: | ||
|  | 		return nil, fmt.Errorf("unexpected value: %T", x) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Parse preprocesses and parses a translation unit and returns an *AST or | ||
|  | // error, if any. | ||
|  | // | ||
|  | // Search paths listed in includePaths and sysIncludePaths are used to resolve | ||
|  | // #include "foo.h" and #include <foo.h> preprocessing directives respectively. | ||
|  | // A special search path "@" is interpreted as 'the same directory as where the | ||
|  | // file with the #include directive is'. | ||
|  | // | ||
|  | // The sources should typically provide, usually in this particular order: | ||
|  | // | ||
|  | // - predefined macros, eg. | ||
|  | // | ||
|  | //	#define __SIZE_TYPE__ long unsigned int | ||
|  | // | ||
|  | // - built-in declarations, eg. | ||
|  | // | ||
|  | //	int __builtin_printf(char *__format, ...); | ||
|  | // | ||
|  | // - command-line provided directives, eg. | ||
|  | // | ||
|  | //	#define FOO | ||
|  | //	#define BAR 42 | ||
|  | //	#undef QUX | ||
|  | // | ||
|  | // - normal C sources, eg. | ||
|  | // | ||
|  | //	int main() {} | ||
|  | // | ||
|  | // All search and file paths should be absolute paths. | ||
|  | // | ||
|  | // If the preprocessed translation unit is empty, the function may return (nil, | ||
|  | // nil). | ||
|  | // | ||
|  | // The parser does only the minimum declarations/identifier resolving necessary | ||
|  | // for correct parsing. Redeclarations are not checked. | ||
|  | // | ||
|  | // Declarators (*Declarator) and StructDeclarators (*StructDeclarator) are | ||
|  | // inserted in the appropriate scopes. | ||
|  | // | ||
|  | // Tagged struct/union specifier definitions (*StructOrUnionSpecifier) are | ||
|  | // inserted in the appropriate scopes. | ||
|  | // | ||
|  | // Tagged enum specifier definitions (*EnumSpecifier) and enumeration constants | ||
|  | // (*Enumerator) are inserted in the appropriate scopes. | ||
|  | // | ||
|  | // Labels (*LabeledStatement) are inserted in the appropriate scopes. | ||
|  | func Parse(cfg *Config, includePaths, sysIncludePaths []string, sources []Source) (*AST, error) { | ||
|  | 	return parse(newContext(cfg), includePaths, sysIncludePaths, sources) | ||
|  | } | ||
|  | 
 | ||
|  | func parse(ctx *context, includePaths, sysIncludePaths []string, sources []Source) (*AST, error) { | ||
|  | 	if s := ctx.cfg.SharedFunctionDefinitions; s != nil { | ||
|  | 		if s.M == nil { | ||
|  | 			s.M = map[*FunctionDefinition]struct{}{} | ||
|  | 		} | ||
|  | 		if s.m == nil { | ||
|  | 			s.m = map[sharedFunctionDefinitionKey]*FunctionDefinition{} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if debugWorkingDir || ctx.cfg.DebugWorkingDir { | ||
|  | 		switch wd, err := os.Getwd(); err { | ||
|  | 		case nil: | ||
|  | 			fmt.Fprintf(os.Stderr, "OS working dir: %s\n", wd) | ||
|  | 		default: | ||
|  | 			fmt.Fprintf(os.Stderr, "OS working dir: error %s\n", err) | ||
|  | 		} | ||
|  | 		fmt.Fprintf(os.Stderr, "Config.WorkingDir: %s\n", ctx.cfg.WorkingDir) | ||
|  | 	} | ||
|  | 	if debugIncludePaths || ctx.cfg.DebugIncludePaths { | ||
|  | 		fmt.Fprintf(os.Stderr, "include paths: %v\n", includePaths) | ||
|  | 		fmt.Fprintf(os.Stderr, "system include paths: %v\n", sysIncludePaths) | ||
|  | 	} | ||
|  | 	ctx.includePaths = includePaths | ||
|  | 	ctx.sysIncludePaths = sysIncludePaths | ||
|  | 	var in []source | ||
|  | 	for _, v := range sources { | ||
|  | 		ts, err := cache.get(ctx, v) | ||
|  | 		if err != nil { | ||
|  | 			return nil, err | ||
|  | 		} | ||
|  | 
 | ||
|  | 		in = append(in, ts) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	p := newParser(ctx, make(chan *[]Token, 5000)) //DONE benchmark tuned | ||
|  | 	var sep StringID | ||
|  | 	var ssep []byte | ||
|  | 	var seq int32 | ||
|  | 	cpp := newCPP(ctx) | ||
|  | 	go func() { | ||
|  | 
 | ||
|  | 		defer func() { | ||
|  | 			close(p.in) | ||
|  | 			ctx.intMaxWidth = cpp.intMaxWidth() | ||
|  | 		}() | ||
|  | 
 | ||
|  | 		toks := tokenPool.Get().(*[]Token) | ||
|  | 		*toks = (*toks)[:0] | ||
|  | 		for pline := range cpp.translationPhase4(in) { | ||
|  | 			line := *pline | ||
|  | 			for _, tok := range line { | ||
|  | 				switch tok.char { | ||
|  | 				case ' ', '\n': | ||
|  | 					if ctx.cfg.PreserveOnlyLastNonBlankSeparator { | ||
|  | 						if strings.TrimSpace(tok.value.String()) != "" { | ||
|  | 							sep = tok.value | ||
|  | 						} | ||
|  | 						break | ||
|  | 					} | ||
|  | 
 | ||
|  | 					switch { | ||
|  | 					case sep != 0: | ||
|  | 						ssep = append(ssep, tok.String()...) | ||
|  | 					default: | ||
|  | 						sep = tok.value | ||
|  | 						ssep = append(ssep[:0], sep.String()...) | ||
|  | 					} | ||
|  | 				default: | ||
|  | 					var t Token | ||
|  | 					t.Rune = tok.char | ||
|  | 					switch { | ||
|  | 					case len(ssep) != 0: | ||
|  | 						t.Sep = dict.id(ssep) | ||
|  | 					default: | ||
|  | 						t.Sep = sep | ||
|  | 					} | ||
|  | 					t.Value = tok.value | ||
|  | 					t.Src = tok.src | ||
|  | 					t.file = tok.file | ||
|  | 					t.macro = tok.macro | ||
|  | 					t.pos = tok.pos | ||
|  | 					seq++ | ||
|  | 					t.seq = seq | ||
|  | 					*toks = append(*toks, t) | ||
|  | 					sep = 0 | ||
|  | 					ssep = ssep[:0] | ||
|  | 				} | ||
|  | 			} | ||
|  | 			token4Pool.Put(pline) | ||
|  | 			var c rune | ||
|  | 			if n := len(*toks); n != 0 { | ||
|  | 				c = (*toks)[n-1].Rune | ||
|  | 			} | ||
|  | 			switch c { | ||
|  | 			case STRINGLITERAL, LONGSTRINGLITERAL: | ||
|  | 				// nop | ||
|  | 			default: | ||
|  | 				if len(*toks) != 0 { | ||
|  | 					p.in <- translationPhase5(ctx, toks) | ||
|  | 					toks = tokenPool.Get().(*[]Token) | ||
|  | 					*toks = (*toks)[:0] | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 		if len(*toks) != 0 { | ||
|  | 			p.in <- translationPhase5(ctx, toks) | ||
|  | 		} | ||
|  | 	}() | ||
|  | 
 | ||
|  | 	tu := p.translationUnit() | ||
|  | 	if p.errored { // Must drain | ||
|  | 		go func() { | ||
|  | 			for range p.in { | ||
|  | 			} | ||
|  | 		}() | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if err := ctx.Err(); err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if p.errored && !ctx.cfg.ignoreErrors { | ||
|  | 		return nil, fmt.Errorf("%v: syntax error", p.tok.Position()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if p.scopes != 0 { | ||
|  | 		panic(internalErrorf("invalid scope nesting but no error reported")) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ts := sep | ||
|  | 	if len(ssep) != 0 { | ||
|  | 		ts = dict.id(ssep) | ||
|  | 	} | ||
|  | 	return &AST{ | ||
|  | 		Macros:            cpp.macros, | ||
|  | 		Scope:             p.fileScope, | ||
|  | 		TLD:               map[*Declarator]struct{}{}, | ||
|  | 		TrailingSeperator: ts, | ||
|  | 		TranslationUnit:   tu, | ||
|  | 		cfg:               ctx.cfg, | ||
|  | 		cpp:               cpp, | ||
|  | 	}, nil | ||
|  | } | ||
|  | 
 | ||
|  | func translationPhase5(ctx *context, toks *[]Token) *[]Token { | ||
|  | 	// [0], 5.1.1.2, 5 | ||
|  | 	// | ||
|  | 	// Each source character set member and escape sequence in character | ||
|  | 	// constants and string literals is converted to the corresponding | ||
|  | 	// member of the execution character set; if there is no corresponding | ||
|  | 	// member, it is converted to an implementation- defined member other | ||
|  | 	// than the null (wide) character. | ||
|  | 	for i, tok := range *toks { | ||
|  | 		var cpt cppToken | ||
|  | 		switch tok.Rune { | ||
|  | 		case STRINGLITERAL, LONGSTRINGLITERAL: | ||
|  | 			cpt.char = tok.Rune | ||
|  | 			cpt.value = tok.Value | ||
|  | 			cpt.src = tok.Src | ||
|  | 			cpt.file = tok.file | ||
|  | 			cpt.pos = tok.pos | ||
|  | 			(*toks)[i].Value = dict.sid(stringConst(ctx, cpt)) | ||
|  | 		case CHARCONST, LONGCHARCONST: | ||
|  | 			var cpt cppToken | ||
|  | 			cpt.char = tok.Rune | ||
|  | 			cpt.value = tok.Value | ||
|  | 			cpt.src = tok.Src | ||
|  | 			cpt.file = tok.file | ||
|  | 			cpt.pos = tok.pos | ||
|  | 			switch r := charConst(ctx, cpt); { | ||
|  | 			case r <= 255: | ||
|  | 				(*toks)[i].Value = dict.sid(string(r)) | ||
|  | 			default: | ||
|  | 				switch cpt.char { | ||
|  | 				case CHARCONST: | ||
|  | 					ctx.err(tok.Position(), "invalid character constant: %s", tok.Value) | ||
|  | 				default: | ||
|  | 					(*toks)[i].Value = dict.sid(string(r)) | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return toks | ||
|  | } | ||
|  | 
 | ||
|  | // Preprocess preprocesses a translation unit and outputs the result to w. | ||
|  | // | ||
|  | // Please see Parse for the documentation of the other parameters. | ||
|  | func Preprocess(cfg *Config, includePaths, sysIncludePaths []string, sources []Source, w io.Writer) error { | ||
|  | 	ctx := newContext(cfg) | ||
|  | 	if debugWorkingDir || ctx.cfg.DebugWorkingDir { | ||
|  | 		switch wd, err := os.Getwd(); err { | ||
|  | 		case nil: | ||
|  | 			fmt.Fprintf(os.Stderr, "OS working dir: %s\n", wd) | ||
|  | 		default: | ||
|  | 			fmt.Fprintf(os.Stderr, "OS working dir: error %s\n", err) | ||
|  | 		} | ||
|  | 		fmt.Fprintf(os.Stderr, "Config.WorkingDir: %s\n", ctx.cfg.WorkingDir) | ||
|  | 	} | ||
|  | 	if debugIncludePaths || ctx.cfg.DebugIncludePaths { | ||
|  | 		fmt.Fprintf(os.Stderr, "include paths: %v\n", includePaths) | ||
|  | 		fmt.Fprintf(os.Stderr, "system include paths: %v\n", sysIncludePaths) | ||
|  | 	} | ||
|  | 	ctx.includePaths = includePaths | ||
|  | 	ctx.sysIncludePaths = sysIncludePaths | ||
|  | 	var in []source | ||
|  | 	for _, v := range sources { | ||
|  | 		ts, err := cache.get(ctx, v) | ||
|  | 		if err != nil { | ||
|  | 			return err | ||
|  | 		} | ||
|  | 
 | ||
|  | 		in = append(in, ts) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var sep StringID | ||
|  | 	cpp := newCPP(ctx) | ||
|  | 	toks := tokenPool.Get().(*[]Token) | ||
|  | 	*toks = (*toks)[:0] | ||
|  | 	for pline := range cpp.translationPhase4(in) { | ||
|  | 		line := *pline | ||
|  | 		for _, tok := range line { | ||
|  | 			switch tok.char { | ||
|  | 			case ' ', '\n': | ||
|  | 				if ctx.cfg.PreserveOnlyLastNonBlankSeparator { | ||
|  | 					if strings.TrimSpace(tok.value.String()) != "" { | ||
|  | 						sep = tok.value | ||
|  | 					} | ||
|  | 					break | ||
|  | 				} | ||
|  | 
 | ||
|  | 				switch { | ||
|  | 				case sep != 0: | ||
|  | 					sep = dict.sid(sep.String() + tok.String()) | ||
|  | 				default: | ||
|  | 					sep = tok.value | ||
|  | 				} | ||
|  | 			default: | ||
|  | 				var t Token | ||
|  | 				t.Rune = tok.char | ||
|  | 				t.Sep = sep | ||
|  | 				t.Value = tok.value | ||
|  | 				t.Src = tok.src | ||
|  | 				t.file = tok.file | ||
|  | 				t.pos = tok.pos | ||
|  | 				*toks = append(*toks, t) | ||
|  | 				sep = 0 | ||
|  | 			} | ||
|  | 		} | ||
|  | 		token4Pool.Put(pline) | ||
|  | 		var c rune | ||
|  | 		if n := len(*toks); n != 0 { | ||
|  | 			c = (*toks)[n-1].Rune | ||
|  | 		} | ||
|  | 		switch c { | ||
|  | 		case STRINGLITERAL, LONGSTRINGLITERAL: | ||
|  | 			// nop | ||
|  | 		default: | ||
|  | 			if len(*toks) != 0 { | ||
|  | 				for _, v := range *translationPhase5(ctx, toks) { | ||
|  | 					if err := wTok(w, v); err != nil { | ||
|  | 						return err | ||
|  | 					} | ||
|  | 				} | ||
|  | 				toks = tokenPool.Get().(*[]Token) | ||
|  | 				*toks = (*toks)[:0] | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if len(*toks) != 0 { | ||
|  | 		for _, v := range *translationPhase5(ctx, toks) { | ||
|  | 			if err := wTok(w, v); err != nil { | ||
|  | 				return err | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if _, err := fmt.Fprintln(w); err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 	return ctx.Err() | ||
|  | } | ||
|  | 
 | ||
|  | func wTok(w io.Writer, tok Token) (err error) { | ||
|  | 	switch tok.Rune { | ||
|  | 	case STRINGLITERAL, LONGSTRINGLITERAL: | ||
|  | 		_, err = fmt.Fprintf(w, `%s"%s"`, tok.Sep, cQuotedString(tok.String())) | ||
|  | 	case CHARCONST, LONGCHARCONST: | ||
|  | 		_, err = fmt.Fprintf(w, `%s'%s'`, tok.Sep, cQuotedString(tok.String())) | ||
|  | 	default: | ||
|  | 		_, err = fmt.Fprintf(w, "%s%s", tok.Sep, tok) | ||
|  | 	} | ||
|  | 	return err | ||
|  | } | ||
|  | 
 | ||
|  | func cQuotedString(s string) []byte { | ||
|  | 	var b []byte | ||
|  | 	for i := 0; i < len(s); i++ { | ||
|  | 		c := s[i] | ||
|  | 		switch c { | ||
|  | 		case '\b': | ||
|  | 			b = append(b, '\\', 'b') | ||
|  | 			continue | ||
|  | 		case '\f': | ||
|  | 			b = append(b, '\\', 'f') | ||
|  | 			continue | ||
|  | 		case '\n': | ||
|  | 			b = append(b, '\\', 'n') | ||
|  | 			continue | ||
|  | 		case '\r': | ||
|  | 			b = append(b, '\\', 'r') | ||
|  | 			continue | ||
|  | 		case '\t': | ||
|  | 			b = append(b, '\\', 't') | ||
|  | 			continue | ||
|  | 		case '\\': | ||
|  | 			b = append(b, '\\', '\\') | ||
|  | 			continue | ||
|  | 		case '"': | ||
|  | 			b = append(b, '\\', '"') | ||
|  | 			continue | ||
|  | 		} | ||
|  | 
 | ||
|  | 		switch { | ||
|  | 		case c < ' ' || c >= 0x7f: | ||
|  | 			b = append(b, '\\', octal(c>>6), octal(c>>3), octal(c)) | ||
|  | 		default: | ||
|  | 			b = append(b, c) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return b | ||
|  | } | ||
|  | 
 | ||
|  | func octal(b byte) byte { return '0' + b&7 } | ||
|  | 
 | ||
|  | var trcSource = Source{"<builtin-trc>", ` | ||
|  | extern void *stderr; | ||
|  | int fflush(void *stream); | ||
|  | int fprintf(void *stream, const char *format, ...); | ||
|  | `, false} | ||
|  | 
 | ||
|  | // Translate parses and typechecks a translation unit  and returns an *AST or | ||
|  | // error, if any. | ||
|  | // | ||
|  | // Please see Parse for the documentation of the parameters. | ||
|  | func Translate(cfg *Config, includePaths, sysIncludePaths []string, sources []Source) (*AST, error) { | ||
|  | 	if cfg.InjectTracingCode { | ||
|  | 		for i, v := range sources { | ||
|  | 			if filepath.Ext(v.Name) == ".c" { | ||
|  | 				sources = append(append(append([]Source(nil), sources[:i]...), trcSource), sources[i:]...) | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return translate(newContext(cfg), includePaths, sysIncludePaths, sources) | ||
|  | } | ||
|  | 
 | ||
|  | func translate(ctx *context, includePaths, sysIncludePaths []string, sources []Source) (*AST, error) { | ||
|  | 	ast, err := parse(ctx, includePaths, sysIncludePaths, sources) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ctx, err = ast.typecheck(); err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ast.PtrdiffType = ptrdiffT(ctx, ast.Scope, Token{}) | ||
|  | 	ast.SizeType = sizeT(ctx, ast.Scope, Token{}) | ||
|  | 	ast.WideCharType = wcharT(ctx, ast.Scope, Token{}) | ||
|  | 	return ast, nil | ||
|  | } | ||
|  | 
 | ||
|  | // Typecheck determines types of objects and expressions and verifies types are | ||
|  | // valid in the context they are used. | ||
|  | func (n *AST) Typecheck() error { | ||
|  | 	_, err := n.typecheck() | ||
|  | 	return err | ||
|  | } | ||
|  | 
 | ||
|  | func (n *AST) typecheck() (*context, error) { | ||
|  | 	ctx := newContext(n.cfg) | ||
|  | 	if err := ctx.cfg.ABI.sanityCheck(ctx, int(ctx.intMaxWidth), n.Scope); err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ctx.intBits = int(ctx.cfg.ABI.Types[Int].Size) * 8 | ||
|  | 	ctx.ast = n | ||
|  | 	n.TranslationUnit.check(ctx) | ||
|  | 	n.Structs = ctx.structs | ||
|  | 	var a []int | ||
|  | 	for k := range n.Scope { | ||
|  | 		a = append(a, int(k)) | ||
|  | 	} | ||
|  | 	sort.Ints(a) | ||
|  | 	for _, v := range a { | ||
|  | 		nm := StringID(v) | ||
|  | 		defs := n.Scope[nm] | ||
|  | 		var r, w int | ||
|  | 		for _, v := range defs { | ||
|  | 			switch x := v.(type) { | ||
|  | 			case *Declarator: | ||
|  | 				r += x.Read | ||
|  | 				w += x.Write | ||
|  | 			} | ||
|  | 		} | ||
|  | 		for _, v := range defs { | ||
|  | 			switch x := v.(type) { | ||
|  | 			case *Declarator: | ||
|  | 				x.Read = r | ||
|  | 				x.Write = w | ||
|  | 			} | ||
|  | 		} | ||
|  | 		var pruned *Declarator | ||
|  | 		for _, v := range defs { | ||
|  | 			switch x := v.(type) { | ||
|  | 			case *Declarator: | ||
|  | 				//TODO check compatible types | ||
|  | 				switch { | ||
|  | 				case x.IsExtern() && !x.fnDef: | ||
|  | 					// nop | ||
|  | 				case pruned == nil: | ||
|  | 					pruned = x | ||
|  | 				case pruned.hasInitializer && x.hasInitializer: | ||
|  | 					ctx.errNode(x, "multiple initializers for the same symbol") | ||
|  | 					continue | ||
|  | 				case pruned.fnDef && x.fnDef: | ||
|  | 					ctx.errNode(x, "multiple function definitions") | ||
|  | 					continue | ||
|  | 				case x.hasInitializer || x.fnDef: | ||
|  | 					pruned = x | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 		if pruned == nil { | ||
|  | 			continue | ||
|  | 		} | ||
|  | 
 | ||
|  | 		n.TLD[pruned] = struct{}{} | ||
|  | 	} | ||
|  | 	n.Enums = ctx.enums | ||
|  | 	n.StructTypes = ctx.structTypes | ||
|  | 	return ctx, ctx.Err() | ||
|  | } | ||
|  | 
 | ||
|  | func (n *AlignmentSpecifier) align() int { | ||
|  | 	switch n.Case { | ||
|  | 	case AlignmentSpecifierAlignasType: // "_Alignas" '(' TypeName ')' | ||
|  | 		return n.TypeName.Type().Align() | ||
|  | 	case AlignmentSpecifierAlignasExpr: // "_Alignas" '(' ConstantExpression ')' | ||
|  | 		return n.ConstantExpression.Operand.Type().Align() | ||
|  | 	default: | ||
|  | 		panic(internalError()) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Closure reports the variables closed over by a nested function (case | ||
|  | // BlockItemFuncDef). | ||
|  | func (n *BlockItem) Closure() map[StringID]struct{} { return n.closure } | ||
|  | 
 | ||
|  | // FunctionDefinition returns the nested function (case BlockItemFuncDef). | ||
|  | func (n *BlockItem) FunctionDefinition() *FunctionDefinition { return n.fn } | ||
|  | 
 | ||
|  | func (n *Declarator) IsStatic() bool          { return n.td != nil && n.td.static() } | ||
|  | func (n *Declarator) isVisible(at int32) bool { return at == 0 || n.DirectDeclarator.ends() < at } | ||
|  | 
 | ||
|  | func (n *Declarator) setLHS(lhs *Declarator) { | ||
|  | 	if n == nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if n.lhs == nil { | ||
|  | 		n.lhs = map[*Declarator]struct{}{} | ||
|  | 	} | ||
|  | 	n.lhs[lhs] = struct{}{} | ||
|  | } | ||
|  | 
 | ||
|  | // LHS reports which declarators n is used in assignment RHS or which function | ||
|  | // declarators n is used in a function argument. To collect this information, | ||
|  | // TrackAssignments in Config must be set during type checking. | ||
|  | // The returned map may contain a nil key. That means that n is assigned to a | ||
|  | // declarator not known at typechecking time. | ||
|  | func (n *Declarator) LHS() map[*Declarator]struct{} { return n.lhs } | ||
|  | 
 | ||
|  | // Called reports whether n is involved in expr in expr(callArgs). | ||
|  | func (n *Declarator) Called() bool { return n.called } | ||
|  | 
 | ||
|  | // FunctionDefinition returns the function definition associated with n, if any. | ||
|  | func (n *Declarator) FunctionDefinition() *FunctionDefinition { | ||
|  | 	return n.funcDefinition | ||
|  | } | ||
|  | 
 | ||
|  | // NameTok returns n's declaring name token. | ||
|  | func (n *Declarator) NameTok() (r Token) { | ||
|  | 	if n == nil || n.DirectDeclarator == nil { | ||
|  | 		return r | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return n.DirectDeclarator.NameTok() | ||
|  | } | ||
|  | 
 | ||
|  | // LexicalScope returns the lexical scope of n. | ||
|  | func (n *Declarator) LexicalScope() Scope { return n.DirectDeclarator.lexicalScope } | ||
|  | 
 | ||
|  | // Name returns n's declared name. | ||
|  | func (n *Declarator) Name() StringID { | ||
|  | 	if n == nil || n.DirectDeclarator == nil { | ||
|  | 		return 0 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return n.DirectDeclarator.Name() | ||
|  | } | ||
|  | 
 | ||
|  | // ParamScope returns the scope in which n's function parameters are declared | ||
|  | // if the underlying type of n is a function or nil otherwise. If n is part of | ||
|  | // a function definition the scope is the same as the scope of the function | ||
|  | // body. | ||
|  | func (n *Declarator) ParamScope() Scope { | ||
|  | 	if n == nil { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return n.DirectDeclarator.ParamScope() | ||
|  | } | ||
|  | 
 | ||
|  | // Type returns the type of n. | ||
|  | func (n *Declarator) Type() Type { return n.typ } | ||
|  | 
 | ||
|  | // IsExtern reports whether n was declared with storage class specifier 'extern'. | ||
|  | func (n *Declarator) IsExtern() bool { return n.td != nil && n.td.extern() } | ||
|  | 
 | ||
|  | func (n *DeclarationSpecifiers) auto() bool        { return n != nil && n.class&fAuto != 0 } | ||
|  | func (n *DeclarationSpecifiers) extern() bool      { return n != nil && n.class&fExtern != 0 } | ||
|  | func (n *DeclarationSpecifiers) register() bool    { return n != nil && n.class&fRegister != 0 } | ||
|  | func (n *DeclarationSpecifiers) static() bool      { return n != nil && n.class&fStatic != 0 } | ||
|  | func (n *DeclarationSpecifiers) threadLocal() bool { return n != nil && n.class&fThreadLocal != 0 } | ||
|  | func (n *DeclarationSpecifiers) typedef() bool     { return n != nil && n.class&fTypedef != 0 } | ||
|  | 
 | ||
|  | func (n *DirectAbstractDeclarator) TypeQualifier() Type { return n.typeQualifiers } | ||
|  | 
 | ||
|  | func (n *DirectDeclarator) ends() int32 { | ||
|  | 	switch n.Case { | ||
|  | 	case DirectDeclaratorIdent: // IDENTIFIER | ||
|  | 		return n.Token.seq | ||
|  | 	case DirectDeclaratorDecl: // '(' Declarator ')' | ||
|  | 		return n.Token2.seq | ||
|  | 	case DirectDeclaratorArr: // DirectDeclarator '[' TypeQualifierList AssignmentExpression ']' | ||
|  | 		return n.Token2.seq | ||
|  | 	case DirectDeclaratorStaticArr: // DirectDeclarator '[' "static" TypeQualifierList AssignmentExpression ']' | ||
|  | 		return n.Token3.seq | ||
|  | 	case DirectDeclaratorArrStatic: // DirectDeclarator '[' TypeQualifierList "static" AssignmentExpression ']' | ||
|  | 		return n.Token3.seq | ||
|  | 	case DirectDeclaratorStar: // DirectDeclarator '[' TypeQualifierList '*' ']' | ||
|  | 		return n.Token3.seq | ||
|  | 	case DirectDeclaratorFuncParam: // DirectDeclarator '(' ParameterTypeList ')' | ||
|  | 		return n.Token2.seq | ||
|  | 	case DirectDeclaratorFuncIdent: // DirectDeclarator '(' IdentifierList ')' | ||
|  | 		return n.Token2.seq | ||
|  | 	default: | ||
|  | 		panic(internalError()) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *DirectDeclarator) TypeQualifier() Type { return n.typeQualifiers } | ||
|  | 
 | ||
|  | // NameTok returns n's declarin name token. | ||
|  | func (n *DirectDeclarator) NameTok() (r Token) { | ||
|  | 	for { | ||
|  | 		if n == nil { | ||
|  | 			return r | ||
|  | 		} | ||
|  | 
 | ||
|  | 		switch n.Case { | ||
|  | 		case DirectDeclaratorIdent: // IDENTIFIER | ||
|  | 			return n.Token | ||
|  | 		case DirectDeclaratorDecl: // '(' Declarator ')' | ||
|  | 			return n.Declarator.NameTok() | ||
|  | 		default: | ||
|  | 			n = n.DirectDeclarator | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Name returns n's declared name. | ||
|  | func (n *DirectDeclarator) Name() StringID { | ||
|  | 	for { | ||
|  | 		if n == nil { | ||
|  | 			return 0 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		switch n.Case { | ||
|  | 		case DirectDeclaratorIdent: // IDENTIFIER | ||
|  | 			return n.Token.Value | ||
|  | 		case DirectDeclaratorDecl: // '(' Declarator ')' | ||
|  | 			return n.Declarator.Name() | ||
|  | 		default: | ||
|  | 			n = n.DirectDeclarator | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // ParamScope returns the innermost scope in which function parameters are | ||
|  | // declared for Case DirectDeclaratorFuncParam or DirectDeclaratorFuncIdent or | ||
|  | // nil otherwise. | ||
|  | func (n *DirectDeclarator) ParamScope() Scope { | ||
|  | 	if n == nil { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switch n.Case { | ||
|  | 	case DirectDeclaratorIdent: // IDENTIFIER | ||
|  | 		return nil | ||
|  | 	case DirectDeclaratorDecl: // '(' Declarator ')' | ||
|  | 		return n.Declarator.ParamScope() | ||
|  | 	case DirectDeclaratorArr: // DirectDeclarator '[' TypeQualifierList AssignmentExpression ']' | ||
|  | 		return n.DirectDeclarator.ParamScope() | ||
|  | 	case DirectDeclaratorStaticArr: // DirectDeclarator '[' "static" TypeQualifierList AssignmentExpression ']' | ||
|  | 		return n.DirectDeclarator.ParamScope() | ||
|  | 	case DirectDeclaratorArrStatic: // DirectDeclarator '[' TypeQualifierList "static" AssignmentExpression ']' | ||
|  | 		return n.DirectDeclarator.ParamScope() | ||
|  | 	case DirectDeclaratorStar: // DirectDeclarator '[' TypeQualifierList '*' ']' | ||
|  | 		return n.DirectDeclarator.ParamScope() | ||
|  | 	case DirectDeclaratorFuncParam: // DirectDeclarator '(' ParameterTypeList ')' | ||
|  | 		if s := n.DirectDeclarator.ParamScope(); s != nil { | ||
|  | 			return s | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return n.paramScope | ||
|  | 	case DirectDeclaratorFuncIdent: // DirectDeclarator '(' IdentifierList ')' | ||
|  | 		if s := n.DirectDeclarator.ParamScope(); s != nil { | ||
|  | 			return s | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return n.paramScope | ||
|  | 	default: | ||
|  | 		panic(internalError()) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *Enumerator) isVisible(at int32) bool { return n.Token.seq < at } | ||
|  | 
 | ||
|  | func (n *EnumSpecifier) Type() Type { return n.typ } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of the binary operation are promoted to. | ||
|  | func (n *EqualityExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of the binary operation are promoted to. | ||
|  | func (n *AdditiveExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of the binary operation are promoted to. | ||
|  | func (n *MultiplicativeExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of the binary operation are promoted to. | ||
|  | func (n *InclusiveOrExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of the binary operation are promoted to. | ||
|  | func (n *ExclusiveOrExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of the binary operation are promoted to. | ||
|  | func (n *AndExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | func (n *InitDeclarator) Value() *InitializerValue { return n.initializer } | ||
|  | 
 | ||
|  | // FirstDesignatorField returns the first field a designator denotes, if any. | ||
|  | func (n *Initializer) FirstDesignatorField() Field { return n.field0 } | ||
|  | 
 | ||
|  | // TrailingComma returns the comma token following n, if any. | ||
|  | func (n *Initializer) TrailingComma() *Token { return n.trailingComma } | ||
|  | 
 | ||
|  | // IsConst reports whether n is constant. | ||
|  | func (n *Initializer) IsConst() bool { return n == nil || n.isConst } | ||
|  | 
 | ||
|  | // IsZero reports whether n is a zero value. | ||
|  | func (n *Initializer) IsZero() bool { return n == nil || n.isZero } | ||
|  | 
 | ||
|  | // List returns n as a flattened list of all items that are case | ||
|  | // InitializerExpr. | ||
|  | func (n *Initializer) List() []*Initializer { return n.list } | ||
|  | 
 | ||
|  | // Parent returns the parent of n, if any. | ||
|  | func (n *Initializer) Parent() *Initializer { return n.parent } | ||
|  | 
 | ||
|  | // Type returns the type this initializer initializes. | ||
|  | func (n *Initializer) Type() Type { return n.typ } | ||
|  | 
 | ||
|  | // IsConst reports whether n is constant. | ||
|  | func (n *InitializerList) IsConst() bool { return n == nil || n.isConst } | ||
|  | 
 | ||
|  | // IsZero reports whether n is a zero value. | ||
|  | func (n *InitializerList) IsZero() bool { return n == nil || n.isZero } | ||
|  | 
 | ||
|  | // List returns n as a flattened list of all items that are case | ||
|  | // InitializerExpr. | ||
|  | func (n *InitializerList) List() []*Initializer { | ||
|  | 	if n == nil { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return n.list | ||
|  | } | ||
|  | 
 | ||
|  | // IsEmpty reprts whether n is an empty list. | ||
|  | func (n *InitializerList) IsEmpty() bool { return len(n.list) == 0 } | ||
|  | 
 | ||
|  | // LexicalScope returns the lexical scope of n. | ||
|  | func (n *JumpStatement) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // LexicalScope returns the lexical scope of n. | ||
|  | func (n *LabeledStatement) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | func (n *ParameterDeclaration) Type() Type { return n.typ } | ||
|  | 
 | ||
|  | func (n *Pointer) TypeQualifier() Type { return n.typeQualifiers } | ||
|  | 
 | ||
|  | // ResolvedIn reports which scope the identifier of cases | ||
|  | // PrimaryExpressionIdent, PrimaryExpressionEnum were resolved in, if any. | ||
|  | func (n *PrimaryExpression) ResolvedIn() Scope { return n.resolvedIn } | ||
|  | 
 | ||
|  | // ResolvedTo reports which Node the identifier of cases | ||
|  | // PrimaryExpressionIdent, PrimaryExpressionEnum resolved to, if any. | ||
|  | func (n *PrimaryExpression) ResolvedTo() Node { return n.resolvedTo } | ||
|  | 
 | ||
|  | // Promote returns the type the operands of the binary operation are promoted to. | ||
|  | func (n *RelationalExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | // Cases returns the cases a switch statement consist of, in source order. | ||
|  | func (n *SelectionStatement) Cases() []*LabeledStatement { return n.cases } | ||
|  | 
 | ||
|  | // Promote returns the type the shift count operand is promoted to. | ||
|  | func (n *ShiftExpression) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | func (n *StructOrUnionSpecifier) Type() Type { return n.typ } | ||
|  | 
 | ||
|  | // Promote returns the type the type the switch expression is promoted to. | ||
|  | func (n *SelectionStatement) Promote() Type { return n.promote } | ||
|  | 
 | ||
|  | // Type returns the type of n. | ||
|  | func (n *TypeName) Type() Type { return n.typ } | ||
|  | 
 | ||
|  | // // LexicalScope returns the lexical scope of n. | ||
|  | // func (n *AttributeValue) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // // Scope returns n's scope. | ||
|  | // func (n *CompoundStatement) Scope() Scope { return n.scope } | ||
|  | 
 | ||
|  | // // LexicalScope returns the lexical scope of n. | ||
|  | // func (n *Designator) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // // LexicalScope returns the lexical scope of n. | ||
|  | // func (n *DirectDeclarator) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // LexicalScope returns the lexical scope of n. | ||
|  | func (n *EnumSpecifier) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // // LexicalScope returns the lexical scope of n. | ||
|  | // func (n *IdentifierList) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // // LexicalScope returns the lexical scope of n. | ||
|  | // func (n *PrimaryExpression) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // // LexicalScope returns the lexical scope of n. | ||
|  | // func (n *StructOrUnionSpecifier) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | // // ResolvedIn reports which scope the identifier of case | ||
|  | // // TypeSpecifierTypedefName was resolved in, if any. | ||
|  | // func (n *TypeSpecifier) ResolvedIn() Scope { return n.resolvedIn } | ||
|  | 
 | ||
|  | // // LexicalScope returns the lexical scope of n. | ||
|  | // func (n *UnaryExpression) LexicalScope() Scope { return n.lexicalScope } | ||
|  | 
 | ||
|  | func (n *UnaryExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case UnaryExpressionPostfix: // PostfixExpression | ||
|  | 		return n.PostfixExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *PostfixExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case PostfixExpressionPrimary: // PrimaryExpression | ||
|  | 		return n.PrimaryExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *PrimaryExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case PrimaryExpressionIdent: // IDENTIFIER | ||
|  | 		if n.Operand != nil { | ||
|  | 			return n.Operand.Declarator() | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return nil | ||
|  | 	case PrimaryExpressionExpr: // '(' Expression ')' | ||
|  | 		return n.Expression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *Expression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case ExpressionAssign: // AssignmentExpression | ||
|  | 		return n.AssignmentExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *AssignmentExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case AssignmentExpressionCond: // ConditionalExpression | ||
|  | 		return n.ConditionalExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *ConditionalExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case ConditionalExpressionLOr: // LogicalOrExpression | ||
|  | 		return n.LogicalOrExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *LogicalOrExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case LogicalOrExpressionLAnd: // LogicalAndExpression | ||
|  | 		return n.LogicalAndExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *LogicalAndExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case LogicalAndExpressionOr: // InclusiveOrExpression | ||
|  | 		return n.InclusiveOrExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *InclusiveOrExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case InclusiveOrExpressionXor: // ExclusiveOrExpression | ||
|  | 		return n.ExclusiveOrExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *ExclusiveOrExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case ExclusiveOrExpressionAnd: // AndExpression | ||
|  | 		return n.AndExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *AndExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case AndExpressionEq: // EqualityExpression | ||
|  | 		return n.EqualityExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *EqualityExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case EqualityExpressionRel: // RelationalExpression | ||
|  | 		return n.RelationalExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *RelationalExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case RelationalExpressionShift: // ShiftExpression | ||
|  | 		return n.ShiftExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *ShiftExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case ShiftExpressionAdd: // AdditiveExpression | ||
|  | 		return n.AdditiveExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *AdditiveExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case AdditiveExpressionMul: // MultiplicativeExpression | ||
|  | 		return n.MultiplicativeExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *MultiplicativeExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case MultiplicativeExpressionCast: // CastExpression | ||
|  | 		return n.CastExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (n *CastExpression) Declarator() *Declarator { | ||
|  | 	switch n.Case { | ||
|  | 	case CastExpressionUnary: // UnaryExpression | ||
|  | 		return n.UnaryExpression.Declarator() | ||
|  | 	default: | ||
|  | 		return nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Has reports whether n has any of attributes in key. | ||
|  | func (n *AttributeSpecifier) Has(key ...StringID) (*ExpressionList, bool) { | ||
|  | 	if n == nil { | ||
|  | 		return nil, false | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for list := n.AttributeValueList; list != nil; list = list.AttributeValueList { | ||
|  | 		av := list.AttributeValue | ||
|  | 		for _, k := range key { | ||
|  | 			if av.Token.Value == k { | ||
|  | 				switch av.Case { | ||
|  | 				case AttributeValueIdent: // IDENTIFIER | ||
|  | 					return nil, true | ||
|  | 				case AttributeValueExpr: // IDENTIFIER '(' ExpressionList ')' | ||
|  | 					return av.ExpressionList, true | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return nil, false | ||
|  | } | ||
|  | 
 | ||
|  | // Has reports whether n has any of attributes in key. | ||
|  | func (n *AttributeSpecifierList) Has(key ...StringID) (*ExpressionList, bool) { | ||
|  | 	for ; n != nil; n = n.AttributeSpecifierList { | ||
|  | 		if exprList, ok := n.AttributeSpecifier.Has(key...); ok { | ||
|  | 			return exprList, ok | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return nil, false | ||
|  | } | ||
|  | 
 | ||
|  | // Parent returns the CompoundStatement that contains n, if any. | ||
|  | func (n *CompoundStatement) Parent() *CompoundStatement { return n.parent } | ||
|  | 
 | ||
|  | // IsJumpTarget returns whether n or any of its children contain a named | ||
|  | // labeled statement. | ||
|  | func (n *CompoundStatement) IsJumpTarget() bool { return n.isJumpTarget } | ||
|  | 
 | ||
|  | func (n *CompoundStatement) hasLabel() { | ||
|  | 	for ; n != nil; n = n.parent { | ||
|  | 		n.isJumpTarget = true | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Declarations returns the list of declarations in n. | ||
|  | func (n *CompoundStatement) Declarations() []*Declaration { return n.declarations } | ||
|  | 
 | ||
|  | // Children returns the list of n's children. | ||
|  | func (n *CompoundStatement) Children() []*CompoundStatement { return n.children } | ||
|  | 
 | ||
|  | // CompoundStatements returns the list of compound statements in n. | ||
|  | func (n *FunctionDefinition) CompoundStatements() []*CompoundStatement { return n.compoundStatements } | ||
|  | 
 | ||
|  | // CompoundStatement returns the block containing n. | ||
|  | func (n *LabeledStatement) CompoundStatement() *CompoundStatement { return n.block } | ||
|  | 
 | ||
|  | // LabeledStatements returns labeled statements of n. | ||
|  | func (n *CompoundStatement) LabeledStatements() []*LabeledStatement { return n.labeledStmts } | ||
|  | 
 | ||
|  | // HasInitializer reports whether d has an initializator. | ||
|  | func (n *Declarator) HasInitializer() bool { return n.hasInitializer } | ||
|  | 
 | ||
|  | // Context reports the statement, if any, a break or continue belongs to. Valid | ||
|  | // only after typecheck and for n.Case == JumpStatementBreak or | ||
|  | // JumpStatementContinue. | ||
|  | func (n *JumpStatement) Context() Node { return n.context } | ||
|  | 
 | ||
|  | // IsFunctionPrototype reports whether n is a function prototype. | ||
|  | func (n *Declarator) IsFunctionPrototype() bool { | ||
|  | 	return n != nil && n.Type() != nil && n.Type().Kind() == Function && !n.fnDef && !n.IsParameter | ||
|  | } | ||
|  | 
 | ||
|  | // DeclarationSpecifiers returns the declaration specifiers associated with n or nil. | ||
|  | func (n *Declarator) DeclarationSpecifiers() *DeclarationSpecifiers { | ||
|  | 	if x, ok := n.td.(*DeclarationSpecifiers); ok { | ||
|  | 		return x | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // SpecifierQualifierList returns the specifier qualifer list associated with n or nil. | ||
|  | func (n *Declarator) SpecifierQualifierList() *SpecifierQualifierList { | ||
|  | 	if x, ok := n.td.(*SpecifierQualifierList); ok { | ||
|  | 		return x | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // TypeQualifier returns the type qualifiers associated with n or nil. | ||
|  | func (n *Declarator) TypeQualifiers() *TypeQualifiers { | ||
|  | 	if x, ok := n.td.(*TypeQualifiers); ok { | ||
|  | 		return x | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // StructDeclaration returns the struct declaration associated with n. | ||
|  | func (n *StructDeclarator) StructDeclaration() *StructDeclaration { return n.decl } |