2 * Copyright 2006-2007 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
37 * The Scanner for Importing
41 #define IMP_Literal 129
42 #define IMP_Number 130
43 #define IMP_Define 131
46 machine inline_token_scan;
56 IMP_Define IMP_Word IMP_Number => { cerr << ( "define" ) << endl; };
57 IMP_Word '=' IMP_Number => { cerr << ( "const1" ) << endl; };
58 IMP_Word '=' IMP_Literal => { cerr << ( "const2" ) << endl; };
65 void ImportScanner::token( int token, char *start, char *end )
67 if ( cur_token == max_tokens ) {
69 int *pe = token_data + cur_token;
74 if ( tok_tokstart == 0 )
77 cerr << "BLOCK BREAK" << endl;
78 cur_token = pe - tok_tokstart;
79 memmove( token_data, tok_tokstart, cur_token*sizeof(int) );
83 token_data[cur_token++] = token;
90 # This is sent by the driver code.
94 # Identifiers, numbers, commetns, and other common things.
95 ident = ( alpha | '_' ) ( alpha |digit |'_' )*;
97 hex_number = '0x' [0-9a-fA-F]+;
100 '/*' ( any | NL )* :>> '*/';
105 c_cpp_comment = c_comment | cpp_comment;
107 # These literal forms are common to C-like host code and ragel.
108 s_literal = "'" ([^'\\] | NL | '\\' (any | NL))* "'";
109 d_literal = '"' ([^"\\] | NL | '\\' (any | NL))* '"';
111 whitespace = [ \t] | NL;
114 # Outside code scanner. These tokens get passed through.
116 'define' => { token( IMP_Define, 0, 0 ); };
117 ident => { token( IMP_Word, chr_tokstart, chr_tokend ); };
118 number => { token( IMP_Number, chr_tokstart, chr_tokend ); };
120 s_literal | d_literal => { token( IMP_Literal, chr_tokstart, chr_tokend ); };
123 any => { token( *chr_tokstart, 0, 0 ); };
129 void ImportScanner::do_scan()
132 char *buf = new char[bufsize];
133 const char last_char = 0;
134 int chr_cs, chr_act, have = 0;
137 /* Init the section parser and the character scanner. */
141 char *p = buf + have;
142 int space = bufsize - have;
145 /* We filled up the buffer trying to scan a token. Grow it. */
146 bufsize = bufsize * 2;
147 char *newbuf = new char[bufsize];
149 /* Recompute p and space. */
151 space = bufsize - have;
153 /* Patch up pointers possibly in use. */
154 if ( chr_tokstart != 0 )
155 chr_tokstart = newbuf + ( chr_tokstart - buf );
156 chr_tokend = newbuf + ( chr_tokend - buf );
158 /* Copy the new buffer in. */
159 memcpy( newbuf, buf, have );
164 input.read( p, space );
165 int len = input.gcount();
167 /* If we see eof then append the EOF char. */
169 p[0] = last_char, len = 1;
176 /* Check if we failed. */
177 if ( chr_cs == inline_scan_error ) {
178 /* Machine failed before finding a token. I'm not yet sure if this
180 scan_error() << "scanner error" << endl;
184 /* Decide if we need to preserve anything. */
185 char *preserve = chr_tokstart;
187 /* Now set up the prefix. */
191 /* There is data that needs to be shifted over. */
192 have = pe - preserve;
193 memmove( buf, preserve, have );
194 unsigned int shiftback = preserve - buf;
195 if ( chr_tokstart != 0 )
196 chr_tokstart -= shiftback;
197 chr_tokend -= shiftback;
206 ostream &ImportScanner::scan_error()
208 /* Maintain the error count. */
210 cerr << fileName << ":" << line << ":" << column << ": ";
226 machine section_parse;
232 void Scanner::init( )
237 bool Scanner::active()
242 if ( parser == 0 && ! parserExistsError ) {
243 scan_error() << "there is no previous specification name" << endl;
244 parserExistsError = true;
253 ostream &Scanner::scan_error()
255 /* Maintain the error count. */
257 cerr << fileName << ":" << line << ":" << column << ": ";
261 bool Scanner::recursiveInclude( char *inclFileName, char *inclSectionName )
263 for ( IncludeStack::Iter si = includeStack; si.lte(); si++ ) {
264 if ( strcmp( si->fileName, inclFileName ) == 0 &&
265 strcmp( si->sectionName, inclSectionName ) == 0 )
273 void Scanner::updateCol()
278 //cerr << "adding " << tokend - from << " to column" << endl;
279 column += tokend - from;
283 void Scanner::token( int type, char c )
285 token( type, &c, &c + 1 );
288 void Scanner::token( int type )
294 machine section_parse;
296 # This relies on the the kelbt implementation and the order
297 # that tokens are declared.
304 action clear_words { word = lit = 0; word_len = lit_len = 0; }
305 action store_word { word = tokdata; word_len = toklen; }
306 action store_lit { lit = tokdata; lit_len = toklen; }
308 action mach_err { scan_error() << "bad machine statement" << endl; }
309 action incl_err { scan_error() << "bad include statement" << endl; }
310 action write_err { scan_error() << "bad write statement" << endl; }
312 action handle_machine
314 /* Assign a name to the machine. */
315 char *machine = word;
317 if ( inclSectionTarg == 0 ) {
318 ignoreSection = false;
320 ParserDictEl *pdEl = parserDict.find( machine );
322 pdEl = new ParserDictEl( machine );
323 pdEl->value = new Parser( fileName, machine, sectionLoc );
325 parserDict.insert( pdEl );
328 parser = pdEl->value;
330 else if ( strcmp( inclSectionTarg, machine ) == 0 ) {
331 /* found include target */
332 ignoreSection = false;
333 parser = inclToParser;
336 /* ignoring section */
337 ignoreSection = true;
343 ( KW_Machine TK_Word @store_word ';' ) @handle_machine
344 <>err mach_err <>eof mach_err;
346 action handle_include
349 char *inclSectionName = word;
350 char *inclFileName = 0;
352 /* Implement defaults for the input file and section name. */
353 if ( inclSectionName == 0 )
354 inclSectionName = parser->sectionName;
357 inclFileName = prepareFileName( lit, lit_len );
359 inclFileName = fileName;
361 /* Check for a recursive include structure. Add the current file/section
362 * name then check if what we are including is already in the stack. */
363 includeStack.append( IncludeStackItem( fileName, parser->sectionName ) );
365 if ( recursiveInclude( inclFileName, inclSectionName ) )
366 scan_error() << "include: this is a recursive include operation" << endl;
368 /* Open the input file for reading. */
369 ifstream *inFile = new ifstream( inclFileName );
370 if ( ! inFile->is_open() ) {
371 scan_error() << "include: could not open " <<
372 inclFileName << " for reading" << endl;
375 Scanner scanner( inclFileName, *inFile, output, parser,
376 inclSectionName, includeDepth+1 );
381 /* Remove the last element (len-1) */
382 includeStack.remove( -1 );
387 TK_Word @store_word ( TK_Literal @store_lit )? |
388 TK_Literal @store_lit
392 ( KW_Include include_names ';' ) @handle_include
393 <>err incl_err <>eof incl_err;
397 if ( active() && machineSpec == 0 && machineName == 0 ) {
399 " def_name=\"" << parser->sectionName << "\""
400 " line=\"" << line << "\""
401 " col=\"" << column << "\""
408 if ( active() && machineSpec == 0 && machineName == 0 )
409 output << "<arg>" << tokdata << "</arg>";
414 if ( active() && machineSpec == 0 && machineName == 0 )
415 output << "</write>\n";
419 ( KW_Write @write_command
420 ( TK_Word @write_arg )+ ';' @write_close )
421 <>err write_err <>eof write_err;
425 /* Send the token off to the parser. */
430 cerr << "scanner:" << line << ":" << column <<
431 ": sending token to the parser " << Parser_lelNames[*p];
432 cerr << " " << toklen;
434 cerr << " " << tokdata;
438 loc.fileName = fileName;
442 parser->token( loc, type, tokdata, toklen );
446 # Catch everything else.
447 everything_else = ^( KW_Machine | KW_Include | KW_Write ) @handle_token;
457 void Scanner::token( int type, char *start, char *end )
466 tokdata = new char[toklen+1];
467 memcpy( tokdata, start, toklen );
472 machine section_parse;
478 /* Record the last token for use in controlling the scan of subsequent
483 void Scanner::startSection( )
485 parserExistsError = false;
487 if ( includeDepth == 0 ) {
488 if ( machineSpec == 0 && machineName == 0 )
489 output << "</host>\n";
492 sectionLoc.fileName = fileName;
493 sectionLoc.line = line;
497 void Scanner::endSection( )
499 /* Execute the eof actions for the section parser. */
501 machine section_parse;
505 /* Close off the section with the parser. */
508 loc.fileName = fileName;
512 parser->token( loc, TK_EndSection, 0, 0 );
515 if ( includeDepth == 0 ) {
516 if ( machineSpec == 0 && machineName == 0 ) {
517 /* The end section may include a newline on the end, so
518 * we use the last line, which will count the newline. */
519 output << "<host line=\"" << line << "\">";
527 # This is sent by the driver code.
537 # Identifiers, numbers, commetns, and other common things.
538 ident = ( alpha | '_' ) ( alpha |digit |'_' )*;
540 hex_number = '0x' [0-9a-fA-F]+;
543 '/*' ( any | NL )* :>> '*/';
548 c_cpp_comment = c_comment | cpp_comment;
550 # These literal forms are common to C-like host code and ragel.
551 s_literal = "'" ([^'\\] | NL | '\\' (any | NL))* "'";
552 d_literal = '"' ([^"\\] | NL | '\\' (any | NL))* '"';
554 whitespace = [ \t] | NL;
555 pound_comment = '#' [^\n]* NL;
557 # An inline block of code. This is specified as a scanned, but is sent to
558 # the parser as one long block. The inline_block pointer is used to handle
559 # the preservation of the data.
561 # Inline expression keywords.
562 "fpc" => { token( KW_PChar ); };
563 "fc" => { token( KW_Char ); };
564 "fcurs" => { token( KW_CurState ); };
565 "ftargs" => { token( KW_TargState ); };
567 whitespaceOn = false;
571 # Inline statement keywords.
573 whitespaceOn = false;
576 "fexec" => { token( KW_Exec, 0, 0 ); };
578 whitespaceOn = false;
582 whitespaceOn = false;
586 whitespaceOn = false;
590 whitespaceOn = false;
594 whitespaceOn = false;
598 ident => { token( TK_Word, tokstart, tokend ); };
600 number => { token( TK_UInt, tokstart, tokend ); };
601 hex_number => { token( TK_Hex, tokstart, tokend ); };
603 ( s_literal | d_literal )
604 => { token( IL_Literal, tokstart, tokend ); };
608 token( IL_WhiteSpace, tokstart, tokend );
610 c_cpp_comment => { token( IL_Comment, tokstart, tokend ); };
612 "::" => { token( TK_NameSep, tokstart, tokend ); };
614 # Some symbols need to go to the parser as with their cardinal value as
615 # the token type (as opposed to being sent as anonymous symbols)
616 # because they are part of the sequences which we interpret. The * ) ;
617 # symbols cause whitespace parsing to come back on. This gets turned
618 # off by some keywords.
622 token( *tokstart, tokstart, tokend );
623 if ( inlineBlockType == SemiTerminated )
629 token( *tokstart, tokstart, tokend );
632 [,(] => { token( *tokstart, tokstart, tokend ); };
635 token( IL_Symbol, tokstart, tokend );
640 if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
641 /* Inline code block ends. */
646 /* Either a semi terminated inline block or only the closing
647 * brace of some inner scope, not the block's closing brace. */
648 token( IL_Symbol, tokstart, tokend );
653 scan_error() << "unterminated code block" << endl;
656 # Send every other character as a symbol.
657 any => { token( IL_Symbol, tokstart, tokend ); };
661 # Escape sequences in OR expressions.
662 '\\0' => { token( RE_Char, '\0' ); };
663 '\\a' => { token( RE_Char, '\a' ); };
664 '\\b' => { token( RE_Char, '\b' ); };
665 '\\t' => { token( RE_Char, '\t' ); };
666 '\\n' => { token( RE_Char, '\n' ); };
667 '\\v' => { token( RE_Char, '\v' ); };
668 '\\f' => { token( RE_Char, '\f' ); };
669 '\\r' => { token( RE_Char, '\r' ); };
670 '\\\n' => { updateCol(); };
671 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
673 # Range dash in an OR expression.
674 '-' => { token( RE_Dash, 0, 0 ); };
676 # Terminate an OR expression.
677 ']' => { token( RE_SqClose ); fret; };
680 scan_error() << "unterminated OR literal" << endl;
683 # Characters in an OR expression.
684 [^\]] => { token( RE_Char, tokstart, tokend ); };
689 # Escape sequences in regular expressions.
690 '\\0' => { token( RE_Char, '\0' ); };
691 '\\a' => { token( RE_Char, '\a' ); };
692 '\\b' => { token( RE_Char, '\b' ); };
693 '\\t' => { token( RE_Char, '\t' ); };
694 '\\n' => { token( RE_Char, '\n' ); };
695 '\\v' => { token( RE_Char, '\v' ); };
696 '\\f' => { token( RE_Char, '\f' ); };
697 '\\r' => { token( RE_Char, '\r' ); };
698 '\\\n' => { updateCol(); };
699 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
701 # Terminate an OR expression.
703 token( RE_Slash, tokstart, tokend );
707 # Special characters.
708 '.' => { token( RE_Dot ); };
709 '*' => { token( RE_Star ); };
711 '[' => { token( RE_SqOpen ); fcall or_literal; };
712 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
715 scan_error() << "unterminated regular expression" << endl;
718 # Characters in an OR expression.
719 [^\/] => { token( RE_Char, tokstart, tokend ); };
722 # We need a separate token space here to avoid the ragel keywords.
723 write_statement := |*
724 ident => { token( TK_Word, tokstart, tokend ); } ;
725 [ \t\n]+ => { updateCol(); };
726 ';' => { token( ';' ); fgoto parser_def; };
729 scan_error() << "unterminated write statement" << endl;
733 # Parser definitions.
735 'machine' => { token( KW_Machine ); };
736 'include' => { token( KW_Include ); };
739 fgoto write_statement;
741 'action' => { token( KW_Action ); };
742 'alphtype' => { token( KW_AlphType ); };
744 # FIXME: Enable this post 5.17.
745 # 'range' => { token( KW_Range ); };
749 inlineBlockType = SemiTerminated;
754 inlineBlockType = SemiTerminated;
758 token( KW_Variable );
759 inlineBlockType = SemiTerminated;
762 'when' => { token( KW_When ); };
763 'eof' => { token( KW_Eof ); };
764 'err' => { token( KW_Err ); };
765 'lerr' => { token( KW_Lerr ); };
766 'to' => { token( KW_To ); };
767 'from' => { token( KW_From ); };
768 'export' => { token( KW_Export ); };
771 ident => { token( TK_Word, tokstart, tokend ); } ;
774 number => { token( TK_UInt, tokstart, tokend ); };
775 hex_number => { token( TK_Hex, tokstart, tokend ); };
777 # Literals, with optionals.
778 ( s_literal | d_literal ) [i]?
779 => { token( TK_Literal, tokstart, tokend ); };
781 '[' => { token( RE_SqOpen ); fcall or_literal; };
782 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
784 '/' => { token( RE_Slash ); fgoto re_literal; };
787 pound_comment => { updateCol(); };
789 ':=' => { token( TK_ColonEquals ); };
792 ">~" => { token( TK_StartToState ); };
793 "$~" => { token( TK_AllToState ); };
794 "%~" => { token( TK_FinalToState ); };
795 "<~" => { token( TK_NotStartToState ); };
796 "@~" => { token( TK_NotFinalToState ); };
797 "<>~" => { token( TK_MiddleToState ); };
800 ">*" => { token( TK_StartFromState ); };
801 "$*" => { token( TK_AllFromState ); };
802 "%*" => { token( TK_FinalFromState ); };
803 "<*" => { token( TK_NotStartFromState ); };
804 "@*" => { token( TK_NotFinalFromState ); };
805 "<>*" => { token( TK_MiddleFromState ); };
808 ">/" => { token( TK_StartEOF ); };
809 "$/" => { token( TK_AllEOF ); };
810 "%/" => { token( TK_FinalEOF ); };
811 "</" => { token( TK_NotStartEOF ); };
812 "@/" => { token( TK_NotFinalEOF ); };
813 "<>/" => { token( TK_MiddleEOF ); };
815 # Global Error actions.
816 ">!" => { token( TK_StartGblError ); };
817 "$!" => { token( TK_AllGblError ); };
818 "%!" => { token( TK_FinalGblError ); };
819 "<!" => { token( TK_NotStartGblError ); };
820 "@!" => { token( TK_NotFinalGblError ); };
821 "<>!" => { token( TK_MiddleGblError ); };
823 # Local error actions.
824 ">^" => { token( TK_StartLocalError ); };
825 "$^" => { token( TK_AllLocalError ); };
826 "%^" => { token( TK_FinalLocalError ); };
827 "<^" => { token( TK_NotStartLocalError ); };
828 "@^" => { token( TK_NotFinalLocalError ); };
829 "<>^" => { token( TK_MiddleLocalError ); };
832 "<>" => { token( TK_Middle ); };
835 '>?' => { token( TK_StartCond ); };
836 '$?' => { token( TK_AllCond ); };
837 '%?' => { token( TK_LeavingCond ); };
839 '..' => { token( TK_DotDot ); };
840 '**' => { token( TK_StarStar ); };
841 '--' => { token( TK_DashDash ); };
842 '->' => { token( TK_Arrow ); };
843 '=>' => { token( TK_DoubleArrow ); };
845 ":>" => { token( TK_ColonGt ); };
846 ":>>" => { token( TK_ColonGtGt ); };
847 "<:" => { token( TK_LtColon ); };
849 # Opening of longest match.
850 "|*" => { token( TK_BarStar ); };
852 # Separater for name references.
853 "::" => { token( TK_NameSep, tokstart, tokend ); };
861 [ \t\r]+ => { updateCol(); };
863 # If we are in a single line machine then newline may end the spec.
866 if ( singleLineSpec ) {
873 if ( lastToken == KW_Export || lastToken == KW_Entry )
878 inlineBlockType = CurlyDelimited;
884 scan_error() << "unterminated ragel section" << endl;
887 any => { token( *tokstart ); } ;
893 /* If no errors and we are at the bottom of the include stack (the
894 * source file listed on the command line) then write out the data. */
895 if ( includeDepth == 0 && machineSpec == 0 && machineName == 0 )
896 xmlEscapeHost( output, tokstart, tokend-tokstart );
899 # Outside code scanner. These tokens get passed through.
903 c_cpp_comment => pass;
904 s_literal | d_literal => pass;
907 singleLineSpec = false;
913 singleLineSpec = true;
926 void Scanner::do_scan()
929 char *buf = new char[bufsize];
930 const char last_char = 0;
931 int cs, act, have = 0;
935 bool singleLineSpec = false;
936 InlineBlockType inlineBlockType = CurlyDelimited;
938 /* Init the section parser and the character scanner. */
943 char *p = buf + have;
944 int space = bufsize - have;
947 /* We filled up the buffer trying to scan a token. Grow it. */
948 bufsize = bufsize * 2;
949 char *newbuf = new char[bufsize];
951 /* Recompute p and space. */
953 space = bufsize - have;
955 /* Patch up pointers possibly in use. */
957 tokstart = newbuf + ( tokstart - buf );
958 tokend = newbuf + ( tokend - buf );
960 /* Copy the new buffer in. */
961 memcpy( newbuf, buf, have );
966 input.read( p, space );
967 int len = input.gcount();
969 /* If we see eof then append the EOF char. */
971 p[0] = last_char, len = 1;
978 /* Check if we failed. */
979 if ( cs == rlscan_error ) {
980 /* Machine failed before finding a token. I'm not yet sure if this
982 scan_error() << "scanner error" << endl;
986 /* Decide if we need to preserve anything. */
987 char *preserve = tokstart;
989 /* Now set up the prefix. */
993 /* There is data that needs to be shifted over. */
994 have = pe - preserve;
995 memmove( buf, preserve, have );
996 unsigned int shiftback = preserve - buf;
998 tokstart -= shiftback;
1008 void scan( char *fileName, istream &input, ostream &output )