1 /*
2  *  Copyright 2001-2006 Adrian Thurston <thurston@complang.org>
3  *            2004 Erich Ocean <eric.ocean@ampede.com>
4  *            2005 Alan West <alan@alanz.com>
5  */
6 
7 /*  This file is part of Ragel.
8  *
9  *  Ragel is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  Ragel is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with Ragel; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #include "ragel.h"
25 #include "csipgoto.h"
26 #include "redfsm.h"
27 #include "gendata.h"
28 #include "bstmap.h"
29 
useAgainLabel()30 bool CSharpIpGotoCodeGen::useAgainLabel()
31 {
32 	return redFsm->anyRegActionRets() ||
33 			redFsm->anyRegActionByValControl() ||
34 			redFsm->anyRegNextStmt();
35 }
36 
GOTO(ostream & ret,int gotoDest,bool inFinish)37 void CSharpIpGotoCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish )
38 {
39 	ret << "{" << CTRL_FLOW() << "goto st" << gotoDest << ";}";
40 }
41 
CALL(ostream & ret,int callDest,int targState,bool inFinish)42 void CSharpIpGotoCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
43 {
44 	if ( prePushExpr != 0 ) {
45 		ret << "{";
46 		INLINE_LIST( ret, prePushExpr, 0, false );
47 	}
48 
49 	ret << "{" << STACK() << "[" << TOP() << "++] = " << targState <<
50 			"; " << CTRL_FLOW() << "goto st" << callDest << ";}";
51 
52 	if ( prePushExpr != 0 )
53 		ret << "}";
54 }
55 
CALL_EXPR(ostream & ret,GenInlineItem * ilItem,int targState,bool inFinish)56 void CSharpIpGotoCodeGen::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish )
57 {
58 	if ( prePushExpr != 0 ) {
59 		ret << "{";
60 		INLINE_LIST( ret, prePushExpr, 0, false );
61 	}
62 
63 	ret << "{" << STACK() << "[" << TOP() << "++] = " << targState << "; " << vCS() << " = (";
64 	INLINE_LIST( ret, ilItem->children, 0, inFinish );
65 	ret << "); " << CTRL_FLOW() << "goto _again;}";
66 
67 	if ( prePushExpr != 0 )
68 		ret << "}";
69 }
70 
RET(ostream & ret,bool inFinish)71 void CSharpIpGotoCodeGen::RET( ostream &ret, bool inFinish )
72 {
73 	ret << "{" << vCS() << " = " << STACK() << "[--" << TOP() << "];";
74 
75 	if ( postPopExpr != 0 ) {
76 		ret << "{";
77 		INLINE_LIST( ret, postPopExpr, 0, false );
78 		ret << "}";
79 	}
80 
81 	ret << CTRL_FLOW() << "goto _again;}";
82 }
83 
GOTO_EXPR(ostream & ret,GenInlineItem * ilItem,bool inFinish)84 void CSharpIpGotoCodeGen::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
85 {
86 	ret << "{" << vCS() << " = (";
87 	INLINE_LIST( ret, ilItem->children, 0, inFinish );
88 	ret << "); " << CTRL_FLOW() << "goto _again;}";
89 }
90 
NEXT(ostream & ret,int nextDest,bool inFinish)91 void CSharpIpGotoCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
92 {
93 	ret << vCS() << " = " << nextDest << ";";
94 }
95 
NEXT_EXPR(ostream & ret,GenInlineItem * ilItem,bool inFinish)96 void CSharpIpGotoCodeGen::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
97 {
98 	ret << vCS() << " = (";
99 	INLINE_LIST( ret, ilItem->children, 0, inFinish );
100 	ret << ");";
101 }
102 
CURS(ostream & ret,bool inFinish)103 void CSharpIpGotoCodeGen::CURS( ostream &ret, bool inFinish )
104 {
105 	ret << "(_ps)";
106 }
107 
TARGS(ostream & ret,bool inFinish,int targState)108 void CSharpIpGotoCodeGen::TARGS( ostream &ret, bool inFinish, int targState )
109 {
110 	ret << targState;
111 }
112 
BREAK(ostream & ret,int targState)113 void CSharpIpGotoCodeGen::BREAK( ostream &ret, int targState )
114 {
115 	/* FIXME: If this code generator is made active then BREAK generation
116 	 * needs to check csForced. */
117 	outLabelUsed = true;
118 	ret << "{" << P() << "++; " << vCS() << " = " << targState <<
119 			"; " << CTRL_FLOW() << "goto _out;}";
120 }
121 
IN_TRANS_ACTIONS(RedStateAp * state)122 bool CSharpIpGotoCodeGen::IN_TRANS_ACTIONS( RedStateAp *state )
123 {
124 	bool anyWritten = false;
125 
126 	/* Emit any transitions that have actions and that go to this state. */
127 	for ( int it = 0; it < state->numInTrans; it++ ) {
128 		RedTransAp *trans = state->inTrans[it];
129 		if ( trans->action != 0 && trans->labelNeeded ) {
130 			/* Remember that we wrote an action so we know to write the
131 			 * line directive for going back to the output. */
132 			anyWritten = true;
133 
134 			/* Write the label for the transition so it can be jumped to. */
135 			out << "tr" << trans->id << ":\n";
136 
137 			/* If the action contains a next, then we must preload the current
138 			 * state since the action may or may not set it. */
139 			if ( trans->action->anyNextStmt() )
140 				out << "	" << vCS() << " = " << trans->targ->id << ";\n";
141 
142 			/* Write each action in the list. */
143 			for ( GenActionTable::Iter item = trans->action->key; item.lte(); item++ )
144 				ACTION( out, item->value, trans->targ->id, false );
145 
146 			/* If the action contains a next then we need to reload, otherwise
147 			 * jump directly to the target state. */
148 			if ( trans->action->anyNextStmt() )
149 				out << "\tgoto _again;\n";
150 			else
151 				out << "\tgoto st" << trans->targ->id << ";\n";
152 		}
153 	}
154 
155 	return anyWritten;
156 }
157 
158 /* Called from GotoCodeGen::STATE_GOTOS just before writing the gotos for each
159  * state. */
GOTO_HEADER(RedStateAp * state)160 void CSharpIpGotoCodeGen::GOTO_HEADER( RedStateAp *state )
161 {
162 	bool anyWritten = IN_TRANS_ACTIONS( state );
163 
164 	if ( state->labelNeeded )
165 		out << "st" << state->id << ":\n";
166 
167 	if ( state->toStateAction != 0 ) {
168 		/* Remember that we wrote an action. Write every action in the list. */
169 		anyWritten = true;
170 		for ( GenActionTable::Iter item = state->toStateAction->key; item.lte(); item++ )
171 			ACTION( out, item->value, state->id, false );
172 	}
173 
174 	/* Advance and test buffer pos. */
175 	if ( state->labelNeeded ) {
176 		if ( !noEnd ) {
177 			out <<
178 				"	if ( ++" << P() << " == " << PE() << " )\n"
179 				"		goto _test_eof" << state->id << ";\n";
180 		}
181 		else {
182 			out <<
183 				"	" << P() << " += 1;\n";
184 		}
185 	}
186 
187 	/* Give the state a switch case. */
188 	out << "case " << state->id << ":\n";
189 
190 	if ( state->fromStateAction != 0 ) {
191 		/* Remember that we wrote an action. Write every action in the list. */
192 		anyWritten = true;
193 		for ( GenActionTable::Iter item = state->fromStateAction->key; item.lte(); item++ )
194 			ACTION( out, item->value, state->id, false );
195 	}
196 
197 	if ( anyWritten )
198 		genLineDirective( out );
199 
200 	/* Record the prev state if necessary. */
201 	if ( state->anyRegCurStateRef() )
202 		out << "	_ps = " << state->id << ";\n";
203 }
204 
STATE_GOTO_ERROR()205 void CSharpIpGotoCodeGen::STATE_GOTO_ERROR()
206 {
207 	/* In the error state we need to emit some stuff that usually goes into
208 	 * the header. */
209 	RedStateAp *state = redFsm->errState;
210 	bool anyWritten = IN_TRANS_ACTIONS( state );
211 
212 	/* No case label needed since we don't switch on the error state. */
213 	if ( anyWritten )
214 		genLineDirective( out );
215 
216 	if ( state->labelNeeded )
217 		out << "st" << state->id << ":\n";
218 
219 	/* Break out here. */
220 	outLabelUsed = true;
221 	out << vCS() << " = " << state->id << ";\n";
222 	out << "	goto _out;\n";
223 }
224 
225 
226 /* Emit the goto to take for a given transition. */
TRANS_GOTO(RedTransAp * trans,int level)227 std::ostream &CSharpIpGotoCodeGen::TRANS_GOTO( RedTransAp *trans, int level )
228 {
229 	if ( trans->action != 0 ) {
230 		/* Go to the transition which will go to the state. */
231 		out << TABS(level) << "goto tr" << trans->id << ";";
232 	}
233 	else {
234 		/* Go directly to the target state. */
235 		out << TABS(level) << "goto st" << trans->targ->id << ";";
236 	}
237 	return out;
238 }
239 
EXIT_STATES()240 std::ostream &CSharpIpGotoCodeGen::EXIT_STATES()
241 {
242 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
243 		if ( st->outNeeded ) {
244 			testEofUsed = true;
245 			out << "	_test_eof" << st->id << ": " << vCS() << " = " <<
246 					st->id << "; goto _test_eof; \n";
247 		}
248 	}
249 	return out;
250 }
251 
AGAIN_CASES()252 std::ostream &CSharpIpGotoCodeGen::AGAIN_CASES()
253 {
254 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
255 		out <<
256 			"		case " << st->id << ": goto st" << st->id << ";\n";
257 	}
258 	return out;
259 }
260 
FINISH_CASES()261 std::ostream &CSharpIpGotoCodeGen::FINISH_CASES()
262 {
263 	bool anyWritten = false;
264 
265 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
266 		if ( st->eofAction != 0 ) {
267 			if ( st->eofAction->eofRefs == 0 )
268 				st->eofAction->eofRefs = new IntSet;
269 			st->eofAction->eofRefs->insert( st->id );
270 		}
271 	}
272 
273 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
274 		if ( st->eofTrans != 0 )
275 			out << "	case " << st->id << ": goto tr" << st->eofTrans->id << ";\n";
276 	}
277 
278 	for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) {
279 		if ( act->eofRefs != 0 ) {
280 			for ( IntSet::Iter pst = *act->eofRefs; pst.lte(); pst++ )
281 				out << "	case " << *pst << ": \n";
282 
283 			/* Remember that we wrote a trans so we know to write the
284 			 * line directive for going back to the output. */
285 			anyWritten = true;
286 
287 			/* Write each action in the eof action list. */
288 			for ( GenActionTable::Iter item = act->key; item.lte(); item++ )
289 				ACTION( out, item->value, STATE_ERR_STATE, true );
290 			out << "\tbreak;\n";
291 		}
292 	}
293 
294 	if ( anyWritten )
295 		genLineDirective( out );
296 	return out;
297 }
298 
setLabelsNeeded(GenInlineList * inlineList)299 void CSharpIpGotoCodeGen::setLabelsNeeded( GenInlineList *inlineList )
300 {
301 	for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) {
302 		switch ( item->type ) {
303 		case GenInlineItem::Goto: case GenInlineItem::Call: {
304 			/* Mark the target as needing a label. */
305 			item->targState->labelNeeded = true;
306 			break;
307 		}
308 		default: break;
309 		}
310 
311 		if ( item->children != 0 )
312 			setLabelsNeeded( item->children );
313 	}
314 }
315 
316 /* Set up labelNeeded flag for each state. */
setLabelsNeeded()317 void CSharpIpGotoCodeGen::setLabelsNeeded()
318 {
319 	/* If we use the _again label, then we the _again switch, which uses all
320 	 * labels. */
321 	if ( useAgainLabel() ) {
322 		for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
323 			st->labelNeeded = true;
324 	}
325 	else {
326 		/* Do not use all labels by default, init all labelNeeded vars to false. */
327 		for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
328 			st->labelNeeded = false;
329 
330 		/* Walk all transitions and set only those that have targs. */
331 		for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
332 			/* If there is no action with a next statement, then the label will be
333 			 * needed. */
334 			if ( trans->action == 0 || !trans->action->anyNextStmt() )
335 				trans->targ->labelNeeded = true;
336 
337 			/* Need labels for states that have goto or calls in action code
338 			 * invoked on characters (ie, not from out action code). */
339 			if ( trans->action != 0 ) {
340 				/* Loop the actions. */
341 				for ( GenActionTable::Iter act = trans->action->key; act.lte(); act++ ) {
342 					/* Get the action and walk it's tree. */
343 					setLabelsNeeded( act->value->inlineList );
344 				}
345 			}
346 		}
347 	}
348 
349 	if ( !noEnd ) {
350 		for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
351 			if ( st != redFsm->errState )
352 				st->outNeeded = st->labelNeeded;
353 		}
354 	}
355 }
356 
writeData()357 void CSharpIpGotoCodeGen::writeData()
358 {
359 	STATE_IDS();
360 }
361 
writeExec()362 void CSharpIpGotoCodeGen::writeExec()
363 {
364 	/* Must set labels immediately before writing because we may depend on the
365 	 * noend write option. */
366 	setLabelsNeeded();
367 	testEofUsed = false;
368 	outLabelUsed = false;
369 
370 	out << "	{\n";
371 
372 	if ( redFsm->anyRegCurStateRef() )
373 		out << "	int _ps = 0;\n";
374 
375 	if ( redFsm->anyConditions() )
376 		out << "	" << WIDE_ALPH_TYPE() << " _widec;\n";
377 
378 	if ( !noEnd ) {
379 		testEofUsed = true;
380 		out <<
381 			"	if ( " << P() << " == " << PE() << " )\n"
382 			"		goto _test_eof;\n";
383 	}
384 
385 	if ( useAgainLabel() ) {
386 		out <<
387 			"	goto _resume;\n"
388 			"\n"
389 			"_again:\n"
390 			"	switch ( " << vCS() << " ) {\n";
391 			AGAIN_CASES() <<
392 			"	default: break;\n"
393 			"	}\n"
394 			"\n";
395 
396 		if ( !noEnd ) {
397 			testEofUsed = true;
398 			out <<
399 				"	if ( ++" << P() << " == " << PE() << " )\n"
400 				"		goto _test_eof;\n";
401 		}
402 		else {
403 			out <<
404 				"	" << P() << " += 1;\n";
405 		}
406 
407 		out << "_resume:\n";
408 	}
409 
410 	out <<
411 		"	switch ( " << vCS() << " )\n	{\n";
412 		STATE_GOTOS();
413 		SWITCH_DEFAULT() <<
414 		"	}\n";
415 		EXIT_STATES() <<
416 		"\n";
417 
418 	if ( testEofUsed )
419 		out << "	_test_eof: {}\n";
420 
421 	if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) {
422 		out <<
423 			"	if ( " << P() << " == " << vEOF() << " )\n"
424 			"	{\n"
425 			"	switch ( " << vCS() << " ) {\n";
426 			FINISH_CASES();
427 			SWITCH_DEFAULT() <<
428 			"	}\n"
429 			"	}\n"
430 			"\n";
431 	}
432 
433 	if ( outLabelUsed )
434 		out << "	_out: {}\n";
435 
436 	out <<
437 		"	}\n";
438 }
439