2 * Copyright 2005-2007 Adrian Thurston <thurston@cs.queensu.ca>
5 /* This file is part of Ragel.
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.
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.
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
28 /* Total error count. */
29 int gblErrorCount = 0;
31 CodeGenData::CodeGenData( ostream &out )
59 hasLongestMatch(false),
62 writeFirstFinal(true),
68 void CodeGenData::createMachine()
70 redFsm = new RedFsmAp();
73 void CodeGenData::initActionList( unsigned long length )
75 allActions = new Action[length];
76 for ( unsigned long a = 0; a < length; a++ )
77 actionList.append( allActions+a );
80 void CodeGenData::newAction( int anum, char *name, int line,
81 int col, InlineList *inlineList )
83 allActions[anum].actionId = anum;
84 allActions[anum].name = name;
85 allActions[anum].loc.line = line;
86 allActions[anum].loc.col = col;
87 allActions[anum].inlineList = inlineList;
90 void CodeGenData::initActionTableList( unsigned long length )
92 allActionTables = new RedAction[length];
95 void CodeGenData::initStateList( unsigned long length )
97 allStates = new RedStateAp[length];
98 for ( unsigned long s = 0; s < length; s++ )
99 redFsm->stateList.append( allStates+s );
101 /* We get the start state as an offset, set the pointer now. */
102 if ( startState >= 0 )
103 redFsm->startState = allStates + startState;
105 redFsm->errState = allStates + errState;
106 for ( EntryIdVect::Iter en = entryPointIds; en.lte(); en++ )
107 redFsm->entryPoints.insert( allStates + *en );
109 /* The nextStateId is no longer used to assign state ids (they come in set
110 * from the frontend now), however generation code still depends on it.
111 * Should eventually remove this variable. */
112 redFsm->nextStateId = redFsm->stateList.length();
115 void CodeGenData::setStartState( unsigned long startState )
117 this->startState = startState;
120 void CodeGenData::setErrorState( unsigned long errState )
122 this->errState = errState;
125 void CodeGenData::addEntryPoint( char *name, unsigned long entryState )
127 entryPointIds.append( entryState );
128 entryPointNames.append( name );
131 void CodeGenData::initTransList( int snum, unsigned long length )
133 /* Could preallocate the out range to save time growing it. For now do
137 void CodeGenData::newTrans( int snum, int tnum, Key lowKey,
138 Key highKey, long targ, long action )
140 /* Get the current state and range. */
141 RedStateAp *curState = allStates + snum;
142 RedTransList &destRange = curState->outRange;
144 if ( curState == redFsm->errState )
147 /* Make the new transitions. */
148 RedStateAp *targState = targ >= 0 ? (allStates + targ) :
149 wantComplete ? redFsm->getErrorState() : 0;
150 RedAction *actionTable = action >= 0 ? (allActionTables + action) : 0;
151 RedTransAp *trans = redFsm->allocateTrans( targState, actionTable );
152 RedTransEl transEl( lowKey, highKey, trans );
154 if ( wantComplete ) {
155 /* If the machine is to be complete then we need to fill any gaps with
156 * the error transitions. */
157 if ( destRange.length() == 0 ) {
158 /* Range is currently empty. */
159 if ( keyOps->minKey < lowKey ) {
160 /* The first range doesn't start at the low end. */
161 Key fillHighKey = lowKey;
162 fillHighKey.decrement();
164 /* Create the filler with the state's error transition. */
165 RedTransEl newTel( keyOps->minKey, fillHighKey, redFsm->getErrorTrans() );
166 destRange.append( newTel );
170 /* The range list is not empty, get the the last range. */
171 RedTransEl *last = &destRange[destRange.length()-1];
172 Key nextKey = last->highKey;
174 if ( nextKey < lowKey ) {
175 /* There is a gap to fill. Make the high key. */
176 Key fillHighKey = lowKey;
177 fillHighKey.decrement();
179 /* Create the filler with the state's error transtion. */
180 RedTransEl newTel( nextKey, fillHighKey, redFsm->getErrorTrans() );
181 destRange.append( newTel );
186 /* Filler taken care of. Append the range. */
187 destRange.append( RedTransEl( lowKey, highKey, trans ) );
190 void CodeGenData::finishTransList( int snum )
192 /* Get the current state and range. */
193 RedStateAp *curState = allStates + snum;
194 RedTransList &destRange = curState->outRange;
196 if ( curState == redFsm->errState )
199 /* If building a complete machine we may need filler on the end. */
200 if ( wantComplete ) {
201 /* Check if there are any ranges already. */
202 if ( destRange.length() == 0 ) {
203 /* Fill with the whole alphabet. */
204 /* Add the range on the lower and upper bound. */
205 RedTransEl newTel( keyOps->minKey, keyOps->maxKey, redFsm->getErrorTrans() );
206 destRange.append( newTel );
209 /* Get the last and check for a gap on the end. */
210 RedTransEl *last = &destRange[destRange.length()-1];
211 if ( last->highKey < keyOps->maxKey ) {
212 /* Make the high key. */
213 Key fillLowKey = last->highKey;
214 fillLowKey.increment();
216 /* Create the new range with the error trans and append it. */
217 RedTransEl newTel( fillLowKey, keyOps->maxKey, redFsm->getErrorTrans() );
218 destRange.append( newTel );
224 void CodeGenData::setId( int snum, int id )
226 RedStateAp *curState = allStates + snum;
230 void CodeGenData::setFinal( int snum )
232 RedStateAp *curState = allStates + snum;
233 curState->isFinal = true;
237 void CodeGenData::setStateActions( int snum, long toStateAction,
238 long fromStateAction, long eofAction )
240 RedStateAp *curState = allStates + snum;
241 if ( toStateAction >= 0 )
242 curState->toStateAction = allActionTables + toStateAction;
243 if ( fromStateAction >= 0 )
244 curState->fromStateAction = allActionTables + fromStateAction;
245 if ( eofAction >= 0 )
246 curState->eofAction = allActionTables + eofAction;
249 void CodeGenData::resolveTargetStates( InlineList *inlineList )
251 for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
252 switch ( item->type ) {
253 case InlineItem::Goto: case InlineItem::Call:
254 case InlineItem::Next: case InlineItem::Entry:
255 item->targState = allStates + item->targId;
261 if ( item->children != 0 )
262 resolveTargetStates( item->children );
266 void CodeGenData::closeMachine()
268 for ( ActionList::Iter a = actionList; a.lte(); a++ )
269 resolveTargetStates( a->inlineList );
271 /* Note that even if we want a complete graph we do not give the error
272 * state a default transition. All machines break out of the processing
273 * loop when in the error state. */
275 for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
276 for ( StateCondList::Iter sci = st->stateCondList; sci.lte(); sci++ )
277 st->stateCondVect.append( sci );
282 bool CodeGenData::setAlphType( char *data )
284 HostType *alphType = findAlphTypeInternal( data );
288 thisKeyOps.setAlphType( alphType );
292 void CodeGenData::initCondSpaceList( ulong length )
294 allCondSpaces = new CondSpace[length];
295 for ( ulong c = 0; c < length; c++ )
296 condSpaceList.append( allCondSpaces + c );
299 void CodeGenData::newCondSpace( int cnum, int condSpaceId, Key baseKey )
301 CondSpace *cond = allCondSpaces + cnum;
302 cond->condSpaceId = condSpaceId;
303 cond->baseKey = baseKey;
306 void CodeGenData::condSpaceItem( int cnum, long condActionId )
308 CondSpace *cond = allCondSpaces + cnum;
309 cond->condSet.append( allActions + condActionId );
312 void CodeGenData::initStateCondList( int snum, ulong length )
314 /* Could preallocate these, as we could with transitions. */
317 void CodeGenData::addStateCond( int snum, Key lowKey, Key highKey, long condNum )
319 RedStateAp *curState = allStates + snum;
321 /* Create the new state condition. */
322 StateCond *stateCond = new StateCond;
323 stateCond->lowKey = lowKey;
324 stateCond->highKey = highKey;
326 /* Assign it a cond space. */
327 CondSpace *condSpace = allCondSpaces + condNum;
328 stateCond->condSpace = condSpace;
330 curState->stateCondList.append( stateCond );
334 CondSpace *CodeGenData::findCondSpace( Key lowKey, Key highKey )
336 for ( CondSpaceList::Iter cs = condSpaceList; cs.lte(); cs++ ) {
337 Key csHighKey = cs->baseKey;
338 csHighKey += keyOps->alphSize() * (1 << cs->condSet.length());
340 if ( lowKey >= cs->baseKey && highKey <= csHighKey )
346 Condition *CodeGenData::findCondition( Key key )
348 for ( ConditionList::Iter cond = conditionList; cond.lte(); cond++ ) {
349 Key upperKey = cond->baseKey + (1 << cond->condSet.length());
350 if ( cond->baseKey <= key && key <= upperKey )
356 Key CodeGenData::findMaxKey()
358 Key maxKey = keyOps->maxKey;
359 for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
360 assert( st->outSingle.length() == 0 );
361 assert( st->defTrans == 0 );
363 long rangeLen = st->outRange.length();
364 if ( rangeLen > 0 ) {
365 Key highKey = st->outRange[rangeLen-1].highKey;
366 if ( highKey > maxKey )
373 void CodeGenData::findFinalActionRefs()
375 for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
376 /* Rerence count out of single transitions. */
377 for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
378 if ( rtel->value->action != 0 ) {
379 rtel->value->action->numTransRefs += 1;
380 for ( ActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
381 item->value->numTransRefs += 1;
385 /* Reference count out of range transitions. */
386 for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
387 if ( rtel->value->action != 0 ) {
388 rtel->value->action->numTransRefs += 1;
389 for ( ActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
390 item->value->numTransRefs += 1;
394 /* Reference count default transition. */
395 if ( st->defTrans != 0 && st->defTrans->action != 0 ) {
396 st->defTrans->action->numTransRefs += 1;
397 for ( ActionTable::Iter item = st->defTrans->action->key; item.lte(); item++ )
398 item->value->numTransRefs += 1;
401 /* Reference count to state actions. */
402 if ( st->toStateAction != 0 ) {
403 st->toStateAction->numToStateRefs += 1;
404 for ( ActionTable::Iter item = st->toStateAction->key; item.lte(); item++ )
405 item->value->numToStateRefs += 1;
408 /* Reference count from state actions. */
409 if ( st->fromStateAction != 0 ) {
410 st->fromStateAction->numFromStateRefs += 1;
411 for ( ActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ )
412 item->value->numFromStateRefs += 1;
415 /* Reference count EOF actions. */
416 if ( st->eofAction != 0 ) {
417 st->eofAction->numEofRefs += 1;
418 for ( ActionTable::Iter item = st->eofAction->key; item.lte(); item++ )
419 item->value->numEofRefs += 1;
424 void CodeGenData::analyzeAction( Action *act, InlineList *inlineList )
426 for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
427 /* Only consider actions that are referenced. */
428 if ( act->numRefs() > 0 ) {
429 if ( item->type == InlineItem::Goto || item->type == InlineItem::GotoExpr )
430 redFsm->bAnyActionGotos = true;
431 else if ( item->type == InlineItem::Call || item->type == InlineItem::CallExpr )
432 redFsm->bAnyActionCalls = true;
433 else if ( item->type == InlineItem::Ret )
434 redFsm->bAnyActionRets = true;
437 /* Check for various things in regular actions. */
438 if ( act->numTransRefs > 0 || act->numToStateRefs > 0 || act->numFromStateRefs > 0 ) {
439 /* Any returns in regular actions? */
440 if ( item->type == InlineItem::Ret )
441 redFsm->bAnyRegActionRets = true;
443 /* Any next statements in the regular actions? */
444 if ( item->type == InlineItem::Next || item->type == InlineItem::NextExpr )
445 redFsm->bAnyRegNextStmt = true;
447 /* Any by value control in regular actions? */
448 if ( item->type == InlineItem::CallExpr || item->type == InlineItem::GotoExpr )
449 redFsm->bAnyRegActionByValControl = true;
451 /* Any references to the current state in regular actions? */
452 if ( item->type == InlineItem::Curs )
453 redFsm->bAnyRegCurStateRef = true;
455 if ( item->type == InlineItem::Break )
456 redFsm->bAnyRegBreak = true;
459 if ( item->children != 0 )
460 analyzeAction( act, item->children );
464 void CodeGenData::analyzeActionList( RedAction *redAct, InlineList *inlineList )
466 for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
467 /* Any next statements in the action table? */
468 if ( item->type == InlineItem::Next || item->type == InlineItem::NextExpr )
469 redAct->bAnyNextStmt = true;
471 /* Any references to the current state. */
472 if ( item->type == InlineItem::Curs )
473 redAct->bAnyCurStateRef = true;
475 if ( item->type == InlineItem::Break )
476 redAct->bAnyBreakStmt = true;
478 if ( item->children != 0 )
479 analyzeActionList( redAct, item->children );
483 /* Assign ids to referenced actions. */
484 void CodeGenData::assignActionIds()
486 int nextActionId = 0;
487 for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
488 /* Only ever interested in referenced actions. */
489 if ( act->numRefs() > 0 )
490 act->actionId = nextActionId++;
494 void CodeGenData::setValueLimits()
496 redFsm->maxSingleLen = 0;
497 redFsm->maxRangeLen = 0;
498 redFsm->maxKeyOffset = 0;
499 redFsm->maxIndexOffset = 0;
500 redFsm->maxActListId = 0;
501 redFsm->maxActionLoc = 0;
502 redFsm->maxActArrItem = 0;
504 redFsm->maxCondSpan = 0;
505 redFsm->maxFlatIndexOffset = 0;
506 redFsm->maxCondOffset = 0;
507 redFsm->maxCondLen = 0;
508 redFsm->maxCondSpaceId = 0;
509 redFsm->maxCondIndexOffset = 0;
511 /* In both of these cases the 0 index is reserved for no value, so the max
512 * is one more than it would be if they started at 0. */
513 redFsm->maxIndex = redFsm->transSet.length();
514 redFsm->maxCond = condSpaceList.length();
516 /* The nextStateId - 1 is the last state id assigned. */
517 redFsm->maxState = redFsm->nextStateId - 1;
519 for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) {
520 if ( csi->condSpaceId > redFsm->maxCondSpaceId )
521 redFsm->maxCondSpaceId = csi->condSpaceId;
524 for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
525 /* Maximum cond length. */
526 if ( st->stateCondList.length() > redFsm->maxCondLen )
527 redFsm->maxCondLen = st->stateCondList.length();
529 /* Maximum single length. */
530 if ( st->outSingle.length() > redFsm->maxSingleLen )
531 redFsm->maxSingleLen = st->outSingle.length();
533 /* Maximum range length. */
534 if ( st->outRange.length() > redFsm->maxRangeLen )
535 redFsm->maxRangeLen = st->outRange.length();
537 /* The key offset index offset for the state after last is not used, skip it.. */
539 redFsm->maxCondOffset += st->stateCondList.length();
540 redFsm->maxKeyOffset += st->outSingle.length() + st->outRange.length()*2;
541 redFsm->maxIndexOffset += st->outSingle.length() + st->outRange.length() + 1;
545 if ( st->condList != 0 ) {
546 unsigned long long span = keyOps->span( st->condLowKey, st->condHighKey );
547 if ( span > redFsm->maxCondSpan )
548 redFsm->maxCondSpan = span;
552 if ( st->transList != 0 ) {
553 unsigned long long span = keyOps->span( st->lowKey, st->highKey );
554 if ( span > redFsm->maxSpan )
555 redFsm->maxSpan = span;
558 /* Max cond index offset. */
560 if ( st->condList != 0 )
561 redFsm->maxCondIndexOffset += keyOps->span( st->condLowKey, st->condHighKey );
564 /* Max flat index offset. */
566 if ( st->transList != 0 )
567 redFsm->maxFlatIndexOffset += keyOps->span( st->lowKey, st->highKey );
568 redFsm->maxFlatIndexOffset += 1;
572 for ( ActionTableMap::Iter at = redFsm->actionMap; at.lte(); at++ ) {
573 /* Maximum id of action lists. */
574 if ( at->actListId+1 > redFsm->maxActListId )
575 redFsm->maxActListId = at->actListId+1;
577 /* Maximum location of items in action array. */
578 if ( at->location+1 > redFsm->maxActionLoc )
579 redFsm->maxActionLoc = at->location+1;
581 /* Maximum values going into the action array. */
582 if ( at->key.length() > redFsm->maxActArrItem )
583 redFsm->maxActArrItem = at->key.length();
584 for ( ActionTable::Iter item = at->key; item.lte(); item++ ) {
585 if ( item->value->actionId > redFsm->maxActArrItem )
586 redFsm->maxActArrItem = item->value->actionId;
593 /* Gather various info on the machine. */
594 void CodeGenData::analyzeMachine()
596 /* Find the true count of action references. */
597 findFinalActionRefs();
599 /* Check if there are any calls in action code. */
600 for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
601 /* Record the occurrence of various kinds of actions. */
602 if ( act->numToStateRefs > 0 )
603 redFsm->bAnyToStateActions = true;
604 if ( act->numFromStateRefs > 0 )
605 redFsm->bAnyFromStateActions = true;
606 if ( act->numEofRefs > 0 )
607 redFsm->bAnyEofActions = true;
608 if ( act->numTransRefs > 0 )
609 redFsm->bAnyRegActions = true;
611 /* Recurse through the action's parse tree looking for various things. */
612 analyzeAction( act, act->inlineList );
615 /* Analyze reduced action lists. */
616 for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
617 for ( ActionTable::Iter act = redAct->key; act.lte(); act++ )
618 analyzeActionList( redAct, act->value->inlineList );
621 /* Find states that have transitions with actions that have next
623 for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
624 /* Check any actions out of outSinge. */
625 for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
626 if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
627 st->bAnyRegCurStateRef = true;
630 /* Check any actions out of outRange. */
631 for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
632 if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
633 st->bAnyRegCurStateRef = true;
636 /* Check any action out of default. */
637 if ( st->defTrans != 0 && st->defTrans->action != 0 &&
638 st->defTrans->action->anyCurStateRef() )
639 st->bAnyRegCurStateRef = true;
641 if ( st->stateCondList.length() > 0 )
642 redFsm->bAnyConditions = true;
645 /* Assign ids to actions that are referenced. */
648 /* Set the maximums of various values used for deciding types. */
652 void CodeGenData::writeStatement( InputLoc &loc, int nargs, char **args )
654 /* FIXME: This should be moved to the virtual functions in the code
657 * Force a newline. */
659 genLineDirective( out );
661 if ( strcmp( args[0], "data" ) == 0 ) {
662 for ( int i = 1; i < nargs; i++ ) {
663 if ( strcmp( args[i], "noerror" ) == 0 )
665 else if ( strcmp( args[i], "noprefix" ) == 0 )
667 else if ( strcmp( args[i], "nofinal" ) == 0 )
668 writeFirstFinal = false;
670 source_warning(loc) << "unrecognized write option \"" <<
671 args[i] << "\"" << endl;
676 else if ( strcmp( args[0], "init" ) == 0 ) {
677 for ( int i = 1; i < nargs; i++ ) {
678 if ( strcmp( args[i], "nocs" ) == 0 )
681 source_warning(loc) << "unrecognized write option \"" <<
682 args[i] << "\"" << endl;
687 else if ( strcmp( args[0], "exec" ) == 0 ) {
688 for ( int i = 1; i < nargs; i++ ) {
689 if ( strcmp( args[i], "noend" ) == 0 )
692 source_warning(loc) << "unrecognized write option \"" <<
693 args[i] << "\"" << endl;
698 else if ( strcmp( args[0], "eof" ) == 0 ) {
699 for ( int i = 1; i < nargs; i++ ) {
700 source_warning(loc) << "unrecognized write option \"" <<
701 args[i] << "\"" << endl;
705 else if ( strcmp( args[0], "exports" ) == 0 ) {
706 for ( int i = 1; i < nargs; i++ ) {
707 source_warning(loc) << "unrecognized write option \"" <<
708 args[i] << "\"" << endl;
713 /* EMIT An error here. */
714 source_error(loc) << "unrecognized write command \"" <<
715 args[0] << "\"" << endl;
719 ostream &CodeGenData::source_warning( const InputLoc &loc )
721 cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": warning: ";
725 ostream &CodeGenData::source_error( const InputLoc &loc )
728 assert( sourceFileName != 0 );
729 cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": ";