/*
* 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;
}
}
}
}