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.
- package/debug/Harness.js +27 -39
- package/dist/fable.compatible.js +273 -54
- package/dist/fable.compatible.min.js +2 -2
- package/dist/fable.compatible.min.js.map +1 -1
- package/dist/fable.js +239 -20
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/example_applications/data/Fruit-Data.json +694 -0
- package/example_applications/data/Fruit-Manyfest.json +56 -0
- package/example_applications/mathematical_playground/AppData.json +8 -0
- package/example_applications/mathematical_playground/Equations.json +12 -0
- package/example_applications/mathematical_playground/Math-Solver-Harness.js +89 -0
- package/package.json +1 -1
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +80 -20
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js +5 -5
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js +1 -1
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json +1 -0
- package/source/services/Fable-Service-ExpressionParser.js +22 -3
- package/source/services/Fable-Service-Math.js +570 -19
- package/test/ExpressionParser_tests.js +17 -0
- package/test/Math_test.js +54 -3
- package/test/data/chocodata.json +248 -0
|
@@ -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,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,22 +1,82 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sqrt":
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"abs":
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"rad":
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
}
|
package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js
CHANGED
|
@@ -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
|
|
568
|
-
|
|
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 (!
|
|
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
|
-
|
|
576
|
+
tmpAbstractAssignToken.VirtualSymbolName = tmpResults.PostfixTokenObjects[i].VirtualSymbolName;
|
|
577
577
|
break;
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
580
|
}
|
|
581
|
-
tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(
|
|
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
|
}
|
|
@@ -113,9 +113,18 @@ class FableServiceExpressionParser extends libFableServiceBase
|
|
|
113
113
|
}
|
|
114
114
|
catch(pError)
|
|
115
115
|
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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 : {};
|