FreeBASIC  0.91.0
parser-quirk-on.bas
Go to the documentation of this file.
1 '' quirk conditional statements (ON ... GOTO|GOSUB) 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 '':::::
13 function cGOTBStmt _
14  ( _
15  byval expr as ASTNODE ptr, _
16  byval isgoto as integer _
17  ) as integer
18 
19  dim as integer l = any
20  dim as FBSYMBOL ptr sym = any, exitlabel = any
21  dim as ulongint values(0 to FB_MAXGOTBITEMS-1) = any
22  dim as FBSYMBOL ptr labels(0 to FB_MAXGOTBITEMS-1) = any
23  dim as FBSYMCHAIN ptr chain_ = any
24  dim as FBSYMBOL ptr base_parent = any
25 
26  function = FALSE
27 
28  '' ON expr GOTO label1, label2, etc.
29  '' works similar to a
30  '' SELECT CASE AS CONST expr
31  '' CASE 1
32  '' CASE 2
33  '' etc.
34  '' it also uses a jump table, however the values are "preset",
35  '' in contrast to SELECT where they're given via CASE statements.
36  '' expr = 1 -> label1
37  '' expr = 2 -> label2
38  '' etc.
39  '' This means: minval = 1, maxval = labelcount.
40 
41  '' convert to uinteger if needed
42  if( astGetDataType( expr ) <> FB_DATATYPE_UINT ) then
43  expr = astNewCONV( FB_DATATYPE_UINT, NULL, expr )
44  end if
45 
46  '' store expression into a temp var
47  sym = symbAddTempVar( FB_DATATYPE_UINT )
48  expr = astNewASSIGN( astNewVAR( sym ), expr )
49  if( expr = NULL ) then
50  exit function
51  end if
52  astAdd( expr )
53 
54  '' read labels
55  l = 0
56  do
57  '' Label
58  select case lexGetClass( )
59  case FB_TKCLASS_NUMLITERAL, FB_TKCLASS_IDENTIFIER
60  chain_ = cIdentifier( base_parent )
61 
62  '' Not not too many target labels yet?
63  if( l < FB_MAXGOTBITEMS ) then
64  labels(l) = symbFindByClass( chain_, FB_SYMBCLASS_LABEL )
65  if( labels(l) = NULL ) then
66  labels(l) = symbAddLabel( lexGetText( ), FB_SYMBOPT_CREATEALIAS )
67  end if
68  elseif( l = FB_MAXGOTBITEMS ) then '' (Only show the error once)
69  errReport( FB_ERRMSG_TOOMANYLABELS )
70  '' Error recovery: continue parsing all labels, but don't add
71  '' them to the table anymore
72  end if
73 
74  lexSkipToken( )
75 
76  case else
77  errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
78  if( l < FB_MAXGOTBITEMS ) then
79  '' error recovery: fake an label
80  labels(l) = symbAddLabel( symbUniqueLabel( ), FB_SYMBOPT_NONE )
81  end if
82  end select
83 
84  l += 1
85  loop while( hMatch( CHAR_COMMA ) )
86 
87  '' Too many target labels?
88  if( l >= FB_MAXGOTBITEMS ) then
89  l = FB_MAXGOTBITEMS - 1
90  end if
91 
92  exitlabel = symbAddLabel( NULL )
93 
94  '' Fill beginning of values buffer with the 1,2,3,4,... values
95  for i as integer = 0 to l - 1
96  values(i) = i + 1
97  next
98 
99  '' labelcount = l, minval = 1, maxval = l
100  expr = astBuildJMPTB( sym, @values(0), @labels(0), l, exitlabel, 1, l )
101 
102  if( isgoto ) then
103  astAdd( expr )
104  else
105  astGosubAddJumpPtr( parser.currproc, expr, exitlabel )
106  end if
107 
108  '' emit exit label
109  astAdd( astNewLABEL( exitlabel ) )
110 
111  function = TRUE
112 end function
113 
114 '':::::
115 ''OnStmt = ON LOCAL? (Keyword | Expression) (GOTO|GOSUB) Label .
116 ''
117 function cOnStmt _
118  ( _
119  _
120  ) as integer
121 
122  dim as ASTNODE ptr expr = any
123  dim as integer isgoto = any, islocal = any, isrestore = any
124  dim as FBSYMBOL ptr label = any
125  dim as FBSYMCHAIN ptr chain_ = any
126  dim as FBSYMBOL ptr base_parent = any
127 
128  function = FALSE
129 
130  '' ON
131  lexSkipToken( )
132 
133  '' LOCAL?
134  if( hMatch( FB_TK_LOCAL ) ) then
135  if( fbIsModLevel( ) ) then
136  errReport( FB_ERRMSG_SYNTAXERROR, TRUE )
137  exit function
138  end if
139  islocal = TRUE
140  else
141  islocal = FALSE
142  end if
143 
144  '' ERROR | Expression
145  expr = NULL
146  if( lexGetToken( ) = FB_TK_ERROR ) then
147  lexSkipToken( )
148  else
149  hMatchExpressionEx( expr, FB_DATATYPE_INTEGER )
150  end if
151 
152  '' GOTO|GOSUB
153  select case lexGetToken( )
154  case FB_TK_GOTO
155  lexSkipToken( )
156  isgoto = TRUE
157 
158  case FB_TK_GOSUB
159  '' can't do GOSUB with ON ERROR
160  if( expr = NULL ) then
161  errReport( FB_ERRMSG_SYNTAXERROR )
162  '' error recovery: fake an expr
163  expr = astNewCONSTi( 0 )
164  end if
165 
166  if( fbLangOptIsSet( FB_LANG_OPT_GOSUB ) = FALSE ) then
167  errReportNotAllowed( FB_LANG_OPT_GOSUB )
168  hSkipStmt( )
169  return TRUE
170  end if
171 
172  '' gosub allowed by OPTION GOSUB?
173  if( env.opt.gosub ) then
174  lexSkipToken( )
175  isgoto = FALSE
176  else
177  '' GOSUB is allowed, but hasn't been enabled with OPTION GOSUB
178  errReport( FB_ERRMSG_SYNTAXERROR )
179  hSkipStmt( )
180  return TRUE
181  end if
182 
183  case else
184  errReport( FB_ERRMSG_SYNTAXERROR )
185  '' error recovery: skip stmt
186  hSkipStmt( )
187  return TRUE
188  end select
189 
190  '' on error?
191  if( expr = NULL ) then
192  isrestore = FALSE
193  '' ON ERROR GOTO 0?
194  if( lexGetClass( ) = FB_TKCLASS_NUMLITERAL ) then
195  if( *lexGetText( ) = "0" ) then
196  lexSkipToken( )
197  isrestore = TRUE
198  end if
199  end if
200 
201  if( isrestore = FALSE ) then
202  '' Label
203  chain_ = cIdentifier( base_parent )
204 
205  label = symbFindByClass( chain_, FB_SYMBCLASS_LABEL )
206  if( label = NULL ) then
207  label = symbAddLabel( lexGetText( ), FB_SYMBOPT_CREATEALIAS )
208  end if
209 
210  lexSkipToken( )
211 
212  expr = astNewADDROF( astNewVAR( label ) )
213  rtlErrorSetHandler( expr, (islocal = TRUE) )
214 
215  else
216  rtlErrorSetHandler( astNewCONSTi( NULL, FB_DATATYPE_UINT ), (islocal = TRUE) )
217  end if
218 
219  function = TRUE
220 
221  else
222  function = cGOTBStmt( expr, isgoto )
223  end if
224 
225 end function
226