К сожалению, потоки имеют только очень минимальную и элементарную поддержку возврата.
В прошлый раз, когда я нуждался в этом, я писал свои собственные классы чтения, которые обертывали поток, но имели буфер для возврата вещей и чтения из потока, только когда этот буфер пуст. У них были способы получить состояние, и вы могли зафиксировать состояние или выполнить откат к более раннему состоянию.
Действие по умолчанию в деструкторе класса состояний состояло в откате, так что вы могли анализировать заранее, не задумываясь об обработке ошибок, поскольку исключение просто откатывало бы состояние синтаксического анализатора до точки, где пробовали другое правило грамматики. (Я думаю, что это называется возвращением.) Вот эскиз:
class parse_buffer {
friend class parse_state;
public:
typedef std::string::size_type index_type;
parse_buffer(std::istream& str);
index_type get_current_index() const;
void set_current_index(index_type) const;
std::string get_next_string(bool skip_ws = true) const;
char get_next_char(bool skip_ws = true);
char peek_next_char(bool skip_ws = true);
std::string get_error_string() const; // returns string starting at error idx
index_type get_error_index() const;
void set_error_index(index_type);
bool eof() const;
// ...
};
class parse_state {
public:
parse_state(parse_buffer&);
~parse_state();
void commit();
void rollback();
// ...
};
Это должно дать вам представление. Он не имеет реализации, но это было просто и должно быть легко переделано. Кроме того, в реальном коде было много удобных функций, таких как функции чтения, которые читают строку с разделителями, потребляют строку, если это было одно из нескольких заданных ключевых слов, читают строку и преобразуют ее в тип, заданный для каждого параметра шаблона, и тому подобное.
Идея заключалась в том, что функция установит индекс ошибки в свою исходную позицию, сохранит состояние анализа и попытается выполнить синтаксический анализ до тех пор, пока не преуспеет или не перейдет в тупик. В последнем случае это просто выкинет исключение. Это уничтожило бы parse_state
объекты в стеке, откатив состояние до функции, которая могла бы перехватить исключение и либо попробовать что-то еще, либо вывести ошибку (в этом месте get_error_string()
.)
Если вам нужен действительно быстрый синтаксический анализатор, эта стратегия может быть неправильной, но тогда потоки часто тоже замедляются. OTOH, в последний раз, когда я использовал что-то подобное, я создал парсер XPath, который работает на проприетарном DOM, который используется для представления сцен в 3D-рендере. И это был , а не синтаксический анализатор XPath, который получил все тепло от парней, пытающихся получить более высокую частоту кадров. :)