%{
    #define _SKIP_YYFLEXLEXER_
    #include "scanner.ih"

%}

%option yyclass="Scanner" outfile="yylex.cc"
%option c++ 8bit warn noyywrap yylineno
%option debug

%x xstring pstring pxstring string comment quote block includeOnly

OCTAL   [0-7]
OCT3    OCTAL{3}
HEX     [0-9a-fA-F]
HEX2    HEX{2}
ID1     [a-zA-Z_]
ID2     [0-9a-zA-Z_]
IDENT   {ID1}{ID2}*


%%

            int nKept;      // used by <xstring>
            int ret;

            if (d_includeOnly)
                BEGIN includeOnly;


<INITIAL,block>{

"{"          {
                    // open or count a nested a block 
                d_block.open(yylineno, sourceName()); 
                BEGIN block;
            }
    /*
        The whitespace-eating RegExes (REs) will normally simply consume the
        WS. However, if d_retWS is unequal 0 then WS is returned. This is
        sometimes needed (e.g., inside code blocks to be able to return the ws
        as part of the code-block). Comment is part of the WS returning REs
    */

[ \t]+       {
                 if (d_block)
                     d_block += " ";
             }
             
[\n]+        {
                 if (d_block)
                     d_block += "\n";
             }

"//".*       ;   // ignore eoln comment in source blocks

    /* If comment is entered from `block' either a blank or a newline will be
        added to the block as soon as the matching end-comment is seen, and
        the scanner will return to its block-miniscanner state
    */
"/*"         {
                 d_commentChar[0] = ' ';
                 BEGIN comment;
             }
}

<INITIAL,includeOnly>"%include"[ \t]*   {
                                            BEGIN pxstring;
                                            d_include = true;
                                        }

    /*
        Blocks start at { and end at their matching } char. They may contain
        comment and whitespace, but whitespace is reduced to single blanks or
        newlines. All STRING and QUOTE constants are kept as-is, and are
        registered as skip-ranges for $-checks
    */

<block>{

"}"         {
                if (d_block.close())    // close a block
                {
                    BEGIN INITIAL;
                    return Parser::BLOCK;
                }
            }

"\""        {
                BEGIN string;
                d_block.beginSkip();
                yymore();
            }

"'"         {
                BEGIN quote;
                d_block.beginSkip();
                yymore();
            }

.           d_block(yytext);

}

"%baseclass-header"[ \t]*       {
                                    BEGIN pxstring;
                                    return Parser::BASECLASS_HEADER;
                                }

"%baseclass-preinclude"[ \t]*   {
                                    BEGIN pxstring;
                                    return Parser::BASECLASS_PREINCLUDE;
                                }
"%class-header"[ \t]*           {
                                    BEGIN pxstring;
                                    return Parser::CLASS_HEADER;
                                }
"%class-name"                   return Parser::CLASS_NAME;
"%debug"                        return Parser::DEBUGFLAG;
"%error-verbose"                return Parser::ERROR_VERBOSE;
"%expect"                       return Parser::EXPECT;
"%filenames"[ \t]*              {
                                    BEGIN pxstring;
                                    return Parser::FILENAMES;
                                }
"%implementation-header"[ \t]*  {
                                    BEGIN pxstring;
                                    return Parser::IMPLEMENTATION_HEADER;
                                }
"%left"                         return Parser::LEFT;
"%lines"                        return Parser::LINES;
"%locationstruct"               return Parser::LOCATIONSTRUCT;
"%lsp-needed"                   return Parser::LSP_NEEDED;
"%ltype"[ \t]*                  {
                                    BEGIN xstring;
                                    return Parser::LTYPE;
                                }
"%namespace"                    return Parser::NAMESPACE;
"%negative-dollar-indices"      return Parser::NEG_DOLLAR;
"%nonassoc"                     return Parser::NONASSOC;
"%parsefun-source"[ \t]*        {
                                    BEGIN pxstring;
                                    return Parser::PARSEFUN_SOURCE;
                                }
"%parsefun-source"[ \t]*        {
                                    BEGIN pxstring;
                                    return Parser::PARSEFUN_SOURCE;
                                }
"%prec"                         return Parser::PREC;
"%print"                        {
                                    BEGIN pxstring;
                                    return Parser::PRINT;
                                }
"%right"                        return Parser::RIGHT;
"%required-tokens"              return Parser::REQUIRED;
"%scanner"[ \t]*                {
                                    BEGIN pxstring;
                                    return Parser::SCANNER_INCLUDE;
                                }
"%scanner-token-function"[ \t]* {
                                    BEGIN pxstring;
                                    return Parser::SCANNER_TOKEN_FUNCTION;
                                }
"%start"                        return Parser::START;
"%stype"                        {
                                    BEGIN xstring;
                                    return Parser::STYPE;
                                }
"%token"                        return Parser::TOKEN;
"%type"                         return Parser::TYPE;
"%union"                        return Parser::UNION;
"%%"                            return Parser::TWO_PERCENTS;

"'"                             {
                                    BEGIN quote;
                                    yymore();
                                }

"\""                            {
                                    BEGIN string;
                                    yymore();
                                }

{IDENT}                         return Parser::IDENTIFIER;

[0-9]+                          return setNumber();

.                               return yytext[0];

    /*
        At the end of input, check to see if we should switch back to a
        previously pushed file
    */

<<EOF>>                     {
                                if (!popSource(YY_CURRENT_BUFFER))
                                    yyterminate();
                            }

    /*
        pxstring selects either pstring, xstring or string
    */
<pxstring>{
        
"\""    {
            yymore();
            BEGIN string;
        }

"<"     {
            yymore();
            BEGIN pstring;
        }

\n      {
            yyless(0);
            BEGIN INITIAL;
        }
            
.       {
            yyless(0);
            BEGIN xstring;
        }
}

    /*                            
        string may be entered from block and pxstring
        strings are all series (including escaped chars, like \") surrounded by
        double quotes:
    */
<string>{
        
"\""    {
            if (d_block.endSkip(yytext))
                BEGIN block;
            else
            {
                BEGIN INITIAL;

                ret = yytextChk(&nKept, 3, Parser::STRING);
                if (ret)
                    return ret;

                pushSource(YY_CURRENT_BUFFER, 
                                            YY_BUF_SIZE);
            }
        }

"\\".   |              
.       |
\n      yymore();

}

<comment>{
.                  ;

\n                 d_commentChar[0] = '\n';
                   
"*/"               {
                       if (!d_block)
                           BEGIN INITIAL;
                       else
                       {
                           d_block += d_commentChar;
                           BEGIN block;
                       }
                   }
}

    /*
        when include is requested, pick all chars, but at %include
        switch file
    */
<includeOnly>.|\n             cout << yytext << flush;

    /*
        a pstring is a string surrounded by < and >
    */

<pstring>{
        
">"     {
            BEGIN INITIAL;

            ret = yytextChk(&nKept, 3, Parser::PSTRING);
            if (ret)
                return ret;

            pushSource(YY_CURRENT_BUFFER, YY_BUF_SIZE);
        }

"\\".   |              
.       |
\n      yymore();

}

    /*
        quote may be entered from INITIAL and block. 
        quoted constants start with a quote. They may be octal or hex numbers,
        escaped chars, or quoted constants 
    */
                            
<quote>{

"\\"{OCT3}"'"        {
                         if (d_block.skip(yytext))
                             BEGIN block;
                         else
                         {
                             BEGIN INITIAL;
                             octal();        // quoted octal constant
                             return Parser::QUOTE;
                         }
                     }
                     
"\\x"{HEX2}"'"       {
                         if (d_block.skip(yytext))
                             BEGIN block;
                         else
                         {
                             BEGIN INITIAL;
                             hexadecimal(); // quoted hex constant
                             return Parser::QUOTE;
                         }
                     }
                     
"\\"[abfnrtv]"'"     {
                         if (d_block(yytext))
                             BEGIN block;
                         else
                         {
                             BEGIN INITIAL;
                             escape();       // quoted escape char
                             return Parser::QUOTE;
                         }
                     }    
"\\"."'"             {
                         // other quoted escaped char
                         if (d_block.skip(yytext))
                             BEGIN block;
                         else           
                         {
                             BEGIN INITIAL;
                             d_number = yytext[2];
                             return Parser::QUOTE;
                         }
                     }
."'"                 {
                         if (d_block.skip(yytext))
                             BEGIN block;
                         else
                         {
                             BEGIN INITIAL;  // simple quoted constant
                             d_number = yytext[1];
                             return Parser::QUOTE;
                         }
                     }
[^']+"'"             {
                         if (d_block.skip(yytext))
                             BEGIN block;
                         else
                         {
                             lineMsg() << "multiple characters in "
                                         "quoted constant " << 
                                         yytext << err;
                             d_number = 0;
                             BEGIN INITIAL;
                             return Parser::QUOTE;
                         }
                     }
}

    /* 
        xstring returns the next string delimited by either blanks, tabs,
        newlines or C/C++ comment. Strings delimited by "..." are returned as
        STRING, strings delimited by <...> as PSTRING. If d_include was set
        before entering this mini scanner a file-switch is
        realized. Otherwise, the string is returned as a Parser::XSTRING
    */
<xstring>{
        
"\\".         yymore();

"//"          |
"/*"          {
                  yyless(yyleng - 2);

                  BEGIN (d_includeOnly ? includeOnly : INITIAL);

                  ret = yytextChk(&nKept, 1, Parser::XSTRING);
                  yyless(nKept);

                  if (ret)
                      return ret;
                
                  pushSource(YY_CURRENT_BUFFER, YY_BUF_SIZE);
              }
            
[ \t]*$       {
                  BEGIN (d_includeOnly ? includeOnly : INITIAL);
              
                  ret = yytextChk(&nKept, 1, Parser::XSTRING);
                  yyless(nKept);
                  if (ret)
                      return ret;

                  pushSource(YY_CURRENT_BUFFER, YY_BUF_SIZE);
              }

.           yymore();

}




%%

