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