// test parse var assert = require('assert'), approx = require('../../../tools/approx.js'), math = require('../../../index.js'), Complex = math.type.Complex, Matrix = math.type.Matrix, Unit = math.type.Unit; /** * Helper function to parse an expression and immediately evaluate its results * @param {String} expr * @param {Object} [scope] * @return result */ function parseAndEval(expr, scope) { var node = math.parse(expr, scope); return node.eval(); } describe('parse', function() { it('should respect operator precedence', function() { assert.equal(parseAndEval('4-2+3'), 5); assert.equal(parseAndEval('4-(2+3)'), -1); assert.equal(parseAndEval('4-2-3'), -1); assert.equal(parseAndEval('4-(2-3)'), 5); assert.equal(parseAndEval('2+3*4'), 14); assert.equal(parseAndEval('2*3+4'), 10); assert.equal(parseAndEval('2*3^2'), 18); assert.equal(parseAndEval('2^3'), 8); assert.equal(parseAndEval('2^3^4'), Math.pow(2, Math.pow(3, 4))); assert.equal(parseAndEval('1.5^1.5^1.5'), parseAndEval('1.5^(1.5^1.5)')); assert.equal(parseAndEval('1.5^1.5^1.5^1.5'), parseAndEval('1.5^(1.5^(1.5^1.5))')); assert.equal(parseAndEval('-3^2'), -9); assert.equal(parseAndEval('(-3)^2'), 9); assert.equal(parseAndEval('2^3!'), 64); assert.equal(parseAndEval('2^(3!)'), 64); assert.equal(parseAndEval('-4!'), -24); assert.equal(parseAndEval('3!+2'), 8); assert.deepEqual(parseAndEval('[1,2;3,4]\' * 2'), new Matrix([[2,6],[4,8]])); assert.deepEqual(parseAndEval('[1,2;3,4]\' * [5,6;7,8]'), new Matrix([[26,30],[38,44]])); assert.deepEqual(parseAndEval('[1,2;3,4] * [5,6;7,8]\''), new Matrix([[17,23],[39,53]])); assert.deepEqual(parseAndEval('[1,2;3,4]\'+2'), new Matrix([[3,5],[4,6]])); }); it('should parse valid numbers', function() { assert.equal(parseAndEval('3'), 3); assert.equal(parseAndEval('3.2'), 3.2); assert.equal(parseAndEval('003.2'), 3.2); assert.equal(parseAndEval('003.200'), 3.2); assert.equal(parseAndEval('.2'), 0.2); assert.equal(parseAndEval('2.'), 2); assert.equal(parseAndEval('3e2'), 300); assert.equal(parseAndEval('300e2'), 30000); assert.equal(parseAndEval('300e+2'), 30000); assert.equal(parseAndEval('300e-2'), 3); assert.equal(parseAndEval('300E-2'), 3); assert.equal(parseAndEval('3.2e2'), 320); }); it('should throw an error with invalid numbers', function() { assert.throws(function () {parseAndEval('.'); }); assert.throws(function () {parseAndEval('3.2.2'); }); assert.throws(function () {parseAndEval('3.2e2.2'); }); }); it('should parse constants', function() { assert.deepEqual(parseAndEval('i'), math.complex(0, 1)); assert.deepEqual(parseAndEval('pi'), Math.PI); }); it('should parse function calls', function() { assert.equal(parseAndEval('sqrt(4)'), 2); assert.equal(parseAndEval('sqrt(6+3)'), 3); assert.equal(parseAndEval('atan2(2,2)'), 0.7853981633974483); }); it('should parse valid variable assignments', function() { var scope = {}; assert.equal(parseAndEval('a = 0.75', scope), 0.75); assert.equal(parseAndEval('a + 2', scope), 2.75); assert.equal(parseAndEval('a = 2', scope), 2); assert.equal(parseAndEval('a + 2', scope), 4); approx.equal(parseAndEval('pi * 2', scope), 6.283185307179586); }); it('should throw an error on invalid assignments', function() { //assert.throws(function () {parseAndEval('sin(2) = 0.75')}, SyntaxError); // TODO: should this throw an exception? assert.throws(function () {parseAndEval('sin + 2 = 3')}, SyntaxError); }); it('should throw an error on undefined symbol', function() { assert.throws(function() {parseAndEval('a + 2'); }); }); it('should parse constants', function() { assert.ok(math.parse('pi') instanceof math.expression.node.Node); assert.equal(math.parse('pi').eval(), Math.PI); }); it('should parse arithmetic operations', function() { assert.equal(math.parse('(2+3)/4').eval(), 1.25); assert.equal(math.parse('0 + 2').toString(), 'ans = 0 + 2'); }); it('should parse functions', function() { assert.deepEqual(math.parse('sqrt(-4)').eval(), new Complex(0, 2)); }); it('should parse a series of expressions', function() { assert.deepEqual(math.parse(['a=3', 'b=4', 'a*b']).map(function (node) { return node.eval(); }), [3, 4, 12]); assert.deepEqual(math.parse(new Matrix(['a=3', 'b=4', 'a*b'])).map(function (node) { return node.eval(); }), new Matrix([3, 4, 12])); }); it('should parse multiple expressions', function() { assert.deepEqual(math.parse('a=3\nb=4\na*b').eval(), [3, 4, 12]); assert.deepEqual(math.parse('b = 43; b * 4').eval(), [172]); }); it('should parse a custom function', function() { assert.deepEqual(math.parse('function f(x) = a * x; a=2; f(4)').eval(), [8]); }); it('should throw an error if called with wrong number of arguments', function() { assert.throws(function () {math.parse()}, SyntaxError); assert.throws(function () {math.parse(1,2,3)}, SyntaxError); }); it('should throw an error if called with a number', function() { assert.throws(function () {math.parse(23)}, TypeError); }); it('should throw an error if called with a unit', function() { assert.throws(function () {math.parse(math.unit('5cm'))}, TypeError); }); it('should throw an error if called with a complex number', function() { assert.throws(function () {math.parse(new Complex(2,3))}, TypeError); }); it('should throw an error if called with a boolean', function() { assert.throws(function () {math.parse(true)}, TypeError); }); it('should handle the given scope', function() { var scope = { a: 3, b: 4 }; assert.deepEqual(math.parse('a*b', scope).eval(), 12); assert.deepEqual(math.parse('c=5', scope).eval(), 5); assert.deepEqual(math.parse('function f(x) = x^a', scope).eval(), 'f(x)'); assert.deepEqual(scope, { a: 3, b: 4, c: 5, f: 'f(x)', ans: 'f(x)' }); assert.equal(scope.f(3), 27); scope.a = 2; assert.equal(scope.f(3), 9); scope.hello = function (name) { return 'hello, ' + name + '!'; }; assert.deepEqual(math.parse('hello("jos")', scope).eval(), 'hello, jos!'); }); it('should correctly strinigfy a node tree', function() { assert.equal(math.parse('0').toString(), 'ans = 0'); assert.equal(math.parse('"hello"').toString(), 'ans = "hello"'); assert.equal(math.parse('[1, 2 + 3i, 4]').toString(), 'ans = [[1, 2 + 3i, 4]]'); }); it('shouldn\'t break with multiple unary minuses', function() { assert.equal(math.eval('5-3'), 2); assert.equal(math.eval('5--3'), 8); assert.equal(math.eval('5---3'), 2); assert.equal(math.eval('5+---3'), 2); assert.equal(math.eval('5----3'), 8); assert.equal(math.eval('5+--(2+1)'), 8); }); it('operators', function () { it('should parse +', function() { assert.equal(math.eval('2 + 3'), 5); assert.equal(math.eval('2 + 3 + 4'), 9); }); it('should parse /', function() { assert.equal(math.eval('4 / 2'), 2); assert.equal(math.eval('8 / 2 / 2'), 2); }); it('should parse ./', function() { assert.equal(math.eval('4 ./ 2'), 2); assert.equal(math.eval('8 ./ 2 / 2'), 2); }); it('should parse .*', function() { approx.equal(math.eval('4 .* 2'), 8); approx.equal(math.eval('8 .* 2 .* 2'), 32); }); it('should parse .^', function() { approx.equal(math.eval('2.^3'), 8); approx.equal(math.eval('-2.^2'), -4); // -(2^2) approx.equal(math.eval('2.^3.^4'), 2.41785163922926e+24); // 2^(3^4) }); it('should parse ==', function() { assert.equal(math.eval('2 == 3'), false); assert.equal(math.eval('2 == 2'), true); }); it('should parse >', function() { assert.equal(math.eval('2 > 3'), false); assert.equal(math.eval('2 > 2'), false); assert.equal(math.eval('2 > 1'), true); }); it('should parse >=', function() { assert.equal(math.eval('2 >= 3'), false); assert.equal(math.eval('2 >= 2'), true); assert.equal(math.eval('2 >= 1'), true); }); it('should parse %', function() { approx.equal(math.eval('8 % 3'), 2); }); it.skip('should parse mod', function() { approx.equal(math.eval('8 mod 3'), 2); }); it.skip('should parse *', function() { approx.equal(math.eval('4 * 2'), 8); approx.equal(math.eval('8 * 2 * 2'), 32); }); it.skip('should parse ^', function() { approx.equal(math.eval('2^3'), 8); approx.equal(math.eval('-2^2'), -4); // -(2^2) approx.equal(math.eval('2^3^4'), 2.41785163922926e+24); // 2^(3^4) }); it.skip('should parse <', function() { assert.equal(math.eval('2 < 3'), true); assert.equal(math.eval('2 < 2'), false); assert.equal(math.eval('2 < 1'), false); }); it.skip('should parse <=', function() { assert.equal(math.eval('2 <= 3'), true); assert.equal(math.eval('2 <= 2'), true); assert.equal(math.eval('2 <= 1'), false); }); it.skip('should parse -', function() { assert.equal(math.eval('4 - 2'), 2); assert.equal(math.eval('8 - 2 - 2'), 4); }); it.skip('should parse unary -', function() { assert.equal(math.eval('-2'), -2); assert.equal(math.eval('4*-2'), -8); assert.equal(math.eval('4 * -2'), -8); assert.equal(math.eval('4+-2'), 2); assert.equal(math.eval('4 + -2'), 2); assert.equal(math.eval('4--2'), 6); assert.equal(math.eval('4 - -2'), 6); }); it.skip('should parse unary !=', function() { assert.equal(math.eval('2 != 3'), true); assert.equal(math.eval('2 != 2'), false); }); it.skip('should parse :', function() { assert.equal(math.eval('2:5'), [2,3,4,5]); assert.equal(math.eval('5:-1:2'), [5,4,3,2]); }); it('should parse in', function() { assert.equal(math.eval('2.54 cm in inch').toString(), '1 inch'); assert.equal(math.eval('2.54 cm + 2 inch in foot').toString(), '0.25 foot'); }); }); it('should parse nested assignments', function() { var scope = []; assert.equal(parseAndEval('c = d = (e = 4.5)', scope), 4.5); assert.equal(scope.c, 4.5); assert.equal(scope.d, 4.5); assert.equal(scope.e, 4.5); assert.deepEqual(parseAndEval('a = [1,2,f=3]', scope), new Matrix([[1,2,3]])); assert.equal(scope.f, 3); assert.equal(parseAndEval('2 + (g = 3 + 4)', scope), 9); assert.equal(scope.g, 7); }); it('should throw an error for invalid nested assignments', function() { assert.throws(function () {parseAndEval('a(j = 3)', {})}, SyntaxError); }); it('should parse function assignments', function() { var scope = {}; parseAndEval('x=100', scope); // for testing scoping of the function variables assert.equal(parseAndEval('function f(x) = x^2', scope), 'f(x)'); assert.equal(parseAndEval('f(3)', scope), 9); assert.equal(scope.f(3), 9); assert.equal(scope.x, 100); assert.equal(parseAndEval('function g(x, y) = x^y', scope), 'g(x, y)'); assert.equal(parseAndEval('g(4,5)', scope), 1024); assert.equal(scope.g(4,5), 1024); }); it ('should correctly evaluate variables in assigned functions', function () { var scope = {}; assert.equal(parseAndEval('a = 3', scope), 3); assert.equal(parseAndEval('function f(x) = a * x', scope), 'f(x)'); assert.equal(parseAndEval('f(2)', scope), 6); assert.equal(parseAndEval('a = 5', scope), 5); assert.equal(parseAndEval('f(2)', scope), 10); assert.equal(parseAndEval('function g(x) = x^q', scope), 'g(x)'); assert.equal(parseAndEval('q = 4/2', scope), 2); assert.equal(parseAndEval('g(3)', scope), 9); }); it('should throw an error for undefined variables in an assigned function', function() { var scope = {}; assert.equal(parseAndEval('function g(x) = x^q', scope), 'g(x)'); assert.throws(function () { parseAndEval('g(3)', scope); }, function (err) { return (err instanceof Error) && (err.toString() == 'Error: Undefined symbol q'); }); }); it('should parse ranges', function() { assert.ok(parseAndEval('2:5') instanceof Array); assert.deepEqual(parseAndEval('2:5'), [2,3,4,5]); assert.deepEqual(parseAndEval('10:-2:0'), [10,8,6,4,2,0]); assert.deepEqual(parseAndEval('2:4.0'), [2,3,4]); assert.deepEqual(parseAndEval('2:4.5'), [2,3,4]); assert.deepEqual(parseAndEval('2:4.1'), [2,3,4]); assert.deepEqual(parseAndEval('2:3.9'), [2,3]); assert.deepEqual(parseAndEval('2:3.5'), [2,3]); assert.deepEqual(parseAndEval('3:-1:0.5'), [3,2,1]); assert.deepEqual(parseAndEval('3:-1:0.5'), [3,2,1]); assert.deepEqual(parseAndEval('3:-1:0.1'), [3,2,1]); assert.deepEqual(parseAndEval('3:-1:-0.1'), [3,2,1,0]); }); it('should get a matrix subset', function() { var scope = { a: new Matrix([ [1,2,3], [4,5,6], [7,8,9] ]) }; assert.deepEqual(parseAndEval('a(2, :)', scope), new Matrix([[4,5,6]])); assert.deepEqual(parseAndEval('a(2, :2)', scope), new Matrix([[4,5]])); assert.deepEqual(parseAndEval('a(2, :end-1)', scope), new Matrix([[4,5]])); assert.deepEqual(parseAndEval('a(2, 2:)', scope), new Matrix([[5,6]])); assert.deepEqual(parseAndEval('a(2, 2:3)', scope), new Matrix([[5,6]])); assert.deepEqual(parseAndEval('a(2, 1:2:3)', scope), new Matrix([[4,6]])); assert.deepEqual(parseAndEval('a(:, 2)', scope), new Matrix([[2],[5],[8]])); assert.deepEqual(parseAndEval('a(:2, 2)', scope), new Matrix([[2],[5]])); assert.deepEqual(parseAndEval('a(:end-1, 2)', scope), new Matrix([[2],[5]])); assert.deepEqual(parseAndEval('a(2:, 2)', scope), new Matrix([[5],[8]])); assert.deepEqual(parseAndEval('a(2:3, 2)', scope), new Matrix([[5],[8]])); assert.deepEqual(parseAndEval('a(1:2:3, 2)', scope), new Matrix([[2],[8]])); // TODO: implement and test support for Array (instead of Matrix) }); it('should get a string subset', function() { var scope = {}; assert.deepEqual(parseAndEval('c="hello"', scope), "hello"); assert.deepEqual(parseAndEval('c(2:4)', scope), "ell"); assert.deepEqual(parseAndEval('c(5:-1:1)', scope), "olleh"); assert.deepEqual(parseAndEval('c(end-2:-1:1)', scope), "leh"); }); it('should set a string subset', function() { var scope = {}; assert.deepEqual(parseAndEval('c="hello"', scope), "hello"); assert.deepEqual(parseAndEval('c(1) = "H"', scope), "Hello"); assert.deepEqual(parseAndEval('c', scope), "Hello"); assert.deepEqual(parseAndEval('c(6:11) = " world"', scope), "Hello world"); assert.deepEqual(parseAndEval('c', scope), "Hello world"); assert.deepEqual(scope.c, "Hello world"); }); it('should parse matrix resizings', function() { var scope = {}; assert.deepEqual(parseAndEval('a = []', scope), new Matrix([])); assert.deepEqual(parseAndEval('a(1:3,1) = [1;2;3]', scope), new Matrix([[1],[2],[3]])); assert.deepEqual(parseAndEval('a(:,2) = [4;5;6]', scope), new Matrix([[1,4],[2,5],[3,6]])); assert.deepEqual(parseAndEval('a = []', scope), new Matrix([])); assert.deepEqual(parseAndEval('a(1,3) = 3', scope), new Matrix([[0,0,3]])); assert.deepEqual(parseAndEval('a(2,:) = [4,5,6]', scope), new Matrix([[0,0,3],[4,5,6]])); assert.deepEqual(parseAndEval('a = []', scope), new Matrix([])); assert.deepEqual(parseAndEval('a(3,1) = 3', scope), new Matrix([[0],[0],[3]])); assert.deepEqual(parseAndEval('a(:,2) = [4;5;6]', scope), new Matrix([[0,4],[0,5],[3,6]])); assert.deepEqual(parseAndEval('a = []', scope), new Matrix([])); assert.deepEqual(parseAndEval('a(1,1:3) = [1,2,3]', scope), new Matrix([[1,2,3]])); assert.deepEqual(parseAndEval('a(2,:) = [4,5,6]', scope), new Matrix([[1,2,3],[4,5,6]])); }); it('should parse a matrix', function() { assert.ok(parseAndEval('[1,2;3,4]') instanceof Matrix); var m = parseAndEval('[1,2,3;4,5,6]'); assert.deepEqual(m.size(), [2,3]); assert.deepEqual(m, new Matrix([[1,2,3],[4,5,6]])); var b = parseAndEval('[5, 6; 1, 1]'); assert.deepEqual(b.size(), [2,2]); assert.deepEqual(b, new Matrix([[5,6],[1,1]])); assert.deepEqual(parseAndEval('[ ]'), new Matrix([])); }); it('should get/set the matrix correctly', function() { var scope = {}; parseAndEval('a=[1,2;3,4]', scope); parseAndEval('a(1,1) = 100', scope); assert.deepEqual(scope.a.size(), [2,2]); assert.deepEqual(scope.a, new Matrix([[100,2],[3,4]])); parseAndEval('a(2:3,2:3) = [10,11;12,13]', scope); assert.deepEqual(scope.a.size(), [3,3]); assert.deepEqual(scope.a, new Matrix([[100,2,0],[3,10,11],[0,12,13]])); var a = scope.a; assert.deepEqual(a.get(math.index([0,3], [0,2])), new Matrix([[100,2],[3,10],[0,12]])); assert.deepEqual(parseAndEval('a(1:3,1:2)', scope), new Matrix([[100,2],[3,10],[0,12]])); scope.b = [[1,2],[3,4]]; assert.deepEqual(parseAndEval('b(1,:)', scope), [[1, 2]]); // TODO: matrix should be squeezed }); it('should get/set the matrix correctly for 3d matrices', function() { var scope = {}; assert.deepEqual(parseAndEval('f=[1,2;3,4]', scope), new Matrix([[1,2],[3,4]])); assert.deepEqual(parseAndEval('size(f)', scope), new Matrix([2,2])); /* TODO: doesn't work correctly assert.deepEqual(parseAndEval('f(:,:,1)=[5,6;7,8]', scope), new Matrix([ [ [1,2], [3,4] ], [ [5,6], [7,8] ] ])); */ scope.f = new Matrix([ [ [1,5], [2,6] ], [ [3,7], [4,8] ] ]); assert.deepEqual(parseAndEval('size(f)', scope), new Matrix([2,2,2])); assert.deepEqual(parseAndEval('f(:,:,1)', scope), new Matrix([[[1],[2]],[[3],[4]]])); // TODO: last dimension should be squeezed assert.deepEqual(parseAndEval('f(:,:,2)', scope), new Matrix([[[5],[6]],[[7],[8]]])); // TODO: last dimension should be squeezed assert.deepEqual(parseAndEval('f(:,2,:)', scope), new Matrix([[[2,6]],[[4,8]]])); assert.deepEqual(parseAndEval('f(2,:,:)', scope), new Matrix([[[3,7],[4,8]]])); parseAndEval('a=diag([1,2,3,4])', scope); assert.deepEqual(parseAndEval('a(3:end, 3:end)', scope), new Matrix([[3,0],[0,4]])); assert.deepEqual(parseAndEval('a(3:end, 2:end)=9*ones(2,3)', scope), new Matrix([ [1,0,0,0], [0,2,0,0], [0,9,9,9], [0,9,9,9] ])); assert.deepEqual(parseAndEval('a(2:end-1, 2:end-1)', scope), new Matrix([[2,0],[9,9]])); }); it('should parse matrix concatenations', function() { var scope = {}; parseAndEval('a=[1,2;3,4]', scope); parseAndEval('b=[5,6;7,8]', scope); assert.deepEqual(parseAndEval('c=[a,b]', scope), new Matrix([[1,2,5,6],[3,4,7,8]])); assert.deepEqual(parseAndEval('c=[a;b]', scope), new Matrix([[1,2],[3,4],[5,6],[7,8]])); assert.deepEqual(parseAndEval('c=[a,b;b,a]', scope), new Matrix([[1,2,5,6],[3,4,7,8],[5,6,1,2],[7,8,3,4]])); assert.deepEqual(parseAndEval('c=[[1,2]; [3,4]]', scope), new Matrix([[1,2],[3,4]])); assert.deepEqual(parseAndEval('c=[1; [2;3]]', scope), new Matrix([[1],[2],[3]])); assert.deepEqual(parseAndEval('d=1:3', scope), [1,2,3]); assert.deepEqual(parseAndEval('[d,d]', scope), new Matrix([[1,2,3,1,2,3]])); assert.deepEqual(parseAndEval('[d;d]', scope), new Matrix([[1,2,3],[1,2,3]])); assert.deepEqual(parseAndEval('e=1+d', scope), [2,3,4]); // e is an Array assert.deepEqual(parseAndEval('size(e)', scope), [3]); assert.deepEqual(parseAndEval('[e,e]', scope), new Matrix([[2,3,4,2,3,4]])); assert.deepEqual(parseAndEval('[e;e]', scope), new Matrix([[2,3,4],[2,3,4]])); assert.deepEqual(parseAndEval('[[],[]]', scope), new Matrix([[]])); assert.deepEqual(parseAndEval('[[],[]]', scope).size(), [1, 0]); }); it('should throw an error for invalid matrix concatenations', function() { var scope = {}; assert.throws(function () {parseAndEval('c=[a; [1,2,3] ]', scope)}); }); it('should parse matrix transpositions', function() { assert.deepEqual(parseAndEval('[1,2,3;4,5,6]\''), new Matrix([[1,4],[2,5],[3,6]])); assert.ok(parseAndEval('[1,2,3;4,5,6]\'') instanceof Matrix); assert.deepEqual(parseAndEval('23\''), 23); assert.deepEqual(parseAndEval('[1:5]'), new Matrix([[1,2,3,4,5]])); assert.deepEqual(parseAndEval('[1:5]\''), new Matrix([[1],[2],[3],[4],[5]])); assert.deepEqual(parseAndEval('size([1:5])'), new Matrix([1, 5])); }); it('should parse element wise operators', function() { assert.deepEqual(parseAndEval('2.*3'), 6); assert.deepEqual(parseAndEval('2 .* 3'), 6); assert.deepEqual(parseAndEval('2. * 3'), 6); assert.deepEqual(parseAndEval('2 .^ 3'), 8); assert.deepEqual(parseAndEval('4./2'), 2); assert.deepEqual(parseAndEval('4 ./ 2'), 2); assert.deepEqual(parseAndEval('a=3; a.*4'), [12]); assert.deepEqual(parseAndEval('[1,2,3] .* [1,2,3]'), new Matrix([[1,4,9]])); assert.deepEqual(parseAndEval('[1,2,3] ./ [1,2,3]'), new Matrix([[1,1,1]])); assert.deepEqual(parseAndEval('[2,3] .^ [2,3]'), new Matrix([[4,27]])); }); it('should parse measurement units', function() { var scope = {}; assert.deepEqual(parseAndEval('5cm', scope), new Unit(5, 'cm')); assert.ok(parseAndEval('5cm', scope) instanceof Unit); // TODO: not so nice comparing units via toString assert.equal(parseAndEval('(5.08 cm * 1000) in inch', scope).toString(), '2000 inch'); assert.equal(parseAndEval('(5.08 cm * 1000) in mm', scope).toString(), '50800 mm'); assert.equal(parseAndEval('ans in inch', scope).toString(), '2000 inch'); }); it.skip('should evaluate operator "in" with correct precedence ', function () { // TODO: this following expression gives an error assert.deepEqual(parseAndEval('5.08 cm * 1000 in inch'), new Unit(2000, 'inch')); }); it('should parse undefined symbols, defining symbols, and removing symbols', function() { var scope = {}; var n = math.parse('q', scope); assert.throws(function () { n.eval(); }); math.parse('q=33', scope).eval(); assert.equal(n.eval(), 33); delete scope.q; assert.throws(function () { n.eval(); }); n = math.parse('qq(1,1)=33', scope); assert.throws(function () { n.eval(); }); math.parse('qq=[1,2;3,4]', scope).eval(); assert.deepEqual(n.eval(), new Matrix([[33,2],[3,4]])); math.parse('qq=[4]', scope).eval(); assert.deepEqual(n.eval(), new Matrix([[33]])); delete scope.qq; assert.throws(function () { n.eval(); }); }); it('should parse functions', function () { assert.equal(math.eval('abs(-4.2)'), 4.2); assert.equal(math.eval('add(2, 3)'), 5); approx.deepEqual(math.eval('1+exp(pi*i)'), new Complex(0, 0)); assert.equal(math.eval('unequal(2, 3)'), true); }); it('should support custom node handlers', function() { function CustomNode (params, paramScopes) { this.params = params; this.paramScopes = paramScopes; } CustomNode.prototype = new math.expression.node.Node(); CustomNode.prototype.toString = function () { return 'CustomNode'; }; CustomNode.prototype.eval = function () { var strParams = []; this.params.forEach(function (param) { strParams.push(param.toString()); }); return 'CustomNode(' + strParams.join(', ') + ')'; }; math.expression.node.handlers['custom'] = CustomNode; var node = math.parse('custom(x, (2+x), sin(x))'); assert.equal(node.eval(), 'CustomNode(x, 2 + x, sin(x))'); }); });