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