FreeBASIC  0.91.0
parser-compound.bas
Go to the documentation of this file.
1 '' compound statements (FOR, DO, WHILE, ...) top-level plus EXIT, END and CONTINUE parsing
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 #include once "rtl.bi"
10 
11 declare sub parserSelectStmtInit ( )
12 declare sub parserSelectStmtEnd ( )
13 declare sub parserSelConstStmtInit ( )
14 declare sub parserSelConstStmtEnd ( )
15 declare sub cCompoundEnd( )
16 
18  parser.stmt.for = NULL
19  parser.stmt.do = NULL
20  parser.stmt.while = NULL
21  parser.stmt.select = NULL
22  parser.stmt.proc = NULL
23  parser.stmt.with.sym = NULL
24 end sub
25 
29 end sub
30 
34 end sub
35 
36 #macro CHECK_CODEMASK( for_tk, until_tk )
37  if( cCompStmtIsAllowed( FB_CMPSTMT_MASK_CODE ) = FALSE ) then
38  hSkipCompound( for_tk, until_tk )
39  exit function
40  end if
41 #endmacro
42 
43 '':::::
44 ''CompoundStmt = IfStatement
45 '' | ForStatement
46 '' | DoStatement
47 '' | WhileStatement
48 '' | SelectStatement
49 '' | ExitStatement
50 '' | ContinueStatement
51 '' | EndStatement
52 ''
53 function cCompoundStmt as integer
54  '' QB mode?
55  if( env.clopt.lang = FB_LANG_QB ) then
56  if( lexGetType() <> FB_DATATYPE_INVALID ) then
57  return FALSE
58  end if
59  end if
60 
61  select case as const lexGetToken( )
62  case FB_TK_IF
63  CHECK_CODEMASK( FB_TK_IF, FB_TK_IF )
64  cIfStmtBegin( )
65 
66  case FB_TK_FOR
67  CHECK_CODEMASK( FB_TK_FOR, FB_TK_NEXT )
68  cForStmtBegin( )
69 
70  case FB_TK_DO
71  CHECK_CODEMASK( FB_TK_DO, FB_TK_LOOP )
72  cDoStmtBegin( )
73 
74  case FB_TK_WHILE
75  CHECK_CODEMASK( FB_TK_WHILE, FB_TK_WEND )
77 
78  case FB_TK_SELECT
79  CHECK_CODEMASK( FB_TK_SELECT, FB_TK_SELECT )
81 
82  case FB_TK_WITH
83  CHECK_CODEMASK( FB_TK_WITH, FB_TK_WITH )
85 
86  case FB_TK_SCOPE
87  CHECK_CODEMASK( FB_TK_SCOPE, FB_TK_SCOPE )
89 
90  case FB_TK_NAMESPACE
92 
93  case FB_TK_EXTERN
95 
96  case FB_TK_ELSE, FB_TK_ELSEIF
97  cIfStmtNext( )
98 
99  case FB_TK_CASE
100  cSelectStmtNext( )
101 
102  case FB_TK_LOOP
103  cDoStmtEnd( )
104 
105  case FB_TK_NEXT
106  cForStmtEnd( )
107 
108  case FB_TK_WEND
109  cWhileStmtEnd( )
110 
111  case FB_TK_EXIT
112  cExitStatement( )
113 
114  case FB_TK_CONTINUE
116 
117  case FB_TK_END
118  '' any compound END will be parsed by the compound stmt
119  if( lexGetLookAheadClass( 1 ) <> FB_TKCLASS_KEYWORD ) then
120  CHECK_CODEMASK( INVALID, INVALID )
121  cEndStatement( )
122  else
123  cCompoundEnd( )
124  end if
125 
126  case FB_TK_ENDIF
127  cIfStmtEnd( )
128 
129  case FB_TK_USING
130  cUsingStmt( )
131 
132  case else
133  return FALSE
134  end select
135 
136  function = TRUE
137 end function
138 
139 '' EndStatement = END Expression? .
141  dim as ASTNODE ptr errlevel = any
142 
143  '' END
144  lexSkipToken( )
145 
146  '' Expression?
147  select case as const lexGetToken( )
148  case FB_TK_STMTSEP, FB_TK_EOL, FB_TK_EOF, FB_TK_COMMENT, FB_TK_REM, _
149  FB_TK_ELSE, FB_TK_END, FB_TK_ENDIF
150  errlevel = NULL
151  case else
152  errlevel = cExpression( )
153  end select
154 
155  rtlExitApp( errlevel )
156 end sub
157 
158 function hCheckForCtorResult( ) as integer
159  function = FB_ERRMSG_OK
160  if( symbHasCtor( parser.currproc ) ) then
161  if( symbGetProcStatReturnUsed( parser.currproc ) ) then
162  '' EXIT FUNCTION cannot be allowed in combination
163  '' with RETURN and a ctor result, because it would
164  '' not call the result constructor.
165  function = FB_ERRMSG_MISSINGRETURNFORCTORRESULT
166  else
167  '' EXIT FUNCTION used, and no RETURN yet:
168  '' make it behave like FUNCTION=, to ensure the result ctor
169  '' is called at the top.
170  symbSetProcStatAssignUsed( parser.currproc )
171  end if
172  end if
173 end function
174 
175 '':::::
176 #macro hExitError( errnum )
177  errReport( errnum )
178  '' error recovery: skip stmt
179  hSkipStmt( )
180  return
181 #endmacro
182 
183 '' ExitStatement = EXIT (FOR | DO | WHILE | SELECT | SUB | FUNCTION)
185  dim as FBSYMBOL ptr label = NULL
186 
187  '' EXIT
188  lexSkipToken( )
189 
190  '' (FOR | DO | WHILE | SELECT | SUB | FUNCTION) (',')*
191  select case as const lexGetToken( )
192  case FB_TK_FOR
193  if( parser.stmt.for = NULL ) then
194  hExitError( FB_ERRMSG_ILLEGALOUTSIDEFORSTMT )
195  end if
196 
197  lexSkipToken( )
198 
199  dim as FB_CMPSTMTSTK ptr stk = parser.stmt.for
200  do while( lexGetToken( ) = CHAR_COMMA )
201  lexSkipToken( )
202 
203  if( lexGetToken( ) <> FB_TK_FOR ) then
204  hExitError( FB_ERRMSG_EXPECTEDFOR )
205  end if
206 
207  stk = stk->for.last
208  if( stk = NULL ) then
209  hExitError( FB_ERRMSG_NOENCLOSEDFORSTMT )
210  end if
211 
212  lexSkipToken( )
213  loop
214 
215  label = stk->for.endlabel
216 
217  case FB_TK_DO
218  if( parser.stmt.do = NULL ) then
219  hExitError( FB_ERRMSG_ILLEGALOUTSIDEDOSTMT )
220  end if
221 
222  lexSkipToken( )
223 
224  dim as FB_CMPSTMTSTK ptr stk = parser.stmt.do
225  do while( lexGetToken( ) = CHAR_COMMA )
226  lexSkipToken( )
227 
228  if( lexGetToken( ) <> FB_TK_DO ) then
229  hExitError( FB_ERRMSG_EXPECTEDDO )
230  end if
231 
232  stk = stk->do.last
233  if( stk = NULL ) then
234  hExitError( FB_ERRMSG_NOENCLOSEDDOSTMT )
235  end if
236 
237  lexSkipToken( )
238  loop
239 
240  label = stk->do.endlabel
241 
242  case FB_TK_WHILE
243  if( parser.stmt.while = NULL ) then
244  hExitError( FB_ERRMSG_ILLEGALOUTSIDEWHILESTMT )
245  end if
246 
247  lexSkipToken( )
248 
249  dim as FB_CMPSTMTSTK ptr stk = parser.stmt.while
250  do while( lexGetToken( ) = CHAR_COMMA )
251  lexSkipToken( )
252 
253  if( lexGetToken( ) <> FB_TK_WHILE ) then
254  hExitError( FB_ERRMSG_EXPECTEDWHILE )
255  end if
256 
257  stk = stk->while.last
258  if( stk = NULL ) then
259  hExitError( FB_ERRMSG_NOENCLOSEDWHILESTMT )
260  end if
261 
262  lexSkipToken( )
263  loop
264 
265  label = stk->while.endlabel
266 
267  case FB_TK_SELECT
268  if( parser.stmt.select = NULL ) then
269  hExitError( FB_ERRMSG_ILLEGALOUTSIDESELSTMT )
270  end if
271 
272  lexSkipToken( )
273 
274  dim as FB_CMPSTMTSTK ptr stk = parser.stmt.select
275  do while( lexGetToken( ) = CHAR_COMMA )
276  lexSkipToken( )
277 
278  if( lexGetToken( ) <> FB_TK_SELECT ) then
279  hExitError( FB_ERRMSG_EXPECTEDSELECT )
280  end if
281 
282  stk = stk->select.last
283  if( stk = NULL ) then
284  hExitError( FB_ERRMSG_NOENCLOSEDSELSTMT )
285  end if
286 
287  lexSkipToken( )
288  loop
289 
290  label = stk->select.endlabel
291 
292  case FB_TK_SUB, FB_TK_FUNCTION, FB_TK_PROPERTY, FB_TK_OPERATOR, _
293  FB_TK_CONSTRUCTOR, FB_TK_DESTRUCTOR
294 
295  if( parser.stmt.proc <> NULL ) then
296  label = parser.stmt.proc->proc.endlabel
297  end if
298 
299  if( label = NULL ) then
300  hExitError( FB_ERRMSG_ILLEGALOUTSIDEAPROC )
301  end if
302 
303  dim as FB_ERRMSG errnum = FB_ERRMSG_OK
304 
305  select case as const lexGetToken( )
306  case FB_TK_SUB
307  if( symbGetType( parser.currproc ) = FB_DATATYPE_VOID ) then
308  if( (symbGetAttrib( parser.currproc ) and _
309  (FB_SYMBATTRIB_PROPERTY or FB_SYMBATTRIB_OPERATOR or _
310  FB_SYMBATTRIB_CONSTRUCTOR or FB_SYMBATTRIB_DESTRUCTOR)) <> 0 ) then
311  errnum = FB_ERRMSG_ILLEGALOUTSIDEASUB
312  end if
313  else
314  errnum = FB_ERRMSG_ILLEGALOUTSIDEASUB
315  end if
316 
317  case FB_TK_FUNCTION
318  if( symbGetType( parser.currproc ) <> FB_DATATYPE_VOID ) then
319  if( (symbGetAttrib( parser.currproc ) and _
320  (FB_SYMBATTRIB_PROPERTY or FB_SYMBATTRIB_OPERATOR or _
321  FB_SYMBATTRIB_CONSTRUCTOR or FB_SYMBATTRIB_DESTRUCTOR)) <> 0 ) then
322  errnum = FB_ERRMSG_ILLEGALOUTSIDEAFUNCTION
323  else
324  errnum = hCheckForCtorResult( )
325  end if
326  else
327  errnum = FB_ERRMSG_ILLEGALOUTSIDEAFUNCTION
328  end if
329 
330  case FB_TK_PROPERTY
331  if( symbIsProperty( parser.currproc ) ) then
332  errnum = hCheckForCtorResult( )
333  else
334  errnum = FB_ERRMSG_ILLEGALOUTSIDEANPROPERTY
335  end if
336 
337  case FB_TK_OPERATOR
338  if( symbIsOperator( parser.currproc ) ) then
339  errnum = hCheckForCtorResult( )
340  else
341  errnum = FB_ERRMSG_ILLEGALOUTSIDEANOPERATOR
342  end if
343 
344  case FB_TK_CONSTRUCTOR
345  if( symbIsConstructor( parser.currproc ) = FALSE ) then
346  errnum = FB_ERRMSG_ILLEGALOUTSIDEACTOR
347  end if
348 
349  case FB_TK_DESTRUCTOR
350  if( symbIsDestructor( parser.currproc ) = FALSE ) then
351  errnum = FB_ERRMSG_ILLEGALOUTSIDEADTOR
352  end if
353 
354  end select
355 
356  if( errnum <> FB_ERRMSG_OK ) then
357  hExitError( errnum )
358  end if
359 
360  lexSkipToken( )
361 
362  case else
363  hExitError( FB_ERRMSG_INVALIDEXITSTMT )
364  end select
365 
366  astScopeBreak( label )
367 end sub
368 
369 '' ContinueStatement = CONTINUE (FOR | DO | WHILE)
371  dim as FBSYMBOL ptr label = NULL
372 
373  '' CONTINUE
374  lexSkipToken( )
375 
376  '' (FOR | DO | WHILE) (',')*
377  select case as const lexGetToken( )
378  case FB_TK_FOR
379  if( parser.stmt.for = NULL ) then
380  hExitError( FB_ERRMSG_ILLEGALOUTSIDEFORSTMT )
381  end if
382 
383  lexSkipToken( )
384 
385  dim as FB_CMPSTMTSTK ptr stk = parser.stmt.for
386  do while( lexGetToken( ) = CHAR_COMMA )
387  lexSkipToken( )
388 
389  if( lexGetToken( ) <> FB_TK_FOR ) then
390  hExitError( FB_ERRMSG_EXPECTEDFOR )
391  end if
392 
393  stk = stk->for.last
394  if( stk = NULL ) then
395  hExitError( FB_ERRMSG_NOENCLOSEDFORSTMT )
396  end if
397 
398  lexSkipToken( )
399  loop
400 
401  label = stk->for.cmplabel
402 
403  case FB_TK_DO
404  if( parser.stmt.do = NULL ) then
405  hExitError( FB_ERRMSG_ILLEGALOUTSIDEDOSTMT )
406  end if
407 
408  lexSkipToken( )
409 
410  dim as FB_CMPSTMTSTK ptr stk = parser.stmt.do
411  do while( lexGetToken( ) = CHAR_COMMA )
412  lexSkipToken( )
413 
414  if( lexGetToken( ) <> FB_TK_DO ) then
415  hExitError( FB_ERRMSG_EXPECTEDDO )
416  end if
417 
418  stk = stk->do.last
419  if( stk = NULL ) then
420  hExitError( FB_ERRMSG_NOENCLOSEDDOSTMT )
421  end if
422 
423  lexSkipToken( )
424  loop
425 
426  label = stk->do.cmplabel
427 
428  case FB_TK_WHILE
429  if( parser.stmt.while = NULL ) then
430  hExitError( FB_ERRMSG_ILLEGALOUTSIDEWHILESTMT )
431  end if
432 
433  lexSkipToken( )
434 
435  dim as FB_CMPSTMTSTK ptr stk = parser.stmt.while
436  do while( lexGetToken( ) = CHAR_COMMA )
437  lexSkipToken( )
438 
439  if( lexGetToken( ) <> FB_TK_WHILE ) then
440  hExitError( FB_ERRMSG_EXPECTEDWHILE )
441  end if
442 
443  stk = stk->while.last
444  if( stk = NULL ) then
445  hExitError( FB_ERRMSG_NOENCLOSEDWHILESTMT )
446  end if
447 
448  lexSkipToken( )
449  loop
450 
451  label = stk->while.cmplabel
452 
453  case else
454  hExitError( FB_ERRMSG_INVALIDCONTINUESTMT )
455  end select
456 
457  astScopeBreak( label )
458 end sub
459 
460 '' CompoundEnd = END (IF | SELECT | SUB | FUNCTION | SCOPE | WITH | NAMESPACE | EXTERN)
462  select case as const lexGetLookAhead( 1 )
463  case FB_TK_IF
464  cIfStmtEnd( )
465 
466  case FB_TK_SELECT
467  cSelectStmtEnd( )
468 
469  case FB_TK_SUB, FB_TK_FUNCTION, FB_TK_CONSTRUCTOR, FB_TK_DESTRUCTOR, _
470  FB_TK_OPERATOR, FB_TK_PROPERTY
471  cProcStmtEnd( )
472 
473  case FB_TK_SCOPE
474  cScopeStmtEnd( )
475 
476  case FB_TK_WITH
477  cWithStmtEnd( )
478 
479  case FB_TK_NAMESPACE
481 
482  case FB_TK_EXTERN
483  cExternStmtEnd( )
484 
485  '' QB quirk: IF expr THEN END ELSE ...|ENDIF|END IF
486  case FB_TK_ELSE, FB_TK_END, FB_TK_ENDIF
487  cEndStatement( )
488 
489  case else
490  errReport( FB_ERRMSG_ILLEGALEND )
491  '' error recovery: skip stmt
492  hSkipStmt( )
493  end select
494 end sub
495 
496 '':::::
497 function cCompStmtCheck( ) as integer
498  dim as integer errmsg
499  dim as FB_CMPSTMTSTK ptr stk
500 
501  stk = stackGetTOS( @parser.stmt.stk )
502  if( stk = NULL ) then
503  return TRUE
504  end if
505 
506  select case as const stk->id
507  case FB_TK_IF
508  errmsg = FB_ERRMSG_EXPECTEDENDIF
509 
510  case FB_TK_SELECT
511  errmsg = FB_ERRMSG_EXPECTEDENDSELECT
512 
513  case FB_TK_SCOPE
514  errmsg = FB_ERRMSG_EXPECTEDENDSCOPE
515 
516  case FB_TK_WITH
517  errmsg = FB_ERRMSG_EXPECTEDENDWITH
518 
519  case FB_TK_NAMESPACE
520  errmsg = FB_ERRMSG_EXPECTEDENDNAMESPACE
521 
522  case FB_TK_EXTERN
523  errmsg = FB_ERRMSG_EXPECTEDENDEXTERN
524 
525  case FB_TK_FUNCTION
526  select case as const stk->proc.tkn
527  case FB_TK_SUB
528  errmsg = FB_ERRMSG_EXPECTEDENDSUB
529  case FB_TK_FUNCTION
530  errmsg = FB_ERRMSG_EXPECTEDENDFUNCTION
531  case FB_TK_CONSTRUCTOR
532  errmsg = FB_ERRMSG_EXPECTEDENDCTOR
533  case FB_TK_DESTRUCTOR
534  errmsg = FB_ERRMSG_EXPECTEDENDDTOR
535  case FB_TK_OPERATOR
536  errmsg = FB_ERRMSG_EXPECTEDENDOPERATOR
537  case FB_TK_PROPERTY
538  errmsg = FB_ERRMSG_EXPECTEDENDPROPERTY
539  end select
540 
541  case FB_TK_DO
542  errmsg = FB_ERRMSG_EXPECTEDLOOP
543 
544  case FB_TK_WHILE
545  errmsg = FB_ERRMSG_EXPECTEDWEND
546 
547  case FB_TK_FOR
548  errmsg = FB_ERRMSG_EXPECTEDNEXT
549 
550  end select
551 
552  errReport( errmsg )
553 
554  function = FALSE
555 
556 end function
557 
558 '':::::
559 function cCompStmtPush _
560  ( _
561  byval id as FB_TOKEN, _
562  byval allowmask as FB_CMPSTMT_MASK _
563  ) as FB_CMPSTMTSTK ptr static
564 
565  dim as FB_CMPSTMTSTK ptr stk
566 
567  stk = stackPush( @parser.stmt.stk )
568  stk->id = id
569  stk->allowmask = allowmask
570  stk->scopenode = NULL
571 
572  '' same current values, if any
573  select case as const id
574  case FB_TK_DO
575  stk->do.last = parser.stmt.do
576  parser.stmt.do = stk
577 
578  case FB_TK_FOR
579  stk->for.last = parser.stmt.for
580  parser.stmt.for = stk
581 
582  case FB_TK_SELECT
583  stk->select.last = parser.stmt.select
584  parser.stmt.select = stk
585 
586  case FB_TK_WHILE
587  stk->while.last = parser.stmt.while
588  parser.stmt.while = stk
589 
590  case FB_TK_FUNCTION
591  stk->proc.last = parser.stmt.proc
592  parser.stmt.proc = stk
593  end select
594 
595  parser.stmt.id = id
596 
597  function = stk
598 
599 end function
600 
601 '':::::
602 function cCompStmtGetTOS _
603  ( _
604  byval forid as FB_TOKEN, _
605  byval showerror as integer _
606  ) as FB_CMPSTMTSTK ptr static
607 
608  dim as FB_CMPSTMTSTK ptr stk
609  dim as integer iserror
610 
611  stk = stackGetTOS( @parser.stmt.stk )
612  iserror = (stk = NULL)
613 
614  if( iserror = FALSE ) then
615  '' not the expected id?
616  iserror = (stk->id <> forid)
617  end if
618 
619  if( iserror ) then
620  if( showerror ) then
621  if( stk <> NULL ) then
622  cCompStmtCheck( )
623 
624  else
625  dim as integer errmsg
626  select case as const forid
627  case FB_TK_DO
628  errmsg = FB_ERRMSG_LOOPWITHOUTDO
629 
630  case FB_TK_EXTERN
631  errmsg = FB_ERRMSG_ENDEXTERNWITHOUTEXTERN
632 
633  case FB_TK_FOR
634  errmsg = FB_ERRMSG_NEXTWITHOUTFOR
635 
636  case FB_TK_IF
637  errmsg = FB_ERRMSG_ENDIFWITHOUTIF
638 
639  case FB_TK_SCOPE
640  errmsg = FB_ERRMSG_ENDSCOPEWITHOUTSCOPE
641 
642  case FB_TK_SELECT
643  errmsg = FB_ERRMSG_ENDSELECTWITHOUTSELECT
644 
645  case FB_TK_WHILE
646  errmsg = FB_ERRMSG_WENDWITHOUTWHILE
647 
648  case FB_TK_WITH
649  errmsg = FB_ERRMSG_ENDWITHWITHOUTWITH
650 
651  case FB_TK_FUNCTION
652  errmsg = FB_ERRMSG_ENDSUBWITHOUTSUB
653 
654  case FB_TK_NAMESPACE
655  errmsg = FB_ERRMSG_ENDNAMESPACEWITHOUTNAMESPACE
656  end select
657 
658  errReport( errmsg )
659  end if
660  end if
661 
662  function = NULL
663 
664  else
665  function = stk
666  end if
667 
668 end function
669 
670 sub cCompStmtPop( byval stk as FB_CMPSTMTSTK ptr )
671  '' restore old values if any
672  select case as const stk->id
673  case FB_TK_DO
674  parser.stmt.do = stk->do.last
675 
676  case FB_TK_FOR
677  parser.stmt.for = stk->for.last
678 
679  case FB_TK_SELECT
680  parser.stmt.select = stk->select.last
681 
682  case FB_TK_WHILE
683  parser.stmt.while = stk->while.last
684 
685  case FB_TK_FUNCTION
686  parser.stmt.proc = stk->proc.last
687  end select
688 
689  stackPop( @parser.stmt.stk )
690 
691  stk = stackGetTOS( @parser.stmt.stk )
692  if( stk ) then
693  parser.stmt.id = stk->id
694  else
695  parser.stmt.id = 0
696  end if
697 end sub
698 
699 function cCompStmtIsAllowed( byval allowmask as FB_CMPSTMT_MASK ) as integer
700  dim as FB_CMPSTMTSTK ptr stk = any
701  dim as integer errmsg = any
702 
703  stk = stackGetTOS( @parser.stmt.stk )
704 
705  '' module-level? anything allowed..
706  if( stk = NULL ) then
707  return TRUE
708  end if
709 
710  '' allowed?
711  if( stk->allowmask and allowmask ) then
712  return TRUE
713  end if
714 
715  '' error..
716  if( fbIsModLevel( ) = FALSE ) then
717  errmsg = FB_ERRMSG_ILLEGALINSIDEASUB
718  else
719  if( symbIsGlobalNamespc( ) ) then
720  if( stk->id = FB_TK_SELECT ) then
721  errmsg = FB_ERRMSG_ILLEGALINSIDESELECT
722  else
723  errmsg = FB_ERRMSG_ILLEGALINSIDEASCOPE
724  end if
725  else
726  errmsg = FB_ERRMSG_ILLEGALINSIDEANAMESPC
727  end if
728  end if
729 
730  errReport( errmsg )
731 
732  function = FALSE
733 end function
734