mirror of
https://github.com/josdejong/mathjs.git
synced 2026-01-25 15:07:57 +00:00
commit
b261c55f48
@ -139,3 +139,95 @@ math.import({
|
||||
math.eval('myFunction(2 + 3, sqrt(4))');
|
||||
// returns 'arguments: 2 + 3, sqrt(4), evaluated: 5, 2'
|
||||
```
|
||||
|
||||
## Custom LaTeX conversion
|
||||
|
||||
You can provide the `toTex` function of an expression tree with your own LaTeX converters.
|
||||
This can be used to override the builtin LaTeX conversion or provide LaTeX output for your own custom functions.
|
||||
|
||||
You can pass your own callback(s) to `toTex`. If it returns nothing, the standard LaTeX conversion will be use.
|
||||
If your callback returns a string, this string will be used.
|
||||
|
||||
There's two ways of passing callbacks:
|
||||
1. Pass an object that maps function names to callbacks. Those callbacks will be used for FunctionNodes with
|
||||
functions of that name.
|
||||
2. Pass a function to `toTex`. This function will then be used for every node.
|
||||
|
||||
|
||||
**Examples for option 2**
|
||||
```js
|
||||
var customFunctions = {
|
||||
binomial: function (n, k) {
|
||||
//calculate n choose k
|
||||
// (do some stuff)
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
var customLaTeX = {
|
||||
'binomial': function (node, callbacks) { //provide toTex for your own custom function
|
||||
return '\\binom{' + node.args[0].toTex(callbacks) + '}{' + node.args[1].toTex(callbacks) + '}';
|
||||
},
|
||||
'factorial': function (node, callbacks) { //override toTex for builtin functions
|
||||
return 'factorial\\left(' + node.args[0] + '\\right)';
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can simply use your custom toTex functions by passing them to `toTex`:
|
||||
```js
|
||||
math.import(customFunctions);
|
||||
var expression = math.parse('binomial(factorial(2),1)');
|
||||
var latex = expression.toTex(customLaTeX);
|
||||
//latex now contains "\binom{factorial\\left(2\\right)}{1}"
|
||||
```
|
||||
|
||||
**Examples for option 2:**
|
||||
|
||||
```js
|
||||
var customLaTeX = function (node, callback) {
|
||||
if ((node.type === 'OperatorNode') && (node.fn === 'add')) {
|
||||
//don't forget to pass the callback to the toTex functions
|
||||
return node.args[0].toTex(callback) + ' plus ' + node.args[1].toTex(callback);
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
if (node.value == 0) {
|
||||
return '\\mbox{zero}';
|
||||
}
|
||||
else if (node.value == 1) {
|
||||
return '\\mbox{one}';
|
||||
}
|
||||
else if (node.value == 2) {
|
||||
return '\\mbox{two}';
|
||||
}
|
||||
else {
|
||||
return node.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var expression = math.parse('1+2');
|
||||
var latex = expression.toTex(customLaTeX);
|
||||
//latex now contains '\mbox{one} plus \mbox{two}'
|
||||
```
|
||||
Another example in conjunction with custom functions:
|
||||
```js
|
||||
var customFunctions = {
|
||||
binomial: function (n, k) {
|
||||
//calculate n choose k
|
||||
// (do some stuff)
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
var customLaTeX = function (node, callback) {
|
||||
if ((node.type === 'FunctionNode') && (node.name === 'binomial')) {
|
||||
return '\\binom{' + node.args[0].toTex(callback) + '}{' + node.args[1].toTex(callback) + '}';
|
||||
}
|
||||
};
|
||||
|
||||
math.import(customFunctions);
|
||||
var expression = math.parse('binomial(2,1)');
|
||||
var latex = expression.toTex(customLaTeX);
|
||||
//latex now contains "\binom{2}{1}"
|
||||
```
|
||||
|
||||
@ -95,26 +95,28 @@ ArrayNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @param {String} type
|
||||
* @return {String} str
|
||||
*/
|
||||
ArrayNode.prototype.toTex = function(type) {
|
||||
type = type || 'bmatrix';
|
||||
var s = '\\begin{' + type + '}';
|
||||
ArrayNode.prototype._toTex = function(callbacks) {
|
||||
this.latexType = this.latexType || 'bmatrix';
|
||||
var s = '\\begin{' + this.latexType + '}';
|
||||
|
||||
this.nodes.forEach(function(node) {
|
||||
if (node.nodes) {
|
||||
s += node.nodes.map(function(childNode) {
|
||||
return childNode.toTex();
|
||||
return childNode.toTex(callbacks);
|
||||
}).join('&');
|
||||
}
|
||||
else {
|
||||
s += node.toTex();
|
||||
s += node.toTex(callbacks);
|
||||
}
|
||||
|
||||
// new line
|
||||
s += '\\\\';
|
||||
});
|
||||
s += '\\end{' + type + '}';
|
||||
s += '\\end{' + this.latexType + '}';
|
||||
return s;
|
||||
};
|
||||
|
||||
|
||||
@ -89,13 +89,14 @@ AssignmentNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String}
|
||||
*/
|
||||
AssignmentNode.prototype.toTex = function() {
|
||||
AssignmentNode.prototype._toTex = function(callbacks) {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr);
|
||||
|
||||
var expr = this.expr.toTex();
|
||||
var expr = this.expr.toTex(callbacks);
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
//adds visible round brackets
|
||||
expr = latex.addBraces(expr, true);
|
||||
|
||||
@ -122,11 +122,12 @@ BlockNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
BlockNode.prototype.toTex = function() {
|
||||
BlockNode.prototype._toTex = function(callbacks) {
|
||||
return this.blocks.map(function (param) {
|
||||
return param.node.toTex() + (param.visible ? '' : ';');
|
||||
return param.node.toTex(callbacks) + (param.visible ? '' : ';');
|
||||
}).join('\n');
|
||||
};
|
||||
|
||||
|
||||
@ -151,15 +151,16 @@ ConditionalNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
ConditionalNode.prototype.toTex = function() {
|
||||
ConditionalNode.prototype._toTex = function(callbacks) {
|
||||
var s = (
|
||||
latex.addBraces(this.trueExpr.toTex()) +
|
||||
latex.addBraces(this.trueExpr.toTex(callbacks)) +
|
||||
', &\\quad' +
|
||||
latex.addBraces('\\text{if}\\;' + this.condition.toTex())
|
||||
latex.addBraces('\\text{if}\\;' + this.condition.toTex(callbacks))
|
||||
) + '\\\\' + (
|
||||
latex.addBraces(this.falseExpr.toTex()) +
|
||||
latex.addBraces(this.falseExpr.toTex(callbacks)) +
|
||||
', &\\quad' +
|
||||
latex.addBraces('\\text{otherwise}')
|
||||
);
|
||||
|
||||
@ -156,9 +156,10 @@ ConstantNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
ConstantNode.prototype.toTex = function() {
|
||||
ConstantNode.prototype._toTex = function(callbacks) {
|
||||
var value = this.value,
|
||||
index;
|
||||
switch (this.valueType) {
|
||||
|
||||
@ -110,13 +110,14 @@ FunctionAssignmentNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
FunctionAssignmentNode.prototype.toTex = function() {
|
||||
FunctionAssignmentNode.prototype._toTex = function(callbacks) {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr);
|
||||
|
||||
var expr = this.expr.toTex();
|
||||
var expr = this.expr.toTex(callbacks);
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
//adds visible round brackets
|
||||
expr = latex.addBraces(expr, true);
|
||||
@ -127,7 +128,7 @@ FunctionAssignmentNode.prototype.toTex = function() {
|
||||
}
|
||||
|
||||
return latex.toFunction(this.name)
|
||||
+ latex.addBraces(this.params.map(latex.toSymbol).join(', '), true) + '='
|
||||
+ latex.addBraces(this.params.map(latex.toSymbol).join(', '), true) + '=' //FIXME, this doesn't call toTex on the parameters AFAIK (toSymbol)
|
||||
+ expr;
|
||||
};
|
||||
|
||||
|
||||
@ -113,10 +113,11 @@ FunctionNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
FunctionNode.prototype.toTex = function() {
|
||||
return latex.toArgs(this);
|
||||
FunctionNode.prototype._toTex = function(callbacks) {
|
||||
return latex.toArgs(this, callbacks);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -209,10 +209,11 @@ IndexNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
IndexNode.prototype.toTex = function() {
|
||||
return this.object.toTex() + '[' + this.ranges.join(', ') + ']';
|
||||
IndexNode.prototype._toTex = function(callbacks) {
|
||||
return this.object.toTex(callbacks) + '[' + this.ranges.join(', ') + ']';
|
||||
};
|
||||
|
||||
module.exports = IndexNode;
|
||||
module.exports = IndexNode;
|
||||
|
||||
@ -221,11 +221,62 @@ Node.prototype.toString = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* Get LaTeX representation. (wrapper function)
|
||||
* This functions get's either an object containing callbacks or
|
||||
* a single callback. It decides whether to call the callback and if
|
||||
* not or if the callback returns nothing, it calls the default
|
||||
* LaTeX implementation of the node (_toTex).
|
||||
*
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String}
|
||||
*/
|
||||
Node.prototype.toTex = function() {
|
||||
return '';
|
||||
Node.prototype.toTex = function(callback) {
|
||||
var customTex;
|
||||
if (this.type === 'ArrayNode') {
|
||||
//FIXME this is only a workaround for a breaking change,
|
||||
//remove this in version2
|
||||
delete this.latexType;
|
||||
}
|
||||
if (typeof callback === 'object') {
|
||||
if ((this.type === 'FunctionNode') && callback.hasOwnProperty(this.name)) {
|
||||
//if callback is a map of callback functions and this is a FunctionNode
|
||||
customTex = callback[this.name](this, callback);
|
||||
}
|
||||
}
|
||||
else if (typeof callback === 'function') {
|
||||
//if callback is a function
|
||||
customTex = callback(this, callback);
|
||||
}
|
||||
else if ((typeof callback === 'string') && (this.type === 'ArrayNode')) {
|
||||
//FIXME this is only a workaround for a breaking change,
|
||||
//remove this in version2
|
||||
this.latexType = callback;
|
||||
}
|
||||
else if (typeof callback !== 'undefined') {
|
||||
throw new TypeError('Object or function expected as callback');
|
||||
}
|
||||
|
||||
if (typeof customTex !== 'undefined') {
|
||||
return customTex;
|
||||
}
|
||||
|
||||
return this._toTex(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal function to generate the LaTeX output.
|
||||
* This has to be implemented by every Node
|
||||
*
|
||||
* @param {Object}|function}
|
||||
* @throws {Error}
|
||||
*/
|
||||
Node.prototype._toTex = function () {
|
||||
if (this.type === 'Node') {
|
||||
//FIXME remove this in v2
|
||||
return '';
|
||||
}
|
||||
//must be implemented by each of the Node implementations
|
||||
throw new Error('_toTex not implemented for this Node');
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -236,9 +236,10 @@ OperatorNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
OperatorNode.prototype.toTex = function() {
|
||||
OperatorNode.prototype._toTex = function(callbacks) {
|
||||
var args = this.args;
|
||||
var parens = calculateNecessaryParentheses(this, args);
|
||||
var op = latex.toOperator(this.op); //operator
|
||||
@ -247,7 +248,7 @@ OperatorNode.prototype.toTex = function() {
|
||||
case 1: //unary operators
|
||||
var assoc = operators.getAssociativity(this);
|
||||
|
||||
var operand = args[0].toTex();
|
||||
var operand = args[0].toTex(callbacks);
|
||||
if (parens[0]) {
|
||||
operand = latex.addBraces(operand, true);
|
||||
}
|
||||
@ -265,9 +266,9 @@ OperatorNode.prototype.toTex = function() {
|
||||
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 lhsTex = latex.addBraces(lhs.toTex(callbacks), parens[0]);
|
||||
var rhs = args[1]; //right hand side
|
||||
var rhsTex = latex.addBraces(rhs.toTex(), parens[1]);
|
||||
var rhsTex = latex.addBraces(rhs.toTex(callbacks), parens[1]);
|
||||
|
||||
switch (this.getIdentifier()) {
|
||||
case 'OperatorNode:divide':
|
||||
@ -275,7 +276,7 @@ OperatorNode.prototype.toTex = function() {
|
||||
return op + lhsTex + rhsTex;
|
||||
|
||||
case 'OperatorNode:to':
|
||||
rhsTex = latex.toUnit(rhs.toTex());
|
||||
rhsTex = latex.toUnit(rhs.toTex(callbacks));
|
||||
rhsTex = latex.addBraces(rhsTex, parens[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -121,14 +121,15 @@ RangeNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @params {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
*/
|
||||
RangeNode.prototype.toTex = function() {
|
||||
var str = this.start.toTex();
|
||||
RangeNode.prototype._toTex = function(callbacks) {
|
||||
var str = this.start.toTex(callbacks);
|
||||
if (this.step) {
|
||||
str += ':' + this.step.toTex();
|
||||
str += ':' + this.step.toTex(callbacks);
|
||||
}
|
||||
str += ':' + this.end.toTex();
|
||||
str += ':' + this.end.toTex(callbacks);
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
@ -99,11 +99,12 @@ SymbolNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String} str
|
||||
* @override
|
||||
*/
|
||||
SymbolNode.prototype.toTex = function() {
|
||||
return latex.toSymbol(this.name);
|
||||
SymbolNode.prototype._toTex = function(callbacks) {
|
||||
return latex.toSymbol(this.name, callbacks);
|
||||
};
|
||||
|
||||
module.exports = SymbolNode;
|
||||
|
||||
@ -84,10 +84,11 @@ UpdateNode.prototype.toString = function() {
|
||||
|
||||
/**
|
||||
* Get LaTeX representation
|
||||
* @param {Object|function} callback(s)
|
||||
* @return {String}
|
||||
*/
|
||||
UpdateNode.prototype.toTex = function() {
|
||||
return this.index.toTex() + ' = ' + this.expr.toTex();
|
||||
UpdateNode.prototype._toTex = function(callbacks) {
|
||||
return this.index.toTex(callbacks) + ' = ' + this.expr.toTex(callbacks);
|
||||
};
|
||||
|
||||
module.exports = UpdateNode;
|
||||
|
||||
@ -286,7 +286,7 @@ exports.addBraces = function(s, brace, type) {
|
||||
return braces[0] + s + braces[1];
|
||||
};
|
||||
|
||||
exports.toArgs = function(that) {
|
||||
exports.toArgs = function(that, customFunctions) {
|
||||
var name = that.name,
|
||||
args = that.args,
|
||||
func = exports.toSymbol(that.name),
|
||||
@ -358,13 +358,13 @@ exports.toArgs = function(that) {
|
||||
op = '!';
|
||||
}
|
||||
else {
|
||||
return '{\\left(' + args[0].toTex() + '\\right)!}';
|
||||
return '{\\left(' + args[0].toTex(customFunctions) + '\\right)!}';
|
||||
}
|
||||
}
|
||||
else {
|
||||
// op = 'P';
|
||||
var n = args[0].toTex(),
|
||||
k = args[1].toTex();
|
||||
var n = args[0].toTex(customFunctions),
|
||||
k = args[1].toTex(customFunctions);
|
||||
return '\\frac{' + n + '!}{\\left(' + n + ' - ' + k + '\\right)!}';
|
||||
}
|
||||
break;
|
||||
@ -385,7 +385,7 @@ exports.toArgs = function(that) {
|
||||
type = 'lr';
|
||||
|
||||
if (args.length === 2) {
|
||||
var tmp = args[1].toTex();
|
||||
var tmp = args[1].toTex(customFunctions);
|
||||
|
||||
if (tmp === '\\text{inf}') {
|
||||
tmp = '\\infty';
|
||||
@ -417,7 +417,7 @@ exports.toArgs = function(that) {
|
||||
type = 'lr';
|
||||
|
||||
if (args.length === 2) {
|
||||
suffix = '_' + exports.addBraces(args[1].toTex());
|
||||
suffix = '_' + exports.addBraces(args[1].toTex(customFunctions));
|
||||
args = [args[0]];
|
||||
}
|
||||
break;
|
||||
@ -437,7 +437,7 @@ exports.toArgs = function(that) {
|
||||
case 'log':
|
||||
var base = 'e';
|
||||
if (args.length === 2) {
|
||||
base = args[1].toTex();
|
||||
base = args[1].toTex(customFunctions);
|
||||
func = '\\log_{' + base + '}';
|
||||
args = [args[0]];
|
||||
}
|
||||
@ -466,7 +466,11 @@ exports.toArgs = function(that) {
|
||||
|
||||
case 'det':
|
||||
if (that.args[0] instanceof ArrayNode) {
|
||||
return that.args[0].toTex('vmatrix');
|
||||
//FIXME passing 'vmatrix' like that is really ugly
|
||||
that.args[0].latexType = 'vmatrix';
|
||||
var latex = that.args[0].toTex(customFunctions);
|
||||
delete that.args[0].latexType;
|
||||
return latex;
|
||||
}
|
||||
|
||||
brace = 'vmatrix';
|
||||
@ -480,7 +484,7 @@ exports.toArgs = function(that) {
|
||||
|
||||
if (op !== null) {
|
||||
brace = (op === '+' || op === '-');
|
||||
texParams = (new OperatorNode(op, name, args)).toTex();
|
||||
texParams = (new OperatorNode(op, name, args)).toTex(customFunctions);
|
||||
}
|
||||
else {
|
||||
op = ', ';
|
||||
@ -491,7 +495,7 @@ exports.toArgs = function(that) {
|
||||
}
|
||||
|
||||
texParams = texParams || args.map(function(param) {
|
||||
return '{' + param.toTex() + '}' ;
|
||||
return '{' + param.toTex(customFunctions) + '}' ;
|
||||
}).join(op);
|
||||
|
||||
return prefix + (showFunc ? func : '') +
|
||||
|
||||
@ -236,4 +236,29 @@ describe('ArrayNode', function() {
|
||||
assert.equal(n.toTex(), '\\begin{bmatrix}1&2\\\\3&4\\\\\\end{bmatrix}');
|
||||
});
|
||||
|
||||
it ('should LaTeX an ArrayNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'ArrayNode') {
|
||||
var latex = '\\left[';
|
||||
node.nodes.forEach(function (node) {
|
||||
latex += node.toTex(callback) + ', ';
|
||||
});
|
||||
|
||||
latex += '\\right]';
|
||||
return latex;
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
|
||||
var n = new ArrayNode([a, b]);
|
||||
|
||||
assert.equal(n.toTex(customFunction), '\\left[const\\left(1, number\\right), const\\left(2, number\\right), \\right]');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -225,4 +225,22 @@ describe('AssignmentNode', function() {
|
||||
assert.equal(n.toTex(), '{b}=\\left({{a}={2}}\\right)');
|
||||
});
|
||||
|
||||
it ('should LaTeX an AssignmentNode with custom toTex', function () {
|
||||
//Also checks if custom funcions get passed to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'AssignmentNode') {
|
||||
return node.name + '\\mbox{equals}' + node.expr.toTex(callback);
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
|
||||
var n = new AssignmentNode('a', a);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'a\\mbox{equals}const\\left(1, number\\right)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -256,4 +256,28 @@ describe('BlockNode', function() {
|
||||
assert.equal(n.toTex(), '5\n{foo}={3};\nfoo');
|
||||
});
|
||||
|
||||
it ('should LaTeX a BlockNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'BlockNode') {
|
||||
var latex = '';
|
||||
node.blocks.forEach(function (block) {
|
||||
latex += block.node.toTex(callback) + '; ';
|
||||
});
|
||||
|
||||
return latex;
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
|
||||
var n = new BlockNode([{node: a}, {node: b}]);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'const\\left(1, number\\right); const\\left(2, number\\right); ');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -260,4 +260,26 @@ describe('ConditionalNode', function() {
|
||||
assert.equal(n.toTex(), '\\left\\{\\begin{array}{l l}{{a}={2}}, &\\quad{\\text{if}\\;true}\\\\{{b}={3}}, &\\quad{\\text{otherwise}}\\end{array}\\right.');
|
||||
});
|
||||
|
||||
it ('should LaTeX a ConditionalNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'ConditionalNode') {
|
||||
return 'if ' + node.condition.toTex(callback)
|
||||
+ ' then ' + node.trueExpr.toTex(callback)
|
||||
+ ' else ' + node.falseExpr.toTex(callback);
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
var c = new ConstantNode(3);
|
||||
|
||||
var n = new ConditionalNode(a, b, c);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'if const\\left(1, number\\right) then const\\left(2, number\\right) else const\\left(3, number\\right)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -139,4 +139,17 @@ describe('ConstantNode', function() {
|
||||
assert.equal(new ConstantNode('null', 'null').toTex(), 'null');
|
||||
});
|
||||
|
||||
it ('should LaTeX a ConstantNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var n = new ConstantNode(1);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'const\\left(1, number\\right)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -206,4 +206,29 @@ describe('FunctionAssignmentNode', function() {
|
||||
|
||||
assert.equal(n.toTex(), 'f\\left({x}\\right)=\\left({{a}={2}}\\right)');
|
||||
});
|
||||
|
||||
it ('should LaTeX a FunctionAssignmentNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'FunctionAssignmentNode') {
|
||||
var latex = '\\mbox{' + node.name + '}\\left(';
|
||||
node.params.forEach(function (param) {
|
||||
latex += param + ', ';
|
||||
});
|
||||
|
||||
latex += '\\right)=' + node.expr.toTex(callback);
|
||||
return latex;
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
|
||||
var n = new FunctionAssignmentNode('func', ['x'], a);
|
||||
|
||||
assert.equal(n.toTex(customFunction), '\\mbox{func}\\left(x, \\right)=const\\left(1, number\\right)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -288,4 +288,48 @@ describe('FunctionNode', function() {
|
||||
assert.equal(n.getIdentifier(), 'FunctionNode:factorial');
|
||||
});
|
||||
|
||||
it ('should LaTeX a FunctionNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'FunctionNode') {
|
||||
var latex = '\\mbox{' + node.name + '}\\left(';
|
||||
node.args.forEach(function (arg) {
|
||||
latex += arg.toTex(callback) + ', ';
|
||||
});
|
||||
latex += '\\right)';
|
||||
return latex;
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
|
||||
var n1 = new FunctionNode('add', [a, b]);
|
||||
var n2 = new FunctionNode('subtract', [a, b]);
|
||||
|
||||
assert.equal(n1.toTex(customFunction), '\\mbox{add}\\left(const\\left(1, number\\right), const\\left(2, number\\right), \\right)');
|
||||
assert.equal(n2.toTex(customFunction), '\\mbox{subtract}\\left(const\\left(1, number\\right), const\\left(2, number\\right), \\right)');
|
||||
});
|
||||
|
||||
it ('should LaTeX a FunctionNode with custom toTex for a single function', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = {
|
||||
'add': function (node, callbacks) {
|
||||
return node.args[0].toTex(callbacks)
|
||||
+ ' ' + node.name + ' '
|
||||
+ node.args[1].toTex(callbacks);
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
|
||||
var n = new FunctionNode('add', [a, b]);
|
||||
|
||||
assert.equal(n.toTex(customFunction), '1 add 2');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -285,4 +285,29 @@ describe('IndexNode', function() {
|
||||
assert.equal(n2.toTex(), 'a[]')
|
||||
});
|
||||
|
||||
it ('should LaTeX an IndexNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'IndexNode') {
|
||||
var latex = node.object.toTex(callback) + ' at ';
|
||||
node.ranges.forEach(function (range) {
|
||||
latex += range.toTex(callback) + ', ';
|
||||
});
|
||||
|
||||
return latex;
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new SymbolNode('a');
|
||||
var b = new ConstantNode(1);
|
||||
var c = new ConstantNode(2);
|
||||
|
||||
var n = new IndexNode(a, [b, c]);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'a at const\\left(1, number\\right), const\\left(2, number\\right), ');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -74,9 +74,29 @@ describe('Node', function() {
|
||||
assert.equal(node.toString(), '');
|
||||
});
|
||||
|
||||
it ('should LaTeX a Node', function () {
|
||||
var node = new Node();
|
||||
assert.equal(node.toTex(), '');
|
||||
it ('should throw an error when calling _toTex', function () {
|
||||
assert.throws(function () {
|
||||
var node = new Node();
|
||||
node.type = 'SpecialNode'; //this is necessary because toTex
|
||||
//returns '' for a Node
|
||||
node._toTex();
|
||||
}, /_toTex not implemented for this Node/);
|
||||
});
|
||||
|
||||
it ('should ignore custom toTex if it returns nothing', function () {
|
||||
var callback1 = function (node, callback) {};
|
||||
var callback2 = {
|
||||
bla: function (node, callbacks) {}
|
||||
};
|
||||
var mymath = math.create();
|
||||
mymath.expression.node.Node.prototype._toTex = function () {
|
||||
return 'default';
|
||||
};
|
||||
var n1 = new mymath.expression.node.Node();
|
||||
var n2 = new mymath.expression.node.FunctionNode('bla', []);
|
||||
|
||||
assert.equal(n1.toTex(callback1), 'default');
|
||||
assert.equal(n2.toTex(callback2), 'bla\\left({}\\right)');
|
||||
});
|
||||
|
||||
it ('should throw an error in case of wrong arguments for compile', function () {
|
||||
|
||||
@ -378,4 +378,48 @@ describe('OperatorNode', function() {
|
||||
assert.equal(n.getIdentifier(), 'OperatorNode:add');
|
||||
});
|
||||
|
||||
it ('should LaTeX an OperatorNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'OperatorNode') {
|
||||
return node.op + node.fn + '('
|
||||
+ node.args[0].toTex(callback)
|
||||
+ ', ' + node.args[1].toTex(callback) + ')';
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
|
||||
var n1 = new OperatorNode('+', 'add', [a, b]);
|
||||
var n2 = new OperatorNode('-', 'subtract', [a, b]);
|
||||
|
||||
assert.equal(n1.toTex(customFunction), '+add(const\\left(1, number\\right), const\\left(2, number\\right))');
|
||||
assert.equal(n2.toTex(customFunction), '-subtract(const\\left(1, number\\right), const\\left(2, number\\right))');
|
||||
});
|
||||
|
||||
it ('should LaTeX an OperatorNode with custom toTex for a single operator', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if ((node.type === 'OperatorNode') && (node.fn === 'add')) {
|
||||
return node.args[0].toTex(callback)
|
||||
+ node.op + node.fn + node.op +
|
||||
node.args[1].toTex(callback);
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
|
||||
var n = new OperatorNode('+', 'add', [a, b]);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'const\\left(1, number\\right)+add+const\\left(2, number\\right)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -289,4 +289,26 @@ describe('RangeNode', function() {
|
||||
assert.equal(n.toTex(), '0:2:10');
|
||||
});
|
||||
|
||||
it ('should LaTeX a RangeNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'RangeNode') {
|
||||
return 'from ' + node.start.toTex(callback)
|
||||
+ ' to ' + node.end.toTex(callback)
|
||||
+ ' with steps of ' + node.step.toTex(callback);
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new ConstantNode(1);
|
||||
var b = new ConstantNode(2);
|
||||
var c = new ConstantNode(3);
|
||||
|
||||
var n = new RangeNode(a, b, c);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'from const\\left(1, number\\right) to const\\left(2, number\\right) with steps of const\\left(3, number\\right)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -107,4 +107,17 @@ describe('SymbolNode', function() {
|
||||
assert.equal(s.toTex(), 'foo');
|
||||
});
|
||||
|
||||
it ('should LaTeX a SymbolNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'SymbolNode') {
|
||||
return 'symbol(' + node.name + ')';
|
||||
}
|
||||
};
|
||||
|
||||
var n = new SymbolNode('a');
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'symbol(a)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -322,4 +322,34 @@ describe('UpdateNode', function() {
|
||||
assert.equal(n.toString(), 'a[2, 1] = 5');
|
||||
});
|
||||
|
||||
it ('should LaTeX an UpdateNode with custom toTex', function () {
|
||||
//Also checks if the custom functions get passed on to the children
|
||||
var customFunction = function (node, callback) {
|
||||
if (node.type === 'UpdateNode') {
|
||||
return node.index.toTex(callback) + ' equals ' + node.expr.toTex(callback);
|
||||
}
|
||||
else if (node.type === 'IndexNode') {
|
||||
var latex = node.object.toTex(callback) + ' at ';
|
||||
node.ranges.forEach(function (range) {
|
||||
latex += range.toTex(callback) + ', ';
|
||||
});
|
||||
return latex;
|
||||
}
|
||||
else if (node.type === 'ConstantNode') {
|
||||
return 'const\\left(' + node.value + ', ' + node.valueType + '\\right)'
|
||||
}
|
||||
};
|
||||
|
||||
var a = new SymbolNode('a');
|
||||
var ranges = [
|
||||
new ConstantNode(2),
|
||||
new ConstantNode(1)
|
||||
];
|
||||
var v = new ConstantNode(5);
|
||||
|
||||
var n = new UpdateNode(new IndexNode(a, ranges), v);
|
||||
|
||||
assert.equal(n.toTex(customFunction), 'a at const\\left(2, number\\right), const\\left(1, number\\right), equals const\\left(5, number\\right)');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user