fable 3.1.32 → 3.1.33

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.
Files changed (33) hide show
  1. package/dist/fable.js +463 -370
  2. package/dist/fable.js.map +1 -1
  3. package/dist/fable.min.js +2 -2
  4. package/dist/fable.min.js.map +1 -1
  5. package/example_applications/mathematical_playground/README.md +27 -0
  6. package/package.json +2 -2
  7. package/source/Fable.js +0 -1
  8. package/source/services/Fable-Service-CSVParser.js +177 -177
  9. package/source/services/Fable-Service-DataFormat.js +22 -22
  10. package/source/services/Fable-Service-DataGeneration.js +161 -161
  11. package/source/services/Fable-Service-DateManipulation.js +209 -209
  12. package/source/services/Fable-Service-EnvironmentData-Web.js +3 -3
  13. package/source/services/Fable-Service-EnvironmentData.js +3 -3
  14. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer-DirectiveMutation.js +136 -0
  15. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js +6 -0
  16. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +39 -0
  17. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js +2 -2
  18. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json +9 -0
  19. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ValueMarshal.js +2 -2
  20. package/source/services/Fable-Service-ExpressionParser.js +124 -22
  21. package/source/services/Fable-Service-FilePersistence.js +2 -2
  22. package/source/services/Fable-Service-Logic.js +1 -1
  23. package/source/services/Fable-Service-Math.js +293 -57
  24. package/source/services/Fable-Service-MetaTemplate.js +2 -2
  25. package/source/services/Fable-Service-Operation.js +7 -7
  26. package/source/services/Fable-Service-ProgressTime.js +3 -3
  27. package/source/services/Fable-Service-RestClient.js +2 -2
  28. package/source/services/Fable-Service-Template.js +4 -4
  29. package/source/services/Fable-Service-Utility.js +6 -6
  30. package/test/ExpressionParser_tests.js +53 -0
  31. package/test/Math_test.js +81 -1
  32. package/source/services/Fable-Service-ExpressionParser/RNI_Randy.json +0 -1
  33. package/source/services/Fable-Service-ExpressionParser/RNI_Tool.json +0 -1
@@ -294,6 +294,11 @@
294
294
  "Address": "fable.Math.cleanValueObject"
295
295
  },
296
296
 
297
+ "polynomialregression": {
298
+ "Name": "Perform an nth degree Polynomial Regression on a Set of X and Y Values",
299
+ "Address": "fable.Math.polynomialRegression"
300
+ },
301
+
297
302
  "randominteger": {
298
303
  "Name": "Random Integer",
299
304
  "Address": "fable.DataGeneration.randomInteger"
@@ -397,5 +402,39 @@
397
402
  "createvalueobjectbyhashes": {
398
403
  "Name": "Create Value Object by Hashes",
399
404
  "Address": "fable.Utility.createValueObjectByHashes"
405
+ },
406
+
407
+ "polynomialregression": {
408
+ "Name": "Perform an nth degree Polynomial Regression on a Set of X and Y Values",
409
+ "Address": "fable.Math.polynomialRegression"
410
+ },
411
+ "leastsquares": {
412
+ "Name": "Perform a Least Squares Regression on a Set of Independent Variable Vectors and a Dependent Variable Vector",
413
+ "Address": "fable.Math.leastSquares"
414
+ },
415
+ "linest": {
416
+ "Name": "Perform a Least Squares Regression on a Set of Independent Variable Vectors and a Dependent Variable Vector",
417
+ "Address": "fable.Math.leastSquares"
418
+ },
419
+
420
+ "matrixtranspose": {
421
+ "Name": "Transpose a Matrix",
422
+ "Address": "fable.Math.matrixTranspose"
423
+ },
424
+ "matrixmultiply": {
425
+ "Name": "Multiply Two Matrices",
426
+ "Address": "fable.Math.matrixMultiply"
427
+ },
428
+ "matrixvectormultiply": {
429
+ "Name": "Multiply a Matrix by a Vector",
430
+ "Address": "fable.Math.matrixVectorMultiply"
431
+ },
432
+ "matrixinverse": {
433
+ "Name": "Inverse a Matrix",
434
+ "Address": "fable.Math.matrixInverse"
435
+ },
436
+ "gaussianelimination": {
437
+ "Name": "Solve a System of Linear Equations using Gaussian Elimination",
438
+ "Address": "fable.Math.gaussianElimination"
400
439
  }
401
440
  }
@@ -135,7 +135,7 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
135
135
  catch (pError)
136
136
  {
137
137
  tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ${i} with function ${tmpStepResultObject.ExpressionStep.Operation.Token}: ${pError}`);
138
- this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
138
+ this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1], { Stack: pError.stack });
139
139
  return false;
140
140
  }
141
141
  }
@@ -151,7 +151,7 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
151
151
  catch (pError)
152
152
  {
153
153
  tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ${i} with function ${tmpStepResultObject.ExpressionStep.Operation.Token}: ${pError}`);
154
- this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
154
+ this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1], { Stack: pError.stack });
155
155
  return false;
156
156
  }
157
157
  }
@@ -8,6 +8,15 @@
8
8
  "Type": "Assignment"
9
9
  },
10
10
 
11
+ ":":
12
+ {
13
+ "Name": "Expression Begin",
14
+ "Token": ":",
15
+ "Function": "fable.Math.expressionBegin",
16
+ "Precedence": 0,
17
+ "Type": "Assignment"
18
+ },
19
+
11
20
  "?=":
12
21
  {
13
22
  "Name": "Null or Empty Coalescing Assign Value",
@@ -10,7 +10,7 @@ class ExpressionParserValueMarshal extends libExpressionParserOperationBase
10
10
 
11
11
  /**
12
12
  * Substitutes values in tokenized objects based on the provided data source and manifest.
13
- *
13
+ *
14
14
  * TODO: Move this to its own file in the "Fable-Service-ExpressionParser" directory.
15
15
  *
16
16
  * @param {Array} pTokenizedObjects - The array of tokenized objects.
@@ -154,4 +154,4 @@ class ExpressionParserValueMarshal extends libExpressionParserOperationBase
154
154
  }
155
155
  }
156
156
 
157
- module.exports = ExpressionParserValueMarshal;
157
+ module.exports = ExpressionParserValueMarshal;
@@ -1,24 +1,23 @@
1
- const { PE } = require('big.js');
2
1
  const libFableServiceBase = require('fable-serviceproviderbase');
3
2
 
4
3
  /* Trying a different pattern for this service ...
5
4
  *
6
5
  * This service is a simple expression parser that can handle math expressions, with magic(tm) lookup of addresses with a manifest.
7
- *
6
+ *
8
7
  * Each method works multiple ways.
9
- *
8
+ *
10
9
  * 1. You can pass in a results object, and, it will put the state for that step outcome into the results object.
11
10
  * 2. It always returns the state, and works without the results object.
12
- *
13
- *
11
+ *
12
+ *
14
13
  * Learned a lot from this npm package: https://www.npmjs.com/package/math-expression-evaluator
15
14
  * And its related code at github: https://github.com/bugwheels94/math-expression-evaluator
16
- *
15
+ *
17
16
  * There were two problems with the codebase above...
18
- *
17
+ *
19
18
  * First, the code was very unreadable and determining it was correct or extending it
20
19
  * was out of the question.
21
- *
20
+ *
22
21
  * Second, and this is a larger issue, is that we need the expressions to be parsed as
23
22
  * arbitrary precision. When I determined that extending the library to use string-based
24
23
  * numbers and an arbitrary precision library as the back-end would have taken a significantly
@@ -37,6 +36,11 @@ class FableServiceExpressionParser extends libFableServiceBase
37
36
  {
38
37
  super(pFable, pOptions, pServiceHash);
39
38
 
39
+ /** @type {import('../Fable.js') & { Math: import('./Fable-Service-Math.js') }} */
40
+ this.fable;
41
+ /** @type {any} */
42
+ this.log;
43
+
40
44
  // The configuration for tokens that the solver recognizes, with precedence and friendly names.
41
45
  this.tokenMap = require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json');
42
46
 
@@ -88,6 +92,7 @@ class FableServiceExpressionParser extends libFableServiceBase
88
92
 
89
93
  // These are sub-services for the tokenizer, linter, compiler, marshaler and solver.
90
94
  this.fable.addServiceTypeIfNotExists('ExpressionParser-Tokenizer', require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js'));
95
+ this.fable.addServiceTypeIfNotExists('ExpressionParser-TokenizerDirectiveMutation', require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer-DirectiveMutation.js'));
91
96
  this.fable.addServiceTypeIfNotExists('ExpressionParser-Linter', require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Linter.js'));
92
97
  this.fable.addServiceTypeIfNotExists('ExpressionParser-Postfix', require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js'));
93
98
  this.fable.addServiceTypeIfNotExists('ExpressionParser-ValueMarshal', require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ValueMarshal.js'));
@@ -98,6 +103,7 @@ class FableServiceExpressionParser extends libFableServiceBase
98
103
 
99
104
  // This code instantitates these fable services to child objects of this service, but does not pollute the main fable with them.
100
105
  this.Tokenizer = this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-Tokenizer');
106
+ this.Tokenizer.TokenizerDirectiveMutation = this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-TokenizerDirectiveMutation');
101
107
  this.Linter = this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-Linter');
102
108
  this.Postfix = this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-Postfix');
103
109
  this.ValueMarshal = this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-ValueMarshal');
@@ -113,7 +119,7 @@ class FableServiceExpressionParser extends libFableServiceBase
113
119
  this.Messaging.connectExpressionParser(this);
114
120
 
115
121
  this.GenericManifest = this.fable.newManyfest();
116
-
122
+
117
123
  // This will look for a LogNoisiness on fable (or one that falls in from pict) and if it doesn't exist, set one for this service.
118
124
  this.LogNoisiness = ('LogNoisiness' in this.fable) ? this.fable.LogNoisiness : 0;
119
125
  }
@@ -156,7 +162,7 @@ class FableServiceExpressionParser extends libFableServiceBase
156
162
 
157
163
  /**
158
164
  * Substitutes values in tokenized objects.
159
- *
165
+ *
160
166
  * This means marshaling data from pDataSource into the array of objects with the passed in Manifest (or a generic manifest) to prepare for solving.
161
167
  *
162
168
  * @param {Array} pTokenizedObjects - The array of tokenized objects.
@@ -186,12 +192,12 @@ class FableServiceExpressionParser extends libFableServiceBase
186
192
 
187
193
  /**
188
194
  * Solves the given expression using the provided data and manifest.
189
- *
195
+ *
190
196
  * @param {string} pExpression - The expression to solve.
191
- * @param {object} pDataSourceObject - (optional) The data source object (e.g. AppData).
192
- * @param {object} pResultObject - (optional) The result object containing the full postfix expression list, internal variables and solver history.
193
- * @param {object} pManifest - (optional) The manifest object for dereferencing variables.
194
- * @param {object} pDataDestinationObject - (optional) The data destination object for where to marshal the result into.
197
+ * @param {Record<string, any>} [pDataSourceObject] - (optional) The data source object (e.g. AppData).
198
+ * @param {Record<string, any>} [pResultObject] - (optional) The result object containing the full postfix expression list, internal variables and solver history.
199
+ * @param {import('manyfest')} [pManifest] - (optional) The manifest object for dereferencing variables.
200
+ * @param {Record<string, any>} [pDataDestinationObject] - (optional) The data destination object for where to marshal the result into.
195
201
  * @returns {any} - The result of solving the expression.
196
202
  */
197
203
  solve(pExpression, pDataSourceObject, pResultObject, pManifest, pDataDestinationObject)
@@ -202,15 +208,111 @@ class FableServiceExpressionParser extends libFableServiceBase
202
208
 
203
209
  // This is technically a "pre-compile" and we can keep this Results Object around to reuse for better performance. Not required.
204
210
  this.tokenize(pExpression, tmpResultsObject);
211
+
212
+ // Lint the tokenized expression to make sure it's valid
205
213
  this.lintTokenizedExpression(tmpResultsObject.RawTokens, tmpResultsObject);
206
214
  this.buildPostfixedSolveList(tmpResultsObject.RawTokens, tmpResultsObject);
207
-
208
- // This is where the data from variables gets marshaled into their symbols (from AppData or the like)
209
- this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpDataSourceObject, tmpResultsObject, pManifest);
210
-
211
- // Finally this is the expr solving method, which returns a string and also marshals it into tmpDataDestinationObject
212
- return this.solvePostfixedExpression(tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject, pManifest);
215
+
216
+ if (tmpResultsObject.SolverDirectives.Code == 'SERIES')
217
+ {
218
+ // Make sure tmpResultsObject.SolverDirective values for From: null, To: null, Step: null are all numeric and non-zero where applicable
219
+ let tmpFrom = this.fable.Math.parsePrecise(tmpResultsObject.SolverDirectives.From, NaN);
220
+ let tmpTo = this.fable.Math.parsePrecise(tmpResultsObject.SolverDirectives.To, NaN);
221
+ let tmpStep = this.fable.Math.parsePrecise(tmpResultsObject.SolverDirectives.Step, NaN);
222
+
223
+ if (isNaN(tmpStep))
224
+ {
225
+ tmpStep = '1';
226
+ }
227
+
228
+ if (isNaN(tmpFrom) || isNaN(tmpTo))
229
+ {
230
+ tmpResultsObject.ExpressionParserLog.push(`ExpressionParser.solve detected invalid SERIES directive parameters. FROM, TO must be numeric.`);
231
+ this.log.warn(tmpResultsObject.ExpressionParserLog[tmpResultsObject.ExpressionParserLog.length-1]);
232
+ return null;
233
+ }
234
+
235
+ // Make sure from/to are not equal
236
+ if (this.fable.Math.comparePrecise(tmpFrom, tmpTo) == 0)
237
+ {
238
+ tmpResultsObject.ExpressionParserLog.push(`ExpressionParser.solve detected invalid SERIES directive parameters. FROM and TO cannot be equal.`);
239
+ this.log.warn(tmpResultsObject.ExpressionParserLog[tmpResultsObject.ExpressionParserLog.length-1]);
240
+ return null;
241
+ }
242
+
243
+ // Make sure that Step is the correct positive/negative based on From and To
244
+ if (this.fable.Math.comparePrecise(tmpStep, '0') == 0)
245
+ {
246
+ tmpResultsObject.ExpressionParserLog.push(`ExpressionParser.solve detected invalid SERIES directive parameters. STEP cannot be zero.`);
247
+ this.log.warn(tmpResultsObject.ExpressionParserLog[tmpResultsObject.ExpressionParserLog.length-1]);
248
+ return null;
249
+ }
250
+ if (this.fable.Math.comparePrecise(tmpFrom, tmpTo) < 0)
251
+ {
252
+ // From < To so Step must be positive
253
+ if (this.fable.Math.comparePrecise(tmpStep, '0') < 0)
254
+ {
255
+ tmpResultsObject.ExpressionParserLog.push(`ExpressionParser.solve detected invalid SERIES directive parameters. STEP must be positive when FROM < TO.`);
256
+ this.log.warn(tmpResultsObject.ExpressionParserLog[tmpResultsObject.ExpressionParserLog.length-1]);
257
+ return null;
258
+ }
259
+ }
260
+ else
261
+ {
262
+ // From >= To so Step must be negative
263
+ if (this.fable.Math.comparePrecise(tmpStep, '0') > 0)
264
+ {
265
+ tmpResultsObject.ExpressionParserLog.push(`ExpressionParser.solve detected invalid SERIES directive parameters. STEP must be negative when FROM >= TO.`);
266
+ this.log.warn(tmpResultsObject.ExpressionParserLog[tmpResultsObject.ExpressionParserLog.length-1]);
267
+ return null;
268
+ }
269
+ }
270
+
271
+ // Get the number of iterations we need to perform
272
+ let tmpIterations = parseInt(this.fable.Math.floorPrecise(this.fable.Math.dividePrecise(this.fable.Math.subtractPrecise(tmpTo, tmpFrom), tmpStep)));
273
+
274
+ let tmpValueArray = [];
275
+
276
+ for (let i = 0; i <= tmpIterations; i++)
277
+ {
278
+ let tmpCurrentValueOfN = this.fable.Math.addPrecise(tmpFrom, this.fable.Math.multiplyPrecise(tmpStep, i.toString()));
279
+
280
+ // Jimmy up the data source with the current N value, stepIndex and all the other data from the source object
281
+ // This generates a data source object every time on purpose so we can remarshal in values that changed in the destination
282
+ let tmpSeriesStepDataSourceObject = Object.assign({}, tmpDataSourceObject);
283
+ tmpSeriesStepDataSourceObject.n = tmpCurrentValueOfN;
284
+ tmpSeriesStepDataSourceObject.stepIndex = i;
285
+
286
+ let tmpMutatedValues = this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpSeriesStepDataSourceObject, tmpResultsObject, pManifest);
287
+
288
+ tmpValueArray.push( this.solvePostfixedExpression( tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject, pManifest) );
289
+
290
+ for (let j = 0; j < tmpMutatedValues.length; j++)
291
+ {
292
+ tmpMutatedValues[j].Resolved = false;
293
+ }
294
+ }
295
+
296
+ // Do the assignment
297
+ let tmpAssignmentManifestHash = tmpResultsObject.PostfixedAssignmentAddress;
298
+ if ((tmpResultsObject.OriginalRawTokens[1] === '=') && (typeof(tmpResultsObject.OriginalRawTokens[0]) === 'string') && (tmpResultsObject.OriginalRawTokens[0].length > 0))
299
+ {
300
+ tmpAssignmentManifestHash = tmpResultsObject.OriginalRawTokens[0];
301
+ }
302
+
303
+ let tmpManifest = (typeof(pManifest) === 'object') ? pManifest : this.fable.newManyfest();
304
+ tmpManifest.setValueByHash(tmpDataDestinationObject, tmpAssignmentManifestHash, tmpValueArray);
305
+
306
+ return tmpValueArray;
307
+ }
308
+ else // For 'SOLVE' or anything else that didn't work
309
+ {
310
+ // This is where the data from variables gets marshaled into their symbols (from AppData or the like)
311
+ this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects, tmpDataSourceObject, tmpResultsObject, pManifest);
312
+ // Finally this is the expr solving method, which returns a string and also marshals it into tmpDataDestinationObject
313
+ return this.solvePostfixedExpression(tmpResultsObject.PostfixSolveList, tmpDataDestinationObject, tmpResultsObject, pManifest);
314
+ }
213
315
  }
214
316
  }
215
317
 
216
- module.exports = FableServiceExpressionParser;
318
+ module.exports = FableServiceExpressionParser;
@@ -86,7 +86,7 @@ class FableServiceFilePersistence extends libFableServiceBase
86
86
  {
87
87
  this.fable.log.error(`CSV Read of ${pFilePath} Error: ${pError}`, pError);
88
88
  };
89
-
89
+
90
90
  return this.lineReaderFactory(pFilePath,
91
91
  (pLine) =>
92
92
  {
@@ -265,4 +265,4 @@ class FableServiceFilePersistence extends libFableServiceBase
265
265
  }
266
266
  }
267
267
 
268
- module.exports = FableServiceFilePersistence;
268
+ module.exports = FableServiceFilePersistence;
@@ -9,7 +9,7 @@ class FableServiceLogic extends libFableServiceBase
9
9
  */
10
10
  constructor(pFable, pOptions, pServiceHash)
11
11
  {
12
- super(pFable, pOptions, pServiceHash);
12
+ super(pFable, pOptions, pServiceHash);
13
13
  }
14
14
 
15
15
  /**