Files
create/src/App/ExpressionParser.h
2025-12-25 11:55:37 -06:00

676 lines
20 KiB
C++

// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* Copyright (c) 2019 Zheng Lei (realthunder) <realthunder.dev@gmail.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
****************************************************************************/
#ifndef EXPRESSION_PARSER_H
#define EXPRESSION_PARSER_H
#include "Expression.h"
#include <Base/Matrix.h>
#include <Base/Quantity.h>
#include <Base/Vector3D.h>
namespace App
{
////////////////////////////////////////////////////////////////////////////////////
// Expecting the extended expression is going to be constantly amended (to
// conform to Python), we move most of the class declarations here to avoid
// constant recompiling of the whole FC code base, as the expression header is
// included by everyone
///////////////////////////////////////////////////////////////////////////////////
struct AppExport Expression::Component
{
ObjectIdentifier::Component comp;
Expression* e1;
Expression* e2;
Expression* e3;
explicit Component(const std::string& n);
Component(Expression* e1, Expression* e2, Expression* e3, bool isRange = false);
explicit Component(const ObjectIdentifier::Component& comp);
Component(const Component& other);
~Component();
Component& operator=(const Component&) = delete;
void visit(ExpressionVisitor& v);
bool isTouched() const;
void toString(std::ostream& ss, bool persistent) const;
Component* copy() const;
Component* eval() const;
Py::Object get(const Expression* owner, const Py::Object& pyobj) const;
void set(const Expression* owner, Py::Object& pyobj, const Py::Object& value) const;
void del(const Expression* owner, Py::Object& pyobj) const;
};
////////////////////////////////////////////////////////////////////////////////////
/**
* Part of an expressions that contains a unit.
*
*/
class AppExport UnitExpression: public Expression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit UnitExpression(const App::DocumentObject* _owner = nullptr,
const Base::Quantity& _quantity = Base::Quantity(),
const std::string& _unitStr = std::string());
~UnitExpression() override;
Expression* simplify() const override;
void setUnit(const Base::Quantity& _quantity);
void setQuantity(const Base::Quantity& _quantity);
double getValue() const
{
return quantity.getValue();
}
const Base::Unit& getUnit() const
{
return quantity.getUnit();
}
const Base::Quantity& getQuantity() const
{
return quantity;
}
const std::string getUnitString() const
{
return unitStr;
}
double getScaler() const
{
return quantity.getValue();
}
protected:
Expression* _copy() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
Py::Object _getPyValue() const override;
protected:
mutable PyObject* cache = nullptr;
private:
Base::Quantity quantity;
std::string unitStr; /**< The unit string from the original parsed string */
};
/**
* Class implementing a number with an optional unit
*/
class AppExport NumberExpression: public UnitExpression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit NumberExpression(const App::DocumentObject* _owner = nullptr,
const Base::Quantity& quantity = Base::Quantity());
Expression* simplify() const override;
void negate();
bool isInteger(long* v = nullptr) const;
protected:
Expression* _copy() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
};
class AppExport ConstantExpression: public NumberExpression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit ConstantExpression(const App::DocumentObject* _owner = nullptr,
const char* _name = "",
const Base::Quantity& _quantity = Base::Quantity());
std::string getName() const
{
return name;
}
bool isNumber() const;
protected:
Py::Object _getPyValue() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
Expression* _copy() const override;
protected:
const char* name;
};
/**
* Class implementing an infix expression.
*
*/
class AppExport OperatorExpression: public UnitExpression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
enum Operator
{
NONE,
ADD,
SUB,
MUL,
DIV,
MOD,
POW,
EQ,
NEQ,
LT,
GT,
LTE,
GTE,
UNIT,
NEG,
POS
};
explicit OperatorExpression(const App::DocumentObject* _owner = nullptr,
Expression* _left = nullptr,
Operator _op = NONE,
Expression* _right = nullptr);
~OperatorExpression() override;
bool isTouched() const override;
Expression* simplify() const override;
int priority() const override;
Operator getOperator() const
{
return op;
}
Expression* getLeft() const
{
return left;
}
Expression* getRight() const
{
return right;
}
protected:
Expression* _copy() const override;
Py::Object _getPyValue() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
void _visit(ExpressionVisitor& v) override;
virtual bool isCommutative() const;
virtual bool isLeftAssociative() const;
virtual bool isRightAssociative() const;
Operator op; /**< Operator working on left and right */
Expression* left; /**< Left operand */
Expression* right; /**< Right operand */
};
class AppExport ConditionalExpression: public Expression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit ConditionalExpression(const App::DocumentObject* _owner = nullptr,
Expression* _condition = nullptr,
Expression* _trueExpr = nullptr,
Expression* _falseExpr = nullptr);
~ConditionalExpression() override;
bool isTouched() const override;
Expression* simplify() const override;
int priority() const override;
protected:
Expression* _copy() const override;
void _visit(ExpressionVisitor& v) override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
Py::Object _getPyValue() const override;
protected:
Expression* condition; /**< Condition */
Expression* trueExpr; /**< Expression if abs(condition) is > 0.5 */
Expression* falseExpr; /**< Expression if abs(condition) is < 0.5 */
};
/**
* Class implementing various functions, e.g sin, cos, etc.
*
*/
class AppExport FunctionExpression: public UnitExpression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
enum Function
{
NONE,
// Normal functions taking one or two arguments
ABS,
ACOS,
ASIN,
ATAN,
ATAN2,
CATH,
CBRT,
CEIL,
COS,
COSH,
EXP,
FLOOR,
HYPOT,
LOG,
LOG10,
MOD,
POW,
ROUND,
SIN,
SINH,
SQRT,
TAN,
TANH,
TRUNC,
// Vector
VANGLE,
VCROSS,
VDOT,
VLINEDIST,
VLINESEGDIST,
VLINEPROJ,
VNORMALIZE,
VPLANEDIST,
VPLANEPROJ,
VSCALE,
VSCALEX,
VSCALEY,
VSCALEZ,
// Matrix
MINVERT, // invert matrix/placement/rotation
MROTATE, // Rotate matrix/placement/rotation around axis, by rotation object, or by euler
// angles.
MROTATEX, // Rotate matrix/placement/rotation around x-axis.
MROTATEY, // Rotate matrix/placement/rotation around y-axis.
MROTATEZ, // Rotate matrix/placement/rotation around z-axis.
MSCALE, // matrix scale by vector
MTRANSLATE, // Translate matrix/placement.
// Object creation
CREATE, // Create new object of a given type.
LIST, // Create Python list.
MATRIX, // Create matrix object.
PLACEMENT, // Create placement object.
ROTATION, // Create rotation object.
ROTATIONX, // Create x-axis rotation object.
ROTATIONY, // Create y-axis rotation object.
ROTATIONZ, // Create z-axis rotation object.
STR, // stringify
PARSEQUANT, // parse string quantity
TRANSLATIONM, // Create translation matrix object.
TUPLE, // Create Python tuple.
VECTOR, // Create vector object.
HIDDENREF, // hidden reference that has no dependency check
HREF, // deprecated alias of HIDDENREF
// Non aggregated logical
NOT, // logical NOT
// Aggregates
AGGREGATES,
AVERAGE,
COUNT,
MAX,
MIN,
STDDEV,
SUM,
// Logical aggregates, evaluates to {0,1}
AND, // logical AND
OR, // logical OR
// Last one
LAST,
};
explicit FunctionExpression(const App::DocumentObject* _owner = nullptr,
Function _f = NONE,
std::string&& name = std::string(),
std::vector<Expression*> _args = std::vector<Expression*>());
~FunctionExpression() override;
bool isTouched() const override;
Expression* simplify() const override;
static Py::Object
evaluate(const Expression* owner, int type, const std::vector<Expression*>& args);
Function getFunction() const
{
return f;
}
const std::vector<Expression*>& getArgs() const
{
return args;
}
protected:
static Py::Object
evalAggregate(const Expression* owner, int type, const std::vector<Expression*>& args);
static Base::Vector3d evaluateSecondVectorArgument(const Expression* expression,
const std::vector<Expression*>& arguments);
static double extractLengthValueArgument(const Expression* expression,
const std::vector<Expression*>& arguments,
int argumentIndex);
static Base::Vector3d extractVectorArgument(const Expression* expression,
const std::vector<Expression*>& arguments,
int argumentIndex);
static void initialiseObject(const Py::Object* object,
const std::vector<Expression*>& arguments,
const unsigned long offset = 0);
static Py::Object transformFirstArgument(const Expression* expression,
const std::vector<Expression*>& arguments,
const Base::Matrix4D* transformationMatrix);
static Py::Object translationMatrix(double x, double y, double z);
Py::Object _getPyValue() const override;
Expression* _copy() const override;
void _visit(ExpressionVisitor& v) override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
Function f; /**< Function to execute */
std::string fname;
std::vector<Expression*> args; /** Arguments to function*/
};
/**
* Class implementing a reference to a property. If the name is unqualified,
* the owner of the expression is searched. If it is qualified, the document
* that contains the owning document object is searched for other document
* objects to search. Both labels and internal document names are searched.
*
*/
class AppExport VariableExpression: public UnitExpression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit VariableExpression(const App::DocumentObject* _owner = nullptr,
const ObjectIdentifier& _var = ObjectIdentifier());
~VariableExpression() override;
bool isTouched() const override;
Expression* simplify() const override;
std::string name() const
{
return var.getPropertyName();
}
ObjectIdentifier getPath() const
{
return var;
}
void setPath(const ObjectIdentifier& path);
const App::Property* getProperty() const;
void addComponent(Component* component) override;
protected:
Expression* _copy() const override;
Py::Object _getPyValue() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
bool _isIndexable() const override;
void _getIdentifiers(std::map<App::ObjectIdentifier, bool>&) const override;
bool _adjustLinks(const std::set<App::DocumentObject*>&, ExpressionVisitor&) override;
void _importSubNames(const ObjectIdentifier::SubNameMap&) override;
void _updateLabelReference(App::DocumentObject*, const std::string&, const char*) override;
bool _updateElementReference(App::DocumentObject*, bool, ExpressionVisitor&) override;
bool _relabeledDocument(const std::string&, const std::string&, ExpressionVisitor&) override;
bool _renameObjectIdentifier(const std::map<ObjectIdentifier, ObjectIdentifier>&,
const ObjectIdentifier&,
ExpressionVisitor&) override;
void _collectReplacement(std::map<ObjectIdentifier, ObjectIdentifier>&,
const App::DocumentObject* parent,
App::DocumentObject* oldObj,
App::DocumentObject* newObj) const override;
void _moveCells(const CellAddress&, int, int, ExpressionVisitor&) override;
void _offsetCells(int, int, ExpressionVisitor&) override;
protected:
ObjectIdentifier var; /**< Variable name */
};
//////////////////////////////////////////////////////////////////////
class AppExport PyObjectExpression: public Expression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit PyObjectExpression(const App::DocumentObject* _owner = nullptr,
PyObject* pyobj = nullptr,
bool owned = false)
: Expression(_owner)
{
setPyValue(pyobj, owned);
}
~PyObjectExpression() override;
void setPyValue(Py::Object pyobj);
void setPyValue(PyObject* pyobj, bool owned = false);
Expression* simplify() const override
{
return copy();
}
protected:
Expression* _copy() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
Py::Object _getPyValue() const override;
protected:
PyObject* pyObj = nullptr;
};
/**
* Class implementing a string. Used to signal either a genuine string or
* a failed evaluation of an expression.
*/
class AppExport StringExpression: public Expression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit StringExpression(const App::DocumentObject* _owner = nullptr,
const std::string& _text = std::string());
~StringExpression() override;
Expression* simplify() const override;
virtual std::string getText() const
{
return text;
}
protected:
Expression* _copy() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
Py::Object _getPyValue() const override;
bool _isIndexable() const override
{
return true;
}
private:
std::string text; /**< Text string */
mutable PyObject* cache = nullptr;
};
class AppExport RangeExpression: public App::Expression
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
explicit RangeExpression(const App::DocumentObject* _owner = nullptr,
const std::string& begin = std::string(),
const std::string& end = std::string());
~RangeExpression() override = default;
bool isTouched() const override;
App::Expression* simplify() const override;
Range getRange() const;
protected:
Expression* _copy() const override;
void _toString(std::ostream& ss, bool persistent, int indent) const override;
Py::Object _getPyValue() const override;
void _getIdentifiers(std::map<App::ObjectIdentifier, bool>&) const override;
bool _renameObjectIdentifier(const std::map<ObjectIdentifier, ObjectIdentifier>&,
const ObjectIdentifier&,
ExpressionVisitor&) override;
void _moveCells(const CellAddress&, int, int, ExpressionVisitor&) override;
void _offsetCells(int, int, ExpressionVisitor&) override;
protected:
std::string begin;
std::string end;
};
/**
* @brief Namespace for parsing expressions.
*
* Contains functionality for parsing expressions, the units of
* expressions, whether a token is an identifier, unit, or constant.
*/
namespace ExpressionParser
{
AppExport Expression* parse(const App::DocumentObject* owner, const char* buffer);
AppExport UnitExpression* parseUnit(const App::DocumentObject* owner, const char* buffer);
AppExport ObjectIdentifier parsePath(const App::DocumentObject* owner, const char* buffer);
AppExport bool isTokenAnIndentifier(const std::string& str);
AppExport bool isTokenAConstant(const std::string& str);
AppExport bool isTokenAUnit(const std::string& str);
AppExport std::vector<std::tuple<int, int, std::string>> tokenize(const std::string& str);
/// Convenient class to mark begin of importing
class AppExport ExpressionImporter
{
public:
explicit ExpressionImporter(Base::XMLReader& reader);
~ExpressionImporter();
static Base::XMLReader* reader();
};
AppExport bool isModuleImported(PyObject*);
/**
* @brief The semantic_type class encapsulates the value in the parse tree during parsing.
*/
class semantic_type
{
public:
struct
{
Base::Quantity scaler;
std::string unitStr;
} quantity;
Expression::Component* component {nullptr};
Expression* expr {nullptr};
ObjectIdentifier path;
std::deque<ObjectIdentifier::Component> components;
long long int ivalue {0};
double fvalue {0};
struct
{
const char* name = "";
double fvalue = 0;
} constant;
std::vector<Expression*> arguments;
std::vector<Expression*> list;
std::string string;
std::pair<FunctionExpression::Function, std::string> func;
ObjectIdentifier::String string_or_identifier;
semantic_type()
: func({FunctionExpression::NONE, std::string()})
{}
};
#define YYSTYPE semantic_type
#include "Expression.tab.h"
#undef YYTOKENTYPE
#undef YYSTYPE
#undef YYSTYPE_ISDECLARED
} // namespace ExpressionParser
} // namespace App
#endif // EXPRESSION_PARSER_H