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