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