NAMESPACE_UPP

struct CalcPacket;
class  CalcNode;
class  CalcContext;

typedef RefPtr<CalcNode> CalcNodePtr;

class CalcNode : public RefBase
{
public:
	CalcNode(String name = Null, pick_ Vector<CalcNodePtr> args = Vector<CalcNodePtr>()) : name(name), args(args) {}
	CalcNode(String name, const Vector<CalcNodePtr>& args, int deep) : name(name), args(args, 0) {}
	CalcNode(String name, CalcNodePtr arg) : name(name) { args.SetCount(1); args[0] = arg; }
	CalcNode(String name, CalcNodePtr arg1, CalcNodePtr arg2) : name(name) { args.SetCount(2); args[0] = arg1; args[1] = arg2; }
	CalcNode(const CalcNode& node) : name(node.name), args(node.args, 0) {}
	virtual ~CalcNode();

	virtual Value       Calc(CalcContext& context) const = 0;
	virtual CalcNodePtr Clone() const                  { return CalcNodePtr(); }
	virtual String      Format() const                 { return Null; }
	virtual bool        IsConst() const                { return false; }
	virtual CalcNodePtr Optimize(CalcContext& context) { return this; }

	Value               TryCalc(CalcContext& context) const; // catches Exc to return ErrorValue

	double              CalcDouble(CalcContext& context) const;
	String              CalcString(CalcContext& context) const;
	int                 CalcInt(CalcContext& context) const;
	Date                CalcDate(CalcContext& context) const;
	Time                CalcTime(CalcContext& context) const;
	bool                CalcBool(CalcContext& context) const;

	String              GetName() const { return name; }

public:
	String              name;
	Vector<CalcNodePtr> args;

protected:
	void                Expect(const char *s, String found) const; // throws Exc

};

class CalcConstNode : public CalcNode
{
public:
	CalcConstNode(Value value) : CalcNode("(const)"), value(value) {}

	virtual CalcNodePtr Clone() const { return new CalcConstNode(*this); }
	virtual Value       Calc(CalcContext& context) const;
	virtual String      Format() const;
	virtual bool        IsConst() const                { return true; }

public:
	Value               value;
};

class CalcLambdaNode : public CalcNode
{
public:
	CalcLambdaNode(CalcNodePtr arg) : CalcNode("#", arg) {}

	virtual CalcNodePtr Clone() const { return new CalcLambdaNode(*this); }
	virtual Value       Calc(CalcContext& context) const;
	virtual String      Format() const;
};

class CalcLogOrNode : public CalcNode
{
public:
	CalcLogOrNode(CalcNodePtr left, CalcNodePtr right) : CalcNode("||", left, right) {}

	virtual CalcNodePtr Clone() const { return new CalcLogOrNode(*this); }
	virtual Value       Calc(CalcContext& context) const;
	virtual String      Format() const;
	virtual CalcNodePtr Optimize(CalcContext& context);
};

class CalcLogAndNode : public CalcNode
{
public:
	CalcLogAndNode(CalcNodePtr left, CalcNodePtr right) : CalcNode("&&", left, right) {}

	virtual CalcNodePtr Clone() const { return new CalcLogAndNode(*this); }
	virtual Value       Calc(CalcContext& context) const;
	virtual String      Format() const;
	virtual CalcNodePtr Optimize(CalcContext& context);
};

class CalcSelectNode : public CalcNode
{
public:
	CalcSelectNode(CalcNodePtr cond, CalcNodePtr left, CalcNodePtr right)
		: CalcNode("?:") { args.SetCount(3); args[0] = cond; args[1] = left; args[2] = right; }

	virtual CalcNodePtr Clone() const { return new CalcSelectNode(*this); }
	virtual Value       Calc(CalcContext& context) const;
	virtual String      Format() const;
	virtual CalcNodePtr Optimize(CalcContext& context);
	enum { COND, IFTRUE, IFFALSE };
};

class CalcSwitchNode : public CalcNode
{
public:
	CalcSwitchNode(CalcNodePtr cond, pick_ Vector<CalcNodePtr>& list) : CalcNode("{}")
	{ args.Reserve(list.GetCount() + 1); args.Add(cond); args.AppendPick(list); }

	virtual CalcNodePtr Clone() const { return new CalcSwitchNode(*this); }
	virtual Value       Calc(CalcContext& context) const;
	virtual String      Format() const;
	virtual CalcNodePtr Optimize(CalcContext& context);
};

class CalcFunctionNode : public CalcNode
{
public:
	CalcFunctionNode(String name)                                            : CalcNode(name) {}
	CalcFunctionNode(String name, CalcNodePtr arg)                           : CalcNode(name, arg) {}
	CalcFunctionNode(String name, CalcNodePtr arg1, CalcNodePtr arg2)        : CalcNode(name, arg1, arg2) {}
	CalcFunctionNode(String name, pick_ Vector<CalcNodePtr>& args)           : CalcNode(name, args) {}
	CalcFunctionNode(String name, const Vector<CalcNodePtr>& args, int deep) : CalcNode(name, args, 0) {}

	void                ScanArgs(CalcPacket& packet) const;

	virtual CalcNodePtr Clone() const  { return new CalcFunctionNode(*this); }
	virtual Value       Calc(CalcContext& context) const;
	virtual String      Format() const;
};

void        RunDlgCalc(CalcContext& context);

CalcNodePtr GetVariableNode(String varname);
CalcNodePtr GetFunctionNode(String fnname, pick_ Vector<CalcNodePtr>& args);

inline bool IsIdent(byte c)
{
	return IsAlNum(c) || c == '_' || c == '$' || c == '@';
}

int CharFilterCalcIdent(int c);
int CharFilterCalcUpperIdent(int c);

struct CalcPacket
{
	CalcPacket(CalcContext& context, const String& name) : context(context), name(name)
		, help(false)
		{}

	String         GetTypeNames() const;

	CalcContext&   context;
	String         name;
	Vector<Value>  args;
	Value          result;
	bool           help;
};

typedef bool (*CalcProc)(CalcPacket& packet);
typedef VectorMap<String, CalcProc> CalcProcMap;

typedef Gate1<CalcPacket&> CalcGate;
typedef VectorMap<String, CalcGate> CalcGateMap;

class HelpCalc
{
public:
	HelpCalc(CalcGate proc, const char *ident, const char *topic, String (*groupfn)());

	class Compare
	{
	public:
		enum MODE { ALPHABETIC, GROUPS };
		Compare(MODE mode = ALPHABETIC) : mode(mode), lang(GetLanguageInfo()) {}

		bool operator () (const HelpCalc *a, const HelpCalc *b) const;

	private:
		MODE mode;
		const LanguageInfo& lang;
	};

	String         GetTitle() const;

public:
//	int            ord;
	CalcGate       proc;
	String       (*groupfn)();
	String         ident;
	String         topic;

private:
	void Init(CalcGate proc, const char *ident, const char *topic, String (*groupfn)());
};

typedef Vector<const HelpCalc *> HelpCalcMap;

HelpCalcMap&   GetHelpCalcMap();
void           AddHelpCalc(const HelpCalc& calc);
Vector<String> GetHelpCalcGroups();

/*
class HelpCalcIndex
{
public:
	typedef VectorMap<String, const HelpCalc *> IdMap;

	static HelpCalcIndex& Get();

	int                   Add(const HelpCalc& calc) { index.Add(calc.ident, &calc); return index.GetCount(); }

	void                  PutSummary() const;
	void                  PutGroups() const;
	void                  AddIndexEntries() const; // adds entries to dynamic index

	const IdMap&          GetIndex() const { return index; }

private:
	IdMap                 index;

private:
	HelpCalcIndex();
};
*/

String HelpCalcLink(const char *id, const char *ref = " (viz str._#)", bool bold = true);

class CalcSymbols : Moveable<CalcSymbols>, DeepCopyOption<CalcSymbols>
{
public:
	CalcSymbols() {}
	CalcSymbols(const CalcSymbols& l, int deep)
	: var_index(l.var_index, 0)
	, var_value(l.var_value, 0)
	, var_const(l.var_const, 0)
	, functions(l.functions, 0) {}

	void                     Clear();
	void                     Add(String name, CalcGate proc)   { functions.Add(name, proc); }
	void                     Set(String name, Value v, bool c) { int i = var_index.FindAdd(name); var_value.DoIndex(i) = v; var_const.DoIndex(i) = c; }
	void                     Remove(String name);

	Index<String>            var_index;
	Vector<Value>            var_value;
	Vector<bool>             var_const;
	CalcGateMap              functions;
};

class CalcSymbolsLevel : public CalcSymbols
{
public:
	CalcSymbolsLevel(Vector<CalcSymbols *>& owner) : owner(owner) { owner.Add(this); }
	~CalcSymbolsLevel()                                           { owner.Drop(); }

private:
	Vector<CalcSymbols *>& owner;
};

template <class T>
class CalcLocalItem;

template <class T>
class CalcLocalMap
{
public:
	CalcLocalMap() : head(0), tail(0) {}

	void Add(CalcLocalItem<T> *item);
	void Set(T *this_ptr, CalcSymbols& map);
	void Set(T *this_ptr) { Set(this_ptr, this_ptr -> stack.Top()); }

private:
	CalcLocalItem<T> *head, *tail;
};

template <class T>
class CalcLocalItem
{
public:
	CalcLocalItem(CalcLocalMap<T>& map, const String& name, bool (T::*method)(CalcPacket&));

	String             name;
	bool          (T::*method)(CalcPacket&);
	CalcLocalItem<T>  *next;
};

template <class T>
CalcLocalItem<T>::CalcLocalItem(CalcLocalMap<T>& map, const String& name, bool (T::*method)(CalcPacket&))
: next(0)
, name(name)
, method(method)
{
	map.Add(this);
}

template <class T>
void CalcLocalMap<T>::Add(CalcLocalItem<T> *item)
{
	if(head == 0)
		head = tail = item;
	else
		tail = tail -> next = item;
}

template <class T>
void CalcLocalMap<T>::Set(T *this_ptr, CalcSymbols& map)
{
	for(CalcLocalItem<T> *p = head; p; p = p -> next)
		map.functions.Add(p -> name, callback(this_ptr, p -> method));
}

#define DECLARE_LOCAL_MAP() static CalcLocalMap<CLASSNAME>& GetLocalMap();
#define IMPLEMENT_LOCAL_MAP() GLOBAL_VAR(CalcLocalMap<BASECLASS>, BASECLASS::GetLocalMap)

class CalcContext : Moveable<CalcContext>, DeepCopyOption<CalcContext>
{
public:
	typedef CalcContext CLASSNAME;

		class Nesting
	{
	public:
		Nesting(CalcContext& context) : context(context) { context.PushLevel(); }
		~Nesting()                                       { context.PopLevel(); }

	private:
		CalcContext& context;
	};

		class Global
	{
	public:
		Global(const String& name, CalcProc proc) { GetGlobals().Add(name, proc); }
	};

	CalcContext();
	CalcContext(const CalcContext& context, int);
	virtual ~CalcContext();

	Value               Evaluate(String expr);
	double              EvaluateDouble(String expr);
	String              EvaluateString(String expr);
	int                 EvaluateInt(String expr);
	Date                EvaluateDate(String expr);
	Time                EvaluateTime(String expr);
	bool                EvaluateBool(String expr);

	String              OptimizeConstant(String expr);
	CalcNodePtr         OptimizeConstant(CalcNodePtr src);

	String              Convert(String s, bool throw_errors, const class Convert& convert = NoConvert());
	static void         ParseConvert(String s, Vector<String>& sparts, Vector<CalcNodePtr>& cparts, bool throw_errors);
	String              CalcConvert(const Vector<String>& sparts, const Vector<CalcNodePtr>& cparts, bool throw_errors, const UPP::Convert& convert = NoConvert());

	virtual Value       Calc(CalcPacket& packet);
	virtual Value       TryCalc(CalcPacket& packet);
	virtual String      GetTypeName(Value value) const;
	virtual String      FormatNull(Value value, bool no_text = false);
	virtual String      FormatText(Value value, const UPP::Convert& convert = NoConvert());

	virtual void        PushLevel();
	virtual void        PopLevel();

	CalcContext&        Add(String name, CalcGate proc)           { stack.Top().Add(name, proc); return *this; }
	CalcContext&        Set(String name, Value v, bool c = false) { stack.Top().Set(name, v, c); return *this; }
	CalcContext&        Remove(String name)                       { stack.Top().Remove(name); return *this; }
	Value               Get(String name) const;

	void                AddExternal(CalcSymbols& e)                    { externals.Add(&e); }
	void                AddExternal(const Vector<CalcSymbols *>& ext)  { externals.Append(ext); }
	void                RemoveExternal(CalcSymbols& e);

	static CalcProcMap& GetGlobals();

public:
	enum { MAX_STACK_DEPTH = 1000 };

	int                 language;
	int                 nesting;
	bool                opt_const;
	Array<CalcSymbols>  stack;
	Vector<CalcSymbols *> externals;
};

template <>
struct CalcType<const CalcNode *> : public CalcRawType<const CalcNode *>
{
	static Value           ToValue(const CalcNode *cn) { return cn ? RawValue<const CalcNode *>(cn) : Value(); }
	static const CalcNode *ValueTo(Value v)            { return IsType(v) ? RawValue<const CalcNode *>::Extract(v) : 0; }
	static String          Describe();
};

class CalcParser
{
public:
	CalcParser();

	virtual CalcNodePtr   Scan(const char *text);
	virtual CalcNodePtr   ScanVoid(const char *text);

protected:
	virtual CalcNodePtr   ScanAny();
//	virtual CalcNodePtr   ScanLambda();
	virtual CalcNodePtr   ScanSelect();
	virtual CalcNodePtr   ScanLogOr();
	virtual CalcNodePtr   ScanLogAnd();
	virtual CalcNodePtr   ScanEq();
	virtual CalcNodePtr   ScanCompare();
	virtual CalcNodePtr   ScanBitXor();
	virtual CalcNodePtr   ScanBitAnd();
	virtual CalcNodePtr   ScanBitOr();
	virtual CalcNodePtr   ScanAdditive();
	virtual CalcNodePtr   ScanShift();
	virtual CalcNodePtr   ScanMultiplicative();
	virtual CalcNodePtr   ScanPow();
	virtual CalcNodePtr   ScanPrefix();
	virtual CalcNodePtr   ScanPostfix(CalcNodePtr node);
	virtual void          ScanArgs(Vector<CalcNodePtr>& dest);

	void                  Clear(const char *t);

	int                   Skip();
	bool                  Expect(const String& s);

	String                GetIdent();
	Value                 GetNumberOrDate();
	String                GetString();

	enum OPERATOR
	{
		OP_NONE,
		OP_QUESTION, OP_COLON,
		OP_LOG_AND, OP_LOG_OR,
		OP_LT, OP_LE, OP_GE, OP_GT,
		OP_EQ, OP_NE,
		OP_BIT_AND, OP_BIT_OR, OP_BIT_XOR,
		OP_LSHIFT, OP_RSHIFT,
		OP_ADD, OP_SUB,
		OP_MUL, OP_DIV, OP_MOD,
		OP_POW,
		OP_LOG_NOT, OP_BIT_NOT,
		OP_LAMBDA,
		OP_LPAR, OP_RPAR,
		OP_LBRACKET, OP_RBRACKET,
		OP_LBRACE, OP_RBRACE,
		OP_COMMA,
		OP_DOT, OP_DOTS,

		OP_LAST,
	};

	virtual int           GetOperator();
	int                   SkipOperator();
	bool                  Check(int op);
	bool                  Force(int op, const char *expect);

protected:
	const char           *start;
	const char           *pos;

	int                   op_last;
	const char           *op_begin;
	const char           *op_end;
};

template <class BTA, class R>
inline bool CalcCast(CalcPacket& packet, R (*fn)(), BTA * = 0)
{
	if(packet.help)
	{ // generate help info
		packet.result = CalcType<R>::Describe();
		return true;
	}
	if(packet.args.GetCount() != 0)
		return false;
	packet.result = CalcType<R>::ToValue(fn());
	return true;
}

template <class BTA, class R>
inline bool CalcCastPacket(CalcPacket& packet, R (*fn)(CalcPacket& packet), BTA * = 0)
{
	if(packet.help)
	{ // generate help info
		packet.result = CalcType<R>::Describe();
		return true;
	}
	if(packet.args.GetCount() != 0)
		return false;
	packet.result = CalcType<R>::ToValue(fn(packet));
	return true;
}

template <class BTA, class O, class M, class R>
inline bool CalcCastMember(CalcPacket& packet, O *clss, R (M::*fn)(), BTA * = 0)
{
	if(packet.help)
	{ // generate help info
		packet.result = CalcType<R>::Describe();
		return true;
	}
	if(packet.args.GetCount() != 0)
		return false;
	packet.result = CalcType<R>::ToValue((clss ->* fn)());
	return true;
}

struct BTA0 { enum { NIL = 0 }; };
struct BTA1 { enum { NIL = 1 }; };

#define E__Class(I)   class C##I
#define E__Arg(I)     C##I
#define E__Descr(I)   packet.args.Add(CalcType<C##I>::Describe())
#define E__ChkArg(I) \
	if(BTA::NIL && IsNull(packet.args[I - 1])) \
		null = true; \
	else if(!CalcType<C##I>::IsType(packet.args[I - 1])) \
		return false;
#define E__CvArg(I) CalcType<C##I>::ValueTo(packet.args[I - 1])

#define CalcCastTemplate(NM, I) \
template <class BTA, class R, __List##I(E__Class)> \
inline bool NM(CalcPacket& packet, R (*fn)(CP_ARG_PREFIX __List##I(E__Arg)), BTA * = 0) \
{ \
	if(packet.help) \
	{ \
		packet.result = CalcType<R>::Describe(); \
		__List##I(E__Descr); \
		return true; \
	} \
	if(packet.args.GetCount() != I) \
		return false; \
	bool null = false; \
	__Expand##I(E__ChkArg) \
	if(BTA::NIL && null) \
	{ \
		packet.result = Value(); \
		return true; \
	} \
	packet.result = CalcType<R>::ToValue(fn(CP_PREFIX \
		__List##I(E__CvArg))); \
	return true; \
}

#define CalcCastMemberTemplate(I) \
template <class BTA, class O, class M, class R, __List##I(E__Class)> \
inline bool CalcCastMember(CalcPacket& packet, O *clss, R (M::*fn)(__List##I(E__Arg)), BTA * = 0) \
{ \
	if(packet.help) \
	{ \
		packet.result = CalcType<R>::Describe();  \
		__List##I(E__Descr); \
		return true; \
	} \
	if(packet.args.GetCount() != I) \
		return false; \
	bool null = false; \
	__Expand##I(E__ChkArg) \
	if(BTA::NIL && null) \
	{ \
		packet.result = Value(); \
		return true; \
	} \
	packet.result = CalcType<R>::ToValue((clss ->* fn)( \
		__List##I(E__CvArg))); \
	return true; \
} \

#define CP_ARG_PREFIX
#define CP_PREFIX
CalcCastTemplate(CalcCast, 1)
CalcCastTemplate(CalcCast, 2)
CalcCastTemplate(CalcCast, 3)
CalcCastTemplate(CalcCast, 4)
CalcCastTemplate(CalcCast, 5)
CalcCastTemplate(CalcCast, 6)
#undef  CP_ARG_PREFIX
#undef  CP_PREFIX

#define CP_ARG_PREFIX CalcPacket& packet,
#define CP_PREFIX packet,
CalcCastTemplate(CalcCastPacket, 1)
CalcCastTemplate(CalcCastPacket, 2)
CalcCastTemplate(CalcCastPacket, 3)
CalcCastTemplate(CalcCastPacket, 4)
CalcCastTemplate(CalcCastPacket, 5)
CalcCastTemplate(CalcCastPacket, 6)
#undef  CP_ARG_PREFIX
#undef  CP_PREFIX

CalcCastMemberTemplate(1)
CalcCastMemberTemplate(2)
CalcCastMemberTemplate(3)
CalcCastMemberTemplate(4)
CalcCastMemberTemplate(5)
CalcCastMemberTemplate(6)

#define FGENID2(tag, lnum) __##tag##lnum##__
#define FGENID1(tag, lnum) FGENID2(tag, lnum)
#define FGENID(a, b) COMBINE(COMBINE(a, b), __LINE__)

#ifndef NOHELP
#define FDECLTH(tag, topic, id, group, proc) \
static GLOBAL_VARP(HelpCalc, FGENID(chlp, tag), (callback(proc), id, "Calc$" topic, group)); \
INITBLOCK_(FGENID(hblk, tag)) { FGENID(chlp, tag)(); } \
static void FGENID(chlt, tag)(String& out) { out.Cat(FGENID(chlp, tag)().GetTitle()); } \
RegisterHelpTopicInfoTag(COMBINE(tag, __LINE__), "Calc$" topic, __FILE__, callback(&FGENID(chlt, tag)), CNULL)
#else//NOHELP
#define FDECLTH(tag, topic, id, group, proc)
#endif//NOHELP

#define FDECLT(null, tag, topic, id, group, call) \
static bool FGENID(proc, tag)(CalcPacket& packet) { return CalcCast<null>(packet, call); } \
INITBLOCK_(FGENID(gblk, tag)) { static CalcContext::Global FGENID(init, tag)(id, &FGENID(proc, tag)); } \
FDECLTH(tag, topic, id, group, &FGENID(proc, tag))

#define FDECLTA(null, tag, topic, id, group, call) \
static bool FGENID(proc, tag)(CalcPacket& packet) { return CalcCastPacket<null>(packet, call); } \
INITBLOCK_(FGENID(gblk, tag)) { static CalcContext::Global FGENID(init, tag)(id, &FGENID(proc, tag)); } \
FDECLTH(tag, topic, id, group, &FGENID(proc, tag))

#define FDECLQ(topic, id, group, gate) \
INITBLOCK_(FGENID(gblk, tag)) { static CalcContext::Global FGENID(init, dflt)(id, gate); } \
FDECLTH(dflt, topic, id, group, gate)

#define FDECL(id, call, group)        FDECLT(BTA0, dflt, ASSTRING(call),               id,           group, call)
#define FDECLX(id, group)             FDECLT(BTA0, id,  ASSTRING(id),                 ASSTRING(id), group, id)
#define FDECLP(id, x, group)          FDECLT(BTA0, id,  "C" ASSTRING(id) ASSTRING(x), ASSTRING(id), group, COMBINE3(C, id, x))

#define FDECL0(id, call, group)       FDECLT(BTA1, dflt, ASSTRING(call),               id,           group, call)
#define FDECLX0(id, group)            FDECLT(BTA1, id,  ASSTRING(id),                 ASSTRING(id), group, id)
#define FDECLP0(id, x, group)         FDECLT(BTA1, id,  "C" ASSTRING(id) ASSTRING(x), ASSTRING(id), group, COMBINE3(C, id, x))

#define FDECLA(id, call, group)       FDECLTA(BTA0, dflt, ASSTRING(call),               id,           group, call)
#define FDECLAX(id, group)            FDECLTA(BTA0, id,  ASSTRING(id),                 ASSTRING(id), group, id)
#define FDECLAP(id, x, group)         FDECLTA(BTA0, id,  "C" ASSTRING(id) ASSTRING(x), ASSTRING(id), group, COMBINE3(C, id, x))

#define FDECLA0(id, call, group)      FDECLTA(BTA1, dflt, ASSTRING(call),               id,           group, call)
#define FDECLAX0(id, group)           FDECLTA(BTA1, id,  ASSTRING(id),                 ASSTRING(id), group, id)
#define FDECLAP0(id, x, group)        FDECLTA(BTA1, id,  "C" ASSTRING(id) ASSTRING(x), ASSTRING(id), group, COMBINE3(C, id, x))

#define MDECLT(null, topic, id, group, call) \
struct FGENID(clcp, dflt) : public BASECLASS \
{ FGENID(clcp, dflt)(); bool CalcIt(CalcPacket& packet) { return CalcCastMember<null>(packet, this, &BASECLASS::call); } }; \
static GLOBAL_VARP(HelpCalc, FGENID(chlp, dflt), (callback(static_cast<BASECLASS *>(0), \
		brutal_cast<bool (BASECLASS::*)(CalcPacket&)>(&FGENID(clcp, dflt)::CalcIt)), \
		id, "Calc$" topic, group)); \
INITBLOCK_(FGENID(mblk, tag)) { \
	FGENID(chlp, dflt)(); \
	static CalcLocalItem<BASECLASS> FGENID(clci, dflt)(BASECLASS::GetLocalMap(), id, \
			brutal_cast<bool (BASECLASS::*)(CalcPacket&)>(&FGENID(clcp, dflt)::CalcIt)); \
} \
static void FGENID(chlt, dflt)(String& out) { out.Cat(FGENID(chlp, dflt)().GetTitle()); } \
RegisterHelpTopicInfo("Calc$" topic, __FILE__, callback(&FGENID(chlt, dflt)), CNULL)

#define MDECL(id, x, group)   MDECLT(BTA0, ASSTRING(call),               id,           group, call)
#define MDECL0(id, x, group)  MDECLT(BTA1, ASSTRING(id),                 id,           group, COMBINE3(C, id, x))
#define MDECLP(id, x, group)  MDECLT(BTA0, "C" ASSTRING(id) ASSTRING(x), ASSTRING(id), group, COMBINE3(C, id, x))
#define MDECLP0(id, x, group) MDECLT(BTA1, "C" ASSTRING(id) ASSTRING(x), ASSTRING(id), group, COMBINE3(C, id, x))

END_UPP_NAMESPACE
