Для разработки парсера Spirit X3 я хочу использовать семантические действия (сноска 1). Для меня важно контролировать, как хранить атрибуты в контейнерах STL.
Этот вопрос о том, как контролировать, чтобы атрибут анализатора: _attr (ctx) соответствовал типу правила: _val (ctx), чтобы его можно было назначить правильно. Может быть, этот вопрос сводится к тому, как применять недокументированные
transform_attribute функция. Но, пожалуйста, прочитайте со мной, чтобы увидеть, действительно ли это то, что решает это для меня в примере кода.
Типы печати объектов / переменных
Что мне показалось очень полезным, так это возможность печатать типы _attr (ctx) и _val (ctx) в семантическом действии, когда я экспериментирую с различными грамматическими выражениями.
Итак, основываясь на ответе Говарда Хиннанта , я написал файл заголовка утилиты, чтобы предоставить такие средства в соответствии с моими предпочтениями.
code below is to be put in a file named utility.h
#include <string>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
namespace utility
{
template<typename T>
std::string type2string()
{
std::string r;
typedef typename std::remove_reference<T>::type TR;
std::string space = "";
if ( std::is_const<TR>::value )
{ r = "const"; space = " "; }
if ( std::is_volatile<TR>::value )
{ r += space + " volatile"; space = " "; }
int status;
char* demangled =
abi::__cxa_demangle( typeid(TR).name(), nullptr, nullptr, &status );
switch ( status )
{
case 0: { goto proceed; }
case -1: { r = "type2string failed: malloc failure"; goto fail; }
case -2: { r = "type2string failed: " + std::string(typeid(TR).name()) +
" nonvalid C++ ABI name"; goto fail; }
case -3: { r = "type2string failed: invalid argument(s)"; goto fail; }
default: { r = "type2string failed: unknown status " +
status; goto fail; }
}
proceed:
r += space + demangled;
free( demangled );
/* references are without a space */
if ( std::is_lvalue_reference<T>::value ) { r += '&'; }
if ( std::is_rvalue_reference<T>::value ) { r += "&&"; }
fail:
return r;
}
}
Теперь действительный пример кода:
#include <cstddef>
#include <cstdio>
#include <cstdint>
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <utility> // this is for std::move
#include "utility.h" // to print types
namespace client
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
namespace semantic_actions
{
using x3::_val; // assign to _val( ctx )
using x3::_attr; // from _attr( ctx )
struct move_assign
{
template <typename Context>
void operator()(const Context& ctx) const
{
printf( "move_assign\n" );
_val( ctx ) = std::move( _attr( ctx ) );
}
};
struct print_type
{
template <typename Context>
void operator()(const Context& ctx) const
{
printf( "print_type\n" );
std::string str;
str = utility::type2string< decltype( _attr( ctx ) ) >();
printf( "_attr type: %s\n", str.c_str() );
// reuse str
str = utility::type2string< decltype( _val( ctx ) ) >();
printf( "_val type: %s\n", str.c_str() );
}
};
}
namespace parser
{
using x3::char_;
using x3::lit;
using namespace semantic_actions;
x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";
const auto main_rule__def = (*( !lit(';') >> char_) >> lit(';'))[print_type()][move_assign()];
BOOST_SPIRIT_DEFINE( main_rule_ )
const auto entry_point = x3::skip(x3::space)[ main_rule_ ];
}
}
int main()
{
printf( "Give me a string to test rule.\n" );
printf( "Type [q or Q] to quit.\n" );
std::string input_str;
std::string output_str;
while (getline(std::cin, input_str))
{
if ( input_str.empty() || input_str[0] == 'q' || input_str[0] == 'Q')
{ break; }
auto first = input_str.begin(), last = input_str.end();
if ( parse( first, last, client::parser::entry_point, output_str) )
{
printf( "Parsing succeeded\n" );
printf( "input: \"%s\"\n", input_str.c_str() );
printf( "output: \"%s\"\n", output_str.c_str() );
}
else
{
printf( "Parsing failed\n" );
}
}
return 0;
}
Ввод всегда:
abcd;
выход:
Give me a string to test rule.
Type [q or Q] to quit.
<main_rule>
<try>abcd;</try>
print_type
_attr type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
move_assign
<success></success>
<attributes>[a, b, c, d]</attributes>
</main_rule>
Parsing succeeded
input: "abcd;"
output: "abcd"
Хорошо, пока все хорошо, но предположим, что я хотел бы включить точку с запятой в анализируемый результат. Я изменяю грамматическую линию на:
const auto main_rule__def = (*( !lit(';') >> char_) >> char_(";"))[print_type()];
Примечание. Я удалил семантическое действие [move_assign ()], поскольку оно не может быть скомпилировано из-за несовместимых типов _attr и _val.
Теперь вывод:
Give me a string to test rule.
Type [q or Q] to quit.
<main_rule>
<try>abcd;</try>
print_type
_attr type: boost::fusion::deque<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
<success></success>
<attributes>[]</attributes>
</main_rule>
Parsing succeeded
input: "abcd;"
output: ""
Теперь тип _attr boost :: fusion :: deque <> - это не то, что я хочу, я просто то, что должно быть std :: string. Я не понимаю, почему, если у меня есть полная правая часть грамматического назначения в скобках семантического действия, _attr все еще не имеет тип _val. Поможет ли здесь функция X3 transform_attribute? И как мне это применить? Или как еще один хороший способ решить эту проблему, не работая с интерфейсами Boost Fusion или другими деталями реализации.
Текущее решение
Текущий обходной путь для меня - определить другое правило, которое будет назначено из первого правила семантическим действием. Только там _attr имеет тип std :: string.
namespace parser
{
using x3::char_;
using x3::lit;
using namespace semantic_actions;
x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";
x3::rule<struct main_rule2_class, std::string> main_rule2_ = "main_rule2";
const auto main_rule__def = *( !lit(';') >> char_) >> char_(";");
const auto main_rule2__def = main_rule_[print_type()][move_assign()];
BOOST_SPIRIT_DEFINE( main_rule_, main_rule2_ )
const auto entry_point = x3::skip(x3::space)[ main_rule2_ ];
}
выход:
Give me a string to test rule.
Type [q or Q] to quit.
<main_rule2>
<try>abcd;</try>
<main_rule>
<try>abcd;</try>
<success></success>
<attributes>[a, b, c, d, ;]</attributes>
</main_rule>
print_type
_attr type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
move_assign
<success></success>
<attributes>[a, b, c, d, ;]</attributes>
</main_rule2>
Parsing succeeded
input: "abcd;"
output: "abcd;"
Я надеюсь, что есть способ без необходимости создавать другое правило, просто чтобы тип _attr соответствовал _val.
* * Тысяча сорок-девять (1)
Я не ценю скрытый ум, который авторы вложили в эту библиотеку. Как только одно невинно выглядящее изменение может сломать приложение. Принимая во внимание, что более четкий и продуманный подход будет гораздо яснее сообщать, что происходит Я просто должен снять это с моей груди.