У меня есть рекордный синтаксический анализатор, который выдает одно из нескольких исключений для указания, какое правило перестало работать.
Вступительная часть:
#include
#include
#include
#include
#include
#include
#include
using namespace boost::spirit;
using namespace boost::spirit::ascii;
using namespace boost::spirit::qi;
using namespace boost::spirit::qi::labels;
using boost::phoenix::function;
using boost::phoenix::ref;
using boost::spirit::qi::eol;
using boost::spirit::qi::fail;
using boost::spirit::qi::lit;
using boost::spirit::qi::on_error;
using BOOST_SPIRIT_CLASSIC_NS::file_position;
using BOOST_SPIRIT_CLASSIC_NS::position_iterator;
Мы используем position_iterator
от Духа. Классик, таким образом, следующий оператор потоковой вставки удобен.
std::ostream&
operator<<(std::ostream& o, const file_position &fp)
{
o << fp.file << ": " << fp.line << ',' << fp.column;
return o;
}
Шаблон err_t
факторизует шаблон для выдачи исключений, связанных с различными формами отказа синтаксического анализа.
template
struct err_t {
template
struct result { typedef void type; };
template
void operator() (info const &what, Iterator errPos, Iterator last) const
{
std::stringstream ss;
ss << errPos.get_position()
<< ": expecting " << what
<< " near '" << std::string(errPos, last) << "'\n";
throw Exception(ss.str());
}
};
Исключения, используемые наряду с их err_t
обертки:
class MissingA : public std::runtime_error {
public: MissingA(const std::string &s) : std::runtime_error(s) {}
};
class MissingB : public std::runtime_error {
public: MissingB(const std::string &s) : std::runtime_error(s) {}
};
class MissingC : public std::runtime_error {
public: MissingC(const std::string &s) : std::runtime_error(s) {}
};
function > const missingA = err_t();
function > const missingB = err_t();
function > const missingC = err_t();
function > const other_error =
err_t();
Грамматика ищет простые последовательности. Без eps
, start
управляйте сбоями, а не a
на пустом входе.
template
struct my_grammar
: grammar
{
my_grammar(int &result)
: my_grammar::base_type(start)
, result(result)
{
a = eps > lit("Header A") > eol;
b = eps > lit("Header B") > eol;
c = eps > lit("C:") > int_[ref(result) = _1] > eol;
start = a > b > c;
a.name("A");
b.name("B");
c.name("C");
on_error(start, other_error(_4, _3, _2));
on_error(a, missingA(_4, _3, _2));
on_error(b, missingB(_4, _3, _2));
on_error(c, missingC(_4, _3, _2));
}
rule start;
rule a;
rule b;
rule c;
int &result;
};
В my_parse
, мы выводим содержание потока в a std::string
и используйте position_iterator
отслеживать местоположение синтаксического анализа.
int
my_parse(const std::string &path, std::istream &is)
{
std::string buf;
is.unsetf(std::ios::skipws);
std::copy(std::istream_iterator(is),
std::istream_iterator(),
std::back_inserter(buf));
typedef position_iterator itertype;
typedef my_grammar grammar;
itertype it(buf.begin(), buf.end(), path);
itertype end;
int result;
grammar g(result);
bool r = phrase_parse(it, end, g, boost::spirit::ascii::space);
if (r && it == end) {
std::cerr << "success!\n";
return result;
}
else {
file_position fpos = it.get_position();
std::cerr << "parse failed at " << fpos << '\n';
return -9999;
}
}
Наконец, основная программа
int main()
{
std::stringstream ss;
ss << "Header A\n"
<< "Header B\n"
<< "C: 3\n";
int val = my_parse("path", ss);
std::cout << "val = " << val << '\n';
return 0;
}
Код выше бросков MissingA
:
terminate called after throwing an instance of 'MissingA' what(): path: 2,1: expecting near 'Header B C: 3 '
Я думал, что шкипер, возможно, использовал новую строку, но попытку lexeme[eol]
вместо этого приведенный тот же результат.
Я должен пропускать что-то очевидное, потому что это кажется одним из самого тривиального вида синтаксических анализаторов для записи. Что я делаю неправильно?
Да, шкипер ест символы новой строки. лексема [eol]
тоже не помогает, потому что директива lexeme вызывает шкипера перед переключением в режим без шкипера (подробнее см. здесь ).
Чтобы избежать пропуска новой строки, либо используйте другой тип пропуска, либо оберните компоненты eol
в no_skip [eol]
, что семантически эквивалентно лексеме [ ]
, за исключением того, что он не вызывает шкипера. Однако обратите внимание, что no_skip []
был добавлен только недавно, поэтому он будет доступен только в следующем выпуске (Boost V1.43). Но он уже есть в Boost SVN (предварительную документацию см. здесь ).