Can now embed the negative sense of a condition.
[external/ragel.git] / ragel / rlscan.rl
1 /*
2  *  Copyright 2006-2007 Adrian Thurston <thurston@cs.queensu.ca>
3  */
4
5 /*  This file is part of Ragel.
6  *
7  *  Ragel is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  * 
12  *  Ragel is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  * 
17  *  You should have received a copy of the GNU General Public License
18  *  along with Ragel; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
20  */
21
22 #include <iostream>
23 #include <fstream>
24 #include <string.h>
25
26 #include "ragel.h"
27 #include "rlscan.h"
28
29 //#define LOG_TOKENS
30
31 using std::ifstream;
32 using std::istream;
33 using std::ostream;
34 using std::cout;
35 using std::cerr;
36 using std::endl;
37
38 enum InlineBlockType
39 {
40         CurlyDelimited,
41         SemiTerminated
42 };
43
44
45 /*
46  * The Scanner for Importing
47  */
48
49 %%{
50         machine inline_token_scan;
51         alphtype int;
52         access tok_;
53
54         # Import scanner tokens.
55         import "rlparse.h"; 
56
57         main := |*
58                 # Define of number.
59                 IMP_Define IMP_Word IMP_UInt => { 
60                         int base = tok_tokstart - token_data;
61                         int nameOff = 1;
62                         int numOff = 2;
63
64                         directToParser( inclToParser, fileName, line, column, TK_Word, 
65                                         token_strings[base+nameOff], token_lens[base+nameOff] );
66                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
67                         directToParser( inclToParser, fileName, line, column, TK_UInt,
68                                         token_strings[base+numOff], token_lens[base+numOff] );
69                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
70                 };
71
72                 # Assignment of number.
73                 IMP_Word '=' IMP_UInt => { 
74                         int base = tok_tokstart - token_data;
75                         int nameOff = 0;
76                         int numOff = 2;
77
78                         directToParser( inclToParser, fileName, line, column, TK_Word, 
79                                         token_strings[base+nameOff], token_lens[base+nameOff] );
80                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
81                         directToParser( inclToParser, fileName, line, column, TK_UInt,
82                                         token_strings[base+numOff], token_lens[base+numOff] );
83                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
84                 };
85
86                 # Define of literal.
87                 IMP_Define IMP_Word IMP_Literal => { 
88                         int base = tok_tokstart - token_data;
89                         int nameOff = 1;
90                         int litOff = 2;
91
92                         directToParser( inclToParser, fileName, line, column, TK_Word, 
93                                         token_strings[base+nameOff], token_lens[base+nameOff] );
94                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
95                         directToParser( inclToParser, fileName, line, column, TK_Literal,
96                                         token_strings[base+litOff], token_lens[base+litOff] );
97                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
98                 };
99
100                 # Assignment of literal.
101                 IMP_Word '=' IMP_Literal => { 
102                         int base = tok_tokstart - token_data;
103                         int nameOff = 0;
104                         int litOff = 2;
105
106                         directToParser( inclToParser, fileName, line, column, TK_Word, 
107                                         token_strings[base+nameOff], token_lens[base+nameOff] );
108                         directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
109                         directToParser( inclToParser, fileName, line, column, TK_Literal,
110                                         token_strings[base+litOff], token_lens[base+litOff] );
111                         directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
112                 };
113
114                 # Catch everything else.
115                 any;
116         *|;
117 }%%
118
119 %% write data;
120
121 void Scanner::flushImport()
122 {
123         int *p = token_data;
124         int *pe = token_data + cur_token;
125
126         %% write init;
127         %% write exec;
128
129         if ( tok_tokstart == 0 )
130                 cur_token = 0;
131         else {
132                 cur_token = pe - tok_tokstart;
133                 int ts_offset = tok_tokstart - token_data;
134                 memmove( token_data, token_data+ts_offset, cur_token*sizeof(token_data[0]) );
135                 memmove( token_strings, token_strings+ts_offset, cur_token*sizeof(token_strings[0]) );
136                 memmove( token_lens, token_lens+ts_offset, cur_token*sizeof(token_lens[0]) );
137         }
138 }
139
140 void Scanner::directToParser( Parser *toParser, char *tokFileName, int tokLine, 
141                 int tokColumn, int type, char *tokdata, int toklen )
142 {
143         InputLoc loc;
144
145         #ifdef LOG_TOKENS
146         cerr << "scanner:" << tokLine << ":" << tokColumn << 
147                         ": sending token to the parser " << Parser_lelNames[type];
148         cerr << " " << toklen;
149         if ( tokdata != 0 )
150                 cerr << " " << tokdata;
151         cerr << endl;
152         #endif
153
154         loc.fileName = tokFileName;
155         loc.line = tokLine;
156         loc.col = tokColumn;
157
158         toParser->token( loc, type, tokdata, toklen );
159 }
160
161 void Scanner::importToken( int token, char *start, char *end )
162 {
163         if ( cur_token == max_tokens )
164                 flushImport();
165
166         token_data[cur_token] = token;
167         if ( start == 0 ) {
168                 token_strings[cur_token] = 0;
169                 token_lens[cur_token] = 0;
170         }
171         else {
172                 int toklen = end-start;
173                 token_lens[cur_token] = toklen;
174                 token_strings[cur_token] = new char[toklen+1];
175                 memcpy( token_strings[cur_token], start, toklen );
176                 token_strings[cur_token][toklen] = 0;
177         }
178         cur_token++;
179 }
180
181 void Scanner::pass( int token, char *start, char *end )
182 {
183         if ( importMachines )
184                 importToken( token, start, end );
185         pass();
186 }
187
188 void Scanner::pass()
189 {
190         updateCol();
191
192         /* If no errors and we are at the bottom of the include stack (the
193          * source file listed on the command line) then write out the data. */
194         if ( includeDepth == 0 && machineSpec == 0 && machineName == 0 )
195                 xmlEscapeHost( output, tokstart, tokend-tokstart );
196 }
197
198 /*
199  * The scanner for processing sections, includes, imports, etc.
200  */
201
202 %%{
203         machine section_parse;
204         alphtype int;
205         write data;
206 }%%
207
208
209 void Scanner::init( )
210 {
211         %% write init;
212 }
213
214 bool Scanner::active()
215 {
216         if ( ignoreSection )
217                 return false;
218
219         if ( parser == 0 && ! parserExistsError ) {
220                 scan_error() << "there is no previous specification name" << endl;
221                 parserExistsError = true;
222         }
223
224         if ( parser == 0 )
225                 return false;
226
227         return true;
228 }
229
230 ostream &Scanner::scan_error()
231 {
232         /* Maintain the error count. */
233         gblErrorCount += 1;
234         cerr << fileName << ":" << line << ":" << column << ": ";
235         return cerr;
236 }
237
238 bool Scanner::recursiveInclude( char *inclFileName, char *inclSectionName )
239 {
240         for ( IncludeStack::Iter si = includeStack; si.lte(); si++ ) {
241                 if ( strcmp( si->fileName, inclFileName ) == 0 &&
242                                 strcmp( si->sectionName, inclSectionName ) == 0 )
243                 {
244                         return true;
245                 }
246         }
247         return false;   
248 }
249
250 void Scanner::updateCol()
251 {
252         char *from = lastnl;
253         if ( from == 0 )
254                 from = tokstart;
255         //cerr << "adding " << tokend - from << " to column" << endl;
256         column += tokend - from;
257         lastnl = 0;
258 }
259
260 %%{
261         machine section_parse;
262
263         # Need the defines representing tokens.
264         import "rlparse.h"; 
265
266         action clear_words { word = lit = 0; word_len = lit_len = 0; }
267         action store_word { word = tokdata; word_len = toklen; }
268         action store_lit { lit = tokdata; lit_len = toklen; }
269
270         action mach_err { scan_error() << "bad machine statement" << endl; }
271         action incl_err { scan_error() << "bad include statement" << endl; }
272         action import_err { scan_error() << "bad import statement" << endl; }
273         action write_err { scan_error() << "bad write statement" << endl; }
274
275         action handle_machine
276         {
277                 /* Assign a name to the machine. */
278                 char *machine = word;
279
280                 if ( !importMachines && inclSectionTarg == 0 ) {
281                         ignoreSection = false;
282
283                         ParserDictEl *pdEl = parserDict.find( machine );
284                         if ( pdEl == 0 ) {
285                                 pdEl = new ParserDictEl( machine );
286                                 pdEl->value = new Parser( fileName, machine, sectionLoc );
287                                 pdEl->value->init();
288                                 parserDict.insert( pdEl );
289                         }
290
291                         parser = pdEl->value;
292                 }
293                 else if ( !importMachines && strcmp( inclSectionTarg, machine ) == 0 ) {
294                         /* found include target */
295                         ignoreSection = false;
296                         parser = inclToParser;
297                 }
298                 else {
299                         /* ignoring section */
300                         ignoreSection = true;
301                         parser = 0;
302                 }
303         }
304
305         machine_stmt =
306                 ( KW_Machine TK_Word @store_word ';' ) @handle_machine
307                 <>err mach_err <>eof mach_err;
308
309         action handle_include
310         {
311                 if ( active() ) {
312                         char *inclSectionName = word;
313                         char *inclFileName = 0;
314
315                         /* Implement defaults for the input file and section name. */
316                         if ( inclSectionName == 0 )
317                                 inclSectionName = parser->sectionName;
318
319                         if ( lit != 0 ) 
320                                 inclFileName = prepareFileName( lit, lit_len );
321                         else
322                                 inclFileName = fileName;
323
324                         /* Check for a recursive include structure. Add the current file/section
325                          * name then check if what we are including is already in the stack. */
326                         includeStack.append( IncludeStackItem( fileName, parser->sectionName ) );
327
328                         if ( recursiveInclude( inclFileName, inclSectionName ) )
329                                 scan_error() << "include: this is a recursive include operation" << endl;
330                         else {
331                                 /* Open the input file for reading. */
332                                 ifstream *inFile = new ifstream( inclFileName );
333                                 if ( ! inFile->is_open() ) {
334                                         scan_error() << "include: could not open " << 
335                                                         inclFileName << " for reading" << endl;
336                                 }
337
338                                 Scanner scanner( inclFileName, *inFile, output, parser,
339                                                 inclSectionName, includeDepth+1, false );
340                                 scanner.do_scan( );
341                                 delete inFile;
342                         }
343
344                         /* Remove the last element (len-1) */
345                         includeStack.remove( -1 );
346                 }
347         }
348
349         include_names = (
350                 TK_Word @store_word ( TK_Literal @store_lit )? |
351                 TK_Literal @store_lit
352         ) >clear_words;
353
354         include_stmt =
355                 ( KW_Include include_names ';' ) @handle_include
356                 <>err incl_err <>eof incl_err;
357
358         action handle_import
359         {
360                 if ( active() ) {
361                         char *importFileName = prepareFileName( lit, lit_len );
362
363                         /* Open the input file for reading. */
364                         ifstream *inFile = new ifstream( importFileName );
365                         if ( ! inFile->is_open() ) {
366                                 scan_error() << "import: could not open " << 
367                                                 importFileName << " for reading" << endl;
368                         }
369
370                         Scanner scanner( importFileName, *inFile, output, parser,
371                                         0, includeDepth+1, true );
372                         scanner.do_scan( );
373                         scanner.importToken( 0, 0, 0 );
374                         scanner.flushImport();
375                         delete inFile;
376                 }
377         }
378
379         import_stmt =
380                 ( KW_Import TK_Literal @store_lit ';' ) @handle_import
381                 <>err import_err <>eof import_err;
382
383         action write_command
384         {
385                 if ( active() && machineSpec == 0 && machineName == 0 ) {
386                         output << "<write"
387                                         " def_name=\"" << parser->sectionName << "\""
388                                         " line=\"" << line << "\""
389                                         " col=\"" << column << "\""
390                                         ">";
391                 }
392         }
393
394         action write_arg
395         {
396                 if ( active() && machineSpec == 0 && machineName == 0 )
397                         output << "<arg>" << tokdata << "</arg>";
398         }
399
400         action write_close
401         {
402                 if ( active() && machineSpec == 0 && machineName == 0 )
403                         output << "</write>\n";
404         }
405
406         write_stmt =
407                 ( KW_Write @write_command 
408                 ( TK_Word @write_arg )+ ';' @write_close )
409                 <>err write_err <>eof write_err;
410
411         action handle_token
412         {
413                 /* Send the token off to the parser. */
414                 if ( active() )
415                         directToParser( parser, fileName, line, column, type, tokdata, toklen );
416         }
417
418         # Catch everything else.
419         everything_else = 
420                 ^( KW_Machine | KW_Include | KW_Import | KW_Write ) @handle_token;
421
422         main := ( 
423                 machine_stmt |
424                 include_stmt |
425                 import_stmt |
426                 write_stmt |
427                 everything_else
428         )*;
429 }%%
430
431 void Scanner::token( int type, char c )
432 {
433         token( type, &c, &c + 1 );
434 }
435
436 void Scanner::token( int type )
437 {
438         token( type, 0, 0 );
439 }
440
441 void Scanner::token( int type, char *start, char *end )
442 {
443         char *tokdata = 0;
444         int toklen = 0;
445         if ( start != 0 ) {
446                 toklen = end-start;
447                 tokdata = new char[toklen+1];
448                 memcpy( tokdata, start, toklen );
449                 tokdata[toklen] = 0;
450         }
451
452         processToken( type, tokdata, toklen );
453 }
454
455 void Scanner::processToken( int type, char *tokdata, int toklen )
456 {
457         int *p = &type;
458         int *pe = &type + 1;
459
460         %%{
461                 machine section_parse;
462                 write exec;
463         }%%
464
465         updateCol();
466
467         /* Record the last token for use in controlling the scan of subsequent
468          * tokens. */
469         lastToken = type;
470 }
471
472 void Scanner::startSection( )
473 {
474         parserExistsError = false;
475
476         if ( includeDepth == 0 ) {
477                 if ( machineSpec == 0 && machineName == 0 )
478                         output << "</host>\n";
479         }
480
481         sectionLoc.fileName = fileName;
482         sectionLoc.line = line;
483         sectionLoc.col = 0;
484 }
485
486 void Scanner::endSection( )
487 {
488         /* Execute the eof actions for the section parser. */
489         %%{
490                 machine section_parse;
491                 write eof;
492         }%%
493
494         /* Close off the section with the parser. */
495         if ( active() ) {
496                 InputLoc loc;
497                 loc.fileName = fileName;
498                 loc.line = line;
499                 loc.col = 0;
500
501                 parser->token( loc, TK_EndSection, 0, 0 );
502         }
503
504         if ( includeDepth == 0 ) {
505                 if ( machineSpec == 0 && machineName == 0 ) {
506                         /* The end section may include a newline on the end, so
507                          * we use the last line, which will count the newline. */
508                         output << "<host line=\"" << line << "\">";
509                 }
510         }
511 }
512
513 %%{
514         machine rlscan;
515
516         # This is sent by the driver code.
517         EOF = 0;
518         
519         action inc_nl { 
520                 lastnl = p; 
521                 column = 0;
522                 line++;
523         }
524         NL = '\n' @inc_nl;
525
526         # Identifiers, numbers, commetns, and other common things.
527         ident = ( alpha | '_' ) ( alpha |digit |'_' )*;
528         number = digit+;
529         hex_number = '0x' [0-9a-fA-F]+;
530
531         c_comment = 
532                 '/*' ( any | NL )* :>> '*/';
533
534         cpp_comment =
535                 '//' [^\n]* NL;
536
537         c_cpp_comment = c_comment | cpp_comment;
538
539         ruby_comment = '#' [^\n]* NL;
540
541         # These literal forms are common to host code and ragel.
542         s_literal = "'" ([^'\\] | NL | '\\' (any | NL))* "'";
543         d_literal = '"' ([^"\\] | NL | '\\' (any | NL))* '"';
544         host_re_literal = '/' ([^/\\] | NL | '\\' (any | NL))* '/';
545
546         whitespace = [ \t] | NL;
547         pound_comment = '#' [^\n]* NL;
548
549         # An inline block of code for Ruby.
550         inline_code_ruby := |*
551                 # Inline expression keywords.
552                 "fpc" => { token( KW_PChar ); };
553                 "fc" => { token( KW_Char ); };
554                 "fcurs" => { token( KW_CurState ); };
555                 "ftargs" => { token( KW_TargState ); };
556                 "fentry" => { 
557                         whitespaceOn = false; 
558                         token( KW_Entry );
559                 };
560
561                 # Inline statement keywords.
562                 "fhold" => { 
563                         whitespaceOn = false; 
564                         token( KW_Hold );
565                 };
566                 "fexec" => { token( KW_Exec, 0, 0 ); };
567                 "fgoto" => { 
568                         whitespaceOn = false; 
569                         token( KW_Goto );
570                 };
571                 "fnext" => { 
572                         whitespaceOn = false; 
573                         token( KW_Next );
574                 };
575                 "fcall" => { 
576                         whitespaceOn = false; 
577                         token( KW_Call );
578                 };
579                 "fret" => { 
580                         whitespaceOn = false; 
581                         token( KW_Ret );
582                 };
583                 "fbreak" => { 
584                         whitespaceOn = false; 
585                         token( KW_Break );
586                 };
587
588                 ident => { token( TK_Word, tokstart, tokend ); };
589
590                 number => { token( TK_UInt, tokstart, tokend ); };
591                 hex_number => { token( TK_Hex, tokstart, tokend ); };
592
593                 ( s_literal | d_literal | host_re_literal ) 
594                         => { token( IL_Literal, tokstart, tokend ); };
595
596                 whitespace+ => { 
597                         if ( whitespaceOn ) 
598                                 token( IL_WhiteSpace, tokstart, tokend );
599                 };
600
601                 ruby_comment => { token( IL_Comment, tokstart, tokend ); };
602
603                 "::" => { token( TK_NameSep, tokstart, tokend ); };
604
605                 # Some symbols need to go to the parser as with their cardinal value as
606                 # the token type (as opposed to being sent as anonymous symbols)
607                 # because they are part of the sequences which we interpret. The * ) ;
608                 # symbols cause whitespace parsing to come back on. This gets turned
609                 # off by some keywords.
610
611                 ";" => {
612                         whitespaceOn = true;
613                         token( *tokstart, tokstart, tokend );
614                         if ( inlineBlockType == SemiTerminated )
615                                 fret;
616                 };
617
618                 [*)] => { 
619                         whitespaceOn = true;
620                         token( *tokstart, tokstart, tokend );
621                 };
622
623                 [,(] => { token( *tokstart, tokstart, tokend ); };
624
625                 '{' => { 
626                         token( IL_Symbol, tokstart, tokend );
627                         curly_count += 1; 
628                 };
629
630                 '}' => { 
631                         if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
632                                 /* Inline code block ends. */
633                                 token( '}' );
634                                 fret;
635                         }
636                         else {
637                                 /* Either a semi terminated inline block or only the closing
638                                  * brace of some inner scope, not the block's closing brace. */
639                                 token( IL_Symbol, tokstart, tokend );
640                         }
641                 };
642
643                 EOF => {
644                         scan_error() << "unterminated code block" << endl;
645                 };
646
647                 # Send every other character as a symbol.
648                 any => { token( IL_Symbol, tokstart, tokend ); };
649         *|;
650
651
652         # An inline block of code for languages other than Ruby.
653         inline_code := |*
654                 # Inline expression keywords.
655                 "fpc" => { token( KW_PChar ); };
656                 "fc" => { token( KW_Char ); };
657                 "fcurs" => { token( KW_CurState ); };
658                 "ftargs" => { token( KW_TargState ); };
659                 "fentry" => { 
660                         whitespaceOn = false; 
661                         token( KW_Entry );
662                 };
663
664                 # Inline statement keywords.
665                 "fhold" => { 
666                         whitespaceOn = false; 
667                         token( KW_Hold );
668                 };
669                 "fexec" => { token( KW_Exec, 0, 0 ); };
670                 "fgoto" => { 
671                         whitespaceOn = false; 
672                         token( KW_Goto );
673                 };
674                 "fnext" => { 
675                         whitespaceOn = false; 
676                         token( KW_Next );
677                 };
678                 "fcall" => { 
679                         whitespaceOn = false; 
680                         token( KW_Call );
681                 };
682                 "fret" => { 
683                         whitespaceOn = false; 
684                         token( KW_Ret );
685                 };
686                 "fbreak" => { 
687                         whitespaceOn = false; 
688                         token( KW_Break );
689                 };
690
691                 ident => { token( TK_Word, tokstart, tokend ); };
692
693                 number => { token( TK_UInt, tokstart, tokend ); };
694                 hex_number => { token( TK_Hex, tokstart, tokend ); };
695
696                 ( s_literal | d_literal ) 
697                         => { token( IL_Literal, tokstart, tokend ); };
698
699                 whitespace+ => { 
700                         if ( whitespaceOn ) 
701                                 token( IL_WhiteSpace, tokstart, tokend );
702                 };
703
704                 c_cpp_comment => { token( IL_Comment, tokstart, tokend ); };
705
706                 "::" => { token( TK_NameSep, tokstart, tokend ); };
707
708                 # Some symbols need to go to the parser as with their cardinal value as
709                 # the token type (as opposed to being sent as anonymous symbols)
710                 # because they are part of the sequences which we interpret. The * ) ;
711                 # symbols cause whitespace parsing to come back on. This gets turned
712                 # off by some keywords.
713
714                 ";" => {
715                         whitespaceOn = true;
716                         token( *tokstart, tokstart, tokend );
717                         if ( inlineBlockType == SemiTerminated )
718                                 fret;
719                 };
720
721                 [*)] => { 
722                         whitespaceOn = true;
723                         token( *tokstart, tokstart, tokend );
724                 };
725
726                 [,(] => { token( *tokstart, tokstart, tokend ); };
727
728                 '{' => { 
729                         token( IL_Symbol, tokstart, tokend );
730                         curly_count += 1; 
731                 };
732
733                 '}' => { 
734                         if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
735                                 /* Inline code block ends. */
736                                 token( '}' );
737                                 fret;
738                         }
739                         else {
740                                 /* Either a semi terminated inline block or only the closing
741                                  * brace of some inner scope, not the block's closing brace. */
742                                 token( IL_Symbol, tokstart, tokend );
743                         }
744                 };
745
746                 EOF => {
747                         scan_error() << "unterminated code block" << endl;
748                 };
749
750                 # Send every other character as a symbol.
751                 any => { token( IL_Symbol, tokstart, tokend ); };
752         *|;
753
754         or_literal := |*
755                 # Escape sequences in OR expressions.
756                 '\\0' => { token( RE_Char, '\0' ); };
757                 '\\a' => { token( RE_Char, '\a' ); };
758                 '\\b' => { token( RE_Char, '\b' ); };
759                 '\\t' => { token( RE_Char, '\t' ); };
760                 '\\n' => { token( RE_Char, '\n' ); };
761                 '\\v' => { token( RE_Char, '\v' ); };
762                 '\\f' => { token( RE_Char, '\f' ); };
763                 '\\r' => { token( RE_Char, '\r' ); };
764                 '\\\n' => { updateCol(); };
765                 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
766
767                 # Range dash in an OR expression.
768                 '-' => { token( RE_Dash, 0, 0 ); };
769
770                 # Terminate an OR expression.
771                 ']'     => { token( RE_SqClose ); fret; };
772
773                 EOF => {
774                         scan_error() << "unterminated OR literal" << endl;
775                 };
776
777                 # Characters in an OR expression.
778                 [^\]] => { token( RE_Char, tokstart, tokend ); };
779
780         *|;
781
782         ragel_re_literal := |*
783                 # Escape sequences in regular expressions.
784                 '\\0' => { token( RE_Char, '\0' ); };
785                 '\\a' => { token( RE_Char, '\a' ); };
786                 '\\b' => { token( RE_Char, '\b' ); };
787                 '\\t' => { token( RE_Char, '\t' ); };
788                 '\\n' => { token( RE_Char, '\n' ); };
789                 '\\v' => { token( RE_Char, '\v' ); };
790                 '\\f' => { token( RE_Char, '\f' ); };
791                 '\\r' => { token( RE_Char, '\r' ); };
792                 '\\\n' => { updateCol(); };
793                 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
794
795                 # Terminate an OR expression.
796                 '/' [i]? => { 
797                         token( RE_Slash, tokstart, tokend ); 
798                         fgoto parser_def;
799                 };
800
801                 # Special characters.
802                 '.' => { token( RE_Dot ); };
803                 '*' => { token( RE_Star ); };
804
805                 '[' => { token( RE_SqOpen ); fcall or_literal; };
806                 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
807
808                 EOF => {
809                         scan_error() << "unterminated regular expression" << endl;
810                 };
811
812                 # Characters in an OR expression.
813                 [^\/] => { token( RE_Char, tokstart, tokend ); };
814         *|;
815
816         # We need a separate token space here to avoid the ragel keywords.
817         write_statement := |*
818                 ident => { token( TK_Word, tokstart, tokend ); } ;
819                 [ \t\n]+ => { updateCol(); };
820                 ';' => { token( ';' ); fgoto parser_def; };
821
822                 EOF => {
823                         scan_error() << "unterminated write statement" << endl;
824                 };
825         *|;
826
827         # Parser definitions. 
828         parser_def := |*
829                 'machine' => { token( KW_Machine ); };
830                 'include' => { token( KW_Include ); };
831                 'import' => { token( KW_Import ); };
832                 'write' => { 
833                         token( KW_Write );
834                         fgoto write_statement;
835                 };
836                 'action' => { token( KW_Action ); };
837                 'alphtype' => { token( KW_AlphType ); };
838
839                 # FIXME: Enable this post 5.17.
840                 # 'range' => { token( KW_Range ); };
841
842                 'getkey' => { 
843                         token( KW_GetKey );
844                         inlineBlockType = SemiTerminated;
845                         if ( hostLangType == RubyCode )
846                                 fcall inline_code_ruby;
847                         else
848                                 fcall inline_code;
849                 };
850                 'access' => { 
851                         token( KW_Access );
852                         inlineBlockType = SemiTerminated;
853                         if ( hostLangType == RubyCode )
854                                 fcall inline_code_ruby;
855                         else
856                                 fcall inline_code;
857                 };
858                 'variable' => { 
859                         token( KW_Variable );
860                         inlineBlockType = SemiTerminated;
861                         if ( hostLangType == RubyCode )
862                                 fcall inline_code_ruby;
863                         else
864                                 fcall inline_code;
865                 };
866                 'when' => { token( KW_When ); };
867                 'inwhen' => { token( KW_InWhen ); };
868                 'outwhen' => { token( KW_OutWhen ); };
869                 'eof' => { token( KW_Eof ); };
870                 'err' => { token( KW_Err ); };
871                 'lerr' => { token( KW_Lerr ); };
872                 'to' => { token( KW_To ); };
873                 'from' => { token( KW_From ); };
874                 'export' => { token( KW_Export ); };
875
876                 # Identifiers.
877                 ident => { token( TK_Word, tokstart, tokend ); } ;
878
879                 # Numbers
880                 number => { token( TK_UInt, tokstart, tokend ); };
881                 hex_number => { token( TK_Hex, tokstart, tokend ); };
882
883                 # Literals, with optionals.
884                 ( s_literal | d_literal ) [i]? 
885                         => { token( TK_Literal, tokstart, tokend ); };
886
887                 '[' => { token( RE_SqOpen ); fcall or_literal; };
888                 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
889
890                 '/' => { token( RE_Slash ); fgoto ragel_re_literal; };
891
892                 # Ignore.
893                 pound_comment => { updateCol(); };
894
895                 ':=' => { token( TK_ColonEquals ); };
896
897                 # To State Actions.
898                 ">~" => { token( TK_StartToState ); };
899                 "$~" => { token( TK_AllToState ); };
900                 "%~" => { token( TK_FinalToState ); };
901                 "<~" => { token( TK_NotStartToState ); };
902                 "@~" => { token( TK_NotFinalToState ); };
903                 "<>~" => { token( TK_MiddleToState ); };
904
905                 # From State actions
906                 ">*" => { token( TK_StartFromState ); };
907                 "$*" => { token( TK_AllFromState ); };
908                 "%*" => { token( TK_FinalFromState ); };
909                 "<*" => { token( TK_NotStartFromState ); };
910                 "@*" => { token( TK_NotFinalFromState ); };
911                 "<>*" => { token( TK_MiddleFromState ); };
912
913                 # EOF Actions.
914                 ">/" => { token( TK_StartEOF ); };
915                 "$/" => { token( TK_AllEOF ); };
916                 "%/" => { token( TK_FinalEOF ); };
917                 "</" => { token( TK_NotStartEOF ); };
918                 "@/" => { token( TK_NotFinalEOF ); };
919                 "<>/" => { token( TK_MiddleEOF ); };
920
921                 # Global Error actions.
922                 ">!" => { token( TK_StartGblError ); };
923                 "$!" => { token( TK_AllGblError ); };
924                 "%!" => { token( TK_FinalGblError ); };
925                 "<!" => { token( TK_NotStartGblError ); };
926                 "@!" => { token( TK_NotFinalGblError ); };
927                 "<>!" => { token( TK_MiddleGblError ); };
928
929                 # Local error actions.
930                 ">^" => { token( TK_StartLocalError ); };
931                 "$^" => { token( TK_AllLocalError ); };
932                 "%^" => { token( TK_FinalLocalError ); };
933                 "<^" => { token( TK_NotStartLocalError ); };
934                 "@^" => { token( TK_NotFinalLocalError ); };
935                 "<>^" => { token( TK_MiddleLocalError ); };
936
937                 # Middle.
938                 "<>" => { token( TK_Middle ); };
939
940                 # Conditions. 
941                 '>?' => { token( TK_StartCond ); };
942                 '$?' => { token( TK_AllCond ); };
943                 '%?' => { token( TK_LeavingCond ); };
944
945                 '..' => { token( TK_DotDot ); };
946                 '**' => { token( TK_StarStar ); };
947                 '--' => { token( TK_DashDash ); };
948                 '->' => { token( TK_Arrow ); };
949                 '=>' => { token( TK_DoubleArrow ); };
950
951                 ":>"  => { token( TK_ColonGt ); };
952                 ":>>" => { token( TK_ColonGtGt ); };
953                 "<:"  => { token( TK_LtColon ); };
954
955                 # Opening of longest match.
956                 "|*" => { token( TK_BarStar ); };
957
958                 # Separater for name references.
959                 "::" => { token( TK_NameSep, tokstart, tokend ); };
960
961                 '}%%' => { 
962                         updateCol();
963                         endSection();
964                         fret;
965                 };
966
967                 [ \t\r]+ => { updateCol(); };
968
969                 # If we are in a single line machine then newline may end the spec.
970                 NL => {
971                         updateCol();
972                         if ( singleLineSpec ) {
973                                 endSection();
974                                 fret;
975                         }
976                 };
977
978                 '{' => { 
979                         if ( lastToken == KW_Export || lastToken == KW_Entry )
980                                 token( '{' );
981                         else {
982                                 token( '{' );
983                                 curly_count = 1; 
984                                 inlineBlockType = CurlyDelimited;
985                                 if ( hostLangType == RubyCode )
986                                         fcall inline_code_ruby;
987                                 else
988                                         fcall inline_code;
989                         }
990                 };
991
992                 EOF => {
993                         scan_error() << "unterminated ragel section" << endl;
994                 };
995
996                 any => { token( *tokstart ); } ;
997         *|;
998
999         # Outside code scanner. These tokens get passed through.
1000         main_ruby := |*
1001                 ident => { pass( IMP_Word, tokstart, tokend ); };
1002                 number => { pass( IMP_UInt, tokstart, tokend ); };
1003                 ruby_comment => { pass(); };
1004                 ( s_literal | d_literal | host_re_literal ) 
1005                         => { pass( IMP_Literal, tokstart, tokend ); };
1006
1007                 '%%{' => { 
1008                         updateCol();
1009                         singleLineSpec = false;
1010                         startSection();
1011                         fcall parser_def;
1012                 };
1013                 '%%' => { 
1014                         updateCol();
1015                         singleLineSpec = true;
1016                         startSection();
1017                         fcall parser_def;
1018                 };
1019                 whitespace+ => { pass(); };
1020                 EOF;
1021                 any => { pass( *tokstart, 0, 0 ); };
1022         *|;
1023
1024         # Outside code scanner. These tokens get passed through.
1025         main := |*
1026                 'define' => { pass( IMP_Define, 0, 0 ); };
1027                 ident => { pass( IMP_Word, tokstart, tokend ); };
1028                 number => { pass( IMP_UInt, tokstart, tokend ); };
1029                 c_cpp_comment => { pass(); };
1030                 ( s_literal | d_literal ) => { pass( IMP_Literal, tokstart, tokend ); };
1031
1032                 '%%{' => { 
1033                         updateCol();
1034                         singleLineSpec = false;
1035                         startSection();
1036                         fcall parser_def;
1037                 };
1038                 '%%' => { 
1039                         updateCol();
1040                         singleLineSpec = true;
1041                         startSection();
1042                         fcall parser_def;
1043                 };
1044                 whitespace+ => { pass(); };
1045                 EOF;
1046                 any => { pass( *tokstart, 0, 0 ); };
1047         *|;
1048 }%%
1049
1050 %% write data;
1051
1052 void Scanner::do_scan()
1053 {
1054         int bufsize = 8;
1055         char *buf = new char[bufsize];
1056         const char last_char = 0;
1057         int cs, act, have = 0;
1058         int top;
1059
1060         /* The stack is two deep, one level for going into ragel defs from the main
1061          * machines which process outside code, and another for going into or literals
1062          * from either a ragel spec, or a regular expression. */
1063         int stack[2];
1064         int curly_count = 0;
1065         bool execute = true;
1066         bool singleLineSpec = false;
1067         InlineBlockType inlineBlockType = CurlyDelimited;
1068
1069         /* Init the section parser and the character scanner. */
1070         init();
1071         %% write init;
1072
1073         /* Set up the start state. FIXME: After 5.20 is released the nocs write
1074          * init option should be used, the main machine eliminated and this statement moved
1075          * above the write init. */
1076         if ( hostLangType == RubyCode )
1077                 cs = rlscan_en_main_ruby;
1078         else
1079                 cs = rlscan_en_main;
1080         
1081         while ( execute ) {
1082                 char *p = buf + have;
1083                 int space = bufsize - have;
1084
1085                 if ( space == 0 ) {
1086                         /* We filled up the buffer trying to scan a token. Grow it. */
1087                         bufsize = bufsize * 2;
1088                         char *newbuf = new char[bufsize];
1089
1090                         /* Recompute p and space. */
1091                         p = newbuf + have;
1092                         space = bufsize - have;
1093
1094                         /* Patch up pointers possibly in use. */
1095                         if ( tokstart != 0 )
1096                                 tokstart = newbuf + ( tokstart - buf );
1097                         tokend = newbuf + ( tokend - buf );
1098
1099                         /* Copy the new buffer in. */
1100                         memcpy( newbuf, buf, have );
1101                         delete[] buf;
1102                         buf = newbuf;
1103                 }
1104
1105                 input.read( p, space );
1106                 int len = input.gcount();
1107
1108                 /* If we see eof then append the EOF char. */
1109                 if ( len == 0 ) {
1110                         p[0] = last_char, len = 1;
1111                         execute = false;
1112                 }
1113
1114                 char *pe = p + len;
1115                 %% write exec;
1116
1117                 /* Check if we failed. */
1118                 if ( cs == rlscan_error ) {
1119                         /* Machine failed before finding a token. I'm not yet sure if this
1120                          * is reachable. */
1121                         scan_error() << "scanner error" << endl;
1122                         exit(1);
1123                 }
1124
1125                 /* Decide if we need to preserve anything. */
1126                 char *preserve = tokstart;
1127
1128                 /* Now set up the prefix. */
1129                 if ( preserve == 0 )
1130                         have = 0;
1131                 else {
1132                         /* There is data that needs to be shifted over. */
1133                         have = pe - preserve;
1134                         memmove( buf, preserve, have );
1135                         unsigned int shiftback = preserve - buf;
1136                         if ( tokstart != 0 )
1137                                 tokstart -= shiftback;
1138                         tokend -= shiftback;
1139
1140                         preserve = buf;
1141                 }
1142         }
1143
1144         delete[] buf;
1145 }