fable 3.0.133 → 3.0.134

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.
@@ -0,0 +1,56 @@
1
+ {
2
+ "Scope": "FruitsOfTheWorld",
3
+ "Descriptors":
4
+ {
5
+ "FruityVice[].name":
6
+ {
7
+ "Name": "Fruit Name",
8
+ "Hash": "Name",
9
+ "DataType": "String",
10
+ "Default": "(unnamed fruit)"
11
+ },
12
+ "FruityVice[].family":
13
+ {
14
+ "Name": "Family",
15
+ "Hash": "Family",
16
+ "DataType": "String"
17
+ },
18
+ "FruityVice[].order":
19
+ {
20
+ "Name": "Order",
21
+ "Hash": "Order",
22
+ "DataType": "String"
23
+ },
24
+ "FruityVice[].genus":
25
+ {
26
+ "Name": "Genus",
27
+ "Hash": "Genus",
28
+ "DataType": "String"
29
+ },
30
+ "FruityVice[].nutritions.calories":
31
+ {
32
+ "Name": "Calories",
33
+ "Hash": "Calories"
34
+ },
35
+ "FruityVice[].nutritions.fat":
36
+ {
37
+ "Name": "Fat",
38
+ "Hash": "Fat"
39
+ },
40
+ "FruityVice[].nutritions.protein":
41
+ {
42
+ "Name": "Protein",
43
+ "Hash": "Protein"
44
+ },
45
+ "FruityVice[].nutritions.carbohydrates":
46
+ {
47
+ "Name": "Carbohydrates",
48
+ "Hash": "Carbohydrates"
49
+ },
50
+ "FruityVice[].nutritions.sugar":
51
+ {
52
+ "Name": "Sugar",
53
+ "Hash": "Sugar"
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "PR": 1.5,
3
+ "Z": "20.036237",
4
+ "C": -13,
5
+
6
+ "Depth": 100.203,
7
+ "Width": 10.5
8
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "Expressions":
3
+ [
4
+ { "Equation":"Result = 5+3 - sqrt(75 / (3 + Depth) * Width)^ 3", "ExpectedResult":"-13.078386362213538715906797395732300153182132216343566001917247" },
5
+ { "Equation":"Result = (160 * PR * Z) / (C / 100) * PR * Z + (160 * (1 - C / 100))", "ExpectedResult":"-1111529.088640468" },
6
+ { "Equation":"1.5 * sqrt(8 * 2.423782342^2) / 10", "ExpectedResult":"1.02832375808904701855" },
7
+ { "Equation":"Result = ((15000 * 2) / 4)^2 + 100 - 10 * (35 + 5)", "ExpectedResult":"562500" },
8
+ { "Equation":"Result = 1 * sqrt(16)", "ExpectedResult":"4" },
9
+ { "Equation":"Result = sqrt(100 * (C + 30)) + sin(Depth - Width) / 10", "ExpectedResult":"41.32965489638783839821" },
10
+ { "Equation":"Result = 3.5 + 50 + 10 * 10 / 5 - 1.5", "ExpectedResult":"72" }
11
+ ]
12
+ }
@@ -0,0 +1,89 @@
1
+ const libFable = require('../../source/Fable.js');
2
+
3
+ const _Fable = new libFable({"Product": "Ti"});
4
+ // This instantiates the ExpressionParser service provider
5
+ _Fable.instantiateServiceProviderIfNotExists('ExpressionParser');
6
+
7
+ /* * * * * * * * * * * * * * * * *
8
+ *
9
+ * Solve a basic math problem.
10
+ *
11
+ */
12
+ _Fable.log.info(`Beginning Basic Math Exercises....`);
13
+ // Just type numbers and watch it go brrr
14
+ _Fable.log.info(`One-liner solve is: ${_Fable.ExpressionParser.solve("Result = 73.5 * 28.8 * 200.5")}`);
15
+ // Let's put it in a variable for easier exercises
16
+ const _ExpressionParser = _Fable.ExpressionParser;
17
+ // Look up somoe variables in an object
18
+ _Fable.log.info(`One-liner solve with variables is: ${_ExpressionParser.solve("Volume = Width * Height * Depth", {"Width": 73.5, "Height": 28.8, "Depth": 200.5})}`);
19
+
20
+
21
+ /* * * * * * * * * * * * * * * * *
22
+ *
23
+ * A simple run through of some mathematical expressions
24
+ *
25
+ */
26
+ _Fable.log.info(`Beginning Run-through for Set of Test Expressions....`);
27
+ // An array of equations with expected values
28
+ let _Equations = require(`./Equations.json`);
29
+ // The application state is a plain javascript object we pass into the solver to pull variables from
30
+ let _AppData = require(`./AppData.json`);
31
+ // The manifest is a Manyfest which describes hashes for complex addresses in the application state object
32
+ // For example you can't use "Student[0].Age" as a variable in the expression
33
+ // ...but you can use "Student[0].Age" as an address in the manifest with a hash of "StudentAge"
34
+ // ...and then reference "StudentAge" in the expression.
35
+ let tmpManifestConfiguration = { "Scope":"None", "Descriptors":[] };
36
+ let tmpManifest = _Fable.newManyfest(tmpManifestConfiguration);
37
+ // Run each expression in the Equations.json file through the expression parser.
38
+ for (let i = 0; i < _Equations.Expressions.length; i++)
39
+ {
40
+ let tmpResultValue = _ExpressionParser.solve(_Equations.Expressions[i].Equation, _AppData, {}, tmpManifest);
41
+ console.log(`Expression [${i}]: [${_Equations.Expressions[i].Equation}] ==> ${tmpResultValue}`);
42
+ if (tmpResultValue !== _Equations.Expressions[i].ExpectedResult)
43
+ {
44
+ console.log(`Error: Equation ${_Equations.Expressions[i].Equation} expected [${_Equations.Expressions[i].ExpectedResult}] but got [${tmpResultValue}]`);
45
+ }
46
+ }
47
+
48
+
49
+ /* * * * * * * * * * * * * * * * *
50
+ *
51
+ * A manual run through of each phase of the expression parser
52
+ *
53
+ */
54
+ // Load up some fruit data to play around with
55
+ let _FruitData = require(`../data/Fruit-Data.json`);
56
+ let _FruitManifestDescription = require(`../data/Fruit-Manyfest.json`);
57
+ let _FruitManifest = _Fable.newManyfest(_FruitManifestDescription);
58
+
59
+ _Fable.log.info(`Beginning Manual Solve with Embedded Fruit Data....`);
60
+
61
+ // The expression we pass into the solver is just a string
62
+ let tmpExpression = 'HyperMax.HealthIndex = (SUM(Calories) / SUM(Sugar)) * MEDIAN(Fat) + (SQRT(AVG(Protein)) - (PI() + 99)';
63
+ _Fable.log.info(`Solving tmpExpression: [${tmpExpression}]`);
64
+
65
+ // This is an object where the parser will write out the results of each phase of the compiler/parser/solver
66
+ let tmpExpressionParseOutcome = {};
67
+ let tmpSolverResultsObject = {};
68
+
69
+ // Step 1: Tokenize the expression
70
+ let complexTokenizedResults = _ExpressionParser.tokenize(tmpExpression, tmpExpressionParseOutcome);
71
+ // Step 2: Lint the tokenized expression (this is optional but very helpful for user feedback)
72
+ let complexLintedResults = _ExpressionParser.lintTokenizedExpression(complexTokenizedResults, tmpExpressionParseOutcome);
73
+ // Step 3: Parse the tokenized expression (this generates a postfix expression)
74
+ let complexPostfixedResults = _ExpressionParser.buildPostfixedSolveList(complexTokenizedResults, tmpExpressionParseOutcome);
75
+ // Step 4: Substitute the values references in the tokenized objects representing data within the postfixed expression
76
+ _ExpressionParser.substituteValuesInTokenizedObjects(tmpExpressionParseOutcome.PostfixTokenObjects, _FruitData, tmpExpressionParseOutcome, _FruitManifest);
77
+ // Now that we have a postfixed expression and the mapped-in values, show the user what the solution steps look like
78
+ for (let i = 0; i < tmpExpressionParseOutcome.PostfixSolveList.length; i++)
79
+ {
80
+ let tmpToken = tmpExpressionParseOutcome.PostfixSolveList[i];
81
+ console.log(`${i}: ${tmpToken.VirtualSymbolName} = (${tmpToken.LeftValue.Token}::${tmpToken.LeftValue.Value}) ${tmpToken.Operation.Token} (${tmpToken.RightValue.Token}::${tmpToken.RightValue.Value}) `)
82
+ }
83
+ // Step 5: Solve the postfixed expression
84
+ let tmpResultValue = _ExpressionParser.solvePostfixedExpression(tmpExpressionParseOutcome.PostfixSolveList, tmpSolverResultsObject, tmpExpressionParseOutcome, _FruitManifest);
85
+
86
+ // Step 6: Look at the results.
87
+ console.log(`Outcome object: ${JSON.stringify(tmpSolverResultsObject)}`);
88
+ console.log(`Result of ${tmpExpression} solve: ${tmpResultValue}`);
89
+ console.log('QED');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fable",
3
- "version": "3.0.133",
3
+ "version": "3.0.134",
4
4
  "description": "A service dependency injection, configuration and logging library.",
5
5
  "main": "source/Fable.js",
6
6
  "scripts": {
@@ -1,22 +1,82 @@
1
1
  {
2
- "sqrt":
3
- {
4
- "Name": "Square Root",
5
- "Address": "fable.Math.sqrtPrecise"
6
- },
7
- "abs":
8
- {
9
- "Name": "Absolute Value",
10
- "Address": "fable.Math.absPrecise"
11
- },
12
- "rad":
13
- {
14
- "Name": "Degrees to Radians",
15
- "Address": "fable.Math.radPrecise"
16
- },
17
- "sin":
18
- {
19
- "Name": "Sine",
20
- "Address": "fable.Math.sin"
21
- }
2
+ "sqrt": {
3
+ "Name": "Square Root",
4
+ "Address": "fable.Math.sqrtPrecise"
5
+ },
6
+
7
+ "abs": {
8
+ "Name": "Absolute Value",
9
+ "Address": "fable.Math.absPrecise"
10
+ },
11
+
12
+ "rad": {
13
+ "Name": "Degrees to Radians",
14
+ "Address": "fable.Math.radPrecise"
15
+ },
16
+
17
+ "pi": {
18
+ "Name": "Pi",
19
+ "Address": "fable.Math.piPrecise"
20
+ },
21
+ "sin": {
22
+ "Name": "Sine",
23
+ "Address": "fable.Math.sin"
24
+ },
25
+ "cos": {
26
+ "Name": "Cosine",
27
+ "Address": "fable.Math.cos"
28
+ },
29
+ "tan": {
30
+ "Name": "Tangent",
31
+ "Address": "fable.Math.tan"
32
+ },
33
+
34
+ "count": {
35
+ "Name": "Count Set Elements",
36
+ "Address": "fable.Math.countSetElements"
37
+ },
38
+
39
+ "sortset": {
40
+ "Name": "Sort Set",
41
+ "Address": "fable.Math.sortSetPrecise"
42
+ },
43
+ "bucketset": {
44
+ "Name": "Bucket Set",
45
+ "Address": "fable.Math.bucketSetPrecise"
46
+ },
47
+ "sorthistogram": {
48
+ "Name": "Sort Histogram",
49
+ "Address": "fable.Math.sortHistogramPrecise"
50
+ },
51
+
52
+ "max": {
53
+ "Name": "Maximum",
54
+ "Address": "fable.Math.maxPrecise"
55
+ },
56
+ "min": {
57
+ "Name": "Minimum",
58
+ "Address": "fable.Math.minPrecise"
59
+ },
60
+
61
+ "sum": {
62
+ "Name": "Sum",
63
+ "Address": "fable.Math.sumPrecise"
64
+ },
65
+
66
+ "avg": {
67
+ "Name": "Average",
68
+ "Address": "fable.Math.averagePrecise"
69
+ },
70
+ "mean": {
71
+ "Name": "Mean",
72
+ "Address": "fable.Math.meanPrecise"
73
+ },
74
+ "median": {
75
+ "Name": "Median",
76
+ "Address": "fable.Math.medianPrecise"
77
+ },
78
+ "mode": {
79
+ "Name": "Mode",
80
+ "Address": "fable.Math.modePrecise"
81
+ }
22
82
  }
@@ -564,21 +564,21 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
564
564
  // If this is a layer with one value, presume it's an assignment.
565
565
  if (tmpSolveLayerTokens.length === 1)
566
566
  {
567
- let tmpAbstractMultiplyToken = this.getTokenContainerObject('*');
568
- tmpAbstractMultiplyToken.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];
567
+ let tmpAbstractAssignToken = this.getTokenContainerObject('=');
568
+ tmpAbstractAssignToken.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];
569
569
  // If this doesn't have a matching solvelayerstack, get the virtual symbol name from the parenthesis group it's in
570
- if (!tmpAbstractMultiplyToken.VirtualSymbolName)
570
+ if (!tmpAbstractAssignToken.VirtualSymbolName)
571
571
  {
572
572
  for (let i = 0; i < tmpResults.PostfixTokenObjects.length; i++)
573
573
  {
574
574
  if (tmpResults.PostfixTokenObjects[i].ParenthesisStack === tmpSolveLayerTokens[0].SolveLayerStack)
575
575
  {
576
- tmpAbstractMultiplyToken.VirtualSymbolName = tmpResults.PostfixTokenObjects[i].VirtualSymbolName;
576
+ tmpAbstractAssignToken.VirtualSymbolName = tmpResults.PostfixTokenObjects[i].VirtualSymbolName;
577
577
  break;
578
578
  }
579
579
  }
580
580
  }
581
- tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractMultiplyToken, this.getTokenContainerObject('1.0'), tmpSolveLayerTokens[0]));
581
+ tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractAssignToken, tmpSolveLayerTokens[0], this.getTokenContainerObject('0.0')));
582
582
  continue;
583
583
  }
584
584
 
@@ -100,7 +100,7 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
100
100
  }
101
101
  catch (pError)
102
102
  {
103
- tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ${i} with function ${tmpStepResultObject.ExpressionStep.Operation.Token}.`);
103
+ tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ${i} with function ${tmpStepResultObject.ExpressionStep.Operation.Token}: ${pError}`);
104
104
  this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
105
105
  return false;
106
106
  }
@@ -3,6 +3,7 @@
3
3
  {
4
4
  "Name": "Equals",
5
5
  "Token": "=",
6
+ "Function": "fable.Math.assignValue",
6
7
  "Precedence": 0,
7
8
  "Type": "Assignment"
8
9
  },
@@ -113,9 +113,18 @@ class FableServiceExpressionParser extends libFableServiceBase
113
113
  }
114
114
  catch(pError)
115
115
  {
116
- tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric value for the state address ${tmpToken.Token} at index ${i}`);
117
- this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
118
- tmpToken.Resolved = false;
116
+ // TODO: Should we allow this to be a function? Good god the complexity and beauty of that...
117
+ if (Array.isArray(tmpValue) || (typeof(tmpValue) === 'object'))
118
+ {
119
+ tmpToken.Resolved = true;
120
+ tmpToken.Value = tmpValue;
121
+ }
122
+ else
123
+ {
124
+ tmpResults.ExpressionParserLog.push(`INFO: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric, non-set value for the state address ${tmpToken.Token} at index ${i}`);
125
+ this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
126
+ tmpToken.Resolved = false;
127
+ }
119
128
  }
120
129
  }
121
130
  }
@@ -190,6 +199,16 @@ class FableServiceExpressionParser extends libFableServiceBase
190
199
  return this.Solver.solvePostfixedExpression(pPostfixedExpression, pDataDestinationObject, pResultObject, pManifest);
191
200
  }
192
201
 
202
+ /**
203
+ * Solves the given expression using the provided data and manifest.
204
+ *
205
+ * @param {string} pExpression - The expression to solve.
206
+ * @param {object} pDataSourceObject - (optional) The data source object (e.g. AppData).
207
+ * @param {object} pResultObject - (optional) The result object containing the full postfix expression list, internal variables and solver history.
208
+ * @param {object} pManifest - (optional) The manifest object for dereferencing variables.
209
+ * @param {object} pDataDestinationObject - (optional) The data destination object for where to marshal the result into.
210
+ * @returns {any} - The result of solving the expression.
211
+ */
193
212
  solve(pExpression, pDataSourceObject, pResultObject, pManifest, pDataDestinationObject)
194
213
  {
195
214
  let tmpResultsObject = (typeof(pResultObject) === 'object') ? pResultObject : {};