// 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 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(), true)) case CHARCONST, LONGCHARCONST: _, err = fmt.Fprintf(w, `%s'%s'`, tok.Sep, cQuotedString(tok.String(), false)) default: _, err = fmt.Fprintf(w, "%s%s", tok.Sep, tok) } return err } func cQuotedString(s string, isString bool) []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 '"': switch { case isString: b = append(b, '\\', '"') default: b = append(b, '"') } continue case '\'': switch { case isString: b = append(b, '\'') default: 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{"", ` 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() } // IsImplicit reports whether n was not declared nor defined, only inferred. func (n *Declarator) IsImplicit() bool { return n.implicit } 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 of an union type // 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 } func (n *TypeSpecifier) list() (r []*TypeSpecifier) { switch n.Case { case TypeSpecifierAtomic: return n.AtomicTypeSpecifier.list default: return []*TypeSpecifier{n} } } // // 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 }