FreeBASIC  0.91.0
parser-compound-for.bas
Go to the documentation of this file.
1 '' FOR..NEXT compound statement parsing
2 ''
3 '' chng: sep/2004 written [v1ctor]
4 
5 
6 #include once "fb.bi"
7 #include once "fbint.bi"
8 #include once "parser.bi"
9 #include once "ast.bi"
10 #include once "symb.bi"
11 
13  FOR_ISUDT = &h0001
14  FOR_HASCTOR = &h0002
15  FOR_ISLOCAL = &h0004
16 end enum
17 
18 #define CREATEFAKEID( ) _
19  astNewVAR( symbAddTempVar( FB_DATATYPE_INTEGER ) )
20 
21 declare function hUdtCallOpOvl _
22  ( _
23  byval parent as FBSYMBOL ptr, _
24  byval op as AST_OP, _
25  byval inst_expr as ASTNODE ptr, _
26  byval second_arg as ASTNODE ptr, _
27  byval third_arg as ASTNODE ptr = NULL _
28  ) as ASTNODE ptr
29 
30 declare sub hFlushBOP _
31  ( _
32  byval op as integer, _
33  byval lhs as FB_CMPSTMT_FORELM ptr, _
34  byval rhs as FB_CMPSTMT_FORELM ptr, _
35  byval ex as FBSYMBOL ptr _
36  )
37 
38 declare sub hFlushSelfBOP _
39  ( _
40  byval op as integer, _
41  byval lhs as FB_CMPSTMT_FORELM ptr, _
42  byval rhs as FB_CMPSTMT_FORELM ptr _
43  )
44 
45 ''::::
46 function hElmToExpr _
47  ( _
48  byval v as FB_CMPSTMT_FORELM ptr _
49  ) as ASTNODE ptr
50 
51  '' This function creates an AST node using the value
52  '' contained in the FB_CMPSTMT_FORELM. The structure
53  '' either contains a symbol, which is used, or if no
54  '' symbol is found then the embedded value is used to
55  '' create a constant, which is used instead.
56  '' The AST node is returned.
57 
58  '' if there's an embedded symbol, use it
59  if( v->sym <> NULL ) then
60  function = astNewVAR( v->sym )
61 
62  '' make a constant...
63  else
64  function = astNewCONST( @v->value, v->dtype )
65  end if
66 
67 end function
68 
69 '':::::
70 sub hUdtFor _
71  ( _
72  byval stk as FB_CMPSTMTSTK ptr _
73  )
74 
75  dim as ASTNODE ptr proc = any, step_expr = NULL
76 
77  '' only pass the step arg if it's an explicit step
78  if( stk->for.explicit_step ) then
79  step_expr = hElmToExpr( @stk->for.stp )
80  end if
81 
82  proc = hUdtCallOpOvl( symbGetSubtype( stk->for.cnt.sym ), _
83  AST_OP_FOR, _
84  hElmToExpr( @stk->for.cnt ), _
85  step_expr )
86 
87  if( proc <> NULL ) then
88  astAdd( proc )
89  end if
90 
91 end sub
92 
93 '':::::
94 sub hUdtStep _
95  ( _
96  byval stk as FB_CMPSTMTSTK ptr _
97  )
98 
99  dim as ASTNODE ptr proc = any, step_expr = NULL
100 
101  '' only pass the step arg if it's an explicit step
102  if( stk->for.explicit_step ) then
103  step_expr = hElmToExpr( @stk->for.stp )
104  end if
105 
106  proc = hUdtCallOpOvl( symbGetSubtype( stk->for.cnt.sym ), _
107  AST_OP_STEP, _
108  hElmToExpr( @stk->for.cnt ), _
109  step_expr )
110 
111  if( proc <> NULL ) then
112  astAdd( proc )
113  end if
114 
115 end sub
116 
117 '':::::
118 sub hUdtNext _
119  ( _
120  byval stk as FB_CMPSTMTSTK ptr _
121  )
122 
123  dim as ASTNODE ptr proc = any, step_expr = NULL
124 
125  '' only pass the step arg if it's an explicit step
126  if( stk->for.explicit_step ) then
127  step_expr = hElmToExpr( @stk->for.stp )
128  end if
129 
130  proc = hUdtCallOpOvl( symbGetSubtype( stk->for.cnt.sym ), _
131  AST_OP_NEXT, _
132  hElmToExpr( @stk->for.cnt ), _
133  hElmToExpr( @stk->for.end ), _
134  step_expr )
135 
136  if( proc <> NULL ) then
137  '' if proc(...) <> 0 then goto init
138  astAdd( astNewBOP( AST_OP_NE, _
139  proc, _
140  astNewCONSTi( 0 ), _
141  stk->for.inilabel, _
142  AST_OPOPT_NONE ) )
143  end if
144 
145 end sub
146 
147 '':::::
148 sub hScalarStep _
149  ( _
150  byval stk as FB_CMPSTMTSTK ptr _
151  )
152 
153  '' counter += step
154  hFlushSelfBOP( AST_OP_ADD_SELF, @stk->for.cnt, @stk->for.stp )
155 
156 end sub
157 
158 '':::::
159 sub hScalarNext _
160  ( _
161  byval stk as FB_CMPSTMTSTK ptr _
162  )
163 
164  '' is STEP known? (ie: an constant expression)
165  if( stk->for.ispos.sym = NULL ) then
166  '' counter <= or >= end cond?
167  hFlushBOP( iif( stk->for.ispos.value.i, AST_OP_LE, AST_OP_GE ), _
168  @stk->for.cnt, @stk->for.end, stk->for.inilabel )
169 
170  '' STEP unknown, check sign and branch
171  else
172  dim as FBSYMBOL ptr cl = symbAddLabel( NULL )
173 
174  '' if ispositive = FALSE then
175  astAdd( astNewBOP( AST_OP_NE, _
176  hElmToExpr( @stk->for.ispos ), _
177  astNewCONSTi( 0 ), _
178  cl, _
179  AST_OPOPT_NONE ) )
180 
181  '' if counter >= end_condition then
182  '' goto top_of_FOR
183  hFlushBOP( AST_OP_GE, @stk->for.cnt, @stk->for.end, stk->for.inilabel )
184 
185  '' else
186  '' goto skip_positive_check
187  astAdd( astNewBRANCH( AST_OP_JMP, stk->for.endlabel ) )
188 
189  '' end if
190 
191  '' else
192  astAdd( astNewLABEL( cl, FALSE ) )
193 
194  '' if cnt <= end then goto for_ini
195  hFlushBOP( AST_OP_LE, @stk->for.cnt, @stk->for.end, stk->for.inilabel )
196 
197  '' end if
198 
199  '' skip_positive_check:
200  end if
201 
202 end sub
203 
204 function hAddImplicitVar _
205  ( _
206  byval dtype as integer, _
207  byval subtype as FBSYMBOL ptr = NULL _
208  ) as FBSYMBOL ptr
209 
210  dim as FBSYMBOL ptr s = any
211  dim as integer options = any
212 
213  options = 0
214 
215  '' Move the implicit var to procedure-level if the lang mode requests it
216  '' (to prevent the stack memory from being re-used by other locals,
217  '' allowing for "random" GOTOs/GOSUBs in and out of FOR loops)
218  if( fbLangOptIsSet( FB_LANG_OPT_SCOPE ) = FALSE ) then
219  options or= FB_SYMBOPT_UNSCOPE
220  end if
221 
222  '' dim temp as dtype
223  s = symbAddImplicitVar( dtype, subtype, options )
224 
225  if( options and FB_SYMBOPT_UNSCOPE ) then
226  astAddUnscoped( astNewDECL( s, TRUE ) )
227  else
228  astAdd( astNewDECL( s, FALSE ) )
229  end if
230 
231  function = s
232 end function
233 
234 function hStoreTemp _
235  ( _
236  byval dtype as integer, _
237  byval subtype as FBSYMBOL ptr, _
238  byval expr as ASTNODE ptr _
239  ) as FBSYMBOL ptr
240 
241  '' This function creates a temporary symbol,
242  '' which then has the expression 'expr' stored
243  '' into it. The symbol is returned.
244  dim as FBSYMBOL ptr s = hAddImplicitVar( dtype, subtype )
245 
246  '' expr is assigned into the symbol
247  expr = astNewASSIGN( astNewVAR( s ), expr )
248 
249  '' couldn't assign?
250  if( expr = NULL ) then
251  select case as const typeGet( dtype )
252  '' TYPE or CLASS
253  case FB_DATATYPE_STRUCT ', FB_DATATYPE_CLASS
254  errReport( FB_ERRMSG_INVALIDDATATYPES )
255  case else
256  errReport( FB_ERRMSG_UDTINFORNEEDSOPERATORS, TRUE, _
257  astGetOpId( AST_OP_ASSIGN ) )
258  end select
259  else
260  '' add to AST
261  astAdd( expr )
262  end if
263 
264  function = s
265 
266 end function
267 
268 '':::::
269 sub hFlushBOP _
270  ( _
271  byval op as integer, _
272  byval lhs as FB_CMPSTMT_FORELM ptr, _
273  byval rhs as FB_CMPSTMT_FORELM ptr, _
274  byval ex as FBSYMBOL ptr _
275  )
276 
277  '' This sub handles binary expression construction,
278  '' and branching based on the result of those expressions.
279 
280  dim as ASTNODE ptr lhs_expr = any, rhs_expr = any, expr = any
281 
282  '' build expressions from the left and
283  '' right-hand-side variables/constants
284  lhs_expr = hElmToExpr( lhs )
285  rhs_expr = hElmToExpr( rhs )
286 
287  '' attempt to build "lhs op rhs"
288  expr = astNewBOP( op, lhs_expr, rhs_expr, ex, AST_OPOPT_NONE )
289 
290  '' fail?
291  if( expr = NULL ) then
292  '' this can only happen with UDT's
293  errReport( FB_ERRMSG_UDTINFORNEEDSOPERATORS, TRUE, astGetOpId( op ) )
294  exit sub
295  end if
296 
297  '' UDT?
298  if( lhs->dtype = FB_DATATYPE_STRUCT ) then
299  '' handle dtors, etc
300  expr = astBuildBranch( expr, ex, TRUE )
301 
302  '' fail?
303  if( expr = NULL ) then
304  '' this can only happen with UDT's
305  errReport( FB_ERRMSG_UDTINFORNEEDSOPERATORS, TRUE, astGetOpId( op ) )
306  exit sub
307  end if
308  end if
309 
310  '' add to AST
311  astAdd( expr )
312 
313 end sub
314 
315 '':::::
316 function hStepExpression _
317  ( _
318  byval lhs_dtype as integer, _
319  byval lhs_subtype as FBSYMBOL ptr, _
320  byval rhs as FB_CMPSTMT_FORELM ptr _
321  ) as ASTNODE ptr
322 
323  dim as longint length = any
324 
325  '' This function generates the AST node for
326  '' the STEP variable, which is used in hFlushSelfBOP
327  '' as the right-hand-side to the FOR += operation.
328 
329  '' pointer counter?
330  if( typeIsPtr( lhs_dtype ) ) then
331  length = symbCalcDerefLen( lhs_dtype, lhs_subtype )
332  if( length <= 0 ) then
333  errReport( FB_ERRMSG_INCOMPLETETYPE )
334  length = 1
335  end if
336 
337  '' is STEP a complex expression?
338  if( rhs->sym <> NULL ) then
339  '' Creates an AST node with a binary expression.
340  '' The left hand side of the expression is the
341  '' STEP variable in a FOR block, the right-hand-side
342  '' is an unsigned integer constant derived from the
343  '' width of the counter variable.
344  function = astNewBOP( AST_OP_MUL, _
345  astNewVAR( rhs->sym, 0, FB_DATATYPE_INTEGER ), _
346  astNewCONSTi( length ) )
347  '' constant STEP
348  else
349 
350  '' Creates an AST node with a constant integer expression.
351  '' The value of the constant is calculated by
352  '' taking the STEP value, and multiplying it by
353  '' the width of the counter type.
354  function = astNewCONSTi( rhs->value.i * length )
355  end if
356  '' regular variable counter
357  else
358  '' no calculation needed
359  function = hElmToExpr( rhs )
360  end if
361 
362 end function
363 
364 '':::::
365 sub hFlushSelfBOP _
366  ( _
367  byval op as integer, _
368  byval lhs as FB_CMPSTMT_FORELM ptr, _
369  byval rhs as FB_CMPSTMT_FORELM ptr _
370  )
371 
372  '' This sub handles the creation of the '+=' expression.
373 
374  dim as ASTNODE ptr lhs_expr = any, rhs_expr = any, expr = any
375 
376  lhs_expr = astNewVAR( lhs->sym )
377  rhs_expr = hStepExpression( lhs->dtype, symbGetSubtype( lhs->sym ), rhs )
378 
379  '' attept to create the '+=' expression
380  expr = astNewSelfBOP( op, lhs_expr, rhs_expr )
381 
382  '' fail?
383  if( expr = NULL ) then
384  '' this can only happen with UDT's
385  errReport( FB_ERRMSG_UDTINFORNEEDSOPERATORS, TRUE, astGetOpId( op ) )
386  exit sub
387  end if
388 
389  '' add to AST
390  astAdd( expr )
391 
392 end sub
393 
394 function hCallCtor( byval sym as FBSYMBOL ptr ) as integer
395  dim as ASTNODE ptr expr = cInitializer( sym, FB_INIOPT_ISINI )
396  if( expr = NULL ) then
397  exit function
398  end if
399 
400  expr = astTypeIniFlush( expr, sym, AST_INIOPT_ISINI )
401  if( expr = NULL ) then
402  exit function
403  end if
404 
405  astAdd( expr )
406  function = TRUE
407 end function
408 
409 sub hForAssign _
410  ( _
411  byval stk as FB_CMPSTMTSTK ptr, _
412  byref isconst as integer, _
413  byval dtype as integer, _
414  byval subtype as FBSYMBOL ptr, _
415  byval flags as FOR_FLAGS, _
416  byval idexpr as ASTNODE ptr _
417  )
418 
419  '' This function handles the '= InitialCondition'
420  '' expression of a FOR block.
421 
422  '' =
423  if( cAssignToken( ) = FALSE ) then
424  errReport( FB_ERRMSG_EXPECTEDEQ )
425  end if
426 
427  '' Not a local UDT with a constructor?
428  if( ((flags and FOR_HASCTOR) = 0) or ((flags and FOR_ISLOCAL) = 0) ) then
429  dim as ASTNODE ptr expr = cExpression( )
430  if( expr = NULL ) then
431  errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
432  '' error recovery: fake an expr
433  expr = astNewCONSTi( 0 )
434  end if
435 
436  '' initial condition is a non-UDT constant?
437  if( astIsCONST( expr ) and ((flags and FOR_ISUDT) = 0) ) then
438  '' convert the constant to counter's type
439  expr = astNewCONV( dtype, subtype, expr )
440  if( expr = NULL ) then
441  errReport( FB_ERRMSG_INVALIDDATATYPES )
442  '' error recovery: fake an expr
443  expr = astNewCONSTi( 0 )
444  end if
445 
446  '' take the constant value
447  stk->for.cnt.value = *astConstGetVal( expr )
448 
449  isconst += 1
450  end if
451 
452  '' save initial condition into counter
453  expr = astNewASSIGN( idexpr, expr )
454  if( expr = NULL ) then
455  if( (flags and FOR_ISUDT) <> 0 ) then
456  errReport( FB_ERRMSG_INVALIDDATATYPES )
457  else
458  errReport( FB_ERRMSG_UDTINFORNEEDSOPERATORS, TRUE, @"let" )
459  end if
460  else
461  astAdd( expr )
462  end if
463 
464  '' UDT has a constructor and is local..
465  else
466  if( hCallCtor( stk->for.cnt.sym ) = FALSE ) then
467  errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
468  end if
469  end if
470 end sub
471 
472 '':::::
473 sub hForTo _
474  ( _
475  byval stk as FB_CMPSTMTSTK ptr, _
476  byref isconst as integer, _
477  byval dtype as integer, _
478  byval subtype as FBSYMBOL ptr, _
479  byval flags as FOR_FLAGS _
480  )
481 
482  '' This function handles the 'TO EndCondition'
483  '' expression of a FOR block.
484 
485  '' TO
486  if( lexGetToken( ) <> FB_TK_TO ) then
487  errReport( FB_ERRMSG_EXPECTEDTO )
488  else
489  lexSkipToken( )
490  end if
491 
492  '' EndCondition
493 
494  '' UDT has no constructor?
495  if( (flags and FOR_HASCTOR) = 0 ) then
496  dim as ASTNODE ptr expr = cExpression( )
497  if( expr = NULL ) then
498  errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
499  '' error recovery: fake an expr
500  expr = astNewCONSTi( 0 )
501  end if
502 
503  '' EndCondition is a non-UDT constant?
504  if( astIsCONST( expr ) and ((flags and FOR_ISUDT) = 0) ) then
505  expr = astNewCONV( dtype, subtype, expr )
506  if( expr = NULL ) then
507  errReport( FB_ERRMSG_INVALIDDATATYPES )
508  '' error recovery: fake an expr
509  expr = astNewCONSTi( 0 )
510  end if
511 
512  '' remove any symbol, and
513  stk->for.end.sym = NULL
514  stk->for.end.dtype = dtype
515 
516  '' insert constant value instead, deleting
517  '' source expression
518  stk->for.end.value = *astConstGetVal( expr )
519  astDelNode( expr )
520 
521  isconst += 1
522 
523  '' store end condition into a temp var
524  else
525  '' generate a symbol using the expression's type
526  stk->for.end.sym = hStoreTemp( dtype, subtype, expr )
527  stk->for.end.dtype = symbGetType( stk->for.end.sym )
528  end if
529 
530  '' UDT has a constructor..
531  else
532 
533  '' generate a symbol using the expression's type
534  stk->for.end.sym = hAddImplicitVar( dtype, subtype )
535  stk->for.end.dtype = symbGetType( stk->for.end.sym )
536 
537  '' build constructor call
538  if( hCallCtor( stk->for.end.sym ) = FALSE ) then
539  errReport( FB_ERRMSG_INVALIDDATATYPES )
540  end if
541  end if
542 
543 end sub
544 
545 function hStepIsNonNegative _
546  ( _
547  byval dtype as integer, _
548  byval expr as ASTNODE ptr _
549  ) as integer
550 
551  '' don't test unsigned values for non-negativity
552  if( typeIsSigned( dtype ) = FALSE ) then
553  return TRUE
554  end if
555 
556  '' const >= 0?
557  function = astConstGeZero( expr )
558 end function
559 
560 '':::::
561 sub hForStep _
562  ( _
563  byval stk as FB_CMPSTMTSTK ptr, _
564  byref isconst as integer, _
565  byval dtype as integer, _
566  byval subtype as FBSYMBOL ptr, _
567  byval flags as FOR_FLAGS _
568  )
569 
570  '' STEP
571  stk->for.explicit_step = FALSE
572  if( lexGetToken( ) = FB_TK_STEP ) then
573  lexSkipToken( )
574  stk->for.explicit_step = TRUE
575  end if
576 
577  dim as integer iscomplex = FALSE
578 
579  if( (flags and FOR_HASCTOR) = 0 ) then
580  dim as ASTNODE ptr expr = any
581 
582  if( stk->for.explicit_step ) then
583  expr = cExpression( )
584  if( expr = NULL ) then
585  errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
586  '' error recovery: fake an expr
587  expr = astNewCONSTi( 1 )
588  end if
589  else
590  '' no STEP was specified, so it's 1
591  '' (the step's type will be converted below)
592  expr = astNewCONSTi( 1 )
593  end if
594 
595  if( (flags and FOR_ISUDT) = 0) then
596  '' keep signed-ness of expr type, so negative steps will work properly
597  if( typeIsSigned( astGetFullType( expr ) ) ) then
598  dtype = typeToSigned( dtype )
599  else
600  dtype = typeToUnsigned( dtype )
601  end if
602  end if
603 
604  '' store step into a temp var
605 
606  '' non-UDT constant?
607  if( astIsCONST( expr ) and ((flags and FOR_ISUDT) = 0) ) then
608  expr = astNewCONV( dtype, subtype, expr )
609  if( expr = NULL ) then
610  errReport( FB_ERRMSG_INVALIDDATATYPES )
611  '' error recovery: fake an expr
612  expr = astNewCONSTi( 0 )
613  end if
614 
615  '' get step's positivity
616  stk->for.ispos.value.i = hStepIsNonNegative( dtype, expr )
617 
618  '' get constant step
619  stk->for.stp.sym = NULL
620  if( typeIsPtr( dtype ) ) then
621  stk->for.stp.dtype = FB_DATATYPE_LONG
622  else
623  stk->for.stp.dtype = dtype
624  end if
625 
626  '' store expr into value, and del temp expression
627  stk->for.stp.value = *astConstGetVal( expr )
628  astDelNode( expr )
629 
630  isconst += 1
631  else
632  iscomplex = TRUE
633 
634  '' make a copy of type info, so we can hack
635  '' the pointer stuff if necessary
636  dim as integer tmp_dtype = dtype
637  dim as FBSYMBOL ptr tmp_subtype = subtype
638 
639  '' step can't be a pointer if counter is
640  if( typeIsPtr( dtype ) ) then
641  tmp_dtype = FB_DATATYPE_LONG
642  tmp_subtype = NULL
643  end if
644 
645  '' generate a symbol using the expression's type
646  stk->for.stp.sym = hStoreTemp( tmp_dtype, tmp_subtype, expr )
647  stk->for.stp.dtype = symbGetType( stk->for.stp.sym )
648  end if
649 
650  '' UDT has a constructor..
651  else
652  iscomplex = TRUE
653 
654  if( stk->for.explicit_step ) then
655  '' generate a symbol using the expression's type
656  stk->for.stp.sym = hAddImplicitVar( dtype, subtype )
657  stk->for.stp.dtype = symbGetType( stk->for.stp.sym )
658 
659  '' build constructor call
660  if( hCallCtor( stk->for.stp.sym ) = FALSE ) then
661  errReport( FB_ERRMSG_INVALIDDATATYPES )
662  end if
663  end if
664  end if
665 
666  if( typeIsSigned( dtype ) = FALSE and ((flags and FOR_ISUDT) = 0) ) then
667 
668  '' step is unsigned, so non-negative
669  stk->for.ispos.sym = NULL
670  stk->for.ispos.dtype = FB_DATATYPE_INTEGER
671  stk->for.ispos.value.i = -1 '' TRUE
672 
673  '' if STEP's sign is unknown, we have to check for that
674  elseif( iscomplex and ((flags and FOR_ISUDT) = 0) ) then
675  dim as FB_CMPSTMT_FORELM cmp '' zero-init the FBVALUE field, etc.
676  cmp.dtype = stk->for.stp.dtype
677 
678  stk->for.ispos.sym = hAddImplicitVar( FB_DATATYPE_INTEGER )
679  stk->for.ispos.dtype = FB_DATATYPE_INTEGER
680 
681  '' rhs = STEP >= 0
682  dim as ASTNODE ptr rhs = astNewBOP( AST_OP_GE, _
683  hElmToExpr( @stk->for.stp ), _
684  hElmToExpr( @cmp ) )
685 
686  '' GE failed?
687  if( rhs = NULL ) then
688  errReport( FB_ERRMSG_INVALIDDATATYPES )
689  '' fake it
690  rhs = astNewCONSTi( 0 )
691  end if
692 
693  '' is_positive = rhs
694  astAdd( astNewASSIGN( astNewVAR( stk->for.ispos.sym ), rhs ) )
695 
696  '' no need for a sign check
697  else
698  stk->for.ispos.sym = NULL
699  end if
700 end sub
701 
702 '' ForStmtBegin = FOR ID (AS DataType)? '=' Expression TO Expression (STEP Expression)? .
704  dim as FOR_FLAGS flags = 0
705  dim as FBSYMBOL ptr sym = any
706 
707  '' FOR
708  lexSkipToken( )
709 
710  '' ID
711  dim as FBSYMCHAIN ptr chain_ = any
712  dim as FBSYMBOL ptr base_parent = any
713 
714  chain_ = cIdentifier( base_parent, FB_IDOPT_ISDECL or FB_IDOPT_DEFAULT )
715 
716  '' open outer scope
717  dim as ASTNODE ptr outerscopenode = astScopeBegin( )
718  if( outerscopenode = NULL ) then
719  errReport( FB_ERRMSG_RECLEVELTOODEEP )
720  end if
721 
722  dim as ASTNODE ptr idexpr = any, expr = any
723 
724  '' new variable?
725  if( lexGetLookAhead( 1 ) = FB_TK_AS ) then
726  sym = cVarDecl( FB_SYMBATTRIB_NONE, FALSE, lexGetToken( ), TRUE )
727  if( sym = NULL ) then
728  '' error recovery: fake a var
729  idexpr = CREATEFAKEID( )
730  else
731  flags or= FOR_ISLOCAL
732  idexpr = astNewVAR( sym )
733  end if
734 
735  '' tried array...
736  elseif( lexGetLookAhead( 1 ) = CHAR_LPRNT ) then
737  errReport( FB_ERRMSG_EXPECTEDSCALAR, TRUE )
738  '' error recovery: skip until next ')' and fake a var
739  hSkipUntil( CHAR_RPRNT )
740  idexpr = CREATEFAKEID( )
741 
742  '' look up the variable
743  else
744  idexpr = cVariable( chain_ )
745  if( idexpr = NULL ) then
746  errReport( FB_ERRMSG_EXPECTEDVAR )
747  '' error recovery: fake a var
748  idexpr = CREATEFAKEID( )
749  end if
750 
751  if( astIsVAR( idexpr ) = FALSE ) then
752  errReport( FB_ERRMSG_EXPECTEDSCALAR, TRUE )
753  '' error recovery: fake a var
754  astDelTree( idexpr )
755  idexpr = CREATEFAKEID( )
756  end if
757  end if
758 
759  dim as integer dtype = astGetDataType( idexpr )
760  dim as FBSYMBOL ptr subtype = astGetSubType( idexpr )
761 
762  if( typeIsConst( astGetFullType( idexpr ) ) ) then
763  errReport( FB_ERRMSG_CONSTANTCANTBECHANGED )
764  end if
765 
766  select case as const dtype
767  case FB_DATATYPE_BYTE to FB_DATATYPE_DOUBLE
768 
769  case FB_DATATYPE_STRUCT ', FB_DATATYPE_CLASS
770  flags or= FOR_ISUDT
771  if( symbHasCtor( astGetSymbol( idexpr ) ) ) then
772  flags or= FOR_HASCTOR
773  end if
774 
775  case else
776  '' not a ptr?
777  if( typeIsPtr( dtype ) = FALSE ) then
778  errReport( FB_ERRMSG_EXPECTEDSCALAR, TRUE )
779  '' error recovery: fake a var
780  astDelTree( idexpr )
781  idexpr = CREATEFAKEID( )
782  dtype = astGetDataType( idexpr )
783  end if
784  end select
785 
786  '' push a FOR context
787  dim as FB_CMPSTMTSTK ptr stk = cCompStmtPush( FB_TK_FOR )
788 
789  '' extract counter variable from the expression
790  stk->for.cnt.sym = astGetSymbol( idexpr )
791  stk->for.cnt.dtype = dtype
792 
793  dim as integer isconst = 0
794 
795  '' =
796  hForAssign( stk, isconst, dtype, subtype, flags, idexpr )
797 
798  '' TO
799  hForTo( stk, isconst, dtype, subtype, flags )
800 
801  '' STEP
802  hForStep( stk, isconst, dtype, subtype, flags )
803 
804  '' labels
805  dim as FBSYMBOL ptr il = any, tl = any, el = any, cl = any
806 
807  '' test label: jump to the bottom of the for,
808  '' before any code within the block is executed
809  tl = symbAddLabel( NULL, FB_SYMBOPT_NONE )
810 
811  '' comp and end label (will be used by any CONTINUE/EXIT FOR)
812  cl = symbAddLabel( NULL, FB_SYMBOPT_NONE )
813  el = symbAddLabel( NULL, FB_SYMBOPT_NONE )
814 
815  '' we need to "peek" at the end label,
816  '' to allow an overloaded FOR operator to jump to it,
817  '' if the operator returns FALSE
818  stk->for.endlabel = el
819 
820  '' UDT? must call the FOR operator..
821  if( (flags and FOR_ISUDT) <> 0 ) then
822  hUdtFor( stk )
823  end if
824 
825  '' if inic, endc and stepc are all constants,
826  '' check if this branch is needed
827  if( isconst = 3 ) then
828  expr = astNewBOP( iif( stk->for.ispos.value.i, AST_OP_LE, AST_OP_GE ), _
829  astNewCONST( @stk->for.cnt.value, stk->for.cnt.dtype ), _
830  astNewCONST( @stk->for.end.value, stk->for.end.dtype ) )
831 
832  if( astConstFlushToInt( expr ) = 0 ) then
833  astAdd( astNewBRANCH( AST_OP_JMP, el ) )
834  end if
835  else
836  astAdd( astNewBRANCH( AST_OP_JMP, tl ) )
837  end if
838 
839  '' add start label
840  il = symbAddLabel( NULL )
841  astAdd( astNewLABEL( il ) )
842 
843  '' push to stmt stack
844  stk->scopenode = astScopeBegin( )
845  stk->for.outerscopenode = outerscopenode
846  stk->for.testlabel = tl
847  stk->for.inilabel = il
848  stk->for.cmplabel = cl
849 end sub
850 
851 '':::::
852 function hUdtCallOpOvl _
853  ( _
854  byval parent as FBSYMBOL ptr, _
855  byval op as AST_OP, _
856  byval inst_expr as ASTNODE ptr, _
857  byval second_arg as ASTNODE ptr, _
858  byval third_arg as ASTNODE ptr _
859  ) as ASTNODE ptr
860 
861  dim as FBSYMBOL ptr sym = any
862 
863  '' check if op was overloaded
864  sym = symbGetCompOpOvlHead( parent, op )
865 
866  if( sym = NULL ) then
867  errReport( FB_ERRMSG_UDTINFORNEEDSOPERATORS, _
868  TRUE, _
869  astGetOpId( op ) )
870  return NULL
871  end if
872 
873  '' check for overloaded versions (note: don't pass the instance ptr)
874  dim as FB_ERRMSG err_num = any
875  if( second_arg = NULL ) then
876  sym = symbFindClosestOvlProc( sym, 0, NULL, @err_num )
877  else
878  dim as FB_CALL_ARG args(0 to 1) = any
879  dim as integer params = 1
880  with args(0)
881  .expr = second_arg
882  .mode = INVALID
883  .next = NULL
884  end with
885 
886  '' link in that pesky 3rd arg.
887  if( op = AST_OP_NEXT ) then
888  if( third_arg <> NULL ) then
889  args(0).next = @args(1)
890  params += 1
891  with args(1)
892  .expr = third_arg
893  .mode = INVALID
894  .next = NULL
895  end with
896  end if
897  end if
898 
899  sym = symbFindClosestOvlProc( sym, params, @args(0), @err_num )
900  end if
901 
902  if( sym = NULL ) then
903  '' some other error?
904  if( err_num <> FB_ERRMSG_OK ) then
905  errReport( err_num, TRUE )
906 
907  '' build a message for the user
908  else
909  dim as string op_version = *astGetOpId( op ) + " (with"
910  select case as const op
911  case AST_OP_FOR, AST_OP_STEP
912  '' supposed to be 1 arg
913  if( second_arg = NULL ) then
914  op_version += "out"
915  end if
916  case AST_OP_NEXT
917  '' supposed to be 2 args
918  if( third_arg = NULL ) then
919  op_version += "out"
920  end if
921  end select
922  op_version += " step)"
923  errReport( FB_ERRMSG_UDTINFORNEEDSOPERATORS, TRUE, strptr(op_version) )
924  end if
925  return NULL
926  end if
927 
928  function = astBuildCall( sym, inst_expr, second_arg, third_arg )
929 end function
930 
931 sub hForStmtClose(byval stk as FB_CMPSTMTSTK ptr)
932  '' close the scope block
933  if( stk->scopenode <> NULL ) then
934  astScopeEnd( stk->scopenode )
935  end if
936 
937  '' cmp label
938  astAdd( astNewLABEL( stk->for.cmplabel ) )
939 
940  '' UDT?
941  select case symbGetType( stk->for.cnt.sym )
942  case FB_DATATYPE_STRUCT ', FB_DATATYPE_CLASS
943  '' update
944  hUdtStep( stk )
945 
946  '' emit test label
947  astAdd( astNewLABEL( stk->for.testlabel ) )
948 
949  '' check
950  hUdtNext( stk )
951 
952  case else
953  '' update
954  hScalarStep( stk )
955 
956  '' emit test label
957  astAdd( astNewLABEL( stk->for.testlabel ) )
958 
959  '' check
960  hScalarNext( stk )
961  end select
962 
963  '' end label (loop exit)
964  astAdd( astNewLABEL( stk->for.endlabel ) )
965 
966  '' close the outer scope block
967  if( stk->for.outerscopenode <> NULL ) then
968  astScopeEnd( stk->for.outerscopenode )
969  end if
970 end sub
971 
972 '' ForStmtEnd = NEXT (ID (',' ID?))? .
974  dim as FB_CMPSTMTSTK ptr stk = any
975 
976  '' NEXT
977  lexSkipToken( )
978 
979  do
980  '' TOS = top of stack
981  stk = cCompStmtGetTOS( FB_TK_FOR )
982  if( stk = NULL ) then
983  hSkipStmt( )
984  exit sub
985  end if
986 
987  hForStmtClose( stk )
988 
989  '' ID?
990  if( lexGetClass( ) <> FB_TKCLASS_IDENTIFIER ) then
991  '' pop from stmt stack
992  cCompStmtPop( stk )
993  exit do
994  end if
995 
996  '' ID
997  dim as FBSYMCHAIN ptr chain_ = any
998  dim as FBSYMBOL ptr base_parent = any
999  chain_ = cIdentifier( base_parent, FB_IDOPT_ISDECL or FB_IDOPT_DEFAULT )
1000 
1001  '' look up the variable
1002  dim as ASTNODE ptr idexpr = cVariable( chain_ )
1003 
1004  if( idexpr = NULL ) then
1005  errReport( FB_ERRMSG_EXPECTEDVAR )
1006  else
1007  '' Same symbol?
1008  if( idexpr->sym <> stk->for.cnt.sym ) then
1009  errReport( FB_ERRMSG_FORNEXTVARIABLEMISMATCH )
1010  end if
1011 
1012  if( fbPdCheckIsSet( FB_PDCHECK_NEXTVAR ) ) then
1013  errReportWarn( FB_WARNINGMSG_NEXTVARMEANINGLESS, *symbGetName(idexpr->sym) )
1014  end if
1015 
1016  '' delete idexpr, we don't need it, for anything
1017  astDelTree( idexpr )
1018  end if
1019 
1020  '' pop from stmt stack
1021  cCompStmtPop( stk )
1022 
1023  '' ','?
1024  if( lexGetToken( ) <> CHAR_COMMA ) then
1025  exit do
1026  end if
1027 
1028  lexSkipToken( )
1029  loop
1030 end sub
1031