Implemented math.eval and a readonly option for the Parser and Scope.

This commit is contained in:
josdejong 2013-04-13 10:28:10 +02:00
parent f6ae1d70e7
commit b851f418e5
10 changed files with 2822 additions and 2697 deletions

View File

@ -2,6 +2,12 @@
https://github.com/josdejong/mathjs
## not yet released, version 0.7.0
- Implemented method math.eval, which uses a readonly parser to evaluate
expressions.
## 2013-04-13, version 0.6.0
- Implemented chained operations via method math.select(). For example

View File

@ -44,7 +44,6 @@ task('concat', function () {
'./src/type/**/*.js',
'./src/constants.js',
'./src/functions.js',
'./src/function/**/*.js',
'./src/expr/node/Node.js',
'./src/expr/node/Symbol.js',
'./src/expr/node/Constant.js',
@ -56,6 +55,7 @@ task('concat', function () {
'./src/expr/Scope.js',
'./src/expr/Parser.js',
'./src/expr/Workspace.js',
'./src/function/**/*.js',
'./src/compatibility.js',
'./src/init.js'
],

View File

@ -455,6 +455,7 @@ types (Number, Complex, Unit, String, and Array) where applicable.
### Utils
- math.clone(x)
- math.eval(expr)
- math.format([template, ] values)
- math.import(filename | object, override)
- math.select([x])

5411
math.js

File diff suppressed because it is too large Load Diff

6
math.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,9 @@
* @constructor math.expr.Parser
* Parser parses math expressions and evaluates them or returns a node tree.
*
* @param {Object} [options] Available options:
* {boolean} readonly (false by default).
*
* Methods:
* var result = parser.eval(expr); // evaluate an expression
* var value = parser.get(name); // retrieve a variable from the parser
@ -43,13 +46,13 @@
* // clear defined functions and variables
* parser.clear();
*/
math.expr.Parser = function Parser() {
math.expr.Parser = function Parser(options) {
if (this.constructor != Parser) {
throw new SyntaxError(
'Parser constructor must be called with the new operator');
}
this.scope = new math.expr.Scope();
this.scope = new math.expr.Scope(null, options);
};
/**
@ -430,16 +433,18 @@
function parse_ans (scope) {
var expression = parse_function_assignment(scope);
// TODO: not so nice having to specify some special types here...
if (!(expression instanceof Assignment)
// !(expression instanceof FunctionAssignment) && // TODO
// !(expression instanceof plot) // TODO
) {
// create a variable definition for ans
var name = 'ans';
var params = undefined;
var link = scope.createDef(name);
return new Assignment(name, params, expression, link);
if (!scope.readonly) {
// TODO: not so nice having to specify some special types here...
if (!(expression instanceof Assignment)
// !(expression instanceof FunctionAssignment) && // TODO
// !(expression instanceof plot) // TODO
) {
// create a variable definition for ans
var name = 'ans';
var params = undefined;
var link = scope.createDef(name);
return new Assignment(name, params, expression, link);
}
}
return expression;

View File

@ -5,8 +5,15 @@
*
* @constructor mathnotepad.Scope
* @param {Scope} [parentScope]
* @param {Object} [options] Available options:
* {boolean} readonly (false by default).
*/
math.expr.Scope = function Scope(parentScope) {
math.expr.Scope = function Scope(parentScope, options) {
this.readonly = false;
if (options && options.readonly != undefined) {
this.readonly = options.readonly;
}
this.parentScope = parentScope;
this.nestedScopes = undefined;
@ -27,6 +34,10 @@ math.expr.Scope.prototype = {
* @return {math.expr.Scope} nestedScope
*/
createNestedScope: function () {
if (this.readonly) {
throw new Error('Cannot create nested scope: Scope is read-only');
}
var nestedScope = new math.expr.Scope(this);
if (!this.nestedScopes) {
this.nestedScopes = [];
@ -40,6 +51,10 @@ math.expr.Scope.prototype = {
* (parent scope will not be cleared)
*/
clear: function () {
if (this.readonly) {
throw new Error('Cannot clear scope: Scope is read-only');
}
this.symbols = {};
this.defs = {};
this.links = {};
@ -146,6 +161,10 @@ math.expr.Scope.prototype = {
* @return {function} symbol
*/
createDef: function (name, value) {
if (this.readonly) {
throw new Error('Cannot create variable: Scope is read-only');
}
var symbol = this.defs[name];
if (!symbol) {
symbol = this.createSymbol(name);
@ -164,6 +183,10 @@ math.expr.Scope.prototype = {
* @return {function} symbol
*/
createUpdate: function (name) {
if (this.readonly) {
throw new Error('Cannot update variable: Scope is read-only');
}
var symbol = this.updates[name];
if (!symbol) {
symbol = this.createLink(name);

View File

@ -0,0 +1,22 @@
/**
* Evaluate an expression. The expression will be evaluated using a read-only
* instance of a Parser (i.e. variable definitions are not supported).
* @param {String} expr
* @return {*} res
*/
math.eval = function eval(expr) {
if (arguments.length != 1) {
throw newArgumentsError('eval', arguments.length, 1);
}
if (!isString(expr)) {
throw new TypeError('String expected');
}
return _readonlyParser.eval(expr);
};
/** @private */
var _readonlyParser = new math.expr.Parser({
readonly: true
});

View File

@ -122,5 +122,12 @@ assert.throws(function () {
assert.equal(parser.eval('q = 4/2'), 2);
assert.equal(parser.eval('g(3)'), 9);
// test read-only parser
var readonlyParser = new math.expr.Parser({readonly: true});
assert.equal(readonlyParser.get('pi'), Math.PI);
assert.throws(function () {readonlyParser.eval('b = 43');});
assert.throws(function () {readonlyParser.eval('function f(x) = a * x');});
assert.throws(function () {readonlyParser.eval('a([1,1])= [4]');});
assert.throws(function () {readonlyParser.set('a', 3)});
// TODO: extensively test the Parser

View File

@ -55,6 +55,16 @@ a.valueOf()[2].re = 5;
assert.equal(b.valueOf()[2].re, 2);
// test eval
assert.equal(math.eval('pi'), Math.PI);
assert.equal(math.eval('(2+3)/4'), 1.25);
assert.equal(math.eval('sqrt(-4)').toString(), '2i');
assert.throws(function () {math.eval('b = 43');});
assert.throws(function () {math.eval('function f(x) = a * x');});
assert.throws(function () {math.eval('a([1,1])= [4]');});
assert.throws(function () {math.set('a', 3)});
// test format
assert.equal(math.format(2/7), '0.2857142857');
assert.equal(math.format([[1,2],[3,4]]), '[[1, 2], [3, 4]]');