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