#include #include #include #include #include #include #include #include "../shared/MarketHours.h" #include "Semantics.h" #include "IAlertsDaily.h" #include "SimpleDatabaseFields.h" #include "HashHelper.h" #include "Execution.h" namespace Execution { std::string debugDump(Record const *primary) { if (!primary) return "NULL"; TclList result; result<lookUpValue(MainFields::symbol).getString(symbolValid, symbol); bool timeValid; int64_t time = 0; // Initialization avoids a silly complier warning. primary->lookUpValue(MainFields::timestamp).getInt(timeValid, time); if (!(symbolValid && timeValid)) { TclList errors; errors<<"errors"; if (!symbolValid) errors<<"no symbol"; if (!timeValid) errors<<"no time"; result<find(symbol.c_str(), time); TclList alertsDaily; alertsDaily<<"alerts daily"; if (!alertsDailyRecord) alertsDaily<<"NULL"; else alertsDaily<lookUpValue(MainFields::symbol).getString(valid, symbol); return symbol; // Might be empty. } ///////////////////////////////////////////////////////////////////// // fields -> comments // // When looking up a field, internally we only use a field id number. // But we do have access to the name used by the database. We can // use that when sending debug info to the user. ///////////////////////////////////////////////////////////////////// static std::map< int, std::string > makeMap(std::vector< DatabaseFieldInfo > const &vector) { std::map< int, std::string > result; for (auto it = vector.begin(); it != vector.end(); it++) { result[it->id()] = it->databaseName(); } return result; } static std::map< int, std::string > const &getAlertFields() { static const std::map< int, std::string > fields = makeMap(DatabaseFieldInfo::getAlertFields()); return fields; } static std::map< int, std::string > const &getDailyFields() { static const std::map< int, std::string > fields = makeMap(DatabaseFieldInfo::getDailyFields()); return fields; } static const std::string s_UNKNOWN = "UNKNOWN!"; static std::string getComment(std::map< int, std::string > const &fields, int fieldId) { // Start with a space. If we didn't have info, we might return the // empty string. return " /* " + getProperty(fields, fieldId, s_UNKNOWN) + " */"; } ///////////////////////////////////////////////////////////////////// // Executable::TransactionDebug ///////////////////////////////////////////////////////////////////// static __thread std::set< int > *shortCacheNextTime = NULL; Executable::TransactionDebug::TransactionDebug() : _first(!shortCacheNextTime) { if (_first) shortCacheNextTime = new std::set< int >; } Executable::TransactionDebug::~TransactionDebug() { assert(shortCacheNextTime); if (_first) { delete(shortCacheNextTime); shortCacheNextTime = NULL; } } ///////////////////////////////////////////////////////////////////// // Executable1 ///////////////////////////////////////////////////////////////////// std::vector< Executable * > Executable1::allChildrenDebug() { return { _arg1 }; } ///////////////////////////////////////////////////////////////////// // Executable2 ///////////////////////////////////////////////////////////////////// class Executable2 : public Executable, NoCopy, NoAssign { protected: Executable *const _arg1; Executable *const _arg2; Executable2(Executable *arg1, Executable *arg2) : _arg1(arg1), _arg2(arg2) { } virtual std::vector< Executable * > allChildrenDebug() { return { _arg1, _arg2 }; } public: ~Executable2() { delete _arg1; delete _arg2; } }; ///////////////////////////////////////////////////////////////////// // Executable3 ///////////////////////////////////////////////////////////////////// class Executable3 : public Executable, NoCopy, NoAssign { protected: Executable *const _arg1; Executable *const _arg2; Executable *const _arg3; Executable3(Executable *arg1, Executable *arg2, Executable *arg3) : _arg1(arg1), _arg2(arg2), _arg3(arg3) { } virtual std::vector< Executable * > allChildrenDebug() { return { _arg1, _arg2, _arg3 }; } public: ~Executable3() { delete _arg1; delete _arg2; delete _arg3; } }; ///////////////////////////////////////////////////////////////////// // ExecutableN ///////////////////////////////////////////////////////////////////// class ExecutableN : public Executable, NoCopy, NoAssign { protected: typedef std::vector< Executable * > Args; private: Args _args; protected: ExecutableN() { } Args const &getArgs() const { return _args; } void add(Executable *arg) { _args.push_back(arg); } void add(Parse::TreeNode::Args const &args) { for (Parse::TreeNode::Args::const_iterator it = args.begin(); it != args.end(); it++) add(create(*it)); } template< class InType, class OutType > static OutType *tryCreateHelper(Parse::Tree const &source) { InType const *const f = source->as< InType >(); if (!f) return NULL; OutType *const result = new OutType; result->add(f->getArgs()); return result; } std::vector< Executable * > allChildrenDebug() { return _args; } public: ~ExecutableN() { for (Args::iterator it = _args.begin(); it != _args.end(); it++) delete(*it); } }; ///////////////////////////////////////////////////////////////////// // Now ///////////////////////////////////////////////////////////////////// class Now : public Executable { protected: virtual std::string thisItemNameDebug() { return "Now"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { return time(NULL); } static Executable *tryCreate(Parse::Tree const &source) { Parse::NowFunction const *const f = source->as< Parse::NowFunction >(); if (!f) return NULL; return new Now(); } }; ///////////////////////////////////////////////////////////////////// // SecondsAfterOpen ///////////////////////////////////////////////////////////////////// class SecondsAfterOpen : public Executable1 { private: SecondsAfterOpen(Executable *arg1) : Executable1(arg1) { } protected: virtual std::string thisItemNameDebug() { return "SecondsAfterOpen"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool valid; time_t time; _arg1->execute(recordInfo).getInt(valid, time); if (!valid) return ValueBox(); const int result = secondOfTheDay(time) - MARKET_HOURS_OPEN; return result; } static Executable *tryCreate(Parse::Tree const &source) { Parse::SecondsAfterOpen const *const f = source->as< Parse::SecondsAfterOpen >(); if (!f) return NULL; return new SecondsAfterOpen(create(f->getTimestamp())); } }; ///////////////////////////////////////////////////////////////////// // DayOfWeek ///////////////////////////////////////////////////////////////////// class DayOfWeek : public Executable1 { private: DayOfWeek(Executable *arg1) : Executable1(arg1) { } protected: virtual std::string thisItemNameDebug() { return "DayOfWeek"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool valid; time_t time; _arg1->execute(recordInfo).getInt(valid, time); if (!valid) return ValueBox(); struct tm brokenDown; localtime_r(&time, &brokenDown); // 1 = Monday, 2 = Tuesday, …, 5 = Friday. return brokenDown.tm_wday; } static Executable *tryCreate(Parse::Tree const &source) { Parse::DayOfWeek const *const f = source->as< Parse::DayOfWeek >(); if (!f) return NULL; return new DayOfWeek(create(f->getTimestamp())); } }; ///////////////////////////////////////////////////////////////////// // Round ///////////////////////////////////////////////////////////////////// class Round : public Executable1 { private: const double _unit; Round(Executable *toRound, int digits) : Executable1(toRound) , _unit(pow(10, digits)) { } protected: virtual std::string thisItemNameDebug() { return "Round " + ntoa(_unit); } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool valid; double value; _arg1->execute(recordInfo).getDouble(valid, value); if (!valid) return ValueBox(); return round(value * _unit) / _unit; } static Executable *tryCreate(Parse::Tree const &source) { Parse::RoundFunction const *const f = source->as< Parse::RoundFunction >(); if (!f) return NULL; return new Round(create(f->toRound()), f->digits()); } }; ///////////////////////////////////////////////////////////////////// // Reduce // // This deals with a list of arguments whose size can be different // for each object. The other issue is that there are different // algorithms depending if all of the arguments are integers or not. // // Anything that is not a number will cause the entire expression to // immediately halt and return an EMPTY value. Otherwise we keep // track of the values and their types. We call reduceInt() if all // values are integers. We call reduceDouble() if at least one value // is a double. The subclass must fill in the two reduce functions. ///////////////////////////////////////////////////////////////////// class Reduce : public ExecutableN { protected: typedef std::vector< ValueBox > Values; virtual int64_t reduceInt(Values const &values) const =0; virtual double reduceDouble(Values const &values) const =0; Reduce() { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool onlyInt = true; Values results; results.reserve(getArgs().size()); for (Args::const_iterator it = getArgs().begin(); it != getArgs().end(); it++) { ValueBox next = (*it)->execute(recordInfo); switch (next.getType()) { case InternalType::INTEGER: break; case InternalType::DOUBLE: onlyInt = false; break; default: return ValueBox(); } results.push_back(next); } if (onlyInt) return reduceInt(results); else return reduceDouble(results); } }; ///////////////////////////////////////////////////////////////////// // Min ///////////////////////////////////////////////////////////////////// class Min : public Reduce { protected: virtual int64_t reduceInt(Values const &values) const { int64_t result = std::numeric_limits< int64_t >::max(); for (Values::const_iterator it = values.begin(); it != values.end(); it++) { bool valid; int64_t value; it->getInt(valid, value); assert(valid); result = std::min(result, value); } return result; } virtual double reduceDouble(Values const &values) const { double result = std::numeric_limits< double >::max(); for (Values::const_iterator it = values.begin(); it != values.end(); it++) { bool valid; double value; it->getDouble(valid, value); assert(valid); result = std::min(result, value); } return result; } public: static Executable *tryCreate(Parse::Tree const &source) { return tryCreateHelper< Parse::MinFunction, Min >(source); } }; ///////////////////////////////////////////////////////////////////// // Max ///////////////////////////////////////////////////////////////////// class Max : public Reduce { private: protected: virtual int64_t reduceInt(Values const &values) const { // lowest() would be better than min(). lowest() does't exist on becca. int64_t result = std::numeric_limits< int64_t >::min(); for (Values::const_iterator it = values.begin(); it != values.end(); it++) { bool valid; int64_t value; it->getInt(valid, value); assert(valid); result = std::max(result, value); } return result; } virtual double reduceDouble(Values const &values) const { // lowest() would be better than -max(). lowest() does't exist on becca. double result = -std::numeric_limits< double >::max(); for (Values::const_iterator it = values.begin(); it != values.end(); it++) { bool valid; double value; it->getDouble(valid, value); assert(valid); result = std::max(result, value); } return result; } public: static Executable *tryCreate(Parse::Tree const &source) { return tryCreateHelper< Parse::MaxFunction, Max >(source); } }; ///////////////////////////////////////////////////////////////////// // SimpleFunction ///////////////////////////////////////////////////////////////////// static const double LOG10 = log(10); class SimpleFunction : public Executable1 { private: const Parse::Function1::Which _which; SimpleFunction(Parse::Function1::Which which, Executable *input) : Executable1(input), _which(which) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool valid; double value; _arg1->execute(recordInfo).getDouble(valid, value); if (!valid) return ValueBox(); switch (_which) { case Parse::Function1::Abs: value = fabs(value); break; case Parse::Function1::Sin: value = sin(value); break; case Parse::Function1::Cos: value = cos(value); break; case Parse::Function1::Tan: value = tan(value); break; case Parse::Function1::ASin: value = asin(value); break; case Parse::Function1::ACos: value = acos(value); break; case Parse::Function1::ATan: value = atan(value); break; case Parse::Function1::Ceil: value = ceil(value); break; case Parse::Function1::Floor: value = floor(value); break; case Parse::Function1::Exp: value = exp(value); break; case Parse::Function1::Ln: value = log(value); break; case Parse::Function1::Sqrt: value = sqrt(value); break; case Parse::Function1::NONE: assert(false); break; // Do not include a default. // I want to see a compiler warning if I'm missing something. }; return value; } static Executable *tryCreate(Parse::Tree const &source) { Parse::Function1 const *const f = source->as< Parse::Function1 >(); if (!f) return NULL; return new SimpleFunction(f->getWhich(), create(f->getInput())); } }; ///////////////////////////////////////////////////////////////////// // Comparison ///////////////////////////////////////////////////////////////////// class Comparison : public Executable2 { private: const Parse::ComparisonFunction::Which _which; Comparison(Parse::ComparisonFunction::Which which, Executable *LHS, Executable *RHS) : Executable2(LHS, RHS), _which(which) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { const ValueBox LHS = _arg1->execute(recordInfo); const InternalType leftType = LHS.getType(); switch (leftType) { case InternalType::CHAR_STAR: case InternalType::STRING: { bool valid; char const *leftString; LHS.getString(valid, leftString); assert(valid); char const *rightString; const ValueBox RHS = _arg2->execute(recordInfo); RHS.getString(valid, rightString); if (!valid) return ValueBox(); int comparison = strcmp(leftString, rightString); bool result; switch (_which) { case Parse::ComparisonFunction::Less: result = comparison < 0; break; case Parse::ComparisonFunction::Greater: result = comparison > 0; break; case Parse::ComparisonFunction::Equal: result = comparison == 0; break; case Parse::ComparisonFunction::LessOrEqual: result = comparison <= 0; break; case Parse::ComparisonFunction::GreaterOrEqual: result = comparison >= 0; break; case Parse::ComparisonFunction::NotEqual: result = comparison != 0; break; default: // Avoid a compiler warning. throw std::logic_error("Unknown comparison function."); } return ValueBox(result); } case InternalType::INTEGER: case InternalType::DOUBLE: { const ValueBox RHS = _arg2->execute(recordInfo); const bool useInt = (leftType == InternalType::INTEGER) && (RHS.getType() == InternalType::INTEGER); bool result; if (useInt) { const int64_t leftInt = LHS.getInt(); const int64_t rightInt = RHS.getInt(); switch (_which) { case Parse::ComparisonFunction::Less: result = leftInt < rightInt; break; case Parse::ComparisonFunction::Greater: result = leftInt > rightInt; break; case Parse::ComparisonFunction::Equal: result = leftInt == rightInt; break; case Parse::ComparisonFunction::LessOrEqual: result = leftInt <= rightInt; break; case Parse::ComparisonFunction::GreaterOrEqual: result = leftInt >= rightInt; break; case Parse::ComparisonFunction::NotEqual: result = leftInt != rightInt; break; default: // Avoid a compiler warning. throw std::logic_error("Unknown comparison function."); } } else { const double leftDouble = LHS.getDouble(); bool valid; double rightDouble; RHS.getDouble(valid, rightDouble); if (!valid) return ValueBox(); switch (_which) { case Parse::ComparisonFunction::Less: result = leftDouble < rightDouble; break; case Parse::ComparisonFunction::Greater: result = leftDouble > rightDouble; break; case Parse::ComparisonFunction::Equal: result = leftDouble == rightDouble; break; case Parse::ComparisonFunction::LessOrEqual: result = leftDouble <= rightDouble; break; case Parse::ComparisonFunction::GreaterOrEqual: result = leftDouble >= rightDouble; break; case Parse::ComparisonFunction::NotEqual: result = leftDouble != rightDouble; break; default: // Avoid a compiler warning. throw std::logic_error("Unknown comparison function."); } } return ValueBox(result); } default: return ValueBox(); } } static Executable *tryCreate(Parse::Tree const &source) { Parse::ComparisonFunction const *const f = source->as< Parse::ComparisonFunction >(); if (!f) return NULL; return new Comparison(f->getWhich(), create(f->getLHS()), create(f->getRHS())); } virtual std::string thisItemNameDebug() { switch (_which) { case Parse::ComparisonFunction::Less: return "<"; case Parse::ComparisonFunction::Greater: return ">"; case Parse::ComparisonFunction::Equal: return "=="; case Parse::ComparisonFunction::LessOrEqual: return "<="; case Parse::ComparisonFunction::GreaterOrEqual: return ">="; case Parse::ComparisonFunction::NotEqual: return "!="; break; default: return "Unknown comparison function!"; } } }; ///////////////////////////////////////////////////////////////////// // If ///////////////////////////////////////////////////////////////////// class If : public Executable3 { private: If(Executable *condition, Executable *trueBranch, Executable *falseBranch) : Executable3(condition, trueBranch, falseBranch) { } protected: virtual std::string thisItemNameDebug() { return "If"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { Executable *code; if (_arg1->execute(recordInfo).getBoolean()) code = _arg2; else code = _arg3; return code->execute(recordInfo); } static Executable *tryCreate(Parse::Tree const &source) { Parse::IfFunction const *const f = source->as< Parse::IfFunction >(); if (!f) return NULL; return new If(create(f->getCondition()), create(f->getTrueBranch()), create(f->getFalseBranch())); } }; ///////////////////////////////////////////////////////////////////// // NullCoalescing ///////////////////////////////////////////////////////////////////// class NullCoalescing : public ExecutableN { public: virtual ValueBox execute(RecordInfo &recordInfo) const { for (Args::const_iterator it = getArgs().begin(); it != getArgs().end(); it++) { const ValueBox result = (*it)->execute(recordInfo); if (!result.isEmpty()) return result; } return ValueBox(); } static Executable *tryCreate(Parse::Tree const &source) { return tryCreateHelper< Parse::NullCoalescingFunction, NullCoalescing >(source); } }; ///////////////////////////////////////////////////////////////////// // ValidDouble ///////////////////////////////////////////////////////////////////// class ValidDouble : public Executable1 { private: ValidDouble(Executable *input) : Executable1(input) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { InternalType type = _arg1->execute(recordInfo).getType(); const bool value = (type == InternalType::INTEGER) || (type == InternalType::DOUBLE); return ValueBox(value); } static Executable *tryCreate(Parse::Tree const &source) { Parse::ValidDouble const *const f = source->as< Parse::ValidDouble >(); if (!f) return NULL; return new ValidDouble(create(f->getInput())); } }; ///////////////////////////////////////////////////////////////////// // Pow ///////////////////////////////////////////////////////////////////// class Pow : public Executable2 { private: Pow(Executable *base, Executable *power) : Executable2(base, power) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool valid; double value1; _arg1->execute(recordInfo).getDouble(valid, value1); if (!valid) return ValueBox(); double value2; _arg2->execute(recordInfo).getDouble(valid, value2); if (!valid) return ValueBox(); return pow(value1, value2); } static Executable *tryCreate(Parse::Tree const &source) { Parse::PowFunction const *const f = source->as< Parse::PowFunction >(); if (!f) return NULL; return new Pow(create(f->getBase()), create(f->getPower())); } }; ///////////////////////////////////////////////////////////////////// // Log ///////////////////////////////////////////////////////////////////// class Log : public Executable2 { private: Log(Executable *base, Executable *value) : Executable2(base, value) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool valid; double value1; _arg1->execute(recordInfo).getDouble(valid, value1); if (!valid) return ValueBox(); double value2; _arg2->execute(recordInfo).getDouble(valid, value2); if (!valid) return ValueBox(); const double divisor = log(value1); if (!finite(divisor)) // Log of anything base 0 should return an error. If I didn't // explicitly check for this case, log of a positive number base 0 // would be a real number divided by -Infinity, which would be -0! return ValueBox(); return log(value2)/divisor; } static Executable *tryCreate(Parse::Tree const &source) { Parse::LogFunction const *const f = source->as< Parse::LogFunction >(); if (!f) return NULL; return new Log(create(f->getBase()), create(f->getValue())); } }; ///////////////////////////////////////////////////////////////////// // Minus // // If both arguments are integers, the result is an integer. We // automatically promote one argument from an integer to a double if // the other is already a double. ///////////////////////////////////////////////////////////////////// class Minus : public Executable2 { private: Minus(Executable *a, Executable *b) : Executable2(a, b) { } protected: virtual std::string thisItemNameDebug() { return "-"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool useInt; const ValueBox aValue = _arg1->execute(recordInfo); switch (aValue.getType()) { case InternalType::INTEGER: useInt = true; break; case InternalType::DOUBLE: useInt = false; break; default: return ValueBox(); } const ValueBox bValue = _arg2->execute(recordInfo); if (useInt && (bValue.getType() == InternalType::DOUBLE)) useInt = false; if (useInt) { const int64_t aInt = aValue.getInt(); bool valid; int64_t bInt; bValue.getInt(valid, bInt); if (!valid) return ValueBox(); return aInt - bInt; } else { const double aDouble = aValue.getDouble(); bool valid; double bDouble; bValue.getDouble(valid, bDouble); if (!valid) return ValueBox(); return aDouble - bDouble; } } static Executable *tryCreate(Parse::Tree const &source) { Parse::MinusFunction const *const f = source->as< Parse::MinusFunction >(); if (!f) return NULL; return new Minus(create(f->getLeftArg()), create(f->getRightArg())); } }; ///////////////////////////////////////////////////////////////////// // Divide // // Convert integers to doubles, to match the mysql behavior. This // is required and expected for our standard formulas. ///////////////////////////////////////////////////////////////////// class Divide : public Executable2 { private: Divide(Executable *a, Executable *b) : Executable2(a, b) { } protected: virtual std::string thisItemNameDebug() { return "/"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { const ValueBox aValue = _arg1->execute(recordInfo); bool valid; double aDouble; aValue.getDouble(valid, aDouble); if (!valid) return ValueBox(); const ValueBox bValue = _arg2->execute(recordInfo); double bDouble; bValue.getDouble(valid, bDouble); if (!valid) return ValueBox(); return aDouble / bDouble; } static Executable *tryCreate(Parse::Tree const &source) { Parse::DivideFunction const *const f = source->as< Parse::DivideFunction >(); if (!f) return NULL; return new Divide(create(f->getLeftArg()), create(f->getRightArg())); } }; ///////////////////////////////////////////////////////////////////// // Plus // // Add 0 or more numbers. If all inputs are integers, including the // case of no inputs, the result will be an integer. If any input // is not a valid number, the result is EMPTY. Otherwise (all // numbers and at least one double as inputs) the result will be a // double. If necessary we automatically convert from int to double. // // We do no overflow checking on integers. // // This accepts 0 or more numbers, but it's unlikely that we will // have fewer than 2 inputs. The compiler shouldn't generate that. // But we'd accept it if we got it. (The optimizer should get rid // of a case with 0 or 1 inputs. If you skip the optimizer, then // there's no way to get this case in the first place!) ///////////////////////////////////////////////////////////////////// class Plus : public Reduce { protected: virtual int64_t reduceInt(Values const &values) const { int64_t accumulator = 0; for (Values::const_iterator it = values.begin(); it != values.end(); it++) accumulator += (*it).getInt(); return accumulator; } virtual double reduceDouble(Values const &values) const { double accumulator = 0.0; for (Values::const_iterator it = values.begin(); it != values.end(); it++) accumulator += (*it).getDouble(); return accumulator; } virtual std::string thisItemNameDebug() { return "+"; } public: static Executable *tryCreate(Parse::Tree const &source) { return tryCreateHelper< Parse::PlusFunction, Plus >(source); } }; ///////////////////////////////////////////////////////////////////// // Times // // Multiply 0 or more numbers. If all inputs are integers, including // the case of no inputs, the result will be an integer. If any // input is not a valid number, the result is EMPTY. Otherwise (all // numbers and at least one double as inputs) the result will be a // double. If necessary we automatically convert from int to double. // // We do no overflow checking on integers. // // This accepts 0 or more numbers, but it's unlikely that we will // have fewer than 2 inputs. The compiler shouldn't generate that. // But we'd accept it if we got it. (The optimizer should get rid // of a case with 0 or 1 inputs. If you skip the optimizer, then // there's no way to get this case in the first place!) ///////////////////////////////////////////////////////////////////// class Times : public Reduce { protected: virtual int64_t reduceInt(Values const &values) const { int64_t accumulator = 1; for (Values::const_iterator it = values.begin(); it != values.end(); it++) accumulator *= (*it).getInt(); return accumulator; } virtual double reduceDouble(Values const &values) const { double accumulator = 1.0; for (Values::const_iterator it = values.begin(); it != values.end(); it++) accumulator *= (*it).getDouble(); return accumulator; } virtual std::string thisItemNameDebug() { return "-"; } public: static Executable *tryCreate(Parse::Tree const &source) { return tryCreateHelper< Parse::TimesFunction, Times >(source); } }; ///////////////////////////////////////////////////////////////////// // And ///////////////////////////////////////////////////////////////////// class And : public ExecutableN { private: protected: virtual std::string thisItemNameDebug() { return "And"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { for (Args::const_iterator it = getArgs().begin(); it != getArgs().end(); it++) { if (!(*it)->execute(recordInfo).getBoolean()) return false; } return true; } static Executable *tryCreate(Parse::Tree const &source) { return tryCreateHelper< Parse::AndFunction, And >(source); } }; ///////////////////////////////////////////////////////////////////// // Or ///////////////////////////////////////////////////////////////////// class Or : public ExecutableN { private: protected: virtual std::string thisItemNameDebug() { return "Or"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { for (Args::const_iterator it = getArgs().begin(); it != getArgs().end(); it++) { if ((*it)->execute(recordInfo).getBoolean()) return true; } return false; } static Executable *tryCreate(Parse::Tree const &source) { return tryCreateHelper< Parse::OrFunction, Or >(source); } }; ///////////////////////////////////////////////////////////////////// // Not ///////////////////////////////////////////////////////////////////// class Not : public Executable1 { private: Not(Executable *arg1) : Executable1(arg1) { } protected: virtual std::string thisItemNameDebug() { return "!"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { bool valid; bool value; _arg1->execute(recordInfo).getBoolean(valid, value); if (!valid) return ValueBox(); return !value; } static Executable *tryCreate(Parse::Tree const &source) { Parse::NotFunction const *const f = source->as< Parse::NotFunction >(); if (!f) return NULL; return new Not(create(f->getValue())); } }; ///////////////////////////////////////////////////////////////////// // CompanyName ///////////////////////////////////////////////////////////////////// class CompanyName : public Executable1 { private: static const std::string s_CompanyName; CompanyName(Executable *arg1) : Executable1(arg1) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { return CompanyNameManager::get(_arg1->execute(recordInfo)); } static Executable *tryCreate(Parse::Tree const &source) { Parse::CompanyName const *const f = source->as< Parse::CompanyName >(); if (!f) return NULL; return new CompanyName(create(f->getSymbol())); } }; const std::string CompanyName::s_CompanyName = "CompanyName"; ///////////////////////////////////////////////////////////////////// // Sector ///////////////////////////////////////////////////////////////////// class Sector : public Executable1 { private: static const std::string s_Sector; const int _level; Sector(Executable *arg1, int level) : Executable1(arg1), _level(level) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { return NaicsManager::get(_arg1->execute(recordInfo), _level); } static Executable *tryCreate(Parse::Tree const &source) { Parse::SectorStringFunction const *const f = source->as< Parse::SectorStringFunction >(); if (!f) return NULL; return new Sector(create(f->getSymbol()), f->getLevel()); } }; const std::string Sector::s_Sector = "Sector"; ///////////////////////////////////////////////////////////////////// // AlertsDaily ///////////////////////////////////////////////////////////////////// class AlertsDaily : public Executable2 { private: static const std::string s_AlertsDaily; AlertsDaily(Executable *symbol, Executable *time) : Executable2(symbol, time) { } protected: virtual std::string thisItemNameDebug() { return "AlertsDaily"; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { const ValueBox symbolBox = _arg1->execute(recordInfo); bool valid; char const *symbol; symbolBox.getString(valid, symbol); if (!valid) return ValueBox(); int64_t time = 0; // Initialization avoids a silly complier warning. _arg2->execute(recordInfo).getInt(valid, time); if (!valid) return ValueBox(); return IAlertsDaily::get()->find(symbol, time); } static Executable *tryCreate(Parse::Tree const &source) { Parse::AlertsDaily const *const f = source->as< Parse::AlertsDaily >(); if (!f) return NULL; return new AlertsDaily(create(f->getSymbol()), create(f->getTime())); } }; const std::string AlertsDaily::s_AlertsDaily = "AlertsDaily"; ///////////////////////////////////////////////////////////////////// // ReadARecord ///////////////////////////////////////////////////////////////////// class ReadARecord : public Executable1 { private: static const std::string s_ReadARecord; const FieldId _fieldId; ReadARecord(FieldId fieldId, Executable *recordProvider) : Executable1(recordProvider), _fieldId(fieldId) { } protected: virtual std::string thisItemNameDebug() { return "ReadARecord " + ntoa(_fieldId) + getComment(getDailyFields(), _fieldId); } public: virtual ValueBox execute(RecordInfo &recordInfo) const { return _arg1->execute(recordInfo).lookUpValue(_fieldId); } static Executable *tryCreate(Parse::Tree const &source) { Parse::SecondaryField const *const f = source->as< Parse::SecondaryField >(); if (!f) return NULL; return new ReadARecord(f->getFieldId(), create(f->getRecord())); } }; const std::string ReadARecord::s_ReadARecord = "ReadARecord"; ///////////////////////////////////////////////////////////////////// // ReadCurrentRecord ///////////////////////////////////////////////////////////////////// class ReadCurrentRecord : public Executable { private: static const std::string s_ReadCurrentRecord; const FieldId _fieldId; ReadCurrentRecord(FieldId fieldId) : _fieldId(fieldId) { } protected: virtual std::string thisItemNameDebug() { return "ReadCurrentRecord " + ntoa(_fieldId) + getComment(getAlertFields(), _fieldId); } public: virtual ValueBox execute(RecordInfo &recordInfo) const { return recordInfo.currentRecord->lookUpValue(_fieldId); } static Executable *tryCreate(Parse::Tree const &source) { Parse::PrimaryField const *const f = source->as< Parse::PrimaryField >(); if (!f) return NULL; return new ReadCurrentRecord(f->getFieldId()); } }; const std::string ReadCurrentRecord::s_ReadCurrentRecord = "ReadCurrentRecord"; ///////////////////////////////////////////////////////////////////// // AltField ///////////////////////////////////////////////////////////////////// class AltField : Executable1 { private: const std::string _fieldName; AltField(std::string const &fieldName, Executable *source) : Executable1(source), _fieldName(fieldName) { } public: virtual ValueBox execute(RecordInfo &recordInfo) const { const ValueBox sourceBox = _arg1->execute(recordInfo); bool valid; std::string source; sourceBox.getString(valid, source); if (!valid) return ValueBox(); // Perhaps this next part should be cached. PropertyList properties; parseUrlRequest(properties, source); // This is more or less consistent with the php/sql version. It only // extracts numbers. Some of these fields are strings, and it would // be powerful if we could extract those, too! This is poorly defined // in the user documentation and it could change in the future. const double value = strtodDefault(getPropertyDefault(properties, _fieldName), std::numeric_limits< double >::quiet_NaN()); // The conversion to ValueBox will automatically convert NaN to EMPTY. return value; } static Executable *tryCreate(Parse::Tree const &source) { Parse::AltField const *const f = source->as< Parse::AltField >(); if (!f) return NULL; return new AltField(f->getFieldName(), create(f->getSource())); } }; ///////////////////////////////////////////////////////////////////// // CachedValue ///////////////////////////////////////////////////////////////////// //static const std::string s_CachedValue = "CachedValue"; class CachedValue : public Executable1 { private: const int _location; CachedValue(Executable *code, int location) : Executable1(code), _location(location) { } protected: virtual std::string thisItemNameDebug() { return "CachedValue " + ntoa(_location); } virtual std::string childrenNamesDebug() { if (shortCacheNextTime) { if (shortCacheNextTime->count(_location)) return " …"; else shortCacheNextTime->insert(_location); } return Executable1::childrenNamesDebug(); } public: virtual ValueBox execute(RecordInfo &recordInfo) const { if (_location >= (int)recordInfo.cache.size()) { // I've seen some problems in the past. Usually a few lines down // when we go to return a result. We can't copy the result item // and it appears to be corrupt. That causes a core dump, usually // from an assertion failure. I can only assume that comes from // an attempt to read off the end of the cache vector. TclList msg; msg<execute(recordInfo); return result; } static Executable *tryCreate(Parse::Tree const &source) { Parse::CachedValue const *const f = source->as< Parse::CachedValue >(); if (!f) return NULL; return new CachedValue(create(f->getCode()), f->getLocation()); } virtual void cacheInfoDebug(std::map< int, int > &used) { Executable1::cacheInfoDebug(used); used[_location]++; } }; ///////////////////////////////////////////////////////////////////// // StaticValue ///////////////////////////////////////////////////////////////////// class StaticValue : public Executable { private: const ValueBox _value; StaticValue(ValueBox const &value) : _value(value) { } protected: virtual std::string thisItemNameDebug() { return "StaticValue " + _value.shortDebug(); } public: virtual ValueBox execute(RecordInfo &recordInfo) const { return _value; } static Executable *tryCreate(Parse::Tree const &source) { if (Parse::IntTreeNode const *const n = source->as< Parse::IntTreeNode >()) return new StaticValue(n->getValue()); if (Parse::DoubleTreeNode const *const n = source->as< Parse::DoubleTreeNode >()) return new StaticValue(n->getValue()); if (Parse::StringTreeNode const *const n = source->as< Parse::StringTreeNode >()) return new StaticValue(n->getValue()); if (source->as< Parse::NullValue >()) return new StaticValue(ValueBox()); return NULL; } }; ///////////////////////////////////////////////////////////////////// // ConstantStringSet ///////////////////////////////////////////////////////////////////// class ConstantStringSet : public Executable1 { public: typedef std::set< std::string > Matching; private: CharStarHashSet _matching; ConstantStringSet(Executable *value, Matching const &matching) : Executable1(value), _matching() { for (auto it = matching.cbegin(); it != matching.cend(); it++) addString(_matching, *it); } protected: virtual std::string thisItemNameDebug() { // More LISP. (a b c) means that a is a function and b and c are the // arguments. '(a b c) means a list with three items, a, b and c. std::string result = "ConstantStringSet '("; bool first = true; for (auto it = _matching.begin(); it != _matching.end(); it++) { if (first) first = false; else result += ' '; result += "“"; result += it->first; result += "”"; } result += ')'; return result; } public: virtual ValueBox execute(RecordInfo &recordInfo) const { const ValueBox valueBox = _arg1->execute(recordInfo); bool valid; char const *value; valueBox.getString(valid, value); if (!valid) return ValueBox(); // I have to explicitly convert this because otherwise this is ambiguous // between int and double. We've covered most cases automatically, but // count() returns uint64_t. return (bool)_matching.count(value); } static Executable *tryCreate(Parse::Tree const &source) { Parse::ConstantStringSet const *const f = source->as< Parse::ConstantStringSet >(); if (!f) return NULL; return new ConstantStringSet(create(f->getValue()), f->getMatching()); } }; ///////////////////////////////////////////////////////////////////// // Dispatcher ///////////////////////////////////////////////////////////////////// class Dispatcher { private: typedef std::function< Executable*(Parse::Tree const &source) > TryCreate; std::unordered_map< std::type_index, TryCreate > _constructors; template < typename Source, typename Destination > void add() { _constructors[typeid(Source)] = Destination::tryCreate; } Dispatcher() { add< Parse::NowFunction, Now >(); add< Parse::SecondsAfterOpen, SecondsAfterOpen >(); add< Parse::DayOfWeek, DayOfWeek >(); add< Parse::RoundFunction, Round >(); add< Parse::MinFunction, Min >(); add< Parse::MaxFunction, Max >(); add< Parse::Function1, SimpleFunction >(); add< Parse::ComparisonFunction, Comparison >(); add< Parse::IfFunction, If >(); add< Parse::NullCoalescingFunction, NullCoalescing >(); add< Parse::ValidDouble, ValidDouble >(); add< Parse::PowFunction, Pow >(); add< Parse::LogFunction, Log >(); add< Parse::MinusFunction, Minus >(); add< Parse::DivideFunction, Divide >(); add< Parse::PlusFunction, Plus >(); add< Parse::TimesFunction, Times >(); add< Parse::AndFunction, And >(); add< Parse::OrFunction, Or >(); add< Parse::NotFunction, Not >(); add< Parse::CompanyName, CompanyName >(); add< Parse::SectorStringFunction, Sector >(); add< Parse::AlertsDaily, AlertsDaily >(); add< Parse::SecondaryField, ReadARecord >(); add< Parse::PrimaryField, ReadCurrentRecord >(); add< Parse::AltField, AltField >(); add< Parse::CachedValue, CachedValue >(); add< Parse::IntTreeNode, StaticValue >(); add< Parse::DoubleTreeNode, StaticValue >(); add< Parse::StringTreeNode, StaticValue >(); add< Parse::NullValue, StaticValue >(); add< Parse::ConstantStringSet, ConstantStringSet >(); } public: static Dispatcher instance; Executable *tryCreate(Parse::Tree const &source) { auto it = _constructors.find(typeid(*source)); if (it == _constructors.end()) return NULL; else return it->second(source); } }; Dispatcher Dispatcher::instance; ///////////////////////////////////////////////////////////////////// // Executable ///////////////////////////////////////////////////////////////////// Executable *Executable::create(Parse::Tree const &source) { Executable *result; if (ICompile const *const factory = source->as< ICompile >()) { // The item knows how to compile itself. if ((result = factory->compile())) return result; } else if ((result = Dispatcher::instance.tryCreate(source))) return result; // This should never happen. It's tempting to make this into an // assertion failure. I suppose it will happen a lot in development, // before we are finished implementing the compiler. throw source->getToken().makeException("Unable to compile: " + source->shortDebug()); } std::vector< Executable * > Executable::allChildrenDebug() { // By default we have no children. Look at classes like Executable1 // to fill this in. Often We don't use vectors for the real operations // for performance reasons. (Debug operations like this use a vector // everywhere to make the code simpler.) Base classes like Executable1 // and Executable2 take care of the details of having children. return std::vector< Executable * >(); } void Executable::cacheInfoDebug(std::map< int, int > &used) { // By default we have no cache ourselves, but check our children. const std::vector< Executable * > children = allChildrenDebug(); for (auto it = children.begin(); it != children.end(); it++) (*it)->cacheInfoDebug(used); } std::string Executable::thisItemNameDebug() { // By default use the class name. It's a little ugly, but usually // readable. return typeid(*this).name(); } std::string Executable::childrenNamesDebug() { std::string result; std::vector< Executable * > children = allChildrenDebug(); for (auto it = children.begin(); it != children.end(); it++) { result += ' '; result += (*it)->nameDebug(); } return result; } std::string Executable::nameDebug() { std::string result; result += '('; result += thisItemNameDebug(); result += childrenNamesDebug(); result += ')'; return result; } };