/
SymbolNode.js
139 lines (120 loc) · 4.02 KB
/
SymbolNode.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
'use strict';
var latex = require('../../utils/latex');
function factory (type, config, load, typed, math) {
var Node = load(require('./Node'));
var Unit = load(require('../../type/unit/Unit'));
/**
* @constructor SymbolNode
* @extends {Node}
* A symbol node can hold and resolve a symbol
* @param {string} name
* @extends {Node}
*/
function SymbolNode(name) {
if (!(this instanceof SymbolNode)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
// validate input
if (typeof name !== 'string') throw new TypeError('String expected for parameter "name"');
this.name = name;
}
SymbolNode.prototype = new Node();
SymbolNode.prototype.type = 'SymbolNode';
SymbolNode.prototype.isSymbolNode = true;
/**
* Compile the node to javascript code
* @param {Object} defs Object which can be used to define functions
* or constants globally available for the compiled
* expression
* @param {Object} args Object with local function arguments, the key is
* the name of the argument, and the value is `true`.
* The object may not be mutated, but must be
* extended instead.
* @return {string} js
* @private
*/
SymbolNode.prototype._compile = function (defs, args) {
// add a function to the definitions
defs['undef'] = undef;
defs['Unit'] = Unit;
if (args[this.name]) {
// this is a FunctionAssignment argument
// (like an x when inside the expression of a function assignment `f(x) = ...`)
return this.name;
}
else if (this.name in defs.math) {
return '("' + this.name + '" in scope ? scope["' + this.name + '"] : math["' + this.name + '"])';
}
else {
return '(' +
'"' + this.name + '" in scope ? scope["' + this.name + '"] : ' +
(Unit.isValuelessUnit(this.name) ?
'new Unit(null, "' + this.name + '")' :
'undef("' + this.name + '")') +
')';
}
};
/**
* Execute a callback for each of the child nodes of this node
* @param {function(child: Node, path: string, parent: Node)} callback
*/
SymbolNode.prototype.forEach = function (callback) {
// nothing to do, we don't have childs
};
/**
* Create a new SymbolNode having it's childs be the results of calling
* the provided callback function for each of the childs of the original node.
* @param {function(child: Node, path: string, parent: Node) : Node} callback
* @returns {SymbolNode} Returns a clone of the node
*/
SymbolNode.prototype.map = function (callback) {
return this.clone();
};
/**
* Throws an error 'Undefined symbol {name}'
* @param {string} name
*/
function undef (name) {
throw new Error('Undefined symbol ' + name);
}
/**
* Create a clone of this node, a shallow copy
* @return {SymbolNode}
*/
SymbolNode.prototype.clone = function() {
return new SymbolNode(this.name);
};
/**
* Get string representation
* @param {Object} options
* @return {string} str
* @override
*/
SymbolNode.prototype._toString = function(options) {
return this.name;
};
/**
* Get LaTeX representation
* @param {Object} options
* @return {string} str
* @override
*/
SymbolNode.prototype._toTex = function(options) {
var isUnit = false;
if ((typeof math[this.name] === 'undefined') && Unit.isValuelessUnit(this.name)) {
isUnit = true;
}
var symbol = latex.toSymbol(this.name, isUnit);
if (symbol[0] === '\\') {
//no space needed if the symbol starts with '\'
return symbol;
}
//the space prevents symbols from breaking stuff like '\cdot' if it's written right before the symbol
return ' ' + symbol;
};
return SymbolNode;
}
exports.name = 'SymbolNode';
exports.path = 'expression.node';
exports.math = true; // request access to the math namespace as 5th argument of the factory function
exports.factory = factory;