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