/*
 * Copyright 2006-2018 Adrian Thurston <thurston@colm.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "redfsm.h"

#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>

#include <sstream>
#include <iostream>

#include "fsmgraph.h"
#include "parsetree.h"

using std::ostringstream;

string nameOrLoc( GenAction *genAction )
{
	if ( genAction->name != 0 )
		return string(genAction->name);
	else {
		ostringstream ret;
		ret << genAction->loc_line << ":" << genAction->loc_col;
		return ret.str();
	}
}

RedFsm::RedFsm()
:
	wantComplete(false),
	forcedErrorState(false),
	nextActionId(0),
	nextTransId(0),
	errState(0),
	errTrans(0),
	firstFinState(0),
	numFinStates(0),
	allActions(0),
	allActionTables(0),
	allStates(0),
	bAnyToStateActions(false),
	bAnyFromStateActions(false),
	bAnyRegActions(false),
	bAnyEofActions(false),
	bAnyActionGotos(false),
	bAnyActionCalls(false),
	bAnyActionRets(false),
	bAnyRegActionRets(false),
	bAnyRegActionByValControl(false),
	bAnyRegNextStmt(false),
	bAnyRegCurStateRef(false),
	bAnyRegBreak(false),
	bAnyLmSwitchError(false),
	bAnyConditions(false)
{
}

/* Does the machine have any actions. */
bool RedFsm::anyActions()
{
	return actionMap.length() > 0;
}

void RedFsm::depthFirstOrdering( RedState *state )
{
	/* Nothing to do if the state is already on the list. */
	if ( state->onStateList )
		return;

	/* Doing depth first, put state on the list. */
	state->onStateList = true;
	stateList.append( state );
	
//	/* At this point transitions should only be in ranges. */
//	assert( state->outSingle.length() == 0 );
//	assert( state->defTrans == 0 );

	/* Recurse on singles. */
	for ( RedTransList::Iter stel = state->outSingle; stel.lte(); stel++ ) {
		if ( stel->value->targ != 0 )
			depthFirstOrdering( stel->value->targ );
	}

	/* Recurse on everything ranges. */
	for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
		if ( rtel->value->targ != 0 )
			depthFirstOrdering( rtel->value->targ );
	}

	if ( state->defTrans != 0 && state->defTrans->targ != 0 )
		depthFirstOrdering( state->defTrans->targ );
}

/* Ordering states by transition connections. */
void RedFsm::depthFirstOrdering()
{
	/* Init on state list flags. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		st->onStateList = false;
	
	/* Clear out the state list, we will rebuild it. */
	int stateListLen = stateList.length();
	stateList.abandon();

	/* Add back to the state list from the start state and all other entry
	 * points. */
	depthFirstOrdering( startState );
	for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ )
		depthFirstOrdering( *en );
	if ( forcedErrorState )
		depthFirstOrdering( errState );
	
	/* Make sure we put everything back on. */
	assert( stateListLen == stateList.length() );
}

/* Assign state ids by appearance in the state list. */
void RedFsm::sequentialStateIds()
{
	/* Table based machines depend on the state numbers starting at zero. */
	nextStateId = 0;
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		st->id = nextStateId++;
}

/* Stable sort the states by final state status. */
void RedFsm::sortStatesByFinal()
{
	/* Move forward through the list and throw final states onto the end. */
	RedState *state = 0;
	RedState *next = stateList.head;
	RedState *last = stateList.tail;
	while ( state != last ) {
		/* Move forward and load up the next. */
		state = next;
		next = state->next;

		/* Throw to the end? */
		if ( state->isFinal ) {
			stateList.detach( state );
			stateList.append( state );
		}
	}
}

/* Assign state ids by final state state status. */
void RedFsm::sortStateIdsByFinal()
{
	/* Table based machines depend on this starting at zero. */
	nextStateId = 0;

	/* First pass to assign non final ids. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		if ( ! st->isFinal ) 
			st->id = nextStateId++;
	}

	/* Second pass to assign final ids. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		if ( st->isFinal ) 
			st->id = nextStateId++;
	}
}

struct CmpStateById
{
	static int compare( RedState *st1, RedState *st2 )
	{
		if ( st1->id < st2->id )
			return -1;
		else if ( st1->id > st2->id )
			return 1;
		else
			return 0;
	}
};

void RedFsm::sortByStateId()
{
	/* Make the array. */
	int pos = 0;
	RedState **ptrList = new RedState*[stateList.length()];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		ptrList[pos++] = st;
	
	MergeSort<RedState*, CmpStateById> mergeSort;
	mergeSort.sort( ptrList, stateList.length() );

	stateList.abandon();
	for ( int st = 0; st < pos; st++ )
		stateList.append( ptrList[st] );

	delete[] ptrList;
}

/* Find the final state with the lowest id. */
void RedFsm::findFirstFinState()
{
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		if ( st->isFinal && (firstFinState == 0 || st->id < firstFinState->id) )
			firstFinState = st;
	}
}

void RedFsm::assignActionLocs()
{
	int nextLocation = 0;
	for ( GenActionTableMap::Iter act = actionMap; act.lte(); act++ ) {
		/* Store the loc, skip over the array and a null terminator. */
		act->location = nextLocation;
		nextLocation += act->key.length() + 1;		
	}
}

/* Check if we can extend the current range by displacing any ranges
 * ahead to the singles. */
bool RedFsm::canExtend( const RedTransList &list, int pos )
{
	/* Get the transition that we want to extend. */
	RedTrans *extendTrans = list[pos].value;

	/* Look ahead in the transition list. */
	for ( int next = pos + 1; next < list.length(); pos++, next++ ) {
		/* If they are not continuous then cannot extend. */
		Key nextKey = list[next].lowKey;
		nextKey.decrement();
		if ( list[pos].highKey != nextKey )
			break;

		/* Check for the extenstion property. */
		if ( extendTrans == list[next].value )
			return true;

		/* If the span of the next element is more than one, then don't keep
		 * checking, it won't be moved to single. */
		unsigned long long nextSpan = keyOps->span( list[next].lowKey, list[next].highKey );
		if ( nextSpan > 1 )
			break;
	}
	return false;
}

/* Move ranges to the singles list. */
void RedFsm::moveTransToSingle( RedState *state )
{
	RedTransList &range = state->outRange;
	RedTransList &single = state->outSingle;
	for ( int rpos = 0; rpos < range.length(); ) {
		/* Check if this is a range we can extend. */
		if ( canExtend( range, rpos ) ) {
			/* Transfer singles over. */
			while ( range[rpos].value != range[rpos+1].value ) {
				/* Transfer the range to single. */
				single.append( range[rpos+1] );
				range.remove( rpos+1 );
			}
			
			/* Extend. */
			range[rpos].highKey = range[rpos+1].highKey;
			range.remove( rpos+1 );
		}
		/* Maybe move it to the singles. */
		else if ( keyOps->span( range[rpos].lowKey, range[rpos].highKey ) == 1 ) {
			single.append( range[rpos] );
			range.remove( rpos );
		}
		else {
			/* Keeping it in the ranges. */
			rpos += 1;
		}
	}
}

/* Look through ranges and choose suitable single character transitions. */
void RedFsm::chooseSingle()
{
	/* Loop the states. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Rewrite the transition list taking out the suitable single
		 * transtions. */
		moveTransToSingle( st );
	}
}

void RedFsm::makeFlat()
{
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		st->condLowKey = 0;
		st->condHighKey = 0;

		if ( st->outRange.length() == 0 ) {
			st->lowKey = st->highKey = 0;
			st->transList = 0;
		}
		else {
			st->lowKey = st->outRange[0].lowKey;
			st->highKey = st->outRange[st->outRange.length()-1].highKey;
			unsigned long long span = keyOps->span( st->lowKey, st->highKey );
			st->transList = new RedTrans*[ span ];
			memset( st->transList, 0, sizeof(RedTrans*)*span );
			
			for ( RedTransList::Iter trans = st->outRange; trans.lte(); trans++ ) {
				unsigned long long base, trSpan;
				base = keyOps->span( st->lowKey, trans->lowKey )-1;
				trSpan = keyOps->span( trans->lowKey, trans->highKey );
				for ( unsigned long long pos = 0; pos < trSpan; pos++ )
					st->transList[base+pos] = trans->value;
			}

			/* Fill in the gaps with the default transition. */
			for ( unsigned long long pos = 0; pos < span; pos++ ) {
				if ( st->transList[pos] == 0 )
					st->transList[pos] = st->defTrans;
			}
		}
	}
}


/* A default transition has been picked, move it from the outRange to the
 * default pointer. */
void RedFsm::moveToDefault( RedTrans *defTrans, RedState *state )
{
	/* Rewrite the outRange, omitting any ranges that use 
	 * the picked default. */
	RedTransList outRange;
	for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
		/* If it does not take the default, copy it over. */
		if ( rtel->value != defTrans )
			outRange.append( *rtel );
	}

	/* Save off the range we just created into the state's range. */
	state->outRange.transfer( outRange );

	/* Store the default. */
	state->defTrans = defTrans;
}

bool RedFsm::alphabetCovered( RedTransList &outRange )
{
	/* Cannot cover without any out ranges. */
	if ( outRange.length() == 0 )
		return false;

	/* If the first range doesn't start at the the lower bound then the
	 * alphabet is not covered. */
	RedTransList::Iter rtel = outRange;
	if ( keyOps->minKey < rtel->lowKey )
		return false;

	/* Check that every range is next to the previous one. */
	rtel.increment();
	for ( ; rtel.lte(); rtel++ ) {
		Key highKey = rtel[-1].highKey;
		highKey.increment();
		if ( highKey != rtel->lowKey )
			return false;
	}

	/* The last must extend to the upper bound. */
	RedTransEl *last = &outRange[outRange.length()-1];
	if ( last->highKey < keyOps->maxKey )
		return false;

	return true;
}

RedTrans *RedFsm::chooseDefaultSpan( RedState *state )
{
	/* Make a set of transitions from the outRange. */
	RedTransPtrSet stateTransSet;
	for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ )
		stateTransSet.insert( rtel->value );
	
	/* For each transition in the find how many alphabet characters the
	 * transition spans. */
	unsigned long long *span = new unsigned long long[stateTransSet.length()];
	memset( span, 0, sizeof(unsigned long long) * stateTransSet.length() );
	for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
		/* Lookup the transition in the set. */
		RedTrans **inSet = stateTransSet.find( rtel->value );
		int pos = inSet - stateTransSet.data;
		span[pos] += keyOps->span( rtel->lowKey, rtel->highKey );
	}

	/* Find the max span, choose it for making the default. */
	RedTrans *maxTrans = 0;
	unsigned long long maxSpan = 0;
	for ( RedTransPtrSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) {
		if ( span[rtel.pos()] > maxSpan ) {
			maxSpan = span[rtel.pos()];
			maxTrans = *rtel;
		}
	}

	delete[] span;
	return maxTrans;
}

/* Pick default transitions from ranges for the states. */
void RedFsm::chooseDefaultSpan()
{
	/* Loop the states. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Only pick a default transition if the alphabet is covered. This
		 * avoids any transitions in the out range that go to error and avoids
		 * the need for an ERR state. */
		if ( alphabetCovered( st->outRange ) ) {
			/* Pick a default transition by largest span. */
			RedTrans *defTrans = chooseDefaultSpan( st );

			/* Rewrite the transition list taking out the transition we picked
			 * as the default and store the default. */
			moveToDefault( defTrans, st );
		}
	}
}

RedTrans *RedFsm::chooseDefaultGoto( RedState *state )
{
	/* Make a set of transitions from the outRange. */
	RedTransPtrSet stateTransSet;
	for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
		if ( rtel->value->targ == state->next )
			return rtel->value;
	}
	return 0;
}

void RedFsm::chooseDefaultGoto()
{
	/* Loop the states. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Pick a default transition. */
		RedTrans *defTrans = chooseDefaultGoto( st );
		if ( defTrans == 0 )
			defTrans = chooseDefaultSpan( st );

		/* Rewrite the transition list taking out the transition we picked
		 * as the default and store the default. */
		moveToDefault( defTrans, st );
	}
}

RedTrans *RedFsm::chooseDefaultNumRanges( RedState *state )
{
	/* Make a set of transitions from the outRange. */
	RedTransPtrSet stateTransSet;
	for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ )
		stateTransSet.insert( rtel->value );
	
	/* For each transition in the find how many ranges use the transition. */
	int *numRanges = new int[stateTransSet.length()];
	memset( numRanges, 0, sizeof(int) * stateTransSet.length() );
	for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
		/* Lookup the transition in the set. */
		RedTrans **inSet = stateTransSet.find( rtel->value );
		numRanges[inSet - stateTransSet.data] += 1;
	}

	/* Find the max number of ranges. */
	RedTrans *maxTrans = 0;
	int maxNumRanges = 0;
	for ( RedTransPtrSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) {
		if ( numRanges[rtel.pos()] > maxNumRanges ) {
			maxNumRanges = numRanges[rtel.pos()];
			maxTrans = *rtel;
		}
	}

	delete[] numRanges;
	return maxTrans;
}

void RedFsm::chooseDefaultNumRanges()
{
	/* Loop the states. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Pick a default transition. */
		RedTrans *defTrans = chooseDefaultNumRanges( st );

		/* Rewrite the transition list taking out the transition we picked
		 * as the default and store the default. */
		moveToDefault( defTrans, st );
	}
}

RedTrans *RedFsm::getErrorTrans( )
{
	/* If the error trans has not been made aready, make it. */
	if ( errTrans == 0 ) {
		/* This insert should always succeed since no transition created by
		 * the user can point to the error state. */
		errTrans = new RedTrans( getErrorState(), 0, nextTransId++ );
		RedTrans *inRes = transSet.insert( errTrans );
		assert( inRes != 0 );
	}
	return errTrans;
}

RedState *RedFsm::getErrorState()
{
	/* Something went wrong. An error state is needed but one was not supplied
	 * by the frontend. */
	assert( errState != 0 );
	return errState;
}


RedTrans *RedFsm::allocateTrans( RedState *targ, RedAction *action )
{
	/* Create a reduced trans and look for it in the transiton set. */
	RedTrans redTrans( targ, action, 0 );
	RedTrans *inDict = transSet.find( &redTrans );
	if ( inDict == 0 ) {
		inDict = new RedTrans( targ, action, nextTransId++ );
		transSet.insert( inDict );
	}
	return inDict;
}

void RedFsm::partitionFsm( int nparts )
{
	/* At this point the states are ordered by a depth-first traversal. We
	 * will allocate to partitions based on this ordering. */
	this->nParts = nparts;
	int partSize = stateList.length() / nparts;
	int remainder = stateList.length() % nparts;
	int numInPart = partSize;
	int partition = 0;
	if ( remainder-- > 0 )
		numInPart += 1;
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		st->partition = partition;

		numInPart -= 1;
		if ( numInPart == 0 ) {
			partition += 1;
			numInPart = partSize;
			if ( remainder-- > 0 )
				numInPart += 1;
		}
	}
}

void RedFsm::setInTrans()
{
	/* First pass counts the number of transitions. */
	for ( RedTransSet::Iter trans = transSet; trans.lte(); trans++ )
		trans->targ->numInTrans += 1;

	/* Pass over states to allocate the needed memory. Reset the counts so we
	 * can use them as the current size. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		st->inTrans = new RedTrans*[st->numInTrans];
		st->numInTrans = 0;
	}

	/* Second pass over transitions copies pointers into the in trans list. */
	for ( RedTransSet::Iter trans = transSet; trans.lte(); trans++ )
		trans->targ->inTrans[trans->targ->numInTrans++] = trans;
}

void RedFsm::setValueLimits()
{
	maxSingleLen = 0;
	maxRangeLen = 0;
	maxKeyOffset = 0;
	maxIndexOffset = 0;
	maxActListId = 0;
	maxActionLoc = 0;
	maxActArrItem = 0;
	maxSpan = 0;
	maxCondSpan = 0;
	maxFlatIndexOffset = 0;
	maxCondOffset = 0;
	maxCondLen = 0;
	maxCondSpaceId = 0;
	maxCondIndexOffset = 0;

	/* In both of these cases the 0 index is reserved for no value, so the max
	 * is one more than it would be if they started at 0. */
	maxIndex = transSet.length();
	maxCond = 0;

	/* The nextStateId - 1 is the last state id assigned. */
	maxState = nextStateId - 1;

	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Maximum single length. */
		if ( st->outSingle.length() > maxSingleLen )
			maxSingleLen = st->outSingle.length();

		/* Maximum range length. */
		if ( st->outRange.length() > maxRangeLen )
			maxRangeLen = st->outRange.length();

		/* The key offset index offset for the state after last is not used, skip it.. */
		if ( ! st.last() ) {
			maxKeyOffset += st->outSingle.length() + st->outRange.length()*2;
			maxIndexOffset += st->outSingle.length() + st->outRange.length() + 1;
		}

		/* Max key span. */
		if ( st->transList != 0 ) {
			unsigned long long span = keyOps->span( st->lowKey, st->highKey );
			if ( span > maxSpan )
				maxSpan = span;
		}

		/* Max flat index offset. */
		if ( ! st.last() ) {
			if ( st->transList != 0 )
				maxFlatIndexOffset += keyOps->span( st->lowKey, st->highKey );
			maxFlatIndexOffset += 1;
		}
	}

	for ( GenActionTableMap::Iter at = actionMap; at.lte(); at++ ) {
		/* Maximum id of action lists. */
		if ( at->actListId+1 > maxActListId )
			maxActListId = at->actListId+1;

		/* Maximum location of items in action array. */
		if ( at->location+1 > maxActionLoc )
			maxActionLoc = at->location+1;

		/* Maximum values going into the action array. */
		if ( at->key.length() > maxActArrItem )
			maxActArrItem = at->key.length();
		for ( GenActionTable::Iter item = at->key; item.lte(); item++ ) {
			if ( item->value->actionId > maxActArrItem )
				maxActArrItem = item->value->actionId;
		}
	}
}

void RedFsm::findFinalActionRefs()
{
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Rerence count out of single transitions. */
		for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
			if ( rtel->value->action != 0 ) {
				rtel->value->action->numTransRefs += 1;
				for ( GenActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
					item->value->numTransRefs += 1;
			}
		}

		/* Reference count out of range transitions. */
		for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
			if ( rtel->value->action != 0 ) {
				rtel->value->action->numTransRefs += 1;
				for ( GenActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
					item->value->numTransRefs += 1;
			}
		}

		/* Reference count default transition. */
		if ( st->defTrans != 0 && st->defTrans->action != 0 ) {
			st->defTrans->action->numTransRefs += 1;
			for ( GenActionTable::Iter item = st->defTrans->action->key; item.lte(); item++ )
				item->value->numTransRefs += 1;
		}

		/* Reference count to state actions. */
		if ( st->toStateAction != 0 ) {
			st->toStateAction->numToStateRefs += 1;
			for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ )
				item->value->numToStateRefs += 1;
		}

		/* Reference count from state actions. */
		if ( st->fromStateAction != 0 ) {
			st->fromStateAction->numFromStateRefs += 1;
			for ( GenActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ )
				item->value->numFromStateRefs += 1;
		}

		/* Reference count EOF actions. */
		if ( st->eofAction != 0 ) {
			st->eofAction->numEofRefs += 1;
			for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ )
				item->value->numEofRefs += 1;
		}
	}
}

void RedFsm::analyzeAction( GenAction *act, InlineList *inlineList )
{
	for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
		/* Check for various things in regular actions. */
		if ( act->numTransRefs > 0 || act->numToStateRefs > 0 || 
				act->numFromStateRefs > 0 || act->numEofRefs > 0 )
		{
			if ( item->type == InlineItem::LmSwitch && 
					item->tokenRegion->lmSwitchHandlesError )
			{
				bAnyLmSwitchError = true;
			}
		}

		if ( item->children != 0 )
			analyzeAction( act, item->children );
	}
}

void RedFsm::analyzeActionList( RedAction *redAct, InlineList *inlineList )
{
	for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
		if ( item->children != 0 )
			analyzeActionList( redAct, item->children );
	}
}

/* Assign ids to referenced actions. */
void RedFsm::assignActionIds()
{
	int nextActionId = 0;
	for ( GenActionList::Iter act = genActionList; act.lte(); act++ ) {
		/* Only ever interested in referenced actions. */
		if ( numRefs( act ) > 0 )
			act->actionId = nextActionId++;
	}
}

/* Gather various info on the machine. */
void RedFsm::analyzeMachine()
{
	/* Find the true count of action references.  */
	findFinalActionRefs();

	/* Check if there are any calls in action code. */
	for ( GenActionList::Iter act = genActionList; act.lte(); act++ ) {
		/* Record the occurrence of various kinds of actions. */
		if ( act->numToStateRefs > 0 )
			bAnyToStateActions = true;
		if ( act->numFromStateRefs > 0 )
			bAnyFromStateActions = true;
		if ( act->numEofRefs > 0 )
			bAnyEofActions = true;
		if ( act->numTransRefs > 0 )
			bAnyRegActions = true;

		/* Recurse through the action's parse tree looking for various things. */
		analyzeAction( act, act->inlineList );
	}

	/* Analyze reduced action lists. */
	for ( GenActionTableMap::Iter redAct = actionMap; redAct.lte(); redAct++ ) {
		for ( GenActionTable::Iter act = redAct->key; act.lte(); act++ )
			analyzeActionList( redAct, act->value->inlineList );
	}

	/* Find states that have transitions with actions that have next
	 * statements. */
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Check any actions out of outSinge. */
		for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
			if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
				st->bAnyRegCurStateRef = true;
		}

		/* Check any actions out of outRange. */
		for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
			if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
				st->bAnyRegCurStateRef = true;
		}

		/* Check any action out of default. */
		if ( st->defTrans != 0 && st->defTrans->action != 0 && 
				st->defTrans->action->anyCurStateRef() )
			st->bAnyRegCurStateRef = true;
	}

	/* Assign ids to actions that are referenced. */
	assignActionIds();

	/* Set the maximums of various values used for deciding types. */
	setValueLimits();
}

int transAction( RedTrans *trans )
{
	int retAct = 0;
	if ( trans->action != 0 )
		retAct = trans->action->location+1;
	return retAct;
}

int toStateAction( RedState *state )
{
	int act = 0;
	if ( state->toStateAction != 0 )
		act = state->toStateAction->location+1;
	return act;
}

int fromStateAction( RedState *state )
{
	int act = 0;
	if ( state->fromStateAction != 0 )
		act = state->fromStateAction->location+1;
	return act;
}

int eofAction( RedState *state )
{
	int act = 0;
	if ( state->eofAction != 0 )
		act = state->eofAction->location+1;
	return act;
}


fsm_tables *RedFsm::makeFsmTables()
{
	/* The fsm runtime needs states sorted by id. */
	sortByStateId();

	int pos, curKeyOffset, curIndOffset;
	fsm_tables *fsmTables = new fsm_tables;
	fsmTables->num_states = stateList.length();

	/*
	 * actions
	 */

	fsmTables->num_actions = 1;
	for ( GenActionTableMap::Iter act = actionMap; act.lte(); act++ )
		fsmTables->num_actions += 1 + act->key.length();

	pos = 0;
	fsmTables->actions = new long[fsmTables->num_actions];
	fsmTables->actions[pos++] = 0;
	for ( GenActionTableMap::Iter act = actionMap; act.lte(); act++ ) {
		fsmTables->actions[pos++] = act->key.length();
		for ( GenActionTable::Iter item = act->key; item.lte(); item++ )
			fsmTables->actions[pos++] = item->value->actionId;
	}

	/*
	 * keyOffset
	 */
	pos = 0, curKeyOffset = 0;
	fsmTables->key_offsets = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		/* Store the current offset. */
		fsmTables->key_offsets[pos++] = curKeyOffset;

		/* Move the key offset ahead. */
		curKeyOffset += st->outSingle.length() + st->outRange.length()*2;
	}

	/*
	 * transKeys
	 */
	fsmTables->num_trans_keys = 0;
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		fsmTables->num_trans_keys += st->outSingle.length();
		fsmTables->num_trans_keys += 2 * st->outRange.length();
	}

	pos = 0;
	fsmTables->trans_keys = new char[fsmTables->num_trans_keys];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ )
			fsmTables->trans_keys[pos++] = stel->lowKey.getVal();
		for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
			fsmTables->trans_keys[pos++] = rtel->lowKey.getVal();
			fsmTables->trans_keys[pos++] = rtel->highKey.getVal();
		}
	}

	/*
	 * singleLengths
	 */
	pos = 0;
	fsmTables->single_lengths = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		fsmTables->single_lengths[pos++] = st->outSingle.length();

	/*
	 * rangeLengths
	 */
	pos = 0;
	fsmTables->range_lengths = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		fsmTables->range_lengths[pos++] = st->outRange.length();

	/*
	 * indexOffsets
	 */
	pos = 0, curIndOffset = 0;
	fsmTables->index_offsets = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		fsmTables->index_offsets[pos++] = curIndOffset;

		curIndOffset += st->outSingle.length() + st->outRange.length();
		if ( st->defTrans != 0 )
			curIndOffset += 1;
	}

	/*
	 * transTargsWI
	 */
	fsmTables->numTransTargsWI = 0;
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		fsmTables->numTransTargsWI += st->outSingle.length();
		fsmTables->numTransTargsWI += st->outRange.length();
		if ( st->defTrans != 0 )
			fsmTables->numTransTargsWI += 1;
	}

	pos = 0;
	fsmTables->transTargsWI = new long[fsmTables->numTransTargsWI];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ )
			fsmTables->transTargsWI[pos++] = stel->value->targ->id;

		for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ )
			fsmTables->transTargsWI[pos++] = rtel->value->targ->id;

		if ( st->defTrans != 0 )
			fsmTables->transTargsWI[pos++] = st->defTrans->targ->id;
	}

	/*
	 * transActionsWI
	 */
	fsmTables->numTransActionsWI = 0;
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		fsmTables->numTransActionsWI += st->outSingle.length();
		fsmTables->numTransActionsWI += st->outRange.length();
		if ( st->defTrans != 0 )
			fsmTables->numTransActionsWI += 1;
	}

	pos = 0;
	fsmTables->transActionsWI = new long[fsmTables->numTransActionsWI];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ )
			fsmTables->transActionsWI[pos++] = transAction( stel->value );

		for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ )
			fsmTables->transActionsWI[pos++] = transAction( rtel->value );

		if ( st->defTrans != 0 )
			fsmTables->transActionsWI[pos++] = transAction( st->defTrans );
	}

	/*
	 * toStateActions
	 */
	pos = 0;
	fsmTables->to_state_actions = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		fsmTables->to_state_actions[pos++] = toStateAction( st );

	/*
	 * fromStateActions
	 */
	pos = 0;
	fsmTables->from_state_actions = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		fsmTables->from_state_actions[pos++] = fromStateAction( st );

	/*
	 * eofActions
	 */
	pos = 0;
	fsmTables->eof_actions = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ )
		fsmTables->eof_actions[pos++] = eofAction( st );

	/*
	 * eofTargs
	 */
	pos = 0;
	fsmTables->eof_targs = new long[fsmTables->num_states];
	for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
		int targ = -1;
		if ( st->eofTrans != 0 )
			targ = st->eofTrans->targ->id;
		fsmTables->eof_targs[pos++] = targ;
	}

	/* Start state. */
	fsmTables->start_state = startState->id;

	/* First final state. */
	fsmTables->first_final = ( firstFinState != 0 ) ?
		firstFinState->id : nextStateId;

	/* The error state. */
	fsmTables->error_state = ( errState != 0 ) ?
		errState->id : -1;

	/* The array pointing to actions. */
	pos = 0;
	fsmTables->num_action_switch = genActionList.length();
	fsmTables->action_switch = new GenAction*[fsmTables->num_action_switch];
	for ( GenActionList::Iter act = genActionList; act.lte(); act++ )
		fsmTables->action_switch[pos++] = act;
	
	/*
	 * entryByRegion
	 */

	fsmTables->num_regions = regionToEntry.length()+1;
	fsmTables->entry_by_region = new long[fsmTables->num_regions];
	fsmTables->entry_by_region[0] = fsmTables->error_state;

	pos = 1;
	for ( RegionToEntry::Iter en = regionToEntry; en.lte(); en++ ) {
		/* Find the entry state from the entry id. */
		RedEntryMapEl *entryMapEl = redEntryMap.find( *en );
		
		/* Save it off. */
		fsmTables->entry_by_region[pos++] = entryMapEl != 0 ? entryMapEl->value 
				: fsmTables->error_state;
	}
	
	return fsmTables;
}


