@@ -54,8 +54,72 @@ func init() {
5454}
5555
5656// parseConst parses the given string as a C constant.
57- func parseConst (pos token.Pos , fset * token.FileSet , value string , f * cgoFile ) (ast.Expr , * scanner.Error ) {
57+ func parseConst (pos token.Pos , fset * token.FileSet , value string , params []ast. Expr , callerPos token. Pos , f * cgoFile ) (ast.Expr , * scanner.Error ) {
5858 t := newTokenizer (pos , fset , value , f )
59+
60+ // If params is non-nil (could be a zero length slice), this const is
61+ // actually a function-call like expression from another macro.
62+ // This means we have to parse a string like "(a, b) (a+b)".
63+ // We do this by parsing the parameters at the start and then treating the
64+ // following like a normal constant expression.
65+ if params != nil {
66+ // Parse opening paren.
67+ if t .curToken != token .LPAREN {
68+ return nil , unexpectedToken (t , token .LPAREN )
69+ }
70+ t .Next ()
71+
72+ // Parse parameters (identifiers) and closing paren.
73+ var paramIdents []string
74+ for i := 0 ; ; i ++ {
75+ if i == 0 && t .curToken == token .RPAREN {
76+ // No parameters, break early.
77+ t .Next ()
78+ break
79+ }
80+
81+ // Read the parameter name.
82+ if t .curToken != token .IDENT {
83+ return nil , unexpectedToken (t , token .IDENT )
84+ }
85+ paramIdents = append (paramIdents , t .curValue )
86+ t .Next ()
87+
88+ // Read the next token: either a continuation (comma) or end of list
89+ // (rparen).
90+ if t .curToken == token .RPAREN {
91+ // End of parameter list.
92+ t .Next ()
93+ break
94+ } else if t .curToken == token .COMMA {
95+ // Comma, so there will be another parameter name.
96+ t .Next ()
97+ } else {
98+ return nil , & scanner.Error {
99+ Pos : t .fset .Position (t .curPos ),
100+ Msg : "unexpected token " + t .curToken .String () + " inside macro parameters, expected ',' or ')'" ,
101+ }
102+ }
103+ }
104+
105+ // Report an error if there is a mismatch in parameter length.
106+ // The error is reported at the location of the closing paren from the
107+ // caller location.
108+ if len (params ) != len (paramIdents ) {
109+ return nil , & scanner.Error {
110+ Pos : t .fset .Position (callerPos ),
111+ Msg : fmt .Sprintf ("unexpected number of parameters: expected %d, got %d" , len (paramIdents ), len (params )),
112+ }
113+ }
114+
115+ // Assign values to the parameters.
116+ // These parameter names are closer in 'scope' than other identifiers so
117+ // will be used first when parsing an identifier.
118+ for i , name := range paramIdents {
119+ t .params [name ] = params [i ]
120+ }
121+ }
122+
59123 expr , err := parseConstExpr (t , precedenceLowest )
60124 t .Next ()
61125 if t .curToken != token .EOF {
@@ -96,11 +160,59 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
96160}
97161
98162func parseIdent (t * tokenizer ) (ast.Expr , * scanner.Error ) {
99- // Normally the name is something defined in the file (like another macro)
100- // which we get the declaration from using getASTDeclName.
101- // This ensures that names that are only referenced inside a macro are still
102- // getting defined.
163+ // If the identifier is one of the parameters of this function-like macro,
164+ // use the parameter value.
165+ if val , ok := t .params [t .curValue ]; ok {
166+ return val , nil
167+ }
168+
103169 if t .f != nil {
170+ // Check whether this identifier is actually a macro "call" with
171+ // parameters. In that case, we should parse the parameters and pass it
172+ // on to a new invocation of parseConst.
173+ if t .peekToken == token .LPAREN {
174+ if cursor , ok := t .f .names [t .curValue ]; ok && t .f .isFunctionLikeMacro (cursor ) {
175+ // We know the current and peek tokens (the peek one is the '('
176+ // token). So skip ahead until the current token is the first
177+ // unknown token.
178+ t .Next ()
179+ t .Next ()
180+
181+ // Parse the list of parameters until ')' (rparen) is found.
182+ params := []ast.Expr {}
183+ for i := 0 ; ; i ++ {
184+ if i == 0 && t .curToken == token .RPAREN {
185+ break
186+ }
187+ x , err := parseConstExpr (t , precedenceLowest )
188+ if err != nil {
189+ return nil , err
190+ }
191+ params = append (params , x )
192+ t .Next ()
193+ if t .curToken == token .COMMA {
194+ t .Next ()
195+ } else if t .curToken == token .RPAREN {
196+ break
197+ } else {
198+ return nil , & scanner.Error {
199+ Pos : t .fset .Position (t .curPos ),
200+ Msg : "unexpected token " + t .curToken .String () + ", ',' or ')'" ,
201+ }
202+ }
203+ }
204+
205+ // Evaluate the macro value and use it as the identifier value.
206+ rparen := t .curPos
207+ pos , text := t .f .getMacro (cursor )
208+ return parseConst (pos , t .fset , text , params , rparen , t .f )
209+ }
210+ }
211+
212+ // Normally the name is something defined in the file (like another
213+ // macro) which we get the declaration from using getASTDeclName.
214+ // This ensures that names that are only referenced inside a macro are
215+ // still getting defined.
104216 if cursor , ok := t .f .names [t .curValue ]; ok {
105217 return & ast.Ident {
106218 NamePos : t .curPos ,
@@ -184,6 +296,7 @@ type tokenizer struct {
184296 curToken , peekToken token.Token
185297 curValue , peekValue string
186298 buf string
299+ params map [string ]ast.Expr
187300}
188301
189302// newTokenizer initializes a new tokenizer, positioned at the first token in
@@ -195,6 +308,7 @@ func newTokenizer(start token.Pos, fset *token.FileSet, buf string, f *cgoFile)
195308 fset : fset ,
196309 buf : buf ,
197310 peekToken : token .ILLEGAL ,
311+ params : make (map [string ]ast.Expr ),
198312 }
199313 // Parse the first two tokens (cur and peek).
200314 t .Next ()
@@ -246,14 +360,16 @@ func (t *tokenizer) Next() {
246360 t .peekValue = t .buf [:2 ]
247361 t .buf = t .buf [2 :]
248362 return
249- case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^' :
363+ case c == '(' || c == ')' || c == ',' || c == ' +' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^' :
250364 // Single-character tokens.
251365 // TODO: ++ (increment) and -- (decrement) operators.
252366 switch c {
253367 case '(' :
254368 t .peekToken = token .LPAREN
255369 case ')' :
256370 t .peekToken = token .RPAREN
371+ case ',' :
372+ t .peekToken = token .COMMA
257373 case '+' :
258374 t .peekToken = token .ADD
259375 case '-' :
0 commit comments