[poincare] Parsing: expression -> expression is always parsed but return

"undef" when the left expression is neither a custom variable or
function nor a unit.
This makes the behaviour consistent on theses expressions:
2 -> a+a
2 -> 2*a
This commit is contained in:
Émilie Feral
2020-04-15 18:15:24 +02:00
parent e4848a7535
commit 56c45d1390
4 changed files with 45 additions and 44 deletions

View File

@@ -276,32 +276,29 @@ void Parser::parseRightwardsArrow(Expression & leftHandSide, Token::Type stoppin
return;
}
// At this point, m_currentToken is Token::RightwardsArrow.
bool parseId = m_nextToken.is(Token::Identifier) && !IsReservedName(m_nextToken.text(), m_nextToken.length());
if (parseId) {
popToken();
// Try parsing a store
Expression rightHandSide;
parseCustomIdentifier(rightHandSide, m_currentToken.text(), m_currentToken.length(), true);
if (m_status != Status::Progress) {
return;
}
if (!m_nextToken.is(Token::EndOfStream)
|| !(rightHandSide.type() == ExpressionNode::Type::Symbol
|| (rightHandSide.type() == ExpressionNode::Type::Function
&& rightHandSide.childAtIndex(0).type() == ExpressionNode::Type::Symbol)))
{
m_status = Status::Error; // Store expects a single symbol or function.
return;
}
const char * tokenName = m_nextToken.text();
size_t tokenNameLength = m_nextToken.length();
/* Right part of the RightwardsArrow are either a Symbol, a Function or units.
* Even undefined function "plouf(x)" should be interpreted as function and
* not as a multiplication. */
m_symbolPlusParenthesesAreFunctions = true;
Expression rightHandSide = parseUntil(stoppingType);
m_symbolPlusParenthesesAreFunctions = false;
if (m_status != Status::Progress) {
return;
}
// Pattern : "-> a" or "-> f(x)" Try parsing a store
if (m_nextToken.is(Token::EndOfStream) &&
(rightHandSide.type() == ExpressionNode::Type::Symbol
|| (rightHandSide.type() == ExpressionNode::Type::Function
&& rightHandSide.childAtIndex(0).type() == ExpressionNode::Type::Symbol)) &&
!IsReservedName(tokenName, tokenNameLength)) {
leftHandSide = Store::Builder(leftHandSide, static_cast<SymbolAbstract&>(rightHandSide));
return;
}
// Try parsing a unit convert
Expression rightHandSide = parseUntil(stoppingType);
if (m_status != Status::Progress) {
return;
}
if (!m_nextToken.is(Token::EndOfStream) || rightHandSide.isUninitialized() || rightHandSide.type() == ExpressionNode::Type::Store || rightHandSide.type() == ExpressionNode::Type::UnitConvert) {
if (!m_nextToken.is(Token::EndOfStream) || rightHandSide.isUninitialized() || rightHandSide.type() == ExpressionNode::Type::Store || rightHandSide.type() == ExpressionNode::Type::UnitConvert || rightHandSide.type() == ExpressionNode::Type::Equal) {
m_status = Status::Error; // UnitConvert expect a unit on the right.
return;
}
@@ -451,19 +448,19 @@ void Parser::parseSpecialIdentifier(Expression & leftHandSide) {
}
}
void Parser::parseCustomIdentifier(Expression & leftHandSide, const char * name, size_t length, bool symbolPlusParenthesesAreFunctions) {
void Parser::parseCustomIdentifier(Expression & leftHandSide, const char * name, size_t length) {
if (length >= SymbolAbstract::k_maxNameSize) {
m_status = Status::Error; // Identifier name too long.
return;
}
bool poppedParenthesisIsSystem = false;
/* If symbolPlusParenthesesAreFunctions is false, check the context: if the
/* If m_symbolPlusParenthesesAreFunctions is false, check the context: if the
* identifier does not already exist as a function, interpret it as a symbol,
* even if there are parentheses afterwards. */
Context::SymbolAbstractType idType = Context::SymbolAbstractType::None;
if (m_context != nullptr && !symbolPlusParenthesesAreFunctions) {
if (m_context != nullptr && !m_symbolPlusParenthesesAreFunctions) {
idType = m_context->expressionTypeForIdentifier(name, length);
if (idType != Context::SymbolAbstractType::Function) {
leftHandSide = Symbol::Builder(name, length);
@@ -511,7 +508,7 @@ void Parser::parseIdentifier(Expression & leftHandSide, Token::Type stoppingType
} else if (IsSpecialIdentifierName(m_currentToken.text(), m_currentToken.length())) {
parseSpecialIdentifier(leftHandSide);
} else {
parseCustomIdentifier(leftHandSide, m_currentToken.text(), m_currentToken.length(), false);
parseCustomIdentifier(leftHandSide, m_currentToken.text(), m_currentToken.length());
}
isThereImplicitMultiplication();
}

View File

@@ -26,7 +26,8 @@ public:
m_tokenizer(text),
m_currentToken(Token(Token::Undefined)),
m_nextToken(m_tokenizer.popToken()),
m_pendingImplicitMultiplication(false) {}
m_pendingImplicitMultiplication(false),
m_symbolPlusParenthesesAreFunctions(false) {}
Expression parse();
Status getStatus() const { return m_status; }
@@ -75,7 +76,7 @@ private:
void parseReservedFunction(Expression & leftHandSide, const Expression::FunctionHelper * const * functionHelper);
void parseSpecialIdentifier(Expression & leftHandSide);
void parseSequence(Expression & leftHandSide, const char name, Token::Type leftDelimiter1, Token::Type rightDelimiter1, Token::Type leftDelimiter2, Token::Type rightDelimiter2);
void parseCustomIdentifier(Expression & leftHandSide, const char * name, size_t length, bool symbolPlusParenthesesAreFunctions);
void parseCustomIdentifier(Expression & leftHandSide, const char * name, size_t length);
void defaultParseLeftParenthesis(bool isSystemParenthesis, Expression & leftHandSide, Token::Type stoppingType);
// Data members
@@ -88,6 +89,7 @@ private:
Token m_currentToken;
Token m_nextToken;
bool m_pendingImplicitMultiplication;
bool m_symbolPlusParenthesesAreFunctions;
// The array of reserved functions' helpers
static constexpr const Expression::FunctionHelper * s_reservedFunctions[] = {

View File

@@ -239,6 +239,7 @@ QUIZ_CASE(poincare_parsing_parse) {
assert_text_not_parsable("t0000000");
assert_text_not_parsable("[[t0000000[");
assert_text_not_parsable("0→x=0");
assert_text_not_parsable("0→3=0");
assert_text_not_parsable("0=0→x");
assert_text_not_parsable("1ᴇ2ᴇ3");
assert_text_not_parsable("0b001112");
@@ -428,9 +429,7 @@ QUIZ_CASE(poincare_parsing_parse_store) {
assert_text_not_parsable("1→\1"); // UnknownX
assert_text_not_parsable("1→\2"); // UnknownN
assert_text_not_parsable("1→acos");
assert_text_not_parsable("1→f(2)");
assert_text_not_parsable("1→f(f)");
assert_text_not_parsable("3→f(g(4))");
}
QUIZ_CASE(poincare_parsing_parse_unit_convert) {
@@ -438,20 +437,6 @@ QUIZ_CASE(poincare_parsing_parse_unit_convert) {
assert_parsed_expression_is("1→_m", UnitConvert::Builder(BasedInteger::Builder(1), meter));
Expression kilometer = Expression::Parse("_km", nullptr);
assert_parsed_expression_is("1→_m/_km", UnitConvert::Builder(BasedInteger::Builder(1), Division::Builder(meter, kilometer)));
assert_simplify("_m→a", Radian, Real);
assert_simplify("_m→b", Radian, Real);
assert_text_not_parsable("1_km→a×b");
assert_simplify("2→a");
assert_text_not_parsable("3_m→a×_km");
assert_simplify("2→f(x)");
assert_text_not_parsable("3_m→f(2)×_km");
// Clean the storage for other tests
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("b.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
}
QUIZ_CASE(poincare_parsing_implicit_multiplication) {

View File

@@ -1022,6 +1022,9 @@ QUIZ_CASE(poincare_simplification_unit_convert) {
assert_parsed_expression_simplify_to("4×_N×3_N×2_N→_N^3", "24×_N^3");
assert_parsed_expression_simplify_to("1→2", Undefined::Name());
assert_parsed_expression_simplify_to("1→a+a", Undefined::Name());
assert_parsed_expression_simplify_to("1→f(2)", Undefined::Name());
assert_parsed_expression_simplify_to("1→f(g(4))", Undefined::Name());
assert_parsed_expression_simplify_to("1→u(n)", Undefined::Name());
assert_parsed_expression_simplify_to("1→u(n+1)", Undefined::Name());
assert_parsed_expression_simplify_to("1→v(n)", Undefined::Name());
@@ -1040,6 +1043,20 @@ QUIZ_CASE(poincare_simplification_unit_convert) {
assert_parsed_expression_simplify_to("1→3_m", Undefined::Name());
assert_parsed_expression_simplify_to("4→_km/_m", Undefined::Name());
assert_parsed_expression_simplify_to("3×_min→_s+1-1", Undefined::Name());
assert_simplify("_m→a", Radian, Real);
assert_simplify("_m→b", Radian, Real);
assert_parsed_expression_simplify_to("1_km→a×b", Undefined::Name());
assert_simplify("2→a");
assert_parsed_expression_simplify_to("3_m→a×_km", Undefined::Name());
assert_simplify("2→f(x)");
assert_parsed_expression_simplify_to("3_m→f(2)×_km", Undefined::Name());
// Clean the storage for other tests
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("b.exp").destroy();
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
}
QUIZ_CASE(poincare_simplification_complex_format) {