@@ -54,8 +54,72 @@ func init() {
54
54
}
55
55
56
56
// 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 ) {
58
58
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
+
59
123
expr , err := parseConstExpr (t , precedenceLowest )
60
124
t .Next ()
61
125
if t .curToken != token .EOF {
@@ -96,11 +160,59 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
96
160
}
97
161
98
162
func 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
+
103
169
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.
104
216
if cursor , ok := t .f .names [t .curValue ]; ok {
105
217
return & ast.Ident {
106
218
NamePos : t .curPos ,
@@ -184,6 +296,7 @@ type tokenizer struct {
184
296
curToken , peekToken token.Token
185
297
curValue , peekValue string
186
298
buf string
299
+ params map [string ]ast.Expr
187
300
}
188
301
189
302
// 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)
195
308
fset : fset ,
196
309
buf : buf ,
197
310
peekToken : token .ILLEGAL ,
311
+ params : make (map [string ]ast.Expr ),
198
312
}
199
313
// Parse the first two tokens (cur and peek).
200
314
t .Next ()
@@ -246,14 +360,16 @@ func (t *tokenizer) Next() {
246
360
t .peekValue = t .buf [:2 ]
247
361
t .buf = t .buf [2 :]
248
362
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 == '^' :
250
364
// Single-character tokens.
251
365
// TODO: ++ (increment) and -- (decrement) operators.
252
366
switch c {
253
367
case '(' :
254
368
t .peekToken = token .LPAREN
255
369
case ')' :
256
370
t .peekToken = token .RPAREN
371
+ case ',' :
372
+ t .peekToken = token .COMMA
257
373
case '+' :
258
374
t .peekToken = token .ADD
259
375
case '-' :
0 commit comments