fable 3.0.146 → 3.0.148

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.
@@ -40,7 +40,8 @@ for (let i = 0; i < _Equations.Expressions.length; i++)
40
40
  let tmpResultObject = {};
41
41
  let tmpResultValue = _ExpressionParser.solve(_Equations.Expressions[i].Equation, _AppData, tmpResultObject, tmpManifest);
42
42
  console.log(`Expression [${i}]: [${_Equations.Expressions[i].Equation}] ==> ${tmpResultValue}`);
43
- _Fable.ExpressionParser.Messaging.logFunctionOutcome(tmpResultObject);
43
+ console.log(` Expected: ${_Equations.Expressions[i].ExpectedResult}`);
44
+ //_Fable.ExpressionParser.Messaging.logFunctionOutcome(tmpResultObject);
44
45
 
45
46
  if (tmpResultValue !== _Equations.Expressions[i].ExpectedResult)
46
47
  {
@@ -81,7 +82,7 @@ _ExpressionParser.substituteValuesInTokenizedObjects(tmpExpressionParseOutcome.P
81
82
  let tmpResultValue = _ExpressionParser.solvePostfixedExpression(tmpExpressionParseOutcome.PostfixSolveList, tmpSolverResultsObject, tmpExpressionParseOutcome, _FruitManifest);
82
83
 
83
84
  // Now that we have a solved expression and the mapped-in values, show the user the solution
84
- _Fable.ExpressionParser.Messaging.logFunctionOutcome(tmpExpressionParseOutcome);
85
+ //_Fable.ExpressionParser.Messaging.logFunctionOutcome(tmpExpressionParseOutcome);
85
86
 
86
87
  // Step 6: Look at the results.
87
88
  console.log(`Outcome object: ${JSON.stringify(tmpSolverResultsObject)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fable",
3
- "version": "3.0.146",
3
+ "version": "3.0.148",
4
4
  "description": "A service dependency injection, configuration and logging library.",
5
5
  "main": "source/Fable.js",
6
6
  "scripts": {
package/source/Fable.js CHANGED
@@ -310,7 +310,27 @@ class Fable extends libFableServiceBase.CoreServiceProviderBase
310
310
  }
311
311
 
312
312
  return false;
313
- }
313
+ }
314
+
315
+
316
+ /**
317
+ * Generate a safe string to use in filenames for a date. Useful for log file uniqueness and temporary outputs.
318
+ *
319
+ * @static
320
+ * @param {Date} pDate - An optional javascript Date object to generate a datestamp for.
321
+ * @returns {string} - A string formatted as YYYY-MM-DD-HH-MM-SS
322
+ */
323
+ static generateFileNameDateStamp(pDate)
324
+ {
325
+ const tmpDate = pDate || new Date();
326
+ const tmpYear = tmpDate.getFullYear();
327
+ const tmpMonth = String(tmpDate.getMonth() + 1).padStart(2, '0');
328
+ const tmpDay = String(tmpDate.getDate()).padStart(2, '0');
329
+ const tmpHour = String(tmpDate.getHours()).padStart(2, '0');
330
+ const tmpMinute = String(tmpDate.getMinutes()).padStart(2, '0');
331
+ const tmpSecond = String(tmpDate.getSeconds()).padStart(2, '0');
332
+ return `${tmpYear}-${tmpMonth}-${tmpDay}-${tmpHour}-${tmpMinute}-${tmpSecond}`;
333
+ }
314
334
  }
315
335
 
316
336
  // This is for backwards compatibility
@@ -1,66 +1,171 @@
1
1
  const libFableServiceBase = require('fable-serviceproviderbase');
2
2
 
3
+ /**
4
+ * FableServiceDataGeneration class provides various methods for generating random data.
5
+ *
6
+ * @extends libFableServiceBase
7
+ */
3
8
  class FableServiceDataGeneration extends libFableServiceBase
4
9
  {
5
- constructor(pFable, pOptions, pServiceHash)
6
- {
7
- super(pFable, pOptions, pServiceHash);
8
-
9
- this.serviceType = 'DataGeneration';
10
-
11
- this.defaultData = require('./Fable-Service-DataGeneration-DefaultValues.json');
12
- }
13
-
14
- // Return a random integer between pMinimum and pMaximum
15
- randomIntegerBetween(pMinimum, pMaximum)
16
- {
17
- return Math.floor(Math.random() * (pMaximum - pMinimum)) + pMinimum;
18
- }
19
- // Return a random integer up to the passed-in maximum
20
- randomIntegerUpTo(pMaximum)
21
- {
22
- return this.randomIntegerBetween(0, pMaximum);
23
- }
24
- // Return a random integer between 0 and 9999999
25
- randomInteger()
26
- {
27
- return Math.floor(Math.random()*this.defaultData.DefaultIntegerMaximum);
28
- }
29
-
30
-
31
- randomNumericString(pLength, pMaxNumber)
32
- {
33
- let tmpLength = (typeof(pLength) === 'undefined') ? 10 : pLength;
34
- let tmpMaxNumber = (typeof(pMaxNumber) === 'undefined') ? 9999999999 : pMaxNumber;
35
-
36
- return this.services.DataFormat.stringPadStart(this.randomIntegerUpTo(tmpMaxNumber), pLength, '0');
37
- }
38
-
39
-
40
- randomMonth()
41
- {
42
- return this.defaultData.MonthSet[this.randomIntegerUpTo(this.defaultData.MonthSet.length-1)];
43
- }
44
- randomDayOfWeek()
45
- {
46
- return this.defaultData.WeekDaySet[this.randomIntegerUpTo(this.defaultData.WeekDaySet.length-1)];
47
- }
48
-
49
-
50
- randomColor()
51
- {
52
- return this.defaultData.ColorSet[this.randomIntegerUpTo(this.defaultData.ColorSet.length-1)];
53
- }
54
-
55
-
56
- randomName()
57
- {
58
- return this.defaultData.NameSet[this.randomIntegerUpTo(this.defaultData.NameSet.length-1)];
59
- }
60
- randomSurname()
61
- {
62
- return this.defaultData.SurNameSet[this.randomIntegerUpTo(this.defaultData.SurNameSet.length-1)];
63
- }
10
+ constructor(pFable, pOptions, pServiceHash)
11
+ {
12
+ super(pFable, pOptions, pServiceHash);
13
+
14
+ this.serviceType = 'DataGeneration';
15
+
16
+ this.defaultData = require('./Fable-Service-DataGeneration-DefaultValues.json');
17
+ }
18
+
19
+ /**
20
+ * Generates a random integer between the specified minimum and maximum values.
21
+ *
22
+ * @param {number} pMinimum - The minimum value (inclusive).
23
+ * @param {number} pMaximum - The maximum value (exclusive).
24
+ * @returns {number} A random integer between pMinimum and pMaximum.
25
+ */
26
+ randomIntegerBetween(pMinimum, pMaximum)
27
+ {
28
+ try
29
+ {
30
+ let tmpMinimum = parseInt(pMinimum, 10);
31
+ let tmpMaximum = parseInt(pMaximum, 10);
32
+ return Math.floor(Math.random() * (tmpMaximum - tmpMinimum)) + tmpMinimum;
33
+ }
34
+ catch (pError)
35
+ {
36
+ this.fable.log.error('Error in randomIntegerBetween', pError, { 'Minimum': pMinimum, 'Maximum': pMaximum });
37
+ return NaN;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Generates a random integer between 0 (inclusive) and the specified maximum value (exclusive).
43
+ *
44
+ * @param {number} pMaximum - The maximum value (exclusive).
45
+ * @returns {number} A random integer between 0 and pMaximum.
46
+ */
47
+ randomIntegerUpTo(pMaximum)
48
+ {
49
+ return this.randomIntegerBetween(0, pMaximum);
50
+ }
51
+
52
+ /**
53
+ * Generates a random integer between 0 (inclusive) and the default maximum value.
54
+ *
55
+ * @returns {number} A random integer between 0 and the default maximum value.
56
+ */
57
+ randomInteger()
58
+ {
59
+ return Math.floor(Math.random() * this.defaultData.DefaultIntegerMaximum);
60
+ }
61
+
62
+ /**
63
+ * Generates a random float between the specified minimum and maximum values.
64
+ *
65
+ * @param {number} pMinimum - The minimum value (inclusive).
66
+ * @param {number} pMaximum - The maximum value (exclusive).
67
+ * @returns {number} A random float between pMinimum and pMaximum.
68
+ */
69
+ randomFloatBetween(pMinimum, pMaximum)
70
+ {
71
+ try
72
+ {
73
+ let tmpMinimum = parseFloat(pMinimum);
74
+ let tmpMaximum = parseFloat(pMaximum);
75
+ return this.fable.Math.addPrecise(this.fable.Math.multiplyPrecise(Math.random(), this.fable.Math.subtractPrecise(tmpMaximum, tmpMinimum)), tmpMinimum);
76
+ }
77
+ catch (pError)
78
+ {
79
+ this.fable.log.error('Error in randomFloatBetween', pError, { 'Minimum': pMinimum, 'Maximum': pMaximum });
80
+ return NaN;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Generates a random float between 0 (inclusive) and the specified maximum value (exclusive).
86
+ *
87
+ * @param {number} pMaximum - The maximum value (exclusive).
88
+ * @returns {number} A random float between 0 and pMaximum.
89
+ */
90
+ randomFloatUpTo(pMaximum)
91
+ {
92
+ return this.randomFloatBetween(0, pMaximum);
93
+ }
94
+
95
+ /**
96
+ * Generates a random float between 0 (inclusive) and 1 (exclusive).
97
+ *
98
+ * @returns {number} A random float between 0 and 1.
99
+ */
100
+ randomFloat()
101
+ {
102
+ return Math.random();
103
+ }
104
+
105
+ /**
106
+ * Generates a random numeric string of the specified length.
107
+ *
108
+ * @param {number} pLength - The length of the numeric string.
109
+ * @param {number} pMaxNumber - The maximum number to generate.
110
+ * @returns {string} A random numeric string of the specified length.
111
+ */
112
+ randomNumericString(pLength, pMaxNumber)
113
+ {
114
+ let tmpLength = (typeof(pLength) === 'undefined') ? 10 : pLength;
115
+ let tmpMaxNumber = (typeof(pMaxNumber) === 'undefined') ? 9999999999 : pMaxNumber;
116
+
117
+ return this.services.DataFormat.stringPadStart(this.randomIntegerUpTo(tmpMaxNumber), pLength, '0');
118
+ }
119
+
120
+ /**
121
+ * Generates a random month from the default month set.
122
+ *
123
+ * @returns {string} A random month.
124
+ */
125
+ randomMonth()
126
+ {
127
+ return this.defaultData.MonthSet[this.randomIntegerUpTo(this.defaultData.MonthSet.length - 1)];
128
+ }
129
+
130
+ /**
131
+ * Generates a random day of the week from the default week day set.
132
+ *
133
+ * @returns {string} A random day of the week.
134
+ */
135
+ randomDayOfWeek()
136
+ {
137
+ return this.defaultData.WeekDaySet[this.randomIntegerUpTo(this.defaultData.WeekDaySet.length - 1)];
138
+ }
139
+
140
+ /**
141
+ * Generates a random color from the default color set.
142
+ *
143
+ * @returns {string} A random color.
144
+ */
145
+ randomColor()
146
+ {
147
+ return this.defaultData.ColorSet[this.randomIntegerUpTo(this.defaultData.ColorSet.length - 1)];
148
+ }
149
+
150
+ /**
151
+ * Generates a random name from the default name set.
152
+ *
153
+ * @returns {string} A random name.
154
+ */
155
+ randomName()
156
+ {
157
+ return this.defaultData.NameSet[this.randomIntegerUpTo(this.defaultData.NameSet.length - 1)];
158
+ }
159
+
160
+ /**
161
+ * Generates a random surname from the default surname set.
162
+ *
163
+ * @returns {string} A random surname.
164
+ */
165
+ randomSurname()
166
+ {
167
+ return this.defaultData.SurNameSet[this.randomIntegerUpTo(this.defaultData.SurNameSet.length - 1)];
168
+ }
64
169
  }
65
170
 
66
171
  module.exports = FableServiceDataGeneration;
@@ -99,16 +99,43 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
99
99
  }
100
100
 
101
101
  // [ TOKENS ]
102
- if (tmpCharacter in this.ExpressionParser.tokenMap)
102
+ if (tmpCharacter in this.ExpressionParser.tokenRadix)
103
103
  {
104
- if (tmpCurrentToken.length > 0)
104
+ let tmpTokenRadix = this.ExpressionParser.tokenRadix[tmpCharacter];
105
+ // If the token is a literal and has only one entry, it is a single character token and we can just safely add it.
106
+ if (tmpTokenRadix.TokenCount == 1 && tmpTokenRadix.Literal)
105
107
  {
106
- tmpResults.RawTokens.push(tmpCurrentToken);
108
+ if (tmpCurrentToken.length > 0)
109
+ {
110
+ tmpResults.RawTokens.push(tmpCurrentToken);
111
+ }
112
+ tmpCurrentToken = '';
113
+ tmpCurrentTokenType = false;
114
+ tmpResults.RawTokens.push(tmpCharacter);
115
+ continue;
116
+ }
117
+ else
118
+ {
119
+ // This one has multiple options, so literals don't matter. We need to check the token map.
120
+ // The token radix TokenKeys array is sorted longest to shortest
121
+ for (let j = 0; j < tmpTokenRadix.TokenKeys.length; j++)
122
+ {
123
+ let tmpTokenKey = tmpTokenRadix.TokenKeys[j];
124
+ if (pExpression.substr(i, tmpTokenKey.length) == tmpTokenKey)
125
+ {
126
+ if (tmpCurrentToken.length > 0)
127
+ {
128
+ tmpResults.RawTokens.push(tmpTokenKey);
129
+ }
130
+ tmpCurrentToken = '';
131
+ tmpCurrentTokenType = false;
132
+ tmpResults.RawTokens.push(tmpTokenKey);
133
+ i += tmpTokenKey.length - 1;
134
+ break;
135
+ }
136
+ }
137
+ continue;
107
138
  }
108
- tmpCurrentToken = '';
109
- tmpCurrentTokenType = false;
110
- tmpResults.RawTokens.push(tmpCharacter);
111
- continue;
112
139
  }
113
140
 
114
141
  // If it's not an operator, it's a number or address.
@@ -83,8 +83,38 @@
83
83
  "Name": "Round",
84
84
  "Address": "fable.Math.roundPrecise"
85
85
  },
86
- "valuemapcount": {
87
- "Name": "Histogram Count Value Map",
88
- "Address": "fable.Math.valueMapCountPrecise"
86
+ "countSetElements": {
87
+ "Name": "Count Set Elements in a Histogram or Value Map",
88
+ "Address": "fable.Math.countSetElements"
89
+ },
90
+
91
+ "getvalue": {
92
+ "Name": "Get Value from Application State or Services (AppData, etc.)",
93
+ "Address": "fable.Utility.getValue"
94
+ },
95
+
96
+ "randominteger": {
97
+ "Name": "Random Integer",
98
+ "Address": "fable.DataGeneration.randomInteger"
99
+ },
100
+ "randomintegerbetween": {
101
+ "Name": "Random Integer Between Two Numbers",
102
+ "Address": "fable.DataGeneration.randomIntegerBetween"
103
+ },
104
+ "randomintegerupto": {
105
+ "Name": "Random Integer",
106
+ "Address": "fable.DataGeneration.randomIntegerUpTo"
107
+ },
108
+ "randomfloat": {
109
+ "Name": "Random Float",
110
+ "Address": "fable.DataGeneration.randomFloat"
111
+ },
112
+ "randomfloatbetween": {
113
+ "Name": "Random Float",
114
+ "Address": "fable.DataGeneration.randomFloatBetween"
115
+ },
116
+ "randomfloatupto": {
117
+ "Name": "Random Float",
118
+ "Address": "fable.DataGeneration.randomFloatUpTo"
89
119
  }
90
120
  }
@@ -103,14 +103,14 @@ class ExpressionParserLinter extends libExpressionParserOperationBase
103
103
  let tmpEqualityAssignmentIndex = false;
104
104
  for (let i = 0; i < pTokenizedExpression.length; i++)
105
105
  {
106
- if (pTokenizedExpression[i] === '=')
106
+ if ((this.ExpressionParser.tokenMap[pTokenizedExpression[i]]) && (this.ExpressionParser.tokenMap[pTokenizedExpression[i]].Type === 'Assignment'))
107
107
  {
108
108
  tmpEqualityAssignmentCount++;
109
109
  tmpEqualityAssignmentIndex = i;
110
110
 
111
111
  if (tmpEqualityAssignmentCount > 1)
112
112
  {
113
- tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.lintTokenizedExpression found multiple equality assignments in the tokenized expression; equality assignment #${tmpEqualityAssignmentCount} at token index ${i}.`);
113
+ tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.lintTokenizedExpression found multiple equality assignments in the tokenized expression; equality assignment #${tmpEqualityAssignmentCount} operator '${pTokenizedExpression[i]}' at token index ${i}.`);
114
114
  tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
115
115
  this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
116
116
  }
@@ -70,6 +70,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
70
70
  let tmpResults = (typeof(pResultObject) === 'object') ? pResultObject : { ExpressionParserLog: [] };
71
71
 
72
72
  tmpResults.PostfixedAssignmentAddress = 'Result'
73
+ tmpResults.PostfixedAssignmentOperator = this.ExpressionParser.tokenMap['=']; // This is the default assignment operator
73
74
  tmpResults.PostfixTokenObjects = [];
74
75
  tmpResults.PostfixSolveList = [];
75
76
 
@@ -84,16 +85,19 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
84
85
  let tmpEqualsIndex = -1;
85
86
  for (let i = 0; i < pTokenizedExpression.length; i++)
86
87
  {
87
- if ((pTokenizedExpression[i] === '=') && (tmpEqualsIndex < 0))
88
+ if ((this.ExpressionParser.tokenMap[pTokenizedExpression[i]]) && (this.ExpressionParser.tokenMap[pTokenizedExpression[i]].Type === 'Assignment'))
88
89
  {
89
- tmpEqualsIndex = i;
90
- }
91
- // If there are two equality assignments, error and bail out.
92
- else if (pTokenizedExpression[i] === '=')
93
- {
94
- tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.buildPostfixedSolveList found multiple equality assignments in the tokenized expression; equality assignment #${tmpEqualsIndex} at token index ${i}.`);
95
- this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
96
- return tmpResults.PostfixTokenObjects;
90
+ if (tmpEqualsIndex < 0)
91
+ {
92
+ tmpEqualsIndex = i;
93
+ tmpResults.PostfixedAssignmentOperator = this.ExpressionParser.tokenMap[pTokenizedExpression[i]];
94
+ }
95
+ else
96
+ {
97
+ tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.buildPostfixedSolveList found multiple assignment operators in the tokenized expression; assignment operator '${pTokenizedExpression[i]}' #${tmpEqualsIndex} at token index ${i}.`);
98
+ this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
99
+ return tmpResults.PostfixTokenObjects;
100
+ }
97
101
  }
98
102
  }
99
103
 
@@ -563,6 +567,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
563
567
  // If this is a layer with one value, presume it's an assignment.
564
568
  if (tmpSolveLayerTokens.length === 1)
565
569
  {
570
+ // TODO: I think this is correct but with the addition of multiple assignment operators it's less clear.
566
571
  let tmpAbstractAssignToken = this.getTokenContainerObject('=');
567
572
  tmpAbstractAssignToken.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];
568
573
  // If this doesn't have a matching solvelayerstack, get the virtual symbol name from the parenthesis group it's in
@@ -637,7 +642,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
637
642
  }
638
643
 
639
644
  // 7. Lastly set the assignment address.
640
- let tmpAbstractAssignToken = this.getTokenContainerObject('=');
645
+ let tmpAbstractAssignToken = ('PostfixedAssignmentOperator' in tmpResults) ? this.getTokenContainerObject(tmpResults.PostfixedAssignmentOperator.Token) : this.getTokenContainerObject('=');
641
646
  // The address we are assigning to
642
647
  tmpAbstractAssignToken.VirtualSymbolName = tmpResults.PostfixedAssignmentAddress;
643
648
  // The address it's coming from
@@ -178,8 +178,22 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
178
178
  {
179
179
  if (pPostfixedExpression[i].RightValue.Type === 'Token.SolverMarshal')
180
180
  {
181
+ // Set the result in the virtual symbols
181
182
  tmpManifest.setValueAtAddress(tmpResults.VirtualSymbols, pPostfixedExpression[i].VirtualSymbolName, tmpSolverResultValue);
182
- tmpManifest.setValueByHash(tmpDataDestinationObject, pPostfixedExpression[i].VirtualSymbolName, tmpSolverResultValue);
183
+ // Set the value in the destination object
184
+ if (pPostfixedExpression[i].Operation.Descriptor.OnlyEmpty)
185
+ {
186
+ // If it is only on "empty" values, check if the value is empty before assigning
187
+ if (this.fable.Utility.addressIsNullOrEmpty(tmpDataDestinationObject, pPostfixedExpression[i].VirtualSymbolName))
188
+ {
189
+ tmpManifest.setValueByHash(tmpDataDestinationObject, pPostfixedExpression[i].VirtualSymbolName, tmpSolverResultValue);
190
+ }
191
+ }
192
+ else
193
+ {
194
+ // Otherwise, just assign it.
195
+ tmpManifest.setValueByHash(tmpDataDestinationObject, pPostfixedExpression[i].VirtualSymbolName, tmpSolverResultValue);
196
+ }
183
197
  }
184
198
  }
185
199
  tmpResults.RawResult = tmpSolverResultValue;
@@ -190,7 +204,14 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
190
204
  delete tmpResults.fable;
191
205
  }
192
206
 
193
- return tmpSolverResultValue.toString();
207
+ if (typeof(tmpSolverResultValue) !== 'undefined')
208
+ {
209
+ return tmpSolverResultValue.toString();
210
+ }
211
+ else
212
+ {
213
+ return tmpSolverResultValue;
214
+ }
194
215
  }
195
216
  }
196
217
 
@@ -1,13 +1,23 @@
1
1
  {
2
2
  "=":
3
3
  {
4
- "Name": "Equals",
4
+ "Name": "Assign Value",
5
5
  "Token": "=",
6
6
  "Function": "fable.Math.assignValue",
7
7
  "Precedence": 0,
8
8
  "Type": "Assignment"
9
9
  },
10
10
 
11
+ "?=":
12
+ {
13
+ "Name": "Null or Empty Coalescing Assign Value",
14
+ "Token": "?=",
15
+ "Function": "fable.Math.assignValue",
16
+ "OnlyEmpty": true,
17
+ "Precedence": 0,
18
+ "Type": "Assignment"
19
+ },
20
+
11
21
  "(":
12
22
  {
13
23
  "Name": "Left Parenthesis",
@@ -39,8 +39,47 @@ class FableServiceExpressionParser extends libFableServiceBase
39
39
 
40
40
  // The configuration for tokens that the solver recognizes, with precedence and friendly names.
41
41
  this.tokenMap = require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json');
42
- // This precedence is higher than defined in our token map. We could make this value dynamic
43
- this.tokenMaxPrecedence = 10;
42
+
43
+ // Keep track of maximum token precedence
44
+ this.tokenMaxPrecedence = 4;
45
+ // This isn't exactly a radix tree but close enough. It's a map of the first character of the token to the token.
46
+ this.tokenRadix = {};
47
+ let tmpTokenKeys = Object.keys(this.tokenMap);
48
+ for (let i = 0; i < tmpTokenKeys.length; i++)
49
+ {
50
+ let tmpTokenKey = tmpTokenKeys[i];
51
+ let tmpToken = this.tokenMap[tmpTokenKey];
52
+
53
+ tmpToken.Token = tmpTokenKey;
54
+ tmpToken.Length = tmpTokenKey.length;
55
+
56
+ let tmpTokenStartCharacter = tmpToken.Token[0];
57
+ if (!(tmpTokenStartCharacter in this.tokenRadix))
58
+ {
59
+ // With a token count of 1 and a literal of true, we can assume it being in the radix is the token.
60
+ this.tokenRadix[tmpTokenStartCharacter] = (
61
+ {
62
+ TokenCount: 0,
63
+ Literal: false,
64
+ TokenKeys: [],
65
+ TokenMap: {}
66
+ });
67
+ }
68
+
69
+ this.tokenRadix[tmpTokenStartCharacter].TokenCount++;
70
+ if (tmpTokenKey == tmpTokenStartCharacter)
71
+ {
72
+ this.tokenRadix[tmpTokenStartCharacter].Literal = true;
73
+ }
74
+ this.tokenRadix[tmpTokenStartCharacter].TokenMap[tmpToken.Token] = tmpToken;
75
+ this.tokenRadix[tmpTokenStartCharacter].TokenKeys.push(tmpTokenKey);
76
+ this.tokenRadix[tmpTokenStartCharacter].TokenKeys.sort((pLeft, pRight) => pRight.length - pLeft.length);
77
+
78
+ if (this.tokenMaxPrecedence < tmpToken.Precedence)
79
+ {
80
+ this.tokenMaxPrecedence = tmpToken.Precedence;
81
+ }
82
+ }
44
83
 
45
84
  // The configuration for which functions are available to the solver.
46
85
  this.functionMap = require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json');
@@ -126,7 +126,7 @@ class FableServiceMath extends libFableServiceBase
126
126
  {
127
127
  let tmpValue = isNaN(pValue) ? 0 : pValue;
128
128
  let tmpDecimals = isNaN(pDecimals) ? 0 : parseInt(pDecimals, 10);
129
- let tmpRoundingMethod = (typeof (pRoundingMethod) === 'undefined') ? this.roundHalfUp : pRoundingMethod;
129
+ let tmpRoundingMethod = (typeof (pRoundingMethod) === 'undefined') ? this.roundHalfUp : parseInt(pRoundingMethod, 10);
130
130
 
131
131
  let tmpArbitraryValue = new this.fable.Utility.bigNumber(tmpValue);
132
132
  let tmpResult = tmpArbitraryValue.round(tmpDecimals, tmpRoundingMethod);
@@ -561,27 +561,6 @@ class FableServiceMath extends libFableServiceBase
561
561
  return this.bucketSetPrecise(pValueSet);
562
562
  }
563
563
 
564
- /**
565
- *
566
- * @param {Array<Object>} pValueSet - An array of objects
567
- * @param {string} pValueMapAddress - The address in each object to find the value to count in the histogram. Undefined will be 'Unknown'.
568
- */
569
- valueMapCountPrecise(pValueSet, pValueMapAddress)
570
- {
571
- let tmpHistogram = {};
572
-
573
- this.log.info(`ValueSet is a ${typeof(pValueSet)}`);
574
- this.log.info(`ValueMapAddress is ${pValueMapAddress}`);
575
-
576
- return tmpHistogram;
577
- }
578
-
579
- valueMapSumPrecise(pValueSet, pValueMapAddress, pValueMapAmount)
580
- {
581
-
582
- }
583
-
584
-
585
564
  /**
586
565
  * Sorts the histogram object in ascending order based on the frequencies of the buckets.
587
566
  *
@@ -601,7 +580,6 @@ class FableServiceMath extends libFableServiceBase
601
580
  }
602
581
 
603
582
  return tmpSortedHistogram;
604
-
605
583
  }
606
584
 
607
585
  /**
@@ -92,6 +92,51 @@ class FableServiceUtility extends libFableServiceBase
92
92
 
93
93
  return tmpChunkCache;
94
94
  }
95
+
96
+ /**
97
+ * Get a value from fable/pict by hash/address
98
+ * @param {string} pValueAddress - The manyfest hash/address of the value to get
99
+ */
100
+ getValue(pValueAddress)
101
+ {
102
+ // Lazily create a manifest if it doesn't exist
103
+ if (!this.manifest)
104
+ {
105
+ this.manifest = this.fable.newManyfest();
106
+ }
107
+
108
+ // Get the value from the internal manifest and return it
109
+ return this.manifest.getValueByHash(this.fable, pValueAddress);
110
+ }
111
+
112
+ /**
113
+ * Check if a value is null or empty
114
+ * @param {object} pObject - The object to check
115
+ * @param {string} pValueAddress - The manyfest hash/address to check
116
+ */
117
+ addressIsNullOrEmpty(pObject, pValueAddress)
118
+ {
119
+ // Lazily create a manifest if it doesn't exist
120
+ if (!this.manifest)
121
+ {
122
+ this.manifest = this.fable.newManyfest();
123
+ }
124
+
125
+ // If it doesn't exist, it is null or empty.
126
+ if (!this.manifest.checkAddressExists(pObject, pValueAddress))
127
+ {
128
+ return true;
129
+ }
130
+
131
+ // Get the value from the internal manifest and return it
132
+ let tmpValue = this.manifest.getValueByHash(pObject, pValueAddress);
133
+ if (tmpValue === null || tmpValue === '')
134
+ {
135
+ return true;
136
+ }
137
+
138
+ return false;
139
+ }
95
140
 
96
141
  // Convert an ISO string to a javascript date object
97
142
  // Adapted from https://stackoverflow.com/a/54751179