FreeBASIC  0.91.0
parser-decl-proc-params.bas
Go to the documentation of this file.
1 '' proc parameters list declarations (called "arg" by mistake)
2 ''
3 '' chng: sep/2004 written [v1ctor]
4 
5 #include once "fb.bi"
6 #include once "fbint.bi"
7 #include once "parser.bi"
8 #include once "ast.bi"
9 
10 declare function hParamDecl _
11  ( _
12  byval proc as FBSYMBOL ptr, _
13  byval procmode as integer, _
14  byval isproto as integer _
15  ) as FBSYMBOL ptr
16 
17 declare function hParamDeclInstPtr _
18  ( _
19  byval parent as FBSYMBOL ptr, _
20  byval proc as FBSYMBOL ptr _
21  ) as FBSYMBOL ptr
22 
23 '':::::
24 ''Parameters= '(' ParamDecl (',' ParamDecl)* ')' .
25 ''
26 sub cParameters _
27  ( _
28  byval parent as FBSYMBOL ptr, _
29  byval proc as FBSYMBOL ptr, _
30  byval procmode as integer, _
31  byval isproto as integer _
32  )
33 
34  dim as longint length = any
35 
36  length = 0
37 
38  '' method? add the instance pointer (must be done here
39  '' to check for dups)
40  if( symbIsMethod( proc ) ) then
42  length += env.pointersize
43  end if
44 
45  '' '('?
46  if( lexGetToken( ) <> CHAR_LPRNT ) then
47  return
48  end if
49 
50  lexSkipToken( )
51 
52  '' ')'?
53  if( lexGetToken( ) = CHAR_RPRNT ) then
54  lexSkipToken( )
55  return
56  end if
57 
58  do
59  dim as FBSYMBOL ptr param = hParamDecl(proc, procmode, isproto)
60  if( param = NULL ) then
61  exit do
62  end if
63 
64  length += symbGetLen( param )
65 
66  '' vararg?
67  if( param->param.mode = FB_PARAMMODE_VARARG ) then
68  exit do
69  end if
70 
71  '' ','
72  if( lexGetToken( ) <> CHAR_COMMA ) then
73  exit do
74  end if
75 
76  lexSkipToken( )
77  loop
78 
79  '' ')'?
80  if( lexGetToken( ) <> CHAR_RPRNT ) then
81  errReport( FB_ERRMSG_EXPECTEDRPRNT )
82  '' error recovery: skip until ')'
83  hSkipUntil( CHAR_RPRNT, TRUE )
84  else
85  lexSkipToken( )
86  end if
87 
88  '' param list too large? (pretty arbitrary limit)
89  if( length > (env.pointersize * 64) ) then
90  errReportWarn( FB_WARNINGMSG_PARAMLISTSIZETOOBIG, symbGetName( proc ) )
91  end if
92 end sub
93 
94 '':::::
95 sub hParamError _
96  ( _
97  byval proc as FBSYMBOL ptr, _
98  byval pid as zstring ptr, _
99  byval msgnum as FB_ERRMSG = FB_ERRMSG_ILLEGALPARAMSPECAT, _
100  byval delta as integer = 1 _
101  )
102 
103  '' (This can be called before or after adding the parameter, so "delta"
104  '' should be used to ensure we report the proper parameter index)
105 
106  errReportParam( proc, symbGetProcParams( proc ) + delta, pid, msgnum )
107 
108 end sub
109 
110 '':::::
111 sub hParamWarning _
112  ( _
113  byval proc as FBSYMBOL ptr, _
114  byval pid as zstring ptr, _
115  byval msgnum as FB_ERRMSG, _
116  byval delta as integer = 1 _
117  )
118 
119  '' (ditto)
120 
121  errReportParamWarn( proc, symbGetProcParams( proc ) + delta, pid, msgnum )
122 
123 end sub
124 
125 function hOptionalExpr _
126  ( _
127  byval proc as FBSYMBOL ptr, _
128  byval pid as zstring ptr, _
129  byval param as FBSYMBOL ptr _
130  ) as ASTNODE ptr
131 
132  dim as ASTNODE ptr expr = any
133  dim as integer inioptions = any
134 
135  function = NULL
136 
137  '' Must be BYVAL/BYREF in order to allow an optional expression
138  '' (not BYDESC nor VARARG)
139  if( symbGetParamMode( param ) <> FB_PARAMMODE_BYVAL ) then
140  if( symbGetParamMode( param ) <> FB_PARAMMODE_BYREF ) then
141  exit function
142  end if
143  end if
144 
145  '' Complain if it's a forward reference or ANY (possible with BYREF),
146  '' since the type is unknown, we cannot parse an initializer for it.
147  select case( symbGetType( param ) )
148  case FB_DATATYPE_VOID, FB_DATATYPE_FWDREF
149  errReport( FB_ERRMSG_INCOMPLETETYPE )
150  hSkipUntil( CHAR_RPRNT, FALSE, , TRUE )
151  return astNewCONSTi( 0 )
152  end select
153 
154  expr = cInitializer( param, FB_INIOPT_ISINI )
155  if( expr = NULL ) then
156  exit function
157  end if
158 
159  '' No temp dtors should be left registered after the TYPEINI build-up
160  assert( astDtorListIsEmpty( ) )
161 
162  '' Remove bitfields from the AST's bitfield counter - the parameter
163  '' initializer will never be astAdd()ed itself, only cloned.
164  astForgetBitfields( expr )
165 
166  '' Disallow references to local vars, except for temp vars/descriptors
167  if( astTypeIniUsesLocals( expr, FB_SYMBATTRIB_TEMP or FB_SYMBATTRIB_DESCRIPTOR ) ) then
168  hParamError( proc, pid, FB_ERRMSG_INVALIDREFERENCETOLOCAL, 0 )
169  '' no error recovery, caller will take care
170  astDelTree( expr )
171  expr = NULL
172  end if
173 
174  function = expr
175 end function
176 
177 function hMockParam _
178  ( _
179  byval proc as FBSYMBOL ptr, _
180  byval pmode as integer = INVALID _
181  ) as FBSYMBOL ptr
182 
183  dim as integer dtype = any
184 
185  if( pmode = INVALID ) then
186  pmode = env.opt.parammode
187  end if
188 
189  if( pmode = FB_PARAMMODE_VARARG ) then
190  dtype = FB_DATATYPE_INVALID
191  else
192  dtype = FB_DATATYPE_INTEGER
193  end if
194 
195  function = symbAddProcParam( proc, NULL, dtype, NULL, pmode, 0 )
196 end function
197 
198 '':::::
199 '' ParamDecl = (BYVAL|BYREF)? ID (('(' ')')? (AS SymbolType)?)? ('=" (NUM_LIT|STR_LIT))? .
200 ''
201 function hParamDecl _
202  ( _
203  byval proc as FBSYMBOL ptr, _
204  byval proc_mode as integer, _
205  byval isproto as integer _
206  ) as FBSYMBOL ptr
207 
208  static as zstring * FB_MAXNAMELEN+1 idTB(0 to FB_MAXARGRECLEVEL-1)
209  static as integer reclevel = 0
210  dim as zstring ptr id = any
211  dim as ASTNODE ptr optexpr = any
212  dim as integer dtype = any, mode = any
213  dim as integer attrib = any
214  dim as integer readid = any, dotpos = any, doskip = any, dontinit = any, use_default = any
215  dim as FBSYMBOL ptr subtype = any, param = any
216 
217  function = NULL
218 
219  attrib = 0
220 
221  '' '...'?
222  if( lexGetToken( ) = CHAR_DOT ) then
223  if( lexGetLookAhead( 1 ) = CHAR_DOT ) then
224  lexSkipToken( )
225  lexSkipToken( )
226 
227  if( lexGetToken( ) <> CHAR_DOT ) then
228  hParamError( proc, "..." )
229  '' error recovery: skip until next ')', this is the last param
230  hSkipUntil( CHAR_RPRNT )
231  hMockParam( proc, FB_PARAMMODE_VARARG )
232  else
233  lexSkipToken( )
234  end if
235 
236  '' is it the first arg?
237  if( symbGetProcParams( proc ) = 0 ) then
238  hParamError( proc, "...", FB_ERRMSG_VARARGNOTALLOWEDASFIRSTPARAM )
239  return hMockParam( proc, FB_PARAMMODE_VARARG )
240  end if
241 
242  '' not cdecl?
243  if( proc_mode <> FB_FUNCMODE_CDECL ) then
244  hParamError( proc, "...", FB_ERRMSG_VARARGONLYALLOWEDINCDECL )
245  return hMockParam( proc, FB_PARAMMODE_VARARG )
246  end if
247 
248  return symbAddProcParam( proc, NULL, FB_DATATYPE_INVALID, NULL, _
249  FB_PARAMMODE_VARARG, 0 )
250 
251  '' syntax error..
252  else
253  hParamError( proc, "..." )
254  '' error recovery: skip until next ')', this is the last param
255  hSkipUntil( CHAR_RPRNT )
256  return hMockParam( proc, FB_PARAMMODE_VARARG )
257  end if
258  end if
259 
260  '' (BYVAL|BYREF)?
261  select case lexGetToken( )
262  case FB_TK_BYVAL
263  mode = FB_PARAMMODE_BYVAL
264  lexSkipToken( )
265  case FB_TK_BYREF
266  mode = FB_PARAMMODE_BYREF
267  lexSkipToken( )
268  case else
269  mode = INVALID
270  end select
271 
272  '' Check whether a param ID was given or not
273  '' In prototypes they can be omitted, and in fact we even allow
274  '' keywords there and use them as parameter ID, probably to make
275  '' translating C headers easier, except for AS of course because that
276  '' indicates the parameter type.
277  select case( lexGetClass( ) )
278  case FB_TKCLASS_IDENTIFIER
279  '' ID (most common case)
280  readid = TRUE
281 
282  case FB_TKCLASS_KEYWORD, FB_TKCLASS_QUIRKWD
283  '' Keyword
284 
285  '' Only allow keywords in prototypes, but not in bodies
286  if( isproto = FALSE ) then
288  '' error recovery: skip until next ',' or ')' and return a mock param
289  hSkipUntil( CHAR_COMMA )
290  return hMockParam( proc, mode )
291  end if
292 
293  '' AS? (the only keyword that cannot be ignored/treated as ID here)
294  readid = (lexGetToken( ) <> FB_TK_AS)
295 
296  case else
297  '' It's no ID and no keyword; the only other thing that's
298  '' allowed here is an '(' as in '()' array parameter without id,
299  '' in prototypes only of course (only there can param IDs be omitted).
300  if( (lexGetToken( ) <> CHAR_LPRNT) or (isproto = FALSE) ) then
302  '' error recovery: skip until next ',' or ')' and return a mock param
303  hSkipUntil( CHAR_COMMA )
304  return hMockParam( proc, mode )
305  end if
306 
307  readid = FALSE
308 
309  end select
310 
311  ''
312  if( reclevel >= FB_MAXARGRECLEVEL ) then
313  errReport( FB_ERRMSG_RECLEVELTOODEEP )
314  '' error recovery: skip until next ',' or ')' and return a mock param
315  hSkipUntil( CHAR_COMMA )
316  return hMockParam( proc, mode )
317  end if
318 
319  id = @idTB(reclevel)
320  *id = ""
321 
322  '' ID (or keyword used as ID)
323  if( readid ) then
324  *id = *lexGetText( )
325  dotpos = lexGetPeriodPos( )
326 
327  dtype = lexGetType( )
328  hCheckSuffix( dtype )
329 
330  lexSkipToken( )
331  else
332  '' no id
333  dtype = FB_DATATYPE_INVALID
334  end if
335 
336  '' '()' array parentheses, '('?
337  if( lexGetToken( ) = CHAR_LPRNT ) then
338  lexSkipToken( )
339  '' Must be followed by ')', and BYVAL/BYREF cannot be used
340  '' (array() parameters always implicitly are BYDESC)
341  if( (mode <> INVALID) or (hMatch( CHAR_RPRNT ) = FALSE) ) then
342  hParamError( proc, id )
343  end if
344  mode = FB_PARAMMODE_BYDESC
345  end if
346 
347  use_default = FALSE
348  if( mode = INVALID ) then
349  mode = env.opt.parammode
350  use_default = TRUE
351  if( fbPdCheckIsSet( FB_PDCHECK_PARAMMODE ) ) then
352  hParamWarning( proc, id, FB_WARNINGMSG_NOEXPLICITPARAMMODE )
353  end if
354  end if
355 
356  '' (AS SymbolType)?
357  doskip = FALSE
358  if( lexGetToken( ) = FB_TK_AS ) then
359  lexSkipToken( )
360  if( dtype <> FB_DATATYPE_INVALID ) then
361  hParamError( proc, id )
362  exit function
363  end if
364 
365  reclevel += 1
366 
367  '' if it's a proto, allow forward types in byref params
368  dim as integer options = FB_SYMBTYPEOPT_DEFAULT
369 
370  if( mode = FB_PARAMMODE_BYREF ) then
371  if( isproto ) then
372  options or= FB_SYMBTYPEOPT_ALLOWFORWARD
373  end if
374  options and= not FB_SYMBTYPEOPT_CHECKSTRPTR
375  end if
376 
377  if( cSymbolType( dtype, subtype, 0, options ) = FALSE ) then
378  hParamError( proc, id )
379  '' error recovery: fake type
380  dtype = FB_DATATYPE_INTEGER
381  subtype = NULL
382  doskip = TRUE
383  end if
384 
385  if( mode = FB_PARAMMODE_BYVAL ) then
386  '' Disallow BYVAL passing of objects of abstract classes
387  hComplainIfAbstractClass( dtype, subtype )
388  end if
389 
390  reclevel -= 1
391  else
392  if( fbLangOptIsSet( FB_LANG_OPT_DEFTYPE ) = FALSE ) then
393  errReportNotAllowed( FB_LANG_OPT_DEFTYPE, FB_ERRMSG_DEFTYPEONLYVALIDINLANG )
394  doskip = TRUE
395  else
396  if( readid = FALSE ) then
397  hParamError( proc, NULL )
398  doskip = TRUE
399  end if
400  end if
401 
402  subtype = NULL
403  attrib or= FB_SYMBATTRIB_SUFFIXED
404  end if
405 
406  '' in lang FB,
407  if( fbLangIsSet( FB_LANG_FB ) ) then
408  '' we have to delay the true default until now, since
409  '' byval/byref depends on the symbol type
410  if( use_default ) then
411  mode = symbGetDefaultCallConv( typeGet( dtype ), subtype )
412  end if
413  end if
414 
415  '' QB def-by-letter hax
416  if( dtype = FB_DATATYPE_INVALID ) then
417  dtype = symbGetDefType( id )
418  end if
419 
420  if( doskip ) then
421  hSkipUntil( CHAR_COMMA )
422  end if
423 
424  '' check for invalid args
425  select case as const typeGet( dtype )
426  '' can't be a fixed-len string
427  case FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
428  if( mode = FB_PARAMMODE_BYVAL or typeGet( dtype ) = FB_DATATYPE_FIXSTR ) then
429  hParamError( proc, id )
430  '' error recovery: fake correct type
431  dtype = typeAddrOf( dtype )
432  end if
433 
434  '' can't be as ANY on non-prototypes
435  case FB_DATATYPE_VOID
436  if( isproto = FALSE ) then
437  hParamError( proc, id )
438  '' error recovery: fake correct type
439  dtype = typeAddrOf( dtype )
440  else
441  if( mode = FB_PARAMMODE_BYVAL ) then
442  hParamError( proc, id )
443  '' error recovery: fake correct param
444  dtype = typeAddrOf( dtype )
445  end if
446  end if
447 
448  case FB_DATATYPE_STRUCT
449  if( isproto = FALSE ) then
450  '' contains a period?
451  if( dotpos > 0 ) then
452  hParamError( proc, id )
453  end if
454  end if
455 
456  case FB_DATATYPE_STRING
457  if( mode = FB_PARAMMODE_BYVAL ) then
458  if( fbPdCheckIsSet( FB_PDCHECK_PARAMMODE ) ) then
459  hParamWarning( proc, id, FB_WARNINGMSG_BYVALASSTRING )
460  end if
461  end if
462 
463  end select
464 
465  '' default values
466  optexpr = NULL
467  dontinit = FALSE
468 
469  '' Add new param
470  param = symbAddProcParam( proc, iif( isproto, cptr( zstring ptr, NULL ), id ), _
471  dtype, subtype, mode, attrib )
472  if( param = NULL ) then
473  exit function
474  end if
475 
476  if( isproto = FALSE ) then
477  if( symbGetLen( param ) > (env.pointersize * 4) ) then
478  if( fbPdCheckIsSet( FB_PDCHECK_PARAMSIZE ) ) then
479  hParamWarning( proc, id, FB_WARNINGMSG_PARAMSIZETOOBIG, 0 )
480  end if
481  end if
482  end if
483 
484  '' ('=' (expr | ANY))?
485  if( cAssignToken( ) ) then
486  if( mode = FB_PARAMMODE_BYDESC ) then
487  '' ANY?
488  if( lexGetToken( ) = FB_TK_ANY ) then
489  lexSkipToken( )
490  dontinit = TRUE
491  else
492  hParamError( proc, id, , 0 )
493  '' error recovery: skip until next ',' or ')'
494  hSkipUntil( CHAR_COMMA )
495  end if
496  else
497  optexpr = hOptionalExpr( proc, id, param )
498  if( optexpr = NULL ) then
499  hParamError( proc, id, , 0 )
500  '' error recovery: skip until next ',' or ')'
501  hSkipUntil( CHAR_COMMA )
502  end if
503  end if
504  end if
505 
506  if( dontinit ) then
507  symbSetDontInit( param )
508  end if
509 
510  if( optexpr ) then
511  symbMakeParamOptional( proc, param, optexpr )
512  end if
513 
514  function = param
515 end function
516