Fixed #172: parser not being able to evaluate an exponent followed by a unary minus like 2^-3, and a transpose followed by an index like [3]'[1].

This commit is contained in:
jos 2014-05-13 20:57:32 +02:00
parent d83e5b8d43
commit 3276b036a6
3 changed files with 77 additions and 28 deletions

View File

@ -4,10 +4,12 @@ https://github.com/josdejong/mathjs
## not yet released, version 0.21.1
- Fixed function `add` not adding strings and matrices element wise.
- Removed `crypto` library from the bundle.
- Deprecated functions `Parser.parse` and `Parser.compile`. Use
`math.parse` and `math.compile` instead.
- Fixed function `add` not adding strings and matrices element wise.
- Fixed parser not being able to evaluate an exponent followed by a unary minus
like `2^-3`, and a transpose followed by an index like `[3]'[1]`.
## 2014-04-24, version 0.21.0

View File

@ -754,7 +754,7 @@ function parseMultiplyDivide () {
}
/**
* parse units like in '2i', '2 cm'
* parse units conversion 'in' like '5cm in inch'
* @return {Node} node
* @private
*/
@ -811,7 +811,7 @@ function parsePow () {
fn = (name == '^') ? 'pow' : 'epow';
getToken();
params = [node, parsePow()];
params = [node, parseUnary()]; // Go back to unary, we can have '2^-3'
node = new OperatorNode(name, fn, params);
}
@ -841,6 +841,8 @@ function parseLeftHandOperators () {
params = [node];
node = new OperatorNode(name, fn, params);
node = parseParams(node); // cases like "A'[2,3]"
}
return node;

View File

@ -718,6 +718,17 @@ describe('parse', function() {
approx.deepEqual(parseAndEval('2.54 cm in inch'), math.unit(1, 'inch').to('inch'));
});
it('should parse ! (factorial)', function() {
assert.deepEqual(parseAndEval('5!'), 120);
assert.deepEqual(parseAndEval('[1,2,3,4]!'), new Matrix([1,2,6,24]));
assert.deepEqual(parseAndEval('4!+2'), 26);
assert.deepEqual(parseAndEval('4!-2'), 22);
assert.deepEqual(parseAndEval('4!*2'), 48);
assert.deepEqual(parseAndEval('3!!'), 720);
assert.deepEqual(parseAndEval('[1,2;3,1]!\'!'), new Matrix([[1, 720], [2, 1]]));
assert.deepEqual(parseAndEval('[2,3]![2]'), 6);
});
it('should parse \' (transpose)', function() {
assert.deepEqual(parseAndEval('23\''), 23);
assert.deepEqual(parseAndEval('[1,2,3;4,5,6]\''), new Matrix([[1,4],[2,5],[3,6]]));
@ -726,39 +737,73 @@ describe('parse', function() {
assert.deepEqual(parseAndEval('[1:5]\''), new Matrix([[1],[2],[3],[4],[5]]));
assert.deepEqual(parseAndEval('size([1:5])'), new Matrix([1, 5]));
assert.deepEqual(parseAndEval('[1,2;3,4]\''), new Matrix([[1,3],[2,4]]));
assert.deepEqual(parseAndEval('[1,2;3,4]\'[1,2]'), 3);
});
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);
describe('operator precedence', function() {
it('should respect precedence of plus and minus', 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);
it('should respect precedence of plus/minus and multiply/divide', function () {
assert.equal(parseAndEval('2+3*4'), 14);
assert.equal(parseAndEval('2*3+4'), 10);
});
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))'));
it('should respect precedence of plus/minus and pow', function () {
assert.equal(parseAndEval('2+3^2'), 11);
assert.equal(parseAndEval('3^2+2'), 11);
assert.equal(parseAndEval('8-2^2'), 4);
assert.equal(parseAndEval('4^2-2'), 14);
});
assert.equal(parseAndEval('-3^2'), -9);
assert.equal(parseAndEval('(-3)^2'), 9);
it('should respect precedence of multiply/divide and pow', function () {
assert.equal(parseAndEval('2*3^2'), 18);
assert.equal(parseAndEval('3^2*2'), 18);
assert.equal(parseAndEval('8/2^2'), 2);
assert.equal(parseAndEval('4^2/2'), 8);
});
assert.equal(parseAndEval('2^3!'), 64);
assert.equal(parseAndEval('2^(3!)'), 64);
it('should respect precedence of pow', function () {
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('-4!'), -24);
assert.equal(parseAndEval('3!+2'), 8);
it('should respect precedence of unary minus and pow', function () {
assert.equal(parseAndEval('-3^2'), -9);
assert.equal(parseAndEval('(-3)^2'), 9);
assert.equal(parseAndEval('2^-2'), 0.25);
});
assert.equal(parseAndEval('2 > 3 ? true : false'), false);
assert.equal(parseAndEval('2 == 3 ? true : false'), false);
assert.equal(parseAndEval('3 ? 2 + 4 : 2 - 1'), 6);
assert.deepEqual(parseAndEval('3 ? true : false; 22'), [22]);
assert.deepEqual(parseAndEval('3 ? 5cm to m : 5cm in mm'), new Unit(5, 'cm').to('m'));
assert.deepEqual(parseAndEval('2 == 4-2 ? [1,2] : false'), new Matrix([1,2]));
assert.deepEqual(parseAndEval('false ? 1:2:6'), new Matrix([2,3,4,5,6]));
it('should respect precedence of factorial and pow', function () {
assert.equal(parseAndEval('2^3!'), 64);
assert.equal(parseAndEval('2^(3!)'), 64);
assert.equal(parseAndEval('3!^2'), 36);
});
it('should respect precedence of factorial and (unary) plus/minus', function () {
assert.equal(parseAndEval('-4!'), -24);
assert.equal(parseAndEval('3!+2'), 8);
});
it('should respect precedence of transpose', function () {
// TODO: test transpose
});
it('should respect precedence of conditional operator and other operators', function () {
assert.equal(parseAndEval('2 > 3 ? true : false'), false);
assert.equal(parseAndEval('2 == 3 ? true : false'), false);
assert.equal(parseAndEval('3 ? 2 + 4 : 2 - 1'), 6);
assert.deepEqual(parseAndEval('3 ? true : false; 22'), [22]);
assert.deepEqual(parseAndEval('3 ? 5cm to m : 5cm in mm'), new Unit(5, 'cm').to('m'));
assert.deepEqual(parseAndEval('2 == 4-2 ? [1,2] : false'), new Matrix([1,2]));
assert.deepEqual(parseAndEval('false ? 1:2:6'), new Matrix([2,3,4,5,6]));
});
// TODO: extensively test operator precedence