@@ -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,56 @@ 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+ t .Next ()
176+ t .Next ()
177+
178+ // Parse the list of parameters until ')' (rparen) is found.
179+ params := []ast.Expr {}
180+ for i := 0 ; ; i ++ {
181+ if i == 0 && t .curToken == token .RPAREN {
182+ break
183+ }
184+ x , err := parseConstExpr (t , precedenceLowest )
185+ if err != nil {
186+ return nil , err
187+ }
188+ params = append (params , x )
189+ t .Next ()
190+ if t .curToken == token .COMMA {
191+ t .Next ()
192+ } else if t .curToken == token .RPAREN {
193+ break
194+ } else {
195+ return nil , & scanner.Error {
196+ Pos : t .fset .Position (t .curPos ),
197+ Msg : "unexpected token " + t .curToken .String () + ", ',' or ')'" ,
198+ }
199+ }
200+ }
201+
202+ // Evaluate the macro value and use it as the identifier value.
203+ rparen := t .curPos
204+ pos , text := t .f .getMacro (cursor )
205+ return parseConst (pos , t .fset , text , params , rparen , t .f )
206+ }
207+ }
208+
209+ // Normally the name is something defined in the file (like another
210+ // macro) which we get the declaration from using getASTDeclName.
211+ // This ensures that names that are only referenced inside a macro are
212+ // still getting defined.
104213 if cursor , ok := t .f .names [t .curValue ]; ok {
105214 return & ast.Ident {
106215 NamePos : t .curPos ,
@@ -184,6 +293,7 @@ type tokenizer struct {
184293 curToken , peekToken token.Token
185294 curValue , peekValue string
186295 buf string
296+ params map [string ]ast.Expr
187297}
188298
189299// newTokenizer initializes a new tokenizer, positioned at the first token in
@@ -195,6 +305,7 @@ func newTokenizer(start token.Pos, fset *token.FileSet, buf string, f *cgoFile)
195305 fset : fset ,
196306 buf : buf ,
197307 peekToken : token .ILLEGAL ,
308+ params : make (map [string ]ast.Expr ),
198309 }
199310 // Parse the first two tokens (cur and peek).
200311 t .Next ()
@@ -246,14 +357,16 @@ func (t *tokenizer) Next() {
246357 t .peekValue = t .buf [:2 ]
247358 t .buf = t .buf [2 :]
248359 return
249- case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^' :
360+ case c == '(' || c == ')' || c == ',' || c == ' +' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^' :
250361 // Single-character tokens.
251362 // TODO: ++ (increment) and -- (decrement) operators.
252363 switch c {
253364 case '(' :
254365 t .peekToken = token .LPAREN
255366 case ')' :
256367 t .peekToken = token .RPAREN
368+ case ',' :
369+ t .peekToken = token .COMMA
257370 case '+' :
258371 t .peekToken = token .ADD
259372 case '-' :
0 commit comments