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