@ sehe, как я могу исправить эту грамматику для поддержки "\"Column_A\""
? 6 часов назад
К этому времени вы, вероятно, уже поняли, что здесь происходят две разные вещи.
Отдельные проблемы Йо
С одной стороны, у вас есть грамматика (которая позволяет разделить |
столбцы, такие как columna
или "Column_A"
).
С другой стороны, у вас есть семантический анализ (фазагде вы проверяете, что проанализированное содержимое соответствует определенным критериям).
То, что делает вашу жизнь трудной, - это попытка связать их.Не поймите меня неправильно, могут быть (очень редкие) обстоятельства, когда объединение этих обязанностей абсолютно необходимо, но я чувствую, что это всегда будет оптимизацией.Если вам это нужно, Дух - это не ваше дело, и вам, скорее всего, будет предоставлен рукописный анализатор.
Синтаксический анализ
Итак, давайте разберемся с грамматикой просто:
static auto headers = (quoted|bare) % '|' > (eol|eoi);
Правила bare
и quoted
могут быть почти такими же, как и раньше:
static auto quoted = lexeme['"' >> *('\\' >> char_ | "\"\"" >> attr('"') | ~char_('"')) >> '"'];
static auto bare = *(graph - '|');
Как вы можете видеть, это неявно позаботится о цитировании и экранировании какхорошо пропуская пробелы вне лексем.При простом применении это приведет к чистому списку имен столбцов:
std::string const s = "\"columnA\"|column_B| column_c \n";
std::vector<std::string> headers;
bool ok = phrase_parse(begin(s), end(s), Grammar::headers, x3::blank, headers);
std::cout << "Parse " << (ok?"ok":"invalid") << std::endl;
if (ok) for(auto& col : headers) {
std::cout << std::quoted(col) << "\n";
}
Печать Live On Coliru
Parse ok
"columnA"
"column_B"
"column_c"
INTERMEZZO: Стиль кодирования
Давайте структурируем наш код так, чтобы отражалось разделение интересов.Наш код синтаксического анализа может использовать X3, но наш код проверки не обязательно должен находиться в той же единице перевода (файл cpp).
Иметь заголовок, определяющий несколько основных типов:
#include <string>
#include <vector>
using Header = std::string;
using Headers = std::vector<Header>;
Определите операции, которые мы хотим выполнить над ними:
Headers parse_headers(std::string const& input);
bool header_match(Header const& actual, Header const& expected);
bool headers_match(Headers const& actual, Headers const& expected);
Теперь main
можно переписать так:
auto headers = parse_headers("\"columnA\"|column_B| column_c \n");
for(auto& col : headers) {
std::cout << std::quoted(col) << "\n";
}
bool valid = headers_match(headers, {"columna","columnb","columnc"});
std::cout << "Validation " << (valid?"passed":"failed") << "\n";
И, например, parse_headers.cpp
может содержать:
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
namespace Grammar {
using namespace x3;
static auto quoted = lexeme['"' >> *('\\' >> char_ | "\"\"" >> attr('"') | ~char_('"')) >> '"'];
static auto bare = *(graph - '|');
static auto headers = (quoted|bare) % '|' > (eol|eoi);
}
Headers parse_headers(std::string const& input) {
Headers output;
if (phrase_parse(begin(input), end(input), Grammar::headers, x3::blank, output))
return output;
return {}; // or throw, if you prefer
}
Проверка
Это то, что известно как "семантические проверки".Вы берете вектор строк и проверяете их в соответствии с вашей логикой:
#include <boost/range/adaptors.hpp>
#include <boost/algorithm/string.hpp>
bool header_match(Header const& actual, Header const& expected) {
using namespace boost::adaptors;
auto significant = [](unsigned char ch) {
return ch != '_' && std::isgraph(ch);
};
return boost::algorithm::iequals(actual | filtered(significant), expected);
}
bool headers_match(Headers const& actual, Headers const& expected) {
return boost::equal(actual, expected, header_match);
}
Вот и все.Вся мощь алгоритмов и современного C ++ в вашем распоряжении, вам не нужно бороться с ограничениями из-за синтаксического анализа контекста.
Полная демонстрация
Выше, Live On Wandbox
Обе части стали значительно проще:
- ваш синтаксический анализатор не должен иметь дело со странной логикой сравнения
- ваша логика сравнения не имеетприходится иметь дело с грамматическими проблемами (кавычки, экранированные символы, разделители и пробелы)