FreeBASIC  0.91.0
parser-proccall-args.bas
Go to the documentation of this file.
1 '' function call's arguments list (called "param" by mistake)
2 ''
3 '' chng: sep/2004 written [v1ctor]
4 
5 
6 #include once "fb.bi"
7 #include once "fbint.bi"
8 #include once "list.bi"
9 #include once "parser.bi"
10 #include once "ast.bi"
11 
12 '':::::
14 
15  listInit( @parser.ovlarglist, 32*4, len( FB_CALL_ARG ), LIST_FLAGS_NOCLEAR )
16 
17 end sub
18 
20 
21  listEnd( @parser.ovlarglist )
22 
23 end sub
24 
25 '':::::
26 ''ProcArg = BYVAL? (ID(('(' ')')? | Expression) .
27 ''
28 function hProcArg _
29  ( _
30  byval proc as FBSYMBOL ptr, _
31  byval param as FBSYMBOL ptr, _
32  byval argnum as integer, _
33  byref expr as ASTNODE ptr, _
34  byref amode as integer, _
35  byref have_eq_outside_parens as integer, _
36  byval options as FB_PARSEROPT _
37  ) as integer
38 
39  dim as integer pmode = any, old_dtype = any
40  dim as FBSYMBOL ptr oldsym = any
41 
42  function = FALSE
43 
44  pmode = symbGetParamMode( param )
45  amode = INVALID
46  expr = NULL
47 
48  '' BYVAL?
49  if( lexGetToken( ) = FB_TK_BYVAL ) then
50  lexSkipToken( )
51  amode = FB_PARAMMODE_BYVAL
52  end if
53 
54  oldsym = parser.ctxsym
55  old_dtype = parser.ctx_dtype
56  parser.ctxsym = symbGetSubType( param )
57  parser.ctx_dtype = symbGetType( param )
58  parser.have_eq_outside_parens = FALSE
59 
60  '' Expression
61  expr = cExpression( )
62 
63  have_eq_outside_parens or= parser.have_eq_outside_parens
64 
65  '' disable optional opening '{' after first parameter
66  fbSetPrntOptional( FALSE )
67 
68  if( expr = NULL ) then
69  if( (options and FB_PARSEROPT_ISFUNC) <> 0 ) then
70  expr = NULL
71  else
72  '' check for BYVAL if it's the first param, due the optional ()'s
73  if( (argnum = 0) and (amode = INVALID) ) then
74  '' BYVAL?
75  if( hMatch( FB_TK_BYVAL ) ) then
76  amode = FB_PARAMMODE_BYVAL
77  expr = cExpression( )
78  end if
79  end if
80  end if
81  end if
82 
83  parser.ctxsym = oldsym
84  parser.ctx_dtype = old_dtype
85 
86  if( expr = NULL ) then
87  '' check if argument is optional
88  if( symbGetIsOptional( param ) = FALSE ) then
89  if( pmode = FB_PARAMMODE_VARARG ) then
90  exit function
91  end if
92  errReport( FB_ERRMSG_ARGCNTMISMATCH )
93  '' error recovery: fake an expr
94  expr = astNewCONSTz( symbGetType( param ), symbGetSubType( param ) )
95  end if
96  else
97  '' '('')'?
98  if( pmode = FB_PARAMMODE_BYDESC ) then
99  if( lexGetToken( ) = CHAR_LPRNT ) then
100  if( lexGetLookAhead( 1 ) = CHAR_RPRNT ) then
101  if( amode <> INVALID ) then
102  errReport( FB_ERRMSG_PARAMTYPEMISMATCH )
103  end if
104  lexSkipToken( )
105  lexSkipToken( )
106  amode = FB_PARAMMODE_BYDESC
107  end if
108  end if
109  end if
110  end if
111 
112  ''
113  if( amode <> INVALID ) then
114  if( amode <> pmode ) then
115  if( pmode <> FB_PARAMMODE_VARARG ) then
116  '' allow BYVAL params passed to BYREF/BYDESC args
117  '' (to pass NULL to pointers and so on)
118  if( amode <> FB_PARAMMODE_BYVAL ) then
119  if( amode <> pmode ) then
120  errReport( FB_ERRMSG_PARAMTYPEMISMATCH )
121  '' error recovery: discard arg mode
122  amode = pmode
123  end if
124  end if
125  end if
126  end if
127  end if
128 
129  function = TRUE
130 
131 end function
132 
133 '':::::
134 ''ProcParam = BYVAL? (ID(('(' ')')? | Expression) .
135 ''
136 sub hOvlProcArg _
137  ( _
138  byval argnum as integer, _
139  byval arg as FB_CALL_ARG ptr, _
140  byref have_eq_outside_parens as integer, _
141  byval options as FB_PARSEROPT _
142  )
143 
144  dim as FBSYMBOL ptr oldsym = any
145  dim as integer old_dtype = any
146 
147  arg->expr = NULL
148  arg->mode = INVALID
149 
150  '' BYVAL?
151  if( hMatch( FB_TK_BYVAL ) ) then
152  arg->mode = FB_PARAMMODE_BYVAL
153  end if
154 
155  oldsym = parser.ctxsym
156  old_dtype = parser.ctx_dtype
157  parser.ctxsym = NULL
158  parser.ctx_dtype = FB_DATATYPE_INVALID
159  parser.have_eq_outside_parens = FALSE
160 
161  '' Expression
162  arg->expr = cExpression( )
163  if( arg->expr = NULL ) then
164  '' function? assume as optional..
165  if( (options and FB_PARSEROPT_ISFUNC) <> 0 ) then
166  arg->expr = NULL
167  else
168  '' check for BYVAL if it's the first param, due the optional ()'s
169  if( (argnum = 0) and (arg->mode = INVALID) ) then
170  '' BYVAL?
171  if( hMatch( FB_TK_BYVAL ) ) then
172  arg->mode = FB_PARAMMODE_BYVAL
173  arg->expr = cExpression( )
174  end if
175  end if
176  end if
177  end if
178 
179  parser.ctxsym = oldsym
180  parser.ctx_dtype = old_dtype
181  have_eq_outside_parens or= parser.have_eq_outside_parens
182 
183  '' not optional?
184  if( arg->expr <> NULL ) then
185  '' '('')'?
186  if( lexGetToken( ) = CHAR_LPRNT ) then
187  if( lexGetLookAhead( 1 ) = CHAR_RPRNT ) then
188  if( arg->mode <> INVALID ) then
189  errReport( FB_ERRMSG_PARAMTYPEMISMATCH )
190  end if
191  lexSkipToken( )
192  lexSkipToken( )
193  arg->mode = FB_PARAMMODE_BYDESC
194  end if
195  end if
196  end if
197 end sub
198 
200  ( _
201  byval args as integer, _
202  byval have_eq_outside_parens as integer, _
203  byval proc as FBSYMBOL ptr _
204  )
205 
206  dim as integer warn = any
207 
208  '' If there was just one argument, with '=' outside parentheses, in a
209  '' call to a BYREF function like <f (1) = 2>, show a warning -- it could
210  '' easily be misinterpreted as assignment to the BYREF function result.
211  ''
212  '' Also, the warning about this syntax problem should be shown in calls
213  '' to overloaded functions, if any of the overloads is a BYREF function,
214  '' because the exact overload used depends more on semantics than on
215  '' syntax.
216  '' f( a ) = b i.e. f( (a) = b )
217  '' might be a call to BYVAL or BYREF function depending on 'a'
218  '' could resolve to a different overload than
219  '' (f( a )) = b
220 
221  warn = symbProcReturnsByref( proc )
222 
223  if( warn = FALSE ) then
224  '' Also check other overloads, if any (for this to work,
225  '' the passed proc must be the overload head proc)
226  if( symbGetProcIsOverloaded( proc ) ) then
227  do
228  proc = symbGetProcOvlNext( proc )
229  if( proc = NULL ) then
230  exit do
231  end if
232 
233  warn = symbProcReturnsByref( proc )
234  loop until( warn )
235  end if
236  end if
237 
238  warn and= (args = 1)
239  warn and= have_eq_outside_parens
240 
241  if( warn ) then
242  errReportWarn( FB_WARNINGMSG_BYREFEQAFTERPARENS )
243  end if
244 
245 end sub
246 
247 function hGetVtableLookupIfNeeded _
248  ( _
249  byval proc as FBSYMBOL ptr, _
250  byval thisexpr as ASTNODE ptr, _
251  byval options as FB_PARSEROPT _
252  ) as ASTNODE ptr
253 
254  function = NULL
255 
256  '' Explicit BASE.* access?
257  if( options and FB_PARSEROPT_EXPLICITBASE ) then
258  '' Disallow calling ABSTRACTs directly this way
259  if( symbIsAbstract( proc ) ) then
260  errReport( FB_ERRMSG_CALLTOABSTRACT )
261  end if
262  else
263  '' Do vtable lookup (function pointer access to be used
264  '' by the CALL) if it's a VIRTUAL.
265  function = astBuildVtableLookup( proc, thisexpr )
266  end if
267 
268 end function
269 
270 '':::::
271 ''ProcArgList = ProcArg (DECL_SEPARATOR ProcArg)* .
272 ''
273 function hOvlProcArgList _
274  ( _
275  byval base_parent as FBSYMBOL ptr, _
276  byval proc as FBSYMBOL ptr, _
277  byval arg_list as FB_CALL_ARG_LIST ptr, _
278  byval options as FB_PARSEROPT _
279  ) as ASTNODE ptr
280 
281  dim as integer i = any, params = any, args = any, have_eq_outside_parens = any
282  dim as ASTNODE ptr procexpr = any
283  dim as FBSYMBOL ptr param = any, ovlproc = any
284  dim as FB_CALL_ARG ptr arg = any, nxt = any
285  dim as FB_ERRMSG err_num = any
286 
287  function = NULL
288  have_eq_outside_parens = FALSE
289 
290  params = symGetProcOvlMaxParams( proc )
291 
292  args = arg_list->args
293  if( (options and FB_PARSEROPT_HASINSTPTR) <> 0 ) then
294  args -= 1
295  end if
296 
297  if( (options and FB_PARSEROPT_OPTONLY) = 0 ) then
298  dim as integer init_args = args
299 
300  do
301  '' count mismatch?
302  if( args > params ) then
303  errReport( FB_ERRMSG_ARGCNTMISMATCH )
304  '' error recovery: skip until next stmt or ')'
305  if( (options and FB_PARSEROPT_ISFUNC) <> 0 ) then
306  hSkipUntil( CHAR_RPRNT )
307  else
308  hSkipStmt( )
309  end if
310 
311  args -= 1
312  exit do
313  end if
314 
315  '' alloc a new arg
316  arg = symbAllocOvlCallArg( @parser.ovlarglist, arg_list, FALSE )
317 
318  hOvlProcArg( args - init_args, arg, have_eq_outside_parens, options )
319 
320  '' ','?
321  if( lexGetToken( ) <> CHAR_COMMA ) then
322  '' only count this arg if it isn't optional
323  if( arg->expr <> NULL ) then
324  args += 1
325  end if
326 
327  exit do
328  end if
329 
330  lexSkipToken( )
331 
332  '' next
333  args += 1
334  loop
335  end if
336 
337  '' (checking the first overload, the overload head proc)
338  hMaybeWarnAboutEqOutsideParens( args, have_eq_outside_parens, proc )
339 
340  '' try finding the closest overloaded proc (don't pass the instance ptr, if any)
341  dim as FB_SYMBLOOKUPOPT lkup_options = FB_SYMBLOOKUPOPT_NONE
342  if( symbIsProperty( proc ) ) then
343  if( (options and FB_PARSEROPT_ISPROPGET) <> 0 ) then
344  lkup_options = FB_SYMBLOOKUPOPT_PROPGET
345  end if
346  end if
347 
348  ovlproc = symbFindClosestOvlProc( proc, _
349  args, _
350  iif( (options and FB_PARSEROPT_HASINSTPTR) <> 0, _
351  arg_list->head->next, _
352  arg_list->head ), _
353  @err_num, _
354  lkup_options )
355 
356  if( ovlproc = NULL ) then
357  symbFreeOvlCallArgs( @parser.ovlarglist, arg_list )
358 
359  if( err_num = FB_ERRMSG_OK ) then
360  err_num = FB_ERRMSG_NOMATCHINGPROC
361  end if
362 
363  errReportParam( proc, 0, NULL, err_num )
364  '' error recovery: fake an expr
365  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
366  end if
367 
368  proc = ovlproc
369 
370  '' check visibility
371  if( symbCheckAccess( proc ) = FALSE ) then
372  errReportEx( iif( symbIsConstructor( proc ), _
373  FB_ERRMSG_NOACCESSTOCTOR, _
374  FB_ERRMSG_ILLEGALMEMBERACCESS ), _
376  '' error recovery: fake an expr
377  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
378  end if
379 
380  '' method?
381  if( symbIsMethod( proc ) ) then
382  '' calling a method without the instance ptr?
383  if( (options and FB_PARSEROPT_HASINSTPTR) = 0 ) then
384  '' is this really a static access or just a method call from
385  '' another method in the same class?
386  if( (base_parent <> NULL) or (symbIsMethod( parser.currproc ) = FALSE) ) then
387  errReport( FB_ERRMSG_MEMBERISNTSTATIC, TRUE )
388  '' error recovery: fake an expr
389  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
390  end if
391 
392  '' pass the instance ptr of the current method
393  arg = symbAllocOvlCallArg( @parser.ovlarglist, arg_list, TRUE )
394  arg->expr = astBuildInstPtr( _
395  symbGetParamVar( _
396  symbGetProcHeadParam( parser.currproc ) ) )
397  arg->mode = INVALID
398  end if
399 
400  '' re-add the instance ptr
401  args += 1
402 
403  procexpr = hGetVtableLookupIfNeeded( proc, arg_list->head->expr, options )
404  else
405  '' remove the instance ptr
406  if( (options and FB_PARSEROPT_HASINSTPTR) <> 0 ) then
407  arg = arg_list->head
408  arg_list->head = arg->next
409  astDelTree( arg->expr )
410  symbFreeOvlCallArg( @parser.ovlarglist, arg )
411  end if
412  procexpr = NULL
413  end if
414 
415  procexpr = astNewCALL( proc, procexpr )
416 
417  '' add to tree
418  param = symbGetProcHeadParam( proc )
419  arg = arg_list->head
420  for i = 0 to args-1
421  nxt = arg->next
422 
423  if( astNewARG( procexpr, arg->expr, , arg->mode ) = NULL ) then
424  errReport( FB_ERRMSG_PARAMTYPEMISMATCH )
425  '' error recovery: fake an expr (don't try to fake an arg,
426  '' different modes and param types like "as any" would break AST)
427  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
428  end if
429 
430  symbFreeOvlCallArg( @parser.ovlarglist, arg )
431 
432  '' next
433  param = param->next
434  arg = nxt
435  next
436 
437  '' add the end-of-list optional args, if any
438  params = symbGetProcParams( proc )
439  do while( args < params )
440  astNewARG( procexpr, NULL )
441 
442  '' next
443  args += 1
444  param = param->next
445  loop
446 
447  function = procexpr
448 end function
449 
450 '':::::
451 ''ProcArgList = ProcArg (DECL_SEPARATOR ProcArg)* .
452 ''
453 function cProcArgList _
454  ( _
455  byval base_parent as FBSYMBOL ptr, _
456  byval proc as FBSYMBOL ptr, _
457  byval ptrexpr as ASTNODE ptr, _
458  byval arg_list as FB_CALL_ARG_LIST ptr, _
459  byval options as FB_PARSEROPT _
460  ) as ASTNODE ptr
461 
462  dim as integer args = any, params = any, mode = any, have_eq_outside_parens = any
463  dim as FBSYMBOL ptr param = any
464  dim as ASTNODE ptr procexpr = any, expr = any
465  dim as FB_CALL_ARG ptr arg = any
466 
467  '' overloaded?
468  if( symbGetProcIsOverloaded( proc ) ) then
469  '' only if there's more than one overloaded function
470  if( symbGetProcOvlNext( proc ) <> NULL ) then
471  return hOvlProcArgList( base_parent, proc, arg_list, options )
472  end if
473  end if
474 
475  function = NULL
476  have_eq_outside_parens = FALSE
477 
478  '' check visibility
479  if( symbCheckAccess( proc ) = FALSE ) then
480  errReportEx( iif( symbIsConstructor( proc ), _
481  FB_ERRMSG_NOACCESSTOCTOR, _
482  FB_ERRMSG_ILLEGALMEMBERACCESS ), _
484  '' error recovery: fake an expr
485  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
486  end if
487 
488  '' method?
489  if( symbIsMethod( proc ) ) then
490  '' calling a method without the instance ptr?
491  if( (options and FB_PARSEROPT_HASINSTPTR) = 0 ) then
492  '' is this really a static access or just a method call from
493  '' another method in the same class?
494  if( (base_parent <> NULL) or (symbIsMethod( parser.currproc ) = FALSE) ) then
495  errReport( FB_ERRMSG_MEMBERISNTSTATIC, TRUE )
496  '' error recovery: fake an expr
497  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
498  end if
499 
500  '' pass the instance ptr of the current method
501  arg = symbAllocOvlCallArg( @parser.ovlarglist, arg_list, TRUE )
502  arg->expr = astBuildInstPtr( _
503  symbGetParamVar( _
504  symbGetProcHeadParam( parser.currproc ) ) )
505  arg->mode = INVALID
506  end if
507 
508  '' Assuming there is no existing function pointer access,
509  '' since we cannot CALL on two function pointer expressions...
510  '' (i.e. no virtual method pointers for now)
511  assert( ptrexpr = NULL )
512 
513  ptrexpr = hGetVtableLookupIfNeeded( proc, arg_list->head->expr, options )
514  else
515  '' remove the instance ptr
516  if( (options and FB_PARSEROPT_HASINSTPTR) <> 0 ) then
517  arg = arg_list->head
518  arg_list->head = arg->next
519  astDelTree( arg->expr )
520  symbFreeOvlCallArg( @parser.ovlarglist, arg )
521  end if
522  end if
523 
524  procexpr = astNewCALL( proc, ptrexpr )
525 
526  params = symbGetProcParams( proc )
527 
528  param = symbGetProcHeadParam( proc )
529 
530  '' any pre-defined args?
531  arg = arg_list->head
532  do while( arg <> NULL )
533  dim as FB_CALL_ARG ptr nxt = arg->next
534 
535  if( astNewARG( procexpr, arg->expr, , arg->mode ) = NULL ) then
536  exit function
537  end if
538 
539  symbFreeOvlCallArg( @parser.ovlarglist, arg )
540 
541  '' next
542  param = param->next
543  arg = nxt
544  params -= 1
545  loop
546 
547  args = 0
548 
549  '' proc has no args?
550  if( params = 0 ) then
551  '' sub? check the optional parentheses
552  if( (options and FB_PARSEROPT_ISFUNC) = 0 ) then
553  '' '('
554  if( lexGetToken( ) = CHAR_LPRNT ) then
555  lexSkipToken( )
556  '' ')'
557  if( lexGetToken( ) <> CHAR_RPRNT ) then
558  errReport( FB_ERRMSG_EXPECTEDRPRNT )
559  '' error recovery: skip until next ')'
560  hSkipUntil( CHAR_RPRNT, TRUE )
561  else
562  lexSkipToken( )
563  end if
564  end if
565  end if
566 
567  return procexpr
568  end if
569 
570  if( (options and FB_PARSEROPT_OPTONLY) = 0 ) then
571  do
572  '' count mismatch?
573  if( args >= params ) then
574  if( param->param.mode <> FB_PARAMMODE_VARARG ) then
575  errReport( FB_ERRMSG_ARGCNTMISMATCH )
576  '' error recovery: skip until next stmt or ')'
577  if( (options and FB_PARSEROPT_ISFUNC) <> 0 ) then
578  hSkipUntil( CHAR_RPRNT )
579  else
580  hSkipStmt( )
581  end if
582 
583  args -= 1
584  exit do
585  end if
586  end if
587 
588  if( hProcArg( proc, param, args, expr, mode, have_eq_outside_parens, options ) = FALSE ) then
589  exit do
590  end if
591 
592  '' add to tree
593  if( astNewARG( procexpr, expr, , mode ) = NULL ) then
594  '' error recovery: skip until next stmt or ')'
595  if( (options and FB_PARSEROPT_ISFUNC) <> 0 ) then
596  hSkipUntil( CHAR_RPRNT )
597  else
598  hSkipStmt( )
599  end if
600 
601  '' don't try to fake an arg, different modes and param
602  '' types like "as any" would break AST
603  astDelTree( procexpr )
604  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
605  end if
606 
607  '' next
608  args += 1
609  if( args < params ) then
610  param = param->next
611  end if
612 
613  '' ','?
614  loop while( hMatch( CHAR_COMMA ) )
615  end if
616 
617  hMaybeWarnAboutEqOutsideParens( args, have_eq_outside_parens, proc )
618 
619  '' if not all args were given, check for the optional ones
620  do while( args < params )
621  '' var-arg? can't be optional..
622  if( param->param.mode = FB_PARAMMODE_VARARG ) then
623  exit do
624  end if
625 
626  '' not optional?
627  if( symbGetIsOptional( param ) = FALSE ) then
628  errReport( FB_ERRMSG_ARGCNTMISMATCH )
629  '' error recovery: fake an expr
630  astDelTree( procexpr )
631  return astNewCONSTz( symbGetType( proc ), symbGetSubType( proc ) )
632  end if
633 
634  '' add to tree
635  if( astNewARG( procexpr, NULL ) = NULL ) then
636  exit function
637  end if
638 
639  '' next
640  args += 1
641  param = param->next
642  loop
643 
644  function = procexpr
645 end function
646