#ifndef __Semantics_h_ #define __Semantics_h_ #include #include "Parse.h" #include "FieldLists.h" /* These are the classes which represent specific actions in the compiler. * for example, if the user creates a formula like [Price]*2, it will compile * to a TreeNode. These are the various types of TreeNodes that can be * assembled in this process. These are things like "multiply the results of * two other tree nodes" or "look up the price of this stock" or the constant * value 2. These specific types of TreeNode objects are defined in this file. * * Note that TreeNode objects form the middle layer. * 1) Users typically specify what they want as a formula (like "[Price]*2") * or a collaborate string. However a TreeNode can come from anywhere. * 2) TreeNode objects are easy to manipulate. For example we slightly modify * each top list formula when we freeze the market at the close. And the * optimizer can look for common sub-trees. * 3) Eventually you will use Executable::create() to compile the TreeNode * objects into an Executable object you can run. Executable objects are * meant to be fast and simple. * * See Parser::parse() for instructions for making a TreeNode from a formula. * See Strategy.h for instructions for making TreeNodes from collaborate * strings. Or use the various constructors found in this file to create a * TreeNodes directly. */ namespace Parse { // Often represented as $$$ in formulas. This is sometimes the closing // price and sometimes the current price. This varies based on the context. // I think I added this because of top lists, which we often try/pretend to // freeze at the close. See PairedFilterList::fixPrice in AlertConfig.C for // more details. class AppropriatePrice : public TreeNode { private: class Rule : public ReplaceRule { private: const std::string _fieldName; const FieldId _fieldId; protected: virtual void replaceImpl(Tree &tree, Status &status) const; public: Rule(std::string const &fieldName, FieldId fieldId) : _fieldName(fieldName), _fieldId(fieldId) { } }; public: AppropriatePrice(Token const &token) : TreeNode(token) { } // This is the price of the most recent print that we've recieved. This // is probably what set off the alert. We always use this for alerts, // and often for top lists. static Tree useCurrentPrice(Tree orig); // This is the price of the "last" official print. This ignores pre and // post market prints, among other things. This is typically used when the // top list is frozen at the close. static Tree useLastPrice(Tree orig); }; // This is a field which can be found directly in the alert or top_list // record. class PrimaryField : public TreeNode { private: const FieldId _fieldId; protected: virtual int compareSameType(TreeNode const &other) const; virtual uint64_t getHashContributaion() const { return sHash(_fieldId); } public: virtual std::string shortDebug() const; PrimaryField(Token const &token, FieldId fieldId) : TreeNode(token), _fieldId(fieldId) { } // These are well known fields, used in multiple places. static const Tree SYMBOL; static const Tree TIMESTAMP; FieldId getFieldId() const { return _fieldId; } }; // This will replace ANYTHING with a primary field with the given field id. // Presumably SymbolRules or a similar class will take care of the matching // part, so this only has to provide the new value. class PrimaryFieldRule : public ReplaceRule { protected: const FieldId _fieldId; virtual void replaceImpl(Tree &tree, Status &status) const { tree = new PrimaryField(tree->getToken(), _fieldId); status = sRetryTop; } public: PrimaryFieldRule(FieldId fieldId) : _fieldId(fieldId) { } }; // This is an entire record. We need this object for the sake of caching. // We only want to look up the record once. class AlertsDaily : public TreeNode { public: AlertsDaily(Tree symbol, Tree time); virtual Tree construct(Args const &children) const; // This is a field which must be looked up in the alerts_daily table. static Tree field(Token const &token, FieldId fieldId); static const Tree LIST_EXCH; // Listed exchange. Tree getSymbol() const { return getChildren()[0]; } Tree getTime() const { return getChildren()[1]; } }; class SecondaryField : public TreeNode { private: const FieldId _fieldId; protected: virtual int compareSameType(TreeNode const &other) const; virtual uint64_t getHashContributaion() const { return sHash(_fieldId); } public: virtual std::string shortDebug() const; SecondaryField(Token const &token, FieldId fieldId, Tree record); virtual Tree construct(Args const &children) const; FieldId getFieldId() const { return _fieldId; } Tree getRecord() const { return getChildren()[0]; } }; // This will replace ANYTHING with a daily field with the given field name. // Presumably SymbolRules or a similar class will take care of the matching // part, so this only has to provide the new value. class DailyFieldRule : public ReplaceRule { protected: const FieldId _fieldId; virtual void replaceImpl(Tree &tree, Status &status) const { tree = AlertsDaily::field(tree->getToken(), _fieldId); status = sRetryTop; } public: DailyFieldRule(FieldId fieldId) : _fieldId(fieldId) { } }; class AltField : public TreeNode { private: AltField(Token token, std::string const &fieldName, Tree const &source) : TreeNode(token, { Tree(new StringTreeNode(fieldName)), source }) { } AltField(Token token, Args const &children); public: static Tree create(Token token); virtual Tree construct(Args const &children) const; std::string getFieldName() const; Tree getSource() const { return getChildren()[1]; } }; class RoundFunction : public TreeNode { private: void verifyChildren() const; public: // This will throw an exception if the arguments don't look right. virtual Tree construct(Args const &children) const { return new RoundFunction(getToken(), children); } RoundFunction(Token const &token, Tree toRound, int digits) : TreeNode(token, {toRound, IntTreeNode::create(digits)}) { } // This will throw an exception if the arguments don't look right. RoundFunction(Token const &token, Args const &children) : TreeNode(token, children) { verifyChildren(); } Tree toRound() const; int digits() const; }; class SectorStringFunction : public TreeNode { private: Args addSymbol(Args const &args) const; void verifyChildren() const; public: virtual Tree construct(Args const &children) const { return new SectorStringFunction(getToken(), children); } SectorStringFunction(Token const &token, Args const &args) : TreeNode(token, addSymbol(args)) { verifyChildren(); } Tree getSymbol() const { return getChildren()[0]; } int getLevel() const; }; // This will add a list of 0 or more numbers. If an input is anything other // than a number, the result is null. If there are 0 inputs, the result is // 0. If there is one input, and it is valid, that is the result. class PlusFunction : public TreeNode { public: PlusFunction(Token const &token, Args const &args) : TreeNode(token, args) { } PlusFunction(Token const &token, Tree left, Tree right) : TreeNode(token, {left, right}) { } virtual Tree construct(Args const &children) const { return new PlusFunction(getToken(), children); } Args const &getArgs() const { return getChildren(); } }; // This will multiply a list of 0 or more numbers. If an input is anything // other than a number, the result is null. If there are 0 inputs, the // result is 1. If there is one input, and it is valid, that is the result. class TimesFunction : public TreeNode { public: TimesFunction(Token const &token, Args const &args) : TreeNode(token, args) { } TimesFunction(Token const &token, Tree left, Tree right) : TreeNode(token, {left, right}) { } virtual Tree construct(Args const &children) const { return new TimesFunction(getToken(), children); } Args const &getArgs() const { return getChildren(); } }; // Both arguments must be valid numbers or the result will be null. // A unary minus will get converted to a binary minus in the constructor. // "-x" becomes "0-x". class MinusFunction : public TreeNode { public: MinusFunction(Token const &token, Tree toNegate) : TreeNode(token, {IntTreeNode::create(0), toNegate}) { } MinusFunction(Token const &token, Tree left, Tree right) : TreeNode(token, {left, right}) { } virtual Tree construct(Args const &children) const { // This should not be a problem. Replace only calls construct with // the same number of arguments that you start with. assert(children.size() == 2); return new MinusFunction(getToken(), children[0], children[1]); } Tree getLeftArg() const { return getChildren()[0]; } Tree getRightArg() const { return getChildren()[1]; } }; // Both arguments must be valid numbers or the result will be null. Division // by 0 results in null. class DivideFunction : public TreeNode { public: DivideFunction(Token const &token, Tree left, Tree right) : TreeNode(token, {left, right}) { } virtual Tree construct(Args const &children) const { // This should not be a problem. Replace only calls construct with // the same number of arguments that you start with. assert(children.size() == 2); return new DivideFunction(getToken(), children[0], children[1]); } Tree getLeftArg() const { return getChildren()[0]; } Tree getRightArg() const { return getChildren()[1]; } }; class MinFunction : public TreeNode { private: void verifyChildren() const; public: MinFunction(Token const &token, Args const &args) : TreeNode(token, args) { verifyChildren(); } virtual Tree construct(Args const &children) const { return new MinFunction(getToken(), children); } Args const &getArgs() const { return getChildren(); } }; class MaxFunction : public TreeNode { private: void verifyChildren() const; public: MaxFunction(Token const &token, Args const &args) : TreeNode(token, args) { verifyChildren(); } virtual Tree construct(Args const &children) const { return new MaxFunction(getToken(), children); } Args const &getArgs() const { return getChildren(); } }; class IfFunction : public TreeNode { private: // This will ensure that we store 3 arguments. If we have 2, it will add a // default for the 3rd. If we have fewer than 2 or more than 3, this will // throw an exception. static Args checkArgs(Token const &token, Args args); public: IfFunction(Token const &token, Args const &args) : TreeNode(token, checkArgs(token, args)) { } virtual Tree construct(Args const &children) const { return new IfFunction(getToken(), children); } Tree getCondition() const { return getChildren()[0]; } Tree getTrueBranch() const { return getChildren()[1]; } Tree getFalseBranch() const { return getChildren()[2]; } virtual Tree optimize(OptimizationContext context) const; }; class NullCoalescingFunction : public TreeNode { public: NullCoalescingFunction(Token const &token, Args const &args) : TreeNode(token, args) { } NullCoalescingFunction(Args const &args) : TreeNode(Token::EMPTY, args) { } virtual Tree construct(Args const &children) const { return new NullCoalescingFunction(getToken(), children); } Args const &getArgs() const { return getChildren(); } }; // Returns true if the value is a double or integer. Returns false in // any other case. I.e. it returns true if the number can be converted // into a double. class ValidDouble : public TreeNode { public: ValidDouble(Tree input) : TreeNode(Token::EMPTY, {input}) {} virtual Tree construct(Args const &children) const; Tree getInput() const { return getChildren()[0]; } virtual Tree optimize(OptimizationContext context) const; }; class PowFunction : public TreeNode { private: void verifyChildren() const; public: PowFunction(Token const &token, Args const &args) : TreeNode(token, args) { verifyChildren(); } virtual Tree construct(Args const &children) const { return new PowFunction(getToken(), children); } Tree getBase() const { return getChildren()[0]; } Tree getPower() const { return getChildren()[1]; } }; // base ^ log(base, value) = value class LogFunction : public TreeNode { private: void verifyChildren() const; public: LogFunction(Token const &token, Args const &args) : TreeNode(token, args) { verifyChildren(); } virtual Tree construct(Args const &children) const { return new LogFunction(getToken(), children); } Tree getBase() const { return getChildren()[0]; } Tree getValue() const { return getChildren()[1]; } }; class Function1 : public TreeNode { public: enum Which { NONE = -1, Abs, Sin, Cos, Tan, ASin, ACos, ATan, Ceil, Floor, Exp, Ln, Sqrt }; private: const static std::map< std::string, Which > _byName; static Which getEnum(std::string const &name); const Which _which; void verifyChildren() const; Function1(Token const &token, Which which, Args const &args) : TreeNode(token, args), _which(which) { verifyChildren(); } protected: virtual int compareSameType(TreeNode const &other) const; virtual uint64_t getHashContributaion() const { return sHash((char)_which); } public: virtual std::string shortDebug() const; // If the token doesn't represent a known function, this will return // NULL. If it is a known function, and the arguments are wrong, it // will throw a parse exception. Otherwise, it will return the new // Function object. static Tree tryCreate(Token const &token, Args const &args); virtual Tree construct(Args const &children) const { return new Function1(getToken(), _which, children); } Which getWhich() const { return _which; } Tree getInput() const { return getChildren()[0]; } }; class ComparisonFunction : public TreeNode { public: enum Which { Less, Greater, Equal, LessOrEqual, GreaterOrEqual, NotEqual }; private: const static std::map< std::string, Which > _byName; const Which _which; std::string getWhichName() const; void verifyChildren() const; ComparisonFunction(Token const &token, Which which, Args const &args) : TreeNode(token, args), _which(which) { verifyChildren(); } protected: virtual int compareSameType(TreeNode const &other) const; virtual uint64_t getHashContributaion() const { return sHash((char)_which); } public: virtual std::string shortDebug() const; // If the token doesn't represent a known function, this will return // NULL. If it is a known function, and the arguments are wrong, it // will throw a parse exception. Otherwise, it will return the new // Function object. static Tree tryCreate(Token const &token, Args const &args); virtual Tree construct(Args const &children) const { return new ComparisonFunction(getToken(), _which, children); } ComparisonFunction(Which which, Tree LHS, Tree RHS) : TreeNode(Token::EMPTY, { LHS, RHS }), _which(which) { } Which getWhich() const { return _which; } Tree getLHS() const { return getChildren()[0]; } Tree getRHS() const { return getChildren()[1]; } }; // Logical and. && // Lazy and. Goes left to right and stops as soon as it knows for sure. // An EMPTY value on input will be interpreted as a false. // That's required to offer a lazy and. // That's required to match our existing code. class AndFunction : public TreeNode { private: //typedef std::unordered_set< Tree, TreeHashHelper, TreeHashHelper > TreeSet; typedef std::set< Tree, TreeCompare > TreeSet; // Returning true means we need a quick exit. Returning false means we're // still looking. static bool add(TreeSet &accumulator, Tree const &child); bool add(TreeSet &accumulator) const; public: AndFunction(Token const &token, Args const &args) : TreeNode(token, args) { } AndFunction(Args const &args) : TreeNode(Token::EMPTY, args) { } virtual Tree construct(Args const &children) const { return new AndFunction(getToken(), children); } Args const &getArgs() const { return getChildren(); } virtual Tree optimize(OptimizationContext context) const; }; // Logical or. || // Lazy or. Goes left to right and stops as soon as it knows for sure. // An EMPTY value on input will be interpreted as a false. // That's required to offer a lazy or. // That's required to match our existing code. class OrFunction : public TreeNode { private: typedef std::set< Tree, TreeCompare > TreeSet; // Returning true means we need a quick exit. Returning false means we're // still looking. static bool add(TreeSet &accumulator, Tree const &child); bool add(TreeSet &accumulator) const; public: OrFunction(Token const &token, Args const &args) : TreeNode(token, args) { } OrFunction(Args const &args) : TreeNode(Token::EMPTY, args) { } virtual Tree construct(Args const &children) const { return new OrFunction(getToken(), children); } Args const &getArgs() const { return getChildren(); } virtual Tree optimize(OptimizationContext context) const; }; class NotFunction : public TreeNode { public: NotFunction(Token const &token, Args const &args); NotFunction(Tree value) : TreeNode(Token::EMPTY, {value}) { } virtual Tree construct(Args const &children) const { return new NotFunction(getToken(), children); } Tree getValue() const { return getChildren()[0]; } }; class SecondsAfterOpen : public TreeNode { public: static Tree create(Token const &token, Args const &args); SecondsAfterOpen(Token const &token = Token::EMPTY, Tree timestamp = PrimaryField::TIMESTAMP) : TreeNode(token, { timestamp }) { } virtual Tree construct(Args const &children) const { return create(getToken(), children); } Tree getTimestamp() const { return getChildren()[0]; } }; class DayOfWeek : public TreeNode { public: static Tree create(Token const &token, Args const &args); DayOfWeek(Token const &token = Token::EMPTY, Tree timestamp = PrimaryField::TIMESTAMP) : TreeNode(token, { timestamp }) { } virtual Tree construct(Args const &children) const { return create(getToken(), children); } Tree getTimestamp() const { return getChildren()[0]; } }; class CompanyName : public TreeNode { public: static Tree create(Token const &token, Args const &args); CompanyName(Token const &token = Token::EMPTY, Tree symbol = PrimaryField::SYMBOL) : TreeNode(token, {symbol}) { } virtual Tree construct(Args const &children) const { return create(getToken(), children); } Tree getSymbol() const { return getChildren()[0]; } }; // returns time(NULL); // Interesting. We can't really optimize this in our current system. We // purposely compile the strategy once, to save time. If we re-comipled // the strategies every time we ran a query, we'd only have to compute // this once. And we could do some additional constant folding. class NowFunction : public TreeNode { public: NowFunction(Token const &token = Token::EMPTY) : TreeNode(token, {}) { } }; class CommonRules { private: static pthread_once_t _hasBeenInitialized; static CommonRules *_instance; static void initialize(); void updateStandardFilters(); FilterRules *_filterRulesPtr; ReplaceRule::Ref _filterRules; ReplaceRule::Ref _internalResources; ReplaceRule::Ref _publicResources; CommonRules(); public: // These are available for compiling standard filters, like [Price]. ReplaceRule::Ref internalResources() const { return _internalResources; } // These are available for compiling user filters, like [U21]. ReplaceRule::Ref publicResources() const { return _publicResources; } // Maps from a filter name, like "Price", to the defintion of that filter. std::map< std::string, Tree > const &standardFilters() const { return _filterRulesPtr->filters; } static CommonRules &instance(); }; // This is like the SQL "in" operator. But it only works on strings, and // the items on the right side must be constants. class ConstantStringSet : public TreeNode { public: typedef std::set< std::string > Matching; private: Matching _matching; protected: virtual int compareSameType(TreeNode const &other) const; virtual uint64_t getHashContributaion() const; public: virtual Tree construct(Args const &children) const; virtual bool isConstant() const; virtual ValueBox getConstantValue() const; virtual std::string shortDebug() const; Tree getValue() const { return getChildren()[0]; } Matching const &getMatching() const { return _matching; } ConstantStringSet(Token const &token, Tree value, Matching const &matching); // Start with an empty set and add the possible matches one at a time. ConstantStringSet(Token const &token, Tree value); void add(std::string const &match); static Tree alertType(Token const &token); }; class CachedValue : public TreeNode { private: static __thread std::set< int > *_detailsShown; const int _location; protected: virtual int compareSameType(TreeNode const &other) const; virtual uint64_t getHashContributaion() const { return sHash(_location); } public: virtual Tree construct(Args const &children) const; virtual std::string shortDebug() const; // By default, shortDebug() will show you the complete version of this // object. After you call startDebug(), each cached item will only // be displyed once. If you try to diplay it a second time, you will // see a limited version. When you call endDebug(), that reverts to // the default behavoir. When you call startDebug() again, that resets // all counters, so you will see the items the first time again. If you // call startDebug() and endDebug() out of order, they will act reasonably. static void startDebug(); static void endDebug(); int getLocation() const { return _location; } Tree getCode() const { return getChildren()[0]; } CachedValue(int location, Tree code) : TreeNode(Token::EMPTY, {code}), _location(location) { } }; // This takes care of a few things that only apply to alerts. If you // run this on a top list query, nothing with crash. But you will be // looking at fields, like alert_type, which won't exist. If you skip // this rule for top lists, then call assertDone(), you can report a // reasonable error message to the user. class AlertRules : public ReplaceRule { protected: virtual void replaceImpl(Tree &tree, Status &status) const; }; } #endif