/************************************************************************** // Copyright (c) 2004 Max-Planck-Institut Saarbruecken (Germany) // All rights reserved. // // This file is part of BenchmarkParser // // $URL$ // $Id$ // SPDX-License-Identifier: LGPL-3.0-or-later // // // Author(s) : Lutz Kettner **************************************************************************/ %{ #include #include #include #include #include /* Use C++ std::string as semantic value to communicate with lexer */ #define YYSTYPE std::string #include // gets definitions from syntax parser // Public lexer interface, check with decl. in benchmark_parser.h // -------------------------------------------------------------- // Current input stream and associated values. std::istream* benchmark_in = & std::cin; int benchmark_linenumber = 1; std::string benchmark_filename( ""); // Initialize lexer to scan from input stream with name and linenumber. // The caller is responsible for the lifetime of 'in' that must live // during the scan. void benchmark_init_lexer( std::istream& in, std::string name, int linenumber = 1); // Reset lexer to clean state. void benchmark_reset_lexer(); // Writes a trace of the current include file nesting to the 'out' stream. // Appends the 'fill' string after each file listed. void benchmark_include_file_trace( std::ostream& out, std::string fill); // Private lexer interface, all static to keep it local // ---------------------------------------------------- // Stack of input streams: the item stored in the stack. struct Include_stack_item { std::istream* in; // input stream int linenumber; // linenumber in file for error messages std::string filename; // filename (or similar string) YY_BUFFER_STATE buffer; // Opaque pointer to flex-buffer struct. Include_stack_item() {} Include_stack_item( std::istream* i, int ln, std::string name, YY_BUFFER_STATE buf) : in(i), linenumber(ln), filename(name), buffer(buf) {} }; // Stack of input streams is a std::list typedef std::list Include_stack; // We have one static global variable for the stack and some static functions. // The include stack is except for the trace() debug function not // accessible from other files (is not polluting namespaces) since // the Include() function and the <> condition are all handled here. // The function implementations are at the end of this file. static Include_stack include_stack; // Push current state on include_stack, initializes lexer with new file // input stream, name, and linenumber. static void open_include_stream( std::istream *in, std::string name, int linenumber = 1); // Shortcut for open file with name and then call open_include_stream. // Returns false if the file could not be opened successfully. static bool open_include_file( std::string name, int linenumber = 1); // Closes current input stream. Initializes lexer with new file input stream, // name, and linenumber, from the include_stack's top. Pop's an element from // the include_stack. Returns false if include_stack was empty. static bool close_include_stream(); // Make the lexer read from benchmark_in instead of yyin #define YY_INPUT(buf,result,max_size) { \ benchmark_in->read( buf, max_size); \ result = benchmark_in->gcount(); \ } // Count newlines in the string s static void count_newlines( const char* s) { while ( *s) { if ( *s == '\n') ++benchmark_linenumber; ++s; } } static int comment_nesting = 0; // counts nesting depth of () in Comments #define YY_BREAK /* a do nothing */ /* -------------------------------------------------------------------- Parsing Modes: -- INITIAL: main mode for sequence of tokens -- IncludeMode: parses lciInclude filename, -- CommentMode: Comment(...) parsing of nested parantheses # comments and strings are correctly ignored -------------------------------------------------------------------- */ %} %x IncludeMode %x CommentMode space [\t \r]* newline [\n] spcnl [\t \n\r]* integer [+-]?[0-9]+ /* An fnumber consists of up to three parts: * 1) a sign [+-] * 2) a mantissa (digits [0-9] with at most one dot in between) * 3) an exponent [eE]{integer} * * Part 1) is optional. * Part 2) must contain at least one digit, * but may have leading or trailing dot * Part 3) is optional iff the mantissa contains a dot * (so that fnumbers and integers remain distinguishable). */ fnumber [+-]?(([0-9]+[.][0-9]*|[.][0-9]+)([eE]{integer})?|[0-9]+[eE]{integer}) idfier [a-zA-Z][a-zA-Z0-9_]* minus_infty MINUS_INFTY plus_infty PLUS_INFTY filename [^ \t\n\r\\\{\}\[\]()]+ void VOID counterclockwise COUNTERCLOCKWISE clockwise CLOCKWISE %% /* Tokens */ /* --------- */ "FileFormat" { return FileFormat;} "BenchmarkName" { return BenchmarkName; } "Classification" { return Classification; } "List" { return List; } "Rational" { return Rational; } "Polynomial_1" { return Polynomial_1; } "Point_2" { return Point_2; } "AlgebraicReal" { return AlgebraicReal; } "ConicPoint_2" { return ConicPoint_2; } "Conic_2" { return Conic_2; } "ConicArc_2" { return ConicArc_2; } "Circle_2" { return Circle_2; } "LineSegment_2" { return LineSegment_2; } "Cubic_2" { return Cubic_2; } "CircularArc_2" { return CircularArc_2;} "LineArc_2" { return LineArc_2;} "CircularPoint_2" {return CircularPoint_2;} "Quadric_3" { return Quadric_3; } /* Integer numbers and float numbers */ /* --------------------------------- */ {integer} { yylval = std::string( yytext); return INTEGER; } {fnumber} { yylval = std::string( yytext); return FNUMBER; } {minus_infty} { yylval = std::string( yytext); return MINUS_INFTY; } {plus_infty} { yylval = std::string( yytext); return PLUS_INFTY; } {void} { yylval = std::string( yytext); return VOID; } {counterclockwise} { yylval = std::string( yytext); return COUNTERCLOCKWISE; } {clockwise} { yylval = std::string( yytext); return CLOCKWISE; } /* Non-recognized tokens are errors */ /* -------------------------------- */ {idfier} { yylval = std::string( yytext); return UNKNOWN_TOKEN; } /* Handle include files */ /* ------------------------- */ "IncludeFile"{spcnl}[(]{spcnl} { BEGIN( IncludeMode); count_newlines( yytext); break; } {filename} { /* remove remaining chars before the ')' */ int c = yyinput(); while( c != EOF && c != ')') { if ( c == '\n') ++benchmark_linenumber; c = yyinput(); } BEGIN( INITIAL); if ( c == EOF) { yylval = std::string( "Reached EOF while" " parsing include filename '") + std::string( yytext) + std::string( "'."); return ERROR; } open_include_file( yytext); break; } /* Strings with quoted \" and \\ */ /* ------------------------------ */ "\"" { int c = yyinput(); yylval = std::string(""); bool quoted_char = false; while (c != EOF && (c!='"' || quoted_char)){ if ( c == '\n') ++benchmark_linenumber; if ( ! quoted_char && c == '\\') { quoted_char = true; } else { quoted_char = false; yylval.push_back( char(c)); } c = yyinput(); } if ( c == EOF) { yylval = std::string( "Reached EOF while" " parsing string constant '") + yylval + std::string( "'."); BEGIN( INITIAL); return ERROR; } if ( YY_START == INITIAL) { return STRING; } yylval = std::string(""); break; } /* One line comment starting with # */ /* -------------------------------- */ "#" { /* remove remaining chars before the '\n' */ int c = yyinput(); while( c != EOF && c != '\n') { c = yyinput(); } if ( c == '\n') ++benchmark_linenumber; break; } /* Nestable and multi-line Comment( ... ) */ /* -------------------------------------- */ "Comment"{spcnl}"("{spcnl} { BEGIN( CommentMode); comment_nesting = 1; count_newlines( yytext); break; } {newline} { ++benchmark_linenumber; break; } "(" { ++comment_nesting; break; } ")" { if ( --comment_nesting == 0) BEGIN( INITIAL); break; } <> { yylval = std::string( "Reached EOF while " "parsing 'Comment(...)'."); BEGIN( INITIAL); return ERROR; } . { break; } /* Count line numbers for good error messages */ {newline} { ++benchmark_linenumber; break; } /* Ignore white spaces */ {space} { break; } /* stop scanning at EOF, maybe continue with surrounding file */ <> { yyterminate(); } /* single characters passed to the parser: */ [(),] { return yytext[0]; } /* all other single characters are errors */ . { yylval = std::string( "Found illegal char '") + std::string( yytext) + std::string("'."); return ERROR; } %% // implements the proper EOF behavior, pop's file from include stack extern "C" int yywrap() { if ( close_include_stream()) return 0; // there was a file waiting on the stack, we continue scan return 1; // no file in the stack left, we stop scan } // Push current state on include_stack, initializes lexer with new file // input stream, name, and linenumber. static void open_include_stream( std::istream *in, std::string name, int linenumber) { include_stack.push_front( Include_stack_item( benchmark_in, benchmark_linenumber, benchmark_filename, YY_CURRENT_BUFFER)); benchmark_in = in; benchmark_linenumber = linenumber; benchmark_filename = name; yy_switch_to_buffer( yy_create_buffer( 0, YY_BUF_SIZE)); } // Shortcut for open file with name and then call open_include_stream. // Returns false if the file could not be opened successfully. static bool open_include_file( std::string name, int linenumber) { std::ifstream* in = new std::ifstream( name.c_str()); if ( ! *in) { delete in; return false; } open_include_stream( in, name, linenumber); return true; } // Closes current input stream. Initializes lexer with new file input stream, // name, and linenumber, from the include_stack's top. Pop's an element from // the include_stack. Returns false if include_stack was empty. static bool close_include_stream() { if ( include_stack.empty()) return false; delete benchmark_in; // closes the stream benchmark_in = include_stack.front().in; benchmark_linenumber = include_stack.front().linenumber; benchmark_filename = include_stack.front().filename; yy_delete_buffer( YY_CURRENT_BUFFER); yy_switch_to_buffer( include_stack.front().buffer); include_stack.pop_front(); return true; } // Public interface of the lexer component // --------------------------------------- // Initialize lexer to scan from input stream with name and linenumber. // The caller is responsible for the lifetime of 'in' that must last // during the scan. void benchmark_init_lexer( std::istream& in, std::string name, int linenumber) { benchmark_reset_lexer(); benchmark_in = & in; benchmark_linenumber = linenumber; benchmark_filename = name; yy_switch_to_buffer( yy_create_buffer( 0, YY_BUF_SIZE)); } // reset lexer to clean state. Can be used to parse another file then. void benchmark_reset_lexer() { while( close_include_stream()) // close all pre-existing include files ; // empty while body benchmark_in = & std::cin; benchmark_linenumber = 1; benchmark_filename = std::string(""); yyrestart(0); BEGIN( INITIAL); }; // Writes a trace of the current include file nesting to the 'out' stream. void benchmark_include_file_trace( std::ostream& out, std::string fill) { std::size_t n = include_stack.size(); for ( Include_stack::const_iterator i = include_stack.begin(); i != include_stack.end(); ++i) { out << std::setw(3) << n-- << ": '" << i->filename << "' line " << i->linenumber << fill << std::endl; } } // This summy function is only here to suppress a warning for // an unused yyunput function void benchmark_parser_dummy_no_warn_() { yyunput( 1, 0); } // EOF