Use the new import feature of Ragel for bringing in defines from the parser
[external/ragel.git] / ragel / rlscan.rl
1 /*
2  *  Copyright 2006-2007 Adrian Thurston <thurston@cs.queensu.ca>
3  */
4
5 /*  This file is part of Ragel.
6  *
7  *  Ragel is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  * 
12  *  Ragel is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  * 
17  *  You should have received a copy of the GNU General Public License
18  *  along with Ragel; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
20  */
21
22 #include <iostream>
23 #include <fstream>
24 #include <string.h>
25
26 #include "ragel.h"
27 #include "rlscan.h"
28
29 //#define LOG_TOKENS
30
31 using std::ifstream;
32 using std::istream;
33 using std::ostream;
34 using std::cout;
35 using std::cerr;
36 using std::endl;
37
38 enum InlineBlockType
39 {
40         CurlyDelimited,
41         SemiTerminated
42 };
43
44
45 /*
46  * The Scanner for Importing
47  */
48
49 %%{
50         machine inline_token_scan;
51         alphtype int;
52         access tok_;
53
54         # Import scanner tokens.
55         import "rlparse.h"; 
56
57         main := |*
58                 # Define of number.
59                 IMP_Define IMP_Word IMP_UInt => { 
60                         int base = tok_tokstart - token_data;
61                         int nameOff = 1;
62                         int numOff = 2;
63
64                         directToParser( inclToParser, fileName, line, column, TK_Word, 
65                                         token_strings[base+nameOff], token_lens[base+nameOff] );
66                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
67                         directToParser( inclToParser, fileName, line, column, TK_UInt,
68                                         token_strings[base+numOff], token_lens[base+numOff] );
69                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
70                 };
71
72                 # Assignment of number.
73                 IMP_Word '=' IMP_UInt => { 
74                         int base = tok_tokstart - token_data;
75                         int nameOff = 0;
76                         int numOff = 2;
77
78                         directToParser( inclToParser, fileName, line, column, TK_Word, 
79                                         token_strings[base+nameOff], token_lens[base+nameOff] );
80                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
81                         directToParser( inclToParser, fileName, line, column, TK_UInt,
82                                         token_strings[base+numOff], token_lens[base+numOff] );
83                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
84                 };
85
86                 # Define of literal.
87                 IMP_Define IMP_Word IMP_Literal => { 
88                         int base = tok_tokstart - token_data;
89                         int nameOff = 1;
90                         int litOff = 2;
91
92                         directToParser( inclToParser, fileName, line, column, TK_Word, 
93                                         token_strings[base+nameOff], token_lens[base+nameOff] );
94                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
95                         directToParser( inclToParser, fileName, line, column, TK_Literal,
96                                         token_strings[base+litOff], token_lens[base+litOff] );
97                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
98                 };
99
100                 # Assignment of literal.
101                 IMP_Word '=' IMP_Literal => { 
102                         int base = tok_tokstart - token_data;
103                         int nameOff = 0;
104                         int litOff = 2;
105
106                         directToParser( inclToParser, fileName, line, column, TK_Word, 
107                                         token_strings[base+nameOff], token_lens[base+nameOff] );
108                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
109                         directToParser( inclToParser, fileName, line, column, TK_Literal,
110                                         token_strings[base+litOff], token_lens[base+litOff] );
111                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
112                 };
113
114                 # Catch everything else.
115                 any;
116         *|;
117 }%%
118
119 %% write data;
120
121 void Scanner::flushImport()
122 {
123         int *p = token_data;
124         int *pe = token_data + cur_token;
125
126         %% write init;
127         %% write exec;
128
129         if ( tok_tokstart == 0 )
130                 cur_token = 0;
131         else {
132                 cur_token = pe - tok_tokstart;
133                 int ts_offset = tok_tokstart - token_data;
134                 memmove( token_data, token_data+ts_offset, cur_token*sizeof(token_data[0]) );
135                 memmove( token_strings, token_strings+ts_offset, cur_token*sizeof(token_strings[0]) );
136                 memmove( token_lens, token_lens+ts_offset, cur_token*sizeof(token_lens[0]) );
137         }
138 }
139
140 void Scanner::directToParser( Parser *toParser, char *tokFileName, int tokLine, 
141                 int tokColumn, int type, char *tokdata, int toklen )
142 {
143         InputLoc loc;
144
145         #ifdef LOG_TOKENS
146         cerr << "scanner:" << tokLine << ":" << tokColumn << 
147                         ": sending token to the parser " << Parser_lelNames[type];
148         cerr << " " << toklen;
149         if ( tokdata != 0 )
150                 cerr << " " << tokdata;
151         cerr << endl;
152         #endif
153
154         loc.fileName = tokFileName;
155         loc.line = tokLine;
156         loc.col = tokColumn;
157
158         toParser->token( loc, type, tokdata, toklen );
159 }
160
161 void Scanner::importToken( int token, char *start, char *end )
162 {
163         if ( cur_token == max_tokens )
164                 flushImport();
165
166         token_data[cur_token] = token;
167         if ( start == 0 ) {
168                 token_strings[cur_token] = 0;
169                 token_lens[cur_token] = 0;
170         }
171         else {
172                 int toklen = end-start;
173                 token_lens[cur_token] = toklen;
174                 token_strings[cur_token] = new char[toklen+1];
175                 memcpy( token_strings[cur_token], start, toklen );
176                 token_strings[cur_token][toklen] = 0;
177         }
178         cur_token++;
179 }
180
181 void Scanner::pass( int token, char *start, char *end )
182 {
183         if ( importMachines )
184                 importToken( token, start, end );
185         pass();
186 }
187
188 void Scanner::pass()
189 {
190         updateCol();
191
192         /* If no errors and we are at the bottom of the include stack (the
193          * source file listed on the command line) then write out the data. */
194         if ( includeDepth == 0 && machineSpec == 0 && machineName == 0 )
195                 xmlEscapeHost( output, tokstart, tokend-tokstart );
196 }
197
198 /*
199  * The scanner for processing sections, includes, imports, etc.
200  */
201
202 %%{
203         machine section_parse;
204         alphtype int;
205         write data;
206 }%%
207
208
209 void Scanner::init( )
210 {
211         %% write init;
212 }
213
214 bool Scanner::active()
215 {
216         if ( ignoreSection )
217                 return false;
218
219         if ( parser == 0 && ! parserExistsError ) {
220                 scan_error() << "there is no previous specification name" << endl;
221                 parserExistsError = true;
222         }
223
224         if ( parser == 0 )
225                 return false;
226
227         return true;
228 }
229
230 ostream &Scanner::scan_error()
231 {
232         /* Maintain the error count. */
233         gblErrorCount += 1;
234         cerr << fileName << ":" << line << ":" << column << ": ";
235         return cerr;
236 }
237
238 bool Scanner::recursiveInclude( char *inclFileName, char *inclSectionName )
239 {
240         for ( IncludeStack::Iter si = includeStack; si.lte(); si++ ) {
241                 if ( strcmp( si->fileName, inclFileName ) == 0 &&
242                                 strcmp( si->sectionName, inclSectionName ) == 0 )
243                 {
244                         return true;
245                 }
246         }
247         return false;   
248 }
249
250 void Scanner::updateCol()
251 {
252         char *from = lastnl;
253         if ( from == 0 )
254                 from = tokstart;
255         //cerr << "adding " << tokend - from << " to column" << endl;
256         column += tokend - from;
257         lastnl = 0;
258 }
259
260 %%{
261         machine section_parse;
262
263         # Need the defines representing tokens.
264         import "rlparse.h"; 
265
266         action clear_words { word = lit = 0; word_len = lit_len = 0; }
267         action store_word { word = tokdata; word_len = toklen; }
268         action store_lit { lit = tokdata; lit_len = toklen; }
269
270         action mach_err { scan_error() << "bad machine statement" << endl; }
271         action incl_err { scan_error() << "bad include statement" << endl; }
272         action import_err { scan_error() << "bad import statement" << endl; }
273         action write_err { scan_error() << "bad write statement" << endl; }
274
275         action handle_machine
276         {
277                 /* Assign a name to the machine. */
278                 char *machine = word;
279
280                 if ( !importMachines && inclSectionTarg == 0 ) {
281                         ignoreSection = false;
282
283                         ParserDictEl *pdEl = parserDict.find( machine );
284                         if ( pdEl == 0 ) {
285                                 pdEl = new ParserDictEl( machine );
286                                 pdEl->value = new Parser( fileName, machine, sectionLoc );
287                                 pdEl->value->init();
288                                 parserDict.insert( pdEl );
289                         }
290
291                         parser = pdEl->value;
292                 }
293                 else if ( !importMachines && strcmp( inclSectionTarg, machine ) == 0 ) {
294                         /* found include target */
295                         ignoreSection = false;
296                         parser = inclToParser;
297                 }
298                 else {
299                         /* ignoring section */
300                         ignoreSection = true;
301                         parser = 0;
302                 }
303         }
304
305         machine_stmt =
306                 ( KW_Machine TK_Word @store_word ';' ) @handle_machine
307                 <>err mach_err <>eof mach_err;
308
309         action handle_include
310         {
311                 if ( active() ) {
312                         char *inclSectionName = word;
313                         char *inclFileName = 0;
314
315                         /* Implement defaults for the input file and section name. */
316                         if ( inclSectionName == 0 )
317                                 inclSectionName = parser->sectionName;
318
319                         if ( lit != 0 ) 
320                                 inclFileName = prepareFileName( lit, lit_len );
321                         else
322                                 inclFileName = fileName;
323
324                         /* Check for a recursive include structure. Add the current file/section
325                          * name then check if what we are including is already in the stack. */
326                         includeStack.append( IncludeStackItem( fileName, parser->sectionName ) );
327
328                         if ( recursiveInclude( inclFileName, inclSectionName ) )
329                                 scan_error() << "include: this is a recursive include operation" << endl;
330                         else {
331                                 /* Open the input file for reading. */
332                                 ifstream *inFile = new ifstream( inclFileName );
333                                 if ( ! inFile->is_open() ) {
334                                         scan_error() << "include: could not open " << 
335                                                         inclFileName << " for reading" << endl;
336                                 }
337
338                                 Scanner scanner( inclFileName, *inFile, output, parser,
339                                                 inclSectionName, includeDepth+1, false );
340                                 scanner.do_scan( );
341                                 delete inFile;
342                         }
343
344                         /* Remove the last element (len-1) */
345                         includeStack.remove( -1 );
346                 }
347         }
348
349         include_names = (
350                 TK_Word @store_word ( TK_Literal @store_lit )? |
351                 TK_Literal @store_lit
352         ) >clear_words;
353
354         include_stmt =
355                 ( KW_Include include_names ';' ) @handle_include
356                 <>err incl_err <>eof incl_err;
357
358         action handle_import
359         {
360                 if ( active() ) {
361                         char *importFileName = prepareFileName( lit, lit_len );
362
363                         /* Open the input file for reading. */
364                         ifstream *inFile = new ifstream( importFileName );
365                         if ( ! inFile->is_open() ) {
366                                 scan_error() << "import: could not open " << 
367                                                 importFileName << " for reading" << endl;
368                         }
369
370                         Scanner scanner( importFileName, *inFile, output, parser,
371                                         0, includeDepth+1, true );
372                         scanner.do_scan( );
373                         scanner.importToken( 0, 0, 0 );
374                         scanner.flushImport();
375                         delete inFile;
376                 }
377         }
378
379         import_stmt =
380                 ( KW_Import TK_Literal @store_lit ';' ) @handle_import
381                 <>err import_err <>eof import_err;
382
383         action write_command
384         {
385                 if ( active() && machineSpec == 0 && machineName == 0 ) {
386                         output << "<write"
387                                         " def_name=\"" << parser->sectionName << "\""
388                                         " line=\"" << line << "\""
389                                         " col=\"" << column << "\""
390                                         ">";
391                 }
392         }
393
394         action write_arg
395         {
396                 if ( active() && machineSpec == 0 && machineName == 0 )
397                         output << "<arg>" << tokdata << "</arg>";
398         }
399
400         action write_close
401         {
402                 if ( active() && machineSpec == 0 && machineName == 0 )
403                         output << "</write>\n";
404         }
405
406         write_stmt =
407                 ( KW_Write @write_command 
408                 ( TK_Word @write_arg )+ ';' @write_close )
409                 <>err write_err <>eof write_err;
410
411         action handle_token
412         {
413                 /* Send the token off to the parser. */
414                 if ( active() )
415                         directToParser( parser, fileName, line, column, type, tokdata, toklen );
416         }
417
418         # Catch everything else.
419         everything_else = 
420                 ^( KW_Machine | KW_Include | KW_Import | KW_Write ) @handle_token;
421
422         main := ( 
423                 machine_stmt |
424                 include_stmt |
425                 import_stmt |
426                 write_stmt |
427                 everything_else
428         )*;
429 }%%
430
431 void Scanner::token( int type, char c )
432 {
433         token( type, &c, &c + 1 );
434 }
435
436 void Scanner::token( int type )
437 {
438         token( type, 0, 0 );
439 }
440
441 void Scanner::token( int type, char *start, char *end )
442 {
443         char *tokdata = 0;
444         int toklen = 0;
445         if ( start != 0 ) {
446                 toklen = end-start;
447                 tokdata = new char[toklen+1];
448                 memcpy( tokdata, start, toklen );
449                 tokdata[toklen] = 0;
450         }
451
452         processToken( type, tokdata, toklen );
453 }
454
455 void Scanner::processToken( int type, char *tokdata, int toklen )
456 {
457         int *p = &type;
458         int *pe = &type + 1;
459
460         %%{
461                 machine section_parse;
462                 write exec;
463         }%%
464
465         updateCol();
466
467         /* Record the last token for use in controlling the scan of subsequent
468          * tokens. */
469         lastToken = type;
470 }
471
472 void Scanner::startSection( )
473 {
474         parserExistsError = false;
475
476         if ( includeDepth == 0 ) {
477                 if ( machineSpec == 0 && machineName == 0 )
478                         output << "</host>\n";
479         }
480
481         sectionLoc.fileName = fileName;
482         sectionLoc.line = line;
483         sectionLoc.col = 0;
484 }
485
486 void Scanner::endSection( )
487 {
488         /* Execute the eof actions for the section parser. */
489         %%{
490                 machine section_parse;
491                 write eof;
492         }%%
493
494         /* Close off the section with the parser. */
495         if ( active() ) {
496                 InputLoc loc;
497                 loc.fileName = fileName;
498                 loc.line = line;
499                 loc.col = 0;
500
501                 parser->token( loc, TK_EndSection, 0, 0 );
502         }
503
504         if ( includeDepth == 0 ) {
505                 if ( machineSpec == 0 && machineName == 0 ) {
506                         /* The end section may include a newline on the end, so
507                          * we use the last line, which will count the newline. */
508                         output << "<host line=\"" << line << "\">";
509                 }
510         }
511 }
512
513 %%{
514         machine rlscan;
515
516         # This is sent by the driver code.
517         EOF = 0;
518         
519         action inc_nl { 
520                 lastnl = p; 
521                 column = 0;
522                 line++;
523         }
524         NL = '\n' @inc_nl;
525
526         # Identifiers, numbers, commetns, and other common things.
527         ident = ( alpha | '_' ) ( alpha |digit |'_' )*;
528         number = digit+;
529         hex_number = '0x' [0-9a-fA-F]+;
530
531         c_comment = 
532                 '/*' ( any | NL )* :>> '*/';
533
534         cpp_comment =
535                 '//' [^\n]* NL;
536
537         c_cpp_comment = c_comment | cpp_comment;
538
539         # These literal forms are common to C-like host code and ragel.
540         s_literal = "'" ([^'\\] | NL | '\\' (any | NL))* "'";
541         d_literal = '"' ([^"\\] | NL | '\\' (any | NL))* '"';
542
543         whitespace = [ \t] | NL;
544         pound_comment = '#' [^\n]* NL;
545
546         # An inline block of code. This is specified as a scanned, but is sent to
547         # the parser as one long block. The inline_block pointer is used to handle
548         # the preservation of the data.
549         inline_code := |*
550                 # Inline expression keywords.
551                 "fpc" => { token( KW_PChar ); };
552                 "fc" => { token( KW_Char ); };
553                 "fcurs" => { token( KW_CurState ); };
554                 "ftargs" => { token( KW_TargState ); };
555                 "fentry" => { 
556                         whitespaceOn = false; 
557                         token( KW_Entry );
558                 };
559
560                 # Inline statement keywords.
561                 "fhold" => { 
562                         whitespaceOn = false; 
563                         token( KW_Hold );
564                 };
565                 "fexec" => { token( KW_Exec, 0, 0 ); };
566                 "fgoto" => { 
567                         whitespaceOn = false; 
568                         token( KW_Goto );
569                 };
570                 "fnext" => { 
571                         whitespaceOn = false; 
572                         token( KW_Next );
573                 };
574                 "fcall" => { 
575                         whitespaceOn = false; 
576                         token( KW_Call );
577                 };
578                 "fret" => { 
579                         whitespaceOn = false; 
580                         token( KW_Ret );
581                 };
582                 "fbreak" => { 
583                         whitespaceOn = false; 
584                         token( KW_Break );
585                 };
586
587                 ident => { token( TK_Word, tokstart, tokend ); };
588
589                 number => { token( TK_UInt, tokstart, tokend ); };
590                 hex_number => { token( TK_Hex, tokstart, tokend ); };
591
592                 ( s_literal | d_literal ) 
593                         => { token( IL_Literal, tokstart, tokend ); };
594
595                 whitespace+ => { 
596                         if ( whitespaceOn ) 
597                                 token( IL_WhiteSpace, tokstart, tokend );
598                 };
599                 c_cpp_comment => { token( IL_Comment, tokstart, tokend ); };
600
601                 "::" => { token( TK_NameSep, tokstart, tokend ); };
602
603                 # Some symbols need to go to the parser as with their cardinal value as
604                 # the token type (as opposed to being sent as anonymous symbols)
605                 # because they are part of the sequences which we interpret. The * ) ;
606                 # symbols cause whitespace parsing to come back on. This gets turned
607                 # off by some keywords.
608
609                 ";" => {
610                         whitespaceOn = true;
611                         token( *tokstart, tokstart, tokend );
612                         if ( inlineBlockType == SemiTerminated )
613                                 fgoto parser_def;
614                 };
615
616                 [*)] => { 
617                         whitespaceOn = true;
618                         token( *tokstart, tokstart, tokend );
619                 };
620
621                 [,(] => { token( *tokstart, tokstart, tokend ); };
622
623                 '{' => { 
624                         token( IL_Symbol, tokstart, tokend );
625                         curly_count += 1; 
626                 };
627
628                 '}' => { 
629                         if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
630                                 /* Inline code block ends. */
631                                 token( '}' );
632                                 fgoto parser_def;
633                         }
634                         else {
635                                 /* Either a semi terminated inline block or only the closing
636                                  * brace of some inner scope, not the block's closing brace. */
637                                 token( IL_Symbol, tokstart, tokend );
638                         }
639                 };
640
641                 EOF => {
642                         scan_error() << "unterminated code block" << endl;
643                 };
644
645                 # Send every other character as a symbol.
646                 any => { token( IL_Symbol, tokstart, tokend ); };
647         *|;
648
649         or_literal := |*
650                 # Escape sequences in OR expressions.
651                 '\\0' => { token( RE_Char, '\0' ); };
652                 '\\a' => { token( RE_Char, '\a' ); };
653                 '\\b' => { token( RE_Char, '\b' ); };
654                 '\\t' => { token( RE_Char, '\t' ); };
655                 '\\n' => { token( RE_Char, '\n' ); };
656                 '\\v' => { token( RE_Char, '\v' ); };
657                 '\\f' => { token( RE_Char, '\f' ); };
658                 '\\r' => { token( RE_Char, '\r' ); };
659                 '\\\n' => { updateCol(); };
660                 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
661
662                 # Range dash in an OR expression.
663                 '-' => { token( RE_Dash, 0, 0 ); };
664
665                 # Terminate an OR expression.
666                 ']'     => { token( RE_SqClose ); fret; };
667
668                 EOF => {
669                         scan_error() << "unterminated OR literal" << endl;
670                 };
671
672                 # Characters in an OR expression.
673                 [^\]] => { token( RE_Char, tokstart, tokend ); };
674
675         *|;
676
677         re_literal := |*
678                 # Escape sequences in regular expressions.
679                 '\\0' => { token( RE_Char, '\0' ); };
680                 '\\a' => { token( RE_Char, '\a' ); };
681                 '\\b' => { token( RE_Char, '\b' ); };
682                 '\\t' => { token( RE_Char, '\t' ); };
683                 '\\n' => { token( RE_Char, '\n' ); };
684                 '\\v' => { token( RE_Char, '\v' ); };
685                 '\\f' => { token( RE_Char, '\f' ); };
686                 '\\r' => { token( RE_Char, '\r' ); };
687                 '\\\n' => { updateCol(); };
688                 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
689
690                 # Terminate an OR expression.
691                 '/' [i]? => { 
692                         token( RE_Slash, tokstart, tokend ); 
693                         fgoto parser_def;
694                 };
695
696                 # Special characters.
697                 '.' => { token( RE_Dot ); };
698                 '*' => { token( RE_Star ); };
699
700                 '[' => { token( RE_SqOpen ); fcall or_literal; };
701                 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
702
703                 EOF => {
704                         scan_error() << "unterminated regular expression" << endl;
705                 };
706
707                 # Characters in an OR expression.
708                 [^\/] => { token( RE_Char, tokstart, tokend ); };
709         *|;
710
711         # We need a separate token space here to avoid the ragel keywords.
712         write_statement := |*
713                 ident => { token( TK_Word, tokstart, tokend ); } ;
714                 [ \t\n]+ => { updateCol(); };
715                 ';' => { token( ';' ); fgoto parser_def; };
716
717                 EOF => {
718                         scan_error() << "unterminated write statement" << endl;
719                 };
720         *|;
721
722         # Parser definitions. 
723         parser_def := |*
724                 'machine' => { token( KW_Machine ); };
725                 'include' => { token( KW_Include ); };
726                 'import' => { token( KW_Import ); };
727                 'write' => { 
728                         token( KW_Write );
729                         fgoto write_statement;
730                 };
731                 'action' => { token( KW_Action ); };
732                 'alphtype' => { token( KW_AlphType ); };
733
734                 # FIXME: Enable this post 5.17.
735                 # 'range' => { token( KW_Range ); };
736
737                 'getkey' => { 
738                         token( KW_GetKey );
739                         inlineBlockType = SemiTerminated;
740                         fgoto inline_code;
741                 };
742                 'access' => { 
743                         token( KW_Access );
744                         inlineBlockType = SemiTerminated;
745                         fgoto inline_code;
746                 };
747                 'variable' => { 
748                         token( KW_Variable );
749                         inlineBlockType = SemiTerminated;
750                         fgoto inline_code;
751                 };
752                 'when' => { token( KW_When ); };
753                 'eof' => { token( KW_Eof ); };
754                 'err' => { token( KW_Err ); };
755                 'lerr' => { token( KW_Lerr ); };
756                 'to' => { token( KW_To ); };
757                 'from' => { token( KW_From ); };
758                 'export' => { token( KW_Export ); };
759
760                 # Identifiers.
761                 ident => { token( TK_Word, tokstart, tokend ); } ;
762
763                 # Numbers
764                 number => { token( TK_UInt, tokstart, tokend ); };
765                 hex_number => { token( TK_Hex, tokstart, tokend ); };
766
767                 # Literals, with optionals.
768                 ( s_literal | d_literal ) [i]? 
769                         => { token( TK_Literal, tokstart, tokend ); };
770
771                 '[' => { token( RE_SqOpen ); fcall or_literal; };
772                 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
773
774                 '/' => { token( RE_Slash ); fgoto re_literal; };
775
776                 # Ignore.
777                 pound_comment => { updateCol(); };
778
779                 ':=' => { token( TK_ColonEquals ); };
780
781                 # To State Actions.
782                 ">~" => { token( TK_StartToState ); };
783                 "$~" => { token( TK_AllToState ); };
784                 "%~" => { token( TK_FinalToState ); };
785                 "<~" => { token( TK_NotStartToState ); };
786                 "@~" => { token( TK_NotFinalToState ); };
787                 "<>~" => { token( TK_MiddleToState ); };
788
789                 # From State actions
790                 ">*" => { token( TK_StartFromState ); };
791                 "$*" => { token( TK_AllFromState ); };
792                 "%*" => { token( TK_FinalFromState ); };
793                 "<*" => { token( TK_NotStartFromState ); };
794                 "@*" => { token( TK_NotFinalFromState ); };
795                 "<>*" => { token( TK_MiddleFromState ); };
796
797                 # EOF Actions.
798                 ">/" => { token( TK_StartEOF ); };
799                 "$/" => { token( TK_AllEOF ); };
800                 "%/" => { token( TK_FinalEOF ); };
801                 "</" => { token( TK_NotStartEOF ); };
802                 "@/" => { token( TK_NotFinalEOF ); };
803                 "<>/" => { token( TK_MiddleEOF ); };
804
805                 # Global Error actions.
806                 ">!" => { token( TK_StartGblError ); };
807                 "$!" => { token( TK_AllGblError ); };
808                 "%!" => { token( TK_FinalGblError ); };
809                 "<!" => { token( TK_NotStartGblError ); };
810                 "@!" => { token( TK_NotFinalGblError ); };
811                 "<>!" => { token( TK_MiddleGblError ); };
812
813                 # Local error actions.
814                 ">^" => { token( TK_StartLocalError ); };
815                 "$^" => { token( TK_AllLocalError ); };
816                 "%^" => { token( TK_FinalLocalError ); };
817                 "<^" => { token( TK_NotStartLocalError ); };
818                 "@^" => { token( TK_NotFinalLocalError ); };
819                 "<>^" => { token( TK_MiddleLocalError ); };
820
821                 # Middle.
822                 "<>" => { token( TK_Middle ); };
823
824                 # Conditions. 
825                 '>?' => { token( TK_StartCond ); };
826                 '$?' => { token( TK_AllCond ); };
827                 '%?' => { token( TK_LeavingCond ); };
828
829                 '..' => { token( TK_DotDot ); };
830                 '**' => { token( TK_StarStar ); };
831                 '--' => { token( TK_DashDash ); };
832                 '->' => { token( TK_Arrow ); };
833                 '=>' => { token( TK_DoubleArrow ); };
834
835                 ":>"  => { token( TK_ColonGt ); };
836                 ":>>" => { token( TK_ColonGtGt ); };
837                 "<:"  => { token( TK_LtColon ); };
838
839                 # Opening of longest match.
840                 "|*" => { token( TK_BarStar ); };
841
842                 # Separater for name references.
843                 "::" => { token( TK_NameSep, tokstart, tokend ); };
844
845                 '}%%' => { 
846                         updateCol();
847                         endSection();
848                         fgoto main;
849                 };
850
851                 [ \t\r]+ => { updateCol(); };
852
853                 # If we are in a single line machine then newline may end the spec.
854                 NL => {
855                         updateCol();
856                         if ( singleLineSpec ) {
857                                 endSection();
858                                 fgoto main;
859                         }
860                 };
861
862                 '{' => { 
863                         if ( lastToken == KW_Export || lastToken == KW_Entry )
864                                 token( '{' );
865                         else {
866                                 token( '{' );
867                                 curly_count = 1; 
868                                 inlineBlockType = CurlyDelimited;
869                                 fgoto inline_code;
870                         }
871                 };
872
873                 EOF => {
874                         scan_error() << "unterminated ragel section" << endl;
875                 };
876
877                 any => { token( *tokstart ); } ;
878         *|;
879
880         # Outside code scanner. These tokens get passed through.
881         main := |*
882                 'define' => { pass( IMP_Define, 0, 0 ); };
883                 ident => { pass( IMP_Word, tokstart, tokend ); };
884                 number => { pass( IMP_UInt, tokstart, tokend ); };
885                 c_cpp_comment => { pass(); };
886                 ( s_literal | d_literal ) => { pass( IMP_Literal, tokstart, tokend ); };
887
888                 '%%{' => { 
889                         updateCol();
890                         singleLineSpec = false;
891                         startSection();
892                         fgoto parser_def;
893                 };
894                 '%%' => { 
895                         updateCol();
896                         singleLineSpec = true;
897                         startSection();
898                         fgoto parser_def;
899                 };
900                 whitespace+ => { pass(); };
901                 EOF;
902                 any => { pass( *tokstart, 0, 0 ); };
903         *|;
904 }%%
905
906 %% write data;
907
908 void Scanner::do_scan()
909 {
910         int bufsize = 8;
911         char *buf = new char[bufsize];
912         const char last_char = 0;
913         int cs, act, have = 0;
914         int top, stack[1];
915         int curly_count = 0;
916         bool execute = true;
917         bool singleLineSpec = false;
918         InlineBlockType inlineBlockType = CurlyDelimited;
919
920         /* Init the section parser and the character scanner. */
921         init();
922         %% write init;
923
924         while ( execute ) {
925                 char *p = buf + have;
926                 int space = bufsize - have;
927
928                 if ( space == 0 ) {
929                         /* We filled up the buffer trying to scan a token. Grow it. */
930                         bufsize = bufsize * 2;
931                         char *newbuf = new char[bufsize];
932
933                         /* Recompute p and space. */
934                         p = newbuf + have;
935                         space = bufsize - have;
936
937                         /* Patch up pointers possibly in use. */
938                         if ( tokstart != 0 )
939                                 tokstart = newbuf + ( tokstart - buf );
940                         tokend = newbuf + ( tokend - buf );
941
942                         /* Copy the new buffer in. */
943                         memcpy( newbuf, buf, have );
944                         delete[] buf;
945                         buf = newbuf;
946                 }
947
948                 input.read( p, space );
949                 int len = input.gcount();
950
951                 /* If we see eof then append the EOF char. */
952                 if ( len == 0 ) {
953                         p[0] = last_char, len = 1;
954                         execute = false;
955                 }
956
957                 char *pe = p + len;
958                 %% write exec;
959
960                 /* Check if we failed. */
961                 if ( cs == rlscan_error ) {
962                         /* Machine failed before finding a token. I'm not yet sure if this
963                          * is reachable. */
964                         scan_error() << "scanner error" << endl;
965                         exit(1);
966                 }
967
968                 /* Decide if we need to preserve anything. */
969                 char *preserve = tokstart;
970
971                 /* Now set up the prefix. */
972                 if ( preserve == 0 )
973                         have = 0;
974                 else {
975                         /* There is data that needs to be shifted over. */
976                         have = pe - preserve;
977                         memmove( buf, preserve, have );
978                         unsigned int shiftback = preserve - buf;
979                         if ( tokstart != 0 )
980                                 tokstart -= shiftback;
981                         tokend -= shiftback;
982
983                         preserve = buf;
984                 }
985         }
986
987         delete[] buf;
988 }
989
990 void scan( char *fileName, istream &input, ostream &output )
991 {
992 }