Cleanup and code movement related to makeCodeGen.
[external/ragel.git] / ragel / gendata.cpp
1 /*
2  *  Copyright 2005-2007 Adrian Thurston <thurston@complang.org>
3  */
4
5 /*  This file is part of Ragel.
6  *
7  *  Ragel is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  * 
12  *  Ragel is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  * 
17  *  You should have received a copy of the GNU General Public License
18  *  along with Ragel; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
20  */
21
22 #include "gendata.h"
23 #include "ragel.h"
24 #include <iostream>
25
26 /*
27  * Code generators.
28  */
29
30 #include "cstable.h"
31 #include "csftable.h"
32 #include "csflat.h"
33 #include "csfflat.h"
34 #include "csgoto.h"
35 #include "csfgoto.h"
36 #include "csipgoto.h"
37 #include "cssplit.h"
38
39 #include "cdtable.h"
40 #include "cdftable.h"
41 #include "cdflat.h"
42 #include "cdfflat.h"
43 #include "cdgoto.h"
44 #include "cdfgoto.h"
45 #include "cdipgoto.h"
46 #include "cdsplit.h"
47
48 #include "dotcodegen.h"
49
50 #include "javacodegen.h"
51
52 #include "rubytable.h"
53 #include "rubyftable.h"
54 #include "rubyflat.h"
55 #include "rubyfflat.h"
56 #include "rbxgoto.h"
57
58 string itoa( int i )
59 {
60         char buf[16];
61         sprintf( buf, "%i", i );
62         return buf;
63 }
64
65 using std::cout;
66 using std::cerr;
67 using std::endl;
68
69 /* Invoked by the parser when a ragel definition is opened. */
70 CodeGenData *dotMakeCodeGen( const char *sourceFileName, const char *fsmName, 
71                 ostream &out, bool wantComplete )
72 {
73         CodeGenData *codeGen = new GraphvizDotGen(out);
74
75         codeGen->sourceFileName = sourceFileName;
76         codeGen->fsmName = fsmName;
77         codeGen->wantComplete = wantComplete;
78
79         return codeGen;
80 }
81
82 /* Invoked by the parser when a ragel definition is opened. */
83 CodeGenData *cdMakeCodeGen( const char *sourceFileName, const char *fsmName, 
84                 ostream &out, bool wantComplete )
85 {
86         CodeGenData *codeGen = 0;
87         switch ( hostLang->lang ) {
88         case HostLang::C:
89                 switch ( codeStyle ) {
90                 case GenTables:
91                         codeGen = new CTabCodeGen(out);
92                         break;
93                 case GenFTables:
94                         codeGen = new CFTabCodeGen(out);
95                         break;
96                 case GenFlat:
97                         codeGen = new CFlatCodeGen(out);
98                         break;
99                 case GenFFlat:
100                         codeGen = new CFFlatCodeGen(out);
101                         break;
102                 case GenGoto:
103                         codeGen = new CGotoCodeGen(out);
104                         break;
105                 case GenFGoto:
106                         codeGen = new CFGotoCodeGen(out);
107                         break;
108                 case GenIpGoto:
109                         codeGen = new CIpGotoCodeGen(out);
110                         break;
111                 case GenSplit:
112                         codeGen = new CSplitCodeGen(out);
113                         break;
114                 }
115                 break;
116
117         case HostLang::D:
118                 switch ( codeStyle ) {
119                 case GenTables:
120                         codeGen = new DTabCodeGen(out);
121                         break;
122                 case GenFTables:
123                         codeGen = new DFTabCodeGen(out);
124                         break;
125                 case GenFlat:
126                         codeGen = new DFlatCodeGen(out);
127                         break;
128                 case GenFFlat:
129                         codeGen = new DFFlatCodeGen(out);
130                         break;
131                 case GenGoto:
132                         codeGen = new DGotoCodeGen(out);
133                         break;
134                 case GenFGoto:
135                         codeGen = new DFGotoCodeGen(out);
136                         break;
137                 case GenIpGoto:
138                         codeGen = new DIpGotoCodeGen(out);
139                         break;
140                 case GenSplit:
141                         codeGen = new DSplitCodeGen(out);
142                         break;
143                 }
144                 break;
145
146         default: break;
147         }
148
149         codeGen->sourceFileName = sourceFileName;
150         codeGen->fsmName = fsmName;
151         codeGen->wantComplete = wantComplete;
152
153         return codeGen;
154 }
155
156 /* Invoked by the parser when a ragel definition is opened. */
157 CodeGenData *javaMakeCodeGen( const char *sourceFileName, const char *fsmName, 
158                 ostream &out, bool wantComplete )
159 {
160         CodeGenData *codeGen = new JavaTabCodeGen(out);
161
162         codeGen->sourceFileName = sourceFileName;
163         codeGen->fsmName = fsmName;
164         codeGen->wantComplete = wantComplete;
165
166         return codeGen;
167 }
168
169 /* Invoked by the parser when a ragel definition is opened. */
170 CodeGenData *rubyMakeCodeGen( const char *sourceFileName, const char *fsmName, 
171                 ostream &out, bool wantComplete )
172 {
173         CodeGenData *codeGen = 0;
174         switch ( codeStyle ) {
175                 case GenTables: 
176                         codeGen = new RubyTabCodeGen(out);
177                         break;
178                 case GenFTables:
179                         codeGen = new RubyFTabCodeGen(out);
180                         break;
181                 case GenFlat:
182                         codeGen = new RubyFlatCodeGen(out);
183                         break;
184                 case GenFFlat:
185                         codeGen = new RubyFFlatCodeGen(out);
186                         break;
187                 case GenGoto:
188                         if ( rubyImpl == Rubinius ) {
189                                 codeGen = new RbxGotoCodeGen(out);
190                         } else {
191                                 cerr << "Goto style is still _very_ experimental " 
192                                         "and only supported using Rubinius.\n"
193                                         "You may want to enable the --rbx flag "
194                                         " to give it a try.\n";
195                                 exit(1);
196                         }
197                         break;
198                 default:
199                         cout << "Invalid code style\n";
200                         exit(1);
201                         break;
202         }
203         codeGen->sourceFileName = sourceFileName;
204         codeGen->fsmName = fsmName;
205         codeGen->wantComplete = wantComplete;
206
207         return codeGen;
208 }
209
210 /* Invoked by the parser when a ragel definition is opened. */
211 CodeGenData *csharpMakeCodeGen( const char *sourceFileName, const char *fsmName, 
212                 ostream &out, bool wantComplete )
213 {
214         CodeGenData *codeGen = 0;
215
216         switch ( codeStyle ) {
217         case GenTables:
218                 codeGen = new CSharpTabCodeGen(out);
219                 break;
220         case GenFTables:
221                 codeGen = new CSharpFTabCodeGen(out);
222                 break;
223         case GenFlat:
224                 codeGen = new CSharpFlatCodeGen(out);
225                 break;
226         case GenFFlat:
227                 codeGen = new CSharpFFlatCodeGen(out);
228                 break;
229         case GenGoto:
230                 codeGen = new CSharpGotoCodeGen(out);
231                 break;
232         case GenFGoto:
233                 codeGen = new CSharpFGotoCodeGen(out);
234                 break;
235         case GenIpGoto:
236                 codeGen = new CSharpIpGotoCodeGen(out);
237                 break;
238         case GenSplit:
239                 codeGen = new CSharpSplitCodeGen(out);
240                 break;
241         }
242
243         codeGen->sourceFileName = sourceFileName;
244         codeGen->fsmName = fsmName;
245         codeGen->wantComplete = wantComplete;
246
247         return codeGen;
248 }
249
250
251 CodeGenData *makeCodeGen( const char *sourceFileName, const char *fsmName, 
252                 ostream &out, bool wantComplete )
253 {
254         CodeGenData *cgd = 0;
255         if ( generateDot )
256                 cgd = dotMakeCodeGen( sourceFileName, fsmName, out, wantComplete );
257         else if ( hostLang == &hostLangC )
258                 cgd = cdMakeCodeGen( sourceFileName, fsmName, out, wantComplete );
259         else if ( hostLang == &hostLangD )
260                 cgd = cdMakeCodeGen( sourceFileName, fsmName, out, wantComplete );
261         else if ( hostLang == &hostLangJava )
262                 cgd = javaMakeCodeGen( sourceFileName, fsmName, out, wantComplete );
263         else if ( hostLang == &hostLangRuby )
264                 cgd = rubyMakeCodeGen( sourceFileName, fsmName, out, wantComplete );
265         else if ( hostLang == &hostLangCSharp )
266                 cgd = csharpMakeCodeGen( sourceFileName, fsmName, out, wantComplete );
267         return cgd;
268 }
269
270 void lineDirective( ostream &out, const char *fileName, int line )
271 {
272         if ( !generateDot ) {
273                 if ( hostLang == &hostLangC )
274                         cdLineDirective( out, fileName, line );
275                 else if ( hostLang == &hostLangD )
276                         cdLineDirective( out, fileName, line );
277                 else if ( hostLang == &hostLangJava )
278                         javaLineDirective( out, fileName, line );
279                 else if ( hostLang == &hostLangRuby )
280                         rubyLineDirective( out, fileName, line );
281                 else if ( hostLang == &hostLangCSharp )
282                         csharpLineDirective( out, fileName, line );
283         }
284 }
285
286 void genLineDirective( ostream &out )
287 {
288         std::streambuf *sbuf = out.rdbuf();
289         output_filter *filter = static_cast<output_filter*>(sbuf);
290         lineDirective( out, filter->fileName, filter->line + 1 );
291 }
292
293
294 /* Total error count. */
295 /* int gblErrorCount = 0; */
296
297 CodeGenData::CodeGenData( ostream &out )
298 :
299         sourceFileName(0),
300         fsmName(0), 
301         out(out),
302         redFsm(0), 
303         allActions(0),
304         allActionTables(0),
305         allConditions(0),
306         allCondSpaces(0),
307         allStates(0),
308         nameIndex(0),
309         startState(-1),
310         errState(-1),
311         getKeyExpr(0),
312         accessExpr(0),
313         prePushExpr(0),
314         postPopExpr(0),
315         pExpr(0),
316         peExpr(0),
317         eofExpr(0),
318         csExpr(0),
319         topExpr(0),
320         stackExpr(0),
321         actExpr(0),
322         tokstartExpr(0),
323         tokendExpr(0),
324         dataExpr(0),
325         wantComplete(0),
326         hasLongestMatch(false),
327         noEnd(false),
328         noPrefix(false),
329         noFinal(false),
330         noError(false),
331         noCS(false)
332 {}
333
334
335 void CodeGenData::createMachine()
336 {
337         redFsm = new RedFsmAp();
338 }
339
340 void CodeGenData::initActionList( unsigned long length )
341
342         allActions = new GenAction[length];
343         for ( unsigned long a = 0; a < length; a++ )
344                 actionList.append( allActions+a );
345 }
346
347 void CodeGenData::newAction( int anum, const char *name, int line, 
348                 int col, GenInlineList *inlineList )
349 {
350         allActions[anum].actionId = anum;
351         allActions[anum].name = name;
352         allActions[anum].loc.line = line;
353         allActions[anum].loc.col = col;
354         allActions[anum].inlineList = inlineList;
355 }
356
357 void CodeGenData::initActionTableList( unsigned long length )
358
359         allActionTables = new RedAction[length];
360 }
361
362 void CodeGenData::initStateList( unsigned long length )
363 {
364         allStates = new RedStateAp[length];
365         for ( unsigned long s = 0; s < length; s++ )
366                 redFsm->stateList.append( allStates+s );
367
368         /* We get the start state as an offset, set the pointer now. */
369         if ( startState >= 0 )
370                 redFsm->startState = allStates + startState;
371         if ( errState >= 0 )
372                 redFsm->errState = allStates + errState;
373         for ( EntryIdVect::Iter en = entryPointIds; en.lte(); en++ )
374                 redFsm->entryPoints.insert( allStates + *en );
375
376         /* The nextStateId is no longer used to assign state ids (they come in set
377          * from the frontend now), however generation code still depends on it.
378          * Should eventually remove this variable. */
379         redFsm->nextStateId = redFsm->stateList.length();
380 }
381
382 void CodeGenData::setStartState( unsigned long startState )
383 {
384         this->startState = startState;
385 }
386
387 void CodeGenData::setErrorState( unsigned long errState )
388 {
389         this->errState = errState;
390 }
391
392 void CodeGenData::addEntryPoint( char *name, unsigned long entryState )
393 {
394         entryPointIds.append( entryState );
395         entryPointNames.append( name );
396 }
397
398 void CodeGenData::initTransList( int snum, unsigned long length )
399 {
400         /* Could preallocate the out range to save time growing it. For now do
401          * nothing. */
402 }
403
404 void CodeGenData::newTrans( int snum, int tnum, Key lowKey, 
405                 Key highKey, long targ, long action )
406 {
407         /* Get the current state and range. */
408         RedStateAp *curState = allStates + snum;
409         RedTransList &destRange = curState->outRange;
410
411         if ( curState == redFsm->errState )
412                 return;
413
414         /* Make the new transitions. */
415         RedStateAp *targState = targ >= 0 ? (allStates + targ) : 
416                         wantComplete ? redFsm->getErrorState() : 0;
417         RedAction *actionTable = action >= 0 ? (allActionTables + action) : 0;
418         RedTransAp *trans = redFsm->allocateTrans( targState, actionTable );
419         RedTransEl transEl( lowKey, highKey, trans );
420
421         if ( wantComplete ) {
422                 /* If the machine is to be complete then we need to fill any gaps with
423                  * the error transitions. */
424                 if ( destRange.length() == 0 ) {
425                         /* Range is currently empty. */
426                         if ( keyOps->minKey < lowKey ) {
427                                 /* The first range doesn't start at the low end. */
428                                 Key fillHighKey = lowKey;
429                                 fillHighKey.decrement();
430
431                                 /* Create the filler with the state's error transition. */
432                                 RedTransEl newTel( keyOps->minKey, fillHighKey, redFsm->getErrorTrans() );
433                                 destRange.append( newTel );
434                         }
435                 }
436                 else {
437                         /* The range list is not empty, get the the last range. */
438                         RedTransEl *last = &destRange[destRange.length()-1];
439                         Key nextKey = last->highKey;
440                         nextKey.increment();
441                         if ( nextKey < lowKey ) {
442                                 /* There is a gap to fill. Make the high key. */
443                                 Key fillHighKey = lowKey;
444                                 fillHighKey.decrement();
445
446                                 /* Create the filler with the state's error transtion. */
447                                 RedTransEl newTel( nextKey, fillHighKey, redFsm->getErrorTrans() );
448                                 destRange.append( newTel );
449                         }
450                 }
451         }
452
453         /* Filler taken care of. Append the range. */
454         destRange.append( RedTransEl( lowKey, highKey, trans ) );
455 }
456
457 void CodeGenData::finishTransList( int snum )
458 {
459         /* Get the current state and range. */
460         RedStateAp *curState = allStates + snum;
461         RedTransList &destRange = curState->outRange;
462
463         if ( curState == redFsm->errState )
464                 return;
465
466         /* If building a complete machine we may need filler on the end. */
467         if ( wantComplete ) {
468                 /* Check if there are any ranges already. */
469                 if ( destRange.length() == 0 ) {
470                         /* Fill with the whole alphabet. */
471                         /* Add the range on the lower and upper bound. */
472                         RedTransEl newTel( keyOps->minKey, keyOps->maxKey, redFsm->getErrorTrans() );
473                         destRange.append( newTel );
474                 }
475                 else {
476                         /* Get the last and check for a gap on the end. */
477                         RedTransEl *last = &destRange[destRange.length()-1];
478                         if ( last->highKey < keyOps->maxKey ) {
479                                 /* Make the high key. */
480                                 Key fillLowKey = last->highKey;
481                                 fillLowKey.increment();
482
483                                 /* Create the new range with the error trans and append it. */
484                                 RedTransEl newTel( fillLowKey, keyOps->maxKey, redFsm->getErrorTrans() );
485                                 destRange.append( newTel );
486                         }
487                 }
488         }
489 }
490
491 void CodeGenData::setId( int snum, int id )
492 {
493         RedStateAp *curState = allStates + snum;
494         curState->id = id;
495 }
496
497 void CodeGenData::setFinal( int snum )
498 {
499         RedStateAp *curState = allStates + snum;
500         curState->isFinal = true;
501 }
502
503
504 void CodeGenData::setStateActions( int snum, long toStateAction, 
505                 long fromStateAction, long eofAction )
506 {
507         RedStateAp *curState = allStates + snum;
508         if ( toStateAction >= 0 )
509                 curState->toStateAction = allActionTables + toStateAction;
510         if ( fromStateAction >= 0 )
511                 curState->fromStateAction = allActionTables + fromStateAction;
512         if ( eofAction >= 0 )
513                 curState->eofAction = allActionTables + eofAction;
514 }
515
516 void CodeGenData::setEofTrans( int snum, long eofTarget, long actId )
517 {
518         RedStateAp *curState = allStates + snum;
519         RedStateAp *targState = allStates + eofTarget;
520         RedAction *eofAct = allActionTables + actId;
521         curState->eofTrans = redFsm->allocateTrans( targState, eofAct );
522 }
523
524 void CodeGenData::resolveTargetStates( GenInlineList *inlineList )
525 {
526         for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) {
527                 switch ( item->type ) {
528                 case GenInlineItem::Goto: case GenInlineItem::Call:
529                 case GenInlineItem::Next: case GenInlineItem::Entry:
530                         item->targState = allStates + item->targId;
531                         break;
532                 default:
533                         break;
534                 }
535
536                 if ( item->children != 0 )
537                         resolveTargetStates( item->children );
538         }
539 }
540
541 void CodeGenData::closeMachine()
542 {
543         for ( GenActionList::Iter a = actionList; a.lte(); a++ )
544                 resolveTargetStates( a->inlineList );
545
546         /* Note that even if we want a complete graph we do not give the error
547          * state a default transition. All machines break out of the processing
548          * loop when in the error state. */
549
550         for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
551                 for ( GenStateCondList::Iter sci = st->stateCondList; sci.lte(); sci++ )
552                         st->stateCondVect.append( sci );
553         }
554 }
555
556
557 bool CodeGenData::setAlphType( const char *data )
558 {
559         HostType *alphType = findAlphTypeInternal( data );
560         if ( alphType == 0 )
561                 return false;
562
563         thisKeyOps.setAlphType( alphType );
564         return true;
565 }
566
567 void CodeGenData::initCondSpaceList( ulong length )
568 {
569         allCondSpaces = new GenCondSpace[length];
570         for ( ulong c = 0; c < length; c++ )
571                 condSpaceList.append( allCondSpaces + c );
572 }
573
574 void CodeGenData::newCondSpace( int cnum, int condSpaceId, Key baseKey )
575 {
576         GenCondSpace *cond = allCondSpaces + cnum;
577         cond->condSpaceId = condSpaceId;
578         cond->baseKey = baseKey;
579 }
580
581 void CodeGenData::condSpaceItem( int cnum, long condActionId )
582 {
583         GenCondSpace *cond = allCondSpaces + cnum;
584         cond->condSet.append( allActions + condActionId );
585 }
586
587 void CodeGenData::initStateCondList( int snum, ulong length )
588 {
589         /* Could preallocate these, as we could with transitions. */
590 }
591
592 void CodeGenData::addStateCond( int snum, Key lowKey, Key highKey, long condNum )
593 {
594         RedStateAp *curState = allStates + snum;
595
596         /* Create the new state condition. */
597         GenStateCond *stateCond = new GenStateCond;
598         stateCond->lowKey = lowKey;
599         stateCond->highKey = highKey;
600
601         /* Assign it a cond space. */
602         GenCondSpace *condSpace = allCondSpaces + condNum;
603         stateCond->condSpace = condSpace;
604
605         curState->stateCondList.append( stateCond );
606 }
607
608
609 GenCondSpace *CodeGenData::findCondSpace( Key lowKey, Key highKey )
610 {
611         for ( CondSpaceList::Iter cs = condSpaceList; cs.lte(); cs++ ) {
612                 Key csHighKey = cs->baseKey;
613                 csHighKey += keyOps->alphSize() * (1 << cs->condSet.length());
614
615                 if ( lowKey >= cs->baseKey && highKey <= csHighKey )
616                         return cs;
617         }
618         return 0;
619 }
620
621 Condition *CodeGenData::findCondition( Key key )
622 {
623         for ( ConditionList::Iter cond = conditionList; cond.lte(); cond++ ) {
624                 Key upperKey = cond->baseKey + (1 << cond->condSet.length());
625                 if ( cond->baseKey <= key && key <= upperKey )
626                         return cond;
627         }
628         return 0;
629 }
630
631 Key CodeGenData::findMaxKey()
632 {
633         Key maxKey = keyOps->maxKey;
634         for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
635                 assert( st->outSingle.length() == 0 );
636                 assert( st->defTrans == 0 );
637
638                 long rangeLen = st->outRange.length();
639                 if ( rangeLen > 0 ) {
640                         Key highKey = st->outRange[rangeLen-1].highKey;
641                         if ( highKey > maxKey )
642                                 maxKey = highKey;
643                 }
644         }
645         return maxKey;
646 }
647
648 void CodeGenData::findFinalActionRefs()
649 {
650         for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
651                 /* Rerence count out of single transitions. */
652                 for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
653                         if ( rtel->value->action != 0 ) {
654                                 rtel->value->action->numTransRefs += 1;
655                                 for ( GenActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
656                                         item->value->numTransRefs += 1;
657                         }
658                 }
659
660                 /* Reference count out of range transitions. */
661                 for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
662                         if ( rtel->value->action != 0 ) {
663                                 rtel->value->action->numTransRefs += 1;
664                                 for ( GenActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
665                                         item->value->numTransRefs += 1;
666                         }
667                 }
668
669                 /* Reference count default transition. */
670                 if ( st->defTrans != 0 && st->defTrans->action != 0 ) {
671                         st->defTrans->action->numTransRefs += 1;
672                         for ( GenActionTable::Iter item = st->defTrans->action->key; item.lte(); item++ )
673                                 item->value->numTransRefs += 1;
674                 }
675
676                 /* Reference count eof transitions. */
677                 if ( st->eofTrans != 0 && st->eofTrans->action != 0 ) {
678                         st->eofTrans->action->numTransRefs += 1;
679                         for ( GenActionTable::Iter item = st->eofTrans->action->key; item.lte(); item++ )
680                                 item->value->numTransRefs += 1;
681                 }
682
683                 /* Reference count to state actions. */
684                 if ( st->toStateAction != 0 ) {
685                         st->toStateAction->numToStateRefs += 1;
686                         for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ )
687                                 item->value->numToStateRefs += 1;
688                 }
689
690                 /* Reference count from state actions. */
691                 if ( st->fromStateAction != 0 ) {
692                         st->fromStateAction->numFromStateRefs += 1;
693                         for ( GenActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ )
694                                 item->value->numFromStateRefs += 1;
695                 }
696
697                 /* Reference count EOF actions. */
698                 if ( st->eofAction != 0 ) {
699                         st->eofAction->numEofRefs += 1;
700                         for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ )
701                                 item->value->numEofRefs += 1;
702                 }
703         }
704 }
705
706 void CodeGenData::analyzeAction( GenAction *act, GenInlineList *inlineList )
707 {
708         for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) {
709                 /* Only consider actions that are referenced. */
710                 if ( act->numRefs() > 0 ) {
711                         if ( item->type == GenInlineItem::Goto || item->type == GenInlineItem::GotoExpr )
712                                 redFsm->bAnyActionGotos = true;
713                         else if ( item->type == GenInlineItem::Call || item->type == GenInlineItem::CallExpr )
714                                 redFsm->bAnyActionCalls = true;
715                         else if ( item->type == GenInlineItem::Ret )
716                                 redFsm->bAnyActionRets = true;
717                 }
718
719                 /* Check for various things in regular actions. */
720                 if ( act->numTransRefs > 0 || act->numToStateRefs > 0 || act->numFromStateRefs > 0 ) {
721                         /* Any returns in regular actions? */
722                         if ( item->type == GenInlineItem::Ret )
723                                 redFsm->bAnyRegActionRets = true;
724
725                         /* Any next statements in the regular actions? */
726                         if ( item->type == GenInlineItem::Next || item->type == GenInlineItem::NextExpr )
727                                 redFsm->bAnyRegNextStmt = true;
728
729                         /* Any by value control in regular actions? */
730                         if ( item->type == GenInlineItem::CallExpr || item->type == GenInlineItem::GotoExpr )
731                                 redFsm->bAnyRegActionByValControl = true;
732
733                         /* Any references to the current state in regular actions? */
734                         if ( item->type == GenInlineItem::Curs )
735                                 redFsm->bAnyRegCurStateRef = true;
736
737                         if ( item->type == GenInlineItem::Break )
738                                 redFsm->bAnyRegBreak = true;
739                 }
740
741                 if ( item->children != 0 )
742                         analyzeAction( act, item->children );
743         }
744 }
745
746 void CodeGenData::analyzeActionList( RedAction *redAct, GenInlineList *inlineList )
747 {
748         for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) {
749                 /* Any next statements in the action table? */
750                 if ( item->type == GenInlineItem::Next || item->type == GenInlineItem::NextExpr )
751                         redAct->bAnyNextStmt = true;
752
753                 /* Any references to the current state. */
754                 if ( item->type == GenInlineItem::Curs )
755                         redAct->bAnyCurStateRef = true;
756
757                 if ( item->type == GenInlineItem::Break )
758                         redAct->bAnyBreakStmt = true;
759
760                 if ( item->children != 0 )
761                         analyzeActionList( redAct, item->children );
762         }
763 }
764
765 /* Assign ids to referenced actions. */
766 void CodeGenData::assignActionIds()
767 {
768         int nextActionId = 0;
769         for ( GenActionList::Iter act = actionList; act.lte(); act++ ) {
770                 /* Only ever interested in referenced actions. */
771                 if ( act->numRefs() > 0 )
772                         act->actionId = nextActionId++;
773         }
774 }
775
776 void CodeGenData::setValueLimits()
777 {
778         redFsm->maxSingleLen = 0;
779         redFsm->maxRangeLen = 0;
780         redFsm->maxKeyOffset = 0;
781         redFsm->maxIndexOffset = 0;
782         redFsm->maxActListId = 0;
783         redFsm->maxActionLoc = 0;
784         redFsm->maxActArrItem = 0;
785         redFsm->maxSpan = 0;
786         redFsm->maxCondSpan = 0;
787         redFsm->maxFlatIndexOffset = 0;
788         redFsm->maxCondOffset = 0;
789         redFsm->maxCondLen = 0;
790         redFsm->maxCondSpaceId = 0;
791         redFsm->maxCondIndexOffset = 0;
792
793         /* In both of these cases the 0 index is reserved for no value, so the max
794          * is one more than it would be if they started at 0. */
795         redFsm->maxIndex = redFsm->transSet.length();
796         redFsm->maxCond = condSpaceList.length(); 
797
798         /* The nextStateId - 1 is the last state id assigned. */
799         redFsm->maxState = redFsm->nextStateId - 1;
800
801         for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) {
802                 if ( csi->condSpaceId > redFsm->maxCondSpaceId )
803                         redFsm->maxCondSpaceId = csi->condSpaceId;
804         }
805
806         for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
807                 /* Maximum cond length. */
808                 if ( st->stateCondList.length() > redFsm->maxCondLen )
809                         redFsm->maxCondLen = st->stateCondList.length();
810
811                 /* Maximum single length. */
812                 if ( st->outSingle.length() > redFsm->maxSingleLen )
813                         redFsm->maxSingleLen = st->outSingle.length();
814
815                 /* Maximum range length. */
816                 if ( st->outRange.length() > redFsm->maxRangeLen )
817                         redFsm->maxRangeLen = st->outRange.length();
818
819                 /* The key offset index offset for the state after last is not used, skip it.. */
820                 if ( ! st.last() ) {
821                         redFsm->maxCondOffset += st->stateCondList.length();
822                         redFsm->maxKeyOffset += st->outSingle.length() + st->outRange.length()*2;
823                         redFsm->maxIndexOffset += st->outSingle.length() + st->outRange.length() + 2;
824                 }
825
826                 /* Max cond span. */
827                 if ( st->condList != 0 ) {
828                         unsigned long long span = keyOps->span( st->condLowKey, st->condHighKey );
829                         if ( span > redFsm->maxCondSpan )
830                                 redFsm->maxCondSpan = span;
831                 }
832
833                 /* Max key span. */
834                 if ( st->transList != 0 ) {
835                         unsigned long long span = keyOps->span( st->lowKey, st->highKey );
836                         if ( span > redFsm->maxSpan )
837                                 redFsm->maxSpan = span;
838                 }
839
840                 /* Max cond index offset. */
841                 if ( ! st.last() ) {
842                         if ( st->condList != 0 )
843                                 redFsm->maxCondIndexOffset += keyOps->span( st->condLowKey, st->condHighKey );
844                 }
845
846                 /* Max flat index offset. */
847                 if ( ! st.last() ) {
848                         if ( st->transList != 0 )
849                                 redFsm->maxFlatIndexOffset += keyOps->span( st->lowKey, st->highKey );
850                         redFsm->maxFlatIndexOffset += 1;
851                 }
852         }
853
854         for ( GenActionTableMap::Iter at = redFsm->actionMap; at.lte(); at++ ) {
855                 /* Maximum id of action lists. */
856                 if ( at->actListId+1 > redFsm->maxActListId )
857                         redFsm->maxActListId = at->actListId+1;
858
859                 /* Maximum location of items in action array. */
860                 if ( at->location+1 > redFsm->maxActionLoc )
861                         redFsm->maxActionLoc = at->location+1;
862
863                 /* Maximum values going into the action array. */
864                 if ( at->key.length() > redFsm->maxActArrItem )
865                         redFsm->maxActArrItem = at->key.length();
866                 for ( GenActionTable::Iter item = at->key; item.lte(); item++ ) {
867                         if ( item->value->actionId > redFsm->maxActArrItem )
868                                 redFsm->maxActArrItem = item->value->actionId;
869                 }
870         }
871 }
872
873
874
875 /* Gather various info on the machine. */
876 void CodeGenData::analyzeMachine()
877 {
878         /* Find the true count of action references.  */
879         findFinalActionRefs();
880
881         /* Check if there are any calls in action code. */
882         for ( GenActionList::Iter act = actionList; act.lte(); act++ ) {
883                 /* Record the occurrence of various kinds of actions. */
884                 if ( act->numToStateRefs > 0 )
885                         redFsm->bAnyToStateActions = true;
886                 if ( act->numFromStateRefs > 0 )
887                         redFsm->bAnyFromStateActions = true;
888                 if ( act->numEofRefs > 0 )
889                         redFsm->bAnyEofActions = true;
890                 if ( act->numTransRefs > 0 )
891                         redFsm->bAnyRegActions = true;
892
893                 /* Recurse through the action's parse tree looking for various things. */
894                 analyzeAction( act, act->inlineList );
895         }
896
897         /* Analyze reduced action lists. */
898         for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
899                 for ( GenActionTable::Iter act = redAct->key; act.lte(); act++ )
900                         analyzeActionList( redAct, act->value->inlineList );
901         }
902
903         /* Find states that have transitions with actions that have next
904          * statements. */
905         for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
906                 /* Check any actions out of outSinge. */
907                 for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
908                         if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
909                                 st->bAnyRegCurStateRef = true;
910                 }
911
912                 /* Check any actions out of outRange. */
913                 for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
914                         if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
915                                 st->bAnyRegCurStateRef = true;
916                 }
917
918                 /* Check any action out of default. */
919                 if ( st->defTrans != 0 && st->defTrans->action != 0 && 
920                                 st->defTrans->action->anyCurStateRef() )
921                         st->bAnyRegCurStateRef = true;
922                 
923                 if ( st->stateCondList.length() > 0 )
924                         redFsm->bAnyConditions = true;
925
926                 if ( st->eofTrans != 0 )
927                         redFsm->bAnyEofTrans = true;
928         }
929
930         /* Assign ids to actions that are referenced. */
931         assignActionIds();
932
933         /* Set the maximums of various values used for deciding types. */
934         setValueLimits();
935 }
936
937 void CodeGenData::write_option_error( InputLoc &loc, char *arg )
938 {
939         source_warning(loc) << "unrecognized write option \"" << arg << "\"" << endl;
940 }
941
942 void CodeGenData::writeStatement( InputLoc &loc, int nargs, char **args )
943 {
944         /* FIXME: This should be moved to the virtual functions in the code
945          * generators.
946          *
947          * Force a newline. */
948         out << '\n';
949         genLineDirective( out );
950
951         if ( strcmp( args[0], "data" ) == 0 ) {
952                 for ( int i = 1; i < nargs; i++ ) {
953                         if ( strcmp( args[i], "noerror" ) == 0 )
954                                 noError = true;
955                         else if ( strcmp( args[i], "noprefix" ) == 0 )
956                                 noPrefix = true;
957                         else if ( strcmp( args[i], "nofinal" ) == 0 )
958                                 noFinal = true;
959                         else
960                                 write_option_error( loc, args[i] );
961                 }
962                 writeData();
963         }
964         else if ( strcmp( args[0], "init" ) == 0 ) {
965                 for ( int i = 1; i < nargs; i++ ) {
966                         if ( strcmp( args[i], "nocs" ) == 0 )
967                                 noCS = true;
968                         else
969                                 write_option_error( loc, args[i] );
970                 }
971                 writeInit();
972         }
973         else if ( strcmp( args[0], "exec" ) == 0 ) {
974                 for ( int i = 1; i < nargs; i++ ) {
975                         if ( strcmp( args[i], "noend" ) == 0 )
976                                 noEnd = true;
977                         else
978                                 write_option_error( loc, args[i] );
979                 }
980                 writeExec();
981         }
982         else if ( strcmp( args[0], "exports" ) == 0 ) {
983                 for ( int i = 1; i < nargs; i++ )
984                         write_option_error( loc, args[i] );
985                 writeExports();
986         }
987         else if ( strcmp( args[0], "start" ) == 0 ) {
988                 for ( int i = 1; i < nargs; i++ )
989                         write_option_error( loc, args[i] );
990                 writeStart();
991         }
992         else if ( strcmp( args[0], "first_final" ) == 0 ) {
993                 for ( int i = 1; i < nargs; i++ )
994                         write_option_error( loc, args[i] );
995                 writeFirstFinal();
996         }
997         else if ( strcmp( args[0], "error" ) == 0 ) {
998                 for ( int i = 1; i < nargs; i++ )
999                         write_option_error( loc, args[i] );
1000                 writeError();
1001         }
1002         else {
1003                 /* EMIT An error here. */
1004                 source_error(loc) << "unrecognized write command \"" << 
1005                                 args[0] << "\"" << endl;
1006         }
1007 }
1008
1009 ostream &CodeGenData::source_warning( const InputLoc &loc )
1010 {
1011         cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": warning: ";
1012         return cerr;
1013 }
1014
1015 ostream &CodeGenData::source_error( const InputLoc &loc )
1016 {
1017         gblErrorCount += 1;
1018         assert( sourceFileName != 0 );
1019         cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": ";
1020         return cerr;
1021 }
1022
1023