Merge pull request #25198 from pieterhijma/doc-expressions
Doc: Improve the documentation of expressions
This commit is contained in:
@@ -1013,7 +1013,7 @@ ExpressionPtr Expression::importSubNames(const std::map<std::string,std::string>
|
||||
if(key.second.empty() || subNameMap.contains(key))
|
||||
continue;
|
||||
std::string imported = PropertyLinkBase::tryImportSubName(
|
||||
obj,key.second.c_str(),owner->getDocument(), nameMap);
|
||||
obj, key.second.c_str(), owner->getDocument(), nameMap);
|
||||
if(!imported.empty())
|
||||
subNameMap.emplace(std::move(key),std::move(imported));
|
||||
}
|
||||
@@ -1213,14 +1213,6 @@ void UnitExpression::setQuantity(const Quantity &_quantity)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set unit information.
|
||||
*
|
||||
* @param _unit A unit object
|
||||
* @param _unitstr The unit expressed as a string
|
||||
* @param _scaler Scale factor to convert unit into internal unit.
|
||||
*/
|
||||
|
||||
void UnitExpression::setUnit(const Quantity &_quantity)
|
||||
{
|
||||
quantity = _quantity;
|
||||
@@ -1231,33 +1223,16 @@ void UnitExpression::setUnit(const Quantity &_quantity)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify the expression. In this case, a NumberExpression is returned,
|
||||
* as it cannot be simplified any more.
|
||||
*/
|
||||
|
||||
Expression *UnitExpression::simplify() const
|
||||
{
|
||||
return new NumberExpression(owner, quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation, in this case the unit string.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Return a string representation of the expression.
|
||||
*/
|
||||
|
||||
void UnitExpression::_toString(std::ostream &ss, bool,int) const
|
||||
{
|
||||
ss << unitStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the expression.
|
||||
*/
|
||||
|
||||
Expression *UnitExpression::_copy() const
|
||||
{
|
||||
return new UnitExpression(owner, quantity, unitStr);
|
||||
@@ -1280,29 +1255,16 @@ NumberExpression::NumberExpression(const DocumentObject *_owner, const Quantity
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify the expression. For NumberExpressions, we return a copy(), as it cannot
|
||||
* be simplified any more.
|
||||
*/
|
||||
|
||||
Expression *NumberExpression::simplify() const
|
||||
{
|
||||
return copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a copy of the expression.
|
||||
*/
|
||||
|
||||
Expression *NumberExpression::_copy() const
|
||||
{
|
||||
return new NumberExpression(owner, getQuantity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate the stored value.
|
||||
*/
|
||||
|
||||
void NumberExpression::negate()
|
||||
{
|
||||
setQuantity(-getQuantity());
|
||||
@@ -1353,10 +1315,6 @@ OperatorExpression::~OperatorExpression()
|
||||
delete right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the expression is touched or not, i.e relies on properties that are touched.
|
||||
*/
|
||||
|
||||
bool OperatorExpression::isTouched() const
|
||||
{
|
||||
return left->isTouched() || right->isTouched();
|
||||
@@ -1478,14 +1436,6 @@ Py::Object OperatorExpression::_getPyValue() const {
|
||||
return calc(this,op,left,right,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify the expression. For OperatorExpressions, we return a NumberExpression if
|
||||
* both the left and right side can be simplified to NumberExpressions. In this case
|
||||
* we can calculate the final value of the expression.
|
||||
*
|
||||
* @returns Simplified expression.
|
||||
*/
|
||||
|
||||
Expression *OperatorExpression::simplify() const
|
||||
{
|
||||
Expression * v1 = left->simplify();
|
||||
@@ -1501,12 +1451,6 @@ Expression *OperatorExpression::simplify() const
|
||||
return new OperatorExpression(owner, v1, op, v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation of the expression.
|
||||
*
|
||||
* @returns A string representing the expression.
|
||||
*/
|
||||
|
||||
void OperatorExpression::_toString(std::ostream &s, bool persistent,int) const
|
||||
{
|
||||
bool needsParens;
|
||||
@@ -1608,22 +1552,11 @@ void OperatorExpression::_toString(std::ostream &s, bool persistent,int) const
|
||||
right->toString(s,persistent);
|
||||
}
|
||||
|
||||
/**
|
||||
* A deep copy of the expression.
|
||||
*/
|
||||
|
||||
Expression *OperatorExpression::_copy() const
|
||||
{
|
||||
return new OperatorExpression(owner, left->copy(), op, right->copy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the operators priority. This is used to add parentheses where
|
||||
* needed when creating a string representation of the expression.
|
||||
*
|
||||
* @returns The operator's priority.
|
||||
*/
|
||||
|
||||
int OperatorExpression::priority() const
|
||||
{
|
||||
switch (op) {
|
||||
@@ -1850,13 +1783,6 @@ FunctionExpression::~FunctionExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the expressions is considered touched, i.e one or both of its arguments
|
||||
* are touched.
|
||||
*
|
||||
* @return True if touched, false if not.
|
||||
*/
|
||||
|
||||
bool FunctionExpression::isTouched() const
|
||||
{
|
||||
std::vector<Expression*>::const_iterator i = args.begin();
|
||||
@@ -2658,12 +2584,6 @@ Py::Object FunctionExpression::_getPyValue() const {
|
||||
return evaluate(this,f,args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to simplify the expression, i.e calculate all constant expressions.
|
||||
*
|
||||
* @returns A simplified expression.
|
||||
*/
|
||||
|
||||
Expression *FunctionExpression::simplify() const
|
||||
{
|
||||
size_t numerics = 0;
|
||||
@@ -2692,12 +2612,6 @@ Expression *FunctionExpression::simplify() const
|
||||
std::move(simplifiedArgs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation of the expression.
|
||||
*
|
||||
* @returns A string representing the expression.
|
||||
*/
|
||||
|
||||
void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
|
||||
{
|
||||
switch (f) {
|
||||
@@ -2848,12 +2762,6 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
|
||||
ss << ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of the expression.
|
||||
*
|
||||
* @returns A deep copy of the expression.
|
||||
*/
|
||||
|
||||
Expression *FunctionExpression::_copy() const
|
||||
{
|
||||
std::vector<Expression*>::const_iterator i = args.begin();
|
||||
@@ -2891,30 +2799,11 @@ VariableExpression::VariableExpression(const DocumentObject *_owner, const Objec
|
||||
|
||||
VariableExpression::~VariableExpression() = default;
|
||||
|
||||
/**
|
||||
* Determine if the expression is touched or not, i.e whether the Property object it
|
||||
* refers to is touched().
|
||||
*
|
||||
* @returns True if the Property object is touched, false if not.
|
||||
*/
|
||||
|
||||
bool VariableExpression::isTouched() const
|
||||
{
|
||||
return var.isTouched();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the property this expression referse to.
|
||||
*
|
||||
* Unqualified names (i.e the name only without any dots) are resolved in the owning DocumentObjects.
|
||||
* Qualified names are looked up in the owning Document. It is first looked up by its internal name.
|
||||
* If not found, the DocumentObjects' labels searched.
|
||||
*
|
||||
* If something fails, an exception is thrown.
|
||||
*
|
||||
* @returns The Property object if it is derived from either PropertyInteger, PropertyFloat, or PropertyString.
|
||||
*/
|
||||
|
||||
const Property * VariableExpression::getProperty() const
|
||||
{
|
||||
const Property * prop = var.getProperty();
|
||||
@@ -2986,22 +2875,11 @@ void VariableExpression::_toString(std::ostream &ss, bool persistent,int) const
|
||||
ss << var.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify the expression. Simplification of VariableExpression objects is
|
||||
* not possible (if it is instantiated it would be an evaluation instead).
|
||||
*
|
||||
* @returns A copy of the expression.
|
||||
*/
|
||||
|
||||
Expression *VariableExpression::simplify() const
|
||||
{
|
||||
return copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the expression.
|
||||
*/
|
||||
|
||||
Expression *VariableExpression::_copy() const
|
||||
{
|
||||
return new VariableExpression(owner, var);
|
||||
@@ -3202,10 +3080,6 @@ StringExpression::~StringExpression() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify the expression. For strings, this is a simple copy of the object.
|
||||
*/
|
||||
|
||||
Expression *StringExpression::simplify() const
|
||||
{
|
||||
return copy();
|
||||
@@ -3216,10 +3090,6 @@ void StringExpression::_toString(std::ostream &ss, bool,int) const
|
||||
ss << quote(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the expression.
|
||||
*/
|
||||
|
||||
Expression *StringExpression::_copy() const
|
||||
{
|
||||
return new StringExpression(owner, text);
|
||||
@@ -3549,9 +3419,10 @@ bool isModuleImported(PyObject *module) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Error function for parser. Throws a generic Base::Exception with the parser error.
|
||||
* @brief Error function for parser.
|
||||
*
|
||||
* @throws Base::Exception A generic parser error.
|
||||
*/
|
||||
|
||||
void ExpressionParser_yyerror(const char *errorinfo)
|
||||
{
|
||||
(void)errorinfo;
|
||||
@@ -3587,12 +3458,24 @@ double num_change(char* yytext,char dez_delim,char grp_delim)
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static Expression * ScanResult = nullptr; /**< The resulting expression after a successful parsing */
|
||||
static const App::DocumentObject * DocumentObject = nullptr; /**< The DocumentObject that will own the expression */
|
||||
static bool unitExpression = false; /**< True if the parsed string is a unit only */
|
||||
static bool valueExpression = false; /**< True if the parsed string is a full expression */
|
||||
static std::stack<std::string> labels; /**< Label string primitive */
|
||||
static std::map<std::string, FunctionExpression::Function> registered_functions; /**< Registered functions */
|
||||
/// The resulting expression after a successful parsing.
|
||||
static Expression* ScanResult = nullptr;
|
||||
|
||||
/// The DocumentObject that will own the expression.
|
||||
static const App::DocumentObject* DocumentObject = nullptr;
|
||||
|
||||
/// Whether the parsed string is a unit only.
|
||||
static bool unitExpression = false;
|
||||
|
||||
/// Whether the parsed string is a full expression.
|
||||
static bool valueExpression = false;
|
||||
|
||||
/// Label string primitive.
|
||||
static std::stack<std::string> labels;
|
||||
|
||||
/// Registered functions during parsing.
|
||||
static std::map<std::string, FunctionExpression::Function> registered_functions;
|
||||
|
||||
static int last_column;
|
||||
static int column;
|
||||
|
||||
@@ -3889,3 +3772,4 @@ bool ExpressionParser::isTokenAUnit(const std::string & str)
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
@@ -49,41 +49,216 @@ class Document;
|
||||
|
||||
using ExpressionPtr = std::unique_ptr<Expression>;
|
||||
|
||||
AppExport bool isAnyEqual(const App::any &v1, const App::any &v2);
|
||||
|
||||
/**
|
||||
* @brief Check whether two values are equal.
|
||||
*
|
||||
* @param[in] v1 The first value.
|
||||
* @param[in] v2 The second value.
|
||||
* @return true if the values are equal, false otherwise.
|
||||
*/
|
||||
AppExport bool isAnyEqual(const App::any& v1, const App::any& v2);
|
||||
|
||||
/**
|
||||
* @brief Convert an App::any value to a Base::Quantity.
|
||||
*
|
||||
* @param[in] value The value to convert.
|
||||
* @param[in] errmsg Optional error message to use in case of failure.
|
||||
*
|
||||
* @return The converted Base::Quantity.
|
||||
* @throw Base::TypeError if the value cannot be converted to a Base::Quantity.
|
||||
*/
|
||||
AppExport Base::Quantity anyToQuantity(const App::any &value, const char *errmsg = nullptr);
|
||||
|
||||
// clang-format off
|
||||
// Map of depending objects to a map of depending property name to the full referencing object identifier
|
||||
using ExpressionDeps = std::map<App::DocumentObject*, std::map<std::string, std::vector<ObjectIdentifier> > >;
|
||||
/// Map of depending objects to a map of depending property name to the full referencing object identifier
|
||||
using ExpressionDeps = std::map<App::DocumentObject*, std::map<std::string, std::vector<ObjectIdentifier>>>;
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* @brief %Base class for expression visitors.
|
||||
* @ingroup ExpressionFramework
|
||||
*
|
||||
* This is a class that provides functionality to define visitors for
|
||||
* expressions. For a high-level overview of the %Expression framework see
|
||||
* topic @ref ExpressionFramework "Expression Framework".
|
||||
*/
|
||||
class AppExport ExpressionVisitor {
|
||||
public:
|
||||
virtual ~ExpressionVisitor() = default;
|
||||
virtual void visit(Expression &e) = 0;
|
||||
|
||||
/**
|
||||
* @brief Visit an expression.
|
||||
*
|
||||
* This is the method that is called when visiting an expression.
|
||||
*
|
||||
* @param[in,out] e The expression to visit.
|
||||
*/
|
||||
virtual void visit(Expression& e) = 0;
|
||||
|
||||
/// Called before starting modifications
|
||||
virtual void aboutToChange() {}
|
||||
|
||||
/**
|
||||
* @brief Called after a modification has been made.
|
||||
*
|
||||
* @return The number of changes made.
|
||||
*/
|
||||
virtual int changed() const { return 0;}
|
||||
|
||||
/// Reset the change counter.
|
||||
virtual void reset() {}
|
||||
|
||||
/// Get the property link if applicable.
|
||||
virtual App::PropertyLinkBase* getPropertyLink() {return nullptr;}
|
||||
|
||||
protected:
|
||||
void getIdentifiers(Expression &e, std::map<App::ObjectIdentifier, bool> &);
|
||||
bool adjustLinks(Expression &e, const std::set<App::DocumentObject*> &inList);
|
||||
bool relabeledDocument(Expression &e, const std::string &oldName, const std::string &newName);
|
||||
bool renameObjectIdentifier(Expression &e,
|
||||
const std::map<ObjectIdentifier,ObjectIdentifier> &, const ObjectIdentifier &);
|
||||
void collectReplacement(Expression &e, std::map<ObjectIdentifier,ObjectIdentifier> &,
|
||||
const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const;
|
||||
bool updateElementReference(Expression &e, App::DocumentObject *feature,bool reverse);
|
||||
void importSubNames(Expression &e, const ObjectIdentifier::SubNameMap &subNameMap);
|
||||
void updateLabelReference(Expression &e, App::DocumentObject *obj,
|
||||
const std::string &ref, const char *newLabel);
|
||||
void moveCells(Expression &e, const CellAddress &address, int rowCount, int colCount);
|
||||
/**
|
||||
* @brief Get the identifiers used in an expression.
|
||||
*
|
||||
* @param[in] e The expression.
|
||||
* @param[in, out] ids The map to fill with identifiers.
|
||||
*/
|
||||
void getIdentifiers(Expression& e, std::map<App::ObjectIdentifier, bool>& ids);
|
||||
|
||||
/**
|
||||
* @brief Adjust the links in an expression.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] inList The set of document objects to adjust links for.
|
||||
* @return true if any links were adjusted, false otherwise.
|
||||
*/
|
||||
bool adjustLinks(Expression& e, const std::set<App::DocumentObject*>& inList);
|
||||
|
||||
/**
|
||||
* @brief Update label references in an expression.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] oldName The old document name.
|
||||
* @param[in] newName The new document name.
|
||||
* @return true if any label references were updated, false otherwise.
|
||||
*/
|
||||
bool relabeledDocument(Expression& e, const std::string& oldName, const std::string& newName);
|
||||
|
||||
/**
|
||||
* @brief Rename object identifiers in an expression.
|
||||
*
|
||||
* Object identifiers are renamed according to the provided map of old to
|
||||
* new object identifiers. The new identifiers are made relative to @p
|
||||
* path.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] paths The map of old to new object identifiers.
|
||||
* @param[in] path The object identifier path context.
|
||||
*
|
||||
* @return True if any object identifiers were renamed, false otherwise.
|
||||
*/
|
||||
bool renameObjectIdentifier(Expression& e,
|
||||
const std::map<ObjectIdentifier, ObjectIdentifier>& paths,
|
||||
const ObjectIdentifier& path);
|
||||
|
||||
/**
|
||||
* @brief Collect replacements in an expression.
|
||||
*
|
||||
* This function runs before the @ref renameObjectIdentifier function and
|
||||
* collects all occurrences of a specific old document object
|
||||
* and maps them to a new document object in the provided map.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in,out] paths The map to fill with old to new object identifier replacements.
|
||||
* @param[in] parent The parent document object.
|
||||
* @param[in] oldObj The old document object to be replaced.
|
||||
* @param[in] newObj The new document object to replace with.
|
||||
*/
|
||||
void collectReplacement(Expression& e,
|
||||
std::map<ObjectIdentifier, ObjectIdentifier>& paths,
|
||||
const App::DocumentObject* parent,
|
||||
App::DocumentObject* oldObj,
|
||||
App::DocumentObject* newObj) const;
|
||||
|
||||
/**
|
||||
* @brief Update element references in an expression.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] feature The document object feature for which the update
|
||||
* element references should be updated.
|
||||
* @param[in] reverse If true, use the old style, i.e. non-mapped element
|
||||
* reference to query for the new style, i.e. mapped element reference when
|
||||
* update. If false, then the other way around.
|
||||
*
|
||||
* @return true if any element references were updated, false otherwise.
|
||||
*/
|
||||
bool updateElementReference(Expression& e, App::DocumentObject* feature, bool reverse);
|
||||
|
||||
/**
|
||||
* @brief Rewrite sub-names in an expression after importing external
|
||||
* objects.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] subNameMap The map of sub-name replacements.
|
||||
*/
|
||||
void importSubNames(Expression& e, const ObjectIdentifier::SubNameMap& subNameMap);
|
||||
|
||||
/**
|
||||
* @brief Update label references in an expression.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] obj The document object whose label has changed.
|
||||
* @param[in] ref The old label reference.
|
||||
* @param[in] newLabel The new label.
|
||||
*/
|
||||
void updateLabelReference(Expression& e,
|
||||
App::DocumentObject* obj,
|
||||
const std::string& ref,
|
||||
const char* newLabel);
|
||||
|
||||
/**
|
||||
* @brief Move cell references in an expression.
|
||||
*
|
||||
* This function visits the expression and updates all cell references to
|
||||
* @p address. These cell references are then updated by moving them by @p
|
||||
* rowCount rows and @p colCount columns.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] address The cell address that needs to be updated.
|
||||
* @param[in] rowCount The number of rows to move.
|
||||
* @param[in] colCount The number of columns to move.
|
||||
*/
|
||||
void moveCells(Expression& e, const CellAddress& address, int rowCount, int colCount);
|
||||
|
||||
/**
|
||||
* @brief Offset cell references in an expression.
|
||||
*
|
||||
* This function visits the expression and in a range expression, offsets
|
||||
* all cell relative references by @p rowOffset rows and @p colOffset
|
||||
* columns.
|
||||
*
|
||||
* @param[in,out] e The expression.
|
||||
* @param[in] rowOffset The number of rows to offset.
|
||||
* @param[in] colOffset The number of columns to offset.
|
||||
*/
|
||||
void offsetCells(Expression &e, int rowOffset, int colOffset);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for expression visitors that can modify expressions.
|
||||
* @ingroup ExpressionFramework
|
||||
*
|
||||
* With respect to the base class @ref ExpressionVisitor, this class adds
|
||||
* functionality to modify the visited @ref Expression objects and signal on
|
||||
* changes. It also keeps track of the number of modifications made to the
|
||||
* visited expressions. For a high-level overview of the %Expression framework
|
||||
* see topic @ref ExpressionFramework "Expression Framework".
|
||||
*
|
||||
* @tparam P The property type being modified.
|
||||
*/
|
||||
template<class P> class ExpressionModifier : public ExpressionVisitor {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ExpressionModifier object.
|
||||
*
|
||||
* @param[in,out] _prop The property being modified.
|
||||
*/
|
||||
explicit ExpressionModifier(P & _prop)
|
||||
: prop(_prop)
|
||||
, propLink(freecad_cast<App::PropertyLinkBase*>(&prop))
|
||||
@@ -104,9 +279,16 @@ public:
|
||||
App::PropertyLinkBase* getPropertyLink() override {return propLink;}
|
||||
|
||||
protected:
|
||||
P & prop;
|
||||
App::PropertyLinkBase *propLink;
|
||||
/// The property being modified.
|
||||
P& prop;
|
||||
|
||||
/// The property link if applicable.
|
||||
App::PropertyLinkBase* propLink;
|
||||
|
||||
/// The atomic property change signaller.
|
||||
typename AtomicPropertyChangeInterface<P>::AtomicPropertyChange signaller;
|
||||
|
||||
/// The number of changes made.
|
||||
int _changed{0};
|
||||
};
|
||||
|
||||
@@ -122,74 +304,240 @@ class AppExport Expression : public Base::BaseClass {
|
||||
TYPESYSTEM_HEADER_WITH_OVERRIDE();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new Expression object.
|
||||
*
|
||||
* @param[in] _owner The document object that owns this expression.
|
||||
*/
|
||||
explicit Expression(const App::DocumentObject * _owner);
|
||||
|
||||
~Expression() override;
|
||||
|
||||
/**
|
||||
* @brief Check if the expression is touched.
|
||||
*
|
||||
* An expression is touched if one of the properties it references is
|
||||
* touched.
|
||||
*
|
||||
* @return true if the expression is touched, false otherwise.
|
||||
*/
|
||||
virtual bool isTouched() const { return false; }
|
||||
|
||||
Expression * eval() const;
|
||||
/**
|
||||
* @brief Evaluate the expression.
|
||||
*
|
||||
* Evaluating an expression returns another expression that represents a
|
||||
* value. Contrast this with the @ref simplify function that returns a
|
||||
* possibly simpler version of the same expression.
|
||||
*
|
||||
* @return The evaluated expression.
|
||||
*/
|
||||
Expression* eval() const;
|
||||
|
||||
std::string toString(bool persistent=false, bool checkPriority=false, int indent=0) const;
|
||||
/**
|
||||
* @brief Convert the expression to a string.
|
||||
*
|
||||
* @param[in] persistent If true, the string representation is persistent
|
||||
* and can be saved to a file.
|
||||
* @param[in] checkPriority If true, check whether the expression requires
|
||||
* parentheses based on operator priority.
|
||||
* @param[in] indent The indentation level for pretty-printing.
|
||||
*
|
||||
* @return The string representation of the expression.
|
||||
*/
|
||||
std::string toString(bool persistent = false, bool checkPriority = false, int indent = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Write a string representation of the expression to a stream.
|
||||
*
|
||||
* @param[in,out] os The output stream to write to.
|
||||
* @copydoc Expression::toString(bool, bool, int) const
|
||||
*/
|
||||
void toString(std::ostream &os, bool persistent=false, bool checkPriority=false, int indent=0) const;
|
||||
|
||||
static Expression * parse(const App::DocumentObject * owner, const std::string& buffer);
|
||||
/**
|
||||
* @brief Parse an expression from a string.
|
||||
*
|
||||
* @param[in] owner The document object that will own the parsed expression.
|
||||
* @param[in] buffer The string to parse.
|
||||
*
|
||||
* @return The parsed expression.
|
||||
*/
|
||||
static Expression* parse(const App::DocumentObject * owner, const std::string& buffer);
|
||||
|
||||
/// Copy an expression.
|
||||
Expression * copy() const;
|
||||
|
||||
/**
|
||||
* @brief Get the operator priority.
|
||||
*
|
||||
* This is used to determine whether parentheses are needed when
|
||||
* converting the expression to a string.
|
||||
*
|
||||
* @return The operator priority.
|
||||
*/
|
||||
virtual int priority() const;
|
||||
|
||||
void getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const;
|
||||
/**
|
||||
* @brief Get the identifiers in the expression.
|
||||
*
|
||||
* @param[in,out] deps The mapping from object identifiers to a boolean
|
||||
* indicating whether the dependency is hidden (`href` references).
|
||||
*/
|
||||
void getIdentifiers(std::map<App::ObjectIdentifier, bool>& deps) const;
|
||||
|
||||
/**
|
||||
* @brief Get the identifiers in the expression.
|
||||
*
|
||||
* @return The mapping from object identifiers to a boolean indicating
|
||||
* whether the dependency is hidden (`href` references).
|
||||
*/
|
||||
std::map<App::ObjectIdentifier,bool> getIdentifiers() const;
|
||||
|
||||
enum DepOption {
|
||||
DepNormal,
|
||||
DepHidden,
|
||||
DepAll,
|
||||
/// Options for obtaining the dependencies of an expression.
|
||||
enum DepOption
|
||||
{
|
||||
DepNormal, ///< Get normal dependencies.
|
||||
DepHidden, ///< Get the hidden dependencies (`href` references).
|
||||
DepAll, ///< Get both normal and hidden dependencies.
|
||||
};
|
||||
void getDeps(ExpressionDeps &deps, int option=DepNormal) const;
|
||||
|
||||
/**
|
||||
* @brief Get the dependencies of the expression.
|
||||
*
|
||||
* @param[in,out] deps The dependencies to fill.
|
||||
* @param[in] option The dependency option.
|
||||
*/
|
||||
void getDeps(ExpressionDeps& deps, int option = DepNormal) const;
|
||||
|
||||
/**
|
||||
* @brief Get the dependencies of the expression.
|
||||
*
|
||||
* @param[in] option The dependency option.
|
||||
* @return The dependencies.
|
||||
*/
|
||||
ExpressionDeps getDeps(int option=DepNormal) const;
|
||||
|
||||
std::map<App::DocumentObject*,bool> getDepObjects(std::vector<std::string> *labels=nullptr) const;
|
||||
void getDepObjects(std::map<App::DocumentObject*,bool> &, std::vector<std::string> *labels=nullptr) const;
|
||||
/**
|
||||
* @brief Get the dependent document objects of the expression.
|
||||
*
|
||||
* @param[in,out] labels Optional vector to fill with labels of dependent objects.
|
||||
*
|
||||
* @return Map of dependent document objects to a boolean indicating whether
|
||||
* they are hidden (`href` references).
|
||||
*/
|
||||
std::map<App::DocumentObject*, bool> getDepObjects(std::vector<std::string>* labels = nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief Get the dependent document objects of the expression.
|
||||
*
|
||||
* @param[in,out] deps The map to fill with dependent document objects to a
|
||||
* boolean indicating whether they are hidden (`href` references).
|
||||
* @param[in,out] labels Optional vector to fill with labels of dependent objects.
|
||||
*/
|
||||
void getDepObjects(std::map<App::DocumentObject*,bool>& deps, std::vector<std::string> *labels=nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief Import sub-names in the expression after importing external objects.
|
||||
*
|
||||
* @param[in] nameMap The map of sub-name replacements.
|
||||
*
|
||||
* @return The updated expression.
|
||||
*/
|
||||
ExpressionPtr importSubNames(const std::map<std::string,std::string> &nameMap) const;
|
||||
|
||||
/**
|
||||
* @brief Update label references in the expression.
|
||||
*
|
||||
* @param[in] obj The document object whose label has changed.
|
||||
* @param[in] ref The old label reference.
|
||||
* @param[in] newLabel The new label.
|
||||
*
|
||||
* @return The updated expression.
|
||||
*/
|
||||
ExpressionPtr updateLabelReference(App::DocumentObject *obj,
|
||||
const std::string &ref, const char *newLabel) const;
|
||||
|
||||
/**
|
||||
* @brief Replace a document object in the expression.
|
||||
*
|
||||
* @param[in] parent The parent document object.
|
||||
* @param[in] oldObj The old document object to be replaced.
|
||||
* @param[in] newObj The new document object to replace with.
|
||||
*
|
||||
* @return The updated expression.
|
||||
*/
|
||||
ExpressionPtr replaceObject(const App::DocumentObject *parent,
|
||||
App::DocumentObject *oldObj, App::DocumentObject *newObj) const;
|
||||
|
||||
/**
|
||||
* @brief Adjust links according to a new inList.
|
||||
*
|
||||
* @param[in] inList The new inList to adjust links for.
|
||||
* @return true if any links were adjusted, false otherwise.
|
||||
*/
|
||||
bool adjustLinks(const std::set<App::DocumentObject*> &inList);
|
||||
|
||||
/**
|
||||
* @brief Simplify the expression.
|
||||
*
|
||||
* In contrast to @ref eval, which evaluates the expression to a value,
|
||||
* this function simplifies the expression by computing all constant
|
||||
* expressions.
|
||||
*
|
||||
* @return The simplified expression.
|
||||
*/
|
||||
virtual Expression * simplify() const = 0;
|
||||
|
||||
/// Visit the expression with a visitor.
|
||||
void visit(ExpressionVisitor & v);
|
||||
|
||||
/// Exception class for expression errors.
|
||||
class Exception : public Base::Exception {
|
||||
public:
|
||||
explicit Exception(const char *sMessage) : Base::Exception(sMessage) { }
|
||||
};
|
||||
|
||||
/// Get the owner of the expression.
|
||||
App::DocumentObject * getOwner() const { return owner; }
|
||||
|
||||
struct Component;
|
||||
|
||||
/// Add a component to the expression.
|
||||
virtual void addComponent(Component* component);
|
||||
|
||||
using ComponentList = std::vector<Component*>;
|
||||
|
||||
static Component *createComponent(const std::string &n);
|
||||
/// Create a component from a name.
|
||||
static Component* createComponent(const std::string& n);
|
||||
|
||||
/**
|
||||
* @brief Create an index or range component.
|
||||
*
|
||||
* @param[in] e1 Either the index in an array index or the start of a
|
||||
* range.
|
||||
*
|
||||
* @param[in] e2 The end of a range, may be `nullptr`.
|
||||
* @param[in] e3 The step of a range, may be `nullptr`.
|
||||
*/
|
||||
static Component *createComponent(Expression *e1, Expression *e2=nullptr,
|
||||
Expression *e3=nullptr, bool isRange=false);
|
||||
|
||||
/// Check if the expression has a component.
|
||||
bool hasComponent() const {return !components.empty();}
|
||||
|
||||
/// Get the value as boost::any.
|
||||
boost::any getValueAsAny() const;
|
||||
|
||||
/// Get the value as a Python object.
|
||||
Py::Object getPyValue() const;
|
||||
|
||||
/**
|
||||
* @brief Check if this expression is the same as another.
|
||||
*
|
||||
* @param[in] other The other expression to compare with.
|
||||
* @param[in] checkComment If true, also check the comment.
|
||||
*/
|
||||
bool isSame(const Expression &other, bool checkComment=true) const;
|
||||
|
||||
friend class ExpressionVisitor;
|
||||
@@ -220,8 +568,11 @@ protected:
|
||||
|
||||
protected:
|
||||
// clang-format off
|
||||
App::DocumentObject * owner; /**< The document object used to access unqualified variables (i.e local scope) */
|
||||
|
||||
/// The document object used to access unqualified variables (i.e local scope).
|
||||
App::DocumentObject * owner;
|
||||
|
||||
/// The list of components.
|
||||
ComponentList components;
|
||||
|
||||
public:
|
||||
|
||||
@@ -42,6 +42,9 @@ namespace App
|
||||
// included by everyone
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief %Part of an expression that represents an index or range.
|
||||
*/
|
||||
struct AppExport Expression::Component
|
||||
{
|
||||
ObjectIdentifier::Component comp;
|
||||
@@ -143,6 +146,9 @@ public:
|
||||
|
||||
Expression* simplify() const override;
|
||||
|
||||
/**
|
||||
* @brief Negate the stored value.
|
||||
*/
|
||||
void negate();
|
||||
|
||||
bool isInteger(long* v = nullptr) const;
|
||||
@@ -473,6 +479,19 @@ public:
|
||||
|
||||
void setPath(const ObjectIdentifier& path);
|
||||
|
||||
/**
|
||||
* @brief Find the property this expression referse to.
|
||||
*
|
||||
* Unqualified names (i.e the name only without any dots) are resolved in
|
||||
* the owning DocumentObjects. Qualified names are looked up in the owning
|
||||
* Document, first, by its internal name, then if not found, by the
|
||||
* DocumentObjects' labels.
|
||||
*
|
||||
* @return The Property object if it is derived from either
|
||||
* PropertyInteger, PropertyFloat, or PropertyString.
|
||||
*
|
||||
* @trhows Expression::Exception If the property cannot be resolved.
|
||||
*/
|
||||
const App::Property* getProperty() const;
|
||||
|
||||
void addComponent(Component* component) override;
|
||||
|
||||
@@ -139,7 +139,7 @@ DocumentObject* GeoFeature::resolveElement(const DocumentObject* obj,
|
||||
bool append,
|
||||
ElementNameType type,
|
||||
const DocumentObject* filter,
|
||||
const char** _element,
|
||||
const char** element,
|
||||
GeoFeature** geoFeature)
|
||||
{
|
||||
elementName.newName.clear();
|
||||
@@ -150,11 +150,11 @@ DocumentObject* GeoFeature::resolveElement(const DocumentObject* obj,
|
||||
if (!subname) {
|
||||
subname = "";
|
||||
}
|
||||
const char* element = Data::findElementName(subname);
|
||||
if (_element) {
|
||||
*_element = element;
|
||||
const char* foundElement = Data::findElementName(subname);
|
||||
if (element) {
|
||||
*element = foundElement;
|
||||
}
|
||||
auto sobj = obj->getSubObject(std::string(subname, element).c_str());
|
||||
auto sobj = obj->getSubObject(std::string(subname, foundElement).c_str());
|
||||
if (!sobj) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -172,16 +172,16 @@ DocumentObject* GeoFeature::resolveElement(const DocumentObject* obj,
|
||||
if (filter && geo != filter) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!element || !element[0]) {
|
||||
if (!foundElement || !foundElement[0]) {
|
||||
if (append) {
|
||||
elementName.oldName = Data::oldElementName(subname);
|
||||
}
|
||||
return sobj;
|
||||
}
|
||||
|
||||
if (!geo || hasHiddenMarker(element)) {
|
||||
if (!geo || hasHiddenMarker(foundElement)) {
|
||||
if (!append) {
|
||||
elementName.oldName = element;
|
||||
elementName.oldName = foundElement;
|
||||
}
|
||||
else {
|
||||
elementName.oldName = Data::oldElementName(subname);
|
||||
@@ -189,11 +189,11 @@ DocumentObject* GeoFeature::resolveElement(const DocumentObject* obj,
|
||||
return sobj;
|
||||
}
|
||||
if (!append) {
|
||||
elementName = geo->getElementName(element, type);
|
||||
elementName = geo->getElementName(foundElement, type);
|
||||
}
|
||||
else {
|
||||
const auto& names = geo->getElementName(element, type);
|
||||
std::string prefix(subname, element - subname);
|
||||
const auto& names = geo->getElementName(foundElement, type);
|
||||
std::string prefix(subname, foundElement - subname);
|
||||
if (!names.newName.empty()) {
|
||||
elementName.newName = prefix + names.newName;
|
||||
}
|
||||
|
||||
@@ -93,19 +93,23 @@ public:
|
||||
const char* name,
|
||||
ElementNameType type = Normal) const;
|
||||
|
||||
/** Resolve both the new and old style element name
|
||||
/**
|
||||
* @brief Resolve both the new and old style element name.
|
||||
*
|
||||
* @param obj: top parent object
|
||||
* @param subname: subname reference
|
||||
* @param elementName: output of a pair(newElementName,oldElementName)
|
||||
* @param append: Whether to include subname prefix into the returned
|
||||
* element name
|
||||
* @param type: the type of element name to request
|
||||
* @param filter: If none zero, then only perform lookup when the element
|
||||
* owner object is the same as this filter
|
||||
* @param element: return the start of element name in subname
|
||||
* @param[in] obj The top parent object
|
||||
* @param[in] subname The subname reference.
|
||||
* @param[out] elementName Output of a pair(newElementName,oldElementName)
|
||||
* @param[in] append: Whether to include the subname prefix into the
|
||||
* returned element name.
|
||||
* @param[in] type The type of element name to request.
|
||||
* @param[in] filter If not `nullptr`, then only perform lookup when the
|
||||
* element owner object is the same as this filter.
|
||||
* @param[out] element If not `nullptr`, provide start of element name in
|
||||
* subname.
|
||||
* @param[out] geoFeature If not `nullptr`, provide the GeoFeature that
|
||||
* contains the element.
|
||||
*
|
||||
* @return Return the owner object of the element
|
||||
* @return Return the owner object of the element.
|
||||
*/
|
||||
static DocumentObject* resolveElement(const App::DocumentObject* obj,
|
||||
const char* subname,
|
||||
@@ -114,7 +118,7 @@ public:
|
||||
ElementNameType type = Normal,
|
||||
const DocumentObject* filter = nullptr,
|
||||
const char** element = nullptr,
|
||||
GeoFeature** geo = nullptr);
|
||||
GeoFeature** geoFeature = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Deprecated. Calculates the placement in the global reference coordinate system
|
||||
|
||||
@@ -99,28 +99,13 @@ struct PropertyExpressionEngine::Private
|
||||
|
||||
TYPESYSTEM_SOURCE(App::PropertyExpressionEngine, App::PropertyExpressionContainer)
|
||||
|
||||
/**
|
||||
* @brief Construct a new PropertyExpressionEngine object.
|
||||
*/
|
||||
|
||||
PropertyExpressionEngine::PropertyExpressionEngine()
|
||||
: validator(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Destroy the PropertyExpressionEngine object.
|
||||
*/
|
||||
|
||||
PropertyExpressionEngine::~PropertyExpressionEngine() = default;
|
||||
|
||||
/**
|
||||
* @brief Estimate memory size of this property.
|
||||
*
|
||||
* \fixme Should probably return something else than 0.
|
||||
*
|
||||
* @return Size of object.
|
||||
*/
|
||||
|
||||
// fixme Should probably return something else than 0.
|
||||
unsigned int PropertyExpressionEngine::getMemSize() const
|
||||
{
|
||||
return 0;
|
||||
@@ -360,15 +345,6 @@ void PropertyExpressionEngine::Restore(Base::XMLReader& reader)
|
||||
reader.readEndElement("ExpressionEngine");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update graph structure with given path and expression.
|
||||
* @param path Path
|
||||
* @param expression Expression to query for dependencies
|
||||
* @param nodes Map with nodes of graph, including dependencies of 'expression'
|
||||
* @param revNodes Reverse map of the nodes, containing only the given paths, without dependencies.
|
||||
* @param edges Edges in graph
|
||||
*/
|
||||
|
||||
void PropertyExpressionEngine::buildGraphStructures(
|
||||
const ObjectIdentifier& path,
|
||||
const std::shared_ptr<Expression> expression,
|
||||
@@ -410,12 +386,6 @@ void PropertyExpressionEngine::buildGraphStructures(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a canonical object identifier of the given object \a p.
|
||||
* @param p ObjectIndentifier
|
||||
* @return New ObjectIdentifier
|
||||
*/
|
||||
|
||||
ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier& oid) const
|
||||
{
|
||||
DocumentObject* docObj = freecad_cast<DocumentObject*>(getContainer());
|
||||
@@ -446,11 +416,6 @@ ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier&
|
||||
return oid.canonicalPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Number of expressions managed by this object.
|
||||
* @return Number of expressions.
|
||||
*/
|
||||
|
||||
size_t PropertyExpressionEngine::numExpressions() const
|
||||
{
|
||||
return expressions.size();
|
||||
@@ -510,12 +475,6 @@ void PropertyExpressionEngine::onContainerRestored()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get expression for \a path.
|
||||
* @param path ObjectIndentifier to query for.
|
||||
* @return Expression for \a path, or empty boost::any if not found.
|
||||
*/
|
||||
|
||||
const boost::any PropertyExpressionEngine::getPathValue(const App::ObjectIdentifier& path) const
|
||||
{
|
||||
// Get a canonical path
|
||||
@@ -529,13 +488,6 @@ const boost::any PropertyExpressionEngine::getPathValue(const App::ObjectIdentif
|
||||
return boost::any();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set expression with optional comment for \a path.
|
||||
* @param path Path to update
|
||||
* @param expr New expression
|
||||
* @param comment Optional comment.
|
||||
*/
|
||||
|
||||
void PropertyExpressionEngine::setValue(const ObjectIdentifier& path,
|
||||
std::shared_ptr<Expression> expr)
|
||||
{
|
||||
@@ -573,11 +525,9 @@ void PropertyExpressionEngine::setValue(const ObjectIdentifier& path,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The cycle_detector struct is used by the boost graph routines to detect cycles in the
|
||||
* graph.
|
||||
*/
|
||||
|
||||
/* The cycle_detector struct is used by the boost graph routines to detect
|
||||
* cycles in the graph. */
|
||||
struct cycle_detector: public boost::dfs_visitor<>
|
||||
{
|
||||
cycle_detector(bool& has_cycle, int& src)
|
||||
@@ -597,14 +547,6 @@ protected:
|
||||
int& _src;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Build a graph of all expressions in \a exprs.
|
||||
* @param exprs Expressions to use in graph
|
||||
* @param revNodes Map from int[nodeid] to ObjectIndentifer.
|
||||
* @param g Graph to update. May contain additional nodes than in revNodes, because of outside
|
||||
* dependencies.
|
||||
*/
|
||||
|
||||
void PropertyExpressionEngine::buildGraph(const ExpressionMap& exprs,
|
||||
boost::unordered_map<int, ObjectIdentifier>& revNodes,
|
||||
DiGraph& g,
|
||||
@@ -656,12 +598,6 @@ void PropertyExpressionEngine::buildGraph(const ExpressionMap& exprs,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The code below builds a graph for all expressions in the engine, and
|
||||
* finds any circular dependencies. It also computes the internal evaluation
|
||||
* order, in case properties depends on each other.
|
||||
*/
|
||||
|
||||
std::vector<App::ObjectIdentifier>
|
||||
PropertyExpressionEngine::computeEvaluationOrder(ExecuteOption option)
|
||||
{
|
||||
@@ -686,11 +622,6 @@ PropertyExpressionEngine::computeEvaluationOrder(ExecuteOption option)
|
||||
return evaluationOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute and update values of all registered expressions.
|
||||
* @return StdReturn on success.
|
||||
*/
|
||||
|
||||
DocumentObjectExecReturn* App::PropertyExpressionEngine::execute(ExecuteOption option,
|
||||
bool* touched)
|
||||
{
|
||||
@@ -823,12 +754,6 @@ DocumentObjectExecReturn* App::PropertyExpressionEngine::execute(ExecuteOption o
|
||||
return DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find paths to document object.
|
||||
* @param obj Document object
|
||||
* @param paths Object identifier
|
||||
*/
|
||||
|
||||
void PropertyExpressionEngine::getPathsToDocumentObject(
|
||||
DocumentObject* obj,
|
||||
std::vector<App::ObjectIdentifier>& paths) const
|
||||
@@ -854,11 +779,6 @@ void PropertyExpressionEngine::getPathsToDocumentObject(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine whether any dependencies of any of the registered expressions have been touched.
|
||||
* @return True if at least on dependency has been touched.
|
||||
*/
|
||||
|
||||
bool PropertyExpressionEngine::depsAreTouched() const
|
||||
{
|
||||
for (auto& v : _Deps) {
|
||||
@@ -870,13 +790,6 @@ bool PropertyExpressionEngine::depsAreTouched() const
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate the given path and expression.
|
||||
* @param path Object Identifier for expression.
|
||||
* @param expr Expression tree.
|
||||
* @return Empty string on success, error message on failure.
|
||||
*/
|
||||
|
||||
std::string
|
||||
PropertyExpressionEngine::validateExpression(const ObjectIdentifier& path,
|
||||
std::shared_ptr<const Expression> expr) const
|
||||
@@ -928,11 +841,6 @@ PropertyExpressionEngine::validateExpression(const ObjectIdentifier& path,
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rename paths based on \a paths.
|
||||
* @param paths Map with current and new object identifier.
|
||||
*/
|
||||
|
||||
void PropertyExpressionEngine::renameExpressions(
|
||||
const std::map<ObjectIdentifier, ObjectIdentifier>& paths)
|
||||
{
|
||||
@@ -965,11 +873,6 @@ void PropertyExpressionEngine::renameExpressions(
|
||||
hasSetValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rename object identifiers in the registered expressions.
|
||||
* @param paths Map with current and new object identifiers.
|
||||
*/
|
||||
|
||||
void PropertyExpressionEngine::renameObjectIdentifiers(
|
||||
const std::map<ObjectIdentifier, ObjectIdentifier>& paths)
|
||||
{
|
||||
|
||||
@@ -61,11 +61,46 @@ public:
|
||||
PropertyExpressionContainer();
|
||||
~PropertyExpressionContainer() override;
|
||||
|
||||
/**
|
||||
* @brief Get the expressions map.
|
||||
*
|
||||
* This function returns a mapping from object identifier to expression.
|
||||
* The object identifier specifies the property in the document object that
|
||||
* the expression is bound to.
|
||||
*
|
||||
* @return The map of ObjectIdentifier to Expression pointers.
|
||||
*/
|
||||
virtual std::map<App::ObjectIdentifier, const App::Expression*> getExpressions() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the expressions map.
|
||||
*
|
||||
* This function sets the mapping from object identifier to expression.
|
||||
* The object identifier specifies the property in the document object that
|
||||
* the expression is bound to.
|
||||
*
|
||||
* @param[in] exprs The new map of ObjectIdentifier to Expression pointers.
|
||||
*/
|
||||
virtual void setExpressions(std::map<App::ObjectIdentifier, App::ExpressionPtr>&& exprs) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Handle document relabeling.
|
||||
*
|
||||
* Update the expressions in response to a document being relabeled.
|
||||
*
|
||||
* @param[in] doc The document that was relabeled.
|
||||
*/
|
||||
virtual void onRelabeledDocument(const App::Document& doc) = 0;
|
||||
|
||||
/**
|
||||
* @brief Handle dynamic property renaming.
|
||||
*
|
||||
* Update the expressions in response to a dynamic property being renamed.
|
||||
*
|
||||
* @param[in] prop The property that was renamed.
|
||||
* @param[in] oldName The old name of the property.
|
||||
*/
|
||||
virtual void onRenameDynamicProperty(const App::Property& prop, const char* oldName) = 0;
|
||||
|
||||
private:
|
||||
@@ -73,6 +108,18 @@ private:
|
||||
static void slotRenameDynamicProperty(const App::Property& prop, const char* oldName);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The class that manages expressions that target a property in a
|
||||
* document object.
|
||||
* @ingroup ExpressionFramework
|
||||
*
|
||||
* This class manages a set of expressions that are bound to properties in
|
||||
* document objects. It provides functionality to evaluate the expressions,
|
||||
* handle dependencies between expressions, and update the properties they
|
||||
* are bound to. For a high-level overview of the %Expression framework see
|
||||
* topic @ref ExpressionFramework "Expression Framework".
|
||||
*/
|
||||
class AppExport PropertyExpressionEngine
|
||||
: public App::PropertyExpressionContainer,
|
||||
private App::AtomicPropertyChangeInterface<PropertyExpressionEngine>
|
||||
@@ -98,7 +145,7 @@ public:
|
||||
std::shared_ptr<const App::Expression> expr)>;
|
||||
|
||||
/**
|
||||
* @brief The ExpressionInfo struct encapsulates an expression.
|
||||
* @brief This struct encapsulates an expression.
|
||||
*/
|
||||
struct ExpressionInfo
|
||||
{
|
||||
@@ -127,8 +174,8 @@ public:
|
||||
void onRelabeledDocument(const App::Document& doc) override;
|
||||
void onRenameDynamicProperty(const App::Property& prop, const char* oldName) override;
|
||||
|
||||
void setValue()
|
||||
{} // Dummy
|
||||
/// Dummy setValue to satisfy a macro.
|
||||
void setValue() {}
|
||||
|
||||
Property* Copy() const override;
|
||||
|
||||
@@ -138,8 +185,30 @@ public:
|
||||
|
||||
void Restore(Base::XMLReader& reader) override;
|
||||
|
||||
/**
|
||||
* @brief Set a given expression to @a path.
|
||||
*
|
||||
* Note that the "value" in this context is an expression. This means that
|
||||
* this function does not evaluate the expression and updates the property
|
||||
* that @a path points to. It merely registers the expression to be used
|
||||
* when evaluating the property later.
|
||||
*
|
||||
* @param[in] path The path that the expression is targeting.
|
||||
* @param[in] expr The new expression.
|
||||
*/
|
||||
void setValue(const App::ObjectIdentifier& path, std::shared_ptr<App::Expression> expr);
|
||||
|
||||
/**
|
||||
* @brief Get the expression for @a path.
|
||||
*
|
||||
* Note that the "value" in this context is an expression. This means that
|
||||
* this function does not return the evaluated value of the property that
|
||||
* @a path points to. It merely returns the registered expression.
|
||||
*
|
||||
* @param[in] path ObjectIndentifier to query for.
|
||||
*
|
||||
* @return The expression for @a path, or empty boost::any if not found.
|
||||
*/
|
||||
const boost::any getPathValue(const App::ObjectIdentifier& path) const override;
|
||||
|
||||
/// Execute options
|
||||
@@ -154,32 +223,86 @@ public:
|
||||
/// Execute on document restore
|
||||
ExecuteOnRestore,
|
||||
};
|
||||
/** Evaluate the expressions
|
||||
|
||||
/**
|
||||
* @brief Evaluate the expressions.
|
||||
*
|
||||
* @param option: execution option, see ExecuteOption.
|
||||
* Evaluate the expressions and update the properties they are bound to.
|
||||
*
|
||||
* @param[in] option: execution option, see ExecuteOption.
|
||||
* @param[out] touched: if not null, set to true if any property was
|
||||
* changed.
|
||||
*
|
||||
* @return On success a pointer to DocumentObject::StdReturn is returned. On failure, it
|
||||
* returns a pointer to a newly created App::DocumentObjectExecReturn that contains the error
|
||||
* message.
|
||||
*/
|
||||
DocumentObjectExecReturn* execute(ExecuteOption option = ExecuteAll, bool* touched = nullptr);
|
||||
|
||||
void getPathsToDocumentObject(DocumentObject*, std::vector<App::ObjectIdentifier>& paths) const;
|
||||
/**
|
||||
* @brief Find the paths to a given document object.
|
||||
*
|
||||
* @param[in] obj The document object to find paths to.
|
||||
* @param[out] paths Object identifiers that point to @a obj.
|
||||
*/
|
||||
void getPathsToDocumentObject(DocumentObject* obj,
|
||||
std::vector<App::ObjectIdentifier>& paths) const;
|
||||
|
||||
/**
|
||||
* @brief Check if any dependencies are touched.
|
||||
*
|
||||
* Determine whether any dependencies of any of the registered expressions
|
||||
* have been touched.
|
||||
*
|
||||
* @return True if at least on dependency has been touched.
|
||||
*/
|
||||
bool depsAreTouched() const;
|
||||
|
||||
/* Expression validator */
|
||||
/**
|
||||
* @brief Set an extra validator function.
|
||||
*
|
||||
* @param[in] f The validator function.
|
||||
*/
|
||||
void setValidator(ValidatorFunc f)
|
||||
{
|
||||
validator = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate the expression expression for a given path.
|
||||
*
|
||||
* @param[in] path The object identifier that the expression is targeting.
|
||||
* @param expr The expression to validate.
|
||||
*
|
||||
* @return An empty string on success, an error message on failure.
|
||||
*/
|
||||
std::string validateExpression(const App::ObjectIdentifier& path,
|
||||
std::shared_ptr<const App::Expression> expr) const;
|
||||
|
||||
/**
|
||||
* @brief Rename object identifiers in the registered expressions.
|
||||
*
|
||||
* @param[in] paths A map with the current and new object identifiers.
|
||||
*/
|
||||
void renameExpressions(const std::map<App::ObjectIdentifier, App::ObjectIdentifier>& paths);
|
||||
|
||||
/**
|
||||
* @brief Rename object identifiers in the registered expressions.
|
||||
*
|
||||
* @param[in] paths A map with the current and new object identifiers.
|
||||
*/
|
||||
void
|
||||
renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier>& paths);
|
||||
|
||||
App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier& p) const override;
|
||||
/**
|
||||
* @brief Create a canonical object identifier of the given object \a p.
|
||||
*
|
||||
* @param oid The object identifier from which we want a canonical path.
|
||||
* @return The canonical object identifier.
|
||||
*/
|
||||
App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier& oid) const override;
|
||||
|
||||
/// Get the number of expressions managed by this object.
|
||||
size_t numExpressions() const;
|
||||
|
||||
/// signal called when an expression was changed
|
||||
@@ -210,14 +333,42 @@ private:
|
||||
using ExpressionMap = std::map<const App::ObjectIdentifier, ExpressionInfo>;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Compute the evaluation order of the expressions.
|
||||
*
|
||||
* This method builds a graph for all expressions in the engine, and finds
|
||||
* any circular dependencies. It also computes the internal evaluation
|
||||
* order, in case properties depend on each other.
|
||||
*
|
||||
* @param[in] option Execution option, see ExecuteOption.
|
||||
*
|
||||
* @return A vector with the evaluation order of the properties and their
|
||||
* dependencies in terms of object identifiers.
|
||||
*
|
||||
* @throws Base::RuntimeError if a circular dependency is detected.
|
||||
*/
|
||||
std::vector<App::ObjectIdentifier> computeEvaluationOrder(ExecuteOption option);
|
||||
|
||||
/**
|
||||
* @brief Update graph structure with given path and expression.
|
||||
* @param path Path
|
||||
* @param expression Expression to query for dependencies
|
||||
* @param nodes Map with nodes of graph, including dependencies of 'expression'
|
||||
* @param revNodes Reverse map of the nodes, containing only the given paths, without dependencies.
|
||||
* @param edges Edges in graph
|
||||
*/
|
||||
void buildGraphStructures(const App::ObjectIdentifier& path,
|
||||
const std::shared_ptr<Expression> expression,
|
||||
boost::unordered_map<App::ObjectIdentifier, int>& nodes,
|
||||
boost::unordered_map<int, App::ObjectIdentifier>& revNodes,
|
||||
std::vector<Edge>& edges) const;
|
||||
|
||||
/**
|
||||
* @brief Build a graph of all expressions in \a exprs.
|
||||
* @param exprs Expressions to use in graph
|
||||
* @param revNodes Map from int[nodeid] to ObjectIndentifer.
|
||||
* @param g Graph to update. May contain additional nodes than in revNodes, because of outside
|
||||
* dependencies.
|
||||
*/
|
||||
void buildGraph(const ExpressionMap& exprs,
|
||||
boost::unordered_map<int, App::ObjectIdentifier>& revNodes,
|
||||
DiGraph& g,
|
||||
|
||||
@@ -393,28 +393,31 @@ public:
|
||||
|
||||
/// Update all element references in all link properties of \a feature
|
||||
static void updateElementReferences(DocumentObject* feature, bool reverse = false);
|
||||
|
||||
|
||||
/// Update all element references in the _ElementRefMap
|
||||
static void updateAllElementReferences(bool reverse = false);
|
||||
|
||||
/// Obtain link properties that contain element references to a given object
|
||||
static const std::unordered_set<PropertyLinkBase*>& getElementReferences(DocumentObject*);
|
||||
|
||||
/** Helper function for update individual element reference
|
||||
/**
|
||||
* @brief Update individual element references.
|
||||
*
|
||||
* @param feature: if given, than only update element reference belonging
|
||||
* to this feature. If not, then update geometry element
|
||||
* references.
|
||||
* @param sub: the subname reference to be updated.
|
||||
* @param shadow: a pair of new and old style element references to be updated.
|
||||
* @param reverse: if true, then use the old style, i.e. non-mapped element
|
||||
* reference to query for the new style, i.e. mapped
|
||||
* element reference when update. If false, then the other
|
||||
* way around.
|
||||
* @param notify: if true, call aboutToSetValue() before change
|
||||
* This helper function is to be called by each link property in the event
|
||||
* of geometry element reference change due to geometry model changes.
|
||||
*
|
||||
* This helper function is to be called by each link property in the event of
|
||||
* geometry element reference change due to geometry model changes.
|
||||
* @param[in] feature If given, than only update element reference
|
||||
* belonging to this feature. If not, then update all geometry element
|
||||
* references.
|
||||
* @param[in] obj The linked object.
|
||||
* @param[in,out] sub The subname reference to be updated.
|
||||
* @param[in,out] shadow A pair of new and old style element references to be updated.
|
||||
*
|
||||
* @param[in] reverse If true, then use the old style, i.e. non-mapped element
|
||||
* reference to query for the new style, i.e. mapped element reference when
|
||||
* update. If false, then the other way around.
|
||||
*
|
||||
* @param[in] notify: if true, call aboutToSetValue() before change
|
||||
*/
|
||||
bool _updateElementReference(App::DocumentObject* feature,
|
||||
App::DocumentObject* obj,
|
||||
@@ -446,20 +449,21 @@ public:
|
||||
const std::vector<App::DocumentObject*>& objs,
|
||||
bool clear);
|
||||
|
||||
/** Helper function for link import operation
|
||||
/**
|
||||
* @brief Try import a subname reference.
|
||||
*
|
||||
* @param obj: the linked object
|
||||
* @param sub: subname reference
|
||||
* @param doc: importing document
|
||||
* @param nameMap: a name map from source object to its imported counter part
|
||||
* This operation will go through all subname references and import all
|
||||
* externally linked objects. After import, the link property must be
|
||||
* changed to point to the newly imported objects, which should happen
|
||||
* inside the API CopyOnImportExternal(). This function helps to rewrite
|
||||
* subname reference to point to the correct sub objects that are imported.
|
||||
*
|
||||
* @return Return a changed subname reference, or empty string if no change.
|
||||
* @param obj The linked object.
|
||||
* @param sub The subname reference.
|
||||
* @param doc The importing document.
|
||||
* @param nameMap A name map from source object to its imported counter part.
|
||||
*
|
||||
* Link import operation will go through all link property and imports all
|
||||
* externally linked object. After import, the link property must be
|
||||
* changed to point to the newly imported objects, which should happen inside
|
||||
* the API CopyOnImportExternal(). This function helps to rewrite subname
|
||||
* reference to point to the correct sub objects that are imported.
|
||||
* @return A changed subname reference, or an empty string if no change.
|
||||
*/
|
||||
static std::string tryImportSubName(const App::DocumentObject* obj,
|
||||
const char* sub,
|
||||
|
||||
@@ -227,7 +227,11 @@
|
||||
* recomputed. Since properties may depend on other document objects, for
|
||||
* example because of the @ref App::DocumentObject::ExpressionEngine
|
||||
* "ExpressionEngine" property, these document objects have to be computed
|
||||
* first. Managing the order of recomputation is managed by the @ref
|
||||
* first. For more information on the role of expressions in recomputing
|
||||
* document objects, see topic @ref SecExpressionsDocumentObjectRecompute
|
||||
* "Expressions Framework".
|
||||
*
|
||||
* Managing the order of recomputation is managed by the @ref
|
||||
* App::Document "Document" which calls the @ref
|
||||
* App::DocumentObject::recompute() "recompute()" on the document object. To
|
||||
* signal that a document object needs to be recomputed, the document object
|
||||
@@ -251,7 +255,7 @@
|
||||
* Vice versa, if a property of an object is changed, the InList indicates
|
||||
* which other objects depend on it and need to be recomputed.
|
||||
*
|
||||
* So, as mentioned above, links define dependencies between document objects
|
||||
* So, as mentioned above, links define dependencies between document objects.
|
||||
* These dependencies are recorded by means of setting the value of a @ref
|
||||
* App::PropertyLink "PropertyLink" (and similar properties) in a document
|
||||
* object `Obj` to a document object `Value`. Within @ref
|
||||
@@ -466,7 +470,78 @@
|
||||
/**
|
||||
* @defgroup ExpressionFramework Expressions framework
|
||||
* @ingroup APP
|
||||
* @brief The expression system allows users to write expressions and formulas that produce values
|
||||
* @brief A system that allows users to write expressions and formulas that produce values.
|
||||
*
|
||||
* One of the differences between @ref DocumentObjectGroup "DocumentObjects"
|
||||
* and @ref DocumentGroup "Documents" as @ref PropertyFramework
|
||||
* "PropertyContainers" is that document objects support expressions whereas
|
||||
* documents do not. %Document objects have a special hidden property called
|
||||
* @ref App::DocumentObject::ExpressionEngine "ExpressionEngine" of type @ref
|
||||
* App::PropertyExpressionEngine "PropertyExpressionEngine" that contains a
|
||||
* mapping from @ref App::ObjectIdentifier "ObjectIdentifier" to @ref
|
||||
* App::Expression "Expression". An object identifier defines the property
|
||||
* inside the document object that will hold the result of the expression. We
|
||||
* can also say that the expression is bound to the property that the object
|
||||
* identifier identifies.
|
||||
*
|
||||
* Expressions can be as simple as `2 + 3`, but typically they reference other
|
||||
* document objects and their properties, such as `Box001.Length` or even
|
||||
* document objects in other documents such as `myDocument#Box001.Length`.
|
||||
*
|
||||
* Since expressions can reference other document objects, they effectively
|
||||
* link to other document objects, potentially in other documents, and as such,
|
||||
* a @ref App::PropertyExpressionEngine "PropertyExpressionEngine" inherits
|
||||
* from the property external link container @ref App::PropertyXLinkContainer
|
||||
* "PropertyXLinkContainer".
|
||||
*
|
||||
* @section SecExpressionsDocumentObjectRecompute The role of expressions in recomputing Documents
|
||||
*
|
||||
* For a high level overview of how document objects recompute, see topics @ref
|
||||
* DependencyGraph "Document" and @ref SecDocumentObjectRecompute "Document
|
||||
* Objects". On a recompute of a document object, the expressions in the
|
||||
* expression engine are first evaluated by calling the @ref
|
||||
* App::PropertyExpressionEngine::execute "PropertyExpressionEngine::execute()"
|
||||
* function. This function obtains the values of the properties it references
|
||||
* and evaluates the expression to a value that is stored in the property to
|
||||
* which the expression is bound to.
|
||||
*
|
||||
* Now that the properties of the document objects have been updated according
|
||||
* to the expressions, the document object can call @ref
|
||||
* App::DocumentObject::execute "DocumentObject::execute()" to update its
|
||||
* internal properties.
|
||||
*
|
||||
* After this execute of the document objects, the expression engine is
|
||||
* executed again, but now only for the output properties ensuring that
|
||||
* expressions that depend on properties of the document object itself that are
|
||||
* set by @ref App::DocumentObject::execute "DocumentObject::execute()" are
|
||||
* also updated.
|
||||
*
|
||||
* This process makes clear why it is important that there is a well-defined
|
||||
* dependency graph and a topological order of document objects in terms of
|
||||
* their dependencies as was discussed in topic @ref DependencyGraph
|
||||
* "Document": If a document object is executed and references a property of
|
||||
* another document object, we need to make sure that that document object has
|
||||
* fully recomputed as we would compute a stale value of the property
|
||||
* otherwise. Similarly, objects that refer to properties of a recently
|
||||
* recomputed document object, need to be certain that the properties of this
|
||||
* object have the latest value. As such, it is not possible to have a cyclic
|
||||
* dependency between document objects as they would continuously update
|
||||
* themselves.
|
||||
*
|
||||
* @section SecExpressionVisitors Expression Visitors
|
||||
*
|
||||
* Some actions require that expressions are updated. For example, when a document
|
||||
* is relabeled, expressions that refer to the old name must be updated to
|
||||
* refer to the new name.
|
||||
*
|
||||
* For this purpose, the visitor pattern is used. An expression visitor is derived
|
||||
* from @ref App::ExpressionVisitor "ExpressionVisitor" and implements the
|
||||
* virtual function @ref App::ExpressionVisitor::visit "visit()". This
|
||||
* function is called for each node in the expression tree.
|
||||
*
|
||||
* There are two types of visitors: ones that gather information, for example a
|
||||
* list of object identifiers in the expression, and ones that modify the
|
||||
* expression, for example in case a document is relabeled.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user