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