NAMESPACE_UPP struct CalcPacket; class CalcNode; class CalcContext; typedef RefPtr CalcNodePtr; class CalcNode : public RefBase { public: CalcNode(String name = Null, pick_ Vector args = Vector()) : name(name), args(args) {} CalcNode(String name, const Vector& 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 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& 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 CalcSequenceNode : public CalcNode { public: CalcSequenceNode(pick_ Vector nodes); CalcSequenceNode(CalcNodePtr node1, CalcNodePtr node2); virtual CalcNodePtr Clone() const { return new CalcSequenceNode(*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& args) : CalcNode(name, args) {} CalcFunctionNode(String name, const Vector& 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& 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 args; Value result; bool help; }; typedef bool (*CalcProc)(CalcPacket& packet); typedef VectorMap CalcProcMap; typedef Gate1 CalcGate; typedef VectorMap 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 HelpCalcMap; HelpCalcMap& GetHelpCalcMap(); void AddHelpCalc(const HelpCalc& calc); Vector GetHelpCalcGroups(); /* class HelpCalcIndex { public: typedef VectorMap 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, DeepCopyOption { 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 var_index; Vector var_value; Vector var_const; CalcGateMap functions; }; class CalcSymbolsLevel : public CalcSymbols { public: CalcSymbolsLevel(Vector& owner) : owner(owner) { owner.Add(this); } ~CalcSymbolsLevel() { owner.Drop(); } private: Vector& owner; }; template class CalcLocalItem; template class CalcLocalMap { public: CalcLocalMap() : head(0), tail(0) {} void Add(CalcLocalItem *item); void Set(T *this_ptr, CalcSymbols& map); void Set(T *this_ptr) { Set(this_ptr, this_ptr -> stack.Top()); } private: CalcLocalItem *head, *tail; }; template class CalcLocalItem { public: CalcLocalItem(CalcLocalMap& map, const String& name, bool (T::*method)(CalcPacket&)); String name; bool (T::*method)(CalcPacket&); CalcLocalItem *next; }; template CalcLocalItem::CalcLocalItem(CalcLocalMap& map, const String& name, bool (T::*method)(CalcPacket&)) : next(0) , name(name) , method(method) { map.Add(this); } template void CalcLocalMap::Add(CalcLocalItem *item) { if(head == 0) head = tail = item; else tail = tail -> next = item; } template void CalcLocalMap::Set(T *this_ptr, CalcSymbols& map) { for(CalcLocalItem *p = head; p; p = p -> next) map.functions.Add(p -> name, callback(this_ptr, p -> method)); } #define DECLARE_LOCAL_MAP() static CalcLocalMap& GetLocalMap(); #define IMPLEMENT_LOCAL_MAP() GLOBAL_VAR(CalcLocalMap, BASECLASS::GetLocalMap) class CalcContext : Moveable, DeepCopyOption { 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& sparts, Vector& cparts, bool throw_errors); String CalcConvert(const Vector& sparts, const Vector& 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& 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 stack; Vector externals; }; template <> struct CalcType : public CalcRawType { static Value ToValue(const CalcNode *cn) { return cn ? RawValue(cn) : Value(); } static const CalcNode *ValueTo(Value v) { return IsType(v) ? RawValue::Extract(v) : 0; } static String Describe(); }; template <> struct CalcType : public CalcRawType { static Value ToValue(CalcNodePtr cn) { return !!cn ? RawToValue(cn) : Value(); } static CalcNodePtr ValueTo(Value v) { return IsTypeRaw(v) ? UPP::ValueTo(v) : NULL; } 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 ScanSequence(); 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& 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_SEMICOLON, 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 inline bool CalcCast(CalcPacket& packet, R (*fn)(), BTA * = 0) { if(packet.help) { // generate help info packet.result = CalcType::Describe(); return true; } if(packet.args.GetCount() != 0) return false; packet.result = CalcType::ToValue(fn()); return true; } template inline bool CalcCastPacket(CalcPacket& packet, R (*fn)(CalcPacket& packet), BTA * = 0) { if(packet.help) { // generate help info packet.result = CalcType::Describe(); return true; } if(packet.args.GetCount() != 0) return false; packet.result = CalcType::ToValue(fn(packet)); return true; } template inline bool CalcCastMember(CalcPacket& packet, O *clss, R (M::*fn)(), BTA * = 0) { if(packet.help) { // generate help info packet.result = CalcType::Describe(); return true; } if(packet.args.GetCount() != 0) return false; packet.result = CalcType::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::Describe()) #define E__ChkArg(I) \ if(BTA::NIL && IsNull(packet.args[I - 1])) \ null = true; \ else if(!CalcType::IsType(packet.args[I - 1])) \ return false; #define E__CvArg(I) CalcType::ValueTo(packet.args[I - 1]) #define CalcCastTemplate(NM, I) \ template \ inline bool NM(CalcPacket& packet, R (*fn)(CP_ARG_PREFIX __List##I(E__Arg)), BTA * = 0) \ { \ if(packet.help) \ { \ packet.result = CalcType::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::ToValue(fn(CP_PREFIX \ __List##I(E__CvArg))); \ return true; \ } #define CalcCastMemberTemplate(I) \ template \ inline bool CalcCastMember(CalcPacket& packet, O *clss, R (M::*fn)(__List##I(E__Arg)), BTA * = 0) \ { \ if(packet.help) \ { \ packet.result = CalcType::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::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(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(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(packet, this, &BASECLASS::call); } }; \ static GLOBAL_VARP(HelpCalc, FGENID(chlp, dflt), (callback(static_cast(0), \ brutal_cast(&FGENID(clcp, dflt)::CalcIt)), \ id, "Calc$" topic, group)); \ INITBLOCK_(FGENID(mblk, tag)) { \ FGENID(chlp, dflt)(); \ static CalcLocalItem FGENID(clci, dflt)(BASECLASS::GetLocalMap(), id, \ brutal_cast(&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