mirror of
https://github.com/josdejong/mathjs.git
synced 2026-01-25 15:07:57 +00:00
Parser implemented
This commit is contained in:
parent
61b2a45a9a
commit
c896eae319
@ -3,6 +3,5 @@ test
|
||||
img
|
||||
node_modules
|
||||
Jakefile.js
|
||||
math.min.js
|
||||
.idea
|
||||
.npmignore
|
||||
|
||||
11
Jakefile.js
11
Jakefile.js
@ -40,12 +40,19 @@ task('concat', function () {
|
||||
var result = util.concat({
|
||||
src: [
|
||||
'./src/exports.js',
|
||||
'./src/options.js',
|
||||
'./src/util.js',
|
||||
'./src/type/**/*.js',
|
||||
'./src/constants.js',
|
||||
'./src/functions.js',
|
||||
'./src/function/**/*.js'
|
||||
'./src/function/**/*.js',
|
||||
'./src/parser/node/Node.js',
|
||||
'./src/parser/node/Function.js',
|
||||
'./src/parser/node/Constant.js',
|
||||
'./src/parser/node/Block.js',
|
||||
'./src/parser/node/Assignment.js',
|
||||
'./src/parser/node/FunctionAssignment.js',
|
||||
'./src/parser/Scope.js',
|
||||
'./src/parser/Parser.js'
|
||||
],
|
||||
dest: MATHJS,
|
||||
header: util.read(HEADER) + '\n(function() {\n',
|
||||
|
||||
10
README.md
10
README.md
@ -3,16 +3,20 @@
|
||||
[http://mathjs.org](http://mathjs.org)
|
||||
|
||||
Math.js is an extensive math library for JavaScript and Node.js,
|
||||
compatible with the built-in Math library of JavaScript.
|
||||
compatible with JavaScript's built-in Math library.
|
||||
|
||||
Features:
|
||||
|
||||
## Features
|
||||
|
||||
- A flexible expression parser.
|
||||
- Supports numbers, complex values, units, strings, arrays, and matrices.
|
||||
- Supports numbers, complex values, units, strings, arrays**\***, and
|
||||
matrices**\***.
|
||||
- A large set of built-in functions and constants.
|
||||
- Easily extensible with new functions and constants.
|
||||
- Powerful and easy to use.
|
||||
|
||||
**\*** Note: arrays, and matrices are to be implemented.
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
|
||||
22
math.min.js
vendored
22
math.min.js
vendored
File diff suppressed because one or more lines are too long
@ -3,7 +3,12 @@
|
||||
*/
|
||||
var math = {
|
||||
type: {},
|
||||
parser: {}
|
||||
parser: {
|
||||
node: {}
|
||||
},
|
||||
options: {
|
||||
precision: 10 // number of decimals in formatted output
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -16,13 +16,13 @@ function divide(x, y) {
|
||||
}
|
||||
else if (y instanceof Complex) {
|
||||
// number / complex
|
||||
return divideComplex(new Complex(x), y);
|
||||
return divideComplex(new Complex(x, 0), y);
|
||||
}
|
||||
}
|
||||
else if (x instanceof Complex) {
|
||||
if (isNumber(y)) {
|
||||
// complex / number
|
||||
return divideComplex(x, new Complex(y));
|
||||
return divideComplex(x, new Complex(y, 0));
|
||||
}
|
||||
else if (y instanceof Complex) {
|
||||
// complex / complex
|
||||
|
||||
@ -18,7 +18,7 @@ function multiply(x, y) {
|
||||
}
|
||||
else if (y instanceof Complex) {
|
||||
// number * complex
|
||||
return multiplyComplex(new Complex(x), y);
|
||||
return multiplyComplex(new Complex(x, 0), y);
|
||||
}
|
||||
else if (y instanceof Unit) {
|
||||
res = y.copy();
|
||||
@ -29,7 +29,7 @@ function multiply(x, y) {
|
||||
else if (x instanceof Complex) {
|
||||
if (isNumber(y)) {
|
||||
// complex * number
|
||||
return multiplyComplex(x, new Complex(y));
|
||||
return multiplyComplex(x, new Complex(y, 0));
|
||||
}
|
||||
else if (y instanceof Complex) {
|
||||
// complex * complex
|
||||
|
||||
@ -16,16 +16,16 @@ function pow(x, y) {
|
||||
return Math.pow(x, y);
|
||||
}
|
||||
else {
|
||||
return powComplex(new Complex(x), new Complex(y));
|
||||
return powComplex(new Complex(x, 0), new Complex(y, 0));
|
||||
}
|
||||
}
|
||||
else if (y instanceof Complex) {
|
||||
return powComplex(new Complex(x), y);
|
||||
return powComplex(new Complex(x, 0), y);
|
||||
}
|
||||
}
|
||||
else if (x instanceof Complex) {
|
||||
if (isNumber(y)) {
|
||||
return powComplex(x, new Complex(y));
|
||||
return powComplex(x, new Complex(y, 0));
|
||||
}
|
||||
else if (y instanceof Complex) {
|
||||
return powComplex(x, y);
|
||||
|
||||
@ -65,7 +65,7 @@ math.round = round;
|
||||
* @return {Number} roundedValue
|
||||
*/
|
||||
function roundNumber (value, digits) {
|
||||
var p = Math.pow(10, (digits != undefined) ? digits : options.precision);
|
||||
var p = Math.pow(10, (digits != undefined) ? digits : math.options.precision);
|
||||
return Math.round(value * p) / p;
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,20 @@
|
||||
/**
|
||||
* math.js
|
||||
* An extended math library. Includes a parser, real and complex values, units,
|
||||
* matrices, strings, and a large set of functions and constants.
|
||||
* https://github.com/josdejong/mathjs
|
||||
*
|
||||
* Math.js is an extensive math library for JavaScript and Node.js,
|
||||
* compatible with JavaScript's built-in Math library.
|
||||
*
|
||||
* Features:
|
||||
* - A flexible expression parser
|
||||
* - Support for numbers, complex values, units, strings, arrays*,
|
||||
* and matrices*
|
||||
* - A large set of built-in functions and constants
|
||||
* - Easily extensible with new functions and constants
|
||||
* - Powerful and easy to use
|
||||
*
|
||||
* * Note: arrays and matrices are to be implemented.
|
||||
*
|
||||
* @version @@version
|
||||
* @date @@date
|
||||
*
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
/**
|
||||
* Settings for math.js
|
||||
*/
|
||||
|
||||
var options = {
|
||||
precision: 10 // number of decimals in formatted output
|
||||
};
|
||||
|
||||
math.options = options;
|
||||
1148
src/parser/Parser.js
Normal file
1148
src/parser/Parser.js
Normal file
File diff suppressed because it is too large
Load Diff
344
src/parser/Scope.js
Normal file
344
src/parser/Scope.js
Normal file
@ -0,0 +1,344 @@
|
||||
/**
|
||||
* @License Apache 2.0 License
|
||||
*
|
||||
* @Author Jos de Jong
|
||||
* @Date 2012-07-10
|
||||
*/
|
||||
|
||||
/**
|
||||
* Scope
|
||||
* A scope stores functions.
|
||||
*
|
||||
* @constructor mathnotepad.Scope
|
||||
* @param {Scope} [parentScope]
|
||||
*/
|
||||
function Scope(parentScope) {
|
||||
this.parentScope = parentScope;
|
||||
this.nestedScopes = undefined;
|
||||
|
||||
this.symbols = {}; // the actual symbols
|
||||
|
||||
// the following objects are just used to test existence.
|
||||
this.defs = {}; // definitions by name (for example "a = [1, 2; 3, 4]")
|
||||
this.updates = {}; // updates by name (for example "a(2, 1) = 5.2")
|
||||
this.links = {}; // links by name (for example "2 * a")
|
||||
}
|
||||
|
||||
math.parser.node.Scope = Scope;
|
||||
|
||||
/**
|
||||
* Create a nested scope
|
||||
* The variables in a nested scope are not accessible from the parent scope
|
||||
* @return {Scope} nestedScope
|
||||
*/
|
||||
Scope.prototype.createNestedScope = function () {
|
||||
var nestedScope = new Scope(this);
|
||||
if (!this.nestedScopes) {
|
||||
this.nestedScopes = [];
|
||||
}
|
||||
this.nestedScopes.push(nestedScope);
|
||||
return nestedScope;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear all symbols in this scope and its nested scopes
|
||||
* (parent scope will not be cleared)
|
||||
*/
|
||||
Scope.prototype.clear = function () {
|
||||
this.symbols = {};
|
||||
this.defs = {};
|
||||
this.links = {};
|
||||
this.updates = {};
|
||||
|
||||
if (this.nestedScopes) {
|
||||
var nestedScopes = this.nestedScopes;
|
||||
for (var i = 0, iMax = nestedScopes.length; i < iMax; i++) {
|
||||
nestedScopes[i].clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* create a symbol
|
||||
* @param {String} name
|
||||
* @return {function} symbol
|
||||
* @private
|
||||
*/
|
||||
Scope.prototype.createSymbol = function (name) {
|
||||
var symbol = this.symbols[name];
|
||||
if (!symbol) {
|
||||
// get a link to the last definition
|
||||
var lastDef = this.findDef(name);
|
||||
|
||||
// create a new symbol
|
||||
symbol = this.newSymbol(name, lastDef);
|
||||
this.symbols[name] = symbol;
|
||||
|
||||
}
|
||||
return symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new symbol
|
||||
* @param {String} name
|
||||
* @param {*} [value]
|
||||
* @return {function} symbol
|
||||
* @private
|
||||
*/
|
||||
Scope.prototype.newSymbol = function (name, value) {
|
||||
// create a new symbol
|
||||
var symbol = function () {
|
||||
if (!symbol.value) {
|
||||
throw new Error('Undefined symbol ' + name);
|
||||
}
|
||||
if (typeof symbol.value == 'function') {
|
||||
return symbol.value.apply(null, arguments);
|
||||
}
|
||||
else {
|
||||
// TODO: implement subset for all types
|
||||
return symbol.value;
|
||||
}
|
||||
};
|
||||
|
||||
symbol.value = value;
|
||||
|
||||
symbol.toString = function () {
|
||||
return symbol.value ? symbol.value.toString() : '';
|
||||
};
|
||||
|
||||
return symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* create a link to a value.
|
||||
* @param {String} name
|
||||
* @return {function} symbol
|
||||
*/
|
||||
Scope.prototype.createLink = function (name) {
|
||||
var symbol = this.links[name];
|
||||
if (!symbol) {
|
||||
symbol = this.createSymbol(name);
|
||||
this.links[name] = symbol;
|
||||
}
|
||||
return symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a variable definition
|
||||
* Returns the created symbol
|
||||
* @param {String} name
|
||||
* @return {function} symbol
|
||||
*/
|
||||
Scope.prototype.createDef = function (name) {
|
||||
var symbol = this.defs[name];
|
||||
if (!symbol) {
|
||||
symbol = this.createSymbol(name);
|
||||
this.defs[name] = symbol;
|
||||
}
|
||||
return symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a variable update definition
|
||||
* Returns the created symbol
|
||||
* @param {String} name
|
||||
* @return {function} symbol
|
||||
*/
|
||||
Scope.prototype.createUpdate = function (name) {
|
||||
var symbol = this.updates[name];
|
||||
if (!symbol) {
|
||||
symbol = this.createLink(name);
|
||||
this.updates[name] = symbol;
|
||||
}
|
||||
return symbol;
|
||||
};
|
||||
|
||||
/**
|
||||
* get the link to a symbol definition or update.
|
||||
* If the symbol is not found in this scope, it will be looked up in its parent
|
||||
* scope.
|
||||
* @param {String} name
|
||||
* @return {function | undefined} symbol, or undefined when not found
|
||||
*/
|
||||
Scope.prototype.findDef = function (name) {
|
||||
var symbol;
|
||||
|
||||
// check scope
|
||||
symbol = this.defs[name];
|
||||
if (symbol) {
|
||||
return symbol;
|
||||
}
|
||||
symbol = this.updates[name];
|
||||
if (symbol) {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
// check parent scope
|
||||
if (this.parentScope) {
|
||||
return this.parentScope.findDef(name);
|
||||
}
|
||||
else {
|
||||
// this is the root scope (has no parent)
|
||||
|
||||
var newSymbol = this.newSymbol,
|
||||
symbols = this.symbols,
|
||||
defs = this.defs;
|
||||
|
||||
/**
|
||||
* Store a symbol in the root scope
|
||||
* @param {String} name
|
||||
* @param {*} value
|
||||
* @return {function} symbol
|
||||
*/
|
||||
function put(name, value) {
|
||||
var symbol = newSymbol(name, value);
|
||||
symbols[name] = symbol;
|
||||
defs[name] = symbol;
|
||||
return symbol;
|
||||
}
|
||||
|
||||
// check constant (and load the constant)
|
||||
if (name == 'pi') {
|
||||
return put(name, math.PI);
|
||||
}
|
||||
if (name == 'e') {
|
||||
return put(name, math.E);
|
||||
}
|
||||
if (name == 'i') {
|
||||
return put(name, new Complex(0, 1));
|
||||
}
|
||||
|
||||
// check function (and load the function), for example "sin" or "sqrt"
|
||||
// search in the mathnotepad.math namespace for this symbol
|
||||
var fn = math[name];
|
||||
if (fn) {
|
||||
return put(name, fn);
|
||||
}
|
||||
|
||||
// Check if token is a unit
|
||||
// Note: we do not check the upper case name, units are case sensitive!
|
||||
if (Unit.isUnit(name)) {
|
||||
var unit = new Unit(undefined, name);
|
||||
return put(name, unit);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a link to a symbol
|
||||
* @param {String} name
|
||||
*/
|
||||
Scope.prototype.removeLink = function (name) {
|
||||
delete this.links[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a definition of a symbol
|
||||
* @param {String} name
|
||||
*/
|
||||
Scope.prototype.removeDef = function (name) {
|
||||
delete this.defs[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an update definition of a symbol
|
||||
* @param {String} name
|
||||
*/
|
||||
Scope.prototype.removeUpdate = function (name) {
|
||||
delete this.updates[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* initialize the scope and its nested scopes
|
||||
*
|
||||
* All functions are linked to their previous definition
|
||||
* If there is no parentScope, or no definition of the func in the parent scope,
|
||||
* the link will be set undefined
|
||||
*/
|
||||
Scope.prototype.init = function () {
|
||||
var symbols = this.symbols;
|
||||
var parentScope = this.parentScope;
|
||||
|
||||
for (var name in symbols) {
|
||||
if (symbols.hasOwnProperty(name)) {
|
||||
var symbol = symbols[name];
|
||||
symbol.set(parentScope ? parentScope.findDef(name) : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.nestedScopes) {
|
||||
this.nestedScopes.forEach(function (nestedScope) {
|
||||
nestedScope.init();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether this scope or any of its nested scopes contain a link to a
|
||||
* symbol with given name
|
||||
* @param {String} name
|
||||
* @return {boolean} hasLink True if a link with given name is found
|
||||
*/
|
||||
Scope.prototype.hasLink = function (name) {
|
||||
if (this.links[name]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.nestedScopes) {
|
||||
var nestedScopes = this.nestedScopes;
|
||||
for (var i = 0, iMax = nestedScopes.length; i < iMax; i++) {
|
||||
if (nestedScopes[i].hasLink(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether this scope contains a definition of a symbol with given name
|
||||
* @param {String} name
|
||||
* @return {boolean} hasDef True if a definition with given name is found
|
||||
*/
|
||||
Scope.prototype.hasDef = function (name) {
|
||||
return (this.defs[name] != undefined);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether this scope contains an update definition of a symbol with
|
||||
* given name
|
||||
* @param {String} name
|
||||
* @return {boolean} hasUpdate True if an update definition with given name is found
|
||||
*/
|
||||
Scope.prototype.hasUpdate = function (name) {
|
||||
return (this.updates[name] != undefined);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve all undefined symbols
|
||||
* @return {function[]} undefinedSymbols All symbols which are undefined
|
||||
*/
|
||||
Scope.prototype.getUndefinedSymbols = function () {
|
||||
var symbols = this.symbols;
|
||||
var undefinedSymbols = [];
|
||||
for (var i in symbols) {
|
||||
if (symbols.hasOwnProperty(i)) {
|
||||
var symbol = symbols[i];
|
||||
if (symbol.value == undefined) {
|
||||
undefinedSymbols.push(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.nestedScopes) {
|
||||
this.nestedScopes.forEach(function (nestedScope) {
|
||||
undefinedSymbols =
|
||||
undefinedSymbols.concat(nestedScope.getUndefinedSymbols());
|
||||
});
|
||||
}
|
||||
|
||||
return undefinedSymbols;
|
||||
};
|
||||
74
src/parser/node/Assignment.js
Normal file
74
src/parser/node/Assignment.js
Normal file
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @constructor mathnotepad.tree.Assignment
|
||||
* @param {String} name Symbol name
|
||||
* @param {Node[] | undefined} params Zero or more parameters
|
||||
* @param {Node} expr The expression defining the symbol
|
||||
* @param {function} result placeholder for the result
|
||||
*/
|
||||
function Assignment(name, params, expr, result) {
|
||||
this.name = name;
|
||||
this.params = params;
|
||||
this.expr = expr;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
Assignment.prototype = new Node();
|
||||
|
||||
math.parser.node.Assignment = Assignment;
|
||||
|
||||
/**
|
||||
* Evaluate the assignment
|
||||
* @return {*} result
|
||||
*/
|
||||
Assignment.prototype.eval = function() {
|
||||
if (this.expr === undefined) {
|
||||
throw new Error('Undefined symbol ' + this.name);
|
||||
}
|
||||
|
||||
var result;
|
||||
var params = this.params;
|
||||
|
||||
if (params && params.length) {
|
||||
// change part of a matrix, for example "a=[]", "a(2,3)=4.5"
|
||||
var paramResults = [];
|
||||
this.params.forEach(function (param) {
|
||||
paramResults.push(param.eval());
|
||||
});
|
||||
|
||||
var exprResult = this.expr.eval();
|
||||
|
||||
// test if definition is currently undefined
|
||||
if (this.result.value == undefined) {
|
||||
throw new Error('Undefined symbol ' + this.name);
|
||||
}
|
||||
|
||||
var prevResult = this.result.eval();
|
||||
result = prevResult.set(paramResults, exprResult);
|
||||
|
||||
this.result.value = result;
|
||||
}
|
||||
else {
|
||||
// variable definition, for example "a = 3/4"
|
||||
result = this.expr.eval();
|
||||
this.result.value = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String}
|
||||
*/
|
||||
Assignment.prototype.toString = function() {
|
||||
var str = '';
|
||||
|
||||
str += this.name;
|
||||
if (this.params && this.params.length) {
|
||||
str += '(' + this.params.join(', ') + ')';
|
||||
}
|
||||
str += ' = ';
|
||||
str += this.expr.toString();
|
||||
|
||||
return str;
|
||||
};
|
||||
59
src/parser/node/Block.js
Normal file
59
src/parser/node/Block.js
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @constructor math.parser.node.Block
|
||||
* Holds a set with nodes
|
||||
* @extends {Node}
|
||||
*/
|
||||
function Block() {
|
||||
this.params = [];
|
||||
this.visible = [];
|
||||
}
|
||||
|
||||
Block.prototype = new Node();
|
||||
|
||||
math.parser.node.Block = Block;
|
||||
|
||||
/**
|
||||
* Add a parameter
|
||||
* @param {Node} param
|
||||
* @param {Boolean} [visible] true by default
|
||||
*/
|
||||
Block.prototype.add = function (param, visible) {
|
||||
var index = this.params.length;
|
||||
this.params[index] = param;
|
||||
this.visible[index] = (visible != undefined) ? visible : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate the set
|
||||
* @return {*[]} results
|
||||
* @override
|
||||
*/
|
||||
Block.prototype.eval = function() {
|
||||
// evaluate the parameters
|
||||
var results = [];
|
||||
for (var i = 0, iMax = this.params.length; i < iMax; i++) {
|
||||
var result = this.params[i].eval();
|
||||
if (this.visible[i]) {
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String} str
|
||||
* @override
|
||||
*/
|
||||
Block.prototype.toString = function() {
|
||||
var strings = [];
|
||||
|
||||
for (var i = 0, iMax = this.params.length; i < iMax; i++) {
|
||||
if (this.visible[i]) {
|
||||
strings.push('\n ' + this.params[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
return '[' + strings.join(',') + '\n]';
|
||||
};
|
||||
28
src/parser/node/Constant.js
Normal file
28
src/parser/node/Constant.js
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @constructor math.parser.node.Constant
|
||||
* @param {*} value
|
||||
* @extends {Node}
|
||||
*/
|
||||
function Constant(value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
Constant.prototype = new Node();
|
||||
|
||||
math.parser.node.Constant = Constant;
|
||||
|
||||
/**
|
||||
* Evaluate the constant
|
||||
* @return {*} value
|
||||
*/
|
||||
Constant.prototype.eval = function () {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String} str
|
||||
*/
|
||||
Constant.prototype.toString = function() {
|
||||
return this.value ? this.value.toString() : '';
|
||||
};
|
||||
75
src/parser/node/Function.js
Normal file
75
src/parser/node/Function.js
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @constructor math.parser.node.Function
|
||||
* @param {String} [name]
|
||||
* @param {function} fn
|
||||
* @param {Node[]} params
|
||||
* @extends {Node}
|
||||
*/
|
||||
function Function(name, fn, params) {
|
||||
this.name = name;
|
||||
this.fn = fn;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
Function.prototype = new Node();
|
||||
|
||||
math.parser.node.Function = Function;
|
||||
|
||||
/**
|
||||
* Check whether the Function has one or multiple parameters set.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
Function.prototype.hasParams = function () {
|
||||
return (this.params != undefined && this.params.length > 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate the symbol
|
||||
* @return {*} result
|
||||
* @override
|
||||
*/
|
||||
Function.prototype.eval = function() {
|
||||
var fn = this.fn;
|
||||
if (fn === undefined) {
|
||||
throw new Error('Undefined symbol ' + this.name);
|
||||
}
|
||||
|
||||
// evaluate the parameters
|
||||
var results = this.params.map(function (param) {
|
||||
return param.eval();
|
||||
});
|
||||
|
||||
// evaluate the function
|
||||
return fn.apply(this, results);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String} str
|
||||
* @override
|
||||
*/
|
||||
Function.prototype.toString = function() {
|
||||
// variable. format the symbol like "myvar"
|
||||
if (this.name && !this.params) {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/* TODO: determine if the function is an operator
|
||||
// operator. format the operation like "(2 + 3)"
|
||||
if (this.fn && (this.fn instanceof mathnotepad.fn.Operator)) {
|
||||
if (this.params && this.params.length == 2) {
|
||||
return '(' +
|
||||
this.params[0].toString() + ' ' +
|
||||
this.name + ' ' +
|
||||
this.params[1].toString() + ')';
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// function. format the operation like "f(2, 4.2)"
|
||||
var str = this.name;
|
||||
if (this.params && this.params.length) {
|
||||
str += '(' + this.params.join(', ') + ')';
|
||||
}
|
||||
return str;
|
||||
};
|
||||
99
src/parser/node/FunctionAssignment.js
Normal file
99
src/parser/node/FunctionAssignment.js
Normal file
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @constructor FunctionAssignment
|
||||
* assigns a custom defined function
|
||||
*
|
||||
* @param {String} name Function name
|
||||
* @param {String[]} variableNames Variable names
|
||||
* @param {function[]} variables Links to the variables in a scope
|
||||
* @param {Node} expr The function expression
|
||||
* @param {function} result Link to store the result
|
||||
*/
|
||||
function FunctionAssignment(name, variableNames, variables, expr, result) {
|
||||
this.name = name;
|
||||
this.variables = variables;
|
||||
|
||||
this.values = [];
|
||||
for (var i = 0, iMax = this.variables.length; i < iMax; i++) {
|
||||
this.values[i] = (function () {
|
||||
var value = function () {
|
||||
return value.value;
|
||||
};
|
||||
value.value = undefined;
|
||||
return value;
|
||||
})();
|
||||
}
|
||||
|
||||
this.def = this.createFunction(name, variableNames, variables, expr);
|
||||
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
FunctionAssignment.prototype = new Node();
|
||||
|
||||
math.parser.node.FunctionAssignment = FunctionAssignment;
|
||||
|
||||
/**
|
||||
* Create a function from the function assignment
|
||||
* @param {String} name Function name
|
||||
* @param {String[]} variableNames Variable names
|
||||
* @param {function[]} values Zero or more functions returning a value
|
||||
* Each function contains a parameter
|
||||
* name of type String and value of
|
||||
* type mathnotepad.fn.Link
|
||||
* @param {Node} expr The function expression
|
||||
*
|
||||
*/
|
||||
FunctionAssignment.prototype.createFunction = function (name, variableNames,
|
||||
values, expr) {
|
||||
var fn = function () {
|
||||
// validate correct number of arguments
|
||||
var valuesNum = values ? values.length : 0;
|
||||
var argumentsNum = arguments ? arguments.length : 0;
|
||||
if (valuesNum != argumentsNum) {
|
||||
throw newArgumentsError(name, argumentsNum, valuesNum);
|
||||
}
|
||||
|
||||
// fill in all parameter values
|
||||
if (valuesNum > 0) {
|
||||
for (var i = 0; i < valuesNum; i++){
|
||||
values[i].value = arguments[i];
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate the expression
|
||||
return expr.eval();
|
||||
};
|
||||
|
||||
fn.toString = function() {
|
||||
return name + '(' + variableNames.join(', ') + ')';
|
||||
};
|
||||
|
||||
return fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate the function assignment
|
||||
* @return {function} result
|
||||
*/
|
||||
FunctionAssignment.prototype.eval = function() {
|
||||
// link the variables to the values of this function assignment
|
||||
var variables = this.variables,
|
||||
values = this.values;
|
||||
for (var i = 0, iMax = variables.length; i < iMax; i++) {
|
||||
variables[i].value = values[i];
|
||||
}
|
||||
|
||||
// put the definition in the result
|
||||
this.result.value = this.def;
|
||||
|
||||
// TODO: what to return? a neat "function y(x) defined"?
|
||||
return this.def;
|
||||
};
|
||||
|
||||
/**
|
||||
* get string representation
|
||||
* @return {String} str
|
||||
*/
|
||||
FunctionAssignment.prototype.toString = function() {
|
||||
return this.def.toString();
|
||||
};
|
||||
22
src/parser/node/Node.js
Normal file
22
src/parser/node/Node.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Node
|
||||
*/
|
||||
function Node() {}
|
||||
|
||||
math.parser.node.Node = Node;
|
||||
|
||||
/**
|
||||
* Evaluate the node
|
||||
* @return {*} result
|
||||
*/
|
||||
Node.prototype.eval = function () {
|
||||
throw new Error('Cannot evaluate a Node interface');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String}
|
||||
*/
|
||||
Node.prototype.toString = function() {
|
||||
return '';
|
||||
};
|
||||
@ -1,27 +1,133 @@
|
||||
/**
|
||||
* @constructor math.type.Complex
|
||||
* @constructor Complex
|
||||
*
|
||||
* @param {Number} [re]
|
||||
* @param {Number} [im]
|
||||
* A complex value can be constructed in three ways:
|
||||
* var a = new Complex(re, im);
|
||||
* var b = new Complex(str);
|
||||
* var c = new Complex();
|
||||
*
|
||||
* Example usage:
|
||||
* var a = new Complex(3, -4); // 3 - 4i
|
||||
* var b = new Complex('2 + 6i'); // 2 + 6i
|
||||
* var c = new Complex(); // 0 + 0i
|
||||
* var d = math.add(a, b); // 5 + 2i
|
||||
*
|
||||
* @param {Number | String} re A number with the real part of the complex
|
||||
* value, or a string containing a complex number
|
||||
* @param {Number} [im] The imaginary part of the complex value
|
||||
*/
|
||||
function Complex(re, im) {
|
||||
if (this.constructor != Complex) {
|
||||
throw new Error('Complex constructor must be called with the new operator');
|
||||
throw new SyntaxError(
|
||||
'Complex constructor must be called with the new operator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.re = re || 0;
|
||||
switch (arguments.length) {
|
||||
case 2:
|
||||
// re and im numbers provided
|
||||
if (!isNumber(re) || !isNumber(im)) {
|
||||
throw new TypeError(
|
||||
'Two numbers or a single string expected in Complex constructor');
|
||||
}
|
||||
this.re = re;
|
||||
this.im = im;
|
||||
break;
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
this.im = im || 0;
|
||||
case 1:
|
||||
// parse string into a complex number
|
||||
if (!isString(re)) {
|
||||
throw new TypeError(
|
||||
'Two numbers or a single string expected in Complex constructor');
|
||||
}
|
||||
|
||||
// TODO: replace by some nice regexp?
|
||||
// TODO: also support a pattern like "-2.5e+3 - 7.6e-5i"
|
||||
var parts = [],
|
||||
part;
|
||||
var separator = '+';
|
||||
var index = re.lastIndexOf(separator);
|
||||
if (index == -1) {
|
||||
separator = '-';
|
||||
index = re.lastIndexOf(separator);
|
||||
}
|
||||
|
||||
if (index != -1) {
|
||||
part = trim(re.substring(0, index));
|
||||
if (part) {
|
||||
parts.push(part);
|
||||
}
|
||||
part = trim(re.substring(index + 1));
|
||||
if (part) {
|
||||
parts.push(separator + part);
|
||||
}
|
||||
}
|
||||
else {
|
||||
part = trim(re);
|
||||
if (part) {
|
||||
parts.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
var ok = false;
|
||||
switch (parts.length) {
|
||||
case 1:
|
||||
part = parts[0];
|
||||
if (part[part.length - 1].toUpperCase() == 'I') {
|
||||
// complex number
|
||||
this.re = 0;
|
||||
this.im = Number(part.substring(0, part.length - 1));
|
||||
ok = !isNaN(this.im);
|
||||
}
|
||||
else {
|
||||
// real number
|
||||
this.re = Number(part);
|
||||
this.im = 0;
|
||||
ok = !isNaN(this.re);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
part = parts[0];
|
||||
this.re = Number(parts[0]);
|
||||
this.im = Number(parts[1].substring(0, parts[1].length - 1));
|
||||
ok = !isNaN(this.re) && !isNaN(this.im) &&
|
||||
(parts[1][parts[1].length - 1].toUpperCase() == 'I');
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: allow '+3-2'
|
||||
|
||||
if (!ok) {
|
||||
throw new SyntaxError('Invalid string "' + re + '"');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0:
|
||||
// nul values
|
||||
this.re = 0;
|
||||
this.im = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new SyntaxError(
|
||||
'Wrong number of arguments in Complex constructor ' +
|
||||
'(' + arguments.length + ' provided, 0, 1, or 2 expected)');
|
||||
}
|
||||
}
|
||||
|
||||
math.type.Complex = Complex;
|
||||
|
||||
/**
|
||||
* Trim a string
|
||||
* http://stackoverflow.com/a/498995/1262753
|
||||
* @param str
|
||||
* @return {*|void}
|
||||
*/
|
||||
function trim(str) {
|
||||
return str.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether value is a Complex value
|
||||
* @param {*} value
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* The build in Number object of Javascript is used.
|
||||
* Utility functions for Numbers
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* The build in String object of Javascript is used.
|
||||
* Utility functions for Strings
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @constructor math.type.Unit
|
||||
* @constructor Unit
|
||||
*
|
||||
* @param {Number} [value] A value for the unit, like 5.2
|
||||
* @param {String} [prefixUnit] A unit like "cm" or "inch"
|
||||
|
||||
@ -159,4 +159,4 @@ if (!Array.prototype.map) {
|
||||
// 9. return A
|
||||
return A;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,9 @@
|
||||
console.log('sqrt(25) = ' + math.sqrt(25));
|
||||
console.log('sqrt(' + complex1.toString() + ') = ' + math.sqrt(complex1));
|
||||
console.log('sqrt(-4) = ' + math.sqrt(-4));
|
||||
|
||||
var p = new math.parser.Parser();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -9,21 +9,49 @@ var complex1 = new Complex(3, -4);
|
||||
assert.equal(complex1.re, 3);
|
||||
assert.equal(complex1.im, -4);
|
||||
assert.equal(complex1, '3 - 4i');
|
||||
assert.throws(function () { Complex(3, -4); });
|
||||
|
||||
// test constructor
|
||||
assert.equal(new Complex().toString(), '0');
|
||||
assert.equal(new Complex(2, 3).toString(), '2 + 3i');
|
||||
assert.equal(new Complex(2, 0).toString(), '2');
|
||||
assert.equal(new Complex(0, 3).toString(), '3i');
|
||||
assert.equal(new Complex('2 + 3i').toString(), '2 + 3i');
|
||||
assert.equal(new Complex('2 +3i').toString(), '2 + 3i');
|
||||
assert.equal(new Complex('2+3i').toString(), '2 + 3i');
|
||||
assert.equal(new Complex(' 2+3i ').toString(), '2 + 3i');
|
||||
assert.equal(new Complex('2-3i').toString(), '2 - 3i');
|
||||
assert.equal(new Complex('-2-3i').toString(), '-2 - 3i');
|
||||
assert.equal(new Complex('-2+3i').toString(), '-2 + 3i');
|
||||
assert.equal(new Complex('-2+3i').toString(), '-2 + 3i');
|
||||
//assert.equal(new Complex('-2+3e-1i').toString(), '-2 + 0.3i'); // TODO
|
||||
//assert.equal(new Complex('-2+3e+1i').toString(), '-2 + 30i'); // TODO
|
||||
//assert.equal(new Complex('2+3e2i').toString(), '2 + 300i'); // TODO
|
||||
//assert.equal(new Complex('2.2e-1-3.2e-1i').toString(), '0.22 - 0.32i'); // TODO
|
||||
assert.equal(new Complex('2').toString(), '2');
|
||||
assert.equal(new Complex('-2').toString(), '-2');
|
||||
assert.equal(new Complex('3i').toString(), '3i');
|
||||
assert.equal(new Complex('-3i').toString(), '-3i');
|
||||
assert.throws(function () { new Complex(1, 2, 3); });
|
||||
assert.throws(function () { new Complex("str", 2); });
|
||||
assert.throws(function () { new Complex(1, true); });
|
||||
assert.throws(function () { new Complex(2); });
|
||||
assert.throws(function () { new Complex(""); });
|
||||
assert.throws(function () { new Complex("2r"); });
|
||||
assert.throws(function () { new Complex("str"); });
|
||||
assert.throws(function () { new Complex("2i+3i"); });
|
||||
|
||||
assert.throws(function () {
|
||||
Complex(3, -4);
|
||||
});
|
||||
|
||||
// test toString
|
||||
assert.equal(new Complex(), '0');
|
||||
assert.equal(new Complex(0, 2), '2i');
|
||||
assert.equal(new Complex(1, 1), '1 + i');
|
||||
assert.equal(new Complex(1, 2), '1 + 2i');
|
||||
assert.equal(new Complex(1, -1), '1 - i');
|
||||
assert.equal(new Complex(1, -2), '1 - 2i');
|
||||
assert.equal(new Complex(1, 0), '1');
|
||||
assert.equal(new Complex(-1, 2), '-1 + 2i');
|
||||
assert.equal(new Complex(-1, 1), '-1 + i');
|
||||
assert.equal(new Complex().toString(), '0');
|
||||
assert.equal(new Complex(0, 2).toString(), '2i');
|
||||
assert.equal(new Complex(1, 1).toString(), '1 + i');
|
||||
assert.equal(new Complex(1, 2).toString(), '1 + 2i');
|
||||
assert.equal(new Complex(1, -1).toString(), '1 - i');
|
||||
assert.equal(new Complex(1, -2).toString(), '1 - 2i');
|
||||
assert.equal(new Complex(1, 0).toString(), '1');
|
||||
assert.equal(new Complex(-1, 2).toString(), '-1 + 2i');
|
||||
assert.equal(new Complex(-1, 1).toString(), '-1 + i');
|
||||
|
||||
// test copy
|
||||
var copy = complex1.copy();
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* Dependencies:
|
||||
* jake
|
||||
* uglify-js
|
||||
* date-utils
|
||||
* dateable
|
||||
*
|
||||
* Usage:
|
||||
* var util = require('jake-utils');
|
||||
@ -64,8 +64,8 @@ function version() {
|
||||
* {pattern: '@@version', replacement: '1.4.0'}
|
||||
* },
|
||||
* src: [
|
||||
* 'file1.js',
|
||||
* 'file2.js'
|
||||
* 'main.js',
|
||||
* 'other/*.js'
|
||||
* ]
|
||||
* });
|
||||
*
|
||||
@ -137,8 +137,10 @@ function replace (params) {
|
||||
* './src/extra.js',
|
||||
* './src/functions/**',
|
||||
* ],
|
||||
* dest: './lib/mylibrary.js',
|
||||
* header: '// license information...'
|
||||
* dest: './lib/mylibrary.js', // optional
|
||||
* header: '// license information...', // optional
|
||||
* separator: '\n', // optional
|
||||
* footer: '// the end...' // optional
|
||||
* });
|
||||
*
|
||||
* @param {Object} params Object containing:
|
||||
@ -207,8 +209,11 @@ function concat (params) {
|
||||
* src: [
|
||||
* './lib/mylibrary.js'
|
||||
* ],
|
||||
* dest: './lib/mylibrary.min.js',
|
||||
* header: '// license information...'
|
||||
* dest: './lib/mylibrary.min.js', // optional
|
||||
* options: {}, // uglify-js options. optional
|
||||
* header: '// license information...', // optional
|
||||
* separator: '\n', // optional
|
||||
* footer: '// the end...' // optional
|
||||
* });
|
||||
*
|
||||
* @param {Object} params Object containing:
|
||||
@ -228,7 +233,7 @@ function concat (params) {
|
||||
* @return {Object} res Result information. The object contains:
|
||||
* {String[]} src List with the filenames of the
|
||||
* files which are minified
|
||||
* {String} code The contents of the concatenated
|
||||
* {String} code The contents of the minified
|
||||
* file.
|
||||
*/
|
||||
function minify (params) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user