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