App: use Python number protocol in Expression

This adds support to do mul/div with matrix and vector, as well as
Python formated string with operator %.
This commit is contained in:
Zheng, Lei
2019-08-25 16:16:32 +08:00
committed by wmayer
parent 1efadc928e
commit 8cbdc3ad5d
9 changed files with 641 additions and 504 deletions

View File

@@ -656,6 +656,25 @@ bool isAnyEqual(const App::any &v1, const App::any &v2) {
return !!res;
}
Expression* expressionFromPy(const DocumentObject *owner, const Py::Object &value) {
if (value.isNone()) {
return new PyObjectExpression(owner);
}
if(value.isString()) {
return new StringExpression(owner,value.as_string());
} else if (PyObject_TypeCheck(value.ptr(),&QuantityPy::Type)) {
return new NumberExpression(owner,
*static_cast<QuantityPy*>(value.ptr())->getQuantityPtr());
} else if (value.isBoolean()) {
return new BooleanExpression(owner,value.isTrue());
} else {
Quantity q;
if(pyToQuantity(q,value))
return new NumberExpression(owner,q);
}
return new PyObjectExpression(owner,value.ptr());
}
} // namespace App
@@ -987,6 +1006,10 @@ int UnitExpression::priority() const
return 20;
}
Py::Object UnitExpression::getPyValue() const {
return pyFromQuantity(quantity);
}
//
// NumberExpression class
//
@@ -1089,6 +1112,148 @@ bool OperatorExpression::isTouched() const
return left->isTouched() || right->isTouched();
}
static Py::Object calc(const Expression *expr, int op,
const Expression *left, const Expression *right, bool inplace)
{
Py::Object l = left->getPyValue();
// For security reason, restrict supported types
if(!PyObject_TypeCheck(l.ptr(),&PyObjectBase::Type)
&& !l.isNumeric() && !l.isString() && !l.isList() && !l.isDict())
{
__EXPR_THROW(Base::TypeError,"Unsupported operator", expr);
}
// check possible unary operation first
switch(op) {
case OperatorExpression::POS:{
PyObject *res = PyNumber_Positive(l.ptr());
if(!res) EXPR_PY_THROW(expr);
return Py::asObject(res);
}
case OperatorExpression::NEG:{
PyObject *res = PyNumber_Negative(l.ptr());
if(!res) EXPR_PY_THROW(expr);
return Py::asObject(res);
} default:
break;
}
Py::Object r = right->getPyValue();
// For security reason, restrict supported types
if((op!=OperatorExpression::MOD || !l.isString())
&& !PyObject_TypeCheck(r.ptr(),&PyObjectBase::Type)
&& !r.isNumeric()
&& !r.isString()
&& !r.isList()
&& !r.isDict())
{
__EXPR_THROW(Base::TypeError,"Unsupported operator", expr);
}
switch(op) {
#define RICH_COMPARE(_op,_pyop) \
case OperatorExpression::_op: {\
int res = PyObject_RichCompareBool(l.ptr(),r.ptr(),Py_##_pyop);\
if(res<0) EXPR_PY_THROW(expr);\
return Py::Boolean(!!res);\
}
RICH_COMPARE(LT,LT)
RICH_COMPARE(LTE,LE)
RICH_COMPARE(GT,GT)
RICH_COMPARE(GTE,GE)
RICH_COMPARE(EQ,EQ)
RICH_COMPARE(NEQ,NE)
#define _BINARY_OP(_pyop) \
res = inplace?PyNumber_InPlace##_pyop(l.ptr(),r.ptr()):\
PyNumber_##_pyop(l.ptr(),r.ptr());\
if(!res) EXPR_PY_THROW(expr);\
return Py::asObject(res);
#define BINARY_OP(_op,_pyop) \
case OperatorExpression::_op: {\
PyObject *res;\
_BINARY_OP(_pyop);\
}
BINARY_OP(SUB,Subtract)
BINARY_OP(MUL,Multiply)
BINARY_OP(UNIT,Multiply)
BINARY_OP(DIV,TrueDivide)
case OperatorExpression::ADD: {
PyObject *res;
#if PY_MAJOR_VERSION < 3
if (PyString_CheckExact(*l) && PyString_CheckExact(*r)) {
Py_ssize_t v_len = PyString_GET_SIZE(*l);
Py_ssize_t w_len = PyString_GET_SIZE(*r);
Py_ssize_t new_len = v_len + w_len;
if (new_len < 0)
__EXPR_THROW(OverflowError, "strings are too large to concat", expr);
if (l.ptr()->ob_refcnt==1 && !PyString_CHECK_INTERNED(l.ptr())) {
res = Py::new_reference_to(l);
// Must make sure ob_refcnt is still 1
l = Py::Object();
if (_PyString_Resize(&res, new_len) != 0)
EXPR_PY_THROW(expr);
memcpy(PyString_AS_STRING(res) + v_len, PyString_AS_STRING(*r), w_len);
}else{
res = Py::new_reference_to(l);
l = Py::Object();
PyString_Concat(&res,*r);
if(!res) EXPR_PY_THROW(expr);
}
return Py::asObject(res);
}
#else
if (PyUnicode_CheckExact(*l) && PyUnicode_CheckExact(*r)) {
if(inplace) {
res = Py::new_reference_to(l);
// Try to make sure ob_refcnt is 1, although unlike
// PyString_Resize above, PyUnicode_Append can handle other
// cases.
l = Py::Object();
PyUnicode_Append(&res, r.ptr());
}else
res = PyUnicode_Concat(l.ptr(),r.ptr());
if(!res) EXPR_PY_THROW(expr);
return Py::asObject(res);
}
#endif
_BINARY_OP(Add);
}
case OperatorExpression::POW: {
PyObject *res;
if(inplace)
res = PyNumber_InPlacePower(l.ptr(),r.ptr(),Py::None().ptr());
else
res = PyNumber_Power(l.ptr(),r.ptr(),Py::None().ptr());
if(!res) EXPR_PY_THROW(expr);
return Py::asObject(res);
}
case OperatorExpression::MOD: {
PyObject *res;
#if PY_MAJOR_VERSION < 3
if (PyString_CheckExact(l.ptr()) &&
(!PyString_Check(r.ptr()) || PyString_CheckExact(r.ptr())))
res = PyString_Format(l.ptr(), r.ptr());
#else
if (PyUnicode_CheckExact(l.ptr()) &&
(!PyUnicode_Check(r.ptr()) || PyUnicode_CheckExact(r.ptr())))
res = PyUnicode_Format(l.ptr(), r.ptr());
#endif
else if(inplace)
res = PyNumber_InPlaceRemainder(l.ptr(),r.ptr());
else
res = PyNumber_InPlaceRemainder(l.ptr(),r.ptr());
if(!res) EXPR_PY_THROW(expr);
return Py::asObject(res);
}
default:
__EXPR_THROW(RuntimeError,"Unsupported operator",expr);
}
}
/**
* Evaluate the expression. Returns a new Expression with the result, or throws
@@ -1097,83 +1262,17 @@ bool OperatorExpression::isTouched() const
Expression * OperatorExpression::eval() const
{
std::unique_ptr<Expression> e1(left->eval());
NumberExpression * v1;
std::unique_ptr<Expression> e2(right->eval());
NumberExpression * v2;
Expression * output;
Base::PyGILStateLocker lock;
return expressionFromPy(owner,getPyValue());
}
v1 = freecad_dynamic_cast<NumberExpression>(e1.get());
v2 = freecad_dynamic_cast<NumberExpression>(e2.get());
boost::any OperatorExpression::getValueAsAny() const {
Base::PyGILStateLocker lock;
return pyObjectToAny(getPyValue(),true);
}
if (v1 == 0 || v2 == 0)
throw ExpressionError("Invalid expression");
switch (op) {
case ADD:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for + operator");
output = new NumberExpression(owner, v1->getQuantity() + v2->getQuantity());
break;
case SUB:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for - operator");
output = new NumberExpression(owner, v1->getQuantity()- v2->getQuantity());
break;
case MUL:
case UNIT:
output = new NumberExpression(owner, v1->getQuantity() * v2->getQuantity());
break;
case DIV:
output = new NumberExpression(owner, v1->getQuantity() / v2->getQuantity());
break;
case POW:
output = new NumberExpression(owner, v1->getQuantity().pow(v2->getQuantity()) );
break;
case EQ:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for the = operator");
output = new BooleanExpression(owner, essentiallyEqual(v1->getValue(), v2->getValue()) );
break;
case NEQ:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for the != operator");
output = new BooleanExpression(owner, !essentiallyEqual(v1->getValue(), v2->getValue()) );
break;
case LT:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for the < operator");
output = new BooleanExpression(owner, definitelyLessThan(v1->getValue(), v2->getValue()) );
break;
case GT:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for the > operator");
output = new BooleanExpression(owner, definitelyGreaterThan(v1->getValue(), v2->getValue()) );
break;
case LTE:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for the <= operator");
output = new BooleanExpression(owner, definitelyLessThan(v1->getValue(), v2->getValue()) ||
essentiallyEqual(v1->getValue(), v2->getValue()));
break;
case GTE:
if (v1->getUnit() != v2->getUnit())
throw ExpressionError("Incompatible units for the >= operator");
output = new BooleanExpression(owner, essentiallyEqual(v1->getValue(), v2->getValue()) ||
definitelyGreaterThan(v1->getValue(), v2->getValue()));
break;
case NEG:
output = new NumberExpression(owner, -v1->getQuantity() );
break;
case POS:
output = new NumberExpression(owner, v1->getQuantity() );
break;
default:
output = 0;
assert(0);
}
return output;
Py::Object OperatorExpression::getPyValue() const {
return calc(this,op,left,right,true);
}
/**
@@ -1252,6 +1351,9 @@ std::string OperatorExpression::toString(bool persistent) const
case DIV:
s << " / ";
break;
case MOD:
s << " % ";
break;
case POW:
s << " ^ ";
break;
@@ -1334,6 +1436,7 @@ int OperatorExpression::priority() const
return 3;
case MUL:
case DIV:
case MOD:
return 4;
case POW:
return 5;
@@ -1879,6 +1982,16 @@ Expression * FunctionExpression::eval() const
return new NumberExpression(owner, Quantity(scaler * output, unit));
}
boost::any FunctionExpression::getValueAsAny() const {
ExpressionPtr e(eval());
return e->getValueAsAny();
}
Py::Object FunctionExpression::getPyValue() const {
ExpressionPtr e(eval());
return e->getPyValue();
}
/**
* Try to simplify the expression, i.e calculate all constant expressions.
*
@@ -2087,65 +2200,17 @@ const Property * VariableExpression::getProperty() const
Expression * VariableExpression::eval() const
{
const Property * prop = getProperty();
PropertyContainer * parent = prop->getContainer();
Base::PyGILStateLocker lock;
return expressionFromPy(owner,getPyValue());
}
if (!parent->isDerivedFrom(App::DocumentObject::getClassTypeId()))
throw ExpressionError("Property must belong to a document object.");
boost::any VariableExpression::getValueAsAny() const {
Base::PyGILStateLocker lock;
return pyObjectToAny(getPyValue());
}
boost::any value = prop->getPathValue(var);
if (value.type() == typeid(Quantity)) {
Quantity qvalue = boost::any_cast<Quantity>(value);
return new NumberExpression(owner, qvalue);
}
else if (value.type() == typeid(double)) {
double dvalue = boost::any_cast<double>(value);
return new NumberExpression(owner, Quantity(dvalue));
}
else if (value.type() == typeid(float)) {
double fvalue = boost::any_cast<float>(value);
return new NumberExpression(owner, Quantity(fvalue));
}
else if (value.type() == typeid(int)) {
int ivalue = boost::any_cast<int>(value);
return new NumberExpression(owner, Quantity(ivalue));
}
else if (value.type() == typeid(long)) {
long lvalue = boost::any_cast<long>(value);
return new NumberExpression(owner, Quantity(lvalue));
}
else if (value.type() == typeid(bool)) {
double bvalue = boost::any_cast<bool>(value) ? 1.0 : 0.0;
return new NumberExpression(owner, Quantity(bvalue));
}
else if (value.type() == typeid(std::string)) {
std::string svalue = boost::any_cast<std::string>(value);
return new StringExpression(owner, svalue);
}
else if (value.type() == typeid(char*)) {
char* svalue = boost::any_cast<char*>(value);
return new StringExpression(owner, svalue);
}
else if (value.type() == typeid(const char*)) {
const char* svalue = boost::any_cast<const char*>(value);
return new StringExpression(owner, svalue);
}
else if (isAnyPyObject(value)) {
Base::PyGILStateLocker lock;
return new PyObjectExpression(owner,__pyObjectFromAny(value).ptr());
}
throw ExpressionError("Property is of invalid type.");
Py::Object VariableExpression::getPyValue() const {
return var.getPyValue(true);
}
std::string VariableExpression::toString(bool persistent) const {
@@ -2314,7 +2379,7 @@ PyObjectExpression::~PyObjectExpression() {
}
}
Py::Object PyObjectExpression::getPyObject() const {
Py::Object PyObjectExpression::getPyValue() const {
if(!pyObj)
return Py::Object();
return Py::Object(pyObj);
@@ -2406,6 +2471,14 @@ Expression *StringExpression::_copy() const
return new StringExpression(owner, text);
}
boost::any StringExpression::getValueAsAny() const {
return text;
}
Py::Object StringExpression::getPyValue() const {
return Py::String(text);
}
TYPESYSTEM_SOURCE(App::ConditionalExpression, App::Expression)
ConditionalExpression::ConditionalExpression(const DocumentObject *_owner, Expression *_condition, Expression *_trueExpr, Expression *_falseExpr)
@@ -2428,20 +2501,37 @@ bool ConditionalExpression::isTouched() const
return condition->isTouched() || trueExpr->isTouched() || falseExpr->isTouched();
}
Expression *ConditionalExpression::eval() const
{
bool ConditionalExpression::evalCond() const {
std::unique_ptr<Expression> e(condition->eval());
NumberExpression * v = freecad_dynamic_cast<NumberExpression>(e.get());
if (v == 0)
throw ExpressionError("Invalid expression");
if (fabs(v->getValue()) > 0.5)
return fabs(v->getValue()) > 0.5;
}
Expression *ConditionalExpression::eval() const {
if(evalCond())
return trueExpr->eval();
else
return falseExpr->eval();
}
boost::any ConditionalExpression::getValueAsAny() const {
if(evalCond())
return trueExpr->getValueAsAny();
else
return falseExpr->getValueAsAny();
}
Py::Object ConditionalExpression::getPyValue() const {
if(evalCond())
return trueExpr->getPyValue();
else
return falseExpr->getPyValue();
}
Expression *ConditionalExpression::simplify() const
{
std::unique_ptr<Expression> e(condition->simplify());
@@ -2550,6 +2640,10 @@ Expression *RangeExpression::eval() const
throw Exception("Range expression cannot be evaluated");
}
Py::Object RangeExpression::getPyValue() const {
return Py::Object();
}
std::string RangeExpression::toString(bool) const
{
return begin + ":" + end;

View File

@@ -165,7 +165,9 @@ public:
App::DocumentObject * getOwner() const { return owner; }
virtual boost::any getValueAsAny() const { static boost::any empty; return empty; }
virtual boost::any getValueAsAny() const = 0;
virtual Py::Object getPyValue() const = 0;
bool isSame(const Expression &other) const;
@@ -235,6 +237,8 @@ public:
boost::any getValueAsAny() const { return quantity.getUnit().isEmpty() ? boost::any(quantity.getValue()) : boost::any(quantity); }
virtual Py::Object getPyValue() const;
protected:
Base::Quantity quantity;
std::string unitStr; /**< The unit string from the original parsed string */
@@ -307,6 +311,7 @@ public:
SUB,
MUL,
DIV,
MOD,
POW,
EQ,
NEQ,
@@ -342,6 +347,10 @@ public:
Expression * getRight() const { return right; }
virtual boost::any getValueAsAny() const;
virtual Py::Object getPyValue() const;
protected:
virtual bool isCommutative() const;
@@ -370,11 +379,16 @@ public:
virtual std::string toString(bool persistent=false) const;
virtual Expression * _copy() const;
virtual int priority() const;
virtual boost::any getValueAsAny() const;
virtual Py::Object getPyValue() const;
protected:
virtual Expression * _copy() const;
virtual void _visit(ExpressionVisitor & v);
bool evalCond() const;
protected:
@@ -451,6 +465,11 @@ public:
virtual void _visit(ExpressionVisitor & v);
virtual boost::any getValueAsAny() const;
virtual Py::Object getPyValue() const;
protected:
Expression *evalAggregate() const;
@@ -493,6 +512,10 @@ public:
const App::Property *getProperty() const;
virtual boost::any getValueAsAny() const;
virtual Py::Object getPyValue() const;
protected:
virtual void _getDeps(ExpressionDeps &) const;
virtual void _getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *) const;
@@ -529,13 +552,12 @@ public:
virtual ~PyObjectExpression();
Py::Object getPyObject() const;
void setPyObject(Py::Object pyobj);
void setPyObject(PyObject *pyobj, bool owned=false);
virtual std::string toString(bool) const;
virtual boost::any getValueAsAny() const;
virtual Py::Object getPyValue() const;
virtual Expression * eval() const { return copy(); }
virtual Expression * simplify() const { return copy(); }
@@ -567,9 +589,12 @@ public:
virtual int priority() const;
virtual Expression * _copy() const;
virtual boost::any getValueAsAny() const;
virtual Py::Object getPyValue() const;
protected:
virtual Expression * _copy() const;
std::string text; /**< Text string */
};
@@ -587,15 +612,18 @@ public:
virtual std::string toString(bool persistent=false) const;
virtual Expression * _copy() const;
virtual int priority() const;
virtual App::Expression * simplify() const;
Range getRange() const;
virtual boost::any getValueAsAny() const { return boost::any(); }
virtual Py::Object getPyValue() const;
protected:
virtual Expression * _copy() const;
virtual void _getDeps(ExpressionDeps &) const;
virtual bool _renameObjectIdentifier(const std::map<ObjectIdentifier,ObjectIdentifier> &,
const ObjectIdentifier &, ExpressionVisitor &);

View File

@@ -146,7 +146,7 @@ EXPO [eE][-+]?[0-9]+
\<\<(\\(.|\n)|[^\\>\n])*\>\> COUNTCHARS; yylval.string = unquote(yytext); return STRING;
[+()=/*^,\.\{\}\[\]:;@\?#] COUNTCHARS; return *yytext;
[%+()=/*^,\.\{\}\[\]:;@\?#] COUNTCHARS; return *yytext;
"==" COUNTCHARS; return EQ;
"!=" COUNTCHARS; return NEQ;

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,19 @@
/* A Bison parser, made by GNU Bison 2.5. */
/* A Bison parser, made by GNU Bison 3.0.4. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
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 3 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, see <http://www.gnu.org/licenses/>. */
@@ -26,50 +26,56 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
#ifndef YY_YY_EXPRESSIONPARSER_TAB_H_INCLUDED
# define YY_YY_EXPRESSIONPARSER_TAB_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
#endif
#if YYDEBUG
extern int yydebug;
#endif
/* Tokens. */
/* Token type. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
FUNC = 258,
ONE = 259,
NUM = 260,
IDENTIFIER = 261,
UNIT = 262,
INTEGER = 263,
CONSTANT = 264,
CELLADDRESS = 265,
EQ = 266,
NEQ = 267,
LT = 268,
GT = 269,
GTE = 270,
LTE = 271,
STRING = 272,
MINUSSIGN = 273,
PROPERTY_REF = 274,
DOCUMENT = 275,
OBJECT = 276,
EXPONENT = 277,
NEG = 278,
POS = 279
};
enum yytokentype
{
FUNC = 258,
ONE = 259,
NUM = 260,
IDENTIFIER = 261,
UNIT = 262,
INTEGER = 263,
CONSTANT = 264,
CELLADDRESS = 265,
EQ = 266,
NEQ = 267,
LT = 268,
GT = 269,
GTE = 270,
LTE = 271,
STRING = 272,
MINUSSIGN = 273,
PROPERTY_REF = 274,
DOCUMENT = 275,
OBJECT = 276,
EXPONENT = 277,
NUM_AND_UNIT = 278,
NEG = 279,
POS = 280
};
#endif
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE yylval;
int yyparse (void);
#endif /* !YY_YY_EXPRESSIONPARSER_TAB_H_INCLUDED */

View File

@@ -49,7 +49,7 @@ std::stack<FunctionExpression::Function> functions; /**< Function
%left EQ NEQ LT GT GTE LTE
%left '?' ':'
%left MINUSSIGN '+'
%left '*' '/'
%left '*' '/' '%'
%precedence NUM_AND_UNIT
%left '^' /* exponentiation */
%left EXPONENT
@@ -77,6 +77,7 @@ exp: num { $$ = $1;
| exp MINUSSIGN exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::SUB, $3); }
| exp '*' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MUL, $3); }
| exp '/' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| exp '%' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::MOD, $3); }
| exp '/' unit_exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::DIV, $3); }
| exp '^' exp { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::POW, $3); }
| '(' exp ')' { $$ = $2; }
@@ -158,12 +159,12 @@ integer: INTEGER { $$ = $1; }
path: IDENTIFIER { $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
| CELLADDRESS { $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
| IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); }
| IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; }
| IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
| IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $6; }
| IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); }
| IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); }
| IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; }
| IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; }
| IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); }
| IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $6; }
| IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $6; }
| IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $3; }
;

View File

@@ -986,7 +986,7 @@ static const YY_CHAR yy_ec[256] =
1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
1, 1, 1, 1, 1, 4, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 5, 6, 7, 8, 9, 1, 1, 10, 11,
1, 5, 6, 7, 8, 9, 8, 1, 10, 11,
8, 8, 12, 13, 14, 15, 8, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 8, 8, 17,
18, 19, 8, 20, 21, 22, 23, 22, 24, 22,

View File

@@ -705,7 +705,7 @@ void Sheet::updateProperty(CellAddress key)
Base::PyGILStateLocker lock;
auto py_expr = freecad_dynamic_cast<PyObjectExpression>(output.get());
if(py_expr)
setObjectProperty(key, py_expr->getPyObject());
setObjectProperty(key, py_expr->getPyValue());
else
setObjectProperty(key, Py::Object());
}

View File

@@ -136,7 +136,7 @@ class SpreadsheetCases(unittest.TestCase):
self.assertEqual(sheet.H2, u'ERR: Quantity::operator <(): quantities need to have same unit to compare')
self.assertEqual(sheet.H3, u'ERR: Quantity::operator >(): quantities need to have same unit to compare')
self.assertEqual(sheet.H4, 4)
self.assertEqual(sheet.H5, u'ERR: Quantity::operator +(): Unit mismatch in minus operation')
self.assertEqual(sheet.H5, u'ERR: Quantity::operator -(): Unit mismatch in minus operation')
self.assertEqual(sheet.H6, u'ERR: Quantity::operator +=(): Unit mismatch in plus operation')
def assertMostlyEqual(self, a, b):