...Работы у меня сегодня не было, а настроение поизвращаться с C++ — было.
В результате всего этого, появился "шедевер" — шаблонная библиотека статических регулярных выражений, в чем-то смахивающая на SPIRIT. Вот только она гаараздо меньше — всего 7 килобайт. Ну, и гораздо примитивнее, конечно.
Основная ее фишка в том, что компиляция регулярного выражения проводится на этапе компиляции программы — прямо в исполнимый код (довольно страшноватенький — т.к. оптимизация еще впереди). А на этапе выполнения остается только пользоваться функцией поиска совпадений...
пример:
//задаем регулярное выражение (обьяснение примитивов будет ниже)namespace myrx
{
using namespace rx_tmpl;
typedef//определяем регулярное выражение для выделения числа
//с плавающей точкой: [+-]?[0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?
seq<
opt<set<'+','-'> >,
plus<digit>,
opt<seq<c<'.'>, plus<digit> > >,
opt<seq<set<'E','e'>, opt<set<'+','-'> >, plus<digit> > >
>
float_num;
}
//применяем его...void function()
{
string number("123.45E-56 some text");
string::iterator it = number.begin();
if( myrx::float_num::match(it, number.end()) )
{
//строка совпала.
//итератор it указывает на позицию сразу за числом
} else {
//строка не совпала
//позиция итератора не изменилась.
}
}
примитивы:
c<'A'> - литеральный символ.
s<'H','E','L','L','O'> - литеральная строка
set<'A','B','C','D','E','F'> - набор символов - как [ABCDEF].
range<'A','Z'> - диапазон символов - как [A-Z].
range<'A','Z',false> - диапазон символов - как [^A-Z].
ws - пробел или таб [ \t]
digit - цифра - как [0-9]
alpha - английская буква без учета регистра - как [A-Za-z]
star<RX> - ноль или более совпадений - как (abcd)*
plus<RX> - одно или более или более совпадений - как (abcd)+
opt<RX> - необязательное совпадение - как (abcd)?
alt<RX1,RX2> - альтернатива - как (abc|def)
seq<RX1,RX2,RX3,....,RX10> - конкатенация регулярных выражений
ЗЫ. Код будет в следующем сообщении в этой же нитке.
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, Зверёк Харьковский, Вы писали:
ЗХ>а чо, давай сгенерируем что мешает-то?
Если ты настолько кррррутттт, go ahead — генери.
А я данную тему знаю не понаслышке — даже сгенерить голый NFA по Тейлору — задачка далеко нетривиальная.
Причем, получим совершенно неоптимальное представление — в виде графа на указателях и (что самое подлое) без оптимизации e-closure(char).
И, если все же каким-то чудом удастся — исходников будет — дайбожЕ уместиться килобайт в 100
А компилироваться они будут... успеешь поесть, трахнуть жену, выспаться — и далее по циклу раз 5
TMH>>Что же касается скорости, то моя билиотека будет слабже boost::regex как минимум в несколько раз. И только некоторые специально оптимизированные регулярные выражения могут позволить им сравниться в скорости.
ЗХ>а чем это обусловлено?
Я же говорил чем — одни и те же участки строки могут просматриваться несколько раз — особенно этим грешит примитив alt<>.
boost::regex (да и любая себя уважающая библиотека рег.выражений) разбор строки делает за один проход.
TMH>>В целом, я бы рекомендовал применять мою библотеку когда: TMH>>1) Количество рег.выражений малО — не более 10-20. TMH>>2) Все они известны на compile-time TMH>>3) Скорость выполнения поиска совпадений не критична и/или рег.выражения достаточно простЫ.
ЗХ>в этих условиях она будет лучше, чем regex?
Да — хотя бы по времени компиляции и размеру кода.
Что же касается скорости, то как известно, 20% от кода программы исполняются 80% времени жизни программы.
Надо только следить чтобы в эти 20% не попали функции rx_templ::some_regex::match()
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Если совпадение найдено, match() возвращает true и продвигает итератор it на нужное количество символов. Если же совпадение не найдено, возвращается true и итератор остается в прежней позиции.
Собственно, для того чтобы класс стал "примитивом", все что ему необходимо — это реализовать функцию match(). То есть, "расширение и углубление" возможностей данной библиотеки — дело довольно простое.
Примитивы можно разделить на "терминальные" — это те, которые выполняют "реальную работу" — т.е. сравнение итератора в текущей позиции и символа — это шаблоны c<>, s<>, set<>, range<>, ws, alpha, digit,
И "нетерминальные" — которые выполняют логические операции над другими примитивами (как терминальными, так и нет) — это plus<>, star<>, opt<>, alt<>, seq<>.
В результате комбинирования терминальных примитивов при помощи нетерминальных, генерируется "функция совпадения" код которой представляет собой "дерево разбора" регулярного выражения. А выполнение этой функции — это как бы "обход" этого дерева разбора.
Что же касается реализации рекурсивных шаблонов set<> seq<> и s<>, то принцип их построения достаточно хорошо описан у Александреску — в главе про TypeList<>. Единственное, что можно в какой-то мере считать моим достижением в их реализации — удалось обойтись без частичной специализации (это важно для VC6.0) и сделать это достаточно красиво...
Кстати, о рекурсии: класс boost::tuple<> построен примерно на той же технологии, но имхо несколько более "некрасиво".
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, Зверёк Харьковский, Вы писали:
ЗХ>а вот такой вопрос — насколько это эффективнее (и эфективнее ли?) классического бустового варианта?
...Это второй человек, который захотел бенчмарок... Этакие вы ненасытные
Я ж этот код сотворил вчера за 4 часа на коленке
ЗХ>размер получившегося ехе-шника,
Тут надо учитывать количество уникальных регулярных выражений задействованных в программе.
На малом количестве выражений — моя библиотека будет выигрывать, причем на порядки — т.к. она генерит только функции поиска совпадений.
На большам количестве выражений — моя сначала сравняется, а потом начнет отставать.
Я ожидаю, что с boost::regex по размеру она сравнится при количестве рег.выражений около 100 или 200, но это надо проверять...
ЗХ>скорость выполнения,
boost::regex построена на недерминированном конечном автомате — что означает что она только один раз извлекает каждый элемент строки из итератора. Из этого, кстати, следует что она может использовать input_iterator — а это дорогого стОит.
Моя билиотека — это голый и примитивный обход дерева (увы, сгенерировать конечный автомат на шаблонах мне еще слабО ). Несколько спасает то, что этот обход выгоняется в исполнимый код.
Но "откаты назад" и повторные проверки участков последовательности неизбежны — в особенности этим страдает примитив alt<>. Так что, она может использовать только random_access_iterator. Я пробовал адаптировать ее к forward_iterator — получилось, но мне хочется именно input_iterator, поэтому я счел поддержку forward_iterator слишком обременительной и выкинул ее. (В принципе, у меня в башке бродит идея "кэширующего итератора", который мог бы превратить input_iterator в random_access_iterator, но это пока только идея и вопрос, надо ли ?)
Что же касается скорости, то моя билиотека будет слабже boost::regex как минимум в несколько раз. И только некоторые специально оптимизированные регулярные выражения могут позволить им сравниться в скорости.
ЗХ>простоту написания (ну тут явно налицо некоторый проигрыш, хотя, в принципе, можно привыкнуть)
Если привыкнуть, то просто — мне, например, очень просто
В целом, я бы рекомендовал применять мою библотеку когда:
1) Количество рег.выражений малО — не более нескольких 10-20.
2) Все они известны на compile-time
3) Скорость выполнения поиска совпадений не критична и/или рег.выражения достаточно простЫ.
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Изменения:
— подчищен код — убраны ненужные наследования, реализация всех рекурсивных шаблонов приведена к единообразию
— убраны все реверансы в строну forward_iterator — библиотека теперь поддерживает ТОЛЬКО random_access_iterator
— примитив alt<> теперь поддерживает до 10 подвыражений (раньше только 2)
— оптимизирован примитив s<> — убраны ненужные проверки конца последовательности.
Планы на следующие версии:
— примитив select<RX> — выделение подвыражений — будут изменения формата функции match()
— терминальные примитивы нечувствительные к регистру букв — думаю как сделать максимально гибко (эх, где ты, template typedef)
namespace rx_tmpl
{
namespace impl
{
template<wchar_t E, class TAIL>
struct strn
{
enum{ length = 1 + TAIL::length };
template<class InIt>
static bool do_match(InIt& it)
{
return (E == *it) && TAIL::do_match(++it);
}
};
struct strnlast
{
enum{ length = 0 };
template<class InIt>
static bool do_match(InIt& it)
{
return true;
}
};
template<wchar_t E, class TAIL>
struct setn
{
static bool do_match(wchar_t ch)
{
return (E == ch) || TAIL::do_match(ch);
}
};
struct setnlast
{
static bool do_match(wchar_t ch)
{
return false;
}
};
template<class RX, class TAIL>
struct seqn
{
template<class InIt>
static bool do_match(InIt& it, const InIt& end)
{
return RX::match(it,end) && TAIL::do_match(it,end);
}
};
struct seqnlast
{
enum{ last = true };
template<class InIt>
static bool do_match(InIt& it, const InIt&)
{
return true;
}
};
template<class RX, class TAIL>
struct altn /*alternative - like "(abc|def)" */
{
template<bool> struct sel{};
enum{ last = false };
template<class InIt>
static bool do_match(InIt& it, const InIt& end, sel<false>)
{
InIt i1(it); bool b1 = RX::match(i1,end);
InIt i2(it); bool b2 = TAIL::do_match(i2,end);
if( b1 && b2 )
it = (i1 < i2) ? i2 : i1;
else if( b1 )
it = i1;
else if( b2 )
it = i2;
else
return false;
return true;
}
template<class InIt>
static bool do_match(InIt& it, const InIt& end, sel<true>)
{
return RX::match(it,end);
}
template<class InIt>
static bool do_match(InIt& it, const InIt& end)
{
return do_match(it,end, sel<TAIL::last>());
}
};
}//namespace impltemplate<wchar_t E>
struct c /* literal charachter */
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) && (E == *it);
if( res ) ++it;
return res;
}
};
template<
int c0, int c1, int c2 =-1, int c3 =-1, int c4 =-1, int c5 =-1, int c6 =-1, int c7 =-1, int c8 =-1, int c9 =-1,
int c10=-1, int c11=-1, int c12=-1, int c13=-1, int c14=-1, int c15=-1, int c16=-1, int c17=-1, int c18=-1, int c19=-1,
int c20=-1, int c21=-1, int c22=-1, int c23=-1, int c24=-1, int c25=-1, int c26=-1, int c27=-1, int c28=-1, int c29=-1
>
struct s /* literal string - up to 30 symbols */
{
typedef impl::strn<
c0,
typename s<
c1,c2,c3,c4,c5,c6,c7,c8,c9,
c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,
c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,
-1
>::node
> node;
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
InIt tend( it + node::length );
if( !(tend < end || tend == end) )
return false;
InIt t(it);
if( !node::do_match(it) )
{ it = t; return false; }
return true;
}
};
template<>
struct s<
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1>
{
typedef impl::strnlast node;
};
template<
int c0, int c1, int c2 =-1, int c3 =-1, int c4 =-1, int c5 =-1, int c6 =-1, int c7 =-1, int c8 =-1, int c9 =-1,
int c10=-1, int c11=-1, int c12=-1, int c13=-1, int c14=-1, int c15=-1, int c16=-1, int c17=-1, int c18=-1, int c19=-1,
int c20=-1, int c21=-1, int c22=-1, int c23=-1, int c24=-1, int c25=-1, int c26=-1, int c27=-1, int c28=-1, int c29=-1
>
struct set /* charachter set - up to 30 chars - like "[ABCDEFabcdef]"*/
{
typedef impl::setn<
c0,
typename set<
c1,c2,c3,c4,c5,c6,c7,c8,c9,
c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,
c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,
-1
>::node
> node;
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) && node::do_match(*it);
if( res ) ++it;
return res;
}
};
template<>
struct set<
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1>
{
typedef impl::setnlast node;
};
template<wchar_t FRONT, wchar_t BACK, bool INCLUSIVE=true>
struct range /*charachter range - like "[a-d]" or "[^a-d]" */
{
template<bool> struct sel {};
//PXT_CASSERT((FRONT < BACK));template<class InIt>
static bool do_match(InIt& it, const InIt& end, sel<true>)
{
bool res = (it != end) && (FRONT >= *it) && (*it <= BACK);
if( res ) ++it;
return res;
}
template<class InIt>
static bool do_match(InIt& it, const InIt& end, sel<false>)
{
return !do_match(it, end, sel<true>());
}
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return do_match(it, end, sel<INCLUSIVE>());
}
};
struct ws /* whitespace chars */
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) && ((*it == ' ') || (*it == '\t'));
if( res ) ++it;
return res;
}
};
struct digit /* numeric chars */
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) && (*it >= '0') && (*it <= '9');
if( res ) ++it;
return res;
}
};
struct alpha /* numeric chars */
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) && (
((*it >= 'A') && (*it <= 'Z')) ||
((*it >= 'a') && (*it <= 'z'))
);
if( res ) ++it;
return res;
}
};
template<class RX>
struct star /*zero or more occurences of RX - like "(abc)*" */
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
while( true )
{
InIt t(it);
if( false == RX::match(it, end) || it == t )
break;
}
return true;
}
};
template<class RX>
struct plus /*one or more occurences of RX - like "(abc)+" */
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return RX::match(it, end) && star<RX>::match(it, end);
}
};
template<class RX>
struct opt /*optional occurence of RX - like "(abc)?" */
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
RX::match(it, end);
return true;
}
};
struct seqe {};
template<
class RX0, class RX1, class RX2=seqe, class RX3=seqe, class RX4=seqe,
class RX5=seqe, class RX6=seqe, class RX7=seqe, class RX8=seqe, class RX9=seqe
>
struct seq
{
typedef impl::seqn<
RX0,
typename seq<RX1,RX2,RX3,RX4,RX5,RX6,RX7,RX8,RX9,seqe>::node
> node;
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
InIt t(it);
bool res = node::do_match(it,end);
if( !res ) it = t;
return res;
}
};
template<>
struct seq<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
typedef impl::seqnlast node;
};
template<
class RX0, class RX1, class RX2=seqe, class RX3=seqe, class RX4=seqe,
class RX5=seqe, class RX6=seqe, class RX7=seqe, class RX8=seqe, class RX9=seqe
>
struct alt /*alternative - like "(abc|def)" */
{
typedef impl::altn<
RX0,
typename alt<RX1,RX2,RX3,RX4,RX5,RX6,RX7,RX8,RX9,seqe>::node
> node;
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return node::do_match(it,end);
}
};
template<>
struct alt<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
typedef impl::seqnlast node;
};
}//namespace rx_tmpl
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, Дарней, Вы писали:
DEA>>Дело в том, что посредством автоматов некоторые вещи (маркирование подвыражений, обратные ссылки) сделать достаточно трудно, если не невозможно. По крайней мере, мне не удалось на автомате реализовать обратные ссылки.
Д>верно, некоторые вещи сделать нельзя. еще есть проблемы с метасимволами, насколько я понимаю
C метасимволами как раз проблемы нету. Конечно, если автомат NFA. Просто трактуешь этот метасимвол как "простой" символ — и при двустековом моделировании он отработает нормально. А вот для DFA — метасимволы — это пипец полный — в особенности если алфавит — юникодный — автомат разрастается до поистине ненормальных размеров.
Д>но можно попробовать прикрутить дополнительные фичи поверх автомата
...Тогда проще рекурсивно-нисходящим методом...
DEA>>Есть такие варианты: DEA>>(1) останавливаемся, несмотря на то, что есть "не финальные" альтернативы. Получим наикратчайшее совпадение — автомат будет "уступчивым" DEA>>(2) продложаем, пока финальное состояние не будет единственным в наборе альтернатив. Получим наидлиннейшее совпадение — автомат будет "жадным"
Д>а если внутри выражения есть несколько квантификаторов для разных его частей, из них один "жадный" а другой "ленивый"?
То же самое. Метишь состояния соответствующие окончанию подвыражения как "полуфинальные" и при моделировании "уступчивых" подвыражений предпочитаешь полуфинальное состояние всем остальным состояниям.
__________
16.There is no cause so right that one cannot find a fool following it.
Здравствуйте, TepMuHyc, Вы писали:
TMH>...запустил нижеприведенную програмку и просто офигел... TMH>Я, конечно ожидал, что инлайновый код быстрый, но чтобы настолько TMH>
Здравствуйте, TepMuHyc, Вы писали:
TMH>...Работы у меня сегодня не было, а настроение поизвращаться с C++ — было. TMH>В результате всего этого, появился "шедевер" — шаблонная библиотека статических регулярных выражений, в чем-то смахивающая на SPIRIT. Вот только она гаараздо меньше — всего 7 килобайт. Ну, и гораздо примитивнее, конечно.
TMH>Основная ее фишка в том, что компиляция регулярного выражения проводится на этапе компиляции программы — прямо в исполнимый код (довольно страшноватенький — т.к. оптимизация еще впереди). А на этапе выполнения остается только пользоваться функцией поиска совпадений...
просто прелесть!
давно собирался сделать такую же штуку со спиритом (т.к. у тебя все же регекс)
удивительно, как до этого еще бустовцы не додумались.
а вот такой вопрос — насколько это эффективнее (и эфективнее ли?) классического бустового варианта?
я имею в виду все параметры: размер получившегося ехе-шника, скорость выполнения, простоту написания (ну тут явно налицо некоторый проигрыш, хотя, в принципе, можно привыкнуть)
Здравствуйте, TepMuHyc, Вы писали:
TMH>Моя билиотека — это голый и примитивный обход дерева (увы, сгенерировать конечный автомат на шаблонах мне еще слабО ).
а чо, давай сгенерируем что мешает-то?
TMH>Что же касается скорости, то моя билиотека будет слабже boost::regex как минимум в несколько раз. И только некоторые специально оптимизированные регулярные выражения могут позволить им сравниться в скорости.
а чем это обусловлено?
TMH>В целом, я бы рекомендовал применять мою библотеку когда: TMH>1) Количество рег.выражений малО — не более нескольких 10-20. TMH>2) Все они известны на compile-time TMH>3) Скорость выполнения поиска совпадений не критична и/или рег.выражения достаточно простЫ.
Здравствуйте, Кодт, Вы писали:
К>А почему в качестве дефолтного символа в шаблонах использован -1 (русское "я"), а не 0 ?
...Вообще-то да... Спасибо.
В самом скором будущем заменю -1 на 0xBAADC0DE — надеюсь китайцы и прочие японцы против не будут
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, TepMuHyc, Вы писали:
TMH>- терминальные примитивы нечувствительные к регистру букв — думаю как сделать максимально гибко (эх, где ты, template typedef)
А так пойдет?
Здравствуйте, TepMuHyc, Вы писали:
TMH>...Работы у меня сегодня не было, а настроение поизвращаться с C++ — было. TMH>В результате всего этого, появился "шедевер" — шаблонная библиотека статических регулярных выражений, в чем-то смахивающая на SPIRIT. Вот только она гаараздо меньше — всего 7 килобайт. Ну, и гораздо примитивнее, конечно.
TMH>Основная ее фишка в том, что компиляция регулярного выражения проводится на этапе компиляции программы — прямо в исполнимый код (довольно страшноватенький — т.к. оптимизация еще впереди). А на этапе выполнения остается только пользоваться функцией поиска совпадений...
Почти один в один boost::spirit. Только в boost::spirit почти не нужно извращаться с typedef'ами, выражение собирается с помощью вызовов функций. Собственно говоря, твоему подходу это не противоречит, т. к. в boost::spirit на низком уровне имеются подобные шаблоны. А интерфейс в духе boost::spirit можно добавить уже поверх всего написаного.
Здравствуйте, TepMuHyc, Вы писали:
К>>А почему в качестве дефолтного символа в шаблонах использован -1 (русское "я"), а не 0 ? TMH>...Вообще-то да... Спасибо. TMH>В самом скором будущем заменю -1 на 0xBAADC0DE — надеюсь китайцы и прочие японцы против не будут
Где-нибудь наверху определи константу -- enum или просто #define. Чтобы потом можно было по-быстрому править...
Здравствуйте, Кодт, Вы писали:
К>Где-нибудь наверху определи константу -- enum или просто #define. Чтобы потом можно было по-быстрому править...
...это и ежу понятно — писать 0xBAADC0DE 28 раз — можно и мозоли на пальчиках заработать — проще
enum{ stopch = 0xBAADC0DE };
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Изменения:
— подчищен код — убраны ненужные наследования, реализация всех рекурсивных шаблонов приведена к единообразию
— убраны все реверансы в строну forward_iterator — библиотека теперь поддерживает ТОЛЬКО random_access_iterator
— примитив alt<> теперь поддерживает до 10 подвыражений (раньше только 2)
— оптимизирован примитив s<> — убраны ненужные проверки конца последовательности.
Планы на следующие версии:
— примитив select<RX> — выделение подвыражений — будут изменения формата функции match()
— терминальные примитивы нечувствительные к регистру букв — думаю как сделать максимально гибко (эх, где ты, template typedef)
вот:
Изменения:
— как и было обещано, добавил примитив select<RX> для выделения подвыражений. Причем, без серьезной потери производительности, что особенно приятно. Теперь, немного о его использовании:
--выражение для выделения ответа FTP сервера (аналогичное приведенному в boost::regex) будет выглядеть так:
seq<
select< plus<digit> >, //код ответа
select< alt<cc<'-',' '>, endl> >, //символ переноса - если '-' - то ответ многострочный
select< star<not_cc<'\r','\n'> > >, //текст сообщения - все до конца строки
opt<endl> //необязательный перевод строки
>
--вызов теста на совпадение будет выглядеть так:
int nsubmatches;
char* submatches[2*3];//сколько всего планируется подвыражений - границы НЕ проверяютсяchar* text = "200 Hello world\n", ptr = text;
if( ftp_regex::match(ptr, ptr+strlen(ptr), submatches, nsubmatches=0) )
{//совпадает...
submatches[0] //начало совпадения №1
submatches[1] //конец совпадения №1
submatches[2] //начало совпадения №2
submatches[3] //конец совпадения №2
submatches[4] //начало совпадения №3
submatches[5] //конец совпадения №3
}
— основательно переработаны терминальные примитивы для работы с классами символов.
c<'А'> - просто символ
cс<'А','B','C'> - набор символов [ABC]
not_cс<'А','B','C'> - набор символов [^ABC]
range<'А','F',bool> - диапазон символов [A-F]
set<CC0,CC1,...> - комбинанирование вышеуказанных примитивов -
например класс символов [0-9A-Za-z_] будет выглядеть как
set<range<'0','9'>,range<'A','Z'>,range<'a','z'>,c<'_'> >
— добавлено еще несколько предопределенных примитивов
ws - пробел или таб [ \t]
not_ws - все кроме пробела или таба
digit - десятичные цифры [0-9]
not_digit - все кроме десятичных цифр
xdigit - шестнадцатиричные цифры [0-9A-Fa-f]
not_xdigit - все кроме шестнадцатиричных цифр
alpha - английские буквы [A-Za-z]
not_alpha - все кроме английских букв
alphanum - английские буквы и цифры [0-9A-Za-z]
not_alphanum - все кроме английских букв и цифр
endl - конец строки (\r?\n)
— увы, не добавил case-insensitive примитивы — т.к. хорошо подумав, понял, что нечувствительность к регистру — штука очень уж зависящая от локали — а вариантов работы с локалями — масса и формализовать их не так уж легко. Тем более, совсем не хочется делать эту библиотечку зависящей от огромного и страшного пакета <locale>. Так что пусть данный вопрос остается желающим в качестве домашнего задания
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, TepMuHyc, Вы писали:
TMH>...запустил нижеприведенную програмку и просто офигел... TMH>Я, конечно ожидал, что инлайновый код быстрый, но чтобы настолько TMH>
Здравствуйте, TepMuHyc, Вы писали:
TMH>...запустил нижеприведенную програмку и просто офигел... TMH>Я, конечно ожидал, что инлайновый код быстрый, но чтобы настолько TMH>
Что впрочем предсказуемо.
Но, учитывая, что мой код не inline, а статическая либа,
думаю что неплохо
Если компилировать не с /O2 а /O1, то одинаковое время
Но всё равно впечатляет, возьму на заметку
Re[4]: Код: версия 0.00000003 - бенчмарки
От:
Аноним
Дата:
09.12.03 23:49
Оценка:
Здравствуйте, e-Xecutor, Вы писали:
(отвечает TepMuHyc которого задолбал РСДН)
EX>
Здравствуйте, TepMuHyc, Вы писали:
TMH>Здравствуйте, Кодт, Вы писали:
К>>Где-нибудь наверху определи константу -- enum или просто #define. Чтобы потом можно было по-быстрому править... TMH>...это и ежу понятно — писать 0xBAADC0DE 28 раз — можно и мозоли на пальчиках заработать — проще TMH>enum{ stopch = 0xBAADC0DE };
А я наверное заменю на 0xdeadbeef
Правда, Ложь — мне все одно — я имею свое мнение.
Если функция недокументированна — это не значит, что ее не используют все ваши конкуренты в своих продуктах.
Любой строй переходный и отрицать это значит быть закостенелым идиотом.
А>Что есть "skv" и с чем его можно покушать? Иными словами код в студию... А>...А то я к аутпуту могу что хошь приписать... даже отрицательные числа...
skv — это моя библиотечка regexp-ов.
точнее это мои инициалы
Кода там больше ста кил, так что студия треснет, если его в неё внести
Всё не соберусь доку написать да выложить куда-нить...
И попытаемся ответить на вопрос как отделить группы найденные первой альтернативой от групп второй?
Ответ — никак.
Чтобы решить эту проблему, предлагаю придерживаться следуюших правил:
1) Количество найденных групп = количеству select-ов в выражении.
2) Несовпавшая группа обнуляется.
3) star и plus запоминают последнюю найденную группу.
4) для star — пустое совпадение — оба итератора указывают на место этого совпадения.
При этом можно на этапе компиляции подсчитать размер массива, нужного для хранения групп.
А так же несколько оптимизировать вычисления (если известно что групп нет — вызываем версию не работаюшую с группами).
Ниже приводиться исходник, где всё это реализованно.
//http://www.rsdn.ru/Forum/Message.aspx?mid=456007&only=1
//0.00000003#ifndef __STATIC_REGEX_H_INCLUDED__
#define __STATIC_REGEX_H_INCLUDED__
namespace static_regex {
typedef wchar_t rx_char;
struct seqe {};
enum { stpc = 0xBAADC0DE };
template<bool> struct grp_present {};
template<rx_char E>
struct c {/* literal charachter */static bool char_match(rx_char ch)
{ return (E == ch); }
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) && (E == static_cast<rx_char>(*it));
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
};
template<
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct s {/* literal string - up to 30 symbols */typedef s<
c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,
c21,c22,c23,c24,c25,c26,c27,c28,c29,stpc
> tail;
enum { length = 1 + tail::length };
template<class InIt>
static bool do_match(InIt& it)
{
return (c0 == static_cast<rx_char>(*it)) && tail::do_match(++it);
}
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
InIt tend( it + length );
if( !(tend < end || tend == end) )
return false;
InIt t(it);
bool res = do_match(it);
if( !res ) it = t;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
};
template<> struct s<
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc>
{
enum { length = 0 };
template<class InIt>
static bool do_match(InIt&)
{ return true; }
};
template<
bool INCLUSIVE,
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct cc_ /* charachter class - like "[abcdEF]" or "[^abcdEF]" */
{
typedef cc_<INCLUSIVE,
c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,
c21,c22,c23,c24,c25,c26,c27,c28,c29,stpc
> tail;
static bool char_match(rx_char ch)
{ return (c0 == ch) || tail::char_match(ch); }
template<bool> struct sel {};
static bool char_match(rx_char ch, sel<true>)
{ return char_match(ch); }
static bool char_match(rx_char ch, sel<false>)
{ return !char_match(ch); }
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) &&
char_match(static_cast<rx_char>(*it), sel<INCLUSIVE>());
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
};
template<> struct cc_<true,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc>
{
static bool char_match(rx_char ch)
{ return false; }
};
template<> struct cc_<false,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc>
{
static bool char_match(rx_char ch)
{ return false; }
};
template<
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct cc:cc_<true,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29>
{};
template<
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct not_cc:cc_<false,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29>
{};
template<rx_char F, rx_char B, bool INCLUSIVE=true>
struct range /* charachter range - like "[a-d]" or "[^a-d]" */
{
static bool char_match(rx_char ch)
{
return (((F < B) ? F : B) <= ch) && (ch <= ((F < B) ? B : F));
}
template<bool> struct sel {};
static bool char_match(rx_char ch, sel<true>)
{ return char_match(ch); }
static bool char_match(rx_char ch, sel<false>)
{ return !char_match(ch); }
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) &&
char_match(static_cast<rx_char>(*it), sel<INCLUSIVE>());
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
};
template<
class CC0, class CC1, class CC2=seqe, class CC3=seqe, class CC4=seqe,
class CC5=seqe, class CC6=seqe, class CC7=seqe, class CC8=seqe, class CC9=seqe>
struct set {/* charachter set */typedef set<CC1,CC2,CC3,CC4,CC5,CC6,CC7,CC8,CC9,seqe> tail;
static bool char_match(rx_char ch)
{
return CC0::char_match(ch) || tail::char_match(ch);
}
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) &&
char_match( static_cast<rx_char>(*it) );
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
};
template<> struct set<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
static bool char_match(rx_char)
{ return false; }
};
template<class RX>
struct star {/* zero or more occurences of RX - like "(abc)*" */template<class InIt>
static bool match(InIt& it, const InIt& end)
{
while( RX::match(it,end) );
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&, grp_present<false>) {
return match(it, end);
}
template<class InIt>
static bool match(
InIt& it, const InIt& end, InIt* sel, int& nsel, grp_present<true>
) {
InIt _sel2[2*RX::sel_len];
int _nsel = 0, _nsel2 = 0;
while (RX::match(it, end, _sel2, _nsel)) {
for (int i = 0; i < 2*_nsel; ++i)
sel[i] = _sel2[i];
_nsel2 = _nsel;
_nsel = 0;
}
if (_nsel2)
nsel += _nsel2;
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
return match(it,end, sel, nsel, grp_present<(!!sel_len)>());
}
enum {sel_len = RX::sel_len};
};
template<class RX>
struct plus {/* one or more occurences of RX - like "(abc)+" */template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return RX::match(it, end) && star<RX>::match(it, end);
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&, grp_present<false>) {
return match(it, end);
}
template<class InIt>
static bool match(
InIt& it, const InIt& end, InIt* sel, int& nsel, grp_present<true>
) {
int _nsel2 = 0;
InIt _sel2[2*RX::sel_len];
if (!RX::match(it, end, _sel2, _nsel2))
return false;
int _nsel = 0;
star<RX>::match(it, end, sel, _nsel);
if (!_nsel) {
for (int i = 0; i < _nsel2; ++i)
sel[i] = _sel2[i];
_nsel = _nsel2;
}
nsel += _nsel;
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
return match(it, end, sel, nsel, grp_present<(!!sel_len)>());
}
enum {sel_len = RX::sel_len};
};
template<class RX>
struct opt {/* optional occurence of RX - like "(abc)?" */template<class InIt>
static bool match(InIt& it, const InIt& end)
{
RX::match(it, end);
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel)
{
RX::match(it, end, sel, nsel);
return true;
}
enum {sel_len = RX::sel_len};
};
template<
class RX0, class RX1, class RX2=seqe, class RX3=seqe, class RX4=seqe,
class RX5=seqe, class RX6=seqe, class RX7=seqe, class RX8=seqe, class RX9=seqe>
struct seq /* sequence of RX - like "(abc)(def)" */
{
typedef seq<RX1,RX2,RX3,RX4,RX5,RX6,RX7,RX8,RX9,seqe> tail;
template<class InIt>
static bool do_match(InIt& it, const InIt& end)
{
return RX0::match(it,end) && tail::do_match(it,end);
}
template<class InIt>
static bool do_match(InIt& it, const InIt& end, InIt* sel, int& nsel)
{
return RX0::match(it, end, sel, nsel)
&& tail::do_match(it, end, sel + RX0::sel_len, nsel);
}
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
InIt itbk(it);
bool res = do_match(it, end);
if (!res)
it = itbk;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel)
{
InIt itbk(it);
int nselbk = nsel;
bool res = do_match(it, end, sel, nsel);
if( !res ) {
it = itbk;
nsel = nselbk;
}
return res;
}
enum {sel_len = RX0::sel_len + tail::sel_len};
};
template<> struct seq<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
template<class InIt>
static bool do_match(InIt& it, const InIt& end)
{ return true; }
template<class InIt>
static bool do_match(InIt& it, const InIt& end, InIt* sel, int& nsel)
{ return true; }
enum {sel_len = 0};
};
template<
class RX0, class RX1, class RX2=seqe, class RX3=seqe, class RX4=seqe,
class RX5=seqe, class RX6=seqe, class RX7=seqe, class RX8=seqe, class RX9=seqe>
struct alt /* alternative - like "(abc|def)" */
{
typedef alt<RX1,RX2,RX3,RX4,RX5,RX6,RX7,RX8,RX9,seqe> tail;
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
if (RX0::match(it, end))
return true;
else
return tail::match(it, end);
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
if (RX0::match(it, end, sel, nsel))
return true;
else
return tail::match(it, end, sel + RX0::sel_len, nsel);
}
enum {sel_len = RX0::sel_len + tail::sel_len};
};
template<> struct alt<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{ return false; }
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{ return false; }
enum {sel_len = 0};
};
template<class RX>
struct select
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return RX::match(it, end);
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
InIt first(it);
int _nsel = 0;
if (RX::match(it, end, sel + 2, _nsel)) {
sel[0] = first;
sel[1] = it;
nsel += 1 + _nsel;
return true;
} else {
sel[0] = sel[1] = InIt();
return false;
}
}
enum {sel_len = 1};
};
//-////////////////////////////////////////////////////////////////////////template<bool INCLUSIVE> struct ws_
:cc<INCLUSIVE, ' ', '\t'>
{};
typedef ws_<true> ws;
typedef ws_<false> not_ws;
template<bool INCLUSIVE> struct digit_
:range<'0','9',INCLUSIVE>
{};
typedef digit_<true> digit;
typedef digit_<false> not_digit;
template<bool INCLUSIVE> struct xdigit_
:set< range<'0','9',INCLUSIVE>, range<'A','F',INCLUSIVE>, range<'a','f',INCLUSIVE> >
{};
typedef digit_<true> xdigit;
typedef digit_<false> not_xdigit;
template<bool INCLUSIVE> struct alpha_
:set< range<'A','Z',INCLUSIVE>, range<'a','z',INCLUSIVE> >
{};
typedef alpha_<true> alpha;
typedef alpha_<false> not_alpha;
template<bool INCLUSIVE> struct alphanum_
:set< alpha_<INCLUSIVE>, digit_<INCLUSIVE> >
{};
typedef alphanum_<true> alphanum;
typedef alphanum_<false> not_alphanum;
struct endl:seq< opt< c<'\r'> >, c<'\n'> > {};
//Shura addedstruct dot {/*any character*/template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end);
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
};//dot /*any character*/struct ends {/*end of input sequence*/template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return it == end;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it, end);
}
enum {sel_len = 0};
};//ends /*end of input sequence*/
}//namespace static_regex#endif//__STATIC_REGEX_H_INCLUDED__
1) Может всёж-таки завести структурку для группировки — ато некошерно как-то получается...
К тому же требующийся размер массива можно теперь вычислить.
Не могут быть найдены — т.к. операторы повторения (star, plus, opt) реализованы как "неуступчивые". Т.е. заняв какой-либо символ, они не умеют отдать его назад.
К сожалению проблема не в коде этих операторов, а во взаимодействии их и последующих.
Т.е. эту проблему, предположительно, можно решить подправив код seq::match.
3) Завести перегруженные функции помощники, типа:
//Приверка последовательностиtemplate <class RX, class Iter>
bool test(RX, Iter first, Iter last);
//Приверка последовательности и заполнение группtemplate <class RX, class Iter>
bool match(RX, Iter first, Iter last, RX::groups&);
//Поиск в последовательностиtemplate <class RX, class Iter>
bool search(RX, Iter first, Iter last, RX::groups&);
Можно ещё для string перегрузить...
... << RSDN@Home 1.1.0 stable >>
Re: Предложения по дальнейшей разработке
От:
Аноним
Дата:
29.12.03 13:12
Оценка:
Здравствуйте, Tonal-, Вы писали:
T>Предложения по дальнейшей разработке:
T>1) Может всёж-таки завести структурку для группировки — ато некошерно как-то получается...
Пришел к аналогичному выводу. Скоро выложу.
T>2) Выражение типа "/.*\.cpp/" Не могут быть найдены.
Известная беда всех реализаций регулярных выражений.
Что же касается "нежадных" операторов, то у регулярного выражения есть задача: "найти наидлиннейшее совпадение". Чтобы решить ее с "нежадными" операторами, придется перепробовать все комбинаци их "жадности" — от "совсем нежадный" до "почти жадный". Задачка не для слабонервных. И сложность у нее полиномиальная, а то и (не приведи господь) экспоненциальная.
Так что увольте, "нежадные" операторы не будут реализованы никогда — проще потребовать от программиста
чтобы он писал выражения учитывающие "жадность".
например так:
/.*\.cpp/ <==> /[^.]*\.cpp/
T>3) Завести перегруженные функции помощники, типа:
Никто не мешает. Но это лучше отдать на откуп "прикладному программисту"
T>//Поиск в последовательности T>template <class RX, class Iter> T>bool search(RX, Iter first, Iter last, RX::groups&); T>[/ccode]
Это еще одна задача из разряда "и хочется и колется"..
"brute force" подход всегда открыт, но мне не нравится его сложность: O(length(rx) * length(string))
Думаю как притянуть сюда алгоритм Кнута-Морриса-Пратта, но в данной реализации это скорее всего нереализуемо (точнее, реализуемо, но компилируется ОЧЕНЬ долго)
T>Можно ещё для string перегрузить...
Опять-таки "прикладной программист" напишет этот костыль для себя гораздо лучше чем я для него.
Ура, я тут не один! ;-в
T>>2) Выражение типа "/.*\.cpp/" Не могут быть найдены. А>Известная беда всех реализаций регулярных выражений. А>Что же касается "нежадных" операторов, то у регулярного выражения есть задача: "найти наидлиннейшее совпадение". Чтобы решить ее с "нежадными" операторами, придется перепробовать все комбинаци их "жадности" — от "совсем нежадный" до "почти жадный". Задачка не для слабонервных. И сложность у нее полиномиальная, а то и (не приведи господь) экспоненциальная.
Мне кажется, ты немножко путаешь понятия:
Квалификатор повторения может быть "жадным" и "не жадным".
"Жадные" могут быть "уступчивым" и "неуступчивым".
Так что имеем 3 варианта.
Сейчас у нас "жадный неуступчивый", т.е. система не поддерживает откатов.
Это не даёт написать выражение типа "оканчивается на" в обшем виде. Например нельзя написать выражение для выделения доменов первого уровня в url-е, или для выделения имени файла (ведь "." — допустимый символ). Для обработки подобных выражений придётся прибегать к другой библиотеке, а не хочется.
А насчёт "наидлиннейшего" совпадения — для отыскания оного, надо перебрать все возможные варианты.
Пример
"abcc"
/(a*)(abc+|b)/
Тут "наидлиннейшее" бидет вся сторка по пустой первой группе и первой алтернативе во второй. Её и найдёт ДКА, или POSIX НКА. Ну а обычный НКА или интерпретатор найдёт только "ab".
Так что я предлагаю не очень напрягаться по этому поводу (Perl ведь не напрягаются). Лучше попытаться построить ДКА.
А на счёт нетривиальности задачки — всё это достаточно просто реализовать (код ниже).
Чтобы не слишком тормазило решения такие:
А) Для всех классов определить константу undoable — откатываемость.
А1) Для терминалов (c, cc, s...) — false.
А2) Для повторителей и альтернативы — true.
А3) Для контейнеров — вычисляется по или от содержимого.
Б) В последовательности разбираем разные варианты и пишем код.
В) Создаём класс контейнер noundo — который делает содержащееся в нём выражение неуступчивым (undoable = false).
Для моего бенчмарка эта версия даёт всего лишь 4-5 кратный выигрышь в скорости по сравнению с boost::regex.
Но, если переписать варажение с использованием noundo в разумнвх местах, то опять выигрыш примерно на 1,5-2 порядка!
Выражение вот:
typedef seq<
noundo< select< plus<digit> > >, //код ответа
noundo< select< alt<cc<'-',' '>, endl> > >, //символ переноса - если '-' - то ответ многострочный
select< star<not_cc<'\r','\n'> > >, //текст сообщения - все до конца строки
opt<endl> //необязательный перевод строки
>
rx_ftp2;
T>>3) Завести перегруженные функции помощники, типа: А>Никто не мешает. Но это лучше отдать на откуп "прикладному программисту"
Я всёж таки изобразил 2 перегруженных варианта test. Зачем плодить везде одинаковые куски, если можно их вставить в библиотеку не нарушая единства.
Остальные — подожду структурку для групп. ;-в
T>>//Поиск в последовательности
T>>template <class RX, class Iter>
T>>bool search(RX, Iter first, Iter last, RX::groups&);
T>>
А>Это еще одна задача из разряда "и хочется и колется".. А>"brute force" подход всегда открыт, но мне не нравится его сложность: O(length(rx) * length(string)) А>Думаю как притянуть сюда алгоритм Кнута-Морриса-Пратта, но в данной реализации это скорее всего нереализуемо (точнее, реализуемо, но компилируется ОЧЕНЬ долго)
Тут тоже есть много вариантов для оптимизации... хотя согласен, надо пока обдумать...
Таки код:
//http://www.rsdn.ru/Forum/Message.aspx?mid=456007&only=1
//0.00000003#ifndef __STATIC_REGEX_H_INCLUDED__
#define __STATIC_REGEX_H_INCLUDED__
namespace static_regex {
typedef wchar_t rx_char;
struct seqe {};
enum { stpc = 0xBAADC0DE };
template<bool> struct grp_present {};
template<rx_char E>
struct c {/* literal charachter */static bool char_match(rx_char ch)
{ return (E == ch); }
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) && (E == static_cast<rx_char>(*it));
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
enum {undoable = 0};
};
template<
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct s {/* literal string - up to 30 symbols */typedef s<
c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,
c21,c22,c23,c24,c25,c26,c27,c28,c29,stpc
> tail;
enum { length = 1 + tail::length };
template<class InIt>
static bool do_match(InIt& it)
{
return (c0 == static_cast<rx_char>(*it)) && tail::do_match(++it);
}
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
InIt tend( it + length );
if( !(tend < end || tend == end) )
return false;
InIt t(it);
bool res = do_match(it);
if( !res ) it = t;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
enum {undoable = 0};
};
template<> struct s<
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc>
{
enum { length = 0 };
template<class InIt>
static bool do_match(InIt&)
{ return true; }
};
template<
bool INCLUSIVE,
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct cc_ /* charachter class - like "[abcdEF]" or "[^abcdEF]" */
{
typedef cc_<INCLUSIVE,
c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,
c21,c22,c23,c24,c25,c26,c27,c28,c29,stpc
> tail;
static bool char_match(rx_char ch)
{ return (c0 == ch) || tail::char_match(ch); }
template<bool> struct sel {};
static bool char_match(rx_char ch, sel<true>)
{ return char_match(ch); }
static bool char_match(rx_char ch, sel<false>)
{ return !char_match(ch); }
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) &&
char_match(static_cast<rx_char>(*it), sel<INCLUSIVE>());
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
enum {undoable = 0};
};
template<> struct cc_<true,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc>
{
static bool char_match(rx_char ch)
{ return false; }
};
template<> struct cc_<false,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc,
stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc, stpc>
{
static bool char_match(rx_char ch)
{ return false; }
};
template<
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct cc:cc_<true,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29>
{};
template<
int c0, int c1, int c2 =stpc, int c3 =stpc, int c4 =stpc, int c5 =stpc, int c6 =stpc, int c7 =stpc, int c8 =stpc, int c9 =stpc,
int c10=stpc, int c11=stpc, int c12=stpc, int c13=stpc, int c14=stpc, int c15=stpc, int c16=stpc, int c17=stpc, int c18=stpc, int c19=stpc,
int c20=stpc, int c21=stpc, int c22=stpc, int c23=stpc, int c24=stpc, int c25=stpc, int c26=stpc, int c27=stpc, int c28=stpc, int c29=stpc>
struct not_cc:cc_<false,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24,c25,c26,c27,c28,c29>
{};
template<rx_char F, rx_char B, bool INCLUSIVE=true>
struct range /* charachter range - like "[a-d]" or "[^a-d]" */
{
static bool char_match(rx_char ch)
{
return (((F < B) ? F : B) <= ch) && (ch <= ((F < B) ? B : F));
}
template<bool> struct sel {};
static bool char_match(rx_char ch, sel<true>)
{ return char_match(ch); }
static bool char_match(rx_char ch, sel<false>)
{ return !char_match(ch); }
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) &&
char_match(static_cast<rx_char>(*it), sel<INCLUSIVE>());
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
enum {undoable = 0};
};
template<
class CC0, class CC1, class CC2=seqe, class CC3=seqe, class CC4=seqe,
class CC5=seqe, class CC6=seqe, class CC7=seqe, class CC8=seqe, class CC9=seqe>
struct set {/* charachter set */typedef set<CC1,CC2,CC3,CC4,CC5,CC6,CC7,CC8,CC9,seqe> tail;
static bool char_match(rx_char ch)
{
return CC0::char_match(ch) || tail::char_match(ch);
}
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end) &&
char_match( static_cast<rx_char>(*it) );
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
enum {undoable = 0};
};
template<> struct set<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
static bool char_match(rx_char)
{ return false; }
};
template<class RX>
struct star {/* zero or more occurences of RX - like "(abc)*" */template<class InIt>
static bool match(InIt& it, const InIt& end)
{
while( RX::match(it,end) );
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&, grp_present<false>) {
return match(it, end);
}
template<class InIt>
static bool match(
InIt& it, const InIt& end, InIt* sel, int& nsel, grp_present<true>
) {
InIt _sel2[2*RX::sel_len];
int _nsel = 0, _nsel2 = 0;
while (RX::match(it, end, _sel2, _nsel)) {
for (int i = 0; i < 2*_nsel; ++i)
sel[i] = _sel2[i];
_nsel2 = _nsel;
_nsel = 0;
}
if (_nsel2)
nsel += _nsel2;
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
return match(it, end, sel, nsel, grp_present<(!!sel_len)>());
}
enum {sel_len = RX::sel_len};
enum {undoable = 1};
};
template<class RX>
struct plus {/* one or more occurences of RX - like "(abc)+" */template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return RX::match(it, end) && star<RX>::match(it, end);
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&, grp_present<false>) {
return match(it, end);
}
template<class InIt>
static bool match(
InIt& it, const InIt& end, InIt* sel, int& nsel, grp_present<true>
) {
int _nsel2 = 0;
InIt _sel2[2*RX::sel_len];
if (!RX::match(it, end, _sel2, _nsel2))
return false;
int _nsel = 0;
star<RX>::match(it, end, sel, _nsel);
if (!_nsel) {
for (int i = 0; i < _nsel2; ++i)
sel[i] = _sel2[i];
_nsel = _nsel2;
}
nsel += _nsel;
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
return match(it, end, sel, nsel, grp_present<(!!sel_len)>());
}
enum {sel_len = RX::sel_len};
enum {undoable = 1};
};
template<class RX>
struct opt {/* optional occurence of RX - like "(abc)?" */template<class InIt>
static bool match(InIt& it, const InIt& end)
{
RX::match(it, end);
return true;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel)
{
RX::match(it, end, sel, nsel);
return true;
}
enum {sel_len = RX::sel_len};
enum {undoable = 1};
};
template<
class RX0, class RX1, class RX2=seqe, class RX3=seqe, class RX4=seqe,
class RX5=seqe, class RX6=seqe, class RX7=seqe, class RX8=seqe, class RX9=seqe>
struct seq /* sequence of RX - like "(abc)(def)" */
{
typedef seq<RX1,RX2,RX3,RX4,RX5,RX6,RX7,RX8,RX9,seqe> tail;
template <bool> struct simple{};
template<class InIt>
static bool do_match(InIt& it, const InIt& end, simple<true>) {
return RX0::match(it, end) && tail::do_match(it, end);
}
template<class InIt>
static bool do_match(InIt& it, const InIt& end, simple<false>) {
for (InIt _end = end; it != _end; --_end) {
InIt _it = it;
if (!RX0::match(_it, _end))
return false;
if (tail::do_match(_it, end)) {
it = _it;
return true;
}
}
return false;
}
template<class InIt>
static bool do_match(InIt& it, const InIt& end) {
return do_match(
it, end, simple<(!RX0::undoable || tail::end_seq)>()
);
}
template<class InIt>
static bool do_match(
InIt& it, const InIt& end, InIt* sel, int& nsel, simple<true>
) {
return RX0::match(it, end, sel, nsel)
&& tail::do_match(it, end, sel + 2*RX0::sel_len, nsel);
}
template<class InIt>
static bool do_match(
InIt& it, const InIt& end, InIt* sel, int& nsel, grp_present<false>
) {
for (InIt _end = end; it != _end; --_end) {
InIt _it(it);
if (!RX0::match(_it, _end))
return false;
if (tail::do_match(_it, end, sel, nsel)) {
it = _it;
return true;
}
}
return false;
}
template<class InIt>
static bool do_match(
InIt& it, const InIt& end, InIt* sel, int& nsel, grp_present<true>
) {
for (InIt _end = end; it != _end; --_end) {
InIt _it(it);
InIt _sel[RX0::sel_len + 1];
int _nsel = 0;
if (!RX0::match(_it, _end, _sel, _nsel))
return false;
if (tail::do_match(_it, end, sel + 2*RX0::sel_len, nsel)) {
for (int i = 0; i < 2*_nsel; ++i)
sel[i] = _sel[i];
nsel += _nsel;
it = _it;
return true;
}
}
return false;
}
template<class InIt>
static bool do_match(
InIt& it, const InIt& end, InIt* sel, int& nsel, simple<false>
) {
return do_match(it, end, sel, nsel, grp_present<(!!RX0::sel_len)>());
}
template<class InIt>
static bool do_match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
return do_match(
it, end, sel, nsel, simple<(!RX0::undoable || tail::end_seq)>()
);
}
template<class InIt>
static bool match(InIt& it, const InIt& end) {
InIt itbk(it);
bool res = do_match(it, end);
if (!res)
it = itbk;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
InIt itbk(it);
int nselbk = nsel;
bool res = do_match(it, end, sel, nsel);
if( !res ) {
it = itbk;
nsel = nselbk;
}
return res;
}
enum {sel_len = RX0::sel_len + tail::sel_len};
enum {undoable = RX0::undoable || tail::undoable};
enum {end_seq = 0};
};
template<> struct seq<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
template<class InIt>
static bool do_match(InIt&, const InIt&)
{ return true; }
template<class InIt>
static bool do_match(InIt& it, const InIt& end, InIt* sel, int& nsel)
{ return true; }
enum {sel_len = 0};
enum {undoable = 0};
enum {end_seq = 1};
};
template<
class RX0, class RX1, class RX2=seqe, class RX3=seqe, class RX4=seqe,
class RX5=seqe, class RX6=seqe, class RX7=seqe, class RX8=seqe, class RX9=seqe>
struct alt /* alternative - like "(abc|def)" */
{
typedef alt<RX1,RX2,RX3,RX4,RX5,RX6,RX7,RX8,RX9,seqe> tail;
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
if (RX0::match(it, end))
return true;
else
return tail::match(it, end);
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
if (RX0::match(it, end, sel, nsel))
return true;
else
return tail::match(it, end, sel + 2*RX0::sel_len, nsel);
}
enum {sel_len = RX0::sel_len + tail::sel_len};
enum {undoable = 1};
};
template<> struct alt<seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe,seqe>
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{ return false; }
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{ return false; }
enum {sel_len = 0};
enum {undoable = 0};
};
template<class RX>
struct select
{
template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return RX::match(it, end);
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel) {
InIt first(it);
int _nsel = 0;
if (RX::match(it, end, sel + 2, _nsel)) {
sel[0] = first;
sel[1] = it;
nsel += 1 + _nsel;
return true;
} else {
sel[0] = sel[1] = InIt();
return false;
}
}
enum {sel_len = 1};
enum {undoable = RX::undoable};
};
//-////////////////////////////////////////////////////////////////////////template<bool INCLUSIVE> struct ws_
:cc<INCLUSIVE, ' ', '\t'>
{};
typedef ws_<true> ws;
typedef ws_<false> not_ws;
template<bool INCLUSIVE> struct digit_
:range<'0','9',INCLUSIVE>
{};
typedef digit_<true> digit;
typedef digit_<false> not_digit;
template<bool INCLUSIVE> struct xdigit_
:set< range<'0','9',INCLUSIVE>, range<'A','F',INCLUSIVE>, range<'a','f',INCLUSIVE> >
{};
typedef digit_<true> xdigit;
typedef digit_<false> not_xdigit;
template<bool INCLUSIVE> struct alpha_
:set< range<'A','Z',INCLUSIVE>, range<'a','z',INCLUSIVE> >
{};
typedef alpha_<true> alpha;
typedef alpha_<false> not_alpha;
template<bool INCLUSIVE> struct alphanum_
:set< alpha_<INCLUSIVE>, digit_<INCLUSIVE> >
{};
typedef alphanum_<true> alphanum;
typedef alphanum_<false> not_alphanum;
struct endl:seq< opt< c<'\r'> >, c<'\n'> > {};
//Shura addedstruct dot {/*any character*/template<class InIt>
static bool match(InIt& it, const InIt& end)
{
bool res = (it != end);
if( res ) ++it;
return res;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it,end);
}
enum {sel_len = 0};
enum {undoable = 0};
};//dot /*any character*/struct ends {/*end of input sequence*/template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return it == end;
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt*, int&)
{
return match(it, end);
}
enum {sel_len = 0};
enum {undoable = 0};
};//ends /*end of input sequence*/template <class RX>
struct noundo {/*modifer for non undoable repeats*/template<class InIt>
static bool match(InIt& it, const InIt& end)
{
return RX::match(it, end);
}
template<class InIt>
static bool match(InIt& it, const InIt& end, InIt* sel, int& nsel)
{
return RX::match(it, end, sel, nsel);
}
enum {sel_len = RX::sel_len};
enum {undoable = 0};
};//noundo
//Приверка последовательностиtemplate <class RX, class Iter>
bool test(RX, Iter first, Iter last) {
return RX::match(first, last) && first == last;
}
template <class RX, class String>
bool test(RX, const String& str) {
typename str::const_iterator first = str.begin();
return RX::match(first, str.end()) && first == str.end();
}
}//namespace static_regex#endif//__STATIC_REGEX_H_INCLUDED__
-убраны операторы начинающиеся с "not_" — вместо них используйте "name_<false", где "name" — имя оператора. Например not_cc<A,B,C> == cc_<false,A,B,C>
-добавлены многоаргументные версии для всех нетерминальных операторов (т.е. star, plus, opt, и тд) — их имена также заканчиваются на "_"
-у оператора захвата подсовпадения mark<int ID, class RX> появился параметр ID — это произвольный числовой идентификатор подсовпадения.
-Нельзя пользоваться member-функцией RX::match() — она стала насколько мохнатой, что пришлось ввести глобальные функции match().
Их надо использовать так:
-Теперь библиотека использeт контейнеры для хранения подсовпадений. Доступно 2 вида контейнеров:
-- match_array<class InIt,int N> — контейнер основанный на статическом массиве размера N. Если подсовпадений больше чем размер контейнера, то "лишние" подсовпадения игнорируются.
-- match_array<class InIt> — контейнер основанный на std::vector<>.
-- более подробно, смотрите эти классы и класс submatch в исходниках.
Пример использования:
-Добавлен оператор "обратной ссылки" bk<int ID>
Пример использования (псевдокод)
typedef seq<mark<1,cc<'\'','"'>>, s<"HELLO">, bk<1>> myrx;
//myrx совпадет со строкой "HELLO" и со строкой 'HELLO'
-к ранее имевшимся "тупым" операторам-повторителям star<>, plus<>, rep<> добавлены следующие их модификации:
-- "нежадные": xstar<>, xplus<>, xrep<>
Пример использования (псевдокод)
typedef seq<mark<1,plus<dot>>, mark<2,s<".cpp">>> myrx1;
typedef seq<mark<1,xplus<dot>>, mark<2,s<".cpp">>> myrx2;
//myrx2 совпадет со строкой "file1.cpp", а myrx1 - нет,
//т.к. оператор plus<dot> сьест все до конца строки.
-- "жадные": xxstar<>, xxplus<>, xxrep<>
Пример использования (псевдокод)
typedef seq<mark<1,xxplus<dot>>, mark<2,s<".cpp">>> myrx3;
//при проверке строки "file1.cpp.cpp.cpp.cpp"
//myrx2 совпадет с подстрокой "file1.cpp", а myrx3 - с полной строкой,
Внимание: использование этих модификаций влечет за собой значительное падение производительности. "нежадных" — примерно в 2 раза, "жадных" — в десятки раз.
-Бенчмарки
Не улучшились, но и не ухудшились.
Был также проведен тест на производительность "жадных" операторов.
выражение "(.+)(\.cpp)" проверялось на строке "file.cpp.cpp.cpp.cpp" длиной 4 мегабайта. boost::regex оказалась хуже в 10 раз...
Наверное надо включить #include <vector> если есть элементы зависимые от std::vector.
Не совсем понятно назначение функций get_match_at и set_match_count. Почму нельзя просто воспользоваться методами operator[]() и resize(). Тогда не понадобятся friend объявления, которые во первых не совсем тривиальны для шаблонов, а во вторых разные компиляторы их по разному трактуют (Borland например требует полной спецификации шаблонных параметров.)
По моему operator[]() для match_vector и для match_array несколько расходятся. Если уж проверять выход за граници, то для обоих, или уж не проверять совсем. А так как-то не согласованно получается.
Я бы не проверял (по крайний мере в релизе).
Начитался давеча Саттера — он говорит что не следует применять наследование без вестких на то причин — это я о match_vector. Собственно если переименовать функцию reset() в clear() то вместо match_vector можно будет использовать и std::vector, и std::deque и boost::array. В общем непонятна суть ограничений.
Есть некоторый недостаток в ID к mark-у: непонятно как избежать совподения. Оно может случиться не только из за ошибок набивки, но и в случаи собирания выражения из разных частей. ;-с
По моему было бы очень удобно, если для любого вырожения можно было бы получить максимальное количество подсовпадений. Реализация — тривиальна: для символьных классов = 0, для mark = подвырожение + 1, для контейнеров = сумма подвырожений.
Может быть следует предоставить 2 формы alt — полный перебор (POSIX-like), или до первого совпадения (Perl-like) — некоторые выражения можно соптимизировать.
Ну вроде на первый взгляд всё, спать пора. ;-в
... << RSDN@Home 1.1.0 stable >>
Re[3]: Некоторые замечания по коду 4
От:
Аноним
Дата:
02.02.04 14:01
Оценка:
Здравствуйте, Tonal-, Вы писали:
T>Наверное надо включить #include <vector> если есть элементы зависимые от std::vector.
Это я недоглядел.
Данныай файл включен в более развесистую либу, где с вектором разбираются вполне отдельно.
Впрочем, включение <vector> и не надо — достаточно forward-declaration.
T>Не совсем понятно назначение функций get_match_at и set_match_count. Почму нельзя просто воспользоваться методами operator[]() и resize().
Эти функци реализуют дополнительный контейнеро-независимый уровень абстракции.
Читай ниже — поймешь.
T>а во вторых разные компиляторы их по разному трактуют (Borland например требует полной спецификации шаблонных параметров.)
Есть только одна трактовка — стандарт C++. Если компилятор стандарт не понимает, то идет он пусть в /dev/nul
T>По моему operator[]() для match_vector и для match_array несколько расходятся. Если уж проверять выход за граници, то для обоих, или уж не проверять совсем.
Гм. А где я проверяю границы? Это отадно на откуп реализации контейнера.
T>Начитался давеча Саттера — он говорит что не следует применять наследование без вестких на то причин — это я о match_vector. T>Собственно если переименовать функцию reset() в clear() то вместо match_vector можно будет использовать и std::vector, и std::deque и boost::array. T>В общем непонятна суть ограничений.
Суть в том, что требуется обьект способный принимать и хранить подсовпадения.
Для этого был сделан match_array который описывает в описывает минимальные требования к
контейнеру подсовпадений. И уже потом, был добавлен match_vector который аналогичен match_array.
В общем, я согласен с твоим замечанием, что match_vector слишком исскуственно ограничен и можно хранить подсовпадения в любом контейнере который удовлетворяет требованиям "Sequence".
Но это ты как-нибудь сам реализуй
Работы, сосно гря немного:
— переименовать reset() в clear()
— переименовать match_type в value_type
— реализовать get_match_at и set_match_count для контейнеров других типов.
T>Есть некоторый недостаток в ID к mark-у: непонятно как избежать совподения.
В смысле, как избежать совпадения ID у двух разных mark-ов ?
А никак — да и ненужно — раз усер дал разным mark-ам одинаковый ID, то этого он и хотел
T>По моему было бы очень удобно, если для любого вырожения можно было бы получить максимальное количество подсовпадений. Реализация — тривиальна: для символьных классов = 0, для mark = подвырожение + 1, для контейнеров = сумма подвырожений.
Не вижу смысла. Количество подвыражений — функция входной строки, а не регулярного выражения. Вина этому — операторы-повторители.
Например, выражение "(a)*" на строке "aaaaaaaaaaa" даст очень много подвыражений, а на строке "bbbbbb" ни одного
T>Может быть следует предоставить 2 формы alt — полный перебор (POSIX-like),
Этот вариант реализован.
T>или до первого совпадения (Perl-like) — некоторые выражения можно соптимизировать.
Опять-таки: не вижу смысла. Оптимизация такого рода — задача усера. Например, если он напишет
выражение "(.*this|.*that|.*there)" — ежу понятно насколько быстрым будет такое варажение и как его оптимизировать
T>Ну вроде на первый взгляд всё, спать пора. ;-в
Нухшо, словечко "вырожение" от которого меня просто трясет и хочется кого-нибудь спишем на то что тебе спать очень хочется
Здравствуйте, Дарней, Вы писали:
Д>интересно, кто-нибудь знает, как работают POSIX NFA движки? Используют двухстековый метод?
Смотрел gnu regex, там вообще "классические" автоматы не используются.
Генерируется какой-то псевдокод, который потом интерпретируется.
Что-то подобное происходит и в boost::regex, но я там глубоко не копался.
Дело в том, что посредством автоматов некоторые вещи (маркирование подвыражений, обратные ссылки) сделать достаточно трудно, если не невозможно. По крайней мере, мне не удалось на автомате реализовать обратные ссылки.
Д>А как там решается проблема с "ленивыми" квантификаторами?
Для недетерминированного автомата — это вообще не проблема. Он может быть "уступчивым", а может быть "жадным" в зависимости от того, что предпринимать, если в текущем наборе альтернатив есть финальное состояние.
Есть такие варианты:
(1) останавливаемся, несмотря на то, что есть "не финальные" альтернативы. Получим наикратчайшее совпадение — автомат будет "уступчивым"
(2) продложаем, пока финальное состояние не будет единственным в наборе альтернатив. Получим наидлиннейшее совпадение — автомат будет "жадным"
__________
16.There is no cause so right that one cannot find a fool following it.
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>Дело в том, что посредством автоматов некоторые вещи (маркирование подвыражений, обратные ссылки) сделать достаточно трудно, если не невозможно. По крайней мере, мне не удалось на автомате реализовать обратные ссылки.
верно, некоторые вещи сделать нельзя. еще есть проблемы с метасимволами, насколько я понимаю
но можно попробовать прикрутить дополнительные фичи поверх автомата
DEA>Есть такие варианты: DEA>(1) останавливаемся, несмотря на то, что есть "не финальные" альтернативы. Получим наикратчайшее совпадение — автомат будет "уступчивым" DEA>(2) продложаем, пока финальное состояние не будет единственным в наборе альтернатив. Получим наидлиннейшее совпадение — автомат будет "жадным"
а если внутри выражения есть несколько квантификаторов для разных его частей, из них один "жадный" а другой "ленивый"?
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>C метасимволами как раз проблемы нету. Конечно, если автомат NFA. Просто трактуешь этот метасимвол как "простой" символ — и при двустековом моделировании он отработает нормально. А вот для DFA — метасимволы — это пипец полный — в особенности если алфавит — юникодный — автомат разрастается до поистине ненормальных размеров.
не совсем понял. вставлять их во входной поток, наравне с обычными символами?