Parser implemented

This commit is contained in:
josdejong 2013-02-23 23:45:51 +01:00
parent 61b2a45a9a
commit c896eae319
28 changed files with 4305 additions and 115 deletions

View File

@ -3,6 +3,5 @@ test
img
node_modules
Jakefile.js
math.min.js
.idea
.npmignore

View File

@ -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',

View File

@ -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

2266
math.js

File diff suppressed because it is too large Load Diff

22
math.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,12 @@
*/
var math = {
type: {},
parser: {}
parser: {
node: {}
},
options: {
precision: 10 // number of decimals in formatted output
}
};
/**

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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
*

View File

@ -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

File diff suppressed because it is too large Load Diff

344
src/parser/Scope.js Normal file
View 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;
};

View 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
View 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]';
};

View 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() : '';
};

View 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;
};

View 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
View 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 '';
};

View File

@ -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

View File

@ -1,5 +1,5 @@
/**
* The build in Number object of Javascript is used.
* Utility functions for Numbers
*/

View File

@ -1,5 +1,5 @@
/**
* The build in String object of Javascript is used.
* Utility functions for Strings
*/
/**

View File

@ -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"

View File

@ -159,4 +159,4 @@ if (!Array.prototype.map) {
// 9. return A
return A;
};
}
}

View File

@ -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>

View File

@ -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();

View File

@ -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) {