Sorry, не разобрался с прикрепленными файлами
#pragma once
#include "TypeLists.h"
#include <memory>
namespace RegExp {
enum ERROR_STATE {
esNoError = 0,
esLimitError = 1,
esNoMatch = 2,
esOtherError = 3
};
// Интерфейс результата разбора
//////////////////////////////////////////////////////////////////////////////////////////////
class CResIf
{ public:
virtual ~CResIf() {}
virtual void AddNode(CResIf* n) = 0;
virtual void AddValue(const char* v, unsigned size = 0) = 0;
virtual unsigned GetType(void) const = 0;
virtual void SetParent(CResIf* p) {}
};
// Интерфейс интерпретатора регулярных выражений
//////////////////////////////////////////////////////////////////////////////////////////////
// Match может выполняться несколько раз. При передаче src==NULL, продолжается поиск в строке
// использованной в предыдущем вызове
// Reset сбрасывает результат поиска, что позволяет искать пересекающиеся диапазоны
class CRegExpIf
{ public:
virtual bool Match(CResIf*& r, const char* src = NULL, unsigned size = 0) = 0;
virtual const char* Catch(unsigned level, unsigned& size) = 0;
virtual ERROR_STATE Replace(char* dst, unsigned size) = 0;
virtual void Reset(void) = 0;
virtual CRegExpIf* Clone(void) const = 0;
};
enum PREDICATE_TYPE {
ptSpace = 0, // \s
ptBegin = 1, // ^
ptEnd = 2, // $
ptSomeChar = 3, // .
ptDigit = 4, // \d
ptWordChar = 5, // \w
ptWordBorder = 6 // \b
};
enum QUANTIFIER_TYPE {
qtGreed = 0,
qtLazy = 1,
qtFast = 2
};
// Однократное сопоставление
//////////////////////////////////////////////////////////////////////////////////////////////
class O
{ public:
O(): IsOnce(true) {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ if (!IsOnce) return -1;
IsOnce = false;
return size;
}
const char* Catch(unsigned level, unsigned& size) { return NULL; }
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize) { return esOtherError; }
void Clear(void) { IsOnce = true; }
private:
bool IsOnce;
};
// Последовательность подвыражений
//////////////////////////////////////////////////////////////////////////////////////////////
// Должны быть последовательно выполнены все подвыражения
//
// 1. Вызывает Head.Match, пока не вернет -1 (для повтора квантификаторов)
// 2. При неуспехе, вызывает Clear для Head и Tail
template <class T> class E;
template <> class E<NullType>
{ public:
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res) {return 0;}
const char* Catch(unsigned level, unsigned& size) { return NULL; }
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize) {return esNoError;}
void Clear(void) {}
};
template <class T, class U>
class E< TypeList<T,U> >
{ public:
E(): Head(), Tail() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int Offset = 0;
for (;;) {
Offset = Head.Match(m,s,size,res);
if (Offset<0) {
Clear();
return Offset;
}
int r = Tail.Match(m,s+Offset,size-Offset,res);
if (r>=0) {
Offset += r;
break;
}
}
return Offset;
}
const char* Catch(unsigned level, unsigned& size)
{ const char* r = Head.Catch(level,size);
if (r==NULL)
r = Tail.Catch(level,size);
return r;
}
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize)
{ ERROR_STATE r = Head.Replace(m,dst,maxsize);
if (r!=esNoError) return r;
return Tail.Replace(m,dst,maxsize);
}
void Clear(void)
{ Head.Clear();
Tail.Clear();
}
private:
T Head;
E< U > Tail;
};
template <class T> class E
{ public:
E(): Elem() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ return Elem.Match(m,s,size,res);
}
const char* Catch(unsigned level, unsigned& size)
{ return Elem.Catch(level,size);
}
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize)
{ return Elem.Replace(m,dst,maxsize);
}
void Clear(void)
{ Elem.Clear();
}
private:
E< TL_1(T) > Elem;
};
// Дизъюнкция подвыражений
//////////////////////////////////////////////////////////////////////////////////////////////
// Должно быть выполнено любое из подвыражений
template <class T> class D;
template <> class D<NullType>
{ public:
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res) {return -1;}
const char* Catch(unsigned level, unsigned& size) { return NULL; }
void Clear(void) {}
};
template <class T, class U>
class D< TypeList<T,U> >
{ public:
D(): Head(), Tail() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int r = Head.Match(m,s,size,res);
if (r<0)
r = Tail.Match(m,s,size,res);
return r;
}
const char* Catch(unsigned level, unsigned& size)
{ const char* r = Head.Catch(level,size);
if (r==NULL)
r = Tail.Catch(level,size);
return r;
}
void Clear(void)
{ Head.Clear();
Tail.Clear();
}
private:
T Head;
D< U > Tail;
};
template <class T> class D
{ public:
D(): Elem() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int r = Elem.Match(m,s,size,res);
if (r<0) return 0;
return r;
}
const char* Catch(unsigned level, unsigned& size)
{ return Elem.Catch(level,size);
}
void Clear(void)
{ Elem.Clear();
}
private:
T Elem;
};
// Квантификатор
//////////////////////////////////////////////////////////////////////////////////////////////
// Проверка r==0 после Elem.Match предназначена для определения утверждений нулевой ширины
// под квантификатором
template <class T, class U = NullType, int mn = 0, int mx = 0, QUANTIFIER_TYPE t = qtFast>
class Q
{ public:
Q(): Elem(), Stop(), Cnt(-1) {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ return Match(m,s,size,res,Int2Type<t>());
}
const char* Catch(unsigned level, unsigned& size)
{ return Elem.Catch(level,size);
}
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize)
{ for (int i=0;i<mn;i++) {
ERROR_STATE r = Elem.Replace(m,dst,maxsize);
if (r!=esNoError) return r;
}
return esNoError;
}
void Clear(void)
{ Elem.Clear();
}
private:
template <class S>
int StopCond(CRegExpIf* m, const char* s, unsigned size, Type2Type<S>)
{ CResIf* r = NULL;
Stop.Clear();
return Stop.Match(m,s,size,r);
}
template <>
int StopCond(CRegExpIf* m, const char* s, unsigned size, Type2Type<NullType>)
{ return -1;
}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res, Int2Type<qtGreed>)
{ unsigned Offset = 0;
if (Cnt<0) {
for (Cnt=0;;) {
int r = StopCond(m,s+Offset,size-Offset,Type2Type<U>());
if (r>=0) break;
Elem.Clear();
r = Elem.Match(m,s+Offset,size-Offset,res);
if (r<0) return break;
Cnt++;
Offset += r;
if (mx>0)
if (Cnt>=mx) break;
}
if (Cnt<mn) return -1;
return Offset;
}
if (Cnt<=mn) return -1;
Cnt--;
for (int i=0;i<Cnt;i++) {
Elem.Clear();
int r = Elem.Match(m,s+Offset,size-Offset,res);
if (r<0) return -1;
Offset += r;
}
return Offset;
}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res, Int2Type<qtLazy>)
{ unsigned Offset = 0;
if (mx>0)
if (Cnt>=mx) return -1;
for (int i=0;i<Cnt;i++) {
Elem.Clear();
int r = Elem.Match(m,s+Offset,size-Offset,res);
if (r<0) return -1;
Offset += r;
}
for (;;) {
int r = StopCond(m,s+Offset,size-Offset,Type2Type<U>());
if (r>=0) break;
Elem.Clear();
r = Elem.Match(m,s+Offset,size-Offset,res);
if (r<=0) break;
if (Cnt<0) Cnt = 0;
Cnt++;
Offset += r;
if (mx>0)
if (Cnt>=mx) break;
}
if (Cnt==0) return -1;
if (Cnt<0) Cnt = 0;
if (Cnt<mn) return -1;
return Offset;
}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res, Int2Type<qtFast>)
{ unsigned Offset = 0;
if (Cnt>=0) return -1;
for (Cnt=0;;) {
int r = StopCond(m,s+Offset,size-Offset,Type2Type<U>());
if (r>=0) break;
Elem.Clear();
r = Elem.Match(m,s+Offset,size-Offset,res);
if (r<=0) break;
Cnt++;
Offset += r;
if (mx>0)
if (Cnt>=mx) break;
}
if (Cnt<mn) return -1;
return Offset;
}
E< T > Elem;
U Stop;
int Cnt;
};
// Захват
//////////////////////////////////////////////////////////////////////////////////////////////
template <class T> class B
{ public:
B(): Elem(), Start(NULL), Size(0) {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ Clear();
int r = Elem.Match(m,s,size,res);
if (r>=0) {
Start = s;
Size = size;
if (res!=NULL)
res->AddValue(s,Size);
}
return r;
}
const char* Catch(unsigned level, unsigned& size)
{ if (level==0) {
size = Size;
return Start;
}
return Elem.Catch(level-1,size);
}
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize)
{ return Elem.Replace(m,dst,maxsize);
}
void Clear(void)
{ Start = NULL;
Size = 0;
}
private:
E< T > Elem;
const char* Start;
unsigned Size;
};
// Предикат
//////////////////////////////////////////////////////////////////////////////////////////////
// f задает инверсию предиката
template <PREDICATE_TYPE p, bool f = false> class P;
template <bool f> class P<ptSpace,f>: public O
{ public:
P(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int r = -1;
if (size==0) return -1;
if (O::Match(m,s,1,res)<0) return -1;
if (s[0]==' ') r = 1;
if (s[0]==0x09) r = 1;
if (s[0]==0x0A) r = 1;
if (s[0]==0x0D) r = 1;
if (f) r = -r;
return r;
}
};
template <bool f> class P<ptBegin,f>: public O
{ public:
P(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ unsigned sz;
if (O::Match(m,s,0,res)<0) return -1;
const char* str = m->Catch(0,sz);
if (str==NULL) return -1;
int r = -1;
if (str==s) r = 1;
if (f) r = -r;
return r;
}
};
template <bool f>
class P<ptEnd,f>: public O
{ public:
P(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ if (O::Match(m,s,0,res)<0) return -1;
if (size==0) return f?-1:0;
return f?0:-1;
}
};
template <bool f> class P<ptSomeChar,f>: public O
{ public:
P(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ if (size==0) return -1;
if (O::Match(m,s,1,res)<0) return -1;
if (f) return -1;
return 1;
}
};
template <bool f> class P<ptDigit,f>: public O
{ public:
P(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int r = -1;
if (size==0) return -1;
if (O::Match(m,s,1,res)<0) return -1;
if ((s[0]>='0')&&(s[0]<='9')) r = 1;
if (f) r = -r;
return r;
}
};
template <bool f> class P<ptWordChar,f>: public O
{ public:
P(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int r = -1;
if (size==0) return -1;
if (O::Match(m,s,1,res)<0) return -1;
if (s[0]=='_') r = 1;
if ((s[0]>='a')&&(s[0]<='z')) r = 1;
if ((s[0]>='A')&&(s[0]<='Z')) r = 1;
if ((s[0]>='а')&&(s[0]<='я')) r = 1;
if ((s[0]>='А')&&(s[0]<='Я')) r = 1;
if ((s[0]=='ё')||(s[0]=='Ё')) r = 1;
if (P<ptDigit>().Match(m,s,1,res)==1) r = 1;
if (f) r = -r;
return r;
}
};
template <bool f> class P<ptWordBorder,f>: public O
{ public:
P(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ unsigned sz;
if (O::Match(m,s,0,res)<0) return -1;
if (size==0) return f?-1:0;
const char* str = m->Catch(0,sz);
if (str==NULL) return -1;
if (str==s) return f?-1:0;
const char* p = s-1;
if ((P<ptWordChar>().Match(m,p,1,res)==1)&&
(P<ptWordChar>().Match(m,s,1,res)!=1)) return f?-1:0;
if ((P<ptWordChar>().Match(m,p,1,res)!=1)&&
(P<ptWordChar>().Match(m,s,1,res)==1)) return f?-1:0;
return f?0:-1;
}
};
// Диапазон символов
//////////////////////////////////////////////////////////////////////////////////////////////
// f задает инверсию диапазона
template <int start,int end = 0, bool f = false>
class C: public O
{ public:
C(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int r = 1;
if (size==0) return -1;
if (O::Match(m,s,1,res)<0) return -1;
if (s[0]<start) r = -1;
if (end>start) {
if (s[0]>end) r = -1;
} else {
if (s[0]>start) r = -1;
}
if (f) r = -r;
return r;
}
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize)
{ if (maxsize<1) return esLimitError;
if ((end!=0)&&(start!=end)) return esOtherError;
dst[0] = start;
dst++;
maxsize--;
return esNoError;
}
};
// Символьный набор
//////////////////////////////////////////////////////////////////////////////////////////////
// f задает инверсию набора
template <class T, bool f = false> class S;
template <bool f> class S<NullType,f>
{ public:
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ return true;
}
};
template <class T, class U, bool f>
class S< TypeList<T,U>, f>: public O
{ public:
S< TypeList<T,U>, f>(): O(), Head(), Tail() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ int r = -1;
if (size==0) return -1;
if (O::Match(m,s,1,res)<0) return -1;
if ((Head.Match(m,s,1,res)==1)||
(Tail.Match(m,s,1,res)==1)) r = 1;
if (f) r = -r;
return r;
}
private:
T Head;
S< U > Tail;
};
template <class T, bool f>
class S
{ public:
S(): Elem() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ return Elem.Match(m,s,size,res);
}
private:
S<TL_1(T),f> Elem;
};
// Переменная
//////////////////////////////////////////////////////////////////////////////////////////////
template <int level>
class V: public O
{ public:
V(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ unsigned sz;
const char* v = m->Catch(level,sz);
if (v==NULL) return -1;
if (sz>size) return -1;
if (memcmp(s,v,sz)==0) return sz;
return -1;
}
ERROR_STATE Replace(CRegExpIf* m, char*& dst, unsigned& maxsize)
{ unsigned sz;
const char* v = m->Catch(level);
if (v==NULL) return esNoMatch;
if (sz>maxsize) return esLimitError;
memcpy(dst,v,sz);
dst += sz;
maxsize -= sz;
return esNoError;
}
};
// Рекурсивное применение выражения
//////////////////////////////////////////////////////////////////////////////////////////////
template <class T = NullType, class U = NullType> class R;
template <>
class R<NullType,NullType>: public O
{ public:
R(): O() {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ if (size==0) return 0;
std::auto_ptr<CRegExpIf> p(m->Clone());
return p->Match(res,s,size);
}
};
template <class T, class U>
class R: public CRegExpIf,
public O
{ public:
R(): CRegExpIf(), Elem(), Found(-1) {}
int Match(CRegExpIf* m, const char* s, unsigned size, CResIf*& res)
{ Match(res,s,size);
return Found;
}
virtual bool Match(CResIf*& r, const char* src = NULL, unsigned size = 0)
{ CreateNode(r,Type2Type<U>());
Found = Elem.Match(this,src,size,r);
return (Found>=0);
}
virtual const char* Catch(unsigned level, unsigned& size) {return NULL;}
virtual ERROR_STATE Replace(char* dst, unsigned size) {return esOtherError;}
virtual void Reset(void) {}
virtual CRegExpIf* Clone(void) const { return new R<T,U>; }
private:
template <class S>
void CreateNode(CResIf*& r, Type2Type<S>)
{ CResIf* p = new S;
if (r!=NULL) r->AddNode(p);
r = p;
}
template <>
void CreateNode(CResIf*& r, Type2Type<NullType>) {}
E< T > Elem;
int Found;
};
// Интерпретатор регулярных выражений
//////////////////////////////////////////////////////////////////////////////////////////////
template < class T, class U = NullType>
class CRegExp: public CRegExpIf
{ public:
CRegExp(): Expr(), Repl(), Start(NULL), Str(NULL), Size(0), Found(-1) {}
virtual bool Match(CResIf*& r, const char* src = NULL, unsigned size = 0);
virtual const char* Catch(unsigned level, unsigned& size);
virtual ERROR_STATE Replace(char* dst, unsigned size);
virtual void Reset(void) { Found = -1; }
virtual CRegExpIf* Clone(void) const { return new CRegExp<T,U>; }
private:
E< T > Expr;
E< U > Repl;
const char* Start;
const char* Str;
unsigned Size;
public:
int Found;
};
template <class T, class U>
bool CRegExp<T,U>::Match(CResIf*& r, const char* src, unsigned size)
{
if (src!=NULL) {
Str = src;
Start = src;
Size = size;
if (Size==0)
Size = (unsigned)strlen(src);
} else {
if (Str==NULL) return false;
int step = (Found<=0)?1:Found;
if (Size<=(unsigned)step) return false;
Str += step;
Size -= step;
}
Reset();
while ((Found=Expr.Match(this,Str,Size,r))==-1) {
if (Size<=1) return false;
Str++;
Size--;
}
return true;
}
template <class T, class U>
ERROR_STATE CRegExp<T,U>::Replace(char* dst, unsigned size)
{ return Repl.Replace(this,dst,size);
}
template <class T, class U>
const char* CRegExp<T,U>::Catch(unsigned level, unsigned& size)
{
if (level==0) {
size = (Found<0)?Size:Found;
return (Found<0)?Start:Str;
}
return Expr.Catch(level-1,size);
}
}
#pragma once
#include "RegExp.h"
namespace RegExp {
typedef C<'0','9'> Digit;
typedef C<'A','F'> HexUpper;
typedef C<'a','f'> HexLower;
typedef D_2(HexUpper, HexLower) Hex;
typedef C<'0','1'> Max1w0;
typedef C<'0','2'> Max2w0;
typedef C<'0','4'> Max4w0;
typedef C<'0','5'> Max5w0;
typedef C<'1','2'> Max2;
typedef C<'0','5'> Max5;
// Короткая форма представления сетевой маски
typedef E_2(Max2, Digit) Octet29;
typedef E_2(C<3>, Max2w0) Octet32;
typedef D_3(Digit, Octet29, Octet32) IPMaskShort;
typedef Q<Max1w0, NullType, 0, 1> IPForm1p1;
typedef Q<Digit, NullType, 0, 1> IPForm1p2;
typedef E_3(IPForm1p1, Digit, IPForm1p2) IPForm1;
typedef E_3(C<'2'>, Max4w0, Digit) IPForm2;
typedef E_3(C<'2'>, C<'5'>, Max5w0) IPForm3;
typedef B< D_3(IPForm1, IPForm2, IPForm3) > IPOctet;
typedef Q< E_2(C<'.'>, IPOctet), NullType, 3, 3 > IPTail;
typedef E_2(IPOctet, IPTail) IPAddress;
// Mac-адрес
typedef B< Q<Hex, NullType, 2, 2> > MacOctet;
typedef Q< E_2(C<':'>, MacOctet), NullType, 5, 5 > MacTail;
typedef E_2(MacOctet, MacTail) MacAddress;
}
#pragma once
class NullType {};
template <int n>
struct Int2Type
{
enum { value = n };
};
template <class T>
struct Type2Type
{
typedef T result;
};
template <class T, class U>
struct TypeList
{
typedef T Head;
typedef U Tail;
};
#define TL_1(T1) TypeList<T1, NullType >
#define TL_2(T1, T2) TypeList<T1, TL_1(T2) >
#define TL_3(T1, T2, T3) TypeList<T1, TL_2(T2, T3) >
#define TL_4(T1, T2, T3, T4) TypeList<T1, TL_3(T2, T3, T4) >
#define TL_5(T1, T2, T3, T4, T5) TypeList<T1, TL_4(T2, T3, T4, T5) >
#define TL_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TL_5(T2, T3, T4, T5, T6) >
#define TL_7(T1, T2, T3, T4, T5, T6, T7) TypeList<T1, TL_6(T2, T3, T4, T5, T6, T7) >
#define TL_8(T1, T2, T3, T4, T5, T6, T7, T8) TypeList<T1, TL_7(T2, T3, T4, T5, T6, T7, T8) >
#define TL_9(T1, T2, T3, T4, T5, T6, T7, T8, T9) TypeList<T1, TL_8(T2, T3, T4, T5, T6, T7, T8, T9) >
#define TL_10(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) TypeList<T1, TL_9(T2, T3, T4, T5, T6, T7, T8, T9, T10) >
#define C_1(C1) E<TL_1(C<'C1'>) >
#define C_2(C1, C2) E<TL_2(C<'C1'>,C<'C2'>) >
#define C_3(C1, C2, C3) E<TL_3(C<'C1'>,C<'C2'>,C<'C3'>) >
#define C_4(C1, C2, C3, C4) E<TL_4(C<'C1'>,C<'C2'>,C<'C3'>,C<'C4'>) >
#define C_5(C1, C2, C3, C4, C5) E<TL_5(C<'C1'>,C<'C2'>,C<'C3'>,C<'C4'>,C<'C5'>) >
#define C_6(C1, C2, C3, C4, C5, C6) E<TL_6(C<'C1'>,C<'C2'>,C<'C3'>,C<'C4'>,C<'C5'>,C<'C6'>) >
#define C_7(C1, C2, C3, C4, C5, C6, C7) E<TL_7(C<'C1'>,C<'C2'>,C<'C3'>,C<'C4'>,C<'C5'>,C<'C6'>,C<'C7'>) >
#define C_8(C1, C2, C3, C4, C5, C6, C7, C8) E<TL_8(C<'C1'>,C<'C2'>,C<'C3'>,C<'C4'>,C<'C5'>,C<'C6'>,C<'C7'>,C<'C8'>) >
#define C_9(C1, C2, C3, C4, C5, C6, C7, C8, C9) E<TL_9(C<'C1'>,C<'C2'>,C<'C3'>,C<'C4'>,C<'C5'>,C<'C6'>,C<'C7'>,C<'C8'>,C<'C9'>) >
#define C_10(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) E<TL_10(C<'C1'>,C<'C2'>,C<'C3'>,C<'C4'>,C<'C5'>,C<'C6'>,C<'C7'>,C<'C8'>,C<'C9'>,C<'C10'>) >
#define E_1(C1) E< C1 >
#define E_2(C1, C2) E< TL_2(C1, C2) >
#define E_3(C1, C2, C3) E< TL_3(C1, C2, C3) >
#define E_4(C1, C2, C3, C4) E< TL_4(C1, C2, C3, C4) >
#define E_5(C1, C2, C3, C4, C5) E< TL_5(C1, C2, C3, C4, C5) >
#define E_6(C1, C2, C3, C4, C5, C6) E< TL_6(C1, C2, C3, C4, C5, C6) >
#define E_7(C1, C2, C3, C4, C5, C6, C7) E< TL_7(C1, C2, C3, C4, C5, C6, C7) >
#define E_8(C1, C2, C3, C4, C5, C6, C7, C8) E< TL_8(C1, C2, C3, C4, C5, C6, C7, C8) >
#define E_9(C1, C2, C3, C4, C5, C6, C7, C8, C9) E< TL_9(C1, C2, C3, C4, C5, C6, C7, C8, C9) >
#define E_10(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) E< TL_10(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) >
#define D_1(C1) D< C1 >
#define D_2(C1, C2) D< TL_2(C1, C2) >
#define D_3(C1, C2, C3) D< TL_3(C1, C2, C3) >
#define D_4(C1, C2, C3, C4) D< TL_4(C1, C2, C3, C4) >
#define D_5(C1, C2, C3, C4, C5) D< TL_5(C1, C2, C3, C4, C5) >
#define D_6(C1, C2, C3, C4, C5, C6) D< TL_6(C1, C2, C3, C4, C5, C6) >
#define D_7(C1, C2, C3, C4, C5, C6, C7) D< TL_7(C1, C2, C3, C4, C5, C6, C7) >
#define D_8(C1, C2, C3, C4, C5, C6, C7, C8) D< TL_8(C1, C2, C3, C4, C5, C6, C7, C8) >
#define D_9(C1, C2, C3, C4, C5, C6, C7, C8, C9) D< TL_9(C1, C2, C3, C4, C5, C6, C7, C8, C9) >
#define D_10(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) D< TL_10(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) >
#define S_1(C1) S< C1 >
#define S_2(C1, C2) S< TL_2(C1, C2) >
#define S_3(C1, C2, C3) S< TL_3(C1, C2, C3) >
#define S_4(C1, C2, C3, C4) S< TL_4(C1, C2, C3, C4) >
#define S_5(C1, C2, C3, C4, C5) S< TL_5(C1, C2, C3, C4, C5) >
#define S_6(C1, C2, C3, C4, C5, C6) S< TL_6(C1, C2, C3, C4, C5, C6) >
#define S_7(C1, C2, C3, C4, C5, C6, C7) S< TL_7(C1, C2, C3, C4, C5, C6, C7) >
#define S_8(C1, C2, C3, C4, C5, C6, C7, C8) S< TL_8(C1, C2, C3, C4, C5, C6, C7, C8) >
#define S_9(C1, C2, C3, C4, C5, C6, C7, C8, C9) S< TL_9(C1, C2, C3, C4, C5, C6, C7, C8, C9) >
#define S_10(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) S< TL_10(C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) >
Курица — это инструмент, с помощью которого одно яйцо производит другие.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Курица — это инструмент, с помощью которого одно яйцо производит другие.
Курица — это инструмент, с помощью которого одно яйцо производит другие.