/* * Copyright 2008 Adrian Thurston <thurston@complang.org> */ /* 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 "ragel.h" #include "common.h" #include "inputdata.h" #include "parsedata.h" #include "rlparse.h" #include <iostream> #include "dotcodegen.h" using std::cout; using std::cerr; using std::endl; using std::ios; /* Invoked by the parser when the root element is opened. */ void InputData::cdDefaultFileName( const char *inputFile ) { /* If the output format is code and no output file name is given, then * make a default. */ if ( outputFileName == 0 ) { const char *ext = findFileExtension( inputFile ); if ( ext != 0 && strcmp( ext, ".rh" ) == 0 ) outputFileName = fileNameFromStem( inputFile, ".h" ); else { const char *defExtension = 0; switch ( hostLang->lang ) { case HostLang::C: defExtension = ".c"; break; case HostLang::D: defExtension = ".d"; break; case HostLang::D2: defExtension = ".d"; break; default: break; } outputFileName = fileNameFromStem( inputFile, defExtension ); } } } /* Invoked by the parser when the root element is opened. */ void InputData::goDefaultFileName( const char *inputFile ) { /* If the output format is code and no output file name is given, then * make a default. */ if ( outputFileName == 0 ) outputFileName = fileNameFromStem( inputFile, ".go" ); } /* Invoked by the parser when the root element is opened. */ void InputData::javaDefaultFileName( const char *inputFile ) { /* If the output format is code and no output file name is given, then * make a default. */ if ( outputFileName == 0 ) outputFileName = fileNameFromStem( inputFile, ".java" ); } /* Invoked by the parser when the root element is opened. */ void InputData::rubyDefaultFileName( const char *inputFile ) { /* If the output format is code and no output file name is given, then * make a default. */ if ( outputFileName == 0 ) outputFileName = fileNameFromStem( inputFile, ".rb" ); } /* Invoked by the parser when the root element is opened. */ void InputData::csharpDefaultFileName( const char *inputFile ) { /* If the output format is code and no output file name is given, then * make a default. */ if ( outputFileName == 0 ) { const char *ext = findFileExtension( inputFile ); if ( ext != 0 && strcmp( ext, ".rh" ) == 0 ) outputFileName = fileNameFromStem( inputFile, ".h" ); else outputFileName = fileNameFromStem( inputFile, ".cs" ); } } /* Invoked by the parser when the root element is opened. */ void InputData::ocamlDefaultFileName( const char *inputFile ) { /* If the output format is code and no output file name is given, then * make a default. */ if ( outputFileName == 0 ) outputFileName = fileNameFromStem( inputFile, ".ml" ); } void InputData::makeOutputStream() { if ( ! generateDot && ! generateXML ) { switch ( hostLang->lang ) { case HostLang::C: case HostLang::D: case HostLang::D2: cdDefaultFileName( inputFileName ); break; case HostLang::Java: javaDefaultFileName( inputFileName ); break; case HostLang::Go: goDefaultFileName( inputFileName ); break; case HostLang::Ruby: rubyDefaultFileName( inputFileName ); break; case HostLang::CSharp: csharpDefaultFileName( inputFileName ); break; case HostLang::OCaml: ocamlDefaultFileName( inputFileName ); break; } } /* Make sure we are not writing to the same file as the input file. */ if ( outputFileName != 0 ) { if ( strcmp( inputFileName, outputFileName ) == 0 ) { error() << "output file \"" << outputFileName << "\" is the same as the input file" << endl; } /* Create the filter on the output and open it. */ outFilter = new output_filter( outputFileName ); /* Open the output stream, attaching it to the filter. */ outStream = new ostream( outFilter ); } else { /* Writing out ot std out. */ outStream = &cout; } } void InputData::openOutput() { if ( outFilter != 0 ) { outFilter->open( outputFileName, ios::out|ios::trunc ); if ( !outFilter->is_open() ) { error() << "error opening " << outputFileName << " for writing" << endl; exit(1); } } } void InputData::prepareMachineGen() { if ( generateDot ) { /* Locate a machine spec to generate dot output for. We can only emit. * Dot takes one graph at a time. */ if ( machineSpec != 0 ) { /* Machine specified. */ ParserDictEl *pdEl = parserDict.find( machineSpec ); if ( pdEl == 0 ) error() << "could not locate machine specified with -S and/or -M" << endp; dotGenParser = pdEl->value; } else { /* No machine spec given, just use the first one. */ if ( parserList.length() == 0 ) error() << "no machine specification to generate graphviz output" << endp; dotGenParser = parserList.head; } GraphDictEl *gdEl = 0; if ( machineName != 0 ) { gdEl = dotGenParser->pd->graphDict.find( machineName ); if ( gdEl == 0 ) error() << "machine definition/instantiation not found" << endp; } else { /* We are using the whole machine spec. Need to make sure there * are instances in the spec. */ if ( dotGenParser->pd->instanceList.length() == 0 ) error() << "no machine instantiations to generate graphviz output" << endp; } dotGenParser->pd->prepareMachineGen( gdEl ); } else { /* No machine spec or machine name given. Generate everything. */ for ( ParserDict::Iter parser = parserDict; parser.lte(); parser++ ) { ParseData *pd = parser->value->pd; if ( pd->instanceList.length() > 0 ) pd->prepareMachineGen( 0 ); } } } void InputData::generateReduced() { if ( generateDot ) dotGenParser->pd->generateReduced( *this ); else { for ( ParserDict::Iter parser = parserDict; parser.lte(); parser++ ) { ParseData *pd = parser->value->pd; if ( pd->instanceList.length() > 0 ) pd->generateReduced( *this ); } } } /* Send eof to all parsers. */ void InputData::terminateAllParsers( ) { /* FIXME: a proper token is needed here. Suppose we should use the * location of EOF in the last file that the parser was referenced in. */ InputLoc loc; loc.fileName = "<EOF>"; loc.line = 0; loc.col = 0; for ( ParserDict::Iter pdel = parserDict; pdel.lte(); pdel++ ) pdel->value->token( loc, Parser_tk_eof, 0, 0 ); } void InputData::verifyWritesHaveData() { if ( !generateXML && !generateDot ) { for ( InputItemList::Iter ii = inputItems; ii.lte(); ii++ ) { if ( ii->type == InputItem::Write ) { if ( ii->pd->cgd == 0 ) error( ii->loc ) << "no machine instantiations to write" << endl; } } } } void InputData::writeOutput() { if ( generateXML ) writeXML( *outStream ); else if ( generateDot ) static_cast<GraphvizDotGen*>(dotGenParser->pd->cgd)->writeDotFile(); else { bool hostLineDirective = true; for ( InputItemList::Iter ii = inputItems; ii.lte(); ii++ ) { if ( ii->type == InputItem::Write ) { CodeGenData *cgd = ii->pd->cgd; ::keyOps = &cgd->thisKeyOps; hostLineDirective = cgd->writeStatement( ii->loc, ii->writeArgs.length()-1, ii->writeArgs.data ); } else { if ( hostLineDirective ) { /* Write statements can turn off host line directives for * host sections that follow them. */ *outStream << '\n'; lineDirective( *outStream, inputFileName, ii->loc.line ); } *outStream << ii->data.str(); hostLineDirective = true; } } } }