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