Разбор конца фразы перед началом в Boost.Spirit

Я пытаюсь получить Boost.Spirit для разбора искаженных символов MSVC. Они принимают форму:


?myvolatileStaticMember@myclass@@2HC

что означает «volatile int myclass :: myvolatileStaticMember».

«Ключом» к синтаксическому анализу является двойной символ «@@». Перед @@ указывается имя символа, которое состоит из идентификатора C ++, за которым следует ноль или более «@» дополнительных идентификаторов, чтобы полностью представить символ в его абсолютной иерархии пространства имен. После @@ указывается, что это за идентификатор (переменная, функция и т. Д.)

Теперь я могу получить Boost.Spirit для разбора или часть, предшествующая @@ или же часть после @@. Я еще не выяснил, как заставить Boost.Spirit найти @@ и передать то, что до этого, одному настраиваемому анализатору, а что после него — другому настраиваемому анализатору.

Вот мой парсер для части, предшествующей @@:

// This grammar is for a MSVC mangled identifier
template<typename iterator> struct msvc_name : grammar<iterator, SymbolType(), locals<SymbolType, string>>
{
SymbolTypeDict &typedict;
void name_writer(SymbolType &val, const string &i) const { val.name=i; }
void dependent_writer(SymbolType &val, const string &i) const
{
SymbolTypeDict::const_iterator dt=typedict.find(i);
if(dt==typedict.end())
{
auto _dt=typedict.emplace(make_pair(i, SymbolType(SymbolTypeQualifier::None, SymbolTypeType::Namespace, i)));
dt=_dt.first;
}
val.dependents.push_back(&dt->second);
}
// These work by spreading the building of a templated type over multiple calls using local variables _a and _b
// We accumulate template parameters into _a and accumulate mangled symbolness into _b
void begin_template_dependent_writer(SymbolType &, SymbolType &a, string &b, const string &i) const
{
a=SymbolType(SymbolTypeQualifier::None, SymbolTypeType::Class, i);
b=i;
}
void add_template_constant_dependent_writer(SymbolType &a, string &b, long long constant) const
{
string i("_c"+to_string(constant));
SymbolTypeDict::const_iterator dt=typedict.find(i);
if(dt==typedict.end())
{
auto _dt=typedict.emplace(make_pair(i, SymbolType(SymbolTypeQualifier::None, SymbolTypeType::Constant, to_string(constant))));
dt=_dt.first;
}
a.templ_params.push_back(&dt->second);
b.append(i);
}
void add_template_type_dependent_writer(SymbolType &a, string &b, SymbolTypeType type) const
{
string i("_t"+to_string(static_cast<int>(type)));
SymbolTypeDict::const_iterator dt=typedict.find(i);
if(dt==typedict.end())
{
auto _dt=typedict.emplace(make_pair(i, SymbolType(SymbolTypeQualifier::None, type)));
dt=_dt.first;
}
a.templ_params.push_back(&dt->second);
b.append(i);
}
void finish_template_dependent_writer(SymbolType &val, SymbolType &a, string &b) const
{
SymbolTypeDict::const_iterator dt=typedict.find(b);
if(dt==typedict.end())
{
auto _dt=typedict.emplace(make_pair(b, a));
dt=_dt.first;
}
val.dependents.push_back(&dt->second);
}
msvc_name(SymbolTypeDict &_typedict) : msvc_name::base_type(start), typedict(_typedict)
{
identifier=+(char_ - '@');
identifier.name("identifier");
template_dependent_identifier=+(char_ - '@');
template_dependent_identifier.name("template_dependent_identifier");
dependent_identifier=+(char_ - '@');
dependent_identifier.name("dependent_identifier");
start = identifier [ boost::phoenix::bind(&msvc_name::name_writer, this, _val, _1) ] >> *(
lit("@@") >> eps
| (("@?$" > template_dependent_identifier [ boost::phoenix::bind(&msvc_name::begin_template_dependent_writer, this, _val, _a, _b, _1) ])
> "@" > +(( "$0" > constant [ boost::phoenix::bind(&msvc_name::add_template_constant_dependent_writer, this, _a, _b, _1) ])
| type [ boost::phoenix::bind(&msvc_name::add_template_type_dependent_writer, this, _a, _b, _1) ])
>> eps [ boost::phoenix::bind(&msvc_name::finish_template_dependent_writer, this, _val, _a, _b) ])
| ("@" > dependent_identifier [ boost::phoenix::bind(&msvc_name::dependent_writer, this, _val, _1) ]))
;
BOOST_SPIRIT_DEBUG_NODE(start);
start.name("msvc_name");
on_error<boost::spirit::qi::fail, iterator>(start,
cerr << boost::phoenix::val("Parsing error: Expected ") << _4 << boost::phoenix::val(" here: \"")
<< boost::phoenix::construct<string>(_3, _2) << boost::phoenix::val("\"") << endl);
}

rule<iterator, SymbolType(), locals<SymbolType, string>> start;
rule<iterator, string()> identifier, template_dependent_identifier, dependent_identifier;
msvc_type type;
msvc_constant<iterator> constant;
};

Вы заметите «lit (» @@ «) >> eps», где я пытаюсь заставить его перестать совпадать, когда он видит @@. Теперь вот часть, которая должна соответствовать полному искаженному символу:

template<typename iterator> struct msvc_symbol : grammar<iterator, SymbolType()>
{
SymbolTypeDict &typedict;
/* The key to Microsoft symbol mangles is the operator '@@' which consists of a preamble
and a postamble. Immediately following the '@@' operator is:
Variable:
3<type><storage class>
Static member variable:
2<type><storage class>
Function:
<near|far><calling conv>[<stor ret>]   <return type>[<parameter type>...]<term>Z
<Y   |Z  ><A|E|G       >[<?A|?B|?C|?D>]<MangledToSymbolTypeType...>      <@>Z
Member Function:
<protection>[<const>]<calling conv>[<stor ret>]   <return type>[<parameter type>...]<term>Z
<A-V       >[<A-D>  ]<A|E|G       >[<?A|?B|?C|?D>]<MangledToSymbolTypeType...>      <@>Z
*/
msvc_symbol(SymbolTypeDict &_typedict) : msvc_symbol::base_type(start), typedict(_typedict), name(_typedict), variable(_typedict)
{
start="?" >> name >> ("@@" >> variable);
BOOST_SPIRIT_DEBUG_NODE(start);
on_error<boost::spirit::qi::fail, iterator>(start,
cerr << boost::phoenix::val("Parsing error: Expected ") << _4 << boost::phoenix::val(" here: \"")
<< boost::phoenix::construct<string>(_3, _2) << boost::phoenix::val("\"") << endl);
}

rule<iterator, SymbolType()> start;
msvc_name<iterator> name;
msvc_variable<iterator> variable;
};

Итак, это соответствует «?» достаточно легко;). Проблема в том, что он отправляет все после «?» в синтаксический анализатор msvc_name, поэтому вместо бита от @@ и далее до msvc_variable и от остатка до msvc_name, msvc_name потребляет все, вплоть до @@. Это не интуитивно понятно, так как можно было бы подумать, что квадратные скобки означают сделать это в первую очередь.

Поэтому, если я заменю:

start="?" >> name >> ("@@" >> variable);

с

start="?" >> name >> variable;

… все работает отлично.

Однако я бы предпочел не делать это таким образом. В идеале я хочу, чтобы Boost.Spirit аккуратно разделился на @@ в msvc_symbol и как бы «делал все правильно». Я думаю, что я, вероятно, не думаю, достаточно рекурсивно? В любом случае, я в тупике.

Примечание. Да, я знаю, что могу разбить строку на @@ и запустить два отдельных анализатора. Это не то, что я спрашиваю — скорее, я спрашиваю, как настроить Boost.Spirit для анализа конца фразы перед началом.

Также обратите внимание: я знаю, что шкипер может быть использован для создания пробела @@ и выполнения разбиения таким образом. Проблема в том, что то, что стоит перед @@, очень специфично, как и то, что идет после @@. Поэтому это не совсем пустое место.

Заранее большое спасибо всем, кто может помочь. От поиска в Google и Stackoverflow вопросов, связанных с этим, преодоление «жадности слева направо» Boost.Spirit является проблемой для многих людей.

Найл

2

Решение

Казалось бы, вы можете сделать несколько вещей:

  1. явно запретите двойное «@@» там, где вы ожидаете «@». Смотрите также

  2. токенизировать первым (использовать Spirit Lex?)

Здесь я покажу вам рабочий пример первого подхода:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
namespace qi = boost::spirit::qi;

template<typename T> T reversed(T c) {  return T(c.rbegin(), c.rend()); }

int main (int argc, char** argv)
{
const std::string input("?myvolatileStaticMember@myclass@@2HC");

auto f = begin(input), l = end(input);

auto identifier = +~qi::char_("@");
auto delimit    = qi::lit("@") - "@@";

std::vector<std::string> qualifiedName;
std::string typeId;

if (qi::parse(f,l,
'?' >> identifier % delimit >> "@@" >> +qi::char_,
qualifiedName,
typeId))
{
using namespace boost::spirit::karma;
qualifiedName = reversed(qualifiedName);
std::cout << "Qualified name: "  << format(auto_ % "::" << "\n", qualifiedName);
std::cout << "Type indication: '" << typeId << "'\n";
}
}

Выход:

Qualified name: myclass::myvolatileStaticMember
Type indication: '2HC'
2

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector