logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

follow us in feedly

Fontes do MidlParser

A função que dá início à interpretação do arquivo é a MidlParser::ParseMidlFile

CodeEntities.h
#pragma once

struct IdlAttribute
{
   IdlAttribute(const std::string& name, const std::string& value) : Name(name), Value(value) {}
   IdlAttribute(const std::string& name) : Name(name){}

   std::string Name;
   std::string Value;
};

struct IdlParameter
{
   IdlParameter(const std::string& type, const std::string& name) : Type(type), Name(name), IndirectionLevel(0) {}
   IdlParameter() : IndirectionLevel(0) {}

   typedef std::vector<IdlAttribute> AttributesContainer;
   typedef AttributesContainer::iterator AttributesIterator;
   typedef AttributesContainer::const_iterator ConstAttributesIterator;

   AttributesContainer Attributes;
   
   std::string Type;
   std::string Name;
   unsigned int IndirectionLevel;
};

struct IdlMethod
{
   IdlMethod(const std::string& name, const std::string& returnType) : Name(name), ReturnType(returnType) {}
   IdlMethod() {}

   typedef std::vector<IdlAttribute> AttributesContainer;
   typedef AttributesContainer::iterator AttributesIterator;
   typedef AttributesContainer::const_iterator ConstAttributesIterator;

   typedef std::vector<IdlParameter> ParametersContainer;
   typedef ParametersContainer::iterator ParametersIterator;
   typedef ParametersContainer::const_iterator ConstParametersIterator;


   AttributesContainer Attributes;
   std::string ReturnType;
   std::string Name;
   ParametersContainer Parameters;
};

struct IdlInterface
{
   IdlInterface(const std::string& name) : Name(name) {}
   IdlInterface() {}

   typedef std::vector<IdlAttribute> AttributesContainer;
   typedef AttributesContainer::iterator AttributesIterator;
   typedef AttributesContainer::const_iterator ConstAttributesIterator;

   typedef std::vector<IdlMethod> MethodsContainer;
   typedef MethodsContainer::iterator MethodsIterator;
   typedef MethodsContainer::const_iterator ConstMethodsIterator;

   AttributesContainer Attributes;
   std::string Name;
   std::string InheritsFrom;
   MethodsContainer Methods;
};

//
// functor
//
class IdlAttributeFindByName
{
private:
   std::string m_strName;
   //
   // Deve ser um shared_ptr pq um functor STL é passado como valor, e não como referência.
   // Se eu usar auto_ptr, na primeira cópia o ownership do ponteiro vao para a cópia e
   // ficamos sem nada...
   //
   boost::shared_ptr<std::vector<std::string> > m_MoreAttributesToFind;
public:
   IdlAttributeFindByName(const std::string& name)
      : m_strName(name)
   {}

   IdlAttributeFindByName()
   {}

   void AddAttributeToFind(const std::string& str)
   {
      if(m_MoreAttributesToFind.get() == NULL)
         m_MoreAttributesToFind = boost::shared_ptr<std::vector<std::string> >(new std::vector<std::string>());

      m_MoreAttributesToFind->push_back(str);
   }

   bool operator()(const IdlAttribute& att)
   {
      if(m_MoreAttributesToFind.get() == NULL)
         return att.Name == m_strName;
      else
      {
         return att.Name == m_strName || 
            std::find(m_MoreAttributesToFind->begin(), m_MoreAttributesToFind->end(), att.Name) != m_MoreAttributesToFind->end();
      }

   }
};

MidlParser.h
#pragma once

//
// MidlParser
// 
// Faz o parser de um arquivo IDL
//
class MidlParser
{
public:

   class ParseException : public std::exception
   {
   private:
      unsigned int m_line;
      string m_fileName;
   public:
      ParseException(const string& what, MidlParser& parser, const string& fileName) 
         : std::exception(what.c_str()), 
           m_line(parser.GetCurrentLine()),
           m_fileName(fileName)
      {
      }

      void set_line(unsigned int line)
      {
         m_line = line;
      }

      unsigned int get_line() const
      {
         return m_line;
      }

      void set_fileName(const string& fileName)
      {
         m_fileName = fileName;
      }

      const string& get_fileName() const
      {
         return m_fileName;
      }
   };

private:

   bool m_bParseAsImportFile;

   string m_parsedFileName;

   typedef tokenizer<char_separator<char> > TokenizerMidl;
   vector<string> m_KnownInterfaces;
   vector<string> m_KnownTypes;
   map<string, IdlInterface> m_ParsedInterfaces;

   vector<string> m_IncludePaths;

   vector<string> m_ParsedIncludeFiles;

   TokenizerMidl m_Tokenizer;
   TokenizerMidl::iterator m_TokenIterator;
   std::string m_strInvalidToken;

   TokenizerMidl::iterator m_LineCountIterator;
   mutable unsigned int m_uiCurrentLine;

#ifdef _DEBUG
   //
   // isso facilita a visualização em debug
   //
   std::string m_strCurrentToken;
   const char* m_szCurrentToken;
   unsigned int m_uiBreakOnLine;
#endif

   void Log(const string& msg);

   //
   // verifica se é um identificador válido
   //
   const string& Identifier(const string& token);
   const string& NextTokenAsIdentifier();
   const string& CurrentTokenAsIdentifier();

   void CheckIsExpectedToken(const string& str, const string& checked, const char* expected_name = NULL);
   void CheckExpectedNextToken(const std::string expected, const char* expected_name = NULL);
   void CheckExpectedCurrentToken(const std::string expected, const char* expected_name = NULL);
   void CheckNotExpectedToken(const string& str, const string& checked, const char* not_expected_name = NULL);
   void CheckNotExpectedNextToken(const std::string not_expected, const char* not_expected_name = NULL);
   void CheckNotExpectedCurrentToken(const std::string not_expected, const char* not_expected_name = NULL);
   
   void CheckType(const string& str, unsigned int indirectionLevel, bool mustBeInterface = false);
   
   const string& NextTokenAsType(unsigned int indirectionLevel = 0, bool mustBeInterface = false);   
   const string& CurrentTokenAsType(unsigned int indirectionLevel = 0, bool mustBeInterface = false);

   void AddParsedInterface(const IdlInterface& iface);
   void AddKnownInterfaceName(const string& interfaceName);


   unsigned int GetCurrentLine();


   const std::string& NextToken(bool checkEndOfFile = true, bool skipComments = true);
   const std::string& CurrentToken(bool checkEndOfFile = true, bool skipComments = true);

   void SkipUntilToken(const string& token);
   void SkipQuote();
   void SkipUntilClose(const string& open_token, const string& close_token);

   void LoadKnownInterfaces();
   void LoadKnownTypes();

   void ParseMethod(IdlMethod* method);
   void ParseInterface(IdlInterface* iface);
   void ParseAttributes(vector<IdlAttribute>* attributes);
   void ParseImportFile(const string& fileName);

   bool IsEndOfFile();

   bool OpenIncludeFile(const string& fileName, fstream* f, string* fileNameWithPath);
   

public:

   MidlParser();
   void AddFindPath(const string& path);
   void ParseMidlFile(const char* fileName);

   const std::map<string, IdlInterface>& GetParsedInterfaces() const;
   const std::vector<string>& GetKnownInterfaces() const;

   void Dump(ostream& s) const;
   
private:
   void AddKnownType(const string& t);
};


MidlParser.cpp
#pragma once
#include "stdafx.h"

#include "CodeEntities.h"
#include "MidlParser.h"


//
// verifica se é um identificador válido
//
const string& MidlParser::Identifier(const string& token)
{
   bool isValid = true;

   if(token.size() == 0 || (!std::isalpha(token[0]) && token[0] != '_'))
      isValid = false;
   else
   {
      for(string::const_iterator i = token.begin() ; i != token.end() ; ++i)
      {
         char c = *i;
         if(!std::isalpha(c) && !std::isalnum(c) && c != '_')
         {
            isValid = false;
            break;
         }
      }
   }

   if(!isValid)
      throw ParseException("invalid identifier: "" + token + """, *this, m_parsedFileName);

   return token;
}

const string& MidlParser::NextTokenAsIdentifier()
{
   return Identifier(NextToken());
}

const string& MidlParser::CurrentTokenAsIdentifier()
{
   return Identifier(CurrentToken());
}

void MidlParser::CheckIsExpectedToken(const string& str, const string& checked, const char* expected_name)
{
   if(str != checked)
   {
      string ex("expected: ");

      if(expected_name)
         ex += expected_name;
      else
      {
         ex += """;
         ex += checked;
         ex += """;
      }

      throw ParseException(ex, *this, m_parsedFileName);
   }
}

void MidlParser::CheckExpectedNextToken(const std::string expected, const char* expected_name)
{
   CheckIsExpectedToken(NextToken(), expected, expected_name);
}

void MidlParser::CheckExpectedCurrentToken(const std::string expected, const char* expected_name)
{
   CheckIsExpectedToken(CurrentToken(), expected, expected_name);
}

void MidlParser::CheckNotExpectedToken(const string& str, const string& checked, const char* not_expected_name)
{
   if(str == checked)
   {
      string ex("not expected: ");

      if(not_expected_name)
         ex += not_expected_name;
      else
      {
         ex += """;
         ex += checked;
         ex += """;
      }

      throw ParseException(ex, *this, m_parsedFileName);
   }
}

void MidlParser::CheckNotExpectedNextToken(const std::string not_expected, const char* not_expected_name)
{
   CheckNotExpectedToken(NextToken(), not_expected, not_expected_name);
}

void MidlParser::CheckNotExpectedCurrentToken(const std::string not_expected, const char* not_expected_name )
{
   CheckNotExpectedToken(CurrentToken(), not_expected, not_expected_name);
}


void MidlParser::CheckType(const string& str, unsigned int indirectionLevel, bool mustBeInterface )
{

   if(std::find(m_KnownInterfaces.begin(), m_KnownInterfaces.end(), str) == m_KnownInterfaces.end())
   {
      if(mustBeInterface == true)
         throw ParseException("Unknwon Type: "" + str + """, *this, m_parsedFileName);
   }
   else
      return;


   if(std::find(m_KnownTypes.begin(), m_KnownTypes.end(), str) == m_KnownTypes.end())
      throw ParseException("Unknwon Type: "" + str + """, *this, m_parsedFileName);
}

const string& MidlParser::NextTokenAsType(unsigned int indirectionLevel, bool mustBeInterface)
{
   CheckType(NextToken(), indirectionLevel, mustBeInterface);
   return CurrentToken();
}

const string& MidlParser::CurrentTokenAsType(unsigned int indirectionLevel, bool mustBeInterface)
{
   CheckType(CurrentToken(), indirectionLevel, mustBeInterface);
   return CurrentToken();
}

void MidlParser::AddKnownInterfaceName(const string& interfaceName)
{
   if(std::find(m_KnownInterfaces.begin(), m_KnownInterfaces.end(), interfaceName) == m_KnownInterfaces.end())
      m_KnownInterfaces.push_back(interfaceName);
}


void MidlParser::AddParsedInterface(const IdlInterface& iface)
{
   m_ParsedInterfaces.insert(make_pair<string, IdlInterface>(iface.Name, iface));
   m_KnownInterfaces.push_back(iface.Name); 
   AddKnownType(iface.Name);
}



unsigned int MidlParser::GetCurrentLine()
{
   for( ; m_LineCountIterator != m_TokenIterator ; ++m_LineCountIterator)
      if(*m_LineCountIterator == "n")
         m_uiCurrentLine++;

   return m_uiCurrentLine;
}


const std::string& MidlParser::NextToken(bool checkEndOfFile, bool skipComments)
{
   ++m_TokenIterator;

   return CurrentToken(checkEndOfFile, skipComments);
}

const std::string& MidlParser::CurrentToken(bool checkEndOfFile, bool skipComments)
{
   if(IsEndOfFile())
   {
      if(checkEndOfFile)
         throw ParseException("unexpected end of file", *this, m_parsedFileName);
      else
         return m_strInvalidToken;
   }

   if(skipComments)
   {
      for(;;)
      {
         if(*m_TokenIterator != "/" && *m_TokenIterator != "n")
            break;
         //
         // pula os comentários
         //
         while(*m_TokenIterator == "/")
         {
            TokenizerMidl::iterator next = m_TokenIterator;
            next++;

            if(*next == "/")
            {
               while(m_TokenIterator != m_Tokenizer.end() && *++m_TokenIterator != "n");
               
               break;
            }
            else if(*next == "*")
            {
               ++m_TokenIterator;

               for(;;)
               {
                  ++m_TokenIterator;

                  if(IsEndOfFile())
                  {
                     if(checkEndOfFile)
                        throw ParseException("unexpected end of file", *this, m_parsedFileName);
                     else
                        return m_strInvalidToken;
                  }

                  const string& token = *m_TokenIterator;

                  if(token == "*")
                  {
                     ++m_TokenIterator;

                     if(IsEndOfFile())
                     {
                        if(checkEndOfFile)
                           throw ParseException("unexpected end of file", *this, m_parsedFileName);
                        else
                           return m_strInvalidToken;
                     }

                     if(*m_TokenIterator == "/")
                     {
                        ++m_TokenIterator;
                        break;
                     }
                  }
               }
            }
            else
               break;
         }

         //
         // pula os n
         //
         while(++m_TokenIterator != m_Tokenizer.end() && *m_TokenIterator == "n");

         if(IsEndOfFile())
         {
            if(checkEndOfFile)
               throw ParseException("unexpected end of file", *this, m_parsedFileName);
            else
               return m_strInvalidToken;
         }
      }
   }



#ifdef _DEBUG
   //
   // isso facilita a visualização em debug
   //
   m_strCurrentToken = *m_TokenIterator;
   m_szCurrentToken = m_strCurrentToken.c_str();

   if(GetCurrentLine() >= m_uiBreakOnLine)
   {
      m_uiBreakOnLine = 0xFFFFFFFF;
      __asm int 3;
   }

#endif

   return *m_TokenIterator;
}

void MidlParser::SkipUntilToken(const string& token)
{
   std::find(m_TokenIterator, m_Tokenizer.end(), token);
}

void MidlParser::SkipQuote()
{
   bool stringOpened = false;
   for(;;)
   {
      const string& token = NextToken(true, false);
      
      if(token == """)
      {
         if(stringOpened)
            return;
         else
         {
            stringOpened = true;
            continue;
         }
      }

      //
      // pular escape de aspa
      //
      if(token == "" && NextToken(true, false) == """)
         NextToken(true, false);
   }
}

void MidlParser::SkipUntilClose(const string& open_token, const string& close_token)
{
   unsigned int deep = 0;

   for(;;)
   {
      if(CurrentToken() == open_token)
      {
         deep++;
      }
      else if(CurrentToken() == close_token)
      {
         deep--;

         if(deep == 0)
            break;
      }

      NextToken();
   }
}

void MidlParser::ParseMethod(IdlMethod* method)
{
   if(CurrentToken() == "[")
      ParseAttributes(&method->Attributes);

   method->ReturnType = CurrentTokenAsIdentifier();
   method->Name = NextTokenAsIdentifier();

   CheckExpectedNextToken("(");

   if(NextToken() != ")")
   {
      for(unsigned int uiCurrentParameter = 0 ; ; ++uiCurrentParameter)
      {
         IdlParameter param;

         //
         // vamos ver se é atributos
         //
         if(CurrentToken() == "[")
         {
            ParseAttributes(&param.Attributes);
         }

         param.Type = CurrentTokenAsIdentifier();
         
         while(NextToken() == "*")
            param.IndirectionLevel++;

         //
         // tratar sintaxe Metodo(void);
         //
         if(param.Type == "void" && CurrentToken() == ")")
         {
            break;
         }

         CheckType(param.Type, param.IndirectionLevel);

         param.Name = CurrentTokenAsIdentifier();

         method->Parameters.push_back(param);

         if(NextToken() == ")")
         {
            break;
         }

         if(CurrentToken() == ",")
         {
            NextToken();
            continue;
         }

         throw ParseException("expected: "," or ")"", *this, m_parsedFileName);
      }
   }

   NextToken();
   CheckExpectedCurrentToken(";");
   NextToken();
}

void MidlParser::ParseInterface(IdlInterface* iface)
{
   assert(CurrentToken() == "interface");

   iface->Name = NextTokenAsIdentifier();

   //
   // herança de interface
   //
   if(NextToken() == ":")
   {
      CheckNotExpectedNextToken("{", "interface name");

      iface->InheritsFrom = CurrentTokenAsType();

      CheckType(iface->InheritsFrom, 0, true);

      NextToken();
   }

   CheckExpectedCurrentToken("{");

   NextToken();

   //
   // temos que colocar a própria interface na lista de tipos
   // conhecidos, já que um método pode receber um ponteiro
   // do próprio tipo
   //
   AddKnownType(iface->Name);

   

   //
   // métodos
   //
   if(CurrentToken() != "}")
   {
      for(;;)
      {
         IdlMethod method;

         ParseMethod(&method);

         iface->Methods.push_back(method);

         //
         // fim da interface
         //
         if(CurrentToken() == "}")
            break;
      }
   }

   CheckExpectedNextToken(";");
   NextToken();
}

void MidlParser::LoadKnownInterfaces()
{
   //
   // essa maravilha de sintaxe é graças ao boost::assign
   //
   m_KnownInterfaces.clear();
   m_KnownInterfaces += "IUnknown", "IDispatch";
   return;
}

void MidlParser::LoadKnownTypes()
{
   //
   // por enquanto vou colocar os defines tb, para facilitar. Quando
   // isso tiver um pré-processador isso deve ser tirado
   //
   m_KnownTypes.clear();
   m_KnownTypes += "BSTR", "VARIANT", "DWORD", "LONG", "ULONG", "GUID", "REFGUID", 
                   "HRESULT", "BOOL", "LPVOID", "REFCLSID", "WCHAR", "REFIID", "VOID", "void", 
                   "VARIANT_BOOL", "USHORT", "int", "short", "UCHAR";

}

void MidlParser::ParseAttributes(vector<IdlAttribute>* attributes)
{
   std::stringstream buffer;

   assert(CurrentToken() == "[");

   for(;;)
   {
      if(CurrentToken() == "]")
         break;

      IdlAttribute att(NextTokenAsIdentifier());

      //
      // se for vírgula, acabou esse
      //
      if(NextToken() == ",")
      {
         attributes->push_back(att);
         continue;
      }

      //
      // se abre parenteses, tem valor
      //
      if(CurrentToken() == "(")
      {
         if(NextToken() == ")")
            throw ParseException((boost::format("Attribute %1% has null value") % CurrentToken()).str(), *this, m_parsedFileName);

         //
         // vamos encontrar o fim do parênteses
         //
         buffer.clear();

         while(CurrentToken() != ")")
         {
            buffer << CurrentToken();

            NextToken();
         }

         NextToken();

         att.Value = buffer.str();
      }

      attributes->push_back(att);
   }

   NextToken();
}

void MidlParser::Log(const string& msg)
{
   cerr << msg << endl;
}


bool MidlParser::IsEndOfFile()
{
   return m_TokenIterator == m_Tokenizer.end();
}

bool MidlParser::OpenIncludeFile(const string& fileName, fstream* f, string* fileNameWithPath)
{
   for(vector<string>::const_iterator i = m_IncludePaths.begin() ; i != m_IncludePaths.end() ; ++i)
   {
      string str;
      
      f->close();
      f->clear();

      str = *i + fileName;

      f->open(str.c_str(), ios::in);
      
      if(f->good())
      {
         if(fileNameWithPath)
            *fileNameWithPath = str;

         return true;
      }
   }
   return true;
}


void MidlParser::ParseImportFile(const string& fileName)
{
   fstream f;
   string str;

   //
   // deve ter extensão IDL e não ter sido interpretado ainda
   //
   if(fileName.length() < 5 || 
      fileName.substr(fileName.size() - 4, 4) != ".idl" ||
      std::find(m_ParsedIncludeFiles.begin(), m_ParsedIncludeFiles.end(), fileName) != m_ParsedIncludeFiles.end())
      return;

   MidlParser ImportParser;
   
   ImportParser.m_bParseAsImportFile = true;

   ImportParser.m_KnownTypes = m_KnownTypes;
   ImportParser.m_KnownInterfaces = m_KnownInterfaces;
   ImportParser.m_ParsedIncludeFiles = m_ParsedIncludeFiles;


   ImportParser.ParseMidlFile(fileName.c_str());

   m_KnownTypes = ImportParser.m_KnownTypes;
   m_KnownInterfaces = ImportParser.m_KnownInterfaces;
   m_ParsedIncludeFiles = ImportParser.m_ParsedIncludeFiles;
}



//
// 
// PUBLIC
// 
//

MidlParser::MidlParser() : 
   m_Tokenizer(std::string()),
   m_bParseAsImportFile(false)
{
#ifdef _DEBUG
   m_uiBreakOnLine = 0xFFFFFFFF;
#endif

   AddFindPath("C:Program FilesMicrosoft SDKinclude");
}


void MidlParser::ParseMidlFile(const char* fileName)
{
   fstream f;
   string str;
   std::vector<IdlAttribute> attributes;
 
   if(m_bParseAsImportFile)
   {
      if(!OpenIncludeFile(fileName, &f, &m_parsedFileName))
         throw ParseException(string("error opening import file "") + fileName + """, *this, m_parsedFileName);

      getline(f, str, f.widen(EOF));

      m_Tokenizer.assign(str,char_separator<char>("\t\r " , "\n\"*,;:{}/[]()"));

      m_ParsedIncludeFiles.push_back(fileName);

      Log(string("Parsing import file ") + fileName);

   }
   else
   {
      f.open(fileName, ios::in);

      if(f.fail())
         throw ParseException(string("error opening file "") + fileName + """, *this, m_parsedFileName);
   
      getline(f, str, f.widen(EOF));
      m_Tokenizer.assign(str,char_separator<char>("\t\r " , "\n\"*,;:{}/[]()"));

      m_parsedFileName = fileName;

      LoadKnownInterfaces();
      LoadKnownTypes();

   }

   
   m_TokenIterator = m_Tokenizer.begin();
   m_LineCountIterator = m_TokenIterator;
   m_uiCurrentLine = 1;

   for(;;)
   {
      const string token = CurrentToken(false);

      if(IsEndOfFile())
         break;

      //
      // início de atributos
      //
      if(token == "[")
      {
         if(m_bParseAsImportFile)
         {
            SkipUntilClose("[", "]");
            continue;
         }

         attributes.clear();

         ParseAttributes(&attributes);

         //
         // se não for atributo de uma interface vamos ignorar. 
         // Acho melhor do que verificar antes se é uma interface e ter que "rebobinar"
         // o iterator (o que, na realidade, não é possível pq o iterator é forward-only)
         //
         if(CurrentToken() != "interface")
            attributes.clear();
      }
      else if(token == "cpp_quote")
      {
         SkipQuote();
         CheckExpectedNextToken(")");
      }
      else if(token == "coclass")
      {
         SkipUntilClose("{", "}");
         NextToken();
      }
      else if(token == "import")
      {
         stringstream stm;
         
         CheckExpectedNextToken(""");
         
         while(NextToken(true, false) != """)
            stm << CurrentToken(true, false);

         ParseImportFile(stm.str());
         
         CheckExpectedNextToken(";");
         NextToken();
      }
      else if(token == "#define")
      {
         for(;;)
         {
            if(NextToken(true, false) == "n")
               break;

            //
            // se for  vamos pular um token, pq se o próximo
            // for um n ele será pulado. Senão não tem efeito mesmo...
            //
            if(CurrentToken(true, false) == "")
               NextToken(true, false);
         }

      }
      else if(token == "interface")
      {
         if(m_bParseAsImportFile)
         {
            AddKnownInterfaceName(NextTokenAsIdentifier());
            
            if(NextToken() != ";")
               SkipUntilClose("{", "}");

            continue;
         }

         IdlInterface iface;

         if(attributes.empty())
         {
            //
            // se não tem atributos TEM QUE SER um forward declaration
            //
            string name;

            name = NextTokenAsIdentifier();

            CheckExpectedNextToken(";");

            NextToken();

            m_KnownInterfaces.push_back(name);

            continue;
         }

         //
         // coloca os atributos encontrados na interface
         //
         iface.Attributes = attributes;
         attributes.clear();

         ParseInterface(&iface);

         AddParsedInterface(iface);
      }
      else if(token == "enum")
      {
         //
         // por enquanto só vamos pegar o nome do enum para considerarmos
         // como um tipo válido
         //
         SkipUntilClose("{", "}");
         
         if(NextToken() != ";")
         {
            AddKnownType(CurrentTokenAsIdentifier());
            CheckExpectedNextToken(";");
         }
         NextToken();
      }
      else
      {
         NextToken(false);
      }
   }

   if(!m_bParseAsImportFile)
   {
      cout << "Finish (" << g_dwIoTime << " / " << dwTickCount << " / " << std::fixed << percent << ")" << endl;
   }

   return;
}


void MidlParser::AddFindPath(const string& path)
{
   if(std::find(m_IncludePaths.begin(), m_IncludePaths.end(), path) == m_IncludePaths.end())
   {
      if(*path.rbegin() != '')
         m_IncludePaths.push_back(path + "");
      else
         m_IncludePaths.push_back(path);
   }
}

const std::map<string,IdlInterface>& MidlParser::GetParsedInterfaces() const
{
   return m_ParsedInterfaces;
}


const std::vector<string>& MidlParser::GetKnownInterfaces() const
{
   return m_KnownInterfaces;
}


// --------------------------------------------------------------------------
// select1st, select2nd
// --------------------------------------------------------------------------
#include <boost/call_traits.hpp>

template <class Pair>
struct select1st : std::unary_function<Pair,typename Pair::first_type>
{
   typename const Pair::first_type& operator()(typename boost::call_traits<Pair>::param_type x) const
   {
      return x.first;
   }
};

template <class Pair>
struct select2nd : std::unary_function<Pair,typename Pair::second_type>
{
   typename const Pair::second_type& operator()(typename boost::call_traits<Pair>::param_type x) const
   {
      return x.second;
   }
};

void MidlParser::Dump(ostream& s) const
{
   s << "KnownInterfaces" << endl << string(30, '=') << endl;
   std::copy(m_KnownInterfaces.begin(), m_KnownInterfaces.end(), std::ostream_iterator<string>(s, "rn"));

   s << endl << "ParsedInterfaces" << endl << string(30, '=') << endl;

   std::copy(
      boost::make_transform_iterator(m_ParsedInterfaces.begin(), select1st<const std::map<string, IdlInterface>::value_type>()),
      boost::make_transform_iterator(m_ParsedInterfaces.end(), select1st<const std::map<string, IdlInterface>::value_type>()),
      std::ostream_iterator<string>(s, "rn"));


   s.flush();
}
void MidlParser::AddKnownType(const string& t)
{
   if(std::find(m_KnownTypes.begin(), m_KnownTypes.end(), t) ==  m_KnownTypes.end())
   {
      m_KnownTypes.push_back(t);
   }
}

Página feita usando o code@Web, de autoria de Thiago Adams.


Em 11/08/2005 18:56, por Rodrigo Strauss


  
 
 
Comentários
Leonardo Stabile Prates | e-mail | em 14/08/2005 | #
interessante teu parser, me lembra minhas aulas de compiladores na facul, com gramaticas LR e ai vai cacetada de código...

tua solucao usando C++ puro ficou muito bem feita, principalmente a parte do "tokenizer", facilitou muito o servico...

nao curti tua rotina pra carregar os tipos e interfaces conhecidas, no bom e velho jeitão C de declarar strings fica mais simples e visual, ainda q o boost::assign deu uma ajudada... espero q a nova spec do C++ (C++0x) traga essa funcionalidade para facilitar inicializacoes, coisas do tipo:

list<string> t = { "blah", "blah", "blah" };

pelo menos era esse o objetivo deles... isso facilitaria muito teu codigo:

vector<string> m_KnownInterfaces =
{
"IUnknown",
"IDispatch"
};

o mesmo pra tua maquina de estados, quando fiz eu acabei definindo um id pra cada token, e criei um switch pra levar pra cada proximo estado, nao gosto mto de "else ifs" seguidos, mas dae jah eh gosto pessoal... mais uma vez: espero q no C++0x a funcionalidade de switch para strings esteja presente tambem:

switch( token )
{
case "[":
...
break;
case "cpp_quote":
...
break;
}

uma vez jah pensei em criar uma classe abstrata "Token" e derivar meus tokens, para que os estados fossem atingidos por polimorfismo, talvez eu tente no futuro. daria ateh pra resumir codigo com algum class factory bem feito...

soh nao entendi uma coisa (pode ateh parecer meio estupido, mas nao entendi mesmo): voce lancou varias excecoes no codigo mas ninguem esta capturando, vc quer q pare tudo mesmo ou delega o catch pro usuario do parser?!?
Rodrigo Strauss | website | em 14/08/2005 | #
Boost é a melhor coisa que inventaram depois do chocolate em pó.. :-) Eu virei um adepto!

Eu gosto dessa sintaxe [vector += "item1", "item2"], mas isso é gosto pessoal mesmo. O problema dessa sintaxe C é que ela só serve para inicializar, enquanto o boost:assign pode ser usado para adicionar itens, independente do lugar no código.

Eu fiz tudo com elseif para que as coisas mais simples de serem tratadas (como o #ifdef que eu ignoro) ficassem centralizadas, para diminuir a necessidade de ficar navegando no código. As entidades que são mais complexas - como interfaces e métodos - eu separei. Talvez essa não seja a melhor das soluções, mas como eu tinha dito nos posts, minha intenção foi deixar o fonte o mais claro possível e de fácil manutenção. Até para mostrar para esse povo de Java/C# que acha que qualquer código C++ é complicado de mais para escrever e para manter.

Essa classe só faz a interpretação. É isso mesmo que você falou, quem chama a classe que trata as exceções. O intuito é que toda exceção chegue até o caller, para que ele reporte ao usuário. Todo erro do parser é fatal, não existe nenhum erro que o parser possa recuperar. Olhe um exemplo de uso:

int _tmain(int argc, _TCHAR* argv[])
{
MidlParser parser;

if(argc < 2 )
{
ShowUsage();
return -1;
}

try
{
parser.ParseMidlFile(argv[1]);
}
catch(MidlParser::ParseException& ex)
{
cerr << ex.get_fileName() << "(" << ex.get_line() << ") : " << ex.what() << endl;
}

return 0;
}
Leonardo Stabile Prates | e-mail | em 14/08/2005 | #
o boost eh realmente um achado, ainda q eu fique com o peh atras de usar: usa a mesma logica da biblioteca padrao, eh gerenciada por um grupo comum de pessoas e eh confiavel, mas NAO eh a biblioteca padrao! de qqer forma eu gostaria de ver evolucoes em libs como a lambda e mpl... talvez um dia o paradigma funcional tambem esteja presente por completo no C++, espero.

eh um balde de agua fria pros xiitas Java/.NET q acham q vao engolir o C++, na PIOR das hipoteses a linguagem vai se restringir ao meio academico de ciencia da computacao, acabar eh muito improvavel...

problema eh q eh um paradigma q quebra com o q as pessoas estao acostumadas... algo q pra mim soa normal ninguem entende:

for_each(a.begin(), a.end(), std::cout << _1 << ' ');

a partir do momento q voce desenvolve sistemas em grupo isso se torna um problema...

* sim, eu deveria desenvolver em lisp e nao C++, mas a falta de flexibilidade e a porquisse na sintaxe me espantam dessa linguagem, C++ eh bem melhor. um paradigma nao resolve todos os problemas, logo a linguagem deve suportar outros paradigmas pra aumentar o escopo das solucoes...
Rodrigo Strauss | website | em 14/08/2005 | #
O boost é administrado pelo pessoal do padrão C++, e muitas classes do boost serão incluídas no padrão. Eu também não gostos de usar libs de terceiros, mas depois de ler bastante sobre boost me sinto suficientemente seguro para usá-lo.

Essa pior das hipóteses que você falou não vai existir. Nem a SUN e a Microsoft juntas são fortes suficientes para derrubar o C++, e por parte da Microsoft nem existe esse interessa. TUDO que eles fizeram até hoje é em C ou C++. Pode ser que o uso diminua, mas existem momentos onde você precisa de algo que rode muito mais rápido.

Sim, todos os programadores C++ votam para que o ele tenha mais recursos parecidos com as linguagens funcionais. Eu nunca consegui entendê-las direito, mas acho que não estudei o suficiente... :-)
Algo a dizer?
Nome:


Site:


E-mail:


Escreva o número vinte e seis:


 Não mostre meu e-mail no site, não serve pra nada mesmo...

Comentário





Os comentários devem ser sobre assuntos relativos ao post, eu provavelmente apagarei comentários totalmente offtopic. Se quiser me enviar uma mensagem, use o formulário de contato. E não esqueça: isso é um site pessoal e eu me reservo o direito de apagar qualquer comentário ofensivo ou inapropriado.
rebarba rebarba
  ::::