/** * Visits the given ASTNode, translating the piecewise function * to the much simpler 'x % y' format. */ void L3FormulaFormatter_visitModulo ( const ASTNode_t *parent, const ASTNode_t *node, StringBuffer_t *sb, const L3ParserSettings_t *settings ) { unsigned int group = L3FormulaFormatter_isGrouped(parent, node, settings); const ASTNode_t* subnode = ASTNode_getLeftChild(node); if (group) { StringBuffer_appendChar(sb, '('); } //Get x and y from the first child of the piecewise function, // then the first child of that (times), and the first child // of that (minus). L3FormulaFormatter_visit ( subnode, ASTNode_getLeftChild(subnode), sb, settings); StringBuffer_appendChar(sb, ' '); StringBuffer_appendChar(sb, '%'); StringBuffer_appendChar(sb, ' '); subnode = ASTNode_getRightChild(subnode); L3FormulaFormatter_visit ( node, ASTNode_getLeftChild(subnode), sb, settings); if (group) { StringBuffer_appendChar(sb, ')'); } }
/** * Visits the given ASTNode as a unary minus. For this node only the * traversal is preorder. */ void L3FormulaFormatter_visitUMinus ( const ASTNode_t *parent, const ASTNode_t *node, StringBuffer_t *sb, const L3ParserSettings_t *settings ) { //Unary minus is *not* the highest precedence, since it is superceded by 'power' unsigned int group; //If we are supposed to collapse minuses, do so. if (L3ParserSettings_getParseCollapseMinus(settings)) { if (ASTNode_getNumChildren(node) == 1 && ASTNode_isUMinus(ASTNode_getLeftChild(node))) { L3FormulaFormatter_visit(parent, ASTNode_getLeftChild(ASTNode_getLeftChild(node)), sb, settings); return; } } group = L3FormulaFormatter_isGrouped(parent, node, settings); if (group) { StringBuffer_appendChar(sb, '('); } StringBuffer_appendChar(sb, '-'); L3FormulaFormatter_visit ( node, ASTNode_getLeftChild(node), sb, settings); if (group) { StringBuffer_appendChar(sb, ')'); } }
/** * Visits the given ASTNode and continues the inorder traversal. */ void L3FormulaFormatter_visitOther ( const ASTNode_t *parent, const ASTNode_t *node, StringBuffer_t *sb, const L3ParserSettings_t *settings ) { unsigned int numChildren = ASTNode_getNumChildren(node); unsigned int group = L3FormulaFormatter_isGrouped(parent, node, settings); unsigned int n; if (group) { StringBuffer_appendChar(sb, '('); } if (numChildren == 0) { L3FormulaFormatter_format(sb, node, settings); } else if (numChildren == 1) { //I believe this would only be called for invalid ASTNode setups, // but this could in theory occur. This is the safest // behavior I can think of. L3FormulaFormatter_format(sb, node, settings); StringBuffer_appendChar(sb, '('); L3FormulaFormatter_visit( node, ASTNode_getChild(node, 0), sb, settings); StringBuffer_appendChar(sb, ')'); } else { L3FormulaFormatter_visit( node, ASTNode_getChild(node, 0), sb, settings); for (n = 1; n < numChildren; n++) { L3FormulaFormatter_format(sb, node, settings); L3FormulaFormatter_visit( node, ASTNode_getChild(node, n), sb, settings); } } if (group) { StringBuffer_appendChar(sb, ')'); } }
/** * Visits the given ASTNode as a unary not. */ void L3FormulaFormatter_visitUNot ( const ASTNode_t *parent, const ASTNode_t *node, StringBuffer_t *sb, const L3ParserSettings_t *settings ) { //Unary not is also not the highest precedence, since it is superceded by 'power' unsigned int group = L3FormulaFormatter_isGrouped(parent, node, settings); if (group) { StringBuffer_appendChar(sb, '('); } StringBuffer_appendChar(sb, '!'); L3FormulaFormatter_visit ( node, ASTNode_getLeftChild(node), sb, settings); if (group) { StringBuffer_appendChar(sb, ')'); } }
END_TEST START_TEST (test_L3FormulaFormatter_accessWithNULL) { // ensure we survive NULL arguments L3FormulaFormatter_format(NULL, NULL, NULL); L3FormulaFormatter_formatFunction(NULL, NULL, NULL); L3FormulaFormatter_formatOperator(NULL, NULL); L3FormulaFormatter_visit(NULL, NULL, NULL, NULL); L3FormulaFormatter_visitFunction(NULL, NULL, NULL, NULL); L3FormulaFormatter_visitLog10(NULL, NULL, NULL, NULL); L3FormulaFormatter_visitOther(NULL, NULL, NULL, NULL); L3FormulaFormatter_visitSqrt(NULL, NULL, NULL, NULL); L3FormulaFormatter_visitUMinus(NULL, NULL, NULL, NULL); fail_unless( L3FormulaFormatter_isFunction(NULL, NULL) == 0 ); fail_unless( L3FormulaFormatter_isGrouped(NULL, NULL, NULL) == 0 ); fail_unless( SBML_formulaToL3String(NULL) == NULL ); }
END_TEST START_TEST (test_L3FormulaFormatter_isGrouped) { ASTNode_t *p = ASTNode_create(); ASTNode_t *c; /** Empty parent, p is the root of the tree. **/ fail_unless( L3FormulaFormatter_isGrouped(NULL, p, NULL) == 0, NULL ); ASTNode_free(p); /** "1 + 2 * 3" **/ p = SBML_parseL3Formula("1 + 2 * 3"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); /** "(1 + 2) * 3" **/ p = SBML_parseL3Formula("(1 + 2) * 3"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 1, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); /** * "1 + (2 * 3)": * * In this case, explicit grouping is not needed due to operator * precedence rules. */ p = SBML_parseL3Formula("1 + (2 * 3)"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); /** * "foo(1 + 2, 2 * 3)": * * The parent node foo has higher precedence than its children, but * grouping is not nescessary since foo is a function. */ p = SBML_parseL3Formula("foo(1 + 2, 2 * 3)"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); /** * "(a / b) * c": * * In this case, explicit grouping is not needed due to associativity * rules. */ p = SBML_parseL3Formula("(a / b) * c"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); /** * "a / (b * c)": * * In this case, explicit grouping is needed. The operators / and * have * the same precedence, but the parenthesis modifies the associativity. */ p = SBML_parseL3Formula("a / (b * c)"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 1, NULL ); ASTNode_free(p); /** * "a - (b - c)": * * Rainer Machne reported that the above parsed correctly, but was not * formatted correctly. * * The bug was in L3FormulaFormatter_isGrouped(). While it was correctly * handling parent and child ASTNodes of the same precedence, it was not * handling the special subcase where parent and child nodes were the * same operator. For grouping, this only matters for the subtraction * and division operators, as they are not associative. * * An exhaustive set of eight tests follow. */ p = SBML_parseL3Formula("a - (b - c)"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 1, NULL ); ASTNode_free(p); p = SBML_parseL3Formula("a - b - c"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); p = SBML_parseL3Formula("a + (b + c)"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 1, NULL ); ASTNode_free(p); p = SBML_parseL3Formula("a + b + c"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); p = SBML_parseL3Formula("a * (b * c)"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 1, NULL ); ASTNode_free(p); p = SBML_parseL3Formula("a * b * c"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); p = SBML_parseL3Formula("a / (b / c)"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 1, NULL ); ASTNode_free(p); p = SBML_parseL3Formula("a / b / c"); c = ASTNode_getLeftChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); c = ASTNode_getRightChild(p); fail_unless( L3FormulaFormatter_isGrouped(p, c, NULL) == 0, NULL ); ASTNode_free(p); }