/* * main.c * Author: Hannah C. Tang (hctang@cs) * * The driver for the LISHP * (Light Interfacing Santa-Helper Protocol) * compiler * * $Id: main.c,v 1.6 2001/12/19 06:56:42 cse466_t Exp $ * */ #include #include /* For exit() */ #include #include /* For open(), close(), flags, etc */ #include /* For read, close */ #include #include "Sequence.h" #include "Reader.h" #include "Writer.h" #include "Stack.h" #include "common.h" /* * Helper functions */ void printHelp( const char *pProgName ); void compile( FILE *pInFile, int outFD ); void writeOutput( int outFD, void *pOutput, size_t outputSize, enum Reader_TokenType outputType ); void debugPrintProgram( void *pOutput, size_t outputSize ); void debugPrintMode( void *pOutput, size_t outputSize ); /* * Main */ int main( int argc, const char *argv[] ) { const char *pInFileName = NULL; FILE *pInFile = NULL; int outFD = 1; /* 1 is stdout -- this is what we want to default to */ /* Parse arguments */ switch( argc ) { case 3: /* Two arguments -- the device name and the ... */ outFD = open( argv[ 2 ], O_WRONLY | O_TRUNC | O_CREAT ); if( outFD == -1 ) { fprintf( stderr, "Error opening device %s!\n", argv[ 2 ] ); perror( "" ); return -1; } case 2: /* The file name */ pInFileName = argv[ 1 ]; break; case 1: /* No arguments -- go with the defaults */ break; default: printHelp( argv[ 0 ] ); return 0; break; } /* User wants to read from stdin */ if( pInFileName == NULL ) { pInFile = stdin; } else { pInFile = fopen( pInFileName, "r" ); if( !pInFile ) { fprintf( stderr, "Unable to open input file %s for reading!\n", pInFileName ); exit( 0 ); } } /* Compile the program and write it to the file corresponding to outFD */ compile( pInFile, outFD ); /* Release resources */ close( outFD ); return 0; } void printHelp( const char *pProgName ) { printf( "SYNTAX\n" ); printf( "\t%s [FILE] [DEVICE]\n", pProgName ); printf( "\n" ); printf( "Compiles lishp\n" ); printf( " (Light Interfacing Santa-Helper [non-denominational, of course] Protocol)\n" ); printf( " programs and pass the result to DEVICE. " ); printf( "FILE defaults to\n" ); printf( " stdin. DEVICE defaults to stdout\n" ); } void compile( FILE *pInFile, int outFD ) { /* Parser, code gen, and parens-matching modules respectively */ struct Reader *pR = Reader_malloc( pInFile ); struct Writer *pW = Writer_malloc(); struct Stack *pS = Stack_malloc(); struct Sequence progSeq; /* Currently seen program Sequence */ unsigned int modeSeq; /* Currently seen mode Sequence */ enum Reader_TokenType t; /* Currently seen token */ unsigned int blockArg, /* Arg to a block -- that is, a loop or mode */ loopBeginning; /* Writer's "index" to the beginning of a loop */ unsigned int length, period, mode; /* Temp vars to hold values between */ Bool parsingProg = Bool_false, /* Flags for program state */ parsingMode = Bool_false, infiniteLoopSeen = Bool_false; struct Sequence *pBuffer; /* The Writer's output buffer and its size */ size_t bufferSize; /* Parse and codegen until EOF or the cows come home, whichever comes first */ do { /* * Parse */ t = Reader_getNextTokenType( pR ); switch( t ) { case MODE_START: if( !Reader_getMode( pR, &blockArg ) ) { fprintf( stderr, "Unable to get mode number\n" ); exit( 0 ); } parsingMode = Bool_true; break; case PROGRAM_START: if( !Reader_getProgram( pR ) ) { fprintf( stderr, "Unable to get program token\n" ); exit( 0 ); } parsingProg = Bool_true; break; case SEQUENCE: if( parsingMode ) { /* It's a normal mode sequence */ t = MODE_SEQUENCE; if( !Reader_getModeSequence( pR, &modeSeq ) ) { fprintf( stderr, "Unable to get mode sequence\n" ); exit( 0 ); } } else if( parsingProg ) { /* It's a normal program sequence */ t = PROGRAM_SEQUENCE; if( !Reader_getSequence( pR, &length, &period, &mode ) ) { fprintf( stderr, "Unable to get sequence\n" ); exit( 0 ); } else { if( !Sequence_setAll( &progSeq, length, period, mode ) ) { fprintf( stderr, "Invalid length, period, or mode" " value. length %d, period %d, mode %d\n", length, period, mode ); exit( 0 ); } } if( infiniteLoopSeen ) { fprintf( stderr, "Warning: sequence occurs after infinite loop\n" ); } } else { /* Where does this sequence occur?! */ fprintf( stderr, "Sequence specified in unknown scope!\n" ); exit( 0 ); } break; case LOOP_START: if( !Reader_getLoop( pR, &blockArg ) ) { fprintf( stderr, "Unable to get number of loops\n" ); exit( 0 ); } break; case BLOCK_END: if( !Reader_getBlockEnd( pR ) ) { fprintf( stderr, "Unable to get block end\n" ); exit( 0 ); } break; case END_OF_FILE: /* Do nothing at the end of the file */ break; default: fprintf( stderr, "Unknown token type %d found!\n", t ); exit( 0 ); break; } /* * Code gen */ switch( t ) { case PROGRAM_START: /* Programs must be defined at the top level of bracing */ if( !Stack_isEmpty( pS ) ) { fprintf( stderr, "Error: program must be defined at" " global scope\n" ); exit( 0 ); } /* Store the top of this program to pop later */ Stack_push( pS, Writer_getNumSequences( pW ), PROGRAM_START, (unsigned int) -1 ); Writer_appendSequence( pW, &PROGRAM_BEGIN ); break; case PROGRAM_SEQUENCE: /* Program sequences must be defined within a program's scope */ if( Stack_isEmpty( pS ) ) { fprintf( stderr, "Error: Program sequence cannot occur " " at global scope\n" ); exit( 0 ); } Writer_appendSequence( pW, &progSeq ); break; case MODE_START: /* Modes must be defined at the top level of bracing */ if( !Stack_isEmpty( pS ) ) { fprintf( stderr, "Error: mode must be defined at" " global scope\n" ); exit( 0 ); } /* Store the top of this mode to pop later */ Stack_push( pS, Writer_getNumSequences( pW ), MODE_START, blockArg ); Writer_appendSequence( pW, &MODE_BEGIN ); if( blockArg < 0 || blockArg > 0xF ) { /* We do not handle more than 16 modes */ fprintf( stderr, "Invalid mode number %d\n", blockArg ); exit( 0 ); } Writer_appendSequence( pW, &progSeq ); break; case MODE_SEQUENCE: /* Mode sequences must be defined within a mode's scope */ if( Stack_isEmpty( pS ) ) { fprintf( stderr, "Error: Mode sequence cannot occur" " at global scope\n" ); exit( 0 ); } Writer_appendModeSequence( pW, modeSeq ); break; case LOOP_START: /* Loops must be defined within a program's scope */ if( Stack_isEmpty( pS ) ) { fprintf( stderr, "Error: Loop cannot occur at" " global scope\n" ); exit( 0 ); } /* Store the top of this loop to pop later. If this is the beginning of the infinite loop (marked by a 0), write the loop-beginning flag */ Stack_push( pS, Writer_getNumSequences( pW ), LOOP_START, blockArg ); if( blockArg == 0 ) { Writer_appendSequence( pW, &INF_LOOP_BEGIN ); } break; case BLOCK_END: /* End of block -- find out what block-beginning it matched */ Stack_pop( pS, &loopBeginning, &t, &blockArg ); /* Figure out what kind of block we just finished */ switch( t ) { case PROGRAM_START: case MODE_START: /* Write our current mode/program */ pBuffer = Writer_getBuffer( pW, &bufferSize ); writeOutput( outFD, pBuffer, bufferSize, t ); /* Restart the compilation */ Writer_clearBuffer( pW ); parsingMode = Bool_false; parsingProg = Bool_false; break; case LOOP_START: /* If this was the end of the infinite loop, we need to write a "Goto infinite loop" */ if( blockArg == 0 ) { Writer_appendSequence( pW, &GOTO_LOOP ); infiniteLoopSeen = Bool_true; } /* Otherwise, this is a finite loop we need to unroll */ else { Writer_appendSequencesBetween( pW, loopBeginning, Writer_getNumSequences( pW ) - 1, blockArg - 1 ); } break; default: fprintf( stderr, "Unknown block type %d found!\n", t ); break; } break; /* End outer case statement */ case END_OF_FILE: break; default: fprintf( stderr, "Unknown token type %d found!\n", t ); exit( 0 ); break; } } while( t != END_OF_FILE ); /* Release resources */ Stack_free( pS ); Reader_free( pR ); Writer_free( pW ); } void writeOutput( int outFD, void *pOutput, size_t outputSize, enum Reader_TokenType outputType ) { /* DEBUG: Remove these cascaded if's if you don't want to see debug output */ if( outputType == PROGRAM_START ) { debugPrintProgram( pOutput, outputSize ); } else if( outputType == MODE_START ) { debugPrintMode( pOutput, outputSize ); } else { /* Unrecognized type of token. This shouldn't happen */ exit( 0 ); } /* Write the data to the file associated with this file descriptor */ if( outputSize != write( outFD, pOutput, outputSize ) ) { fprintf( stderr, "Error writing %d bytes to device!\n", outputSize ); perror( "" ); exit( 0 ); } } void debugPrintProgram( void *pOutput, size_t outputSize ) { unsigned int i, numSequences = outputSize / sizeof( struct Sequence ); struct Sequence *pSequences; fprintf( stderr, "\nWriting %d program sequences (%d bytes)\n", numSequences, outputSize ); pSequences = ( struct Sequence * ) pOutput; for( i = 0; i < numSequences; i++ ) { Sequence_print( &pSequences[ i ], stderr ); fprintf( stderr, "\n" ); } } void debugPrintMode( void *pOutput, size_t outputSize ) { unsigned int i, numModeSequences; unsigned int *pSequences; /* This looks weird because the first two logical elements of this * buffer are program sequences, and the rest are mode sequences */ /* Number of mode sequences */ numModeSequences = ( outputSize - 2*sizeof(struct Sequence) ) / sizeof( unsigned int ); fprintf( stderr, "\nWriting %d mode sequences (%d bytes)\n", numModeSequences + 2, outputSize ); /* Print the first two (program) sequences */ Sequence_print( &( (struct Sequence*)pOutput)[ 0 ], stderr ); fprintf( stderr, "\n" ); Sequence_print( &( (struct Sequence*)pOutput)[ 1 ], stderr ); fprintf( stderr, "\n" ); /* Loop through the rest of the (mode) sequences. Notice that we had to index funnily into pOutput to get to the first mode sequence */ pSequences = ( unsigned int* )( (struct Sequence*)pOutput + 2 ); for( i = 0; i < numModeSequences; i++ ) { fprintf( stderr, " %x\n", pSequences[ i ] ); } }