exprify 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -33
- package/dist/exprify.cjs.js +671 -4
- package/dist/exprify.cjs.js.map +1 -1
- package/dist/exprify.esm.js +671 -4
- package/dist/exprify.esm.js.map +1 -1
- package/dist/exprify.js +672 -5
- package/dist/exprify.js.map +1 -1
- package/dist/exprify.min.js +2 -2
- package/dist/exprify.min.js.map +1 -1
- package/docs/README.md +34 -0
- package/docs/assets/css/style.scss +4 -0
- package/docs/tokenType.txt +21 -0
- package/package.json +1 -1
- package/.gitattributes +0 -2
- package/.github/workflows/ci.yml +0 -40
- package/.github/workflows/npm-publish.yml +0 -38
- package/.github/workflows/security-audit.yml +0 -34
- package/CHANGELOG.md +0 -11
- package/doc/tokenType.txt +0 -48
- package/rollup.config.js +0 -80
- package/src/assets/capture.jpg +0 -0
- package/src/core/Exprify.js +0 -140
- package/src/core/context.js +0 -30
- package/src/function/executor.js +0 -64
- package/src/function/internal.js +0 -270
- package/src/function/registry.js +0 -68
- package/src/index.js +0 -2
- package/src/math/operations.js +0 -38
- package/src/parser/astBuild.js +0 -508
- package/src/parser/evaluator.js +0 -430
- package/src/parser/tokenizer.js +0 -399
- package/src/utils/globalUnits.js +0 -217
- package/src/utils/store.js +0 -178
- package/src/variables/store.js +0 -75
- package/test/browser.html +0 -23
- package/test/exprify.test.js +0 -140
package/src/function/internal.js
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
function validateSquareMatrix(matrix) {
|
|
2
|
-
if (!Array.isArray(matrix) || matrix.length === 0) {
|
|
3
|
-
throw new Error("det() expects a non-empty matrix");
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
if (!matrix.every(Array.isArray)) {
|
|
7
|
-
throw new Error("det() expects a 2D matrix");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const size = matrix.length;
|
|
11
|
-
if (!matrix.every((row) => row.length === size)) {
|
|
12
|
-
throw new Error("det() expects a square matrix");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
for (const row of matrix) {
|
|
16
|
-
for (const value of row) {
|
|
17
|
-
if (typeof value !== "number" && typeof value !== "bigint") {
|
|
18
|
-
throw new Error("det() matrix values must be numeric");
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function determinant(matrix) {
|
|
25
|
-
validateSquareMatrix(matrix);
|
|
26
|
-
|
|
27
|
-
if (matrix.length === 1) {
|
|
28
|
-
return matrix[0][0];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (matrix.length === 2) {
|
|
32
|
-
return (matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0]);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return matrix[0].reduce((sum, value, columnIndex) => {
|
|
36
|
-
const minor = matrix.slice(1).map((row) =>
|
|
37
|
-
row.filter((_, index) => index !== columnIndex)
|
|
38
|
-
);
|
|
39
|
-
const cofactor = columnIndex % 2 === 0 ? value : -value;
|
|
40
|
-
return sum + (cofactor * determinant(minor));
|
|
41
|
-
}, 0);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function splitTerms(expression) {
|
|
45
|
-
const normalized = expression.replace(/\s+/g, "");
|
|
46
|
-
if (!normalized) {
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return normalized
|
|
51
|
-
.replace(/-/g, "+-")
|
|
52
|
-
.split("+")
|
|
53
|
-
.filter(Boolean);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function parsePolynomial(expression, variable) {
|
|
57
|
-
const terms = splitTerms(expression);
|
|
58
|
-
const coefficients = new Map();
|
|
59
|
-
|
|
60
|
-
for (const term of terms) {
|
|
61
|
-
if (term.includes(variable)) {
|
|
62
|
-
const [rawCoeff, rawPower] = term.split(variable);
|
|
63
|
-
let coefficient;
|
|
64
|
-
|
|
65
|
-
if (rawCoeff === "" || rawCoeff === "+") coefficient = 1;
|
|
66
|
-
else if (rawCoeff === "-") coefficient = -1;
|
|
67
|
-
else {
|
|
68
|
-
const cleaned = rawCoeff.endsWith("*") ? rawCoeff.slice(0, -1) : rawCoeff;
|
|
69
|
-
coefficient = Number(cleaned);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!Number.isFinite(coefficient)) {
|
|
73
|
-
throw new Error("Unsupported algebra term");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let power = 1;
|
|
77
|
-
if (rawPower) {
|
|
78
|
-
if (!rawPower.startsWith("^")) {
|
|
79
|
-
throw new Error("Unsupported algebra term");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
power = Number(rawPower.slice(1));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!Number.isInteger(power) || power < 0) {
|
|
86
|
-
throw new Error("Only non-negative integer powers are supported");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
coefficients.set(power, (coefficients.get(power) || 0) + coefficient);
|
|
90
|
-
} else {
|
|
91
|
-
const constant = Number(term);
|
|
92
|
-
if (!Number.isFinite(constant)) {
|
|
93
|
-
throw new Error("Unsupported algebra term");
|
|
94
|
-
}
|
|
95
|
-
coefficients.set(0, (coefficients.get(0) || 0) + constant);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return coefficients;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function formatPolynomial(coefficients, variable) {
|
|
103
|
-
const ordered = [...coefficients.entries()]
|
|
104
|
-
.filter(([, coefficient]) => coefficient !== 0)
|
|
105
|
-
.sort((a, b) => b[0] - a[0]);
|
|
106
|
-
|
|
107
|
-
if (!ordered.length) {
|
|
108
|
-
return "0";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return ordered.map(([power, coefficient], index) => {
|
|
112
|
-
const negative = coefficient < 0;
|
|
113
|
-
const absCoeff = Math.abs(coefficient);
|
|
114
|
-
let body;
|
|
115
|
-
|
|
116
|
-
if (power === 0) {
|
|
117
|
-
body = `${absCoeff}`;
|
|
118
|
-
} else if (power === 1) {
|
|
119
|
-
body = absCoeff === 1 ? variable : `${absCoeff} * ${variable}`;
|
|
120
|
-
} else {
|
|
121
|
-
body = absCoeff === 1
|
|
122
|
-
? `${variable}^${power}`
|
|
123
|
-
: `${absCoeff} * ${variable}^${power}`;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (index === 0) {
|
|
127
|
-
return negative ? `-${body}` : body;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return negative ? `- ${body}` : `+ ${body}`;
|
|
131
|
-
}).join(" ");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function simplifyExpression(expression) {
|
|
135
|
-
const compact = expression.replace(/\s+/g, "");
|
|
136
|
-
const variableMatch = compact.match(/[a-zA-Z]+/);
|
|
137
|
-
const variable = variableMatch?.[0] || "x";
|
|
138
|
-
const coefficients = parsePolynomial(expression, variable);
|
|
139
|
-
return formatPolynomial(coefficients, variable);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function derivativeExpression(expression, variable) {
|
|
143
|
-
const coefficients = parsePolynomial(expression, variable);
|
|
144
|
-
const derived = new Map();
|
|
145
|
-
|
|
146
|
-
for (const [power, coefficient] of coefficients.entries()) {
|
|
147
|
-
if (power === 0) continue;
|
|
148
|
-
derived.set(power - 1, (derived.get(power - 1) || 0) + (coefficient * power));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return formatPolynomial(derived, variable);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export const internalFunctions = {
|
|
155
|
-
max: (...args) => {
|
|
156
|
-
if (!args.length) throw new Error("max() requires arguments");
|
|
157
|
-
return Math.max(...args);
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
min: (...args) => {
|
|
161
|
-
if (!args.length) throw new Error("min() requires arguments");
|
|
162
|
-
return Math.min(...args);
|
|
163
|
-
},
|
|
164
|
-
|
|
165
|
-
abs: (x) => Math.abs(x),
|
|
166
|
-
|
|
167
|
-
round: (x) => Math.round(x),
|
|
168
|
-
|
|
169
|
-
floor: (x) => Math.floor(x),
|
|
170
|
-
|
|
171
|
-
ceil: (x) => Math.ceil(x),
|
|
172
|
-
|
|
173
|
-
sqrt: (x) => {
|
|
174
|
-
if (x < 0) throw new Error("sqrt() domain error");
|
|
175
|
-
return Math.sqrt(x);
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
pow: (a, b) => a ** b,
|
|
179
|
-
det: (matrix) => determinant(matrix),
|
|
180
|
-
simplify: (expression) => {
|
|
181
|
-
if (typeof expression !== "string") {
|
|
182
|
-
throw new Error("simplify() expects an expression string");
|
|
183
|
-
}
|
|
184
|
-
return simplifyExpression(expression);
|
|
185
|
-
},
|
|
186
|
-
derivative: (expression, variable = "x") => {
|
|
187
|
-
if (typeof expression !== "string" || typeof variable !== "string") {
|
|
188
|
-
throw new Error("derivative() expects expression and variable strings");
|
|
189
|
-
}
|
|
190
|
-
return derivativeExpression(expression, variable);
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
/* ================= TRIGONOMETRY ================= */
|
|
194
|
-
|
|
195
|
-
sin: (x) => Math.sin(x),
|
|
196
|
-
cos: (x) => Math.cos(x),
|
|
197
|
-
tan: (x) => Math.tan(x),
|
|
198
|
-
|
|
199
|
-
asin: (x) => Math.asin(x),
|
|
200
|
-
acos: (x) => Math.acos(x),
|
|
201
|
-
atan: (x) => Math.atan(x),
|
|
202
|
-
|
|
203
|
-
/* ================= LOG / EXP ================= */
|
|
204
|
-
|
|
205
|
-
log: (x) => {
|
|
206
|
-
if (x <= 0) throw new Error("log() domain error");
|
|
207
|
-
return Math.log(x);
|
|
208
|
-
},
|
|
209
|
-
|
|
210
|
-
log10: (x) => {
|
|
211
|
-
if (x <= 0) throw new Error("log10() domain error");
|
|
212
|
-
return Math.log10(x);
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
exp: (x) => Math.exp(x),
|
|
216
|
-
|
|
217
|
-
/* ================= RANDOM ================= */
|
|
218
|
-
|
|
219
|
-
random: () => Math.random(),
|
|
220
|
-
|
|
221
|
-
/* ================= BOOLEAN / LOGIC ================= */
|
|
222
|
-
|
|
223
|
-
and: (a, b) => Boolean(a && b),
|
|
224
|
-
|
|
225
|
-
or: (a, b) => Boolean(a || b),
|
|
226
|
-
|
|
227
|
-
not: (a) => !a,
|
|
228
|
-
"!": (a) => !a,
|
|
229
|
-
|
|
230
|
-
/* ================= COMPARISON ================= */
|
|
231
|
-
|
|
232
|
-
eq: (a, b) => a === b,
|
|
233
|
-
|
|
234
|
-
neq: (a, b) => a !== b,
|
|
235
|
-
"notEqual": (a, b) => a !== b,
|
|
236
|
-
|
|
237
|
-
gt: (a, b) => a > b,
|
|
238
|
-
"greaterThan": (a, b) => a > b,
|
|
239
|
-
|
|
240
|
-
lt: (a, b) => a < b,
|
|
241
|
-
"lessThan": (a, b) => a < b,
|
|
242
|
-
|
|
243
|
-
gte: (a, b) => a >= b,
|
|
244
|
-
"greaterThanOrEqual": (a, b) => a >= b,
|
|
245
|
-
|
|
246
|
-
lte: (a, b) => a <= b,
|
|
247
|
-
"lessThanOrEqual": (a, b) => a <= b,
|
|
248
|
-
|
|
249
|
-
/* ================= UTILITY ================= */
|
|
250
|
-
|
|
251
|
-
clamp: (x, min, max) => {
|
|
252
|
-
if (min > max) throw new Error("clamp(): min > max");
|
|
253
|
-
return Math.min(Math.max(x, min), max);
|
|
254
|
-
},
|
|
255
|
-
|
|
256
|
-
if: (condition, a, b) => (condition ? a : b),
|
|
257
|
-
|
|
258
|
-
/* ================= TYPE ================= */
|
|
259
|
-
|
|
260
|
-
typeof: (x) => typeof x,
|
|
261
|
-
|
|
262
|
-
/* ================= STRING ================= */
|
|
263
|
-
|
|
264
|
-
length: (x) => {
|
|
265
|
-
if (typeof x === "string" || Array.isArray(x)) {
|
|
266
|
-
return x.length;
|
|
267
|
-
}
|
|
268
|
-
throw new Error("length() expects string or array");
|
|
269
|
-
}
|
|
270
|
-
};
|
package/src/function/registry.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
export function createFunctionRegistry(initial = {}) {
|
|
2
|
-
const store = Object.create(null);
|
|
3
|
-
|
|
4
|
-
for (const key in initial) {
|
|
5
|
-
if (typeof initial[key] === "function") {
|
|
6
|
-
store[key] = initial[key];
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
getAllFunctionsName() {
|
|
12
|
-
return Object.keys(store);
|
|
13
|
-
},
|
|
14
|
-
// register new formula
|
|
15
|
-
register(name, fn) {
|
|
16
|
-
if (typeof name !== "string" || !name) {
|
|
17
|
-
throw new Error("Formula name must be a non-empty string");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (typeof fn !== "function") {
|
|
21
|
-
throw new Error(`Formula "${name}" must be callable`);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
store[name] = fn;
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
// get formula
|
|
28
|
-
get(name) {
|
|
29
|
-
return store[name];
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
// check existence
|
|
33
|
-
has(name) {
|
|
34
|
-
return Object.prototype.hasOwnProperty.call(store, name);
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
// remove formula
|
|
38
|
-
remove(name) {
|
|
39
|
-
delete store[name];
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
// list all
|
|
43
|
-
all() {
|
|
44
|
-
return { ...store };
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
// clear registry
|
|
48
|
-
clear() {
|
|
49
|
-
for (const key in store) {
|
|
50
|
-
delete store[key];
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
// extend multiple
|
|
55
|
-
extend(formulas = {}) {
|
|
56
|
-
for (const name in formulas) {
|
|
57
|
-
if (typeof formulas[name] === "function") {
|
|
58
|
-
store[name] = formulas[name];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
// clone (for scoped instances)
|
|
64
|
-
clone() {
|
|
65
|
-
return createFormulaRegistry(store);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
}
|
package/src/index.js
DELETED
package/src/math/operations.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const isValidNumberPair = (a, b) =>
|
|
2
|
-
(typeof a === typeof b) &&
|
|
3
|
-
(typeof a === 'number' || typeof a === 'bigint');
|
|
4
|
-
|
|
5
|
-
export const mathOperations = Object.freeze({
|
|
6
|
-
power: function(a, b) {
|
|
7
|
-
if (isValidNumberPair(a, b)) return a ** b;
|
|
8
|
-
throw new Error("Invalid types for ^");
|
|
9
|
-
},
|
|
10
|
-
|
|
11
|
-
multiply: function(a, b) {
|
|
12
|
-
if (isValidNumberPair(a, b)) return a * b;
|
|
13
|
-
throw new Error("Invalid types for *");
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
divide: function(a, b) {
|
|
17
|
-
if (isValidNumberPair(a, b)) {
|
|
18
|
-
if (b === 0) throw new Error("Division by zero");
|
|
19
|
-
return a / b;
|
|
20
|
-
}
|
|
21
|
-
throw new Error("Invalid types for /");
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
add: function(a, b) {
|
|
25
|
-
if (isValidNumberPair(a, b)) return a + b;
|
|
26
|
-
if (typeof a === 'string' && typeof b === 'string') return a + b;
|
|
27
|
-
throw new Error("Invalid types for +");
|
|
28
|
-
},
|
|
29
|
-
subtract: function(a, b) {
|
|
30
|
-
if (isValidNumberPair(a, b)) return a - b;
|
|
31
|
-
throw new Error("Invalid types for -");
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
modulus: function(a, b) {
|
|
35
|
-
if (isValidNumberPair(a, b)) return a % b;
|
|
36
|
-
throw new Error("Invalid types for %");
|
|
37
|
-
}
|
|
38
|
-
});
|