[Core] Add Boolean Functions to expressions (AND, OR, NOT) (#22506)
* [Core] Add Boolean Functions to expressions (AND, OR, BOOL, NOT) * [Core] Add `if` function to expressions to overcome ternary operator limitations * [Core] Update expressions grammar to recognize relational operations as arguments * [Core] The `if` function has been removed as no consensus was reached regarding its convenience or necessity. Its inclusion was considered potentially confusing or redundant. * [Core] Make boolean cast based on Confusion threshold.
This commit is contained in:
committed by
GitHub
parent
5d43908edc
commit
d4c38502d8
@@ -52,6 +52,7 @@
|
||||
#include <Base/RotationPy.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/VectorPy.h>
|
||||
#include <Base/Precision.h>
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
|
||||
@@ -168,6 +169,15 @@ static inline T &&cast(App::any &&value) {
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
inline bool asBool(double value) {
|
||||
return std::fabs(value) >= Base::Precision::Confusion();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string unquote(const std::string & input)
|
||||
{
|
||||
assert(input.size() >= 4);
|
||||
@@ -1745,6 +1755,7 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
|
||||
case TANH:
|
||||
case TRUNC:
|
||||
case VNORMALIZE:
|
||||
case NOT:
|
||||
if (args.size() != 1)
|
||||
ARGUMENT_THROW("exactly one required.");
|
||||
break;
|
||||
@@ -1810,6 +1821,8 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
|
||||
case MIN:
|
||||
case STDDEV:
|
||||
case SUM:
|
||||
case AND:
|
||||
case OR:
|
||||
if (args.empty())
|
||||
ARGUMENT_THROW("at least one required.");
|
||||
break;
|
||||
@@ -1973,6 +1986,36 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class AndCollector : public Collector {
|
||||
public:
|
||||
void collect(Quantity value) override
|
||||
{
|
||||
if (first) {
|
||||
q = Quantity(asBool(value.getValue()) ? 1 : 0);
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
if (!asBool(value.getValue())) {
|
||||
q = Quantity(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class OrCollector : public Collector {
|
||||
public:
|
||||
void collect(Quantity value) override
|
||||
{
|
||||
if (first) {
|
||||
q = Quantity(asBool(value.getValue()) ? 1 : 0);
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
if (asBool(value.getValue())) {
|
||||
q = Quantity(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Py::Object FunctionExpression::evalAggregate(
|
||||
const Expression *owner, int f, const std::vector<Expression*> &args)
|
||||
{
|
||||
@@ -1997,6 +2040,12 @@ Py::Object FunctionExpression::evalAggregate(
|
||||
case MAX:
|
||||
c = std::make_unique<MaxCollector>();
|
||||
break;
|
||||
case AND:
|
||||
c = std::make_unique<AndCollector>();
|
||||
break;
|
||||
case OR:
|
||||
c = std::make_unique<OrCollector>();
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
@@ -2499,6 +2548,9 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
if (v1.isDimensionlessOrUnit(Unit::Length) && v2.isDimensionlessOrUnit(Unit::Length) && v3.isDimensionlessOrUnit(Unit::Length))
|
||||
break;
|
||||
_EXPR_THROW("Translation units must be a length or dimensionless.", expr);
|
||||
case NOT:
|
||||
unit = Unit();
|
||||
break;
|
||||
default:
|
||||
_EXPR_THROW("Unknown function: " << f,0);
|
||||
}
|
||||
@@ -2590,6 +2642,9 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
value)));
|
||||
case TRANSLATIONM:
|
||||
return translationMatrix(v1.getValue(), v2.getValue(), v3.getValue());
|
||||
case NOT:
|
||||
output = asBool(value) ? 0 : 1;
|
||||
break;
|
||||
default:
|
||||
_EXPR_THROW("Unknown function: " << f,0);
|
||||
}
|
||||
@@ -2773,6 +2828,12 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
|
||||
ss << "stddev("; break;;
|
||||
case SUM:
|
||||
ss << "sum("; break;;
|
||||
case AND:
|
||||
ss << "and("; break;;
|
||||
case OR:
|
||||
ss << "or("; break;;
|
||||
case NOT:
|
||||
ss << "not("; break;;
|
||||
default:
|
||||
ss << fname << "("; break;;
|
||||
}
|
||||
@@ -3662,6 +3723,8 @@ static void initParser(const App::DocumentObject *owner)
|
||||
registered_functions["hiddenref"] = FunctionExpression::HIDDENREF;
|
||||
registered_functions["href"] = FunctionExpression::HREF;
|
||||
|
||||
registered_functions["not"] = FunctionExpression::NOT;
|
||||
|
||||
// Aggregates
|
||||
registered_functions["average"] = FunctionExpression::AVERAGE;
|
||||
registered_functions["count"] = FunctionExpression::COUNT;
|
||||
@@ -3669,6 +3732,8 @@ static void initParser(const App::DocumentObject *owner)
|
||||
registered_functions["min"] = FunctionExpression::MIN;
|
||||
registered_functions["stddev"] = FunctionExpression::STDDEV;
|
||||
registered_functions["sum"] = FunctionExpression::SUM;
|
||||
registered_functions["and"] = FunctionExpression::AND;
|
||||
registered_functions["or"] = FunctionExpression::OR;
|
||||
|
||||
has_registered_functions = true;
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@
|
||||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
|
||||
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
|
||||
* if you want the limit (max/min) macros for int types.
|
||||
* if you want the limit (max/min) macros for int types.
|
||||
*/
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
@@ -311,7 +311,7 @@ typedef uint32_t flex_uint32_t;
|
||||
typedef signed char flex_int8_t;
|
||||
typedef short int flex_int16_t;
|
||||
typedef int flex_int32_t;
|
||||
typedef unsigned char flex_uint8_t;
|
||||
typedef unsigned char flex_uint8_t;
|
||||
typedef unsigned short int flex_uint16_t;
|
||||
typedef unsigned int flex_uint32_t;
|
||||
|
||||
@@ -422,10 +422,10 @@ extern FILE *yyin, *yyout;
|
||||
#define EOB_ACT_CONTINUE_SCAN 0
|
||||
#define EOB_ACT_END_OF_FILE 1
|
||||
#define EOB_ACT_LAST_MATCH 2
|
||||
|
||||
|
||||
#define YY_LESS_LINENO(n)
|
||||
#define YY_LINENO_REWIND_TO(ptr)
|
||||
|
||||
|
||||
/* Return all but the first "n" matched characters back to the input stream. */
|
||||
#define yyless(n) \
|
||||
do \
|
||||
@@ -8669,7 +8669,7 @@ extern int yywrap ( void );
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_UNPUT
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef yytext_ptr
|
||||
@@ -8796,7 +8796,7 @@ YY_DECL
|
||||
yy_state_type yy_current_state;
|
||||
char *yy_cp, *yy_bp;
|
||||
int yy_act;
|
||||
|
||||
|
||||
if ( !(yy_init) )
|
||||
{
|
||||
(yy_init) = 1;
|
||||
@@ -9936,7 +9936,7 @@ static int yy_get_next_buffer (void)
|
||||
{
|
||||
yy_state_type yy_current_state;
|
||||
char *yy_cp;
|
||||
|
||||
|
||||
yy_current_state = (yy_start);
|
||||
|
||||
for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
|
||||
@@ -9967,7 +9967,7 @@ static int yy_get_next_buffer (void)
|
||||
static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
|
||||
{
|
||||
int yy_is_jam;
|
||||
char *yy_cp = (yy_c_buf_p);
|
||||
char *yy_cp = (yy_c_buf_p);
|
||||
|
||||
YY_CHAR yy_c = 1;
|
||||
if ( yy_accept[yy_current_state] )
|
||||
@@ -10000,7 +10000,7 @@ static int yy_get_next_buffer (void)
|
||||
|
||||
{
|
||||
int c;
|
||||
|
||||
|
||||
*(yy_c_buf_p) = (yy_hold_char);
|
||||
|
||||
if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
|
||||
@@ -10067,12 +10067,12 @@ static int yy_get_next_buffer (void)
|
||||
|
||||
/** Immediately switch to a different input stream.
|
||||
* @param input_file A readable stream.
|
||||
*
|
||||
*
|
||||
* @note This function does not reset the start condition to @c INITIAL .
|
||||
*/
|
||||
void yyrestart (FILE * input_file )
|
||||
{
|
||||
|
||||
|
||||
if ( ! YY_CURRENT_BUFFER ){
|
||||
yyensure_buffer_stack ();
|
||||
YY_CURRENT_BUFFER_LVALUE =
|
||||
@@ -10085,11 +10085,11 @@ static int yy_get_next_buffer (void)
|
||||
|
||||
/** Switch to a different input buffer.
|
||||
* @param new_buffer The new input buffer.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
|
||||
{
|
||||
|
||||
|
||||
/* TODO. We should be able to replace this entire function body
|
||||
* with
|
||||
* yypop_buffer_state();
|
||||
@@ -10120,7 +10120,7 @@ static int yy_get_next_buffer (void)
|
||||
|
||||
static void yy_load_buffer_state (void)
|
||||
{
|
||||
(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
|
||||
(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
|
||||
(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
|
||||
yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
|
||||
(yy_hold_char) = *(yy_c_buf_p);
|
||||
@@ -10129,13 +10129,13 @@ static void yy_load_buffer_state (void)
|
||||
/** Allocate and initialize an input buffer state.
|
||||
* @param file A readable stream.
|
||||
* @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
|
||||
*
|
||||
*
|
||||
* @return the allocated buffer state.
|
||||
*/
|
||||
YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
|
||||
{
|
||||
YY_BUFFER_STATE b;
|
||||
|
||||
|
||||
b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) );
|
||||
if ( ! b )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
|
||||
@@ -10158,11 +10158,11 @@ static void yy_load_buffer_state (void)
|
||||
|
||||
/** Destroy the buffer.
|
||||
* @param b a buffer created with yy_create_buffer()
|
||||
*
|
||||
*
|
||||
*/
|
||||
void yy_delete_buffer (YY_BUFFER_STATE b )
|
||||
{
|
||||
|
||||
|
||||
if ( ! b )
|
||||
return;
|
||||
|
||||
@@ -10183,7 +10183,7 @@ static void yy_load_buffer_state (void)
|
||||
|
||||
{
|
||||
int oerrno = errno;
|
||||
|
||||
|
||||
yy_flush_buffer( b );
|
||||
|
||||
b->yy_input_file = file;
|
||||
@@ -10199,17 +10199,17 @@ static void yy_load_buffer_state (void)
|
||||
}
|
||||
|
||||
b->yy_is_interactive = 0;
|
||||
|
||||
|
||||
errno = oerrno;
|
||||
}
|
||||
|
||||
/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
|
||||
* @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void yy_flush_buffer (YY_BUFFER_STATE b )
|
||||
{
|
||||
if ( ! b )
|
||||
if ( ! b )
|
||||
return;
|
||||
|
||||
b->yy_n_chars = 0;
|
||||
@@ -10234,11 +10234,11 @@ static void yy_load_buffer_state (void)
|
||||
* the current state. This function will allocate the stack
|
||||
* if necessary.
|
||||
* @param new_buffer The new state.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
|
||||
{
|
||||
if (new_buffer == NULL)
|
||||
if (new_buffer == NULL)
|
||||
return;
|
||||
|
||||
yyensure_buffer_stack();
|
||||
@@ -10264,11 +10264,11 @@ void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
|
||||
|
||||
/** Removes and deletes the top of the stack, if present.
|
||||
* The next element becomes the new top.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void yypop_buffer_state (void)
|
||||
{
|
||||
if (!YY_CURRENT_BUFFER)
|
||||
if (!YY_CURRENT_BUFFER)
|
||||
return;
|
||||
|
||||
yy_delete_buffer(YY_CURRENT_BUFFER );
|
||||
@@ -10288,7 +10288,7 @@ void yypop_buffer_state (void)
|
||||
static void yyensure_buffer_stack (void)
|
||||
{
|
||||
yy_size_t num_to_alloc;
|
||||
|
||||
|
||||
if (!(yy_buffer_stack)) {
|
||||
|
||||
/* First allocation is just for 2 elements, since we don't know if this
|
||||
@@ -10331,13 +10331,13 @@ static void yyensure_buffer_stack (void)
|
||||
/** Setup the input buffer state to scan directly from a user-specified character buffer.
|
||||
* @param base the character buffer
|
||||
* @param size the size in bytes of the character buffer
|
||||
*
|
||||
*
|
||||
* @return the newly allocated buffer state object.
|
||||
*/
|
||||
YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
|
||||
{
|
||||
YY_BUFFER_STATE b;
|
||||
|
||||
|
||||
if ( size < 2 ||
|
||||
base[size-2] != YY_END_OF_BUFFER_CHAR ||
|
||||
base[size-1] != YY_END_OF_BUFFER_CHAR )
|
||||
@@ -10366,14 +10366,14 @@ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
|
||||
/** Setup the input buffer state to scan a string. The next call to yylex() will
|
||||
* scan from a @e copy of @a str.
|
||||
* @param yystr a NUL-terminated string to scan
|
||||
*
|
||||
*
|
||||
* @return the newly allocated buffer state object.
|
||||
* @note If you want to scan bytes that may contain NUL values, then use
|
||||
* yy_scan_bytes() instead.
|
||||
*/
|
||||
YY_BUFFER_STATE yy_scan_string (const char * yystr )
|
||||
{
|
||||
|
||||
|
||||
return yy_scan_bytes( yystr, (int) strlen(yystr) );
|
||||
}
|
||||
|
||||
@@ -10381,7 +10381,7 @@ YY_BUFFER_STATE yy_scan_string (const char * yystr )
|
||||
* scan from a @e copy of @a bytes.
|
||||
* @param yybytes the byte buffer to scan
|
||||
* @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
|
||||
*
|
||||
*
|
||||
* @return the newly allocated buffer state object.
|
||||
*/
|
||||
YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
|
||||
@@ -10390,7 +10390,7 @@ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
|
||||
char *buf;
|
||||
yy_size_t n;
|
||||
int i;
|
||||
|
||||
|
||||
/* Get memory for full buffer, including space for trailing EOB's. */
|
||||
n = (yy_size_t) (_yybytes_len + 2);
|
||||
buf = (char *) yyalloc( n );
|
||||
@@ -10444,16 +10444,16 @@ static void yynoreturn yy_fatal_error (const char* msg )
|
||||
/* Accessor methods (get/set functions) to struct members. */
|
||||
|
||||
/** Get the current line number.
|
||||
*
|
||||
*
|
||||
*/
|
||||
int yyget_lineno (void)
|
||||
{
|
||||
|
||||
|
||||
return yylineno;
|
||||
}
|
||||
|
||||
/** Get the input stream.
|
||||
*
|
||||
*
|
||||
*/
|
||||
FILE *yyget_in (void)
|
||||
{
|
||||
@@ -10461,7 +10461,7 @@ FILE *yyget_in (void)
|
||||
}
|
||||
|
||||
/** Get the output stream.
|
||||
*
|
||||
*
|
||||
*/
|
||||
FILE *yyget_out (void)
|
||||
{
|
||||
@@ -10469,7 +10469,7 @@ FILE *yyget_out (void)
|
||||
}
|
||||
|
||||
/** Get the length of the current token.
|
||||
*
|
||||
*
|
||||
*/
|
||||
int yyget_leng (void)
|
||||
{
|
||||
@@ -10477,7 +10477,7 @@ int yyget_leng (void)
|
||||
}
|
||||
|
||||
/** Get the current token.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
char *yyget_text (void)
|
||||
@@ -10487,18 +10487,18 @@ char *yyget_text (void)
|
||||
|
||||
/** Set the current line number.
|
||||
* @param _line_number line number
|
||||
*
|
||||
*
|
||||
*/
|
||||
void yyset_lineno (int _line_number )
|
||||
{
|
||||
|
||||
|
||||
yylineno = _line_number;
|
||||
}
|
||||
|
||||
/** Set the input stream. This does not discard the current
|
||||
* input buffer.
|
||||
* @param _in_str A readable stream.
|
||||
*
|
||||
*
|
||||
* @see yy_switch_to_buffer
|
||||
*/
|
||||
void yyset_in (FILE * _in_str )
|
||||
@@ -10552,7 +10552,7 @@ static int yy_init_globals (void)
|
||||
/* yylex_destroy is for both reentrant and non-reentrant scanners. */
|
||||
int yylex_destroy (void)
|
||||
{
|
||||
|
||||
|
||||
/* Pop the buffer stack, destroying each element. */
|
||||
while(YY_CURRENT_BUFFER){
|
||||
yy_delete_buffer( YY_CURRENT_BUFFER );
|
||||
@@ -10578,7 +10578,7 @@ int yylex_destroy (void)
|
||||
#ifndef yytext_ptr
|
||||
static void yy_flex_strncpy (char* s1, const char * s2, int n )
|
||||
{
|
||||
|
||||
|
||||
int i;
|
||||
for ( i = 0; i < n; ++i )
|
||||
s1[i] = s2[i];
|
||||
@@ -10603,7 +10603,7 @@ void *yyalloc (yy_size_t size )
|
||||
|
||||
void *yyrealloc (void * ptr, yy_size_t size )
|
||||
{
|
||||
|
||||
|
||||
/* The cast to (char *) in the following accommodates both
|
||||
* implementations that use char* generic pointers, and those
|
||||
* that use void* generic pointers. It works with the latter
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,7 +82,7 @@ input: exp { ScanResult = $1; valueExpressi
|
||||
| unit_exp { ScanResult = $1; unitExpression = true; }
|
||||
;
|
||||
|
||||
unit_num: num unit_exp %prec NUM_AND_UNIT { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::UNIT, $2); }
|
||||
unit_num: num unit_exp %prec NUM_AND_UNIT { $$ = new OperatorExpression(DocumentObject, $1, OperatorExpression::UNIT, $2); }
|
||||
| num us_building_unit num us_building_unit %prec NUM_AND_UNIT { $$ = new OperatorExpression(DocumentObject, new OperatorExpression(DocumentObject, $1, OperatorExpression::UNIT, $2), OperatorExpression::ADD, new OperatorExpression(DocumentObject, $3, OperatorExpression::UNIT, $4));}
|
||||
;
|
||||
|
||||
@@ -112,10 +112,13 @@ num: ONE { $$ = new NumberExpression(Docu
|
||||
|
||||
args: exp { $$.push_back($1); }
|
||||
| range { $$.push_back($1); }
|
||||
| cond { $$.push_back($1); }
|
||||
| args ',' exp { $1.push_back($3); $$ = $1; }
|
||||
| args ';' exp { $1.push_back($3); $$ = $1; }
|
||||
| args ',' range { $1.push_back($3); $$ = $1; }
|
||||
| args ';' range { $1.push_back($3); $$ = $1; }
|
||||
| args ',' cond { $1.push_back($3); $$ = $1; }
|
||||
| args ';' cond { $1.push_back($3); $$ = $1; }
|
||||
;
|
||||
|
||||
range: id_or_cell ':' id_or_cell { $$ = new RangeExpression(DocumentObject, $1, $3); }
|
||||
|
||||
@@ -365,6 +365,9 @@ public:
|
||||
HIDDENREF, // hidden reference that has no dependency check
|
||||
HREF, // deprecated alias of HIDDENREF
|
||||
|
||||
// Non aggregated logical
|
||||
NOT, // logical NOT
|
||||
|
||||
// Aggregates
|
||||
AGGREGATES,
|
||||
|
||||
@@ -375,6 +378,10 @@ public:
|
||||
STDDEV,
|
||||
SUM,
|
||||
|
||||
// Logical aggregates, evaluates to {0,1}
|
||||
AND, // logical AND
|
||||
OR, // logical OR
|
||||
|
||||
// Last one
|
||||
LAST,
|
||||
};
|
||||
|
||||
@@ -219,6 +219,97 @@ class SpreadsheetAggregates(unittest.TestCase):
|
||||
)
|
||||
)
|
||||
|
||||
def test_and(self):
|
||||
self.sheet.set("C20", "4")
|
||||
self.sheet.set("C21", "5")
|
||||
self.sheet.set("C22", "6")
|
||||
self.sheet.set("C23", "0")
|
||||
|
||||
self.sheet.set("A1", "=and(1)")
|
||||
self.sheet.set("A2", "=and(1;2)")
|
||||
self.sheet.set("A3", "=and(1;2;3)")
|
||||
self.sheet.set("A4", "=and(1;2;3;C20)")
|
||||
self.sheet.set("A5", "=and(1;2;3;C20:C22)")
|
||||
self.sheet.set("A6", "=and(1;2;3;C20:C23)")
|
||||
|
||||
self.sheet.set("B1", "=and(0)")
|
||||
self.sheet.set("B2", "=and(0;1;2)")
|
||||
self.sheet.set("B3", "=and(0;1;2;3)")
|
||||
self.sheet.set("B4", "=and(1;2;0)")
|
||||
self.sheet.set("B5", "=and(1;2;3;0)")
|
||||
self.sheet.set("B6", "=and(1;0;2)")
|
||||
self.sheet.set("B6", "=and(1;0;2;0;3)")
|
||||
|
||||
self.doc.recompute()
|
||||
|
||||
self.assertEqual(self.sheet.A1, 1)
|
||||
self.assertEqual(self.sheet.A2, 1)
|
||||
self.assertEqual(self.sheet.A3, 1)
|
||||
self.assertEqual(self.sheet.A4, 1)
|
||||
self.assertEqual(self.sheet.A5, 1)
|
||||
self.assertEqual(self.sheet.A6, 0)
|
||||
|
||||
self.assertEqual(self.sheet.B1, 0)
|
||||
self.assertEqual(self.sheet.B2, 0)
|
||||
self.assertEqual(self.sheet.B3, 0)
|
||||
self.assertEqual(self.sheet.B4, 0)
|
||||
self.assertEqual(self.sheet.B5, 0)
|
||||
self.assertEqual(self.sheet.B6, 0)
|
||||
|
||||
def test_or(self):
|
||||
self.sheet.set("C20", "4")
|
||||
self.sheet.set("C21", "5")
|
||||
self.sheet.set("C22", "6")
|
||||
self.sheet.set("C23", "0")
|
||||
self.sheet.set("C24", "0")
|
||||
|
||||
self.sheet.set("A1", "=or(1)")
|
||||
self.sheet.set("A2", "=or(1;2)")
|
||||
self.sheet.set("A3", "=or(1;2;3)")
|
||||
self.sheet.set("A4", "=or(1;2;3;C20)")
|
||||
self.sheet.set("A5", "=or(1;2;3;C20:C22)")
|
||||
self.sheet.set("A6", "=or(1;2;3;C20:C23)")
|
||||
|
||||
self.sheet.set("B1", "=or(0)")
|
||||
self.sheet.set("B2", "=or(0;1;2)")
|
||||
self.sheet.set("B3", "=or(0;1;2;3)")
|
||||
self.sheet.set("B4", "=or(1;2;0)")
|
||||
self.sheet.set("B5", "=or(1;2;3;0)")
|
||||
self.sheet.set("B6", "=or(1;0;2)")
|
||||
self.sheet.set("B6", "=or(1;0;2;0;3)")
|
||||
|
||||
self.sheet.set("C1", "=or(0)")
|
||||
self.sheet.set("C2", "=or(0;0)")
|
||||
self.sheet.set("C3", "=or(0mm;0;0)")
|
||||
self.sheet.set("C4", "=or(0;0;0;C23)")
|
||||
self.sheet.set("C5", "=or(0;0;0;C23:C24)")
|
||||
self.sheet.set("C6", "=or(C23:C24)")
|
||||
self.sheet.set("C7", "=or(C22:C24)")
|
||||
|
||||
self.doc.recompute()
|
||||
|
||||
self.assertEqual(self.sheet.A1, 1)
|
||||
self.assertEqual(self.sheet.A2, 1)
|
||||
self.assertEqual(self.sheet.A3, 1)
|
||||
self.assertEqual(self.sheet.A4, 1)
|
||||
self.assertEqual(self.sheet.A5, 1)
|
||||
self.assertEqual(self.sheet.A6, 1)
|
||||
|
||||
self.assertEqual(self.sheet.B1, 0)
|
||||
self.assertEqual(self.sheet.B2, 1)
|
||||
self.assertEqual(self.sheet.B3, 1)
|
||||
self.assertEqual(self.sheet.B4, 1)
|
||||
self.assertEqual(self.sheet.B5, 1)
|
||||
self.assertEqual(self.sheet.B6, 1)
|
||||
|
||||
self.assertEqual(self.sheet.C1, 0)
|
||||
self.assertEqual(self.sheet.C2, 0)
|
||||
self.assertEqual(self.sheet.C3, 0)
|
||||
self.assertEqual(self.sheet.C4, 0)
|
||||
self.assertEqual(self.sheet.C5, 0)
|
||||
self.assertEqual(self.sheet.C6, 0)
|
||||
self.assertEqual(self.sheet.C7, 1)
|
||||
|
||||
|
||||
#############################################################################################
|
||||
class SpreadsheetFunction(unittest.TestCase):
|
||||
@@ -547,6 +638,35 @@ class SpreadsheetFunction(unittest.TestCase):
|
||||
self.assertTrue(self.sheet.C27.startswith("ERR: Units must be equal"))
|
||||
self.assertMostlyEqual(self.sheet.D27, Units.Quantity("3 mm"))
|
||||
|
||||
def test_not(self):
|
||||
self.sheet.set("A20", "=not(3)")
|
||||
self.sheet.set("B20", "=not(-3)")
|
||||
self.sheet.set("C20", "=not(-3.5)")
|
||||
self.sheet.set("D20", "=not(3mm)")
|
||||
self.sheet.set("E20", "=not(3.5mm)")
|
||||
self.sheet.set("F20", "=not(-3.5mm)")
|
||||
self.sheet.set("G20", "=not(0)")
|
||||
self.sheet.set("H20", "=not(0mm)")
|
||||
self.sheet.set("I20", "=not(1)")
|
||||
|
||||
self.doc.recompute()
|
||||
|
||||
self.assertEqual(self.sheet.A20, 0)
|
||||
self.assertEqual(self.sheet.B20, 0)
|
||||
self.assertEqual(self.sheet.C20, 0)
|
||||
self.assertEqual(self.sheet.D20, 0)
|
||||
self.assertEqual(self.sheet.E20, 0)
|
||||
self.assertEqual(self.sheet.F20, 0)
|
||||
self.assertEqual(self.sheet.G20, 1)
|
||||
self.assertEqual(self.sheet.H20, 1)
|
||||
self.assertEqual(self.sheet.I20, 0)
|
||||
|
||||
self.sheet.set("J21", f"=not(not({1e-7}))")
|
||||
self.sheet.set("J22", f"=not(not({1e-8}))")
|
||||
self.doc.recompute()
|
||||
self.assertEqual(self.sheet.J21, 1)
|
||||
self.assertEqual(self.sheet.J22, 0)
|
||||
|
||||
|
||||
#############################################################################################
|
||||
class SpreadsheetCases(unittest.TestCase):
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#include "App/ExpressionParser.h"
|
||||
#include "App/ExpressionTokenizer.h"
|
||||
|
||||
// +------------------------------------------------+
|
||||
// | Note: For more expression related tests, see: |
|
||||
// | src/Mod/Spreadsheet/TestSpreadsheet.py |
|
||||
// +------------------------------------------------+
|
||||
|
||||
class Expression: public ::testing::Test
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user