/* KLayout Layout Viewer Copyright (C) 2006-2017 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HDR_tlExpression #define HDR_tlExpression #include "tlCommon.h" #include "tlException.h" #include "tlVariant.h" #include "tlString.h" #include #include #include namespace tl { class Eval; class EvalTarget; class Expression; class ExpressionNode; class ExpressionParserContext; /** * @brief An interface handling the evaluation context * * This object serves to provide extended context for the expressions: * * First, this object is supposed to replace angle-bracket expressions * of the kind and <> by a real value. * The handler can be configured through Eval::set_ctx_handler. * * Second, this object provides the database unit value for physical unit conversions. */ class TL_PUBLIC ContextHandler { public: /** * @brief Constructor */ ContextHandler() { } /** * @brief Destructor */ virtual ~ContextHandler() { } /** * @brief Evaluates a single-bracket expression * * This method receives the content of a single bracket and is supposed to * deliver an evaluated value. */ virtual tl::Variant eval_bracket (const std::string &content) const = 0; /** * @brief Evaluates a double-bracket expression * * This method receives the content of a double bracket and is supposed to * deliver an evaluated value. */ virtual tl::Variant eval_double_bracket (const std::string &content) const = 0; /** * @brief Provide the database unit value */ virtual double dbu () const = 0; }; /** * @brief An exception thrown by the evaluation */ class TL_PUBLIC EvalError : public tl::Exception { public: EvalError (const std::string &what, const ExpressionParserContext &context); }; /** * @brief An exception indicating that no such method exists */ class TL_PUBLIC NoMethodError : public EvalError { public: NoMethodError (const std::string &cls_name, const std::string &method, const ExpressionParserContext &context); }; /** * @brief The expression parser context */ class TL_PUBLIC ExpressionParserContext : public tl::Extractor { public: /** * @brief Default constructor */ ExpressionParserContext (); /** * @brief Constructor * * @param expr The expression to which this context refers to * @param ex The initial location of the parser */ ExpressionParserContext (const Expression *expr, const tl::Extractor &ex); /** * @brief Reimplementation of tl::Extractor's error method */ virtual void error (const std::string &message); /** * @brief Gets a string indication where we are currently */ std::string where () const; /** * @brief Sets the expression parent */ void set_expr (const Expression *expr) { mp_expr = expr; } private: const Expression *mp_expr; tl::Extractor m_ex0; }; /** * @brief A node within an expression tree */ class TL_PUBLIC ExpressionNode { public: /** * @brief Constructor */ ExpressionNode (const ExpressionParserContext &context); /** * @brief Constructor with reservation of a certain number of child nodes */ ExpressionNode (const ExpressionParserContext &context, size_t children); /** * @brief Copy ctor */ ExpressionNode (const ExpressionNode &other, const tl::Expression *expr); /** * @brief Destructor */ virtual ~ExpressionNode (); /** * @brief Add a child node */ void add_child (ExpressionNode *node); /** * @brief Execute the node */ virtual void execute (EvalTarget &out) const = 0; /** * @brief Clone the node */ virtual ExpressionNode *clone (const tl::Expression *expr) const = 0; protected: std::vector m_c; ExpressionParserContext m_context; /** * @brief Sets the expression parent */ void set_expr (const tl::Expression *expr) { m_context.set_expr (expr); } }; /** * @brief A class handler for user objects within tl::Variant * * In order to enable objects for expressions, the user object in tl::Variant must be provided with * a class derived from tl::VariantUserClassBase which implements eval_cls to return an EvalClass * implementation which executes the method. */ class TL_PUBLIC EvalClass { public: /** * @brief Constructor * * @param test_function_name The name of the function which will be created and which tests if the variant is of the given type. */ EvalClass () { } /** * @brief Destructor */ virtual ~EvalClass () { } /** * @brief Execute the method with the given name on the object * * @param node The current location in the syntax tree * @param out The return value * @param object The object on which to execute the method * @param method The name of the method * @param args The arguments of the method * * If no method of this kind exists, the implementation may throw a NoMethodError. */ virtual void execute (const ExpressionParserContext &context, tl::Variant &out, tl::Variant &object, const std::string &method, const std::vector &args) const = 0; }; /** * @brief A base class for a function */ class TL_PUBLIC EvalFunction { public: /** * @brief Constructor */ EvalFunction () { } /** * @brief Destructor */ virtual ~EvalFunction () { } /** * @brief The actual execution method * * @param ex The position inside the current expression * @param args The arguments of the method * @return The return value */ virtual void execute (const ExpressionParserContext &context, tl::Variant &out, const std::vector &args) const = 0; }; /** * @brief Represents a expression to evaluate */ class TL_PUBLIC Expression { public: /** * @brief Default constructor */ Expression (); /** * @brief Copy constructor */ Expression (const Expression &d); /** * @brief Assignment */ Expression &operator= (const Expression &d); /** * @brief Execution of the expression */ tl::Variant execute () const; /** * @brief Execution of the expression (return by reference) */ void execute (EvalTarget &v) const; /** * @brief Gets the text of the expression */ const char *text () const { return mp_text != 0 ? mp_text : m_local_text.c_str (); } /** * @brief Sets the local text of the expression */ void set_text (const std::string &s) { m_local_text = s; } /** * @brief Sets the external text of the expression */ void set_text (const char *s) { mp_text = s; } private: const char *mp_text; std::string m_local_text; std::auto_ptr m_root; Eval *mp_eval; friend class Eval; /** * @brief Private constructor for Eval */ Expression (Eval *eval, const std::string &expr); /** * @brief Private constructor for Eval */ Expression (Eval *eval, const char *expr); /** * @brief Accessor to the root node */ std::auto_ptr &root () { return m_root; } }; /** * @brief Provides the context for the expression parser and evaluation */ class TL_PUBLIC Eval { public: /** * @brief Create a new object for expression evaluation * * @param parent The parent evaluation context * @param sloppy True to enable sloppy evaluation for pure parsing */ Eval (const Eval *parent = 0, bool sloppy = false); /** * @brief virtual dtor to enable dynamic_cast on derived classes. */ virtual ~Eval (); /** * @brief Sets an angle-bracket handler * * See \ContextHandler for details. * This method will not take ownership over the object. */ void set_ctx_handler (const ContextHandler *ctx_handler) { mp_ctx_handler = ctx_handler; } /** * @brief Gets the context handler * * If no handler is set locally, the parent context is looked up for one. * If no context layout is present, 0 is returned. */ const ContextHandler *ctx_handler () const { if (mp_ctx_handler) { return mp_ctx_handler; } else if (mp_parent) { return mp_parent->ctx_handler (); } else { return 0; } } /** * @brief Define a global function for use within an expression */ static void define_global_function (const std::string &name, EvalFunction *function) { m_global.define_function (name, function); } /** * @brief Define a function for use within an expression */ void define_function (const std::string &name, EvalFunction *function); /** * @brief Define a global variable for use within an expression */ static void set_global_var (const std::string &name, const tl::Variant &var) { m_global.set_var (name, var); } /** * @brief Define a variable for use within an expression */ void set_var (const std::string &name, const tl::Variant &var); /** * @brief Parse an expression from the extractor * * @param ex The extractor from which to parse the expression * @param top If true, an expression is parsed at top level (as eval does). If false, the exression is parsed at 'atomic' level (as the string interpolation after the '$' does. * @param expr An expression that can be evaluated (out) */ void parse (Expression &expr, tl::Extractor &ex, bool top = true); /** * @brief Convenience method that returns the expression object (caution: poor performance) * * @param ex The extractor from which to parse the expression * @param top If true, an expression is parsed at top level (as eval does). If false, the exression is parsed at 'atomic' level (as the string interpolation after the '$' does. */ Expression parse (tl::Extractor &ex, bool top = true) { Expression expr; parse (expr, ex, top); return expr; } /** * @brief Parse an expression from a string * * @param s The string from which to parse the expression * @param top If true, an expression is parsed at top level (as eval does). If false, the exression is parsed at 'atomic' level (as the string interpolation after the '$' does. * @return An expression string that can be passed to eval. */ void parse (Expression &expr, const std::string &s, bool top = true); /** * @brief Convenience method that returns the expression object (caution: poor performance) * * @param s The string from which to parse the expression * @param top If true, an expression is parsed at top level (as eval does). If false, the exression is parsed at 'atomic' level (as the string interpolation after the '$' does. */ Expression parse (const std::string &s, bool top = true) { Expression expr; parse (expr, s, top); return expr; } /** * @brief Parse an expression string from the extractor * * @param ex The extractor from which to parse the expression * @param top If true, an expression is parsed at top level (as eval does). If false, the exression is parsed at 'atomic' level (as the string interpolation after the '$' does. * @return An expression string that can be used later to construct an Expression from using the parse method */ static std::string parse_expr (tl::Extractor &ex, bool top = true); /** * @brief Interpolate the string and return the result * * Interpolation will replace all expressions of the form * '$' by their string value. */ std::string interpolate (const std::string &str); /** * @brief Provide access to the match substrings */ std::vector &match_substrings () { return m_match_substrings; } /** * @brief Provide access to the match substrings (const version) */ const std::vector &match_substrings () const { return m_match_substrings; } private: friend class Expression; const Eval *mp_parent; std::map m_local_vars; std::map m_local_functions; bool m_sloppy; const ContextHandler *mp_ctx_handler; std::vector m_match_substrings; tl::Variant eval (const std::string &expr); void eval_top (ExpressionParserContext &context, std::auto_ptr &v); void eval_assign (ExpressionParserContext &context, std::auto_ptr &v); void eval_if (ExpressionParserContext &context, std::auto_ptr &v); void eval_boolean (ExpressionParserContext &context, std::auto_ptr &v); void eval_conditional (ExpressionParserContext &context, std::auto_ptr &v); void eval_shift (ExpressionParserContext &context, std::auto_ptr &v); void eval_addsub (ExpressionParserContext &context, std::auto_ptr &v); void eval_product (ExpressionParserContext &context, std::auto_ptr &v); void eval_bitwise (ExpressionParserContext &context, std::auto_ptr &v); void eval_unary (ExpressionParserContext &context, std::auto_ptr &v); void eval_atomic (ExpressionParserContext &context, std::auto_ptr &v, int am); void eval_suffix (ExpressionParserContext &context, std::auto_ptr &v); void resolve_name (const std::string &name, const EvalFunction *&function, const tl::Variant *&value) const; void resolve_var_name (const std::string &name, tl::Variant *&value); static Eval m_global; }; } #endif