--- /dev/null
+/*
+ * Copyright 2001-2005 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <unistd.h>
+
+#include "rlgen-ruby.h"
+#include "xmlparse.h"
+#include "pcheck.h"
+#include "vector.h"
+#include "version.h"
+#include "common.h"
+#include "rubycodegen.h"
+
+using std::istream;
+using std::ifstream;
+using std::ostream;
+using std::ios;
+using std::cin;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+/* Io globals. */
+istream *inStream = 0;
+ostream *outStream = 0;
+output_filter *outFilter = 0;
+char *outputFileName = 0;
+
+/* Graphviz dot file generation. */
+bool graphvizDone = false;
+
+int numSplitPartitions = 0;
+bool printPrintables = false;
+
+/* Print a summary of the options. */
+void usage()
+{
+ cout <<
+"usage: " PROGNAME " [options] file\n"
+"general:\n"
+" -h, -H, -?, --help Print this usage and exit\n"
+" -v, --version Print version information and exit\n"
+" -o <file> Write output to <file>\n"
+ ;
+}
+
+/* Print version information. */
+void version()
+{
+ cout << "Ragel Code Generator for Ruby" << endl <<
+ "Version " VERSION << ", " PUBDATE << endl <<
+ "Copyright (c) 2001-2007 by Adrian Thurston" << endl;
+}
+
+/* Scans a string looking for the file extension. If there is a file
+ * extension then pointer returned points to inside the string
+ * passed in. Otherwise returns null. */
+char *findFileExtension( char *stemFile )
+{
+ char *ppos = stemFile + strlen(stemFile) - 1;
+
+ /* Scan backwards from the end looking for the first dot.
+ * If we encounter a '/' before the first dot, then stop the scan. */
+ while ( 1 ) {
+ /* If we found a dot or got to the beginning of the string then
+ * we are done. */
+ if ( ppos == stemFile || *ppos == '.' )
+ break;
+
+ /* If we hit a / then there is no extension. Done. */
+ if ( *ppos == '/' ) {
+ ppos = stemFile;
+ break;
+ }
+ ppos--;
+ }
+
+ /* If we got to the front of the string then bail we
+ * did not find an extension */
+ if ( ppos == stemFile )
+ ppos = 0;
+
+ return ppos;
+}
+
+/* Make a file name from a stem. Removes the old filename suffix and
+ * replaces it with a new one. Returns a newed up string. */
+char *fileNameFromStem( char *stemFile, char *suffix )
+{
+ int len = strlen( stemFile );
+ assert( len > 0 );
+
+ /* Get the extension. */
+ char *ppos = findFileExtension( stemFile );
+
+ /* If an extension was found, then shorten what we think the len is. */
+ if ( ppos != 0 )
+ len = ppos - stemFile;
+
+ /* Make the return string from the stem and the suffix. */
+ char *retVal = new char[ len + strlen( suffix ) + 1 ];
+ strncpy( retVal, stemFile, len );
+ strcpy( retVal + len, suffix );
+
+ return retVal;
+}
+
+/* Total error count. */
+int gblErrorCount = 0;
+
+ostream &error()
+{
+ gblErrorCount += 1;
+ cerr << PROGNAME ": ";
+ return cerr;
+}
+
+/* Counts newlines before sending sync. */
+int output_filter::sync( )
+{
+ line += 1;
+ return std::filebuf::sync();
+}
+
+/* Counts newlines before sending data out to file. */
+std::streamsize output_filter::xsputn( const char *s, std::streamsize n )
+{
+ for ( int i = 0; i < n; i++ ) {
+ if ( s[i] == '\n' )
+ line += 1;
+ }
+ return std::filebuf::xsputn( s, n );
+}
+
+void escapeLineDirectivePath( std::ostream &out, char *path )
+{
+ for ( char *pc = path; *pc != 0; pc++ ) {
+ if ( *pc == '\\' )
+ out << "\\\\";
+ else
+ out << *pc;
+ }
+}
+
+/*
+ * Callbacks invoked by the XML data parser.
+ */
+
+/* Invoked by the parser when the root element is opened. */
+ostream *openOutput( char *inputFile, char *language )
+{
+ if ( strcmp( language, "Ruby" ) == 0 ) {
+// hostLangType = JavaCode;
+// hostLang = &hostLangJava;
+ }
+ else {
+ error() << "this code genreator is for Java only" << endl;
+ }
+
+ /* If the output format is code and no output file name is given, then
+ * make a default. */
+ if ( outputFileName == 0 ) {
+ char *ext = findFileExtension( inputFile );
+ if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
+ outputFileName = fileNameFromStem( inputFile, ".h" );
+ else {
+ char *defExtension = 0;
+ switch ( hostLangType ) {
+ case CCode: defExtension = ".c"; break;
+ case DCode: defExtension = ".d"; break;
+ case JavaCode: defExtension = ".java"; break;
+ }
+ outputFileName = fileNameFromStem( inputFile, defExtension );
+ }
+ }
+
+ /* Make sure we are not writing to the same file as the input file. */
+ if ( outputFileName != 0 && strcmp( inputFile, outputFileName ) == 0 ) {
+ error() << "output file \"" << outputFileName <<
+ "\" is the same as the input file" << endl;
+ }
+
+ if ( outputFileName != 0 ) {
+ /* Create the filter on the output and open it. */
+ outFilter = new output_filter( outputFileName );
+ outFilter->open( outputFileName, ios::out|ios::trunc );
+ if ( !outFilter->is_open() ) {
+ error() << "error opening " << outputFileName << " for writing" << endl;
+ exit(1);
+ }
+
+ /* Open the output stream, attaching it to the filter. */
+ outStream = new ostream( outFilter );
+ }
+ else {
+ /* Writing out ot std out. */
+ outStream = &cout;
+ }
+ return outStream;
+}
+
+/* Invoked by the parser when a ragel definition is opened. */
+CodeGenData *makeCodeGen( char *sourceFileName, char *fsmName,
+ ostream &out, bool wantComplete )
+{
+ CodeGenData *codeGen = new RubyCodeGen(out);
+
+ codeGen->sourceFileName = sourceFileName;
+ codeGen->fsmName = fsmName;
+ codeGen->wantComplete = wantComplete;
+
+ return codeGen;
+}
+
+/* Main, process args and call yyparse to start scanning input. */
+int main(int argc, char **argv)
+{
+ ParamCheck pc("o:VpT:F:G:vHh?-:P:", argc, argv);
+ char *xmlInputFileName = 0;
+
+ while ( pc.check() ) {
+ switch ( pc.state ) {
+ case ParamCheck::match:
+ switch ( pc.parameter ) {
+ /* Output. */
+ case 'o':
+ if ( *pc.parameterArg == 0 )
+ error() << "a zero length output file name was given" << endl;
+ else if ( outputFileName != 0 )
+ error() << "more than one output file name was given" << endl;
+ else {
+ /* Ok, remember the output file name. */
+ outputFileName = pc.parameterArg;
+ }
+ break;
+
+ /* Version and help. */
+ case 'v':
+ version();
+ exit(0);
+ case 'H': case 'h': case '?':
+ usage();
+ exit(0);
+ case '-':
+ if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
+ usage();
+ exit(0);
+ }
+ else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
+ version();
+ exit(0);
+ }
+ else {
+ error() << "--" << pc.parameterArg <<
+ " is an invalid argument" << endl;
+ break;
+ }
+ }
+ break;
+
+ case ParamCheck::invalid:
+ error() << "-" << pc.parameter << " is an invalid argument" << endl;
+ break;
+
+ case ParamCheck::noparam:
+ if ( *pc.curArg == 0 )
+ error() << "a zero length input file name was given" << endl;
+ else if ( xmlInputFileName != 0 )
+ error() << "more than one input file name was given" << endl;
+ else {
+ /* OK, Remember the filename. */
+ xmlInputFileName = pc.curArg;
+ }
+ break;
+ }
+ }
+
+ /* Bail on above errors. */
+ if ( gblErrorCount > 0 )
+ exit(1);
+
+ /* Open the input file for reading. */
+ if ( xmlInputFileName != 0 ) {
+ /* Open the input file for reading. */
+ ifstream *inFile = new ifstream( xmlInputFileName );
+ inStream = inFile;
+ if ( ! inFile->is_open() )
+ error() << "could not open " << xmlInputFileName << " for reading" << endl;
+ }
+ else {
+ xmlInputFileName = "<stdin>";
+ inStream = &cin;
+ }
+
+ /* Bail on above errors. */
+ if ( gblErrorCount > 0 )
+ exit(1);
+
+ bool wantComplete = true;
+ bool outputActive = true;
+
+ /* Parse the input! */
+ xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
+
+ /* If writing to a file, delete the ostream, causing it to flush.
+ * Standard out is flushed automatically. */
+ if ( outputFileName != 0 ) {
+ delete outStream;
+ delete outFilter;
+ }
+
+ /* Finished, final check for errors.. */
+ if ( gblErrorCount > 0 ) {
+ /* If we opened an output file, remove it. */
+ if ( outputFileName != 0 )
+ unlink( outputFileName );
+ exit(1);
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright 2006-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2007 Victor Hugo Borja <vhborja@gmail.com>
+ */
+
+/* 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 "rlgen-ruby.h"
+#include "rubycodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+#include <iomanip>
+#include <sstream>
+
+/* Integer array line length. */
+#define IALL 8
+
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::cerr;
+using std::endl;
+
+void genLineDirective( ostream &out )
+{
+ std::streambuf *sbuf = out.rdbuf();
+ output_filter *filter = static_cast<output_filter*>(sbuf);
+ lineDirective( out, filter->fileName, filter->line + 1 );
+}
+
+void RubyCodeGen::GOTO( ostream &out, int gotoDest, bool inFinish )
+{
+ out << INDENT_U() << "begin"
+ << INDENT_S() << CS() << " = " << gotoDest
+ << INDENT_S() << "_again.call " << CTRL_FLOW()
+ << INDENT_D() << "end";
+}
+
+void RubyCodeGen::GOTO_EXPR( ostream &out, InlineItem *ilItem, bool inFinish )
+{
+ out << INDENT_U() << "begin"
+ << INDENT_S() << CS() << " = (";
+ INLINE_LIST( out, ilItem->children, 0, inFinish );
+ out << ")"
+ << INDENT_S() << "_again.call " << CTRL_FLOW()
+ << INDENT_D() << "end";
+}
+
+void RubyCodeGen::CALL( ostream &out, int callDest, int targState, bool inFinish )
+{
+ out << INDENT_U() << "begin"
+ << INDENT_S() << TOP() << "+= 1"
+ << INDENT_S() << STACK() << "[" << TOP() << "-1] = " << CS()
+ << INDENT_S() << CS() << " = " << callDest
+ << INDENT_S() << "_again.call " << CTRL_FLOW()
+ << INDENT_D() << "end";
+}
+
+void RubyCodeGen::CALL_EXPR(ostream &out, InlineItem *ilItem, int targState, bool inFinish )
+{
+ out << INDENT_U() << "begin"
+ << INDENT_S() << TOP() << " += 1"
+ << INDENT_S() << STACK() << "[" << TOP() << "-1] = " << CS()
+ << INDENT_S() << CS() << " = (";
+ INLINE_LIST( out, ilItem->children, targState, inFinish );
+ out << ")"
+ << INDENT_S() << "_again.call " << CTRL_FLOW()
+ << INDENT_D() << "end";
+}
+
+void RubyCodeGen::RET( ostream &out, bool inFinish )
+{
+ out << INDENT_U() << "begin"
+ << INDENT_S() << TOP() << " -= 1"
+ << INDENT_S() << CS() << " = " << STACK() << "[" << TOP() << "+1]"
+ << INDENT_S() << "_again.call " << CTRL_FLOW()
+ << INDENT_D() << "end";
+}
+
+void RubyCodeGen::BREAK( ostream &out, int targState )
+{
+ out << "_resume.call " << CTRL_FLOW();
+}
+
+void RubyCodeGen::COND_TRANSLATE()
+{
+ out << INDENT_S() << "_widec = " << GET_KEY()
+ << INDENT_S() << "_keys = " << CO() << "[" << CS() << "]*2"
+ << INDENT_S() << "_klen = " << CL() << "[" << CS() << "]"
+ << INDENT_U() << "if _klen > 0"
+ << INDENT_S() << "_lower = _keys"
+ << INDENT_S() << "_upper = _keys + (_klen<<1) - 2"
+ << INDENT_U() << "loop do"
+ << INDENT_S() << "break if _upper < _lower"
+ << INDENT_S() << "_mid = _lower + (((_upper-_lower) >> 1) & ~1)"
+ << INDENT_U() << "if " << GET_WIDE_KEY() << " < " << CK() << "[_mid]"
+ << INDENT_O() << "_upper = _mid - 2"
+ << INDENT_U() << "elsif " << GET_WIDE_KEY() << " > " << CK() << "[_mid]"
+ << INDENT_O() << "_lower = _mid + 2"
+ << INDENT_U() << "else"
+ << INDENT_U() << "case " << C() << "[" << CO() << "[" << CS() << "]"
+ << " + ((_mid - _keys)>>1)]"
+ ;
+
+ for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) {
+ CondSpace *condSpace = csi;
+ out << INDENT_U() << "when " << condSpace->condSpaceId << ":" ;
+ out << INDENT_S() << "_widec = " << KEY(condSpace->baseKey)
+ << "+ (" << GET_KEY() << " - " << KEY(keyOps->minKey) << ")" ;
+
+ for ( CondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) {
+ Size condValOffset = ((1 << csi.pos()) * keyOps->alphSize());
+ out << INDENT_S() << "_widec += " << condValOffset << " if ( ";
+ CONDITION( out, *csi );
+ out << " )";
+ }
+ }
+
+ out << INDENT_D() << "end # case"
+ << INDENT_D() << "end"
+ << INDENT_D() << "end # loop"
+ << INDENT_D() << "end"
+ ;
+}
+
+void RubyCodeGen::LOCATE_TRANS()
+{
+ out << INDENT_S() << "_keys = " << KO() << "[" << CS() << "]"
+ << INDENT_S() << "_trans = " << IO() << "[" << CS() << "]"
+ << INDENT_S() << "_klen = " << SL() << "[" << CS() << "]"
+ << INDENT_S()
+ << INDENT_U() << "callcc do |_match|"
+ << INDENT_U() << "if _klen > 0"
+ << INDENT_S() << "_lower = _keys"
+ << INDENT_S() << "_upper = _keys + _klen - 1"
+ << INDENT_S()
+ << INDENT_U() << "loop do"
+ << INDENT_S() << "break if _upper < _lower"
+ << INDENT_S() << "_mid = _lower + ( (_upper - _lower) >> 1 )"
+ << INDENT_S()
+ << INDENT_U() << "if " << GET_WIDE_KEY() << " < " << K() << "[_mid]"
+ << INDENT_O() << "_upper = _mid - 1"
+ << INDENT_U() << "elsif " << GET_WIDE_KEY() << " > " << K() << "[_mid]"
+ << INDENT_O() << "_lower = _mid + 1"
+ << INDENT_U() << "else"
+ << INDENT_S() << "_trans += (_mid - _keys)"
+ << INDENT_S() << "_match.call"
+ << INDENT_D() << "end"
+ << INDENT_D() << "end # loop"
+ << INDENT_S() << "_keys += _klen"
+ << INDENT_S() << "_trans += _klen"
+ << INDENT_D() << "end"
+ << INDENT_S()
+ << INDENT_S() << "_klen = " << RL() << "[" << CS() << "]"
+ << INDENT_U() << "if _klen > 0"
+ << INDENT_S() << "_lower = _keys"
+ << INDENT_S() << "_upper = _keys + (_klen << 1) - 2"
+ << INDENT_U() << "loop do"
+ << INDENT_S() << "break if _upper < _lower"
+ << INDENT_S() << "_mid = _lower + (((_upper-_lower) >> 1) & ~1)"
+ << INDENT_U() << "if " << GET_WIDE_KEY() << " < " << K() << "[_mid]"
+ << INDENT_O() << "_upper = _mid - 2"
+ << INDENT_U() << "elsif " << GET_WIDE_KEY() << " > " << K() << "[_mid]"
+ << INDENT_O() << "_lower = _mid + 2"
+ << INDENT_U() << "else"
+ << INDENT_S() << "_trans += ((_mid - _keys) >> 1)"
+ << INDENT_S() << "_match.call"
+ << INDENT_D() << "end"
+ << INDENT_D() << "end # loop"
+ << INDENT_S() << "_trans += _klen"
+ << INDENT_D() << "end"
+ << INDENT_D() << "end # cc _match" ;
+}
+
+void RubyCodeGen::writeOutExec()
+{
+ out << INDENT_U() << "callcc do |_out|"
+ << INDENT_S() << "_klen, _trans, _keys";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << ", _ps";
+ if ( redFsm->anyConditions() )
+ out << ", _widec";
+ if ( redFsm->anyToStateActions() || redFsm->anyRegActions()
+ || redFsm->anyFromStateActions() )
+ out << ", _acts, _nacts";
+
+ out << " = nil" ;
+
+ if ( hasEnd )
+ out << INDENT_S() << "_out.call if " << P() << " == " << PE() ;
+
+ out << INDENT_S() << "_resume = nil"
+ << INDENT_S() << "callcc { |_cc| _resume = _cc }" ;
+
+ if ( redFsm->errState != 0)
+ out << INDENT_S() << "_out.call if " << CS() << " == " << redFsm->errState->id ;
+
+ if ( redFsm->anyRegActions() || redFsm->anyActionGotos() ||
+ redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << INDENT_U() << "callcc do |_again|" ;
+
+ if ( redFsm->anyFromStateActions() ) {
+ out << INDENT_S() << "_acts = " << FSA() << "[" << CS() << "]"
+ << INDENT_S() << "_nacts = " << A() << "[acts]"
+ << INDENT_S() << "_acts += 1"
+ << INDENT_U() << "while _nacts > 0"
+ << INDENT_S() << "_nacts -= 1"
+ << INDENT_S() << " _acts += 1"
+ << INDENT_U() << "case " << A() << "[_acts - 1]" ;
+ FROM_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT()
+ << INDENT_D() << "end # from state action switch"
+ << INDENT_D() << "end"
+ << INDENT_S();
+ }
+
+ if ( redFsm->anyConditions() )
+ COND_TRANSLATE();
+
+ LOCATE_TRANS();
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << INDENT_S() << "_ps = " << CS() ;
+
+ if ( useIndicies )
+ out << INDENT_S() << "_trans = " << I() << "[_trans]" ;
+
+ out << INDENT_S() << CS() << " = " << TT() << "[_trans]" ;
+
+ if ( redFsm->anyRegActions() ) {
+ out << INDENT_S() << "_again.call if " << TA() << "[_trans] == 0"
+ << INDENT_S()
+ << INDENT_S() << "_acts = " << TA() << "[_trans]"
+ << INDENT_S() << "_nacts = " << A() << "[_acts]"
+ << INDENT_S() << "_acts += 1"
+ << INDENT_U() << "while _nacts > 0"
+ << INDENT_S() << "_nacts -= 1"
+ << INDENT_S() << "_acts += 1"
+ << INDENT_U() << "case " << A() << "[_acts - 1]" ;
+ ACTION_SWITCH();
+ SWITCH_DEFAULT()
+ << INDENT_D() << "end # action switch"
+ << INDENT_D() << "end"
+ << INDENT_S();
+ }
+
+ if ( redFsm->anyRegActions() || redFsm->anyActionGotos() ||
+ redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << INDENT_D() << "end # cc _again";
+
+ if ( redFsm->anyToStateActions() ) {
+ out << INDENT_S() << "_acts = " << TSA() << "[" << CS() << "]"
+ << INDENT_S() << "_nacts = " << A() << "[_acts]"
+ << INDENT_S() << "_acts += 1"
+ << INDENT_U() << "while _nacts > 0"
+ << INDENT_S() << "_nacts -= 1"
+ << INDENT_S() << "_acts += 1"
+ << INDENT_U() << "case " << A() << "[acts - 1]" ;
+ TO_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT()
+ << INDENT_D() << "end # to state action switch"
+ << INDENT_D() << "end"
+ << INDENT_S();
+ }
+
+ out << INDENT_S() << P() << " += 1" ;
+
+ if ( hasEnd )
+ out << INDENT_S() << "_resume.call if p != pe";
+
+ out << INDENT_D() << "end # cc _out" ;
+}
+
+void RubyCodeGen::writeOutEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out << INDENT_S() << "_acts = " << EA() << "[" << CS() << "]"
+ << INDENT_S() << "_nacts = " << " " << A() << "[_acts]"
+ << INDENT_S() << "_acts += 1"
+ << INDENT_U() << "while _nacts > 0"
+ << INDENT_S() << "_nacts -= 1"
+ << INDENT_S() << "_acts += 1"
+ << INDENT_S() << "case " << A() << "[_acts - 1]" ;
+ EOF_ACTION_SWITCH();
+ SWITCH_DEFAULT()
+ << INDENT_D() << "end # eof action switch"
+ << INDENT_D() << "end"
+ << INDENT_S();
+ }
+}
+
+std::ostream &RubyCodeGen::FROM_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numFromStateRefs > 0 ) {
+ /* Write the case label, the action */
+ out << INDENT_S() << "when " << act->actionId << ":" ;
+ ACTION( out, act, 0, false );
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+
+std::ostream &RubyCodeGen::TO_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numToStateRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << INDENT_S() << "when " << act->actionId << ":" ;
+ ACTION( out, act, 0, false );
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &RubyCodeGen::EOF_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numEofRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << INDENT_S() << "when " << act->actionId << ":" ;
+ ACTION( out, act, 0, true );
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &RubyCodeGen::ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numTransRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << INDENT_S() << "when " << act->actionId << ":" ;
+ ACTION( out, act, 0, false );
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+
+void RubyCodeGen::writeOutInit()
+{
+ out << INDENT_U() << "begin"
+ << INDENT_S() << CS() << " = " << START();
+
+ /* If there are any calls, then the stack top needs initialization. */
+ if ( redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << INDENT_S() << TOP() << " = 0";
+
+ if ( hasLongestMatch ) {
+ out << INDENT_S() << TOKSTART() << " = " << NULL_ITEM()
+ << INDENT_S() << TOKEND() << " = " << NULL_ITEM()
+ << INDENT_S() << ACT() << " = 0"
+ << INDENT_S();
+ }
+ out << INDENT_D() << "end";
+}
+
+string RubyCodeGen::PTR_CONST()
+{
+ /* Not used in Ruby code. */
+ assert( false );
+ return "final";
+}
+
+std::ostream &RubyCodeGen::OPEN_ARRAY( string type, string name )
+{
+ out << "class << self" << endl
+ << INDENT(1) << "attr_accessor :" << name << endl
+ << INDENT(1) << "private :" << name << ", :" << name << "=" << endl
+ << "end" << endl
+ << "self." << name << " = [" << endl;
+ return out;
+}
+
+std::ostream &RubyCodeGen::CLOSE_ARRAY()
+{
+ return out << "]" << endl;
+}
+
+std::ostream &RubyCodeGen::STATIC_VAR( string type, string name )
+{
+ out << "class << self" << endl
+ << INDENT(1) << "attr_accessor :" << name << endl
+ << "end" << endl
+ << "self." << name;
+ return out;
+}
+
+string RubyCodeGen::UINT( )
+{
+ /* Not used. */
+ assert( false );
+ return "long";
+}
+
+string RubyCodeGen::ARR_OFF( string ptr, string offset )
+{
+ return ptr + " + " + offset;
+}
+
+string RubyCodeGen::CAST( string type )
+{
+ /* No casts on ruby */
+ assert( false );
+ return "";
+}
+
+string RubyCodeGen::NULL_ITEM()
+{
+ return "nil";
+}
+
+string RubyCodeGen::POINTER()
+{
+ /* Not used. */
+ assert( false );
+ return " *";
+}
+
+std::ostream &RubyCodeGen::SWITCH_DEFAULT()
+{
+ return out;
+}
+
+string RubyCodeGen::GET_KEY()
+{
+ ostringstream ret;
+ if ( getKeyExpr != 0 ) {
+ /* Emit the user supplied method of retrieving the key. */
+ ret << "(";
+ INLINE_LIST( ret, getKeyExpr, 0, false );
+ ret << ")";
+ }
+ else {
+ /* Expression for retrieving the key, use simple dereference. */
+ ret << "data[" << P() << "]";
+ }
+ return ret.str();
+}
+
+string RubyCodeGen::CTRL_FLOW()
+{
+ return "if (true)";
+}
+
+void RubyCodeGen::ACTION( ostream &ret, Action *action, int targState, bool inFinish )
+{
+ /* Write the preprocessor line info for going into the source file. */
+ lineDirective( ret, sourceFileName, action->loc.line );
+
+ /* Write the block and close it off. */
+ ret << "begin" << endl << INDENT(1);
+ INLINE_LIST( ret, action->inlineList, targState, inFinish );
+ ret << "end ";
+ lineDirective( ret, sourceFileName, action->loc.line );
+ ret << endl;
+}
+
+string RubyCodeGen::INDENT(int level)
+{
+ string result = "\n";
+ while ( level-- > 0 )
+ result += " "; /* The convention in ruby is 2 spaces per level */
+ return result;
+}
+inline string RubyCodeGen::INDENT_S() { return INDENT(indent_level); }
+inline string RubyCodeGen::INDENT_U() { return INDENT(++indent_level); }
+inline string RubyCodeGen::INDENT_D() { return INDENT(--indent_level); }
+inline string RubyCodeGen::INDENT_O() { return INDENT(indent_level--); }
+
+
+void RubyCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
+{
+ ret << CS() << " = " << nextDest << ";";
+}
+
+void RubyCodeGen::NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << ");";
+}
+
+void RubyCodeGen::EXEC( ostream &ret, InlineItem *item, int targState, int inFinish )
+{
+ /* The parser gives fexec two children. The double brackets are for D
+ * code. If the inline list is a single word it will get interpreted as a
+ * C-style cast by the D compiler. */
+ ret << "{" << P() << " = ((";
+ INLINE_LIST( ret, item->children, targState, inFinish );
+ ret << "))-1;}";
+}
+
+void RubyCodeGen::EXECTE( ostream &ret, InlineItem *item, int targState, int inFinish )
+{
+ /* Tokend version of exec. */
+
+ /* The parser gives fexec two children. The double brackets are for D
+ * code. If the inline list is a single word it will get interpreted as a
+ * C-style cast by the D compiler. */
+ ret << "{" << TOKEND() << " = ((";
+ INLINE_LIST( ret, item->children, targState, inFinish );
+ ret << "));}";
+}
+
+/* Write out an inline tree structure. Walks the list and possibly calls out
+ * to virtual functions than handle language specific items in the tree. */
+void RubyCodeGen::INLINE_LIST( ostream &ret, InlineList *inlineList,
+ int targState, bool inFinish )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case InlineItem::Text:
+ ret << item->data;
+ break;
+ case InlineItem::Goto:
+ GOTO( ret, item->targState->id, inFinish );
+ break;
+ case InlineItem::Call:
+ CALL( ret, item->targState->id, targState, inFinish );
+ break;
+ case InlineItem::Next:
+ NEXT( ret, item->targState->id, inFinish );
+ break;
+ case InlineItem::Ret:
+ RET( ret, inFinish );
+ break;
+ case InlineItem::PChar:
+ ret << P();
+ break;
+ case InlineItem::Char:
+ ret << GET_KEY();
+ break;
+ case InlineItem::Hold:
+ ret << P() << "--;";
+ break;
+ case InlineItem::Exec:
+ EXEC( ret, item, targState, inFinish );
+ break;
+ case InlineItem::HoldTE:
+ ret << TOKEND() << "--;";
+ break;
+ case InlineItem::ExecTE:
+ EXECTE( ret, item, targState, inFinish );
+ break;
+ case InlineItem::Curs:
+ ret << "(_ps)";
+ break;
+ case InlineItem::Targs:
+ ret << "(" << CS() << ")";
+ break;
+ case InlineItem::Entry:
+ ret << item->targState->id;
+ break;
+ case InlineItem::GotoExpr:
+ GOTO_EXPR( ret, item, inFinish );
+ break;
+ case InlineItem::CallExpr:
+ CALL_EXPR( ret, item, targState, inFinish );
+ break;
+ case InlineItem::NextExpr:
+ NEXT_EXPR( ret, item, inFinish );
+ break;
+ case InlineItem::LmSwitch:
+ LM_SWITCH( ret, item, targState, inFinish );
+ break;
+ case InlineItem::LmSetActId:
+ SET_ACT( ret, item );
+ break;
+ case InlineItem::LmSetTokEnd:
+ SET_TOKEND( ret, item );
+ break;
+ case InlineItem::LmGetTokEnd:
+ GET_TOKEND( ret, item );
+ break;
+ case InlineItem::LmInitTokStart:
+ INIT_TOKSTART( ret, item );
+ break;
+ case InlineItem::LmInitAct:
+ INIT_ACT( ret, item );
+ break;
+ case InlineItem::LmSetTokStart:
+ SET_TOKSTART( ret, item );
+ break;
+ case InlineItem::SubAction:
+ SUB_ACTION( ret, item, targState, inFinish );
+ break;
+ case InlineItem::Break:
+ BREAK( ret, targState );
+ break;
+ }
+ }
+}
+
+string RubyCodeGen::DATA_PREFIX()
+{
+ if ( dataPrefix )
+ return FSM_NAME() + "_";
+ return "";
+}
+
+/* Emit the alphabet data type. */
+string RubyCodeGen::ALPH_TYPE()
+{
+ string ret = keyOps->alphType->data1;
+ if ( keyOps->alphType->data2 != 0 ) {
+ ret += " ";
+ ret += + keyOps->alphType->data2;
+ }
+ return ret;
+}
+
+/* Emit the alphabet data type. */
+string RubyCodeGen::WIDE_ALPH_TYPE()
+{
+ string ret;
+ if ( redFsm->maxKey <= keyOps->maxKey )
+ ret = ALPH_TYPE();
+ else {
+ long long maxKeyVal = redFsm->maxKey.getLongLong();
+ HostType *wideType = keyOps->typeSubsumes( keyOps->isSigned, maxKeyVal );
+ assert( wideType != 0 );
+
+ ret = wideType->data1;
+ if ( wideType->data2 != 0 ) {
+ ret += " ";
+ ret += wideType->data2;
+ }
+ }
+ return ret;
+}
+
+/* Determine if we should use indicies or not. */
+void RubyCodeGen::calcIndexSize()
+{
+ int sizeWithInds = 0, sizeWithoutInds = 0;
+
+ /* Calculate cost of using with indicies. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ int totalIndex = st->outSingle.length() + st->outRange.length() +
+ (st->defTrans == 0 ? 0 : 1);
+ sizeWithInds += arrayTypeSize(redFsm->maxIndex) * totalIndex;
+ }
+ sizeWithInds += arrayTypeSize(redFsm->maxState) * redFsm->transSet.length();
+ if ( redFsm->anyActions() )
+ sizeWithInds += arrayTypeSize(redFsm->maxActionLoc) * redFsm->transSet.length();
+
+ /* Calculate the cost of not using indicies. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ int totalIndex = st->outSingle.length() + st->outRange.length() +
+ (st->defTrans == 0 ? 0 : 1);
+ sizeWithoutInds += arrayTypeSize(redFsm->maxState) * totalIndex;
+ if ( redFsm->anyActions() )
+ sizeWithoutInds += arrayTypeSize(redFsm->maxActionLoc) * totalIndex;
+ }
+
+ /* If using indicies reduces the size, use them. */
+ useIndicies = sizeWithInds < sizeWithoutInds;
+}
+
+int RubyCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->location+1;
+ return act;
+}
+
+int RubyCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->location+1;
+ return act;
+}
+
+int RubyCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->location+1;
+ return act;
+}
+
+
+int RubyCodeGen::TRANS_ACTION( RedTransAp *trans )
+{
+ /* If there are actions, emit them. Otherwise emit zero. */
+ int act = 0;
+ if ( trans->action != 0 )
+ act = trans->action->location+1;
+ return act;
+}
+
+std::ostream &RubyCodeGen::COND_OFFSETS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0, curKeyOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the key offset. */
+ ARRAY_ITEM( curKeyOffset, ++totalStateNum, st.last() );
+
+ /* Move the key offset ahead. */
+ curKeyOffset += st->stateCondList.length();
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::KEY_OFFSETS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0, curKeyOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the key offset. */
+ ARRAY_ITEM( curKeyOffset, ++totalStateNum, st.last() );
+
+ /* Move the key offset ahead. */
+ curKeyOffset += st->outSingle.length() + st->outRange.length()*2;
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+
+std::ostream &RubyCodeGen::INDEX_OFFSETS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0, curIndOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the index offset. */
+ ARRAY_ITEM( curIndOffset, ++totalStateNum, st.last() );
+
+ /* Move the index offset ahead. */
+ curIndOffset += st->outSingle.length() + st->outRange.length();
+ if ( st->defTrans != 0 )
+ curIndOffset += 1;
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::COND_LENS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write singles length. */
+ ARRAY_ITEM( st->stateCondList.length(), ++totalStateNum, st.last() );
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+
+std::ostream &RubyCodeGen::SINGLE_LENS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write singles length. */
+ ARRAY_ITEM( st->outSingle.length(), ++totalStateNum, st.last() );
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::RANGE_LENS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Emit length of range index. */
+ ARRAY_ITEM( st->outRange.length(), ++totalStateNum, st.last() );
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::TO_STATE_ACTIONS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ ARRAY_ITEM( TO_STATE_ACTION(st), ++totalStateNum, st.last() );
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::FROM_STATE_ACTIONS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ ARRAY_ITEM( FROM_STATE_ACTION(st), ++totalStateNum, st.last() );
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::EOF_ACTIONS()
+{
+ START_ARRAY_LINE();
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ ARRAY_ITEM( EOF_ACTION(st), ++totalStateNum, st.last() );
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::COND_KEYS()
+{
+ START_ARRAY_LINE();
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Loop the state's transitions. */
+ for ( StateCondList::Iter sc = st->stateCondList; sc.lte(); sc++ ) {
+ /* Lower key. */
+ ARRAY_ITEM( KEY( sc->lowKey ), ++totalTrans, false );
+ ARRAY_ITEM( KEY( sc->highKey ), ++totalTrans, false );
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ ARRAY_ITEM( 0, ++totalTrans, true );
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::COND_SPACES()
+{
+ START_ARRAY_LINE();
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Loop the state's transitions. */
+ for ( StateCondList::Iter sc = st->stateCondList; sc.lte(); sc++ ) {
+ /* Cond Space id. */
+ ARRAY_ITEM( KEY( sc->condSpace->condSpaceId ), ++totalTrans, false );
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ ARRAY_ITEM( 0, ++totalTrans, true );
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::KEYS()
+{
+ START_ARRAY_LINE();
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Loop the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ ARRAY_ITEM( KEY( stel->lowKey ), ++totalTrans, false );
+ }
+
+ /* Loop the state's transitions. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ /* Lower key. */
+ ARRAY_ITEM( KEY( rtel->lowKey ), ++totalTrans, false );
+
+ /* Upper key. */
+ ARRAY_ITEM( KEY( rtel->highKey ), ++totalTrans, false );
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ ARRAY_ITEM( 0, ++totalTrans, true );
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::INDICIES()
+{
+ int totalTrans = 0;
+ START_ARRAY_LINE();
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Walk the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ ARRAY_ITEM( KEY( stel->value->id ), ++totalTrans, false );
+ }
+
+ /* Walk the ranges. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ ARRAY_ITEM( KEY( rtel->value->id ), ++totalTrans, false );
+ }
+
+ /* The state's default index goes next. */
+ if ( st->defTrans != 0 ) {
+ ARRAY_ITEM( KEY( st->defTrans->id ), ++totalTrans, false );
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ ARRAY_ITEM( 0, ++totalTrans, true );
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::TRANS_TARGS()
+{
+ int totalTrans = 0;
+ START_ARRAY_LINE();
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Walk the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ RedTransAp *trans = stel->value;
+ ARRAY_ITEM( KEY( trans->targ->id ), ++totalTrans, false );
+ }
+
+ /* Walk the ranges. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ RedTransAp *trans = rtel->value;
+ ARRAY_ITEM( KEY( trans->targ->id ), ++totalTrans, false );
+ }
+
+ /* The state's default target state. */
+ if ( st->defTrans != 0 ) {
+ RedTransAp *trans = st->defTrans;
+ ARRAY_ITEM( KEY( trans->targ->id ), ++totalTrans, false );
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ ARRAY_ITEM( 0, ++totalTrans, true );
+ END_ARRAY_LINE();
+ return out;
+}
+
+
+std::ostream &RubyCodeGen::TRANS_ACTIONS()
+{
+ int totalTrans = 0;
+ START_ARRAY_LINE();
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Walk the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ RedTransAp *trans = stel->value;
+ ARRAY_ITEM( TRANS_ACTION( trans ), ++totalTrans, false );
+ }
+
+ /* Walk the ranges. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ RedTransAp *trans = rtel->value;
+ ARRAY_ITEM( TRANS_ACTION( trans ), ++totalTrans, false );
+ }
+
+ /* The state's default index goes next. */
+ if ( st->defTrans != 0 ) {
+ RedTransAp *trans = st->defTrans;
+ ARRAY_ITEM( TRANS_ACTION( trans ), ++totalTrans, false );
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ ARRAY_ITEM( 0, ++totalTrans, true );
+ END_ARRAY_LINE();
+ return out;
+}
+
+std::ostream &RubyCodeGen::TRANS_TARGS_WI()
+{
+ /* Transitions must be written ordered by their id. */
+ RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()];
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ transPtrs[trans->id] = trans;
+
+ /* Keep a count of the num of items in the array written. */
+ START_ARRAY_LINE();
+ int totalStates = 0;
+ for ( int t = 0; t < redFsm->transSet.length(); t++ ) {
+ /* Write out the target state. */
+ RedTransAp *trans = transPtrs[t];
+ ARRAY_ITEM( trans->targ->id, ++totalStates, ( t >= redFsm->transSet.length()-1 ) );
+ }
+ END_ARRAY_LINE();
+ delete[] transPtrs;
+ return out;
+}
+
+
+std::ostream &RubyCodeGen::TRANS_ACTIONS_WI()
+{
+ /* Transitions must be written ordered by their id. */
+ RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()];
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ transPtrs[trans->id] = trans;
+
+ /* Keep a count of the num of items in the array written. */
+ START_ARRAY_LINE();
+ int totalAct = 0;
+ for ( int t = 0; t < redFsm->transSet.length(); t++ ) {
+ /* Write the function for the transition. */
+ RedTransAp *trans = transPtrs[t];
+ ARRAY_ITEM( TRANS_ACTION( trans ), ++totalAct, ( t >= redFsm->transSet.length()-1 ) );
+ }
+ END_ARRAY_LINE();
+ delete[] transPtrs;
+ return out;
+}
+
+
+void RubyCodeGen::writeOutData()
+{
+ out <<
+ " private static byte[] unpack_byte(String packed)\n"
+ " {\n"
+ " byte[] ret = new byte[packed.length()];\n"
+ " for (int i = 0; i < packed.length(); i++)\n"
+ " {\n"
+ " int value = packed.charAt(i);\n"
+ " value--;\n"
+ " ret[i] = (byte) value;\n"
+ " }\n"
+ " return ret;\n"
+ " }\n"
+ " private static short[] unpack_short(String packed)\n"
+ " {\n"
+ " short[] ret = new short[packed.length()];\n"
+ " for (int i = 0; i < packed.length(); i++)\n"
+ " {\n"
+ " int value = packed.charAt(i);\n"
+ " value--;\n"
+ " ret[i] = (short) value;\n"
+ " }\n"
+ " return ret;\n"
+ " }\n";
+
+ /* If there are any transtion functions then output the array. If there
+ * are none, don't bother emitting an empty array that won't be used. */
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActArrItem), A() );
+ ACTIONS_ARRAY();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyConditions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondOffset), CO() );
+ COND_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondLen), CL() );
+ COND_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), CK() );
+ COND_KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondSpaceId), C() );
+ COND_SPACES();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxKeyOffset), KO() );
+ KEY_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), K() );
+ KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxSingleLen), SL() );
+ SINGLE_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxRangeLen), RL() );
+ RANGE_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndexOffset), IO() );
+ INDEX_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( useIndicies ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndex), I() );
+ INDICIES();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS_WI();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TA() );
+ TRANS_ACTIONS_WI();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+ }
+ else {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TA() );
+ TRANS_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+ }
+
+ if ( redFsm->anyToStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
+ TO_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
+ FROM_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyEofActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), EA() );
+ EOF_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ STATIC_VAR( "int", START() ) << " = " << START_STATE_ID() << ";\n"
+ "\n";
+
+ if ( writeFirstFinal ) {
+ STATIC_VAR( "int" , FIRST_FINAL() ) << " = " << FIRST_FINAL_STATE() << ";\n"
+ "\n";
+ }
+
+ if ( writeErr ) {
+ STATIC_VAR( "int", ERROR() ) << " = " << ERROR_STATE() << ";\n"
+ "\n";
+ }
+
+}
+
+std::ostream &RubyCodeGen::START_ARRAY_LINE()
+{
+ out << "\t\"";
+ return out;
+}
+
+std::ostream &RubyCodeGen::ARRAY_ITEM( int item, int count, bool last )
+{
+ // 0 codes in 2 bytes in the Java class file and is common,
+ // so we increment all values by one when packing
+ item++;
+
+ std::ios_base::fmtflags originalFlags=out.flags();
+ if ( item < 0x80 )
+ {
+ out << std::oct << "\\" << item;
+ }
+ else
+ {
+ out << std::hex << "\\u" << std::setfill('0') << std::setw(4) << item;
+ }
+ out.flags(originalFlags);
+
+ if ( !last )
+ {
+ if ( count % IALL == 0 )
+ {
+ END_ARRAY_LINE();
+ START_ARRAY_LINE();
+ }
+ }
+ return out;
+}
+
+std::ostream &RubyCodeGen::END_ARRAY_LINE()
+{
+ out << "\" +\n";
+ return out;
+}
+
+
+unsigned int RubyCodeGen::arrayTypeSize( unsigned long maxVal )
+{
+ long long maxValLL = (long long) maxVal;
+ HostType *arrayType = keyOps->typeSubsumes( maxValLL );
+ assert( arrayType != 0 );
+ return arrayType->size;
+}
+
+string RubyCodeGen::ARRAY_TYPE( unsigned long maxVal )
+{
+ long long maxValLL = (long long) maxVal;
+ HostType *arrayType = keyOps->typeSubsumes( maxValLL );
+ assert( arrayType != 0 );
+
+ string ret = arrayType->data1;
+ if ( arrayType->data2 != 0 ) {
+ ret += " ";
+ ret += arrayType->data2;
+ }
+ return ret;
+}
+
+
+/* Write out the fsm name. */
+string RubyCodeGen::FSM_NAME()
+{
+ return fsmName;
+}
+
+/* Emit the offset of the start state as a decimal integer. */
+string RubyCodeGen::START_STATE_ID()
+{
+ ostringstream ret;
+ ret << redFsm->startState->id;
+ return ret.str();
+};
+
+/* Write out the array of actions. */
+std::ostream &RubyCodeGen::ACTIONS_ARRAY()
+{
+ START_ARRAY_LINE();
+ int totalActions = 0;
+ ARRAY_ITEM( 0, ++totalActions, false );
+ for ( ActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) {
+ /* Write out the length, which will never be the last character. */
+ ARRAY_ITEM( act->key.length(), ++totalActions, false );
+
+ for ( ActionTable::Iter item = act->key; item.lte(); item++ ) {
+ ARRAY_ITEM( item->value->actionId, ++totalActions, (act.last() && item.last()) );
+ }
+ }
+ END_ARRAY_LINE();
+ return out;
+}
+
+
+string RubyCodeGen::CS()
+{
+ ostringstream ret;
+ if ( curStateExpr != 0 ) {
+ /* Emit the user supplied method of retrieving the key. */
+ ret << "(";
+ INLINE_LIST( ret, curStateExpr, 0, false );
+ ret << ")";
+ }
+ else {
+ /* Expression for retrieving the key, use simple dereference. */
+ ret << ACCESS() << "cs";
+ }
+ return ret.str();
+}
+
+string RubyCodeGen::ACCESS()
+{
+ ostringstream ret;
+ if ( accessExpr != 0 )
+ INLINE_LIST( ret, accessExpr, 0, false );
+ return ret.str();
+}
+
+string RubyCodeGen::GET_WIDE_KEY()
+{
+ if ( redFsm->anyConditions() )
+ return "_widec";
+ else
+ return GET_KEY();
+}
+
+string RubyCodeGen::GET_WIDE_KEY( RedStateAp *state )
+{
+ if ( state->stateCondList.length() > 0 )
+ return "_widec";
+ else
+ return GET_KEY();
+}
+
+/* Write out level number of tabs. Makes the nested binary search nice
+ * looking. */
+string RubyCodeGen::TABS( int level )
+{
+ string result;
+ while ( level-- > 0 )
+ result += "\t";
+ return result;
+}
+
+int RubyCodeGen::KEY( Key key )
+{
+ return key.getVal();
+}
+
+
+void RubyCodeGen::LM_SWITCH( ostream &ret, InlineItem *item,
+ int targState, int inFinish )
+{
+ ret <<
+ " switch( act ) {\n";
+
+ /* If the switch handles error then we also forced the error state. It
+ * will exist. */
+ if ( item->handlesError ) {
+ ret << " case 0: " << TOKEND() << " = " << TOKSTART() << "; ";
+ GOTO( ret, redFsm->errState->id, inFinish );
+ ret << "\n";
+ }
+
+ for ( InlineList::Iter lma = *item->children; lma.lte(); lma++ ) {
+ /* Write the case label, the action and the case break. */
+ ret << " case " << lma->lmId << ":\n";
+
+ /* Write the block and close it off. */
+ ret << " {";
+ INLINE_LIST( ret, lma->children, targState, inFinish );
+ ret << "}\n";
+
+ ret << " break;\n";
+ }
+ /* Default required for D code. */
+ ret <<
+ " default: break;\n"
+ " }\n"
+ "\t";
+}
+
+void RubyCodeGen::SET_ACT( ostream &ret, InlineItem *item )
+{
+ ret << ACT() << " = " << item->lmId << ";";
+}
+
+void RubyCodeGen::SET_TOKEND( ostream &ret, InlineItem *item )
+{
+ /* The tokend action sets tokend. */
+ ret << TOKEND() << " = " << P();
+ if ( item->offset != 0 )
+ out << "+" << item->offset;
+ out << ";";
+}
+
+void RubyCodeGen::GET_TOKEND( ostream &ret, InlineItem *item )
+{
+ ret << TOKEND();
+}
+
+void RubyCodeGen::INIT_TOKSTART( ostream &ret, InlineItem *item )
+{
+ ret << TOKSTART() << " = " << NULL_ITEM() << ";";
+}
+
+void RubyCodeGen::INIT_ACT( ostream &ret, InlineItem *item )
+{
+ ret << ACT() << " = 0;";
+}
+
+void RubyCodeGen::SET_TOKSTART( ostream &ret, InlineItem *item )
+{
+ ret << TOKSTART() << " = " << P() << ";";
+}
+
+void RubyCodeGen::SUB_ACTION( ostream &ret, InlineItem *item,
+ int targState, bool inFinish )
+{
+ if ( item->children->length() > 0 ) {
+ /* Write the block and close it off. */
+ ret << "{";
+ INLINE_LIST( ret, item->children, targState, inFinish );
+ ret << "}";
+ }
+}
+
+void RubyCodeGen::CONDITION( ostream &ret, Action *condition )
+{
+ ret << "\n";
+ lineDirective( ret, sourceFileName, condition->loc.line );
+ INLINE_LIST( ret, condition->inlineList, 0, false );
+}
+
+string RubyCodeGen::ERROR_STATE()
+{
+ ostringstream ret;
+ if ( redFsm->errState != 0 )
+ ret << redFsm->errState->id;
+ else
+ ret << "-1";
+ return ret.str();
+}
+
+string RubyCodeGen::FIRST_FINAL_STATE()
+{
+ ostringstream ret;
+ if ( redFsm->firstFinState != 0 )
+ ret << redFsm->firstFinState->id;
+ else
+ ret << redFsm->nextStateId;
+ return ret.str();
+}
+
+/* Generate the code for an fsm. Assumes parseData is set up properly. Called
+ * by parser code. */
+void RubyCodeGen::prepareMachine()
+{
+ if ( hasBeenPrepared )
+ return;
+ hasBeenPrepared = true;
+
+ /* Do this before distributing transitions out to singles and defaults
+ * makes life easier. */
+ redFsm->maxKey = findMaxKey();
+
+ redFsm->assignActionLocs();
+
+ /* Order the states. */
+ redFsm->depthFirstOrdering();
+
+ /* For table driven machines the location of the state is used to
+ * identify it so the states must be sorted by their final ids.
+ * Though having a deterministic ordering is important,
+ * specifically preserving the depth first ordering is not because
+ * states are stored in tables. */
+ redFsm->sortStatesByFinal();
+ redFsm->sequentialStateIds();
+
+ /* Find the first final state. This is the final state with the lowest
+ * id. */
+ redFsm->findFirstFinState();
+
+ /* Choose default transitions and the single transition. */
+ redFsm->chooseDefaultSpan();
+
+ /* Maybe do flat expand, otherwise choose single. */
+ redFsm->chooseSingle();
+
+ /* If any errors have occured in the input file then don't write anything. */
+ if ( gblErrorCount > 0 )
+ return;
+
+ /* Anlayze Machine will find the final action reference counts, among
+ * other things. We will use these in reporting the usage
+ * of fsm directives in action code. */
+ analyzeMachine();
+
+ /* Determine if we should use indicies. */
+ calcIndexSize();
+}
+
+void RubyCodeGen::finishRagelDef()
+{
+ prepareMachine();
+}
+
+void RubyCodeGen::writeStatement( InputLoc &loc, int nargs, char **args )
+{
+ /* Force a newline. */
+ out << "\n";
+ genLineDirective( out );
+
+ if ( strcmp( args[0], "data" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ if ( strcmp( args[i], "noerror" ) == 0 )
+ writeErr = false;
+ else if ( strcmp( args[i], "noprefix" ) == 0 )
+ dataPrefix = false;
+ else if ( strcmp( args[i], "nofinal" ) == 0 )
+ writeFirstFinal = false;
+ else {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ }
+ writeOutData();
+ }
+ else if ( strcmp( args[0], "init" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ writeOutInit();
+ }
+ else if ( strcmp( args[0], "exec" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ if ( strcmp( args[i], "noend" ) == 0 )
+ hasEnd = false;
+ else {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ }
+
+ /* Must set labels immediately before writing because we may depend
+ * on the noend write option. */
+ setLabelsNeeded();
+ writeOutExec();
+ }
+ else if ( strcmp( args[0], "eof" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ writeOutEOF();
+ }
+ else {
+ /* EMIT An error here. */
+ }
+}
+
+ostream &RubyCodeGen::source_warning( const InputLoc &loc )
+{
+ cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": warning: ";
+ return cerr;
+}
+
+ostream &RubyCodeGen::source_error( const InputLoc &loc )
+{
+ gblErrorCount += 1;
+ assert( sourceFileName != 0 );
+ cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": ";
+ return cerr;
+}
+
+