В одном из моих проектов мне нужно иметь возможность предоставить очень простой переменный анализатор поиска и замены (в основном для использования в путях). Переменные используются в основном во время запуска и иногда для доступа к файлам (не основная функция программы, просто загрузка ресурсов), поэтому анализатор не обязательно должен быть высокопроизводительным. Однако я бы предпочел, чтобы он был поточно-ориентированным.
Анализатор должен иметь возможность хранить набор переменных (map<string, string>
на данный момент) и иметь возможность заменять токены на соответствующие значения в строках. Значения переменных могут содержать другие переменные, которые будут разрешаться при использовании переменной ( не при ее добавлении, поскольку переменные могут добавляться со временем).
Текущая грамматика переменной выглядит примерно так:
$basepath$/resources/file.txt
/$drive$/$folder$/path/file
Мой текущий анализатор использует пару stringstream
s («output» и «varname»), записывает в поток «output», пока не найдет первый $, поток «varname» до второго $, затем ищет до переменной (используя содержимое varname.str()
). Это очень просто и хорошо работает, даже когда рекурсивно переходит в значения переменных.
String Parse(String input)
{
stringstream output, varname;
bool dest = false;
size_t total = input.length();
size_t pos = 0;
while ( pos < total )
{
char inchar = input[pos];
if ( inchar != '$' )
{
if ( dest ) output << inchar;
else varname << inchar;
} else {
// Is a varname start/end
if ( !dest )
{
varname.clear();
dest = true;
} else {
// Is an end
Variable = mVariables.find(varname.str());
output << Parse(Variable.value());
dest = false;
}
}
++pos;
}
return output.str();
}
(проверка ошибок и тому подобное удалены)
Однако этот метод не дает мне результата, когда я пытаюсь применить его к желаемой грамматике. Я хотел бы что-то похожее на то, что Visual Studio использует для переменных проекта:
$(basepath)/resources/file.txt
/$(drive)/$(folder)/path/file
Я бы тоже хотел иметь возможность:
$(base$(path))/subdir/file
Повторение имени переменной привело меня к стене, и я не уверен, что лучший способ продолжить.
На данный момент у меня есть два возможных понятия:
Итерируйте по входной строке, пока я не найду $, ищи a (как следующий символ, затем найду совпадение) (считая уровни внутри и снаружи, пока не будет достигнута правильная близкая пара). Отправьте этот бит для анализа, затем используйте возвращенное значение в качестве имени переменной. Однако, похоже, что это будет грязно и приведет к большому скопированию.
Вторая концепция - использовать char *
, или, возможно, char * &
, и двигаться вперед, пока я не достигну завершающего нуля. Функция синтаксического анализатора может использовать указатель в рекурсивных вызовах к себе при анализе имен переменных. Я не уверен, как лучше всего реализовать эту технику, кроме того, чтобы каждый вызов отслеживал имя, которое он анализировал, и добавлял возвращаемое значение всех вызовов, которые он делает.
Проект должен компилироваться только в VS2010, так что потоки и строки STL, поддерживаемые биты C ++ 0x и специфичные для Microsoft функции - все это честная игра (универсальное решение предпочтительнее в случае изменения этих требований, но это не так необходимо на данный момент). Однако использование других библиотек бесполезно, особенно не Boost.
Обе мои идеи кажутся более сложными и запутанными, чем необходимо, поэтому я ищу хороший чистый способ справиться с этим. Код, идеи или документы, обсуждающие, как лучше всего это сделать, приветствуются.