diff --git a/poincare/include/poincare/addition.h b/poincare/include/poincare/addition.h index 6408654e4..1230e230e 100644 --- a/poincare/include/poincare/addition.h +++ b/poincare/include/poincare/addition.h @@ -26,7 +26,6 @@ public: Type type() const override { return Type::Addition; } int polynomialDegree(Context * context, const char * symbolName) const override; int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[]) const override; - bool childNeedsUserParentheses(const Expression & child) const override; // Evaluation template static Complex compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat) { return Complex::Builder(c+d); } diff --git a/poincare/include/poincare/n_ary_expression.h b/poincare/include/poincare/n_ary_expression.h index 064d30a24..9c56bc36f 100644 --- a/poincare/include/poincare/n_ary_expression.h +++ b/poincare/include/poincare/n_ary_expression.h @@ -20,6 +20,9 @@ public: } void eraseNumberOfChildren() override { m_numberOfChildren = 0; } + // Properties + bool childNeedsUserParentheses(const Expression & child) const override; + // Comparison typedef int (*ExpressionOrder)(const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted); diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index dc7a2d7ff..05eb97101 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -29,17 +29,6 @@ int AdditionNode::getPolynomialCoefficients(Context * context, const char * symb } // Layout -bool AdditionNode::childNeedsUserParentheses(const Expression & child) const { - if (((child.isNumber() && static_cast(child).sign() == Sign::Negative) - || child.type() == Type::Opposite) - && child.node() != childAtIndex(0)) { - return true; - } - if (child.type() == Type::Conjugate) { - return childNeedsUserParentheses(child.childAtIndex(0)); - } - return false; -} Layout AdditionNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { return LayoutHelper::Infix(Addition(this), floatDisplayMode, numberOfSignificantDigits, "+"); diff --git a/poincare/src/factorial.cpp b/poincare/src/factorial.cpp index 7d2cafddc..b6d5acf15 100644 --- a/poincare/src/factorial.cpp +++ b/poincare/src/factorial.cpp @@ -23,7 +23,9 @@ bool FactorialNode::childNeedsUserParentheses(const Expression & child) const { if (child.isNumber() && static_cast(child).sign() == Sign::Negative) { return true; } - + if (child.type() == Type::Conjugate) { + return childNeedsUserParentheses(child.childAtIndex(0)); + } Type types[] = {Type::Subtraction, Type::Opposite, Type::Multiplication, Type::Addition}; return child.isOfType(types, 4); } diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index ed1d34879..6f50d898d 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -51,17 +51,9 @@ int MultiplicationNode::getPolynomialCoefficients(Context * context, const char } bool MultiplicationNode::childNeedsUserParentheses(const Expression & child) const { - if ((child.isNumber() && static_cast(child).sign() == Sign::Negative) - || child.type() == ExpressionNode::Type::Opposite) - { - if (child.node() == childAtIndex(0)) { - return false; - } + if (NAryExpressionNode::childNeedsUserParentheses(child)) { return true; } - if (child.type() == Type::Conjugate) { - return childNeedsUserParentheses(child.childAtIndex(0)); - } Type types[] = {Type::Subtraction, Type::Addition}; return child.isOfType(types, 2); } diff --git a/poincare/src/n_ary_expression.cpp b/poincare/src/n_ary_expression.cpp index 430e945a6..2bf79507f 100644 --- a/poincare/src/n_ary_expression.cpp +++ b/poincare/src/n_ary_expression.cpp @@ -1,4 +1,5 @@ #include +#include extern "C" { #include #include @@ -6,6 +7,22 @@ extern "C" { namespace Poincare { +bool NAryExpressionNode::childNeedsUserParentheses(const Expression & child) const { + /* Expressions like "-2" require parentheses in Addition/Multiplication except + * when they are the first operand. */ + if (((child.isNumber() && static_cast(child).sign() == Sign::Negative) + || child.type() == Type::Opposite) + /* We use "hasAncestor" instead of "==" because child might not be the + * direct child of the addition/multiplication [e.g. +(conj(-2), 3)] */ + && !child.node()->hasAncestor(childAtIndex(0), true)) { + return true; + } + if (child.type() == Type::Conjugate) { + return childNeedsUserParentheses(child.childAtIndex(0)); + } + return false; +} + void NAryExpressionNode::sortChildrenInPlace(ExpressionOrder order, Context * context, bool canSwapMatrices, bool canBeInterrupted) { Expression reference(this); for (int i = reference.numberOfChildren()-1; i > 0; i--) { diff --git a/poincare/src/opposite.cpp b/poincare/src/opposite.cpp index bdcba9716..b5e33c77c 100644 --- a/poincare/src/opposite.cpp +++ b/poincare/src/opposite.cpp @@ -37,6 +37,9 @@ bool OppositeNode::childNeedsUserParentheses(const Expression & child) const { if (child.isNumber() && static_cast(child).sign() == Sign::Negative) { return true; } + if (child.type() == Type::Conjugate) { + return childNeedsUserParentheses(child.childAtIndex(0)); + } Type types[] = {Type::Addition, Type::Subtraction, Type::Opposite}; return child.isOfType(types, 3); } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 2c9abb138..acebed1cd 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -100,7 +100,9 @@ bool PowerNode::isReal(Context * context) const { } bool PowerNode::childNeedsUserParentheses(const Expression & child) const { - if (child.node() == childAtIndex(0)) { + /* We use "hasAncestor" instead of "==" because child might not be the direct + * child of thepower [e.g. ^(conj(+(A,B), C)] */ + if (child.node()->hasAncestor(childAtIndex(0), true)) { /* ^(-2.3, 4) --> (-2.3)^{4} * ^(2/3, 4) --> (2/3)^{4} */ @@ -108,6 +110,9 @@ bool PowerNode::childNeedsUserParentheses(const Expression & child) const { || (child.type() == Type::Rational && !static_cast(child).isInteger())) { return true; } + if (child.type() == Type::Conjugate) { + return childNeedsUserParentheses(child.childAtIndex(0)); + } // ^(2+3,4) --> (2+3)^{4} Type types[] = {Type::Power, Type::Subtraction, Type::Opposite, Type::Multiplication, Type::Division, Type::Addition}; return child.isOfType(types, 6); diff --git a/poincare/src/subtraction.cpp b/poincare/src/subtraction.cpp index 1233cd5d2..72d16d1b0 100644 --- a/poincare/src/subtraction.cpp +++ b/poincare/src/subtraction.cpp @@ -24,7 +24,10 @@ int SubtractionNode::polynomialDegree(Context * context, const char * symbolName // Private bool SubtractionNode::childNeedsUserParentheses(const Expression & child) const { - if (child.node() == childAtIndex(0)) { + /* First operand of a subtraction never requires parentheses. + * We use "hasAncestor" instead of "==" because child might not be the direct + * child of the subtraction [e.g. 'conj(-2) - 3' ] */ + if (child.node()->hasAncestor(childAtIndex(0), true)) { return false; } if (child.isNumber() && static_cast(child).sign() == Sign::Negative) { diff --git a/poincare/test/parsing.cpp b/poincare/test/parsing.cpp index 36af8a1d8..560714caa 100644 --- a/poincare/test/parsing.cpp +++ b/poincare/test/parsing.cpp @@ -437,4 +437,15 @@ QUIZ_CASE(poincare_parsing_adding_missing_parentheses) { assert_parsed_expression_with_user_parentheses_is("--2", Opposite::Builder(Parenthesis::Builder(Opposite::Builder(Rational::Builder(2))))); assert_parsed_expression_with_user_parentheses_is("\u00122/3\u0013^2", Power::Builder(Parenthesis::Builder(Division::Builder(Rational::Builder(2), Rational::Builder(3))), Rational::Builder(2))); assert_parsed_expression_with_user_parentheses_is("log(1+-2)", CommonLogarithm::Builder(Addition::Builder(Rational::Builder(1),Parenthesis::Builder(Opposite::Builder(Rational::Builder(2)))))); + + // Conjugate expressions + assert_parsed_expression_with_user_parentheses_is("conj(-3)+2", Addition::Builder(Conjugate::Builder(Opposite::Builder(Rational::Builder(3))), Rational::Builder(2))); + assert_parsed_expression_with_user_parentheses_is("2+conj(-3)", Addition::Builder(Rational::Builder(2), Parenthesis::Builder(Conjugate::Builder(Opposite::Builder(Rational::Builder(3)))))); + assert_parsed_expression_with_user_parentheses_is("conj(-3)×2", Multiplication::Builder(Conjugate::Builder(Opposite::Builder(Rational::Builder(3))), Rational::Builder(2))); + assert_parsed_expression_with_user_parentheses_is("2×conj(-3)", Multiplication::Builder(Rational::Builder(2), Parenthesis::Builder(Conjugate::Builder(Opposite::Builder(Rational::Builder(3)))))); + assert_parsed_expression_with_user_parentheses_is("conj(-3)-2", Subtraction::Builder(Conjugate::Builder(Opposite::Builder(Rational::Builder(3))), Rational::Builder(2))); + assert_parsed_expression_with_user_parentheses_is("2-conj(-3)", Subtraction::Builder(Rational::Builder(2), Parenthesis::Builder(Conjugate::Builder(Opposite::Builder(Rational::Builder(3)))))); + assert_parsed_expression_with_user_parentheses_is("conj(2+3)^2", Power::Builder(Parenthesis::Builder(Conjugate::Builder(Addition::Builder(Rational::Builder(2), Rational::Builder(3)))), Rational::Builder(2))); + assert_parsed_expression_with_user_parentheses_is("-conj(2+3)", Opposite::Builder(Parenthesis::Builder(Conjugate::Builder(Addition::Builder(Rational::Builder(2), Rational::Builder(3)))))); + assert_parsed_expression_with_user_parentheses_is("conj(2+3)!", Factorial::Builder(Parenthesis::Builder(Conjugate::Builder(Addition::Builder(Rational::Builder(2), Rational::Builder(3)))))); }