FreeBASIC  0.91.0
parser-quirk-array.bas
Go to the documentation of this file.
1 '' quirk array statements (ERASE, SWAP) and functions (LBOUND, UBOUND) 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 "rtl.bi"
10 #include once "ast.bi"
11 
12 '' EraseStmt = ERASE ID (',' ID)*
13 function cEraseStmt() as integer
14  lexSkipToken( )
15 
16  do
17  var expr = cVarOrDeref( FB_VAREXPROPT_NOARRAYCHECK )
18  if( expr = NULL ) then
19  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
20  hSkipUntil( CHAR_COMMA )
21  else
22  '' ugly hack to deal with arrays w/o indexes
23  if( astIsNIDXARRAY( expr ) ) then
24  var expr2 = astGetLeft( expr )
25  astDelNode( expr )
26  expr = expr2
27  end if
28 
29  '' array?
30  var s = astGetSymbol( expr )
31  if( s <> NULL ) then
32  if( symbIsArray( s ) = FALSE ) then
33  s = NULL
34  end if
35  end if
36 
37  if( s = NULL ) then
38  errReport( FB_ERRMSG_EXPECTEDARRAY )
39  hSkipUntil( CHAR_COMMA )
40  else
41  if( typeIsConst( astGetFullType( expr ) ) ) then
42  errReport( FB_ERRMSG_CONSTANTCANTBECHANGED )
43  end if
44 
45  '' ERASE frees dynamic arrays (destruct only),
46  '' but re-initializes static arrays (destruct and construct).
47  if( symbGetIsDynamic( s ) ) then
48  astAdd( rtlArrayErase( expr, TRUE, TRUE ) )
49  else
50  astAdd( rtlArrayClear( expr ) )
51  end if
52  end if
53  end if
54 
55  '' ','?
56  loop while( hMatch( CHAR_COMMA ) )
57 
58  function = TRUE
59 end function
60 
61 function hMakeRef _
62  ( _
63  byval t as ASTNODE ptr, _
64  byref expr as ASTNODE ptr _
65  ) as ASTNODE ptr
66 
67  '' This is similar to astRemSideFx(), it creates a temp var, assigns the
68  '' expression with side-effects to that, and replaces the expression
69  '' with an access to that temp var. Effectively this causes the
70  '' expression with side-effects to be used only once.
71  ''
72  '' However, here we're taking a reference to the expression instead of
73  '' storing its result, otherwise SWAP would overwrite the temp var,
74  '' not the actual data. This also means LINK nodes must be used,
75  '' because we don't support references across statements...
76 
77  '' var ref
78  var ref = symbAddTempVar( typeAddrOf( astGetFullType( expr ) ), _
79  astGetSubtype( expr ) )
80 
81  '' ref = @expr
82  function = astNewLINK( t, _
83  astNewASSIGN( astNewVAR( ref ), astNewADDROF( expr ) ) )
84 
85  '' Use *ref instead of the original expr
86  expr = astNewDEREF( astNewVAR( ref ) )
87 
88 end function
89 
90 '' SwapStmt = SWAP VarOrDeref ',' VarOrDeref
91 function cSwapStmt() as integer
92  lexSkipToken( )
93 
94  var l = cVarOrDeref( FB_VAREXPROPT_ISASSIGN )
95  if( l = NULL ) then
96  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
97  hSkipStmt( )
98  return TRUE
99  end if
100 
101  if( astIsConstant( l ) ) then
102  errReport( FB_ERRMSG_CONSTANTCANTBECHANGED, TRUE )
103  end if
104 
105  hMatchCOMMA( )
106 
107  var r = cVarOrDeref( FB_VAREXPROPT_ISASSIGN )
108  if( r = NULL ) then
109  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
110  astDelTree( l )
111  hSkipStmt( )
112  return TRUE
113  end if
114 
115  if( astIsConstant( r ) ) then
116  errReport( FB_ERRMSG_CONSTANTCANTBECHANGED, TRUE )
117  end if
118 
119  dim as integer ldtype = astGetDataType( l )
120  dim as integer rdtype = astGetDataType( r )
121 
122  select case ldtype
123  case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR
124  select case rdtype
125  case FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR
126  function = rtlStrSwap( l, r )
127  case else
128  errReport( FB_ERRMSG_TYPEMISMATCH )
129  end select
130  exit function
131  end select
132 
133  if( ldtype = FB_DATATYPE_WCHAR ) then
134  if( rdtype = FB_DATATYPE_WCHAR ) then
135  function = rtlWstrSwap( l, r )
136  else
137  errReport( FB_ERRMSG_TYPEMISMATCH )
138  end if
139  exit function
140  end if
141 
142  '' Check whether a "raw" assignment (no operator overloads) would work.
143  '' Must check both l = r and r = l due to inheritance with UDTs which
144  '' can allow one but not the other (and perhaps there even are other
145  '' cases with similar effect).
146  if( (astCheckASSIGN( l, r ) = FALSE) or _
147  (astCheckASSIGN( r, l ) = FALSE) ) then
148  errReport( FB_ERRMSG_TYPEMISMATCH )
149  exit function
150  end if
151 
152  if( (ldtype = FB_DATATYPE_STRUCT) or (rdtype = FB_DATATYPE_STRUCT) ) then
153  '' This should all be guaranteed by the assignment check above
154  assert( ldtype = FB_DATATYPE_STRUCT )
155  assert( rdtype = FB_DATATYPE_STRUCT )
156  assert( astGetSubtype( l ) = astGetSubtype( r ) )
157  return rtlMemSwap( l, r )
158  end if
159 
160  ''
161  '' For the ASM backend SWAP can be done with PUSH/POP, if...
162  ''
163  '' - it's on integers or floats (structs handled above)
164  ''
165  '' - neither side is a bitfield (for those we always have to use a
166  '' temp var, to get the bitfield accesses built properly)
167  ''
168  '' - both side's types have the same size, otherwise we may push 4
169  '' bytes and pop 8, or similar.
170  ''
171  '' - it's either both integer or both float, so we don't swap between
172  '' integer and float this way. The ASSIGN converts differently than
173  '' the POP, so you'd get different results depending on whether it's
174  '' <SWAP i, f> or <SWAP f, i>.
175  ''
176  dim as integer use_pushpop = TRUE
177  use_pushpop and= (env.clopt.backend = FB_BACKEND_GAS)
178  use_pushpop and= (typeGetSize( ldtype ) = typeGetSize( rdtype ))
179  use_pushpop and= (typeGetClass( ldtype ) = typeGetClass( rdtype ))
180  use_pushpop and= (astIsBITFIELD( l ) = FALSE)
181  use_pushpop and= (astIsBITFIELD( r ) = FALSE)
182 
183  '' A scope to enclose the temp vars
184  dim as ASTNODE ptr scopenode = astScopeBegin( )
185  dim as ASTNODE ptr t = NULL
186 
187  '' Side effects? Then use references to be able to read/write...
188  if( astIsClassOnTree( AST_NODECLASS_CALL, l ) <> NULL ) then
189  t = hMakeRef( t, l )
190  end if
191 
192  if( astIsClassOnTree( AST_NODECLASS_CALL, r ) <> NULL ) then
193  t = hMakeRef( t, r )
194  end if
195 
196  if( use_pushpop ) then
197  '' push clone( l )
198  t = astNewLINK( t, astNewSTACK( AST_OP_PUSH, astCloneTree( l ) ) )
199 
200  '' l = clone( r )
201  t = astNewLINK( t, astNewASSIGN( l, astCloneTree( r ) ) )
202 
203  '' pop r
204  t = astNewLINK( t, astNewSTACK( AST_OP_POP, r ) )
205  else
206  '' var temp = clone( l )
207  var temp = symbAddTempVar( astGetFullType( l ), astGetSubtype( l ) )
208  t = astNewLINK( t, astNewASSIGN( astNewVAR( temp ), astCloneTree( l ) ) )
209 
210  '' l = clone( r )
211  t = astNewLINK( t, astNewASSIGN( l, astCloneTree( r ) ) )
212 
213  '' r = temp
214  t = astNewLINK( t, astNewASSIGN( r, astNewVAR( temp ) ) )
215  end if
216 
217  astAdd( t )
218  astScopeEnd( scopenode )
219  function = TRUE
220 end function
221 
222 '':::::
223 ''cArrayFunct = (LBOUND|UBOUND) '(' ID (',' Expression)? ')' .
224 ''
225 function cArrayFunct(byval tk as FB_TOKEN) as ASTNODE ptr
226  dim as ASTNODE ptr arrayexpr = any, dimexpr = any
227  dim as FBSYMBOL ptr s = any
228 
229  function = NULL
230 
231  select case tk
232  '' (LBOUND|UBOUND) '(' ID (',' Expression)? ')'
233  case FB_TK_LBOUND, FB_TK_UBOUND
234  lexSkipToken( )
235 
236  '' '('
237  hMatchLPRNT( )
238 
239  '' ID
240  arrayexpr = cVarOrDeref( FB_VAREXPROPT_NOARRAYCHECK )
241  if( arrayexpr = NULL ) then
242  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
243  '' error recovery: skip until next ')' and fake an expr
244  hSkipUntil( CHAR_RPRNT, TRUE )
245  return astNewCONSTi( 0 )
246  end if
247 
248  '' ugly hack to deal with arrays w/o indexes
249  if( astIsNIDXARRAY( arrayexpr ) ) then
250  dim as ASTNODE ptr expr = astGetLeft( arrayexpr )
251  astDelNode( arrayexpr )
252  arrayexpr = expr
253  end if
254 
255  '' array?
256  s = astGetSymbol( arrayexpr )
257  if( s <> NULL ) then
258  if( symbIsArray( s ) = FALSE ) then
259  s = NULL
260  end if
261  end if
262 
263  if( s = NULL ) then
264  errReport( FB_ERRMSG_EXPECTEDARRAY, TRUE )
265  '' error recovery: skip until next ')' and fake an expr
266  hSkipUntil( CHAR_RPRNT, TRUE )
267  return astNewCONSTi( 0 )
268  end if
269 
270  '' (',' Expression)?
271  if( hMatch( CHAR_COMMA ) ) then
272  hMatchExpressionEx( dimexpr, FB_DATATYPE_INTEGER )
273  else
274  dimexpr = astNewCONSTi( 1 )
275  end if
276 
277  '' ')'
278  hMatchRPRNT( )
279 
280  function = astBuildArrayBound( arrayexpr, dimexpr, tk )
281  end select
282 end function
283