/*  This file is part of mhmake.
 *
 *  Copyright (C) 2001-2009 Marc Haesen
 *
 *  Mhmake is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Mhmake is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Mhmake.  If not, see <http://www.gnu.org/licenses/>.
*/

/* $Rev$ */

/* -------------- declaration section -------------- */
%name mhmakelexer
%header{
#include "mhmakeparser.h"
#include "fileinfo.h"
#include "rule.h"
#include "util.h"

static uint32 LoadMakMd5(refptr<fileinfo> &Target)
{
  string FileName=Target->GetFullFileName();
  FileName+=".md5_32";
  FILE *pFile=fopen(FileName.c_str(),"rb");
  if (!pFile)
    return 0;
  uint32 Md5_32=0;
  fread(&Md5_32,sizeof(Md5_32),1,pFile);
  fclose(pFile);
  Target->SetCommandsMd5_32(Md5_32);
  return Md5_32;
}

static void SaveMakMd5(refptr<fileinfo> &Target)
{
  string FileName=Target->GetFullFileName();
  FileName+=".md5_32";
  FILE *pFile=fopen(FileName.c_str(),"wb");
  if (!pFile)
  {
    throw string("Error creating file ")+FileName;
  }
  Target->WriteMd5_32(pFile);
  fclose(pFile);
}

%}

%define LEX_PARAM TOKENVALUE &theValue
%define MEMBERS public: \
        struct INSTACK\
        {\
          YY_BUFFER_STATE m_BufferState;\
          string m_FileName;\
          int m_Line;\
          INSTACK(YY_BUFFER_STATE BufferState,string FileName,int Line) :\
            m_BufferState(BufferState),m_FileName(FileName),m_Line(Line) {}\
        };\
        int m_Line;\
        int m_BraceIndent;\
        size_t m_IndentSkip;\
        iterstack<int> m_IndentStack;\
        bool m_IgnoreIncludeError;\
        string m_InputFileName; \
        string m_curtoken; \
        iterstack<INSTACK> m_IncludeStack; \
        mhmakeparser *m_pParser;\
        mhmakeparser *GetParser()\
        {\
          return m_pParser;\
        }

%define CONSTRUCTOR_INIT : m_Line(1), m_BraceIndent(0)

/* -------------- rules section -------------- */
%x INCLUDE IFDEF IF IFNDEF SKIPUNTILELSEORENDIF QUOTE MAKEEXPRES SINGLEQUOTE COMMANDPARSE
%x IFEQ IFNEQ ERRORMACRO MESSAGEMACRO REPARSEMACRO LOAD_MAKEFILE

%%

 /*---------------------------------------------------------------------------*/
[ \t\r]*\n[ ][ \t]* |
[ \t\r]*\n {
  PRINTF(("%s %d: NEWLINE:\n",m_InputFileName.c_str(),m_Line));
  m_Line++;
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
^[s\-]?include {
  PRINTF(("%s %d: INCLUDE: ",m_InputFileName.c_str(),m_Line));
  BEGIN(INCLUDE);
  if (yytext[0]=='-' || yytext[0]=='s')
    m_IgnoreIncludeError=true;
  else
    m_IgnoreIncludeError=false;
  return mhmakeparser::INCLUDEMAK;  // Return a newline to be sure that the previous line is completely parse by yacc (in case it is a variable definition)
}

 /*****************************************************************************/
<INCLUDE>[ \t]*      /* eat the whitespace */
 /*---------------------------------------------------------------------------*/
<INCLUDE>[^\r\n]+ { /* got the include file name */
  mhmakeparser *pParser=GetParser();

  string IncludeFileNames=pParser->ExpandExpression((const char*)yytext);
  PRINTF(("%s -> %s\n",yytext,IncludeFileNames.c_str()));

  const char *pTmp=IncludeFileNames.c_str();
  while (*pTmp)
  {
    string IncludeFileName;
    pTmp=NextItem(pTmp,IncludeFileName);
    if (!IncludeFileName.empty())
    {
      PRINTF(("%s -> %s\n",yytext,IncludeFileName.c_str()));
      refptr<fileinfo> pInclude=GetFileInfo(IncludeFileName,pParser->GetMakeDir());
      /* Already build the include file, in case we already have a rule for it. */
      if (pInclude->GetRule())
      {
        uint32 Md5_32=LoadMakMd5(pInclude);
        pParser->BuildTarget(pInclude);
        if (!pInclude->CompareMd5_32(Md5_32))
          SaveMakMd5(pInclude);
      }

      pParser->AddIncludedMakefile(pInclude);

      string strToInclude=pInclude->GetFullFileName();
      FILE *pTmp = ::fopen(strToInclude.c_str(), "r" );
      if ( ! pTmp )
      {
        if (!m_IgnoreIncludeError)
        {
          iterstack<INSTACK>::reverse_iterator StackIt=m_IncludeStack.rbegin();
          while (StackIt!=m_IncludeStack.rend())
          {
            cout<<" in "<<StackIt->m_FileName<<" ("<<StackIt->m_Line<<")";
            StackIt++;
          }
          cout<<endl;
          cout<<"Warning error opening file "<<strToInclude<<" in "<<m_InputFileName<<" ("<<m_Line<<")\n";
          pParser->IncludeAfterBuild(strToInclude);
        }
      }
      else
      {
        m_IncludeStack.push(INSTACK(YY_mhmakelexer_CURRENT_BUFFER,m_InputFileName,m_Line));
        m_Line=1;

        yyin=pTmp;
        m_InputFileName=strToInclude;

        YY_mhmakelexer_SWITCH_TO_BUFFER(YY_mhmakelexer_CREATE_BUFFER( yyin, YY_BUF_SIZE ) );
      }

    }
  }

  BEGIN(INITIAL);
}

 /*---------------------------------------------------------------------------*/
load_makefile {
  PRINTF(("%s %d: LOAD_MAKEFILE:\n",m_InputFileName.c_str(),m_Line));
  BEGIN(LOAD_MAKEFILE);
  return mhmakeparser::NEWLINE;  // Return a newline to be sure that the previous line is completely parse by yacc (in case it is a variable definition)
}

 /*****************************************************************************/
<LOAD_MAKEFILE>[^\r\n]+ {
  string ListOfMakefiles=GetParser()->ExpandExpression((const char*)yytext);
  PRINTF(("%s %d: LOAD_MAKEFILE: '%s'\n",m_InputFileName.c_str(),m_Line,ListOfMakefiles.c_str()));

  const char *pTmp=ListOfMakefiles.c_str();
  while (*pTmp)
  {
    string Item;
    pTmp=NextCharItem(pTmp,Item,';');
    if (Item.empty())
    {
      throw m_InputFileName + "(" + stringify(m_Line) + "): Error in load_makefile statement";
    }
    GetParser()->AddMakefileToMakefilesToLoad(Item);
  }

}
 /*---------------------------------------------------------------------------*/
<LOAD_MAKEFILE>\r?\n {
  m_Line++;
  BEGIN(INITIAL);
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
[\ \t]+ {
  PRINTF(("%s %d: SPACE:\n",m_InputFileName.c_str(),m_Line));
  return mhmakeparser::SPACE;
}

 /*---------------------------------------------------------------------------*/
[\ t]*=[ \t]*\\[ \t\r]*\n[ \t]* {
  PRINTF(("%s %d: EQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  m_Line++;
  theValue.theString=(const char *)yytext;
  return mhmakeparser::EQUAL;
}

[\ t]*=[ \t]* {
  PRINTF(("%s %d: EQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::EQUAL;
}

 /*---------------------------------------------------------------------------*/
[\ t]*:=[ \t]*\\[ \t\r]*\n[ \t]* {
  m_Line++;
  PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::IMEQUAL;
}

[ \t]*:=[ \t]* {
  PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::IMEQUAL;
}

 /*---------------------------------------------------------------------------*/
[\ t]*\?=[ \t]*\\[ \t\r]*\n[ \t]* {
  m_Line++;
  PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::OPTEQUAL;
}

[ \t]*\?=[ \t]* {
  PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::OPTEQUAL;
}

 /*---------------------------------------------------------------------------*/
[\ t]*\+=[ \t]*\\[ \t\r]*\n[ \t]* {
  PRINTF(("%s %d: PEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  m_Line++;
  return mhmakeparser::PEQUAL;
}

[ \t]*\+=[ \t]* {
  PRINTF(("%s %d: PEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::PEQUAL;
}

 /*---------------------------------------------------------------------------*/
[\ t]*;[ \t]*\\[ \t\r]*\n[ \t]* {
  PRINTF(("%s %d: -SEMICOLON (NEWLINE): %s\n",m_InputFileName.c_str(),m_Line,yytext));
  m_curtoken=g_EmptyString;
  m_Line++;
  BEGIN(COMMANDPARSE);
  return mhmakeparser::NEWLINE;
}

[ \t]*;[ \t]* {
  PRINTF(("%s %d: -SEMICOLON (NEWLINE): %s\n",m_InputFileName.c_str(),m_Line,yytext));
  m_curtoken=g_EmptyString;
  BEGIN(COMMANDPARSE);
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
[\ t]*::[ \t]*\\[ \t\r]*\n[ \t]* {
  PRINTF(("%s %d: DOUBLECOLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  m_Line++;
  theValue.theString=(const char *)yytext;
  return mhmakeparser::DOUBLECOLON;
}

[ \t]*::[ \t]* {
  PRINTF(("%s %d: DOUBLECOLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::DOUBLECOLON;
}

 /*---------------------------------------------------------------------------*/
[\ t]*:[ \t]*\\[ \t\r]*\n[ \t]* {
  PRINTF(("%s %d: COLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  m_Line++;
  theValue.theString=(const char *)yytext;
  return mhmakeparser::COLON;
}

[ \t]*:[ \t]* {
  PRINTF(("%s %d: COLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::COLON;
}

 /*---------------------------------------------------------------------------*/
, {
  PRINTF(("%s %d: COMMA: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::COMMA;
}

 /*---------------------------------------------------------------------------*/
^[ \t]*endif {
  if (m_IndentStack.size())
  {
    m_IndentStack.pop();
    PRINTF(("%s %d: %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
  }
  else
  {
     throw string("Unexpected endif at line ") + stringify(m_Line) + " of " + m_InputFileName;
  }
}

 /*---------------------------------------------------------------------------*/
^[ \t]*ifdef[ \t]*\\[ \t\r]*\n[ \t]* {
  BEGIN(IFDEF);
  m_Line++;
  return mhmakeparser::NEWLINE;
}

^[ \t]*ifdef[ \t]+ {
  BEGIN(IFDEF);
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
^[ \t]*if[ \t]*\\[ \t\r]*\n[ \t]* {
  BEGIN(IF);
  m_Line++;
  return mhmakeparser::NEWLINE;
}

^[ \t]*if[ \t]+ {
  BEGIN(IF);
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
^[ \t]*ifndef[ \t]*\\[ \t\r]*\n[ \t]* {
  BEGIN(IFNDEF);
  m_Line++;
  return mhmakeparser::NEWLINE;
}

^[ \t]*ifndef[ \t]+ {
  BEGIN(IFNDEF);
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
^[ \t]*ifeq[ \t]*\\[ \t\r]*\n[ \t]* {
  BEGIN(IFEQ);
  m_curtoken=g_EmptyString;
  m_Line++;
  return mhmakeparser::NEWLINE;
}

^[ \t]*ifeq[ \t]+ {
  BEGIN(IFEQ);
  m_curtoken=g_EmptyString;
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
^[ \t]*ifneq[ \t]*\\[ \t\r]*\n[ \t]* {
  BEGIN(IFNEQ);
  m_curtoken=g_EmptyString;
  m_Line++;
  return mhmakeparser::NEWLINE;
}

^[ \t]*ifneq[ \t]+ {
  BEGIN(IFNEQ);
  m_curtoken=g_EmptyString;
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
^[ \t]*else[ \t]* {
  if (m_IndentStack.size() && (!m_IndentStack.top()))
  {
    PRINTF(("%s %d: skipping else: depth %d\n",m_InputFileName.c_str(),m_Line,m_IndentStack.size()));
    m_IndentSkip=m_IndentStack.size();
    m_IndentStack.top()=1;
    BEGIN(SKIPUNTILELSEORENDIF);
  }
  else
  {
     throw string("Unexpected else at line ") + stringify(m_Line) + " of file " + m_InputFileName;
  }
}

 /*****************************************************************************/
<IFEQ>\n {
  unput( yytext[0] );
  m_IndentStack.push(0);
  if (GetParser()->IsEqual(m_curtoken))
  {
    PRINTF(("%s %d: Not Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
    BEGIN(INITIAL);
  }
  else
  {
    PRINTF(("%s %d: Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
    m_IndentSkip=m_IndentStack.size();
    BEGIN(SKIPUNTILELSEORENDIF);
  }
}

<IFEQ,IFNEQ>[ \t]*\\[ \t\r]*\n[ \t]* { m_Line++; m_curtoken += g_SpaceString;}

<IFEQ,IFNEQ>\r    /* skip */

<IFEQ,IFNEQ>[^\\\r\n]+ |
<IFEQ,IFNEQ>\\                        { m_curtoken += (const char *)yytext; }

 /*****************************************************************************/
<IFNEQ>\n {
  unput( yytext[0] );
  m_IndentStack.push(0);
  if (!GetParser()->IsEqual(m_curtoken))
  {
    PRINTF(("%s %d: Not Skipping ifneq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
    BEGIN(INITIAL);
  }
  else
  {
    PRINTF(("%s %d: Skipping ifneq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
    m_IndentSkip=m_IndentStack.size();
    BEGIN(SKIPUNTILELSEORENDIF);
  }
}

 /*****************************************************************************/
<IF,IFDEF,IFNDEF>[ \t\r]*  /* skip */

 /*---------------------------------------------------------------------------*/
<IF>[a-zA-Z0-9_]+ {
  m_IndentStack.push(0);
  string Val=GetParser()->ExpandVar((const char *)yytext);
  if (Val.empty() || Val=="0")
  {
    PRINTF(("%s %d: Skipping if %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
    m_IndentSkip=m_IndentStack.size();
    BEGIN(SKIPUNTILELSEORENDIF);
  }
  else
  {
    PRINTF(("%s %d: Not Skipping if %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
    BEGIN(INITIAL);
  }
}

 /*---------------------------------------------------------------------------*/
<IFDEF>[a-zA-Z0-9_]+ {
  m_IndentStack.push(0);
  if (GetParser()->IsDefined((const char *)yytext))
  {
    PRINTF(("%s %d: Not Skipping ifdef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
    BEGIN(INITIAL);
  }
  else
  {
    PRINTF(("%s %d: Skipping ifdef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
    m_IndentSkip=m_IndentStack.size();
    BEGIN(SKIPUNTILELSEORENDIF);
  }
}

 /*****************************************************************************/
<IFNDEF>[a-zA-Z0-9_]+ {
  m_IndentStack.push(0);
  if (!GetParser()->IsDefined((const char *)yytext)) {
    PRINTF(("%s %d: Not Skipping ifndef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
    BEGIN(INITIAL);
  }
  else
  {
    PRINTF(("%s %d: Skipping ifndef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
    m_IndentSkip=m_IndentStack.size();
    BEGIN(SKIPUNTILELSEORENDIF);
  }
}

 /*****************************************************************************/
<SKIPUNTILELSEORENDIF>\n[ ]*endif {
  m_Line++;
  if (!m_IndentStack.size())
  {
    throw string("Unexpected endif at line ") + stringify(m_Line) + " of " + m_InputFileName;
  }
  else
  {
    m_IndentStack.pop();
    PRINTF(("%s %d: endif: depth %d\n",m_InputFileName.c_str(),m_Line,m_IndentStack.size()));
    if (m_IndentStack.size()==m_IndentSkip-1) BEGIN(INITIAL);
  }
}

 /*---------------------------------------------------------------------------*/
<SKIPUNTILELSEORENDIF>\n[ ]*else {
  m_Line++;
  PRINTF(("%s %d: else: depth %d\n",m_InputFileName.c_str(),m_Line,m_IndentStack.size()));
  if (m_IndentStack.top())
  {
    throw string("Unexpected else at line ") + stringify(m_Line) + " of file " + m_InputFileName;
  }
  m_IndentStack.top()=1;
  if (m_IndentStack.size()==m_IndentSkip)
  {
    BEGIN(INITIAL);
  }
}

 /*---------------------------------------------------------------------------*/
<SKIPUNTILELSEORENDIF>\n[ ]*if(def|ndef|eq|neq) {
  m_Line++;
  m_IndentStack.push(0);
  PRINTF(("%s %d: %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext+1,m_IndentStack.size()));
}

 /*---------------------------------------------------------------------------*/
<SKIPUNTILELSEORENDIF>[a-zA-Z]+    /* skip */
 /*---------------------------------------------------------------------------*/
<SKIPUNTILELSEORENDIF>[^a-zA-Z\n]+ /* skip */
 /*---------------------------------------------------------------------------*/
<SKIPUNTILELSEORENDIF>\n[ ]*[a-zA-Z]+ m_Line++;
 /*---------------------------------------------------------------------------*/
<SKIPUNTILELSEORENDIF>\n {
  m_Line++;
}

 /*---------------------------------------------------------------------------*/
[ \t]*#[^\n]* {
  PRINTF(("%s %d: -COMMENT: %s\n",m_InputFileName.c_str(),m_Line,yytext));
}

 /*---------------------------------------------------------------------------*/
[ \t]*\\[ \t\r]*\n[ \t]* {
  PRINTF(("%s %d: SPACE:\n",m_InputFileName.c_str(),m_Line));
  m_Line++;
  return mhmakeparser::SPACE;
}

 /*---------------------------------------------------------------------------*/
\.PHONY {
  PRINTF(("%s %d: .PHONY: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::PHONY;
}

 /*---------------------------------------------------------------------------*/
export {
  PRINTF(("%s %d: export: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::EXPORT;
}


 /*---------------------------------------------------------------------------*/
[a-zA-Z]:[a-zA-Z0-9\\\._\~\-%\@<&/]+\\[ \t\r]*\n {
  size_t EndIndex=::strlen((const char*)yytext);
  while (strchr(" \t\r\n\\",yytext[--EndIndex]));
  yyless(EndIndex+1);

  PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::STRING;
}

[a-zA-Z]:[a-zA-Z0-9\\\._\~\-%\@<&/]+ {
  PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::STRING;
}

 /*---------------------------------------------------------------------------*/
([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*]|\\\ |\\#)+\\[ \t\r]*\n  {
  size_t EndIndex=::strlen((const char*)yytext);
  while (strchr(" \t\r\n\\",yytext[--EndIndex]));
  yyless(EndIndex+1);

  PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::STRING;
}

([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*]|\\\ |\\#)+\+=  {
  size_t Len;
  PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  Len=strlen((const char *)yytext)-2;
  yyless(Len);
  theValue.theString=(const char *)yytext;
  return mhmakeparser::STRING;
}

([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*]|\\\ |\\#)+  {
  PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::STRING;
}

 /*---------------------------------------------------------------------------*/
\"  {
  BEGIN(QUOTE);
  yymore();
}

 /*---------------------------------------------------------------------------*/
\' {
  BEGIN(SINGLEQUOTE);
  yymore();
}

 /*---------------------------------------------------------------------------*/
\$\(  {
  m_BraceIndent++;
  PRINTF(("%s %d: BEGIN MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  BEGIN(MAKEEXPRES);
  yymore();
}

 /*---------------------------------------------------------------------------*/
\$\([ \t]*error[ \t]+ {
  m_BraceIndent++;
  PRINTF(("%s %d: BEGIN ERROR MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  BEGIN(ERRORMACRO);
  m_curtoken=g_EmptyString;
}

 /*---------------------------------------------------------------------------*/
\$\([ \t]*message[ \t]+ {
  m_BraceIndent++;
  PRINTF(("%s %d: BEGIN MESSAGE MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  BEGIN(MESSAGEMACRO);
  m_curtoken=g_EmptyString;
}

 /*---------------------------------------------------------------------------*/
\$\([ \t]*reparse[ \t]+ {
  m_BraceIndent++;
  PRINTF(("%s %d: BEGIN REPARSE MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  BEGIN(REPARSEMACRO);
  m_curtoken=g_EmptyString;
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
\(   {
  PRINTF(("%s %d: OPENBRACE: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::OPENBRACE;
}

 /*---------------------------------------------------------------------------*/
\)   {
  PRINTF(("%s %d: CLOSEBRACE: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  return mhmakeparser::CLOSEBRACE;
}

 /*---------------------------------------------------------------------------*/
\$[<@/$] {
  PRINTF(("%s %d: DOLLAREXPR: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  theValue.theString=(const char *)yytext;
  return mhmakeparser::DOLLAREXPR;
}

 /*---------------------------------------------------------------------------*/
[ \t\r]*\n\t[ \t]* {
  /* token newline */
  PRINTF(("%s %d: NEWLINE\n",m_InputFileName.c_str(),m_Line));
  m_Line++;
  m_curtoken=g_EmptyString;
  BEGIN(COMMANDPARSE);
  return mhmakeparser::NEWLINE;
}

 /*---------------------------------------------------------------------------*/
[^\n] {
  PRINTF(("%s %d: ANYCHAR: %d: %s\n",m_InputFileName.c_str(),m_Line,m_Line,yytext));
}

 /*****************************************************************************/

<COMMANDPARSE>[ \t\r]*\n {
  PRINTF(("%s %d: COMMAND: %d: %s\n",m_InputFileName.c_str(),m_Line,m_Line,m_curtoken.c_str()));
  theValue.theString=m_curtoken;
  m_Line++;
  BEGIN(INITIAL);
  return mhmakeparser::COMMAND;
}

 /*---------------------------------------------------------------------------*/
<COMMANDPARSE>[ \t\r]*\n\t[ \t]* {
  PRINTF(("%s %d: COMMAND: %s\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str()));
  theValue.theString=m_curtoken;
  m_Line++;
  m_curtoken=g_EmptyString;
  return mhmakeparser::COMMAND;
}

 /*---------------------------------------------------------------------------*/
<COMMANDPARSE>[ \t]*\\[ \t\r]*\n[ \t]* {
  m_Line++;
  m_curtoken+=g_SpaceString;
}

 /*---------------------------------------------------------------------------*/
<COMMANDPARSE>[ ]+ {
  m_curtoken+=g_SpaceString;
}

 /*---------------------------------------------------------------------------*/
<COMMANDPARSE>[^ \r\n#\\]+ |
<COMMANDPARSE>\\ {
  m_curtoken+=(const char *)yytext;
}

 /*---------------------------------------------------------------------------*/
<COMMANDPARSE>[ \t]*\\#[^\n]* {
  char ToAdd[100];
  int i=0;
  int nChars=(int)((strchr((const char *)yytext,'#')-(char*)yytext))+1;
  while (strchr(" \t",yytext[i]))
  {
    ToAdd[i]=yytext[i];
    i++;
  }
  ToAdd[i++]='#';
  ToAdd[i++]=0;
  m_curtoken+=ToAdd;
  yyless(nChars);
}

 /*---------------------------------------------------------------------------*/
<COMMANDPARSE>[ \t]*#[^\n]* {
  PRINTF(("%s %d: -COMMENT: %s\n",m_InputFileName.c_str(),m_Line,yytext));
}

 /*****************************************************************************/
<QUOTE>\" {
  PRINTF(("%s %d: QUOTEDSTRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  BEGIN(INITIAL);
  theValue.theString=(const char *)yytext;
  return mhmakeparser::STRING;
}

 /*---------------------------------------------------------------------------*/
<QUOTE>\r /* skip */

<QUOTE>[^\\\"\r\n]+  |
<QUOTE>\\            |
<QUOTE>\\\"          |
<QUOTE>\\#  {
  yymore();
}

 /*****************************************************************************/
<SINGLEQUOTE>\' {
  PRINTF(("%s %d: QUOTEDSTRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
  BEGIN(INITIAL);
  theValue.theString=(const char *)yytext;
  return mhmakeparser::STRING;
}

 /*---------------------------------------------------------------------------*/
<SINGLEQUOTE>\r /* skip */

<SINGLEQUOTE>[^\\\'\r\n]+ |
<SINGLEQUOTE>\\           |
<SINGLEQUOTE>\\\'         |
<SINGLEQUOTE>\\# {
  yymore();
}

 /*****************************************************************************/
<ERRORMACRO>\) {
  m_BraceIndent--;
  PRINTF(("%s %d: CLOSE BRACE ERROR MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  if (!m_BraceIndent)
  {
    PRINTF(("%s %d: ERRORMACRO: %s\n",m_InputFileName.c_str(),m_Line,yytext));
    yyless(0);
    throw GetParser()->ExpandExpression((const char*)yytext);
  } else {
    yymore();
  }
}

 /*****************************************************************************/
<MESSAGEMACRO>\) {
  m_BraceIndent--;
  PRINTF(("%s %d: CLOSE BRACE MESSAGE MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  if (!m_BraceIndent)
  {
    PRINTF(("%s %d: MESSAGEMACRO: %s\n",m_InputFileName.c_str(),m_Line,yytext));
    yytext[strlen((const char*)yytext)-1]=0;
    cerr<<GetParser()->ExpandExpression((const char*)yytext)<<endl;
    BEGIN(INITIAL);
  } else {
    yymore();
  }
}

 /*****************************************************************************/
<REPARSEMACRO>\) {
  m_BraceIndent--;
  PRINTF(("%s %d: CLOSE BRACE REPARSE MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  if (!m_BraceIndent)
  {
    PRINTF(("%s %d: REPARSEMACRO: %s\n",m_InputFileName.c_str(),m_Line,yytext));
    string Deps=GetParser()->ExpandExpression((const char*)yytext);
    PRINTF(("%s %d: REPARSEMACRO expanded: %s\n",m_InputFileName.c_str(),m_Line,Deps.c_str()));
    string::const_reverse_iterator It=Deps.rbegin()+1; // +1 because we don't want the latest brace
    string::const_reverse_iterator ItBeg=Deps.rend();
    while (It!= ItBeg)
    {
      char Char=*It++;
      if (Char==';') Char='\n';
      unput(Char);
    }
    BEGIN(INITIAL);
  }
  else
  {
    yymore();
  }
}

 /*****************************************************************************/
<MAKEEXPRES>\) {
  m_BraceIndent--;
  PRINTF(("%s %d: CLOSE BRACE MAKEEXPRES MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  if (!m_BraceIndent)
  {
    PRINTF(("%s %d: DOLLAREXPR: %s\n",m_InputFileName.c_str(),m_Line,yytext));
    BEGIN(INITIAL);
    theValue.theString=(const char *)yytext;
    return mhmakeparser::DOLLAREXPR;
  }
  else
  {
    yymore();
  }
}

 /*---------------------------------------------------------------------------*/
<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\$\( {
  m_BraceIndent++;
  PRINTF(("%s %d: MACRO extra $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
  yymore();
}

 /*---------------------------------------------------------------------------*/
<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>[^$\(\)\r\n]+ |
<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\$        |
<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\( {
  yymore();
}
<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\r?\n {
  m_Line++;
}

<SKIPUNTILELSEORENDIF><<EOF>> {
  throw string("Missing endif or else statement. #else or #endif used?");
}

<<EOF>> {
  if (m_BraceIndent)
  {
    throw string("Missing closing ) of macro usage in ") + m_InputFileName;
  }
  if (!m_IncludeStack.size())
  {
    if (m_IndentStack.size())
    {
      throw string("Missing endif or else statement in ") + m_InputFileName + ". #else or #endif used";
    }
    yyterminate();
  }
  else
  {
    ::fclose(YY_mhmakelexer_CURRENT_BUFFER->yy_input_file);
    YY_mhmakelexer_DELETE_BUFFER(YY_mhmakelexer_CURRENT_BUFFER);
    INSTACK *pInStack=&m_IncludeStack.top();
    YY_mhmakelexer_SWITCH_TO_BUFFER(pInStack->m_BufferState);
    m_InputFileName=pInStack->m_FileName;
    m_Line=pInStack->m_Line;
    m_IncludeStack.pop();
  }
}
%%