diff --git a/lib/expression/node/AssignmentNode.js b/lib/expression/node/AssignmentNode.js index 49ce5a8a4..90fb93373 100644 --- a/lib/expression/node/AssignmentNode.js +++ b/lib/expression/node/AssignmentNode.js @@ -92,12 +92,24 @@ AssignmentNode.prototype.toString = function() { * @return {String} */ AssignmentNode.prototype.toTex = function() { + var precedence = operators.getPrecedence(this); + var exprPrecedence = operators.getPrecedence(this.expr); + + var expr = this.expr.toTex(); + if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) { + //adds visible round brackets + expr = latex.addBraces(expr, true); + } + else { + //adds (invisible) curly braces + expr = latex.addBraces(expr, false); + } + var brace; if (this.expr instanceof ArrayNode) { brace = ['\\mathbf{', '}']; } - return latex.addBraces(latex.toSymbol(this.name), brace) + '=' + - latex.addBraces(this.expr.toTex()); + return latex.addBraces(latex.toSymbol(this.name), brace) + '=' + expr; }; -module.exports = AssignmentNode; \ No newline at end of file +module.exports = AssignmentNode; diff --git a/lib/expression/node/FunctionAssignmentNode.js b/lib/expression/node/FunctionAssignmentNode.js index c2a8d5477..629360ca1 100644 --- a/lib/expression/node/FunctionAssignmentNode.js +++ b/lib/expression/node/FunctionAssignmentNode.js @@ -113,9 +113,22 @@ FunctionAssignmentNode.prototype.toString = function() { * @return {String} str */ FunctionAssignmentNode.prototype.toTex = function() { - return this.name + - latex.addBraces(this.params.map(latex.toSymbol).join(', '), true) + '=' + - latex.addBraces(this.expr.toTex()); + var precedence = operators.getPrecedence(this); + var exprPrecedence = operators.getPrecedence(this.expr); + + var expr = this.expr.toTex(); + if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) { + //adds visible round brackets + expr = latex.addBraces(expr, true); + } + else { + //add (invisible) curly braces + expr = latex.addBraces(expr, false); + } + + return latex.toFunction(this.name) + + latex.addBraces(this.params.map(latex.toSymbol).join(', '), true) + '=' + + expr; }; module.exports = FunctionAssignmentNode; diff --git a/lib/expression/node/OperatorNode.js b/lib/expression/node/OperatorNode.js index 8398fc17b..5c97904e4 100644 --- a/lib/expression/node/OperatorNode.js +++ b/lib/expression/node/OperatorNode.js @@ -239,98 +239,53 @@ OperatorNode.prototype.toString = function() { * @return {String} str */ OperatorNode.prototype.toTex = function() { - var args = this.args, - mop = latex.toOperator(this.op), - lp = args[0], - rp = args[1]; + var args = this.args; + var parens = calculateNecessaryParentheses(this, args); + var op = latex.toOperator(this.op); //operator - switch (args.length) { - case 1: - var operand = lp.toTex(); - switch (this.op) { - case '-': //unary minus needs brackets around '-' and '+' - if (lp instanceof OperatorNode && (lp.op === '-' || lp.op === '+')) { - return this.op + latex.addBraces(operand, true); - } - case '+': - return this.op + operand; - break; - default: // fox example '5!' - if (lp instanceof OperatorNode) { - return latex.addBraces(operand, true) + this.op; - } - return operand + this.op; - } + switch (args.length) { + case 1: //unary operators + var assoc = operators.getAssociativity(this); - case 2: // for example '2+3' - var lhs = lp.toTex(), - lhb = false, - rhs = rp.toTex(), - rhb = false, - lop = '', - rop = ''; + var operand = args[0].toTex(); + if (parens[0]) { + operand = latex.addBraces(operand, true); + } - switch (this.op) { - case '/': - lop = mop; - mop = ''; + if (assoc === 'right') { //prefix operator + return op + operand; + } + else if (assoc === 'left') { //postfix operator + return operand + op; + } - break; + //fall back to postfix + return operand + op; - case '*': - if (lp instanceof OperatorNode) { - if (lp.op === '+' || lp.op === '-') { - lhb = true; - } - } + case 2: //binary operators + var lhs = args[0]; //left hand side + //reminder: if parens[0] is false, this puts it in curly braces + var lhsTex = latex.addBraces(lhs.toTex(), parens[0]); + var rhs = args[1]; //right hand side + var rhsTex = latex.addBraces(rhs.toTex(), parens[1]); - if (rp instanceof OperatorNode) { - if (rp.op === '+' || rp.op === '-' || rp.op === '*') { - rhb = true; - } - } + switch (this.getIdentifier()) { + case 'OperatorNode:divide': + //op contains '\\frac' at this point + return op + lhsTex + rhsTex; - if ((lp instanceof ConstantNode || lp instanceof OperatorNode) && - (rp instanceof ConstantNode || rp instanceof OperatorNode)) { - mop = ' \\cdot '; - } - else { - mop = ' \\, '; - } + case 'OperatorNode:to': + rhsTex = latex.toUnit(rhs.toTex()); + rhsTex = latex.addBraces(rhsTex, parens[1]); + break; + } + return lhsTex + ' ' + op + ' ' + rhsTex; - break; - - case '-': - if (rp instanceof OperatorNode) { - if (rp.op === '+' | rp.op === '-' ) { - rhb = true; - } - } - break; - - case '^': - if (lp instanceof OperatorNode || lp instanceof FunctionNode) { - lhb = true; - } - else if (lp instanceof SymbolNode) { - lhb = null; - } - - break; - - case 'to': - rhs = latex.toUnit(rhs, true); - break; - } - - lhs = latex.addBraces(lhs, lhb); - rhs = latex.addBraces(rhs, rhb); - - return lop + lhs + mop + rhs + rop; - - default: // this should not occur. format as a function call - return mop + '(' + this.args.map(latex.toSymbol).join(', ') + ')'; - } + default: + //fall back to formatting as a function call + var argumentList = this.args.map(latex.toSymbol).join(', '); + return latex.toFunction(this.fn) + latex.addBraces(argumentList, true); + } }; /** diff --git a/test/expression/node/AssignmentNode.test.js b/test/expression/node/AssignmentNode.test.js index 94c733ac6..8c555081d 100644 --- a/test/expression/node/AssignmentNode.test.js +++ b/test/expression/node/AssignmentNode.test.js @@ -216,4 +216,13 @@ describe('AssignmentNode', function() { assert.equal(n.toTex(), '{b}={3}'); }); + it ('should LaTeX an AssignmentNode containing an AssignmentNode', function () { + var a = new ConstantNode(2); + var b = new AssignmentNode('a', a); + + var n = new AssignmentNode('b', b); + + assert.equal(n.toTex(), '{b}=\\left({{a}={2}}\\right)'); + }); + }); diff --git a/test/expression/node/FunctionAssignmentNode.test.js b/test/expression/node/FunctionAssignmentNode.test.js index 6f3cc3c22..9b6209ea7 100644 --- a/test/expression/node/FunctionAssignmentNode.test.js +++ b/test/expression/node/FunctionAssignmentNode.test.js @@ -195,7 +195,15 @@ describe('FunctionAssignmentNode', function() { var p = new OperatorNode('^', 'pow', [o, a]); var n = new FunctionAssignmentNode('f', ['x'], p); - assert.equal(n.toTex(), 'f\\left({x}\\right)={\\left({\\frac{x}{2}}\\right)^{2}}'); + assert.equal(n.toTex(), 'f\\left({x}\\right)={\\left({\\frac{x}{2}}\\right) ^ {2}}'); }); + it ('should LaTeX a FunctionAssignmentNode containing an AssignmentNode', function () { + var a = new ConstantNode(2); + + var n1 = new AssignmentNode('a', a); + var n = new FunctionAssignmentNode('f', ['x'], n1); + + assert.equal(n.toTex(), 'f\\left({x}\\right)=\\left({{a}={2}}\\right)'); + }); }); diff --git a/test/expression/node/FunctionNode.test.js b/test/expression/node/FunctionNode.test.js index fe6e118ea..d5e743746 100644 --- a/test/expression/node/FunctionNode.test.js +++ b/test/expression/node/FunctionNode.test.js @@ -278,7 +278,7 @@ describe('FunctionNode', function() { var o = new OperatorNode('+', 'add', [c1, c2]); var n3 = new FunctionNode('permutations', [o]); - assert.equal(n3.toTex(), '{\\left({4}+{5}\\right)!}'); + assert.equal(n3.toTex(), '{\\left({4} + {5}\\right)!}'); }); it ('should have an identifier', function () { diff --git a/test/expression/node/OperatorNode.test.js b/test/expression/node/OperatorNode.test.js index 607e94122..07ded28f0 100644 --- a/test/expression/node/OperatorNode.test.js +++ b/test/expression/node/OperatorNode.test.js @@ -276,7 +276,7 @@ describe('OperatorNode', function() { var c = new ConstantNode(4); var n = new OperatorNode('+', 'add', [a, b]); - assert.equal(n.toTex(), '{2}+{3}'); + assert.equal(n.toTex(), '{2} + {3}'); }); it ('should LaTeX an OperatorNode with factorial', function () { @@ -298,8 +298,8 @@ describe('OperatorNode', function() { var n2= new OperatorNode('!', 'factorial', [add] ); var n3= new OperatorNode('!', 'factorial', [mult] ); var n4= new OperatorNode('!', 'factorial', [div] ); - assert.equal(n1.toTex(), '\\left({{2}-{3}}\\right)!'); - assert.equal(n2.toTex(), '\\left({{2}+{3}}\\right)!'); + assert.equal(n1.toTex(), '\\left({{2} - {3}}\\right)!'); + assert.equal(n2.toTex(), '\\left({{2} + {3}}\\right)!'); assert.equal(n3.toTex(), '\\left({{2} \\cdot {3}}\\right)!'); assert.equal(n4.toTex(), '\\left({\\frac{2}{3}}\\right)!'); }); @@ -316,8 +316,8 @@ describe('OperatorNode', function() { var n3 = new OperatorNode('-', 'unaryMinus', [add]); assert.equal(n1.toTex(), '-2'); - assert.equal(n2.toTex(), '-\\left({{2}-{3}}\\right)'); - assert.equal(n3.toTex(), '-\\left({{2}+{3}}\\right)'); + assert.equal(n2.toTex(), '-\\left({{2} - {3}}\\right)'); + assert.equal(n3.toTex(), '-\\left({{2} + {3}}\\right)'); }); it ('should LaTeX an OperatorNode that subtracts an OperatorNode', function() { @@ -331,13 +331,13 @@ describe('OperatorNode', function() { var n1 = new OperatorNode('-', 'subtract', [a, sub]); var n2 = new OperatorNode('-', 'subtract', [a, add]); - assert.equal(n1.toTex(), '{1}-\\left({{2}-{3}}\\right)'); - assert.equal(n2.toTex(), '{1}-\\left({{2}+{3}}\\right)'); + assert.equal(n1.toTex(), '{1} - \\left({{2} - {3}}\\right)'); + assert.equal(n2.toTex(), '{1} - \\left({{2} + {3}}\\right)'); }); it ('should LaTeX an OperatorNode with zero arguments', function () { var n = new OperatorNode('foo', 'foo', []); - assert.equal(n.toTex(), 'foo()'); + assert.equal(n.toTex(), 'foo\\left({}\\right)'); }); it ('should LaTeX an OperatorNode with more than two operators', function () { @@ -346,7 +346,7 @@ describe('OperatorNode', function() { var c = new ConstantNode(4); var n = new OperatorNode('foo', 'foo', [a, b, c]); - assert.equal(n.toTex(), 'foo(2, 3, 4)'); + assert.equal(n.toTex(), 'foo\\left({2, 3, 4}\\right)'); }); @@ -363,10 +363,10 @@ describe('OperatorNode', function() { var m2 = new OperatorNode('*', 'multiply', [n1, c]); var m3 = new OperatorNode('-', 'subtract', [m2, d]); - assert.equal(n1.toTex(), '{2}+{3}'); - assert.equal(n2.toTex(), '{4}-{5}'); - assert.equal(n3.toTex(), '\\left({{2}+{3}}\\right) \\cdot \\left({{4}-{5}}\\right)'); - assert.equal(m3.toTex(), '{\\left({{2}+{3}}\\right) \\cdot {4}}-{5}'); + assert.equal(n1.toTex(), '{2} + {3}'); + assert.equal(n2.toTex(), '{4} - {5}'); + assert.equal(n3.toTex(), '\\left({{2} + {3}}\\right) \\cdot \\left({{4} - {5}}\\right)'); + assert.equal(m3.toTex(), '{\\left({{2} + {3}}\\right) \\cdot {4}} - {5}'); }); it ('should have an identifier', function () {