Added support for Ruby comments and regular expression literals.
[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         ruby_comment = '#' [^\n]* NL;
540
541         # These literal forms are common to host code and ragel.
542         s_literal = "'" ([^'\\] | NL | '\\' (any | NL))* "'";
543         d_literal = '"' ([^"\\] | NL | '\\' (any | NL))* '"';
544         host_re_literal = '/' ([^/\\] | NL | '\\' (any | NL))* '/';
545
546         whitespace = [ \t] | NL;
547         pound_comment = '#' [^\n]* NL;
548
549         # An inline block of code for Ruby.
550         inline_code_ruby := |*
551                 # Inline expression keywords.
552                 "fpc" => { token( KW_PChar ); };
553                 "fc" => { token( KW_Char ); };
554                 "fcurs" => { token( KW_CurState ); };
555                 "ftargs" => { token( KW_TargState ); };
556                 "fentry" => { 
557                         whitespaceOn = false; 
558                         token( KW_Entry );
559                 };
560
561                 # Inline statement keywords.
562                 "fhold" => { 
563                         whitespaceOn = false; 
564                         token( KW_Hold );
565                 };
566                 "fexec" => { token( KW_Exec, 0, 0 ); };
567                 "fgoto" => { 
568                         whitespaceOn = false; 
569                         token( KW_Goto );
570                 };
571                 "fnext" => { 
572                         whitespaceOn = false; 
573                         token( KW_Next );
574                 };
575                 "fcall" => { 
576                         whitespaceOn = false; 
577                         token( KW_Call );
578                 };
579                 "fret" => { 
580                         whitespaceOn = false; 
581                         token( KW_Ret );
582                 };
583                 "fbreak" => { 
584                         whitespaceOn = false; 
585                         token( KW_Break );
586                 };
587
588                 ident => { token( TK_Word, tokstart, tokend ); };
589
590                 number => { token( TK_UInt, tokstart, tokend ); };
591                 hex_number => { token( TK_Hex, tokstart, tokend ); };
592
593                 ( s_literal | d_literal | host_re_literal ) 
594                         => { token( IL_Literal, tokstart, tokend ); };
595
596                 whitespace+ => { 
597                         if ( whitespaceOn ) 
598                                 token( IL_WhiteSpace, tokstart, tokend );
599                 };
600
601                 ruby_comment => { token( IL_Comment, tokstart, tokend ); };
602
603                 "::" => { token( TK_NameSep, tokstart, tokend ); };
604
605                 # Some symbols need to go to the parser as with their cardinal value as
606                 # the token type (as opposed to being sent as anonymous symbols)
607                 # because they are part of the sequences which we interpret. The * ) ;
608                 # symbols cause whitespace parsing to come back on. This gets turned
609                 # off by some keywords.
610
611                 ";" => {
612                         whitespaceOn = true;
613                         token( *tokstart, tokstart, tokend );
614                         if ( inlineBlockType == SemiTerminated )
615                                 fret;
616                 };
617
618                 [*)] => { 
619                         whitespaceOn = true;
620                         token( *tokstart, tokstart, tokend );
621                 };
622
623                 [,(] => { token( *tokstart, tokstart, tokend ); };
624
625                 '{' => { 
626                         token( IL_Symbol, tokstart, tokend );
627                         curly_count += 1; 
628                 };
629
630                 '}' => { 
631                         if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
632                                 /* Inline code block ends. */
633                                 token( '}' );
634                                 fret;
635                         }
636                         else {
637                                 /* Either a semi terminated inline block or only the closing
638                                  * brace of some inner scope, not the block's closing brace. */
639                                 token( IL_Symbol, tokstart, tokend );
640                         }
641                 };
642
643                 EOF => {
644                         scan_error() << "unterminated code block" << endl;
645                 };
646
647                 # Send every other character as a symbol.
648                 any => { token( IL_Symbol, tokstart, tokend ); };
649         *|;
650
651
652         # An inline block of code for languages other than Ruby.
653         inline_code := |*
654                 # Inline expression keywords.
655                 "fpc" => { token( KW_PChar ); };
656                 "fc" => { token( KW_Char ); };
657                 "fcurs" => { token( KW_CurState ); };
658                 "ftargs" => { token( KW_TargState ); };
659                 "fentry" => { 
660                         whitespaceOn = false; 
661                         token( KW_Entry );
662                 };
663
664                 # Inline statement keywords.
665                 "fhold" => { 
666                         whitespaceOn = false; 
667                         token( KW_Hold );
668                 };
669                 "fexec" => { token( KW_Exec, 0, 0 ); };
670                 "fgoto" => { 
671                         whitespaceOn = false; 
672                         token( KW_Goto );
673                 };
674                 "fnext" => { 
675                         whitespaceOn = false; 
676                         token( KW_Next );
677                 };
678                 "fcall" => { 
679                         whitespaceOn = false; 
680                         token( KW_Call );
681                 };
682                 "fret" => { 
683                         whitespaceOn = false; 
684                         token( KW_Ret );
685                 };
686                 "fbreak" => { 
687                         whitespaceOn = false; 
688                         token( KW_Break );
689                 };
690
691                 ident => { token( TK_Word, tokstart, tokend ); };
692
693                 number => { token( TK_UInt, tokstart, tokend ); };
694                 hex_number => { token( TK_Hex, tokstart, tokend ); };
695
696                 ( s_literal | d_literal ) 
697                         => { token( IL_Literal, tokstart, tokend ); };
698
699                 whitespace+ => { 
700                         if ( whitespaceOn ) 
701                                 token( IL_WhiteSpace, tokstart, tokend );
702                 };
703
704                 c_cpp_comment => { token( IL_Comment, tokstart, tokend ); };
705
706                 "::" => { token( TK_NameSep, tokstart, tokend ); };
707
708                 # Some symbols need to go to the parser as with their cardinal value as
709                 # the token type (as opposed to being sent as anonymous symbols)
710                 # because they are part of the sequences which we interpret. The * ) ;
711                 # symbols cause whitespace parsing to come back on. This gets turned
712                 # off by some keywords.
713
714                 ";" => {
715                         whitespaceOn = true;
716                         token( *tokstart, tokstart, tokend );
717                         if ( inlineBlockType == SemiTerminated )
718                                 fret;
719                 };
720
721                 [*)] => { 
722                         whitespaceOn = true;
723                         token( *tokstart, tokstart, tokend );
724                 };
725
726                 [,(] => { token( *tokstart, tokstart, tokend ); };
727
728                 '{' => { 
729                         token( IL_Symbol, tokstart, tokend );
730                         curly_count += 1; 
731                 };
732
733                 '}' => { 
734                         if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
735                                 /* Inline code block ends. */
736                                 token( '}' );
737                                 fret;
738                         }
739                         else {
740                                 /* Either a semi terminated inline block or only the closing
741                                  * brace of some inner scope, not the block's closing brace. */
742                                 token( IL_Symbol, tokstart, tokend );
743                         }
744                 };
745
746                 EOF => {
747                         scan_error() << "unterminated code block" << endl;
748                 };
749
750                 # Send every other character as a symbol.
751                 any => { token( IL_Symbol, tokstart, tokend ); };
752         *|;
753
754         or_literal := |*
755                 # Escape sequences in OR expressions.
756                 '\\0' => { token( RE_Char, '\0' ); };
757                 '\\a' => { token( RE_Char, '\a' ); };
758                 '\\b' => { token( RE_Char, '\b' ); };
759                 '\\t' => { token( RE_Char, '\t' ); };
760                 '\\n' => { token( RE_Char, '\n' ); };
761                 '\\v' => { token( RE_Char, '\v' ); };
762                 '\\f' => { token( RE_Char, '\f' ); };
763                 '\\r' => { token( RE_Char, '\r' ); };
764                 '\\\n' => { updateCol(); };
765                 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
766
767                 # Range dash in an OR expression.
768                 '-' => { token( RE_Dash, 0, 0 ); };
769
770                 # Terminate an OR expression.
771                 ']'     => { token( RE_SqClose ); fret; };
772
773                 EOF => {
774                         scan_error() << "unterminated OR literal" << endl;
775                 };
776
777                 # Characters in an OR expression.
778                 [^\]] => { token( RE_Char, tokstart, tokend ); };
779
780         *|;
781
782         ragel_re_literal := |*
783                 # Escape sequences in regular expressions.
784                 '\\0' => { token( RE_Char, '\0' ); };
785                 '\\a' => { token( RE_Char, '\a' ); };
786                 '\\b' => { token( RE_Char, '\b' ); };
787                 '\\t' => { token( RE_Char, '\t' ); };
788                 '\\n' => { token( RE_Char, '\n' ); };
789                 '\\v' => { token( RE_Char, '\v' ); };
790                 '\\f' => { token( RE_Char, '\f' ); };
791                 '\\r' => { token( RE_Char, '\r' ); };
792                 '\\\n' => { updateCol(); };
793                 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
794
795                 # Terminate an OR expression.
796                 '/' [i]? => { 
797                         token( RE_Slash, tokstart, tokend ); 
798                         fgoto parser_def;
799                 };
800
801                 # Special characters.
802                 '.' => { token( RE_Dot ); };
803                 '*' => { token( RE_Star ); };
804
805                 '[' => { token( RE_SqOpen ); fcall or_literal; };
806                 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
807
808                 EOF => {
809                         scan_error() << "unterminated regular expression" << endl;
810                 };
811
812                 # Characters in an OR expression.
813                 [^\/] => { token( RE_Char, tokstart, tokend ); };
814         *|;
815
816         # We need a separate token space here to avoid the ragel keywords.
817         write_statement := |*
818                 ident => { token( TK_Word, tokstart, tokend ); } ;
819                 [ \t\n]+ => { updateCol(); };
820                 ';' => { token( ';' ); fgoto parser_def; };
821
822                 EOF => {
823                         scan_error() << "unterminated write statement" << endl;
824                 };
825         *|;
826
827         # Parser definitions. 
828         parser_def := |*
829                 'machine' => { token( KW_Machine ); };
830                 'include' => { token( KW_Include ); };
831                 'import' => { token( KW_Import ); };
832                 'write' => { 
833                         token( KW_Write );
834                         fgoto write_statement;
835                 };
836                 'action' => { token( KW_Action ); };
837                 'alphtype' => { token( KW_AlphType ); };
838
839                 # FIXME: Enable this post 5.17.
840                 # 'range' => { token( KW_Range ); };
841
842                 'getkey' => { 
843                         token( KW_GetKey );
844                         inlineBlockType = SemiTerminated;
845                         if ( hostLangType == RubyCode )
846                                 fcall inline_code_ruby;
847                         else
848                                 fcall inline_code;
849                 };
850                 'access' => { 
851                         token( KW_Access );
852                         inlineBlockType = SemiTerminated;
853                         if ( hostLangType == RubyCode )
854                                 fcall inline_code_ruby;
855                         else
856                                 fcall inline_code;
857                 };
858                 'variable' => { 
859                         token( KW_Variable );
860                         inlineBlockType = SemiTerminated;
861                         if ( hostLangType == RubyCode )
862                                 fcall inline_code_ruby;
863                         else
864                                 fcall inline_code;
865                 };
866                 'when' => { token( KW_When ); };
867                 'eof' => { token( KW_Eof ); };
868                 'err' => { token( KW_Err ); };
869                 'lerr' => { token( KW_Lerr ); };
870                 'to' => { token( KW_To ); };
871                 'from' => { token( KW_From ); };
872                 'export' => { token( KW_Export ); };
873
874                 # Identifiers.
875                 ident => { token( TK_Word, tokstart, tokend ); } ;
876
877                 # Numbers
878                 number => { token( TK_UInt, tokstart, tokend ); };
879                 hex_number => { token( TK_Hex, tokstart, tokend ); };
880
881                 # Literals, with optionals.
882                 ( s_literal | d_literal ) [i]? 
883                         => { token( TK_Literal, tokstart, tokend ); };
884
885                 '[' => { token( RE_SqOpen ); fcall or_literal; };
886                 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
887
888                 '/' => { token( RE_Slash ); fgoto ragel_re_literal; };
889
890                 # Ignore.
891                 pound_comment => { updateCol(); };
892
893                 ':=' => { token( TK_ColonEquals ); };
894
895                 # To State Actions.
896                 ">~" => { token( TK_StartToState ); };
897                 "$~" => { token( TK_AllToState ); };
898                 "%~" => { token( TK_FinalToState ); };
899                 "<~" => { token( TK_NotStartToState ); };
900                 "@~" => { token( TK_NotFinalToState ); };
901                 "<>~" => { token( TK_MiddleToState ); };
902
903                 # From State actions
904                 ">*" => { token( TK_StartFromState ); };
905                 "$*" => { token( TK_AllFromState ); };
906                 "%*" => { token( TK_FinalFromState ); };
907                 "<*" => { token( TK_NotStartFromState ); };
908                 "@*" => { token( TK_NotFinalFromState ); };
909                 "<>*" => { token( TK_MiddleFromState ); };
910
911                 # EOF Actions.
912                 ">/" => { token( TK_StartEOF ); };
913                 "$/" => { token( TK_AllEOF ); };
914                 "%/" => { token( TK_FinalEOF ); };
915                 "</" => { token( TK_NotStartEOF ); };
916                 "@/" => { token( TK_NotFinalEOF ); };
917                 "<>/" => { token( TK_MiddleEOF ); };
918
919                 # Global Error actions.
920                 ">!" => { token( TK_StartGblError ); };
921                 "$!" => { token( TK_AllGblError ); };
922                 "%!" => { token( TK_FinalGblError ); };
923                 "<!" => { token( TK_NotStartGblError ); };
924                 "@!" => { token( TK_NotFinalGblError ); };
925                 "<>!" => { token( TK_MiddleGblError ); };
926
927                 # Local error actions.
928                 ">^" => { token( TK_StartLocalError ); };
929                 "$^" => { token( TK_AllLocalError ); };
930                 "%^" => { token( TK_FinalLocalError ); };
931                 "<^" => { token( TK_NotStartLocalError ); };
932                 "@^" => { token( TK_NotFinalLocalError ); };
933                 "<>^" => { token( TK_MiddleLocalError ); };
934
935                 # Middle.
936                 "<>" => { token( TK_Middle ); };
937
938                 # Conditions. 
939                 '>?' => { token( TK_StartCond ); };
940                 '$?' => { token( TK_AllCond ); };
941                 '%?' => { token( TK_LeavingCond ); };
942
943                 '..' => { token( TK_DotDot ); };
944                 '**' => { token( TK_StarStar ); };
945                 '--' => { token( TK_DashDash ); };
946                 '->' => { token( TK_Arrow ); };
947                 '=>' => { token( TK_DoubleArrow ); };
948
949                 ":>"  => { token( TK_ColonGt ); };
950                 ":>>" => { token( TK_ColonGtGt ); };
951                 "<:"  => { token( TK_LtColon ); };
952
953                 # Opening of longest match.
954                 "|*" => { token( TK_BarStar ); };
955
956                 # Separater for name references.
957                 "::" => { token( TK_NameSep, tokstart, tokend ); };
958
959                 '}%%' => { 
960                         updateCol();
961                         endSection();
962                         fret;
963                 };
964
965                 [ \t\r]+ => { updateCol(); };
966
967                 # If we are in a single line machine then newline may end the spec.
968                 NL => {
969                         updateCol();
970                         if ( singleLineSpec ) {
971                                 endSection();
972                                 fret;
973                         }
974                 };
975
976                 '{' => { 
977                         if ( lastToken == KW_Export || lastToken == KW_Entry )
978                                 token( '{' );
979                         else {
980                                 token( '{' );
981                                 curly_count = 1; 
982                                 inlineBlockType = CurlyDelimited;
983                                 if ( hostLangType == RubyCode )
984                                         fcall inline_code_ruby;
985                                 else
986                                         fcall inline_code;
987                         }
988                 };
989
990                 EOF => {
991                         scan_error() << "unterminated ragel section" << endl;
992                 };
993
994                 any => { token( *tokstart ); } ;
995         *|;
996
997         # Outside code scanner. These tokens get passed through.
998         main_ruby := |*
999                 ident => { pass( IMP_Word, tokstart, tokend ); };
1000                 number => { pass( IMP_UInt, tokstart, tokend ); };
1001                 ruby_comment => { pass(); };
1002                 ( s_literal | d_literal | host_re_literal ) 
1003                         => { pass( IMP_Literal, tokstart, tokend ); };
1004
1005                 '%%{' => { 
1006                         updateCol();
1007                         singleLineSpec = false;
1008                         startSection();
1009                         fcall parser_def;
1010                 };
1011                 '%%' => { 
1012                         updateCol();
1013                         singleLineSpec = true;
1014                         startSection();
1015                         fcall parser_def;
1016                 };
1017                 whitespace+ => { pass(); };
1018                 EOF;
1019                 any => { pass( *tokstart, 0, 0 ); };
1020         *|;
1021
1022         # Outside code scanner. These tokens get passed through.
1023         main := |*
1024                 'define' => { pass( IMP_Define, 0, 0 ); };
1025                 ident => { pass( IMP_Word, tokstart, tokend ); };
1026                 number => { pass( IMP_UInt, tokstart, tokend ); };
1027                 c_cpp_comment => { pass(); };
1028                 ( s_literal | d_literal ) => { pass( IMP_Literal, tokstart, tokend ); };
1029
1030                 '%%{' => { 
1031                         updateCol();
1032                         singleLineSpec = false;
1033                         startSection();
1034                         fcall parser_def;
1035                 };
1036                 '%%' => { 
1037                         updateCol();
1038                         singleLineSpec = true;
1039                         startSection();
1040                         fcall parser_def;
1041                 };
1042                 whitespace+ => { pass(); };
1043                 EOF;
1044                 any => { pass( *tokstart, 0, 0 ); };
1045         *|;
1046 }%%
1047
1048 %% write data;
1049
1050 void Scanner::do_scan()
1051 {
1052         int bufsize = 8;
1053         char *buf = new char[bufsize];
1054         const char last_char = 0;
1055         int cs, act, have = 0;
1056         int top;
1057
1058         /* The stack is two deep, one level for going into ragel defs from the main
1059          * machines which process outside code, and another for going into or literals
1060          * from either a ragel spec, or a regular expression. */
1061         int stack[2];
1062         int curly_count = 0;
1063         bool execute = true;
1064         bool singleLineSpec = false;
1065         InlineBlockType inlineBlockType = CurlyDelimited;
1066
1067         /* Init the section parser and the character scanner. */
1068         init();
1069         %% write init;
1070
1071         /* Set up the start state. FIXME: After 5.20 is released the nocs write
1072          * init option should be used, the main machine eliminated and this statement moved
1073          * above the write init. */
1074         if ( hostLangType == RubyCode )
1075                 cs = rlscan_en_main_ruby;
1076         else
1077                 cs = rlscan_en_main;
1078         
1079         while ( execute ) {
1080                 char *p = buf + have;
1081                 int space = bufsize - have;
1082
1083                 if ( space == 0 ) {
1084                         /* We filled up the buffer trying to scan a token. Grow it. */
1085                         bufsize = bufsize * 2;
1086                         char *newbuf = new char[bufsize];
1087
1088                         /* Recompute p and space. */
1089                         p = newbuf + have;
1090                         space = bufsize - have;
1091
1092                         /* Patch up pointers possibly in use. */
1093                         if ( tokstart != 0 )
1094                                 tokstart = newbuf + ( tokstart - buf );
1095                         tokend = newbuf + ( tokend - buf );
1096
1097                         /* Copy the new buffer in. */
1098                         memcpy( newbuf, buf, have );
1099                         delete[] buf;
1100                         buf = newbuf;
1101                 }
1102
1103                 input.read( p, space );
1104                 int len = input.gcount();
1105
1106                 /* If we see eof then append the EOF char. */
1107                 if ( len == 0 ) {
1108                         p[0] = last_char, len = 1;
1109                         execute = false;
1110                 }
1111
1112                 char *pe = p + len;
1113                 %% write exec;
1114
1115                 /* Check if we failed. */
1116                 if ( cs == rlscan_error ) {
1117                         /* Machine failed before finding a token. I'm not yet sure if this
1118                          * is reachable. */
1119                         scan_error() << "scanner error" << endl;
1120                         exit(1);
1121                 }
1122
1123                 /* Decide if we need to preserve anything. */
1124                 char *preserve = tokstart;
1125
1126                 /* Now set up the prefix. */
1127                 if ( preserve == 0 )
1128                         have = 0;
1129                 else {
1130                         /* There is data that needs to be shifted over. */
1131                         have = pe - preserve;
1132                         memmove( buf, preserve, have );
1133                         unsigned int shiftback = preserve - buf;
1134                         if ( tokstart != 0 )
1135                                 tokstart -= shiftback;
1136                         tokend -= shiftback;
1137
1138                         preserve = buf;
1139                 }
1140         }
1141
1142         delete[] buf;
1143 }