FreeBASIC  0.91.0
parser-identifier.bas
Go to the documentation of this file.
1 '' complex (scoped with namespace and/or class) identifiers parsing
2 ''
3 '' chng: may/2006 written [v1ctor]
4 
5 
6 #include once "fb.bi"
7 #include once "fbint.bi"
8 #include once "parser.bi"
9 
10 '':::::
12 
13  do
14  lexSkipToken( LEXCHECK_NOPERIOD )
15 
16  '' '.'?
17  if( lexGetToken( ) <> CHAR_DOT ) then
18  exit do
19  end if
20 
21  select case as const lexGetClass()
22  case FB_TKCLASS_IDENTIFIER, FB_TKCLASS_KEYWORD, FB_TKCLASS_QUIRKWD
23 
24  case else
25  exit do
26  end select
27  loop
28 
29 end sub
30 
31 '':::::
32 function hGlobalId _
33  ( _
34  byval options as FB_IDOPT = FB_IDOPT_SHOWERROR _
35  ) as FBSYMCHAIN ptr
36 
37  function = NULL
38 
39  '' another '.'?
40  if( lexGetLookAhead( 1, LEXCHECK_NOPERIOD ) = CHAR_DOT ) then
41  '' skip the first '.'
42  lexSkipToken( LEXCHECK_NOPERIOD )
43 
44  else
45  '' inside a WITH block, a single '.' is ambiguous..
46  if( parser.stmt.with.sym <> NULL ) then
47  exit function
48  end if
49  end if
50 
51  if( (options and FB_IDOPT_ISDECL) <> 0 ) then
52  '' different name spaces?
53  if( symbIsGlobalNamespc( ) = FALSE ) then
54  errReport( FB_ERRMSG_DECLOUTSIDENAMESPC )
55  end if
56  end if
57 
58  '' skip the '.'
59  lexSkipToken( LEXCHECK_NOPERIOD )
60 
61  '' not an ID?
62  select case lexGetClass( )
63  case FB_TKCLASS_IDENTIFIER, FB_TKCLASS_QUIRKWD
64 
65  case else
66  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
67  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
68  end if
69  exit function
70  end select
71 
72  function = symbLookupAt( @symbGetGlobalNamespc( ), _
73  lexGetText( ), _
74  FALSE, _
75  TRUE )
76 
77 end function
78 
79 '':::::
80 #macro hCheckDecl _
81  ( _
83  parent, _
84  chain_, _
85  options _
86  )
87 
88  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
89  '' declaration?
90  if( (options and FB_IDOPT_ISDECL) <> 0 ) then
91  if( base_parent <> NULL ) then
92  '' different parents?
93  if( symbGetParent( base_parent ) <> symbGetCurrentNamespc( ) ) then
94  errReport( FB_ERRMSG_DECLOUTSIDENAMESPC )
95  return NULL
96  end if
97  end if
98 
99  '' not a decl..
100  else
101  '' check for ambiguous access (dup symbols in different imported namespaces)
102  if( chain_ <> NULL ) then
103  '' same symbol found in more than one hash tb?
104  if( chain_->next <> NULL ) then
105  '' imported namespace?
106  if( chain_->isimport ) then
107  dim as FBSYMBOL ptr ns = symbGetNamespace( chain_->sym ), ns2 = symbGetNamespace( chain_->next->sym )
108  '' first symbol declared in other namespace?
109  if( iif( parent, ns <> parent, ns <> ns2 ) ) then
110  '' more than one imported symbol
111  errReport( FB_ERRMSG_AMBIGUOUSSYMBOLACCESS )
112  '' (don't return NULL or a new variable would be implicitly created)
113  end if
114  end if
115  end if
116  end if
117  end if
118  end if
119 
120 #endmacro
121 
122 function hIsStructAllowed _
123  ( _
124  byval sym as FBSYMBOL ptr, _
125  byval options as FB_IDOPT _
126  ) as integer
127 
128  if( (options and FB_IDOPT_ALLOWSTRUCT) = 0 ) then
129  return FALSE
130  end if
131 
132  '' Ordinary/non-class struct? Won't ever be used as namespace prefix,
133  '' since it doesn't have any methods/static member vars
134  if( symbGetIsUnique( sym ) = FALSE ) then
135  return FALSE
136  end if
137 
138  '' If it's a variable declaration, then the UDT must have STATIC member
139  '' vars, otherwise it couldn't be used as namespace prefix here.
140  '' (this allows any other UDTs to be used as var names)
141  if( options and FB_IDOPT_ISVAR ) then
142  return symbGetUdtHasStaticVar( sym )
143  end if
144 
145  function = TRUE
146 end function
147 
148 '':::::
149 '' Identifier = (ID{namespace|class} '.')* ID
150 '' | ID ('.' ID)* .
151 ''
152 function cIdentifier _
153  ( _
154  byref base_parent as FBSYMBOL ptr, _
155  byval options as FB_IDOPT _
156  ) as FBSYMCHAIN ptr
157 
158  assert((options and FB_IDOPT_DONTCHKPERIOD) = 0)
159 
160  dim as FBSYMCHAIN ptr chain_ = any
161  dim as FBSYMBOL ptr parent = any
162 
163  base_parent = NULL
164 
165  chain_ = lexGetSymChain( )
166 
167  if( fbLangOptIsSet( FB_LANG_OPT_NAMESPC ) = FALSE ) then
168  return chain_
169  end if
170 
171  if( chain_ = NULL ) then
172  '' '.'?
173 
174  if( lexGetToken( ) <> CHAR_DOT ) then
175  return NULL
176  end if
177 
178  chain_ = hGlobalId( options )
179  if( chain_ = NULL ) then
180  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
181  errReportUndef( FB_ERRMSG_UNDEFINEDSYMBOL, lexGetText( ) )
182  else
183  hSkipSymbol( )
184  end if
185 
186  return NULL
187  end if
188  end if
189 
190  parent = NULL
191 
192  do
193  dim as FBSYMBOL ptr sym = chain_->sym
194 
195  select case as const symbGetClass( sym )
196  case FB_SYMBCLASS_NAMESPACE, FB_SYMBCLASS_CLASS, FB_SYMBCLASS_ENUM
197 
198  case FB_SYMBCLASS_STRUCT
199  if( hIsStructAllowed( sym, options ) = FALSE ) then
200  exit do
201  end if
202 
203  case FB_SYMBCLASS_TYPEDEF
204  '' typedef of a TYPE/CLASS?
205  select case( symbGetType( sym ) )
206  case FB_DATATYPE_STRUCT
207  sym = symbGetSubtype( sym )
208 
209  if( hIsStructAllowed( sym, options ) = FALSE ) then
210  exit do
211  end if
212 
213  case FB_DATATYPE_ENUM ', FB_DATATYPE_CLASS
214  sym = symbGetSubtype( sym )
215 
216  case else
217  exit do
218  end select
219 
220  case else
221  exit do
222  end select
223 
224  '' check visibility (of the UDT only, because symbols can be
225  '' overloaded or the names duplicated, so that check can only
226  '' be done by specific functions)
227  if( parent <> NULL ) then
228  if( symbCheckAccess( sym ) = FALSE ) then
229  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
230  errReport( FB_ERRMSG_ILLEGALMEMBERACCESS )
231  end if
232  end if
233  end if
234 
235  '' '.'?
236  if( lexGetLookAhead( 1, LEXCHECK_NOPERIOD ) <> CHAR_DOT ) then
237  '' if it's a namespace, the '.' is obligatory, the
238  '' namespace itself isn't a composite type
239  '' The only exception to that is namespaces appearing
240  '' in preprocessor expressions in #ifdef or #undef etc.
241  '' Those don't pass FB_IDOPT_SHOWERROR, and they skip
242  '' this last namespace id manually (like any other id),
243  '' because for them, this is not a syntax error.
244  if( symbGetClass( sym ) = FB_SYMBCLASS_NAMESPACE ) then
245  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
246  '' skip id
247  lexSkipToken( LEXCHECK_NOPERIOD )
248  errReport( FB_ERRMSG_EXPECTEDPERIOD )
249  end if
250  end if
251 
252  exit do
253  end if
254 
255  '' skip id
256  lexSkipToken( LEXCHECK_NOPERIOD )
257 
258  '' skip '.'
259  lexSkipToken( LEXCHECK_NOPERIOD )
260 
261  parent = sym
262 
263  if( base_parent = NULL ) then
265  end if
266 
267  '' ID
268  select case as const lexGetClass( )
269  case FB_TKCLASS_IDENTIFIER, FB_TKCLASS_QUIRKWD
270 
271  case FB_TKCLASS_OPERATOR, FB_TKCLASS_KEYWORD
272  if( (options and FB_IDOPT_ISOPERATOR ) <> 0 ) then
273  exit do
274  end if
275 
276  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
277  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
278  end if
279 
280  return NULL
281 
282  case else
283  '' Allow '[' for '[]' operator overloads, it's not part
284  '' of FB_TKCLASS_OPERATOR since it's not a real op.
285  if( lexGetToken( ) = CHAR_LBRACKET ) then
286  if( (options and FB_IDOPT_ISOPERATOR ) <> 0 ) then
287  exit do
288  end if
289  end if
290 
291  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
292  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
293  end if
294 
295  return NULL
296  end select
297 
298  '' look up
299  chain_ = symbLookupAt( parent, lexGetText( ), FALSE )
300  if( chain_ = NULL ) then
301  if( (options and FB_IDOPT_SHOWERROR) <> 0 ) then
302  errReportUndef( FB_ERRMSG_UNDEFINEDSYMBOL, lexGetText( ) )
303  else
304  hSkipSymbol( )
305  end if
306 
307  return NULL
308  end if
309 
310  '' check access to non-static members
311  if( (options and FB_IDOPT_CHECKSTATIC) <> 0 ) then
312  '' struct or class?
313  select case symbGetClass( parent )
314  case FB_SYMBCLASS_STRUCT, FB_SYMBCLASS_CLASS
315  '' for each symbol (because dups..)
316  dim as FBSYMCHAIN ptr iter = chain_
317  do
318  dim as FBSYMBOL ptr sym = iter->sym
319  do
320  '' field, never static..
321  if( symbGetClass( sym ) = FB_SYMBCLASS_FIELD ) then
322  errReport( FB_ERRMSG_ACCESSTONONSTATICMEMBER )
323  exit do, do
324  end if
325 
326  sym = sym->hash.next
327  loop while( sym <> NULL )
328 
329  iter = symbChainGetNext( iter )
330  loop while( iter <> NULL )
331  end select
332  end if
333  loop
334 
335  ''
336  hCheckDecl( base_parent, parent, chain_, options )
337 
338  function = chain_
339 
340 end function
341 
342 '':::::
343 '' ParentId = ID{namespace|class} ('.' ID{namespace|class})* .
344 ''
345 function cParentId _
346  ( _
347  byval options as FB_IDOPT _
348  ) as FBSYMBOL ptr
349 
350  dim as FBSYMCHAIN ptr chain_ = any
351  dim as FBSYMBOL ptr sym = any, parent = any, base_parent = any
352 
353  if( fbLangOptIsSet( FB_LANG_OPT_NAMESPC ) = FALSE ) then
354  return NULL
355  end if
356 
357  chain_ = lexGetSymChain( )
358  if( chain_ = NULL ) then
359  '' '.'?
360  if( lexGetToken( ) = CHAR_DOT ) then
361  chain_ = hGlobalId( )
362  end if
363  end if
364 
365  sym = NULL
366  parent = NULL
367  base_parent = NULL
368 
369  do while( chain_ <> NULL )
370 
371  sym = chain_->sym
372  select case as const symbGetClass( sym )
373  case FB_SYMBCLASS_NAMESPACE, FB_SYMBCLASS_CLASS, FB_SYMBCLASS_ENUM
374 
375  case FB_SYMBCLASS_STRUCT
376  if( hIsStructAllowed( sym, options ) = FALSE ) then
377  sym = parent
378  exit do
379  end if
380 
381  case FB_SYMBCLASS_TYPEDEF
382  '' typedef of a TYPE/CLASS?
383  select case( symbGetType( sym ) )
384  case FB_DATATYPE_STRUCT
385  sym = symbGetSubtype( sym )
386 
387  if( hIsStructAllowed( sym, options ) = FALSE ) then
388  sym = parent
389  exit do
390  end if
391 
392  case FB_DATATYPE_ENUM ', FB_DATATYPE_CLASS
393  sym = symbGetSubtype( sym )
394 
395  case else
396  sym = parent
397  exit do
398  end select
399 
400  case else
401  sym = parent
402  exit do
403  end select
404 
405  '' check visibility
406  if( parent <> NULL ) then
407  if( symbCheckAccess( sym ) = FALSE ) then
408  errReport( FB_ERRMSG_ILLEGALMEMBERACCESS )
409  end if
410  end if
411 
412  '' '.'?
413  if( lexGetLookAhead( 1, LEXCHECK_NOPERIOD ) <> CHAR_DOT ) then
414  '' skip id
415  lexSkipToken( LEXCHECK_NOPERIOD )
416 
417  if( (options and FB_IDOPT_DONTCHKPERIOD) <> 0 ) then
418  exit do
419  end if
420 
421  errReport( FB_ERRMSG_EXPECTEDPERIOD )
422  exit do
423  end if
424 
425  '' skip id
426  lexSkipToken( LEXCHECK_NOPERIOD )
427 
428  '' skip '.'
429  lexSkipToken( LEXCHECK_NOPERIOD )
430 
431  parent = sym
432 
433  if( base_parent = NULL ) then
435  end if
436 
437  '' ID
438  select case as const lexGetClass( )
439  case FB_TKCLASS_IDENTIFIER, FB_TKCLASS_QUIRKWD
440 
441  case FB_TKCLASS_OPERATOR, FB_TKCLASS_KEYWORD
442  if( (options and FB_IDOPT_ISOPERATOR ) <> 0 ) then
443  exit do
444  end if
445 
446  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
447  exit do
448 
449  case else
450  '' Allow '[' for '[]' operator overloads, it's not part
451  '' of FB_TKCLASS_OPERATOR since it's not a real op.
452  if( lexGetToken( ) = CHAR_LBRACKET ) then
453  if( (options and FB_IDOPT_ISOPERATOR ) <> 0 ) then
454  exit do
455  end if
456  end if
457 
458  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
459  exit do
460  end select
461 
462  chain_ = symbLookupAt( sym, lexGetText( ), FALSE )
463  loop
464 
465  ''
466  hCheckDecl( base_parent, parent, chain_, options )
467 
468  function = sym
469 
470 end function
471 
473  '' Parse namespace prefix(es) on an identifier in a declaration,
474  '' then complain if it doesn't match the current namespace.
475  '' This is part of requiring declarations to be written inside a
476  '' namespace block in order to add them to a namespace.
477 
478  dim as FBSYMBOL ptr s = cParentId()
479  if( s = NULL ) then
480  return
481  end if
482 
483  select case symbGetClass( s )
484  case FB_SYMBCLASS_NAMESPACE
485  if( s <> symbGetCurrentNamespc( ) ) then
486  errReport( FB_ERRMSG_DECLOUTSIDENAMESPC )
487  end if
488 
489  case FB_SYMBCLASS_CLASS
490  if( s <> symbGetCurrentNamespc( ) ) then
491  errReport( FB_ERRMSG_DECLOUTSIDECLASS )
492  end if
493 
494  end select
495 end sub
496