/* * Copyright 2015 Adrian Thurston */ /* This file is part of Ragel. * * Ragel is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Ragel is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Ragel; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "reducer.h" #include "if.h" #include #include #include using std::endl; using std::ifstream; extern colm_sections rlparse_object; void TopLevel::loadMachineName( string data ) { /* Make/get the priority key. The name may have already been referenced * and therefore exist. */ PriorDictEl *priorDictEl; if ( pd->priorDict.insert( data, pd->fsmCtx->nextPriorKey, &priorDictEl ) ) pd->fsmCtx->nextPriorKey += 1; pd->curDefPriorKey = priorDictEl->value; /* Make/get the local error key. */ LocalErrDictEl *localErrDictEl; if ( pd->localErrDict.insert( data, pd->nextLocalErrKey, &localErrDictEl ) ) pd->nextLocalErrKey += 1; pd->curDefLocalErrKey = localErrDictEl->value; } void TopLevel::tryMachineDef( InputLoc &loc, std::string name, MachineDef *machineDef, bool isInstance ) { GraphDictEl *newEl = pd->graphDict.insert( name ); if ( newEl != 0 ) { /* New element in the dict, all good. */ newEl->value = new VarDef( name, machineDef ); newEl->isInstance = isInstance; newEl->loc = loc; newEl->value->isExport = exportContext[exportContext.length()-1]; /* It it is an instance, put on the instance list. */ if ( isInstance ) pd->instanceList.append( newEl ); } else { // Recover by ignoring the duplicate. pd->id->error(loc) << "fsm \"" << name << "\" previously defined" << endl; } } long TopLevel::tryLongScan( const InputLoc &loc, const char *data ) { /* Convert the priority number to a long. Check for overflow. */ long priorityNum; errno = 0; long aug = strtol( data, 0, 10 ); if ( errno == ERANGE && aug == LONG_MAX ) { /* Priority number too large. Recover by setting the priority to 0. */ pd->id->error(loc) << "priority number " << data << " overflows" << endl; priorityNum = 0; } else if ( errno == ERANGE && aug == LONG_MIN ) { /* Priority number too large in the neg. Recover by using 0. */ pd->id->error(loc) << "priority number " << data << " underflows" << endl; priorityNum = 0; } else { /* No overflow or underflow. */ priorityNum = aug; } return priorityNum; } void TopLevel::loadIncludeData( IncludeRec *el, IncludePass &includePass, const string &fileName ) { /* Count bytes. */ size_t len = 0; for ( IncItem *ii = includePass.incItems.head; ii != 0; ii = ii->next ) len += ii->length; /* Store bytes. */ el->data = new char[len+1]; len = 0; if ( id->inLibRagel ) { for ( IncItem *ii = includePass.incItems.head; ii != 0; ii = ii->next ) { memcpy( el->data + len, id->input + ii->start, ii->length ); len += ii->length; } } else { for ( IncItem *ii = includePass.incItems.head; ii != 0; ii = ii->next ) { std::ifstream f( fileName.c_str() ); f.seekg( ii->start, std::ios::beg ); f.read( el->data + len, ii->length ); size_t read = f.gcount(); if ( read != ii->length ) { pd->id->error(ii->loc) << "unexpected length in read of included file: " "possible change to file" << endp; } len += read; } } el->data[len] = 0; el->len = len; } void TopLevel::include( const InputLoc &incLoc, bool fileSpecified, string fileName, string machine ) { /* Stash the current section name and pd. */ string sectionName = pd->sectionName; ParseData *pd0 = pd; IncludeRec *el = id->includeDict.find( FnMachine( fileName, machine ) ); if ( el == 0 ) { el = new IncludeRec( fileName, machine ); const char **includeChecks = 0; long found = 0; /* First collect the locations of the text using an include pass. */ IncludePass includePass( id, machine ); if ( id->inLibRagel && !fileSpecified ) { el->foundFileName = curFileName; /* In LibRagel and no file was specified in the include statement. * In this case we run the include pass on the input text supplied. */ includePass.reduceStr( fileName.c_str(), id->hostLang, id->input ); } else { const char *inclSectionName = machine.c_str(); /* Implement defaults for the input file and section name. */ if ( inclSectionName == 0 ) inclSectionName = sectionName.c_str(); if ( fileSpecified ) includeChecks = pd->id->makeIncludePathChecks( curFileName, fileName.c_str() ); else { char *test = new char[strlen(curFileName)+1]; strcpy( test, curFileName ); includeChecks = new const char*[2]; includeChecks[0] = test; includeChecks[1] = 0; } ifstream *inFile = pd->id->tryOpenInclude( includeChecks, found ); if ( inFile == 0 ) { id->error(incLoc) << "include: failed to locate file" << endl; const char **tried = includeChecks; while ( *tried != 0 ) id->error(incLoc) << "include: attempted: \"" << *tried++ << '\"' << endl; } else { delete inFile; el->foundFileName = curFileName; /* Don't include anything that's already been included. */ if ( !pd->duplicateInclude( includeChecks[found], inclSectionName ) ) { pd->includeHistory.push_back( IncludeHistoryItem( includeChecks[found], inclSectionName ) ); /* Either we are not in the lib, or a file was specifed, use the * file-based include pass. */ includePass.reduceFile( includeChecks[found], id->hostLang ); } } } if ( includePass.incItems.length() == 0 ) { pd->id->error(incLoc) << "could not find machine " << machine << " in " << fileName << endp; } else { /* Load the data into include el. Save in the dict. */ loadIncludeData( el, includePass, includeChecks[found] ); id->includeDict.insert( el ); includePass.incItems.empty(); } } const char *targetMachine0 = targetMachine; const char *searchMachine0 = searchMachine; includeDepth += 1; pd = 0; targetMachine = sectionName.c_str(); searchMachine = machine.c_str(); reduceStr( el->foundFileName.c_str(), el->data ); pd = pd0; includeDepth -= 1; targetMachine = targetMachine0; searchMachine = searchMachine0; } void TopLevel::loadImport( std::string fileName ) { const char *argv[5]; argv[0] = "rlparse"; argv[1] = "import-file"; argv[2] = fileName.c_str(); argv[3] = id->hostLang->rlhcArg; argv[4] = 0; colm_program *program = colm_new_program( &rlparse_object ); colm_set_debug( program, 0 ); colm_run_program( program, 4, argv ); /* Extract the parse tree. */ start Start = RagelTree( program ); str Error = RagelError( program ); _repeat_import ImportList = RagelImport( program ); if ( Start == 0 ) { pd->id->error(Error.loc()) << fileName << ": parse error: " << Error.text() << std::endl; return; } while ( !ImportList.end() ) { import Import = ImportList.value(); InputLoc loc = Import.loc(); string name = Import.Name().text(); loadMachineName( name ); Literal *literal = 0; switch ( Import.Val().prodName() ) { case import_val::String: { string s = Import.Val().string().text(); Token tok; tok.set( s.c_str(), s.size(), loc ); literal = new Literal( loc, false, tok.data, tok.length, Literal::LitString ); break; } case import_val::Number: { string s = Import.Val().number().text(); Token tok; tok.set( s.c_str(), s.size(), loc ); literal = new Literal( loc, false, tok.data, tok.length, Literal::Number ); break; } } MachineDef *machineDef = new MachineDef( new Join( new Expression( new Term( new FactorWithAug( new FactorWithRep( new FactorWithNeg( new Factor( literal ) ) ) ) ) ) ) ); /* Generic creation of machine for instantiation and assignment. */ tryMachineDef( loc, name, machineDef, false ); machineDef->join->loc = loc; ImportList = ImportList.next(); } id->streamFileNames.append( colm_extract_fns( program ) ); colm_delete_program( program ); } void TopLevel::reduceFile( const char *inputFileName ) { const char *argv[5]; argv[0] = "rlparse"; argv[1] = "toplevel-reduce-file"; argv[2] = inputFileName; argv[3] = id->hostLang->rlhcArg; argv[4] = 0; const char *prevCurFileName = curFileName; curFileName = inputFileName; colm_program *program = colm_new_program( &rlparse_object ); colm_set_debug( program, 0 ); colm_set_reduce_ctx( program, this ); colm_run_program( program, 4, argv ); id->streamFileNames.append( colm_extract_fns( program ) ); str Error = RagelError( program ); if ( Error != 0 ) id->error(Error.loc()) << Error.text() << std::endl; colm_delete_program( program ); curFileName = prevCurFileName; } void TopLevel::reduceStr( const char *inputFileName, const char *input ) { const char *argv[6]; argv[0] = "rlparse"; argv[1] = "toplevel-reduce-str"; argv[2] = inputFileName; argv[3] = id->hostLang->rlhcArg; argv[4] = input; argv[5] = 0; const char *prevCurFileName = curFileName; curFileName = inputFileName; colm_program *program = colm_new_program( &rlparse_object ); colm_set_debug( program, 0 ); colm_set_reduce_ctx( program, this ); colm_run_program( program, 5, argv ); id->streamFileNames.append( colm_extract_fns( program ) ); str Error = RagelError( program ); if ( Error != 0 ) id->error(Error.loc()) << "trs: " << Error.text() << std::endl; colm_delete_program( program ); curFileName = prevCurFileName; } void SectionPass::reduceFile( const char *inputFileName ) { const char *argv[5]; argv[0] = "rlparse"; argv[1] = "section-reduce-file"; argv[2] = inputFileName; argv[3] = id->hostLang->rlhcArg; argv[4] = 0; colm_program *program = colm_new_program( &rlparse_object ); colm_set_debug( program, 0 ); colm_set_reduce_ctx( program, this ); colm_run_program( program, 4, argv ); id->streamFileNames.append( colm_extract_fns( program ) ); str Error = RagelError( program ); if ( Error != 0 ) id->error(Error.loc()) << Error.text() << std::endl; colm_delete_program( program ); } void SectionPass::reduceStr( const char *inputFileName, const char *input ) { const char *argv[6]; argv[0] = "rlparse"; argv[1] = "section-reduce-str"; argv[2] = inputFileName; argv[3] = id->hostLang->rlhcArg; argv[4] = input; argv[5] = 0; colm_program *program = colm_new_program( &rlparse_object ); colm_set_debug( program, 0 ); colm_set_reduce_ctx( program, this ); colm_run_program( program, 5, argv ); id->streamFileNames.append( colm_extract_fns( program ) ); str Error = RagelError( program ); if ( Error != 0 ) id->error(Error.loc()) << "srs: " << Error.text() << std::endl; colm_delete_program( program ); } void IncludePass::reduceFile( const char *inputFileName, const HostLang *hostLang ) { const char *argv[5]; argv[0] = "rlparse"; argv[1] = "include-reduce-file"; argv[2] = inputFileName; argv[3] = hostLang->rlhcArg; argv[4] = 0; colm_program *program = colm_new_program( &rlparse_object ); colm_set_debug( program, 0 ); colm_set_reduce_ctx( program, this ); colm_run_program( program, 4, argv ); id->streamFileNames.append( colm_extract_fns( program ) ); str Error = RagelError( program ); if ( Error != 0 ) id->error(Error.loc()) << Error.text() << std::endl; colm_delete_program( program ); } void IncludePass::reduceStr( const char *inputFileName, const HostLang *hostLang, const char *input ) { const char *argv[6]; argv[0] = "rlparse"; argv[1] = "include-reduce-str"; argv[2] = inputFileName; argv[3] = hostLang->rlhcArg; argv[4] = input; argv[5] = 0; colm_program *program = colm_new_program( &rlparse_object ); colm_set_debug( program, 0 ); colm_set_reduce_ctx( program, this ); colm_run_program( program, 5, argv ); id->streamFileNames.append( colm_extract_fns( program ) ); str Error = RagelError( program ); if ( Error != 0 ) id->error(Error.loc()) << Error.text() << std::endl; colm_delete_program( program ); }