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 "mlgoto.h"
26 #include "redfsm.h"
27 #include "bstmap.h"
28 #include "gendata.h"
29 
30 /* Emit the goto to take for a given transition. */
TRANS_GOTO(RedTransAp * trans,int level)31 std::ostream &OCamlGotoCodeGen::TRANS_GOTO( RedTransAp *trans, int level )
32 {
33 	out << TABS(level) << "tr" << trans->id << " ()";
34 	return out;
35 }
36 
TO_STATE_ACTION_SWITCH()37 std::ostream &OCamlGotoCodeGen::TO_STATE_ACTION_SWITCH()
38 {
39 	/* Walk the list of functions, printing the cases. */
40 	for ( GenActionList::Iter act = actionList; act.lte(); act++ ) {
41 		/* Write out referenced actions. */
42 		if ( act->numToStateRefs > 0 ) {
43 			/* Write the case label, the action and the case break. */
44 			out << "\t| " << act->actionId << " ->\n";
45 			ACTION( out, act, 0, false );
46 			out << "\t()\n";
47 		}
48 	}
49 
50 	genLineDirective( out );
51 	return out;
52 }
53 
FROM_STATE_ACTION_SWITCH()54 std::ostream &OCamlGotoCodeGen::FROM_STATE_ACTION_SWITCH()
55 {
56 	/* Walk the list of functions, printing the cases. */
57 	for ( GenActionList::Iter act = actionList; act.lte(); act++ ) {
58 		/* Write out referenced actions. */
59 		if ( act->numFromStateRefs > 0 ) {
60 			/* Write the case label, the action and the case break. */
61 			out << "\t| " << act->actionId << " ->\n";
62 			ACTION( out, act, 0, false );
63 			out << "\t()\n";
64 		}
65 	}
66 
67 	genLineDirective( out );
68 	return out;
69 }
70 
EOF_ACTION_SWITCH()71 std::ostream &OCamlGotoCodeGen::EOF_ACTION_SWITCH()
72 {
73 	/* Walk the list of functions, printing the cases. */
74 	for ( GenActionList::Iter act = actionList; act.lte(); act++ ) {
75 		/* Write out referenced actions. */
76 		if ( act->numEofRefs > 0 ) {
77 			/* Write the case label, the action and the case break. */
78 			out << "\t| " << act->actionId << " ->\n";
79 			ACTION( out, act, 0, true );
80 			out << "\t()\n";
81 		}
82 	}
83 
84 	genLineDirective( out );
85 	return out;
86 }
87 
ACTION_SWITCH()88 std::ostream &OCamlGotoCodeGen::ACTION_SWITCH()
89 {
90 	/* Walk the list of functions, printing the cases. */
91 	for ( GenActionList::Iter act = actionList; act.lte(); act++ ) {
92 		/* Write out referenced actions. */
93 		if ( act->numTransRefs > 0 ) {
94 			/* Write the case label, the action and the case break. */
95 			out << "\t| " << act->actionId << " ->\n";
96 			ACTION( out, act, 0, false );
97 			out << "\t()\n";
98 		}
99 	}
100 
101 	genLineDirective( out );
102 	return out;
103 }
104 
GOTO_HEADER(RedStateAp * state)105 void OCamlGotoCodeGen::GOTO_HEADER( RedStateAp *state )
106 {
107 	/* Label the state. */
108 	out << "| " << state->id << " ->\n";
109 }
110 
111 
emitSingleSwitch(RedStateAp * state)112 void OCamlGotoCodeGen::emitSingleSwitch( RedStateAp *state )
113 {
114 	/* Load up the singles. */
115 	int numSingles = state->outSingle.length();
116 	RedTransEl *data = state->outSingle.data;
117 
118 	if ( numSingles == 1 ) {
119 		/* If there is a single single key then write it out as an if. */
120 		out << "\tif " << GET_WIDE_KEY(state) << " = " <<
121 				KEY(data[0].lowKey) << " then\n\t\t";
122 
123 		/* Virtual function for writing the target of the transition. */
124 		TRANS_GOTO(data[0].value, 0) << " else\n";
125 	}
126 	else if ( numSingles > 1 ) {
127 		/* Write out single keys in a switch if there is more than one. */
128 		out << "\tmatch " << GET_WIDE_KEY(state) << " with\n";
129 
130 		/* Write out the single indicies. */
131 		for ( int j = 0; j < numSingles; j++ ) {
132 			out << "\t\t| " << ALPHA_KEY(data[j].lowKey) << " -> ";
133 			TRANS_GOTO(data[j].value, 0) << "\n";
134 		}
135 
136 		out << "\t\t| _ ->\n";
137 	}
138 }
139 
emitRangeBSearch(RedStateAp * state,int level,int low,int high,RedTransAp * def)140 void OCamlGotoCodeGen::emitRangeBSearch( RedStateAp *state, int level, int low, int high, RedTransAp* def)
141 {
142 	/* Get the mid position, staying on the lower end of the range. */
143 	int mid = (low + high) >> 1;
144 	RedTransEl *data = state->outRange.data;
145 
146 	/* Determine if we need to look higher or lower. */
147 	bool anyLower = mid > low;
148 	bool anyHigher = mid < high;
149 
150 	/* Determine if the keys at mid are the limits of the alphabet. */
151 	bool limitLow = data[mid].lowKey == keyOps->minKey;
152 	bool limitHigh = data[mid].highKey == keyOps->maxKey;
153 
154 	if ( anyLower && anyHigher ) {
155 		/* Can go lower and higher than mid. */
156 		out << TABS(level) << "if " << GET_WIDE_KEY(state) << " < " <<
157 				KEY(data[mid].lowKey) << " then begin\n";
158 		emitRangeBSearch( state, level+1, low, mid-1, def );
159 		out << TABS(level) << " end else if " << GET_WIDE_KEY(state) << " > " <<
160 				KEY(data[mid].highKey) << " then begin\n";
161 		emitRangeBSearch( state, level+1, mid+1, high, def );
162 		out << TABS(level) << " end else\n";
163 		TRANS_GOTO(data[mid].value, level+1) << "\n";
164 	}
165 	else if ( anyLower && !anyHigher ) {
166 		/* Can go lower than mid but not higher. */
167 		out << TABS(level) << "if " << GET_WIDE_KEY(state) << " < " <<
168 				KEY(data[mid].lowKey) << " then begin\n";
169 		emitRangeBSearch( state, level+1, low, mid-1, def );
170 
171 		/* if the higher is the highest in the alphabet then there is no
172 		 * sense testing it. */
173 		if ( limitHigh ) {
174 			out << TABS(level) << " end else\n";
175 			TRANS_GOTO(data[mid].value, level+1) << "\n";
176 		}
177 		else {
178 			out << TABS(level) << " end else if " << GET_WIDE_KEY(state) << " <= " <<
179 					KEY(data[mid].highKey) << " then\n";
180 			TRANS_GOTO(data[mid].value, level+1) << "\n" << TABS(level) << "else\n";
181       TRANS_GOTO(def, level+1) << "\n";
182 		}
183 	}
184 	else if ( !anyLower && anyHigher ) {
185 		/* Can go higher than mid but not lower. */
186 		out << TABS(level) << "if " << GET_WIDE_KEY(state) << " > " <<
187 				KEY(data[mid].highKey) << " then begin\n";
188 		emitRangeBSearch( state, level+1, mid+1, high, def );
189 
190 		/* If the lower end is the lowest in the alphabet then there is no
191 		 * sense testing it. */
192 		if ( limitLow ) {
193 			out << TABS(level) << " end else\n";
194 			TRANS_GOTO(data[mid].value, level+1) << "\n";
195 		}
196 		else {
197 			out << TABS(level) << " end else if " << GET_WIDE_KEY(state) << " >= " <<
198 					KEY(data[mid].lowKey) << " then\n";
199 			TRANS_GOTO(data[mid].value, level+1) << "\n" << TABS(level) << "else\n";
200       TRANS_GOTO(def, level+1) << "\n";
201 		}
202 	}
203 	else {
204 		/* Cannot go higher or lower than mid. It's mid or bust. What
205 		 * tests to do depends on limits of alphabet. */
206 		if ( !limitLow && !limitHigh ) {
207 			out << TABS(level) << "if " << KEY(data[mid].lowKey) << " <= " <<
208 					GET_WIDE_KEY(state) << " && " << GET_WIDE_KEY(state) << " <= " <<
209 					KEY(data[mid].highKey) << " then\n";
210 			TRANS_GOTO(data[mid].value, level+1) << "\n" << TABS(level) << "else\n";
211       TRANS_GOTO(def, level+1) << "\n";
212 		}
213 		else if ( limitLow && !limitHigh ) {
214 			out << TABS(level) << "if " << GET_WIDE_KEY(state) << " <= " <<
215 					KEY(data[mid].highKey) << " then\n";
216 			TRANS_GOTO(data[mid].value, level+1) << "\n" << TABS(level) << "else\n";
217       TRANS_GOTO(def, level+1) << "\n";
218 		}
219 		else if ( !limitLow && limitHigh ) {
220 			out << TABS(level) << "if " << KEY(data[mid].lowKey) << " <= " <<
221 					GET_WIDE_KEY(state) << " then\n";
222 			TRANS_GOTO(data[mid].value, level+1) << "\n" << TABS(level) << "else\n";
223       TRANS_GOTO(def, level+1) << "\n";
224 		}
225 		else {
226 			/* Both high and low are at the limit. No tests to do. */
227 			TRANS_GOTO(data[mid].value, level+1) << "\n";
228 		}
229 	}
230 }
231 
STATE_GOTO_ERROR()232 void OCamlGotoCodeGen::STATE_GOTO_ERROR()
233 {
234 	/* Label the state and bail immediately. */
235 	outLabelUsed = true;
236 	RedStateAp *state = redFsm->errState;
237 	out << "| " << state->id << " ->\n";
238 	out << "	do_out ()\n";
239 }
240 
COND_TRANSLATE(GenStateCond * stateCond,int level)241 void OCamlGotoCodeGen::COND_TRANSLATE( GenStateCond *stateCond, int level )
242 {
243 	GenCondSpace *condSpace = stateCond->condSpace;
244 	out << TABS(level) << "_widec = " << CAST(WIDE_ALPH_TYPE()) << "(" <<
245 			KEY(condSpace->baseKey) << " + (" << GET_KEY() <<
246 			" - " << KEY(keyOps->minKey) << "));\n";
247 
248 	for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) {
249 		out << TABS(level) << "if ( ";
250 		CONDITION( out, *csi );
251 		Size condValOffset = ((1 << csi.pos()) * keyOps->alphSize());
252 		out << " ) _widec += " << condValOffset << ";\n";
253 	}
254 }
255 
emitCondBSearch(RedStateAp * state,int level,int low,int high)256 void OCamlGotoCodeGen::emitCondBSearch( RedStateAp *state, int level, int low, int high )
257 {
258 	/* Get the mid position, staying on the lower end of the range. */
259 	int mid = (low + high) >> 1;
260 	GenStateCond **data = state->stateCondVect.data;
261 
262 	/* Determine if we need to look higher or lower. */
263 	bool anyLower = mid > low;
264 	bool anyHigher = mid < high;
265 
266 	/* Determine if the keys at mid are the limits of the alphabet. */
267 	bool limitLow = data[mid]->lowKey == keyOps->minKey;
268 	bool limitHigh = data[mid]->highKey == keyOps->maxKey;
269 
270 	if ( anyLower && anyHigher ) {
271 		/* Can go lower and higher than mid. */
272 		out << TABS(level) << "if ( " << GET_KEY() << " < " <<
273 				KEY(data[mid]->lowKey) << " ) {\n";
274 		emitCondBSearch( state, level+1, low, mid-1 );
275 		out << TABS(level) << "} else if ( " << GET_KEY() << " > " <<
276 				KEY(data[mid]->highKey) << " ) {\n";
277 		emitCondBSearch( state, level+1, mid+1, high );
278 		out << TABS(level) << "} else {\n";
279 		COND_TRANSLATE(data[mid], level+1);
280 		out << TABS(level) << "}\n";
281 	}
282 	else if ( anyLower && !anyHigher ) {
283 		/* Can go lower than mid but not higher. */
284 		out << TABS(level) << "if ( " << GET_KEY() << " < " <<
285 				KEY(data[mid]->lowKey) << " ) {\n";
286 		emitCondBSearch( state, level+1, low, mid-1 );
287 
288 		/* if the higher is the highest in the alphabet then there is no
289 		 * sense testing it. */
290 		if ( limitHigh ) {
291 			out << TABS(level) << "} else {\n";
292 			COND_TRANSLATE(data[mid], level+1);
293 			out << TABS(level) << "}\n";
294 		}
295 		else {
296 			out << TABS(level) << "} else if ( " << GET_KEY() << " <= " <<
297 					KEY(data[mid]->highKey) << " ) {\n";
298 			COND_TRANSLATE(data[mid], level+1);
299 			out << TABS(level) << "}\n";
300 		}
301 	}
302 	else if ( !anyLower && anyHigher ) {
303 		/* Can go higher than mid but not lower. */
304 		out << TABS(level) << "if ( " << GET_KEY() << " > " <<
305 				KEY(data[mid]->highKey) << " ) {\n";
306 		emitCondBSearch( state, level+1, mid+1, high );
307 
308 		/* If the lower end is the lowest in the alphabet then there is no
309 		 * sense testing it. */
310 		if ( limitLow ) {
311 			out << TABS(level) << "} else {\n";
312 			COND_TRANSLATE(data[mid], level+1);
313 			out << TABS(level) << "}\n";
314 		}
315 		else {
316 			out << TABS(level) << "} else if ( " << GET_KEY() << " >= " <<
317 					KEY(data[mid]->lowKey) << " ) {\n";
318 			COND_TRANSLATE(data[mid], level+1);
319 			out << TABS(level) << "}\n";
320 		}
321 	}
322 	else {
323 		/* Cannot go higher or lower than mid. It's mid or bust. What
324 		 * tests to do depends on limits of alphabet. */
325 		if ( !limitLow && !limitHigh ) {
326 			out << TABS(level) << "if ( " << KEY(data[mid]->lowKey) << " <= " <<
327 					GET_KEY() << " && " << GET_KEY() << " <= " <<
328 					KEY(data[mid]->highKey) << " ) {\n";
329 			COND_TRANSLATE(data[mid], level+1);
330 			out << TABS(level) << "}\n";
331 		}
332 		else if ( limitLow && !limitHigh ) {
333 			out << TABS(level) << "if ( " << GET_KEY() << " <= " <<
334 					KEY(data[mid]->highKey) << " ) {\n";
335 			COND_TRANSLATE(data[mid], level+1);
336 			out << TABS(level) << "}\n";
337 		}
338 		else if ( !limitLow && limitHigh ) {
339 			out << TABS(level) << "if ( " << KEY(data[mid]->lowKey) << " <= " <<
340 					GET_KEY() << " )\n {";
341 			COND_TRANSLATE(data[mid], level+1);
342 			out << TABS(level) << "}\n";
343 		}
344 		else {
345 			/* Both high and low are at the limit. No tests to do. */
346 			COND_TRANSLATE(data[mid], level);
347 		}
348 	}
349 }
350 
STATE_GOTOS()351 std::ostream &OCamlGotoCodeGen::STATE_GOTOS()
352 {
353 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
354 		if ( st == redFsm->errState )
355 			STATE_GOTO_ERROR();
356 		else {
357 			/* Writing code above state gotos. */
358 			GOTO_HEADER( st );
359       out << "\tbegin\n";
360 
361 			if ( st->stateCondVect.length() > 0 ) {
362 				out << "	_widec = " << GET_KEY() << ";\n";
363 				emitCondBSearch( st, 1, 0, st->stateCondVect.length() - 1 );
364 			}
365 
366 			/* Try singles. */
367 			if ( st->outSingle.length() > 0 )
368 				emitSingleSwitch( st );
369 
370 			/* Default case is to binary search for the ranges, if that fails then */
371 			if ( st->outRange.length() > 0 )
372 				emitRangeBSearch( st, 1, 0, st->outRange.length() - 1, st->defTrans );
373       else
374   			/* Write the default transition. */
375   			TRANS_GOTO( st->defTrans, 1 ) << "\n";
376 
377       out << "\tend\n";
378 		}
379 	}
380 	return out;
381 }
382 
TRANSITIONS()383 std::ostream &OCamlGotoCodeGen::TRANSITIONS()
384 {
385 	/* Emit any transitions that have functions and that go to
386 	 * this state. */
387 	for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
388 		/* Write the label for the transition so it can be jumped to. */
389 		out << "	and tr" << trans->id << " () = ";
390 
391 		/* Destination state. */
392 		if ( trans->action != 0 && trans->action->anyCurStateRef() )
393 			out << "_ps = " << vCS() << ";";
394 		out << vCS() << " <- " << trans->targ->id << "; ";
395 
396 		if ( trans->action != 0 ) {
397 			/* Write out the transition func. */
398 			out << "f" << trans->action->actListId << " ()\n";
399 		}
400 		else {
401 			/* No code to execute, just loop around. */
402 			out << "do_again ()\n";
403 		}
404 	}
405 	return out;
406 }
407 
EXEC_FUNCS()408 std::ostream &OCamlGotoCodeGen::EXEC_FUNCS()
409 {
410 	/* Make labels that set acts and jump to execFuncs. Loop func indicies. */
411 	for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
412 		if ( redAct->numTransRefs > 0 ) {
413 			out << "	and f" << redAct->actListId << " () = " <<
414 				"state.acts <- " << itoa( redAct->location+1 ) << "; "
415 				"execFuncs ()\n";
416 		}
417 	}
418 
419 	out <<
420 		"\n"
421 		"and execFuncs () =\n"
422 		"	state.nacts <- " << AT( A(), POST_INCR( "state.acts") ) << ";\n"
423 		"	begin try while " << POST_DECR("state.nacts") << " > 0 do\n"
424 		"		match " << AT( A(), POST_INCR("state.acts") ) << " with\n";
425 		ACTION_SWITCH();
426 		SWITCH_DEFAULT() <<
427 		"	done with Goto_again -> () end;\n"
428 		"	do_again ()\n";
429 	return out;
430 }
431 
TO_STATE_ACTION(RedStateAp * state)432 unsigned int OCamlGotoCodeGen::TO_STATE_ACTION( RedStateAp *state )
433 {
434 	int act = 0;
435 	if ( state->toStateAction != 0 )
436 		act = state->toStateAction->location+1;
437 	return act;
438 }
439 
FROM_STATE_ACTION(RedStateAp * state)440 unsigned int OCamlGotoCodeGen::FROM_STATE_ACTION( RedStateAp *state )
441 {
442 	int act = 0;
443 	if ( state->fromStateAction != 0 )
444 		act = state->fromStateAction->location+1;
445 	return act;
446 }
447 
EOF_ACTION(RedStateAp * state)448 unsigned int OCamlGotoCodeGen::EOF_ACTION( RedStateAp *state )
449 {
450 	int act = 0;
451 	if ( state->eofAction != 0 )
452 		act = state->eofAction->location+1;
453 	return act;
454 }
455 
TO_STATE_ACTIONS()456 std::ostream &OCamlGotoCodeGen::TO_STATE_ACTIONS()
457 {
458 	/* Take one off for the psuedo start state. */
459 	int numStates = redFsm->stateList.length();
460 	unsigned int *vals = new unsigned int[numStates];
461 	memset( vals, 0, sizeof(unsigned int)*numStates );
462 
463 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
464 		vals[st->id] = TO_STATE_ACTION(st);
465 
466 	out << "\t";
467 	for ( int st = 0; st < redFsm->nextStateId; st++ ) {
468 		/* Write any eof action. */
469 		out << vals[st];
470 		if ( st < numStates-1 ) {
471 			out << ARR_SEP();
472 			if ( (st+1) % IALL == 0 )
473 				out << "\n\t";
474 		}
475 	}
476 	out << "\n";
477 	delete[] vals;
478 	return out;
479 }
480 
FROM_STATE_ACTIONS()481 std::ostream &OCamlGotoCodeGen::FROM_STATE_ACTIONS()
482 {
483 	/* Take one off for the psuedo start state. */
484 	int numStates = redFsm->stateList.length();
485 	unsigned int *vals = new unsigned int[numStates];
486 	memset( vals, 0, sizeof(unsigned int)*numStates );
487 
488 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
489 		vals[st->id] = FROM_STATE_ACTION(st);
490 
491 	out << "\t";
492 	for ( int st = 0; st < redFsm->nextStateId; st++ ) {
493 		/* Write any eof action. */
494 		out << vals[st];
495 		if ( st < numStates-1 ) {
496 			out << ARR_SEP();
497 			if ( (st+1) % IALL == 0 )
498 				out << "\n\t";
499 		}
500 	}
501 	out << "\n";
502 	delete[] vals;
503 	return out;
504 }
505 
EOF_ACTIONS()506 std::ostream &OCamlGotoCodeGen::EOF_ACTIONS()
507 {
508 	/* Take one off for the psuedo start state. */
509 	int numStates = redFsm->stateList.length();
510 	unsigned int *vals = new unsigned int[numStates];
511 	memset( vals, 0, sizeof(unsigned int)*numStates );
512 
513 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
514 		vals[st->id] = EOF_ACTION(st);
515 
516 	out << "\t";
517 	for ( int st = 0; st < redFsm->nextStateId; st++ ) {
518 		/* Write any eof action. */
519 		out << vals[st];
520 		if ( st < numStates-1 ) {
521 			out << ARR_SEP();
522 			if ( (st+1) % IALL == 0 )
523 				out << "\n\t";
524 		}
525 	}
526 	out << "\n";
527 	delete[] vals;
528 	return out;
529 }
530 
FINISH_CASES()531 std::ostream &OCamlGotoCodeGen::FINISH_CASES()
532 {
533 	for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
534 		/* States that are final and have an out action need a case. */
535 		if ( st->eofAction != 0 ) {
536 			/* Write the case label. */
537 			out << "\t\t| " << st->id << " -> ";
538 
539 			/* Write the goto func. */
540 			out << "f" << st->eofAction->actListId << " ()\n";
541 		}
542 	}
543 
544 	return out;
545 }
546 
GOTO(ostream & ret,int gotoDest,bool inFinish)547 void OCamlGotoCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish )
548 {
549 	ret << "begin " << vCS() << " <- " << gotoDest << "; " <<
550 			CTRL_FLOW() << "raise Goto_again end";
551 }
552 
GOTO_EXPR(ostream & ret,GenInlineItem * ilItem,bool inFinish)553 void OCamlGotoCodeGen::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
554 {
555 	ret << "begin " << vCS() << " <- (";
556 	INLINE_LIST( ret, ilItem->children, 0, inFinish );
557 	ret << "); " << CTRL_FLOW() << "raise Goto_again end";
558 }
559 
CURS(ostream & ret,bool inFinish)560 void OCamlGotoCodeGen::CURS( ostream &ret, bool inFinish )
561 {
562 	ret << "(_ps)";
563 }
564 
TARGS(ostream & ret,bool inFinish,int targState)565 void OCamlGotoCodeGen::TARGS( ostream &ret, bool inFinish, int targState )
566 {
567 	ret << "(" << vCS() << ")";
568 }
569 
NEXT(ostream & ret,int nextDest,bool inFinish)570 void OCamlGotoCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
571 {
572 	ret << vCS() << " <- " << nextDest << ";";
573 }
574 
NEXT_EXPR(ostream & ret,GenInlineItem * ilItem,bool inFinish)575 void OCamlGotoCodeGen::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish )
576 {
577 	ret << vCS() << " <- (";
578 	INLINE_LIST( ret, ilItem->children, 0, inFinish );
579 	ret << ");";
580 }
581 
CALL(ostream & ret,int callDest,int targState,bool inFinish)582 void OCamlGotoCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
583 {
584 	if ( prePushExpr != 0 ) {
585 		ret << "begin ";
586 		INLINE_LIST( ret, prePushExpr, 0, false );
587 	}
588 
589 	ret << "begin " << AT( STACK(), POST_INCR(TOP()) ) << " <- " << vCS() << "; ";
590   ret << vCS() << " <- " << callDest << "; " << CTRL_FLOW() << "raise Goto_again end ";
591 
592 	if ( prePushExpr != 0 )
593 		ret << "end";
594 }
595 
CALL_EXPR(ostream & ret,GenInlineItem * ilItem,int targState,bool inFinish)596 void OCamlGotoCodeGen::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish )
597 {
598 	if ( prePushExpr != 0 ) {
599 		ret << "begin ";
600 		INLINE_LIST( ret, prePushExpr, 0, false );
601 	}
602 
603 	ret << "begin " << AT(STACK(), POST_INCR(TOP()) ) << " <- " << vCS() << "; " << vCS() << " <- (";
604 	INLINE_LIST( ret, ilItem->children, targState, inFinish );
605 	ret << "); " << CTRL_FLOW() << "raise Goto_again end ";
606 
607 	if ( prePushExpr != 0 )
608 		ret << "end";
609 }
610 
RET(ostream & ret,bool inFinish)611 void OCamlGotoCodeGen::RET( ostream &ret, bool inFinish )
612 {
613 	ret << "begin " << vCS() << " <- " << AT(STACK(), PRE_DECR(TOP()) ) << "; ";
614 
615 	if ( postPopExpr != 0 ) {
616 		ret << "begin ";
617 		INLINE_LIST( ret, postPopExpr, 0, false );
618 		ret << "end ";
619 	}
620 
621 	ret << CTRL_FLOW() <<  "raise Goto_again end";
622 }
623 
BREAK(ostream & ret,int targState)624 void OCamlGotoCodeGen::BREAK( ostream &ret, int targState )
625 {
626 	outLabelUsed = true;
627 	ret << "begin " << P() << " <- " << P() << " + 1; " << CTRL_FLOW() << "raise Goto_out end";
628 }
629 
writeData()630 void OCamlGotoCodeGen::writeData()
631 {
632 	if ( redFsm->anyActions() ) {
633 		OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActArrItem), A() );
634 		ACTIONS_ARRAY();
635 		CLOSE_ARRAY() <<
636 		"\n";
637 	}
638 
639 	if ( redFsm->anyToStateActions() ) {
640 		OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
641 		TO_STATE_ACTIONS();
642 		CLOSE_ARRAY() <<
643 		"\n";
644 	}
645 
646 	if ( redFsm->anyFromStateActions() ) {
647 		OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
648 		FROM_STATE_ACTIONS();
649 		CLOSE_ARRAY() <<
650 		"\n";
651 	}
652 
653 	if ( redFsm->anyEofActions() ) {
654 		OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), EA() );
655 		EOF_ACTIONS();
656 		CLOSE_ARRAY() <<
657 		"\n";
658 	}
659 
660 	STATE_IDS();
661 
662   out << "type " << TYPE_STATE() << " = { mutable acts : " << ARRAY_TYPE(redFsm->maxActionLoc) <<
663          " ; mutable nacts : " << ARRAY_TYPE(redFsm->maxActArrItem) << "; }"
664     << TOP_SEP();
665 
666   out << "exception Goto_again" << TOP_SEP();
667 }
668 
writeExec()669 void OCamlGotoCodeGen::writeExec()
670 {
671 	testEofUsed = false;
672 	outLabelUsed = false;
673 
674 	out << "	begin\n";
675 
676 //	if ( redFsm->anyRegCurStateRef() )
677 //		out << "	int _ps = 0;\n";
678 
679 	if ( redFsm->anyToStateActions() || redFsm->anyRegActions()
680 			|| redFsm->anyFromStateActions() )
681 	{
682 		out << "	let state = { acts = 0; nacts = 0; } in\n";
683 	}
684 
685 //	if ( redFsm->anyConditions() )
686 //		out << "	" << WIDE_ALPH_TYPE() << " _widec;\n";
687 
688 	out << "\n";
689   out << "	let rec do_start () =\n";
690 
691 	if ( !noEnd ) {
692 		testEofUsed = true;
693 		out <<
694 			"	if " << P() << " = " << PE() << " then\n"
695 			"		do_test_eof ()\n"
696       "\telse\n";
697 	}
698 
699 	if ( redFsm->errState != 0 ) {
700 		outLabelUsed = true;
701 		out <<
702 			"	if " << vCS() << " = " << redFsm->errState->id << " then\n"
703 			"		do_out ()\n"
704       "\telse\n";
705 	}
706   out << "\tdo_resume ()\n";
707 
708 	out << "and do_resume () =\n";
709 
710 	if ( redFsm->anyFromStateActions() ) {
711 		out <<
712 			"	state.acts <- " << AT( FSA(), vCS() ) << ";\n"
713 			"	state.nacts <- " << AT( A(), POST_INCR("state.acts") ) << ";\n"
714 			"	while " << POST_DECR("state.nacts") << " > 0 do\n"
715 			"		begin match " << AT( A(), POST_INCR("state.acts") ) << " with\n";
716 			FROM_STATE_ACTION_SWITCH();
717 			SWITCH_DEFAULT() <<
718 			"		end\n"
719 			"	done;\n"
720 			"\n";
721 	}
722 
723 	out <<
724 		"	begin match " << vCS() << " with\n";
725 		STATE_GOTOS();
726 		SWITCH_DEFAULT() <<
727 		"	end\n"
728 		"\n";
729 		TRANSITIONS() <<
730 		"\n";
731 
732 	if ( redFsm->anyRegActions() )
733 		EXEC_FUNCS() << "\n";
734 
735 //	if ( redFsm->anyRegActions() || redFsm->anyActionGotos() ||
736 //			redFsm->anyActionCalls() || redFsm->anyActionRets() )
737   out << "\tand do_again () =\n";
738 
739 	if ( redFsm->anyToStateActions() ) {
740 		out <<
741 			"	state.acts <- " << AT( TSA(), vCS() ) << ";\n"
742 			"	state.nacts <- " << AT( A(), POST_INCR("state.acts") ) << ";\n"
743 			"	while " << POST_DECR("state.nacts") << " > 0 do\n"
744 			"		begin match " << AT( A(), POST_INCR("state.acts") ) << " with\n";
745 			TO_STATE_ACTION_SWITCH();
746 			SWITCH_DEFAULT() <<
747 			"		end\n"
748 			"	done;\n"
749 			"\n";
750 	}
751 
752 	if ( redFsm->errState != 0 ) {
753 		outLabelUsed = true;
754 		out <<
755 			"	match " << vCS() << " with\n"
756       "\t| " << redFsm->errState->id << " -> do_out ()\n"
757       "\t| _ ->\n";
758 	}
759 
760   out << "\t" << P() << " <- " << P() << " + 1;\n";
761 
762 	if ( !noEnd ) {
763 		out <<
764 			"	if " << P() << " <> " << PE() << " then\n"
765 			"		do_resume ()\n"
766       "\telse do_test_eof ()\n";
767 	}
768 	else {
769 		out <<
770 			"	do_resume ()\n";
771 	}
772 
773 //	if ( testEofUsed )
774 	out << "and do_test_eof () =\n";
775 
776 	if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) {
777 		out <<
778 			"	if " << P() << " = " << vEOF() << " then\n"
779 			"	begin\n";
780 
781 		if ( redFsm->anyEofTrans() ) {
782 			out <<
783 				"	match " << vCS() << " with\n";
784 
785 			for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
786 				if ( st->eofTrans != 0 )
787 					out << "	| " << st->id << " -> tr" << st->eofTrans->id << " ()\n";
788 			}
789 
790 			out << "\t| _ -> ();\n";
791 		}
792 
793 		if ( redFsm->anyEofActions() ) {
794 			out <<
795 				"	let __acts = ref " << AT( EA(), vCS() ) << " in\n"
796 				"	let __nacts = ref " << AT( A(), "!__acts" ) << " in\n"
797         " incr __acts;\n"
798 				"	begin try while !__nacts > 0 do\n"
799         "   decr __nacts;\n"
800 				"		begin match " << AT( A(), POST_INCR("__acts.contents") ) << " with\n";
801 				EOF_ACTION_SWITCH();
802 				SWITCH_DEFAULT() <<
803 				"		end;\n"
804 				"	done with Goto_again -> do_again () end;\n";
805 		}
806 
807 		out <<
808 			"	end\n"
809 			"\n";
810 	}
811   else
812   {
813     out << "\t()\n";
814   }
815 
816 	if ( outLabelUsed )
817 		out << "	and do_out () = ()\n";
818 
819   out << "\tin do_start ()\n";
820 	out << "	end;\n";
821 }
822