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
43 machine section_parse;
53 bool Scanner::active()
58 if ( parser == 0 && ! parserExistsError ) {
59 scan_error() << "there is no previous specification name" << endl;
60 parserExistsError = true;
69 ostream &Scanner::scan_error()
71 /* Maintain the error count. */
73 cerr << fileName << ":" << line << ":" << column << ": ";
77 bool Scanner::recursiveInclude( char *inclFileName, char *inclSectionName )
79 for ( IncludeStack::Iter si = includeStack; si.lte(); si++ ) {
80 if ( strcmp( si->fileName, inclFileName ) == 0 &&
81 strcmp( si->sectionName, inclSectionName ) == 0 )
89 void Scanner::updateCol()
94 //cerr << "adding " << tokend - from << " to column" << endl;
95 column += tokend - from;
99 void Scanner::token( int type, char c )
101 token( type, &c, &c + 1 );
104 void Scanner::token( int type )
110 machine section_parse;
112 # This relies on the the kelbt implementation and the order
113 # that tokens are declared.
120 action clear_words { word = lit = 0; word_len = lit_len = 0; }
121 action store_word { word = tokdata; word_len = toklen; }
122 action store_lit { lit = tokdata; lit_len = toklen; }
124 action mach_err { scan_error() << "bad machine statement" << endl; }
125 action incl_err { scan_error() << "bad include statement" << endl; }
126 action write_err { scan_error() << "bad write statement" << endl; }
128 action handle_machine
130 /* Assign a name to the machine. */
131 char *machine = word;
133 if ( inclSectionTarg == 0 ) {
134 ignoreSection = false;
136 ParserDictEl *pdEl = parserDict.find( machine );
138 pdEl = new ParserDictEl( machine );
139 pdEl->value = new Parser( fileName, machine, sectionLoc );
141 parserDict.insert( pdEl );
144 parser = pdEl->value;
146 else if ( strcmp( inclSectionTarg, machine ) == 0 ) {
147 /* found include target */
148 ignoreSection = false;
149 parser = inclToParser;
152 /* ignoring section */
153 ignoreSection = true;
159 ( KW_Machine TK_Word @store_word ';' ) @handle_machine
160 <>err mach_err <>eof mach_err;
162 action handle_include
165 char *inclSectionName = word;
166 char *inclFileName = 0;
168 /* Implement defaults for the input file and section name. */
169 if ( inclSectionName == 0 )
170 inclSectionName = parser->sectionName;
173 inclFileName = prepareFileName( lit, lit_len );
175 inclFileName = fileName;
177 /* Check for a recursive include structure. Add the current file/section
178 * name then check if what we are including is already in the stack. */
179 includeStack.append( IncludeStackItem( fileName, parser->sectionName ) );
181 if ( recursiveInclude( inclFileName, inclSectionName ) )
182 scan_error() << "include: this is a recursive include operation" << endl;
184 /* Open the input file for reading. */
185 ifstream *inFile = new ifstream( inclFileName );
186 if ( ! inFile->is_open() ) {
187 scan_error() << "include: could not open " <<
188 inclFileName << " for reading" << endl;
191 Scanner scanner( inclFileName, *inFile, output, parser,
192 inclSectionName, includeDepth+1 );
197 /* Remove the last element (len-1) */
198 includeStack.remove( -1 );
203 TK_Word @store_word ( TK_Literal @store_lit )? |
204 TK_Literal @store_lit
208 ( KW_Include include_names ';' ) @handle_include
209 <>err incl_err <>eof incl_err;
213 if ( active() && machineSpec == 0 && machineName == 0 ) {
215 " def_name=\"" << parser->sectionName << "\""
216 " line=\"" << line << "\""
217 " col=\"" << column << "\""
224 if ( active() && machineSpec == 0 && machineName == 0 )
225 output << "<arg>" << tokdata << "</arg>";
230 if ( active() && machineSpec == 0 && machineName == 0 )
231 output << "</write>\n";
235 ( KW_Write @write_command
236 ( TK_Word @write_arg )+ ';' @write_close )
237 <>err write_err <>eof write_err;
241 /* Send the token off to the parser. */
246 cerr << "scanner:" << line << ":" << column <<
247 ": sending token to the parser " << Parser_lelNames[*p];
248 cerr << " " << toklen;
250 cerr << " " << tokdata;
254 loc.fileName = fileName;
258 parser->token( loc, type, tokdata, toklen );
262 # Catch everything else.
263 everything_else = ^( KW_Machine | KW_Include | KW_Write ) @handle_token;
273 void Scanner::token( int type, char *start, char *end )
282 tokdata = new char[toklen+1];
283 memcpy( tokdata, start, toklen );
288 machine section_parse;
294 /* Record the last token for use in controlling the scan of subsequent
299 void Scanner::startSection( )
301 parserExistsError = false;
303 if ( includeDepth == 0 ) {
304 if ( machineSpec == 0 && machineName == 0 )
305 output << "</host>\n";
308 sectionLoc.fileName = fileName;
309 sectionLoc.line = line;
313 void Scanner::endSection( )
315 /* Execute the eof actions for the section parser. */
317 machine section_parse;
321 /* Close off the section with the parser. */
324 loc.fileName = fileName;
328 parser->token( loc, TK_EndSection, 0, 0 );
331 if ( includeDepth == 0 ) {
332 if ( machineSpec == 0 && machineName == 0 ) {
333 /* The end section may include a newline on the end, so
334 * we use the last line, which will count the newline. */
335 output << "<host line=\"" << line << "\">";
343 # This is sent by the driver code.
353 # Identifiers, numbers, commetns, and other common things.
354 ident = ( alpha | '_' ) ( alpha |digit |'_' )*;
356 hex_number = '0x' [0-9a-fA-F]+;
359 '/*' ( any | NL )* :>> '*/';
364 c_cpp_comment = c_comment | cpp_comment;
366 # These literal forms are common to C-like host code and ragel.
367 s_literal = "'" ([^'\\] | NL | '\\' (any | NL))* "'";
368 d_literal = '"' ([^"\\] | NL | '\\' (any | NL))* '"';
370 whitespace = [ \t] | NL;
371 pound_comment = '#' [^\n]* NL;
373 # An inline block of code. This is specified as a scanned, but is sent to
374 # the parser as one long block. The inline_block pointer is used to handle
375 # the preservation of the data.
377 # Inline expression keywords.
378 "fpc" => { token( KW_PChar ); };
379 "fc" => { token( KW_Char ); };
380 "fcurs" => { token( KW_CurState ); };
381 "ftargs" => { token( KW_TargState ); };
383 whitespaceOn = false;
387 # Inline statement keywords.
389 whitespaceOn = false;
392 "fexec" => { token( KW_Exec, 0, 0 ); };
394 whitespaceOn = false;
398 whitespaceOn = false;
402 whitespaceOn = false;
406 whitespaceOn = false;
410 whitespaceOn = false;
414 ident => { token( TK_Word, tokstart, tokend ); };
416 number => { token( TK_UInt, tokstart, tokend ); };
417 hex_number => { token( TK_Hex, tokstart, tokend ); };
419 ( s_literal | d_literal )
420 => { token( IL_Literal, tokstart, tokend ); };
424 token( IL_WhiteSpace, tokstart, tokend );
426 c_cpp_comment => { token( IL_Comment, tokstart, tokend ); };
428 "::" => { token( TK_NameSep, tokstart, tokend ); };
430 # Some symbols need to go to the parser as with their cardinal value as
431 # the token type (as opposed to being sent as anonymous symbols)
432 # because they are part of the sequences which we interpret. The * ) ;
433 # symbols cause whitespace parsing to come back on. This gets turned
434 # off by some keywords.
438 token( *tokstart, tokstart, tokend );
439 if ( inlineBlockType == SemiTerminated )
445 token( *tokstart, tokstart, tokend );
448 [,(] => { token( *tokstart, tokstart, tokend ); };
451 token( IL_Symbol, tokstart, tokend );
456 if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
457 /* Inline code block ends. */
462 /* Either a semi terminated inline block or only the closing
463 * brace of some inner scope, not the block's closing brace. */
464 token( IL_Symbol, tokstart, tokend );
469 scan_error() << "unterminated code block" << endl;
472 # Send every other character as a symbol.
473 any => { token( IL_Symbol, tokstart, tokend ); };
477 # Escape sequences in OR expressions.
478 '\\0' => { token( RE_Char, '\0' ); };
479 '\\a' => { token( RE_Char, '\a' ); };
480 '\\b' => { token( RE_Char, '\b' ); };
481 '\\t' => { token( RE_Char, '\t' ); };
482 '\\n' => { token( RE_Char, '\n' ); };
483 '\\v' => { token( RE_Char, '\v' ); };
484 '\\f' => { token( RE_Char, '\f' ); };
485 '\\r' => { token( RE_Char, '\r' ); };
486 '\\\n' => { updateCol(); };
487 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
489 # Range dash in an OR expression.
490 '-' => { token( RE_Dash, 0, 0 ); };
492 # Terminate an OR expression.
493 ']' => { token( RE_SqClose ); fret; };
496 scan_error() << "unterminated OR literal" << endl;
499 # Characters in an OR expression.
500 [^\]] => { token( RE_Char, tokstart, tokend ); };
505 # Escape sequences in regular expressions.
506 '\\0' => { token( RE_Char, '\0' ); };
507 '\\a' => { token( RE_Char, '\a' ); };
508 '\\b' => { token( RE_Char, '\b' ); };
509 '\\t' => { token( RE_Char, '\t' ); };
510 '\\n' => { token( RE_Char, '\n' ); };
511 '\\v' => { token( RE_Char, '\v' ); };
512 '\\f' => { token( RE_Char, '\f' ); };
513 '\\r' => { token( RE_Char, '\r' ); };
514 '\\\n' => { updateCol(); };
515 '\\' any => { token( RE_Char, tokstart+1, tokend ); };
517 # Terminate an OR expression.
519 token( RE_Slash, tokstart, tokend );
523 # Special characters.
524 '.' => { token( RE_Dot ); };
525 '*' => { token( RE_Star ); };
527 '[' => { token( RE_SqOpen ); fcall or_literal; };
528 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
531 scan_error() << "unterminated regular expression" << endl;
534 # Characters in an OR expression.
535 [^\/] => { token( RE_Char, tokstart, tokend ); };
538 # We need a separate token space here to avoid the ragel keywords.
539 write_statement := |*
540 ident => { token( TK_Word, tokstart, tokend ); } ;
541 [ \t\n]+ => { updateCol(); };
542 ';' => { token( ';' ); fgoto parser_def; };
545 scan_error() << "unterminated write statement" << endl;
549 # Parser definitions.
551 'machine' => { token( KW_Machine ); };
552 'include' => { token( KW_Include ); };
555 fgoto write_statement;
557 'action' => { token( KW_Action ); };
558 'alphtype' => { token( KW_AlphType ); };
560 # FIXME: Enable this post 5.17.
561 # 'range' => { token( KW_Range ); };
565 inlineBlockType = SemiTerminated;
570 inlineBlockType = SemiTerminated;
574 token( KW_Variable );
575 inlineBlockType = SemiTerminated;
578 'when' => { token( KW_When ); };
579 'eof' => { token( KW_Eof ); };
580 'err' => { token( KW_Err ); };
581 'lerr' => { token( KW_Lerr ); };
582 'to' => { token( KW_To ); };
583 'from' => { token( KW_From ); };
584 'export' => { token( KW_Export ); };
587 ident => { token( TK_Word, tokstart, tokend ); } ;
590 number => { token( TK_UInt, tokstart, tokend ); };
591 hex_number => { token( TK_Hex, tokstart, tokend ); };
593 # Literals, with optionals.
594 ( s_literal | d_literal ) [i]?
595 => { token( TK_Literal, tokstart, tokend ); };
597 '[' => { token( RE_SqOpen ); fcall or_literal; };
598 '[^' => { token( RE_SqOpenNeg ); fcall or_literal; };
600 '/' => { token( RE_Slash ); fgoto re_literal; };
603 pound_comment => { updateCol(); };
605 ':=' => { token( TK_ColonEquals ); };
608 ">~" => { token( TK_StartToState ); };
609 "$~" => { token( TK_AllToState ); };
610 "%~" => { token( TK_FinalToState ); };
611 "<~" => { token( TK_NotStartToState ); };
612 "@~" => { token( TK_NotFinalToState ); };
613 "<>~" => { token( TK_MiddleToState ); };
616 ">*" => { token( TK_StartFromState ); };
617 "$*" => { token( TK_AllFromState ); };
618 "%*" => { token( TK_FinalFromState ); };
619 "<*" => { token( TK_NotStartFromState ); };
620 "@*" => { token( TK_NotFinalFromState ); };
621 "<>*" => { token( TK_MiddleFromState ); };
624 ">/" => { token( TK_StartEOF ); };
625 "$/" => { token( TK_AllEOF ); };
626 "%/" => { token( TK_FinalEOF ); };
627 "</" => { token( TK_NotStartEOF ); };
628 "@/" => { token( TK_NotFinalEOF ); };
629 "<>/" => { token( TK_MiddleEOF ); };
631 # Global Error actions.
632 ">!" => { token( TK_StartGblError ); };
633 "$!" => { token( TK_AllGblError ); };
634 "%!" => { token( TK_FinalGblError ); };
635 "<!" => { token( TK_NotStartGblError ); };
636 "@!" => { token( TK_NotFinalGblError ); };
637 "<>!" => { token( TK_MiddleGblError ); };
639 # Local error actions.
640 ">^" => { token( TK_StartLocalError ); };
641 "$^" => { token( TK_AllLocalError ); };
642 "%^" => { token( TK_FinalLocalError ); };
643 "<^" => { token( TK_NotStartLocalError ); };
644 "@^" => { token( TK_NotFinalLocalError ); };
645 "<>^" => { token( TK_MiddleLocalError ); };
648 "<>" => { token( TK_Middle ); };
651 '>?' => { token( TK_StartCond ); };
652 '$?' => { token( TK_AllCond ); };
653 '%?' => { token( TK_LeavingCond ); };
655 '..' => { token( TK_DotDot ); };
656 '**' => { token( TK_StarStar ); };
657 '--' => { token( TK_DashDash ); };
658 '->' => { token( TK_Arrow ); };
659 '=>' => { token( TK_DoubleArrow ); };
661 ":>" => { token( TK_ColonGt ); };
662 ":>>" => { token( TK_ColonGtGt ); };
663 "<:" => { token( TK_LtColon ); };
665 # Opening of longest match.
666 "|*" => { token( TK_BarStar ); };
668 # Separater for name references.
669 "::" => { token( TK_NameSep, tokstart, tokend ); };
677 [ \t\r]+ => { updateCol(); };
679 # If we are in a single line machine then newline may end the spec.
682 if ( singleLineSpec ) {
689 if ( lastToken == KW_Export || lastToken == KW_Entry )
694 inlineBlockType = CurlyDelimited;
700 scan_error() << "unterminated ragel section" << endl;
703 any => { token( *tokstart ); } ;
709 /* If no errors and we are at the bottom of the include stack (the
710 * source file listed on the command line) then write out the data. */
711 if ( includeDepth == 0 && machineSpec == 0 && machineName == 0 )
712 xmlEscapeHost( output, tokstart, tokend-tokstart );
715 # Outside code scanner. These tokens get passed through.
719 c_cpp_comment => pass;
720 s_literal | d_literal => pass;
723 singleLineSpec = false;
729 singleLineSpec = true;
742 void Scanner::do_scan()
745 char *buf = new char[bufsize];
746 const char last_char = 0;
747 int cs, act, have = 0;
751 bool singleLineSpec = false;
752 InlineBlockType inlineBlockType = CurlyDelimited;
754 /* Init the section parser and the character scanner. */
759 char *p = buf + have;
760 int space = bufsize - have;
763 /* We filled up the buffer trying to scan a token. Grow it. */
764 bufsize = bufsize * 2;
765 char *newbuf = new char[bufsize];
767 /* Recompute p and space. */
769 space = bufsize - have;
771 /* Patch up pointers possibly in use. */
773 tokstart = newbuf + ( tokstart - buf );
774 tokend = newbuf + ( tokend - buf );
776 /* Copy the new buffer in. */
777 memcpy( newbuf, buf, have );
782 input.read( p, space );
783 int len = input.gcount();
785 /* If we see eof then append the EOF char. */
787 p[0] = last_char, len = 1;
794 /* Check if we failed. */
795 if ( cs == rlscan_error ) {
796 /* Machine failed before finding a token. I'm not yet sure if this
798 scan_error() << "scanner error" << endl;
802 /* Decide if we need to preserve anything. */
803 char *preserve = tokstart;
805 /* Now set up the prefix. */
809 /* There is data that needs to be shifted over. */
810 have = pe - preserve;
811 memmove( buf, preserve, have );
812 unsigned int shiftback = preserve - buf;
814 tokstart -= shiftback;
824 void scan( char *fileName, istream &input, ostream &output )