0
votes

I am trying out a simple example that I found online that implements Word counter with flex and bison. Following are the .y and .l files:

mc_lexer.l

%{
/* C++ string header, for string ops below */
#include <string>

/* Implementation of yyFlexScanner */ 
#include "mc_scanner.hpp"
#undef  YY_DECL
#define YY_DECL int MC::MC_Scanner::yylex( MC::MC_Parser::semantic_type * 
const lval, MC::MC_Parser::location_type *loc )

/* typedef to make the returns for the tokens shorter */
using token = MC::MC_Parser::token;

/* define yyterminate as this instead of NULL */
#define yyterminate() return( token::END )

/* msvc2010 requires that we exclude this header file. */
#define YY_NO_UNISTD_H

/* update location on matching */
#define YY_USER_ACTION loc->step(); loc->columns(yyleng);

%}

%option debug
%option nodefault
%option yyclass="MC::MC_Scanner"
%option noyywrap
%option c++

%%
%{          /** Code executed at the beginning of yylex **/
            yylval = lval;
%}

[a-z]       {
               return( token::LOWER );
            }

[A-Z]       {
               return( token::UPPER );
            }

[a-zA-Z]+   {
               yylval->build< std::string >( yytext );
               return( token::WORD );
            }

 %%

mc_parser.yy

%skeleton "lalr1.cc"
%require  "3.0"
%debug 
%defines 
%define api.namespace {MC}
%define parser_class_name {MC_Parser}

%code requires{
   namespace MC {
      class MC_Driver;
      class MC_Scanner;
 }

 %parse-param { MC_Scanner  &scanner  }
 %parse-param { MC_Driver  &driver  }

 %code{
 #include <iostream>
 #include <cstdlib>
 #include <fstream>

 /* include for all driver functions */
 #include "mc_driver.hpp"

 #undef yylex
 #define yylex scanner.yylex
 }

... /* rest of the file */

mc_scanner.hpp

#if ! defined(yyFlexLexerOnce)
#include <FlexLexer.h>
#endif

#include "mc_parser.tab.hh"
#include "location.hh"

namespace MC{

    class MC_Scanner : public yyFlexLexer{
    public:

      MC_Scanner(std::istream *in) : yyFlexLexer(in)
      {};
      virtual ~MC_Scanner() {};

      //get rid of override virtual function warning
      using FlexLexer::yylex;

      virtual
      int yylex( MC::MC_Parser::semantic_type * const lval, 
                 MC::MC_Parser::location_type *location );
      // YY_DECL defined in mc_lexer.l
      // Method body created by flex in mc_lexer.yy.cc


     private:
       /* yyval ptr */
      MC::MC_Parser::semantic_type *yylval = nullptr;
   };

} /* end namespace MC */

When I build with the Makefile that I found online, the build succeeds. When I tried to test it out using my build system, the compiler complains about the following lines of code:

#undef yylex
#define yylex scanner.yylex

Since MC_Scanner is just forward declared in .yy file, the compiler complains that the type of scanner is unknown. Now I cannot pound include the "mc_scanner.hpp" in the .y file because it will introduce circular dependency.

Circular dependency: The scanner file mc_scanner.hpp depends on mc_parser.tab.hh because it needs to know what semantic_type is. mc_parser.tab.hh is generated by mc_parser.y.

Now mc_parser.y has the following lines of code:

#undef yylex
#define yylex scanner.yylex

And scanner is declared as follows:

%code requires{
   namespace MC {
      class MC_Driver;
      class MC_Scanner;
 }

 %parse-param { MC_Scanner  &scanner  }
 %parse-param { MC_Driver  &driver  }

So the compiler complains that it cannot determine the type of scanner. And I cannot pound include the mc_scanner.hpp in mc_parser.y because of this circular dependency.

Any ideas as to how I could break this dependency?

1
I hope I don't step on anyones toes - but I'll remove the C++ tag right awayTed Lyngmo

1 Answers

0
votes

I solved the issue. I had to include the scanner class header file just before the grammar block begins.