Merged in Ruby code generator from Victor Hugo Borja. Started with a copy of
authorthurston <thurston@052ea7fc-9027-0410-9066-f65837a77df0>
Sat, 3 Feb 2007 01:14:38 +0000 (01:14 +0000)
committerthurston <thurston@052ea7fc-9027-0410-9066-f65837a77df0>
Sat, 3 Feb 2007 01:14:38 +0000 (01:14 +0000)
the Java code generator written in the framework, then merged in the code
generation that Victor wrote for the old framework and adapted it. This
compiles. Has not yet been tested.

git-svn-id: http://svn.complang.org/ragel/trunk@67 052ea7fc-9027-0410-9066-f65837a77df0

configure
configure.in
rlgen-java/Makefile.in
rlgen-ruby/Makefile.in [new file with mode: 0644]
rlgen-ruby/main.cpp [new file with mode: 0644]
rlgen-ruby/rlgen-ruby.h [new file with mode: 0644]
rlgen-ruby/rubycodegen.cpp [new file with mode: 0644]
rlgen-ruby/rubycodegen.h [new file with mode: 0644]

index 932d569..43f4418 100755 (executable)
--- a/configure
+++ b/configure
@@ -2856,7 +2856,7 @@ _ACEOF
 
 fi
 
-                                                                                ac_config_files="$ac_config_files Makefile common/Makefile ragel/Makefile redfsm/Makefile rlcodegen/Makefile rlgen-java/Makefile doc/Makefile test/Makefile"
+                                                                                          ac_config_files="$ac_config_files Makefile common/Makefile ragel/Makefile redfsm/Makefile rlcodegen/Makefile rlgen-java/Makefile rlgen-ruby/Makefile doc/Makefile test/Makefile"
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
 # tests run on this system so they can be shared between configure
@@ -3387,6 +3387,7 @@ do
   "redfsm/Makefile" ) CONFIG_FILES="$CONFIG_FILES redfsm/Makefile" ;;
   "rlcodegen/Makefile" ) CONFIG_FILES="$CONFIG_FILES rlcodegen/Makefile" ;;
   "rlgen-java/Makefile" ) CONFIG_FILES="$CONFIG_FILES rlgen-java/Makefile" ;;
+  "rlgen-ruby/Makefile" ) CONFIG_FILES="$CONFIG_FILES rlgen-ruby/Makefile" ;;
   "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
   "test/Makefile" ) CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
   "common/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS common/config.h" ;;
index 1537768..7c74d33 100644 (file)
@@ -105,6 +105,6 @@ if test -n "$TXL"; then
 fi
 
 dnl write output files
-AC_OUTPUT(Makefile common/Makefile ragel/Makefile redfsm/Makefile rlcodegen/Makefile rlgen-java/Makefile doc/Makefile test/Makefile)
+AC_OUTPUT(Makefile common/Makefile ragel/Makefile redfsm/Makefile rlcodegen/Makefile rlgen-java/Makefile rlgen-ruby/Makefile doc/Makefile test/Makefile)
 
 echo "configuration of ragel complete"
index ed2f0e1..06e9834 100644 (file)
@@ -1,5 +1,5 @@
 #
-#   Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+#   Copyright 2007 Adrian Thurston <thurston@cs.queensu.ca>
 #
 
 #   This file is part of Ragel.
diff --git a/rlgen-ruby/Makefile.in b/rlgen-ruby/Makefile.in
new file mode 100644 (file)
index 0000000..3b994a0
--- /dev/null
@@ -0,0 +1,66 @@
+#
+#   Copyright 2007 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 
+
+INCS += -I../common -I../redfsm -I../aapl
+DEFS +=
+
+CFLAGS += -g -Wall
+LDFLAGS +=
+
+CC_SRCS = main.cpp rubycodegen.cpp
+
+LIBS = ../common/common.a ../redfsm/redfsm.a
+
+LIBS += @LIBS@
+PREFIX += @prefix@
+
+#*************************************
+
+# Programs
+CXX = @CXX@
+
+# Get objects and dependencies from sources.
+OBJS = $(CC_SRCS:%.cpp=%.o)
+DEPS = $(CC_SRCS:%.cpp=.%.d)
+
+# Get the version info.
+include ../version.mk
+
+# Rules.
+all: rlgen-ruby
+
+rlgen-ruby: $(LIBS) $(OBJS)
+       $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+%.o: %.cpp
+       @$(CXX) -M $(DEFS) $(INCS) $< > .$*.d
+       $(CXX) -c $(CFLAGS) $(DEFS) $(INCS) -o $@ $<
+
+distclean: clean
+       rm -f Makefile
+
+clean:
+       rm -f tags .*.d *.o rlgen-ruby
+
+install: all
+       install -d $(PREFIX)/bin
+       install -s rlgen-ruby $(PREFIX)/bin/rlgen-ruby
+
+-include $(DEPS)
diff --git a/rlgen-ruby/main.cpp b/rlgen-ruby/main.cpp
new file mode 100644 (file)
index 0000000..c952dca
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ *  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;
+}
diff --git a/rlgen-ruby/rlgen-ruby.h b/rlgen-ruby/rlgen-ruby.h
new file mode 100644 (file)
index 0000000..cfaf1f9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2001-2007 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 
+ */
+
+#ifndef _RLGEN_JAVA_H
+#define _RLGEN_JAVA_H
+
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include "avltree.h"
+#include "vector.h"
+#include "config.h"
+
+#define PROGNAME "rlgen-java"
+
+/* Filter on the output stream that keeps track of the number of lines
+ * output. */
+class output_filter : public std::filebuf
+{
+public:
+       output_filter( char *fileName ) : fileName(fileName), line(1) { }
+
+       virtual int sync();
+       virtual std::streamsize xsputn(const char* s, std::streamsize n);
+
+       char *fileName;
+       int line;
+};
+
+extern int gblErrorCount;
+extern char machineMain[];
+
+std::ostream &error();
+char *fileNameFromStem( char *stemFile, char *suffix );
+
+#endif /* _RLGEN_JAVA_H */
diff --git a/rlgen-ruby/rubycodegen.cpp b/rlgen-ruby/rubycodegen.cpp
new file mode 100644 (file)
index 0000000..f27d5f0
--- /dev/null
@@ -0,0 +1,1578 @@
+/*
+ *  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;
+}
+
+
diff --git a/rlgen-ruby/rubycodegen.h b/rlgen-ruby/rubycodegen.h
new file mode 100644 (file)
index 0000000..f864e9c
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ *  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 
+ */
+
+#ifndef _RUBYCODEGEN_H
+#define _RUBYCODEGEN_H
+
+#include <iostream>
+#include <string>
+#include <stdio.h>
+#include "common.h"
+#include "gendata.h"
+
+using std::string;
+using std::ostream;
+
+/*
+ * RubyCodeGen
+ */
+struct RubyCodeGen : public CodeGenData
+{
+       RubyCodeGen( ostream &out ) : 
+               CodeGenData(out),
+               indent_level(1) { }
+
+public:
+       void BREAK( ostream &ret, int targState );
+       void GOTO( ostream &ret, int gotoDest, bool inFinish );
+       void GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+       void CALL( ostream &ret, int callDest, int targState, bool inFinish );
+       void CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish );
+       void RET( ostream &ret, bool inFinish );
+
+       void COND_TRANSLATE();
+       void LOCATE_TRANS();
+       virtual void writeOutExec();
+       virtual void writeOutEOF();
+
+ protected:
+       std::ostream &TO_STATE_ACTION_SWITCH();
+       std::ostream &FROM_STATE_ACTION_SWITCH();
+       std::ostream &EOF_ACTION_SWITCH();
+       std::ostream &ACTION_SWITCH();
+
+       std::ostream &COND_KEYS();
+       std::ostream &COND_SPACES();
+       std::ostream &KEYS();
+       std::ostream &INDICIES();
+       std::ostream &COND_OFFSETS();
+       std::ostream &KEY_OFFSETS();
+       std::ostream &INDEX_OFFSETS();
+       std::ostream &COND_LENS();
+       std::ostream &SINGLE_LENS();
+       std::ostream &RANGE_LENS();
+       std::ostream &TO_STATE_ACTIONS();
+       std::ostream &FROM_STATE_ACTIONS();
+       std::ostream &EOF_ACTIONS();
+       std::ostream &TRANS_TARGS();
+       std::ostream &TRANS_ACTIONS();
+       std::ostream &TRANS_TARGS_WI();
+       std::ostream &TRANS_ACTIONS_WI();
+
+       virtual void writeOutData();
+
+       void NEXT( ostream &ret, int nextDest, bool inFinish );
+       void NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+
+       int TO_STATE_ACTION( RedStateAp *state );
+       int FROM_STATE_ACTION( RedStateAp *state );
+       int EOF_ACTION( RedStateAp *state );
+       int TRANS_ACTION( RedTransAp *trans );
+
+       /* Determine if we should use indicies. */
+       void calcIndexSize();
+
+private:
+       string array_type;
+       string array_name;
+
+public:
+
+       virtual ostream &START_ARRAY_LINE();
+       virtual ostream &ARRAY_ITEM( int item, int count, bool last );
+       virtual ostream &END_ARRAY_LINE();
+
+       string FSM_NAME();
+       string START_STATE_ID();
+       ostream &ACTIONS_ARRAY();
+       string GET_WIDE_KEY();
+       string GET_WIDE_KEY( RedStateAp *state );
+       string TABS( int level );
+       int KEY( Key key );
+       void CONDITION( ostream &ret, Action *condition );
+       string ALPH_TYPE();
+       string WIDE_ALPH_TYPE();
+       string ARRAY_TYPE( unsigned long maxVal );
+
+       string P() { return "p"; }
+       string PE() { return "pe"; }
+
+       string ACCESS();
+       string CS();
+       string STACK() { return ACCESS() + "stack"; }
+       string TOP() { return ACCESS() + "top"; }
+       string TOKSTART() { return ACCESS() + "tokstart"; }
+       string TOKEND() { return ACCESS() + "tokend"; }
+       string ACT() { return ACCESS() + "act"; }
+
+       string DATA_PREFIX();
+       string PM() { return "_" + DATA_PREFIX() + "partition_map"; }
+       string C() { return "_" + DATA_PREFIX() + "cond_spaces"; }
+       string CK() { return "_" + DATA_PREFIX() + "cond_keys"; }
+       string K() { return "_" + DATA_PREFIX() + "trans_keys"; }
+       string I() { return "_" + DATA_PREFIX() + "indicies"; }
+       string CO() { return "_" + DATA_PREFIX() + "cond_offsets"; }
+       string KO() { return "_" + DATA_PREFIX() + "key_offsets"; }
+       string IO() { return "_" + DATA_PREFIX() + "index_offsets"; }
+       string CL() { return "_" + DATA_PREFIX() + "cond_lengths"; }
+       string SL() { return "_" + DATA_PREFIX() + "single_lengths"; }
+       string RL() { return "_" + DATA_PREFIX() + "range_lengths"; }
+       string A() { return "_" + DATA_PREFIX() + "actions"; }
+       string TA() { return "_" + DATA_PREFIX() + "trans_actions_wi"; }
+       string TT() { return "_" + DATA_PREFIX() + "trans_targs_wi"; }
+       string TSA() { return "_" + DATA_PREFIX() + "to_state_actions"; }
+       string FSA() { return "_" + DATA_PREFIX() + "from_state_actions"; }
+       string EA() { return "_" + DATA_PREFIX() + "eof_actions"; }
+       string SP() { return "_" + DATA_PREFIX() + "key_spans"; }
+       string CSP() { return "_" + DATA_PREFIX() + "cond_key_spans"; }
+       string START() { return DATA_PREFIX() + "start"; }
+       string ERROR() { return DATA_PREFIX() + "error"; }
+       string FIRST_FINAL() { return DATA_PREFIX() + "first_final"; }
+       string CTXDATA() { return DATA_PREFIX() + "ctxdata"; }
+
+       void INLINE_LIST( ostream &ret, InlineList *inlineList, int targState, bool inFinish );
+       void EXEC( ostream &ret, InlineItem *item, int targState, int inFinish );
+       void EXECTE( ostream &ret, InlineItem *item, int targState, int inFinish );
+       void LM_SWITCH( ostream &ret, InlineItem *item, int targState, int inFinish );
+       void SET_ACT( ostream &ret, InlineItem *item );
+       void INIT_TOKSTART( ostream &ret, InlineItem *item );
+       void INIT_ACT( ostream &ret, InlineItem *item );
+       void SET_TOKSTART( ostream &ret, InlineItem *item );
+       void SET_TOKEND( ostream &ret, InlineItem *item );
+       void GET_TOKEND( ostream &ret, InlineItem *item );
+       void SUB_ACTION( ostream &ret, InlineItem *item, 
+                       int targState, bool inFinish );
+
+       string ERROR_STATE();
+       string FIRST_FINAL_STATE();
+
+       ostream &source_warning(const InputLoc &loc);
+       ostream &source_error(const InputLoc &loc);
+
+       unsigned int arrayTypeSize( unsigned long maxVal );
+
+       /* Set up labelNeeded flag for each state. Differs for each goto style so
+        * is virtual. */
+       virtual void setLabelsNeeded() {}
+
+       bool outLabelUsed;
+       bool againLabelUsed;
+
+       bool useIndicies;
+
+public:
+       void prepareMachine();
+
+       virtual void finishRagelDef();
+       virtual void writeStatement( InputLoc &loc, int nargs, char **args );
+
+       virtual string NULL_ITEM();
+       virtual string POINTER();
+       virtual ostream &SWITCH_DEFAULT();
+       virtual ostream &OPEN_ARRAY( string type, string name );
+       virtual ostream &CLOSE_ARRAY();
+       virtual ostream &STATIC_VAR( string type, string name );
+       virtual string ARR_OFF( string ptr, string offset );
+       virtual string CAST( string type );
+       virtual string UINT();
+       virtual string PTR_CONST();
+       virtual string GET_KEY();
+       virtual string CTRL_FLOW();
+       virtual void ACTION( ostream &ret, Action *action, int targState, bool inFinish );
+       virtual void writeOutInit();
+
+protected:
+       virtual string INDENT_S();
+       virtual string INDENT_U();
+       virtual string INDENT_D();
+       virtual string INDENT_O();
+
+private:
+       virtual string INDENT(int level);
+       int indent_level;
+};
+
+
+#endif