mirror of
https://github.com/josdejong/mathjs.git
synced 2025-12-08 19:46:04 +00:00
188 lines
4.4 KiB
JavaScript
188 lines
4.4 KiB
JavaScript
var Unit = require('../type/Unit.js');
|
|
|
|
/**
|
|
* Scope
|
|
* A scope stores values of symbols: variables and functions.
|
|
*
|
|
* Syntax:
|
|
* var scope = new Scope(math);
|
|
* var scope = new Scope(math, parentScope);
|
|
* var scope = new Scope(math, symbols);
|
|
* var scope = new Scope(math, parentScope, symbols);
|
|
*
|
|
* Where:
|
|
* {Object} math Link to the (static) math.js namespace
|
|
* {Scope | Object} parentScope Scope will be linked to a parent scope,
|
|
* which is traversed when resolving
|
|
* symbols.
|
|
* {Object} symbols A custom object that will be used to
|
|
* resolve and store variables.
|
|
*
|
|
* @constructor Scope
|
|
* @param {...} [math]
|
|
* @param {*} [arg1]
|
|
* @param {*} [arg2]
|
|
*/
|
|
function Scope(math, arg1, arg2) {
|
|
this.math = math;
|
|
|
|
/** @type {Scope} */
|
|
this.parentScope = null;
|
|
// TODO: rename parentScope to previousScope, add a nextScope, change Scope to a linked list node
|
|
|
|
/** @type {Scope[]} */
|
|
this.subScopes = null;
|
|
// TODO: rename subScopes to childScopes (or childNodes?)
|
|
|
|
/** @type {Object.<String, *>} */
|
|
this.symbols = {}; // variables and functions
|
|
|
|
/** @type {Object.<String, Object>} */
|
|
this.cache = {}; // cache, referring to the scope.symbols object where
|
|
// a variable was last found
|
|
|
|
// read second argument (can be parentScope or symbols map)
|
|
if (arg1) {
|
|
if (arg1 instanceof Scope) {
|
|
this.parentScope = arg1;
|
|
}
|
|
else if (arg1 instanceof Object) {
|
|
this.symbols = arg1;
|
|
}
|
|
}
|
|
|
|
// read second argument (can be symbols map)
|
|
if (arg2) {
|
|
if (arg2 instanceof Object) {
|
|
this.symbols = arg2;
|
|
}
|
|
}
|
|
}
|
|
|
|
Scope.prototype = {
|
|
/**
|
|
* Create a sub scope
|
|
* The variables in a sub scope are not accessible from the parent scope
|
|
* @return {Scope} subScope
|
|
*/
|
|
createSubScope: function () {
|
|
var subScope = new Scope(this.math, this);
|
|
if (!this.subScopes) {
|
|
this.subScopes = [];
|
|
}
|
|
this.subScopes.push(subScope);
|
|
return subScope;
|
|
},
|
|
|
|
/**
|
|
* Get a symbol value by name.
|
|
* Returns undefined if the symbol is not found in this scope or any of
|
|
* its parent scopes.
|
|
* @param {String} name
|
|
* @returns {* | undefined} value
|
|
*/
|
|
get: function (name) {
|
|
var value;
|
|
|
|
// check itself
|
|
value = this.symbols[name];
|
|
if (value !== undefined) {
|
|
return value;
|
|
}
|
|
|
|
// read from cache
|
|
var symbols = this.cache[name];
|
|
if (symbols) {
|
|
return symbols[name];
|
|
}
|
|
|
|
// check parent scope
|
|
var parent = this.parentScope;
|
|
while (parent) {
|
|
value = parent.symbols[name];
|
|
if (value !== undefined) {
|
|
this.cache[name] = parent.symbols;
|
|
return value;
|
|
}
|
|
parent = parent.parentScope;
|
|
}
|
|
|
|
// check static context
|
|
value = this.math[name];
|
|
if (value !== undefined) {
|
|
this.cache[name] = this.math;
|
|
return value;
|
|
}
|
|
|
|
// check if name is a unit
|
|
if (Unit.isPlainUnit(name)) {
|
|
value = new Unit(null, name);
|
|
this.cache[name] = {};
|
|
this.cache[name][name] = value;
|
|
return value;
|
|
}
|
|
|
|
return undefined;
|
|
},
|
|
|
|
/**
|
|
* Test whether this scope contains a symbol (will not check parent scopes)
|
|
* @param {String} name
|
|
* @return {Boolean} hasSymbol
|
|
*/
|
|
has: function (name) {
|
|
return (this.symbols[name] !== undefined);
|
|
},
|
|
|
|
/**
|
|
* Set a symbol value
|
|
* @param {String} name
|
|
* @param {*} value
|
|
* @return {*} value
|
|
*/
|
|
set: function (name, value) {
|
|
return this.symbols[name] = value;
|
|
},
|
|
|
|
/**
|
|
* Remove a symbol by name
|
|
* @param {String} name
|
|
*/
|
|
remove: function(name) {
|
|
delete this.symbols[name];
|
|
},
|
|
|
|
/**
|
|
* Clear all symbols in this scope, its sub scopes, and clear the cache.
|
|
* Parent scopes will not be cleared.
|
|
*/
|
|
clear: function () {
|
|
var symbols = this.symbols;
|
|
for (var name in symbols) {
|
|
if (symbols.hasOwnProperty(name)) {
|
|
delete symbols[name];
|
|
}
|
|
}
|
|
|
|
if (this.subScopes) {
|
|
var subScopes = this.subScopes;
|
|
for (var i = 0, iMax = subScopes.length; i < iMax; i++) {
|
|
subScopes[i].clear();
|
|
}
|
|
}
|
|
|
|
this.clearCache();
|
|
},
|
|
|
|
/**
|
|
* Clear cached links to symbols in other scopes
|
|
*/
|
|
clearCache: function () {
|
|
this.cache = {};
|
|
}
|
|
};
|
|
|
|
Scope.context = []; // static context, for example the math namespace
|
|
|
|
module.exports = Scope;
|