2 * Copyright 2006 Adrian Thurston <thurston@cs.queensu.ca>
5 /* This file is part of Ragel.
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.
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.
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
28 #include "parsedata.h"
40 /* This is used for tracking the current stack of include file/machine pairs. It is
41 * is used to detect and recursive include structure. */
42 struct IncludeStackItem
44 IncludeStackItem( char *fileName, char *sectionName )
45 : fileName(fileName), sectionName(sectionName) {}
51 typedef Vector<IncludeStackItem> IncludeStack;
61 Scanner( char *fileName, istream &input, ostream &output,
62 Parser *inclToParser, char *inclSectionTarg,
65 fileName(fileName), input(input), output(output),
66 inclToParser(inclToParser),
67 inclSectionTarg(inclSectionTarg),
68 includeDepth(includeDepth),
69 line(1), column(1), lastnl(0),
70 parser(0), active(false),
71 parserExistsError(false),
75 bool recursiveInclude( char *inclFileName, char *inclSectionName );
77 char *prepareFileName( char *fileName, int len )
80 Token tokenFnStr, tokenRes;
81 tokenFnStr.data = fileName;
82 tokenFnStr.length = len;
83 tokenFnStr.prepareLitString( tokenRes, caseInsensitive );
88 void token( int type, char *start, char *end );
89 void token( int type, char c );
90 void token( int type );
96 ostream &scan_error();
101 Parser *inclToParser;
102 char *inclSectionTarg;
108 int word_len, lit_len;
110 char *tokstart, *tokend;
114 /* Set by machine statements, these persist from section to section
115 * allowing for unnamed sections. */
118 IncludeStack includeStack;
120 /* This is set if ragel has already emitted an error stating that
121 * no section name has been seen and thus no parser exists. */
122 bool parserExistsError;
124 /* This is for inline code. By default it is on. It goes off for
125 * statements and values in inline blocks which are parsed. */
130 machine section_parse;
135 void Scanner::init( )
140 bool Scanner::parserExists()
145 if ( ! parserExistsError ) {
146 scan_error() << "include: there is no previous specification name" << endl;
147 parserExistsError = true;
152 ostream &Scanner::scan_error()
154 /* Maintain the error count. */
156 cerr << fileName << ":" << line << ":" << column << ": ";
160 bool Scanner::recursiveInclude( char *inclFileName, char *inclSectionName )
162 for ( IncludeStack::Iter si = includeStack; si.lte(); si++ ) {
163 if ( strcmp( si->fileName, inclFileName ) == 0 &&
164 strcmp( si->sectionName, inclSectionName ) == 0 )
172 void Scanner::updateCol()
177 //cerr << "adding " << tokend - from << " to column" << endl;
178 column += tokend - from;
182 void Scanner::token( int type, char c )
184 token( type, &c, &c + 1 );
187 void Scanner::token( int type )
193 machine section_parse;
195 # This relies on the the kelbt implementation and the order
196 # that tokens are declared.
203 action clear_words { word = lit = 0; word_len = lit_len = 0; }
204 action store_word { word = tokdata; word_len = toklen; }
205 action store_lit { lit = tokdata; lit_len = toklen; }
207 action mach_err { scan_error() << "bad machine statement" << endl; }
208 action incl_err { scan_error() << "bad include statement" << endl; }
209 action write_err { scan_error() << "bad write statement" << endl; }
211 action handle_machine
213 /* Assign a name to the machine. */
214 char *machine = word;
216 if ( inclSectionTarg == 0 ) {
219 ParserDictEl *pdEl = parserDict.find( machine );
221 pdEl = new ParserDictEl( machine );
222 pdEl->value = new Parser( fileName, machine, sectionLoc );
224 parserDict.insert( pdEl );
227 parser = pdEl->value;
229 else if ( strcmp( inclSectionTarg, machine ) == 0 ) {
230 /* found include target */
232 parser = inclToParser;
235 /* ignoring section */
242 ( KW_Machine TK_Word @store_word ';' ) @handle_machine
243 <>err mach_err <>eof mach_err;
245 action handle_include
247 if ( active && parserExists() ) {
248 char *inclSectionName = word;
249 char *inclFileName = 0;
251 /* Implement defaults for the input file and section name. */
252 if ( inclSectionName == 0 )
253 inclSectionName = parser->sectionName;
256 inclFileName = prepareFileName( lit, lit_len );
258 inclFileName = fileName;
260 /* Check for a recursive include structure. Add the current file/section
261 * name then check if what we are including is already in the stack. */
262 includeStack.append( IncludeStackItem( fileName, parser->sectionName ) );
264 if ( recursiveInclude( inclFileName, inclSectionName ) )
265 scan_error() << "include: this is a recursive include operation" << endl;
267 /* Open the input file for reading. */
268 ifstream *inFile = new ifstream( inclFileName );
269 if ( ! inFile->is_open() ) {
270 scan_error() << "include: could not open " <<
271 inclFileName << " for reading" << endl;
274 Scanner scanner( inclFileName, *inFile, output, parser,
275 inclSectionName, includeDepth+1 );
281 /* Remove the last element (len-1) */
282 includeStack.remove( -1 );
287 TK_Word @store_word ( TK_Literal @store_lit )? |
288 TK_Literal @store_lit
292 ( KW_Include include_names ';' ) @handle_include
293 <>err incl_err <>eof incl_err;
298 if ( strcmp( tokdata, "data" ) != 0 &&
299 strcmp( tokdata, "init" ) != 0 &&
300 strcmp( tokdata, "exec" ) != 0 &&
301 strcmp( tokdata, "eof" ) != 0 )
303 scan_error() << "unknown write command" << endl;
306 if ( machineSpec == 0 && machineName == 0 ) {
307 output << "<write def_name=\"" << parser->sectionName <<
308 "\" what=\"" << tokdata << "\">";
316 output << "<option>" << tokdata << "</option>";
320 if ( active && machineSpec == 0 && machineName == 0 )
321 output << "</write>\n";
325 ( KW_Write TK_Word @write_command
326 ( TK_Word @write_option )* ';' @write_close )
327 <>err write_err <>eof write_err;
331 /* Send the token off to the parser. */
332 if ( active && parserExists() ) {
336 cerr << "scanner:" << line << ":" << column <<
337 ": sending token to the parser " << lelNames[*p];
338 cerr << " " << toklen;
340 cerr << " " << tokdata;
344 loc.fileName = fileName;
348 parser->token( loc, type, tokdata, toklen );
352 # Catch everything else.
353 everything_else = ^( KW_Machine | KW_Include | KW_Write ) @handle_token;
363 void Scanner::token( int type, char *start, char *end )
372 tokdata = new char[toklen+1];
373 memcpy( tokdata, start, toklen );
378 machine section_parse;
385 void Scanner::startSection( )
387 parserExistsError = false;
389 if ( includeDepth == 0 ) {
390 if ( machineSpec == 0 && machineName == 0 )
391 output << "</host>\n";
394 sectionLoc.fileName = fileName;
395 sectionLoc.line = line;
399 void Scanner::endSection( )
401 /* Execute the eof actions for the section parser. */
403 machine section_parse;
407 /* Close off the section with the parser. */
408 if ( active && parserExists() ) {
410 loc.fileName = fileName;
414 parser->token( loc, TK_EndSection, 0, 0 );
417 if ( includeDepth == 0 ) {
418 if ( machineSpec == 0 && machineName == 0 ) {
419 /* The end section may include a newline on the end, so
420 * we use the last line, which will count the newline. */
421 output << "<host line=\"" << line << "\">";
429 # This is sent by the driver code.
439 # Identifiers, numbers, commetns, and other common things.
440 ident = ( alpha | '_' ) ( alpha |digit |'_' )*;
442 hex_number = '0x' [0-9a-fA-F]+;
445 '/*' ( any | NL )* :>> '*/';
450 c_cpp_comment = c_comment | cpp_comment;
452 # These literal forms are common to C-like host code and ragel.
453 s_literal = "'" ([^'\\] | NL | '\\' (any | NL))* "'";
454 d_literal = '"' ([^"\\] | NL | '\\' (any | NL))* '"';
456 whitespace = [ \t] | NL;
457 pound_comment = '#' [^\n]* NL;
459 # An inline block of code. This is specified as a scanned, but is sent to
460 # the parser as one long block. The inline_block pointer is used to handle
461 # the preservation of the data.
463 # Inline expression keywords.
464 "fpc" => { token( KW_PChar ); };
465 "fc" => { token( KW_Char ); };
466 "fcurs" => { token( KW_CurState ); };
467 "ftargs" => { token( KW_TargState ); };
469 whitespaceOn = false;
473 # Inline statement keywords.
475 whitespaceOn = false;
478 "fexec" => { token( KW_Exec, 0, 0 ); };
480 whitespaceOn = false;
484 whitespaceOn = false;
488 whitespaceOn = false;
492 whitespaceOn = false;
496 whitespaceOn = false;
500 ident => { token( TK_Word, tokstart, tokend ); };
502 number => { token( TK_UInt, tokstart, tokend ); };
503 hex_number => { token( TK_Hex, tokstart, tokend ); };
505 ( s_literal | d_literal )
506 => { token( IL_Literal, tokstart, tokend ); };
510 token( IL_WhiteSpace, tokstart, tokend );
512 c_cpp_comment => { token( IL_Comment, tokstart, tokend ); };
514 "::" => { token( TK_NameSep, tokstart, tokend ); };
516 # Some symbols need to go to the parser as with their cardinal value as
517 # the token type (as opposed to being sent as anonymous symbols)
518 # because they are part of the sequences which we interpret. The * ) ;
519 # symbols cause whitespace parsing to come back on. This gets turned
520 # off by some keywords.
524 token( *tokstart, tokstart, tokend );
525 if ( inlineBlockType == SemiTerminated )
531 token( *tokstart, tokstart, tokend );
534 [,(] => { token( *tokstart, tokstart, tokend ); };
537 token( IL_Symbol, tokstart, tokend );
542 if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
543 /* Inline code block ends. */
548 /* Either a semi terminated inline block or only the closing
549 * brace of some inner scope, not the block's closing brace. */
550 token( IL_Symbol, tokstart, tokend );
555 scan_error() << "unterminated code block" << endl;
558 # Send every other character as a symbol.
559 any => { token( IL_Symbol, tokstart, tokend ); };
563 # Escape sequences in OR expressions.
564 '\\0' => { token( RE_Char, '\0' ); };
565 '\\a' => { token( RE_Char, '\a' ); };
566 '\\b' => { token( RE_Char, '\b' ); };
567 '\\t' => { token( RE_Char, '\t' ); };
568 '\\n' => { token( RE_Char, '\n' ); };
569 '\\v' => { token( RE_Char, '\v' ); };
570 '\\f' => { token( RE_Char, '\f' ); };
571 '\\r' => { token( RE_Char, '\r' ); };
572 '\\\n' => { updateCol(); };
573 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
575 # Range dash in an OR expression.
576 '-' => { token( RE_Dash, 0, 0 ); };
578 # Terminate an OR expression.
579 ']' => { token( RE_SqClose ); fret; };
582 scan_error() << "unterminated OR literal" << endl;
585 # Characters in an OR expression.
586 [^\]] => { token( RE_Char, tokstart, tokend ); };
591 # Escape sequences in regular expressions.
592 '\\0' => { token( RE_Char, '\0' ); };
593 '\\a' => { token( RE_Char, '\a' ); };
594 '\\b' => { token( RE_Char, '\b' ); };
595 '\\t' => { token( RE_Char, '\t' ); };
596 '\\n' => { token( RE_Char, '\n' ); };
597 '\\v' => { token( RE_Char, '\v' ); };
598 '\\f' => { token( RE_Char, '\f' ); };
599 '\\r' => { token( RE_Char, '\r' ); };
600 '\\\n' => { updateCol(); };
601 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
603 # Terminate an OR expression.
605 token( RE_Slash, tokstart, tokend );
609 # Special characters.
610 '.' => { token( RE_Dot ); };
611 '*' => { token( RE_Star ); };
613 '[' => { token( RE_SqOpen ); fcall or_literal; };
614 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
617 scan_error() << "unterminated regular expression" << endl;
620 # Characters in an OR expression.
621 [^\/] => { token( RE_Char, tokstart, tokend ); };
624 # We need a separate token space here to avoid the ragel keywords.
625 write_statement := |*
626 ident => { token( TK_Word, tokstart, tokend ); } ;
627 [ \t\n]+ => { updateCol(); };
628 ';' => { token( ';' ); fgoto parser_def; };
631 scan_error() << "unterminated write statement" << endl;
635 # Parser definitions.
637 'machine' => { token( KW_Machine ); };
638 'include' => { token( KW_Include ); };
641 fgoto write_statement;
643 'action' => { token( KW_Action ); };
644 'alphtype' => { token( KW_AlphType ); };
646 # FIXME: Enable this post 5.17.
647 # 'range' => { token( KW_Range ); };
651 inlineBlockType = SemiTerminated;
656 inlineBlockType = SemiTerminated;
660 token( KW_Variable );
661 inlineBlockType = SemiTerminated;
664 'when' => { token( KW_When ); };
665 'eof' => { token( KW_Eof ); };
666 'err' => { token( KW_Err ); };
667 'lerr' => { token( KW_Lerr ); };
668 'to' => { token( KW_To ); };
669 'from' => { token( KW_From ); };
672 ident => { token( TK_Word, tokstart, tokend ); } ;
675 number => { token( TK_UInt, tokstart, tokend ); };
676 hex_number => { token( TK_Hex, tokstart, tokend ); };
678 # Literals, with optionals.
679 ( s_literal | d_literal ) [i]?
680 => { token( TK_Literal, tokstart, tokend ); };
682 '[' => { token( RE_SqOpen ); fcall or_literal; };
683 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
685 '/' => { token( RE_Slash ); fgoto re_literal; };
688 pound_comment => { updateCol(); };
690 ':=' => { token( TK_ColonEquals ); };
693 ">~" => { token( TK_StartToState ); };
694 "$~" => { token( TK_AllToState ); };
695 "%~" => { token( TK_FinalToState ); };
696 "<~" => { token( TK_NotStartToState ); };
697 "@~" => { token( TK_NotFinalToState ); };
698 "<>~" => { token( TK_MiddleToState ); };
701 ">*" => { token( TK_StartFromState ); };
702 "$*" => { token( TK_AllFromState ); };
703 "%*" => { token( TK_FinalFromState ); };
704 "<*" => { token( TK_NotStartFromState ); };
705 "@*" => { token( TK_NotFinalFromState ); };
706 "<>*" => { token( TK_MiddleFromState ); };
709 ">/" => { token( TK_StartEOF ); };
710 "$/" => { token( TK_AllEOF ); };
711 "%/" => { token( TK_FinalEOF ); };
712 "</" => { token( TK_NotStartEOF ); };
713 "@/" => { token( TK_NotFinalEOF ); };
714 "<>/" => { token( TK_MiddleEOF ); };
716 # Global Error actions.
717 ">!" => { token( TK_StartGblError ); };
718 "$!" => { token( TK_AllGblError ); };
719 "%!" => { token( TK_FinalGblError ); };
720 "<!" => { token( TK_NotStartGblError ); };
721 "@!" => { token( TK_NotFinalGblError ); };
722 "<>!" => { token( TK_MiddleGblError ); };
724 # Local error actions.
725 ">^" => { token( TK_StartLocalError ); };
726 "$^" => { token( TK_AllLocalError ); };
727 "%^" => { token( TK_FinalLocalError ); };
728 "<^" => { token( TK_NotStartLocalError ); };
729 "@^" => { token( TK_NotFinalLocalError ); };
730 "<>^" => { token( TK_MiddleLocalError ); };
733 "<>" => { token( TK_Middle ); };
736 '>?' => { token( TK_StartCond ); };
737 '$?' => { token( TK_AllCond ); };
738 '%?' => { token( TK_LeavingCond ); };
740 '..' => { token( TK_DotDot ); };
741 '**' => { token( TK_StarStar ); };
742 '--' => { token( TK_DashDash ); };
743 '->' => { token( TK_Arrow ); };
744 '=>' => { token( TK_DoubleArrow ); };
746 ":>" => { token( TK_ColonGt ); };
747 ":>>" => { token( TK_ColonGtGt ); };
748 "<:" => { token( TK_LtColon ); };
750 # Opening of longest match.
751 "|*" => { token( TK_BarStar ); };
759 [ \t]+ => { updateCol(); };
761 # If we are in a single line machine then newline may end the spec.
764 if ( singleLineSpec ) {
773 inlineBlockType = CurlyDelimited;
778 scan_error() << "unterminated ragel section" << endl;
781 any => { token( *tokstart ); } ;
787 /* If no errors and we are at the bottom of the include stack (the
788 * source file listed on the command line) then write out the data. */
789 if ( includeDepth == 0 && machineSpec == 0 && machineName == 0 )
790 xmlEscapeHost( output, tokstart, tokend-tokstart );
793 # Outside code scanner. These tokens get passed through.
797 c_cpp_comment => pass;
798 s_literal | d_literal => pass;
801 singleLineSpec = false;
807 singleLineSpec = true;
820 void Scanner::do_scan()
823 char *buf = new char[bufsize];
824 const char last_char = 0;
825 int cs, act, have = 0;
829 bool singleLineSpec = false;
830 InlineBlockType inlineBlockType;
835 char *p = buf + have;
836 int space = bufsize - have;
839 /* We filled up the buffer trying to scan a token. Grow it. */
840 bufsize = bufsize * 2;
841 char *newbuf = new char[bufsize];
843 /* Recompute p and space. */
845 space = bufsize - have;
847 /* Patch up pointers possibly in use. */
849 tokstart = newbuf + ( tokstart - buf );
850 tokend = newbuf + ( tokend - buf );
852 /* Copy the new buffer in. */
853 memcpy( newbuf, buf, have );
858 input.read( p, space );
859 int len = input.gcount();
861 /* If we see eof then append the EOF char. */
863 p[0] = last_char, len = 1;
870 /* Check if we failed. */
871 if ( cs == rlscan_error ) {
872 /* Machine failed before finding a token. I'm not yet sure if this
874 scan_error() << "scanner error" << endl;
878 /* Decide if we need to preserve anything. */
879 char *preserve = tokstart;
881 /* Now set up the prefix. */
885 /* There is data that needs to be shifted over. */
886 have = pe - preserve;
887 memmove( buf, preserve, have );
888 unsigned int shiftback = preserve - buf;
890 tokstart -= shiftback;
900 void scan( char *fileName, istream &input, ostream &output )
902 Scanner scanner( fileName, input, output, 0, 0, 0 );
907 eofLoc.fileName = fileName;
909 eofLoc.line = scanner.line;