FreeBASIC  0.91.0
parser-compound-select.bas
Go to the documentation of this file.
1 '' SELECT CASE [AS CONST]..CASE..END SELECT 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 "rtl.bi"
11 
17 end enum
18 
19 const FB_MAXCASEEXPR = 1024
20 
23  op as integer
24  expr1 as ASTNODE ptr
25  expr2 as ASTNODE ptr
26 end type
27 
28 type FBCTX
29  base as integer
31 end type
32 
33 '' globals
34  dim shared ctx as FBCTX
35 
37  ctx.base = 0
38 end sub
39 
41 end sub
42 
43 '' SelectStatement = SELECT CASE (AS CONST)? Expression .
45  dim as ASTNODE ptr expr = any
46  dim as integer dtype = any, options = any
47  dim as FBSYMBOL ptr sym = any, el = any, subtype = any
48  dim as FB_CMPSTMTSTK ptr stk = any
49 
50  '' SELECT
51  lexSkipToken( )
52 
53  '' CASE
54  if( hMatch( FB_TK_CASE ) = FALSE ) then
55  errReport( FB_ERRMSG_EXPECTEDCASE )
56  end if
57 
58  '' AS?
59  if( lexGetToken( ) = FB_TK_AS ) then
60  lexSkipToken( )
61 
62  '' CONST?
63  if( hMatch( FB_TK_CONST ) ) then
65  return
66  end if
67 
68  errReport( FB_ERRMSG_SYNTAXERROR )
69  end if
70 
71  '' Open outer scope
72  '' This is used to enclose the temporary created below, to make sure
73  '' it's destroyed at the END SELECT, not later. And scoping the temp
74  '' also frees up its stack space later.
75  dim as ASTNODE ptr outerscopenode = astScopeBegin( )
76  if( outerscopenode = NULL ) then
77  errReport( FB_ERRMSG_RECLEVELTOODEEP )
78  end if
79 
80  '' Expression
81  expr = cExpression( )
82  if( expr = NULL ) then
83  errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
84  '' error recovery: fake an expr
85  expr = astNewCONSTi( 0 )
86  end if
87 
88  '' can't be an UDT
89  if( astGetDataType( expr ) = FB_DATATYPE_STRUCT ) then
90  errReport( FB_ERRMSG_INVALIDDATATYPES )
91  astDelTree( expr )
92  '' error recovery: fake an expr
93  expr = astNewCONSTi( 0 )
94  end if
95 
96  '' add exit label
97  el = symbAddLabel( NULL, FB_SYMBOPT_NONE )
98 
99  sym = NULL
100  dtype = astGetFullType( expr )
101  subtype = astGetSubType( expr )
102 
103  if( astIsVAR( expr ) ) then
104  '' No need to copy to a temp var when the expression is just
105  '' a var already (note: might be type-casted, so better use
106  '' the AST node's type, not the symbol's)
107  sym = astGetSymbol( expr )
108  assert( sym )
109  assert( symbIsTemp( sym ) = FALSE )
110  else
111  '' Store expression into a temp var
112  select case typeGet( dtype )
113  '' fixed-len or zstring? temp will be a var-len string..
114  case FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR
115  dtype = FB_DATATYPE_STRING
116  end select
117 
118  options = 0
119  if( fbLangOptIsSet( FB_LANG_OPT_SCOPE ) = FALSE ) then
120  options or= FB_SYMBOPT_UNSCOPE
121  end if
122 
123  '' not a wstring?
124  if( typeGet( dtype ) <> FB_DATATYPE_WCHAR ) then
125  '' dim temp as dtype = expr
126  sym = symbAddImplicitVar( dtype, subtype, options )
127 
128  '' Only need to clear if it's a string because of the
129  '' fb_StrDelete() calls at scope breaks; integers don't
130  '' have clean up, and UDTs aren't supported anyways.
131  '' This also silences the "branch crossing" warnings for
132  '' integers, they aren't needed since integer vars won't
133  '' be accessed anymore once a CASE body was reached,
134  '' unlike string temp vars and their fb_StrDelete().
135  if( typeGet( dtype ) <> FB_DATATYPE_STRING ) then
136  symbSetDontInit( sym )
137  end if
138 
139  if( options and FB_SYMBOPT_UNSCOPE ) then
140  '' Clear at procedure-level if needed,
141  '' and do a normal assignment here
142  astAddUnscoped( astNewDECL( sym, TRUE ) )
143  astAdd( astNewASSIGN( astNewVAR( sym ), expr ) )
144  else
145  astAdd( astNewDECL( sym, FALSE ) )
146  astAdd( astNewASSIGN( astNewVAR( sym ), expr, AST_OPOPT_ISINI ) )
147  end if
148  else
149  '' The wstring expression must be copied into a
150  '' dynamically allocated buffer, just like with string
151  '' expressions, so it can be preserved for comparison
152  '' at every CASE.
153 
154  '' dim temp as wstring ptr = expr
155  sym = symbAddImplicitVar( typeAddrOf( FB_DATATYPE_WCHAR ), NULL, options )
156 
157  '' Mark it as "dynamic wstring" so it will be
158  '' deallocated with fb_WstrDelete() at scope breaks
159  symbSetIsWstring( sym )
160 
161  if( options and FB_SYMBOPT_UNSCOPE ) then
162  '' Clear the pointer at procedure-level,
163  '' and do a normal assignment here
164  astAddUnscoped( astNewDECL( sym, TRUE ) )
165  astAdd( astBuildFakeWstringAssign( sym, expr ) )
166  else
167  '' Just the assignment, used as initializer
168  astAdd( astNewDECL( sym, FALSE ) )
169  astAdd( astBuildFakeWstringAssign( sym, expr, AST_OPOPT_ISINI ) )
170  end if
171  end if
172  end if
173 
174  '' push to stmt stack
175  stk = cCompStmtPush( FB_TK_SELECT, _
176  FB_CMPSTMT_MASK_NOTHING ) '' nothing allowed but CASE's
177  stk->select.isconst = FALSE
178  stk->select.sym = sym
179  stk->select.casecnt = 0
180  stk->select.cmplabel = symbAddLabel( NULL, FB_SYMBOPT_NONE )
181  stk->select.endlabel = el
182  stk->select.outerscopenode = outerscopenode
183 end sub
184 
185 '':::::
186 ''CaseExpression = (Expression (TO Expression)?)?
187 '' | (IS REL_OP Expression)? .
188 ''
189 sub hCaseExpression _
190  ( _
191  byref casectx as FBCASECTX, _
192  byval sym as FBSYMBOL ptr _
193  )
194 
195  casectx.op = AST_OP_EQ
196 
197  '' IS REL_OP Expression
198  if( lexGetToken( ) = FB_TK_IS ) then
199  lexSkipToken( )
200  casectx.op = hFBrelop2IRrelop( lexGetToken( ) )
201  lexSkipToken( )
202  casectx.typ = FB_CASETYPE_IS
203  else
204  casectx.typ = FB_CASETYPE_SINGLE
205  end if
206 
207  '' Expression
208  casectx.expr1 = cExpression( )
209  if( casectx.expr1 = NULL ) then
210  errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
211  '' error recovery: fake an expr
212  casectx.expr1 = astNewCONSTz( iif( symbGetIsWstring( sym ), _
213  FB_DATATYPE_WCHAR, _
214  symbGetType( sym ) ) )
215  end if
216 
217  '' TO Expression
218  if( lexGetToken( ) = FB_TK_TO ) then
219  lexSkipToken( )
220 
221  if( casectx.typ <> FB_CASETYPE_SINGLE ) then
222  errReport( FB_ERRMSG_SYNTAXERROR )
223  '' error recovery: skip until next ',', assume single
224  hSkipUntil( CHAR_COMMA )
225  casectx.typ = FB_CASETYPE_SINGLE
226  else
227  casectx.typ = FB_CASETYPE_RANGE
228  casectx.expr2 = cExpression( )
229  if( casectx.expr2 = NULL ) then
230  errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
231  '' error recovery: skip until next ',', assume single
232  hSkipUntil( CHAR_COMMA )
233  casectx.typ = FB_CASETYPE_SINGLE
234  end if
235  end if
236 
237  end if
238 end sub
239 
240 function hFlushCaseExpr _
241  ( _
242  byref casectx as FBCASECTX, _
243  byval sym as FBSYMBOL ptr, _
244  byval inilabel as FBSYMBOL ptr, _
245  byval nxtlabel as FBSYMBOL ptr, _
246  byval islast as integer _
247  ) as integer
248 
249  dim as ASTNODE ptr expr = any
250 
251  '' if it's the fake "dynamic wstring", do "if *tmp op expr"
252  #define NEWCASEVAR( sym ) _
253  iif( symbGetIsWstring( sym ), _
254  astBuildFakeWstringAccess( sym ), _
255  astNewVAR( sym ) )
256 
257  expr = NEWCASEVAR( sym )
258 
259  if( casectx.typ <> FB_CASETYPE_RANGE ) then
260  if( islast ) then
261  expr = astNewBOP( astGetInverseLogOp( casectx.op ), expr, _
262  casectx.expr1, nxtlabel, AST_OPOPT_NONE )
263  else
264  expr = astNewBOP( casectx.op, expr, _
265  casectx.expr1, inilabel, AST_OPOPT_NONE )
266  end if
267  else
268  expr = astNewBOP( AST_OP_LT, expr, casectx.expr1, nxtlabel, AST_OPOPT_NONE )
269  if( expr = NULL ) then
270  return FALSE
271  end if
272 
273  astAdd( expr )
274 
275  expr = NEWCASEVAR( sym )
276  if( islast ) then
277  expr = astNewBOP( AST_OP_GT, expr, casectx.expr2, nxtlabel, AST_OPOPT_NONE )
278  else
279  expr = astNewBOP( AST_OP_LE, expr, casectx.expr2, inilabel, AST_OPOPT_NONE )
280  end if
281  end if
282 
283  if( expr = NULL ) then
284  return FALSE
285  end if
286 
287  astAdd( expr )
288 
289  function = TRUE
290 end function
291 
292 '' SelectStmtNext = CASE (ELSE | (CaseExpression (',' CaseExpression)*)) .
294  dim as FBSYMBOL ptr il = any, nl = any
295  dim as integer cnt = any, i = any, cntbase = any
296  dim as FB_CMPSTMTSTK ptr stk = any
297 
298  stk = cCompStmtGetTOS( FB_TK_SELECT, FALSE )
299  if( stk = NULL ) then
300  errReport( FB_ERRMSG_CASEWITHOUTSELECT )
301  hSkipStmt( )
302  exit sub
303  end if
304 
305  '' ELSE already parsed?
306  if( stk->select.casecnt = -1 ) then
307  errReport( FB_ERRMSG_EXPECTEDENDSELECT )
308  end if
309 
310  '' default mask now allowed
311  cCompSetAllowmask( stk, FB_CMPSTMT_MASK_DEFAULT )
312 
313  '' AS CONST?
314  if( stk->select.isconst ) then
315  cSelConstStmtNext( stk )
316  exit sub
317  end if
318 
319  '' CASE
320  lexSkipToken( )
321 
322  '' end scope
323  if( stk->scopenode <> NULL ) then
324  astScopeEnd( stk->scopenode )
325  stk->scopenode = NULL
326  end if
327 
328  if( stk->select.casecnt > 0 ) then
329  '' break from block
330  astAdd( astNewBRANCH( AST_OP_JMP, stk->select.endlabel ) )
331 
332  astAdd( astNewLABEL( stk->select.cmplabel ) )
333  stk->select.cmplabel = symbAddLabel( NULL )
334  end if
335 
336  '' ELSE?
337  if( lexGetToken( ) = FB_TK_ELSE ) then
338  lexSkipToken( )
339 
340  '' begin scope
341  stk->scopenode = astScopeBegin( )
342 
343  stk->select.casecnt = -1
344 
345  exit sub
346  end if
347 
348  '' CaseExpression ((',' | TO) CaseExpression)*
349  cnt = 0
350  cntbase = ctx.base
351 
352  do
353  hCaseExpression( ctx.caseTB(cntbase + cnt), stk->select.sym )
354  cnt += 1
355 
356  if( lexGetToken( ) <> CHAR_COMMA ) then
357  exit do
358  end if
359 
360  lexSkipToken( )
361  loop
362 
363  ctx.base += cnt
364 
365  '' add block ini label
366  il = symbAddLabel( NULL )
367 
368  for i = 0 to cnt-1
369  if( i < cnt-1 ) then
370  '' add next label
371  nl = symbAddLabel( NULL, FB_SYMBOPT_NONE )
372  else
373  nl = stk->select.cmplabel
374  end if
375 
376  if( ctx.caseTB(cntbase+i).typ <> FB_CASETYPE_ELSE ) then
377  if( hFlushCaseExpr( ctx.caseTB(cntbase+i), stk->select.sym, _
378  il, nl, i = cnt-1 ) = FALSE ) then
379  errReport( FB_ERRMSG_INVALIDDATATYPES, TRUE )
380  end if
381  end if
382 
383  if( i < cnt-1 ) then
384  '' emit next label
385  astAdd( astNewLABEL( nl ) )
386  end if
387  next
388 
389  ctx.base -= cnt
390 
391  '' emit init block label
392  astAdd( astNewLABEL( il ) )
393 
394  '' begin scope
395  stk->scopenode = astScopeBegin( )
396 
397  stk->select.casecnt += 1
398 end sub
399 
400 '' SelectStmtEnd = END SELECT .
402  dim as FB_CMPSTMTSTK ptr stk = any
403 
404  stk = cCompStmtGetTOS( FB_TK_SELECT )
405  if( stk = NULL ) then
406  hSkipStmt( )
407  exit sub
408  end if
409 
410  '' no CASE's?
411  if( stk->select.casecnt = 0 ) then
412  errReport( FB_ERRMSG_EXPECTEDCASE )
413  end if
414 
415  '' AS CONST?
416  if( stk->select.isconst ) then
417  cSelConstStmtEnd( stk )
418  exit sub
419  end if
420 
421  '' END SELECT
422  lexSkipToken( )
423  lexSkipToken( )
424 
425  '' end scope
426  if( stk->scopenode <> NULL ) then
427  astScopeEnd( stk->scopenode )
428  end if
429 
430  '' emit end label
431  astAdd( astNewLABEL( stk->select.cmplabel ) )
432  astAdd( astNewLABEL( stk->select.endlabel ) )
433 
434  '' Close the outer scope block
435  if( stk->select.outerscopenode <> NULL ) then
436  astScopeEnd( stk->select.outerscopenode )
437  end if
438 
439  '' pop from stmt stack
440  cCompStmtPop( stk )
441 end sub
442