fable 3.1.8 → 3.1.9
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/dist/fable.js +97 -52
- package/dist/fable.js.map +1 -1
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/package.json +1 -1
- package/source/Fable.js +1 -0
- package/source/services/Fable-Service-DataFormat.js +65 -52
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js +2 -2
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +24 -4
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Messaging.js +3 -3
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js +25 -10
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js +20 -30
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json +2 -2
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ValueMarshal.js +2 -0
- package/source/services/Fable-Service-FilePersistence.js +3 -3
- package/source/services/Fable-Service-Logic.js +129 -0
- package/source/services/Fable-Service-Math.js +49 -11
- package/source/services/Fable-Service-Utility.js +35 -1
- package/source/services/Fable-SetConcatArray.js +25 -0
- package/test/CSVParser_tests.js +2 -2
- package/test/DataFormat-StringManipulation_tests.js +17 -1
- package/test/ExpressionParser_tests.js +154 -8
- package/test/FableOperation_tests.js +2 -2
- package/test/FilePersistence_tests.js +2 -2
- package/test/Math_test.js +4 -4
- package/test/MetaTemplating_tests.js +4 -4
package/package.json
CHANGED
package/source/Fable.js
CHANGED
|
@@ -75,6 +75,7 @@ class Fable extends libFableServiceBase.CoreServiceProviderBase
|
|
|
75
75
|
this.addAndInstantiateServiceType('DataFormat', require('./services/Fable-Service-DataFormat.js'));
|
|
76
76
|
this.addAndInstantiateServiceType('DataGeneration', require('./services/Fable-Service-DataGeneration.js'));
|
|
77
77
|
this.addAndInstantiateServiceType('Utility', require('./services/Fable-Service-Utility.js'));
|
|
78
|
+
this.addAndInstantiateServiceType('Logic', require('./services/Fable-Service-Logic.js'));
|
|
78
79
|
this.addAndInstantiateServiceType('Math', require('./services/Fable-Service-Math.js'));
|
|
79
80
|
this.addServiceType('ExpressionParser', require('./services/Fable-Service-ExpressionParser.js'));
|
|
80
81
|
this.addServiceType('RestClient', require('./services/Fable-Service-RestClient.js'));
|
|
@@ -22,6 +22,7 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
22
22
|
this._Regex_formatterDollarsRemoveCommas = /,/gi;
|
|
23
23
|
this._Regex_formatterCleanNonAlphaChar = /[^a-zA-Z]/gi;
|
|
24
24
|
this._Regex_formatterCapitalizeEachWord = /([a-zA-Z]+)/g;
|
|
25
|
+
this._Regex_matcherHTMLEntities = /&(#?[a-zA-Z0-9]+);/g;
|
|
25
26
|
|
|
26
27
|
// TODO: Potentially pull these in from a configuration.
|
|
27
28
|
// TODO: Use locale data for this if it's defaults all the way down.
|
|
@@ -157,6 +158,66 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} pString - The string to resolve
|
|
163
|
+
* @return {string} - The input string with all HTML entities resolved to their character counterparts
|
|
164
|
+
*/
|
|
165
|
+
resolveHtmlEntities(pString)
|
|
166
|
+
{
|
|
167
|
+
if (typeof(pString) !== 'string')
|
|
168
|
+
{
|
|
169
|
+
return pString;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return pString.replace(this._Regex_matcherHTMLEntities, (pMatch, pEntity) =>
|
|
173
|
+
{
|
|
174
|
+
switch (pEntity)
|
|
175
|
+
{
|
|
176
|
+
case 'comma':
|
|
177
|
+
return ',';
|
|
178
|
+
case 'amp':
|
|
179
|
+
return '&';
|
|
180
|
+
case 'lt':
|
|
181
|
+
return '<';
|
|
182
|
+
case 'gt':
|
|
183
|
+
return '>';
|
|
184
|
+
case 'times':
|
|
185
|
+
return '×';
|
|
186
|
+
case 'divide':
|
|
187
|
+
return '÷';
|
|
188
|
+
case 'plus':
|
|
189
|
+
return '+';
|
|
190
|
+
case 'minus':
|
|
191
|
+
return '-';
|
|
192
|
+
case 'infin':
|
|
193
|
+
return '∞';
|
|
194
|
+
case 'ang':
|
|
195
|
+
return '∠';
|
|
196
|
+
case 'quot':
|
|
197
|
+
return '"';
|
|
198
|
+
case 'apos':
|
|
199
|
+
return '\'';
|
|
200
|
+
case 'nbsp':
|
|
201
|
+
return ' ';
|
|
202
|
+
case 'copy':
|
|
203
|
+
return '©';
|
|
204
|
+
case 'reg':
|
|
205
|
+
return '®';
|
|
206
|
+
case 'trade':
|
|
207
|
+
return '™';
|
|
208
|
+
case 'euro':
|
|
209
|
+
return '€';
|
|
210
|
+
default:
|
|
211
|
+
if (!pEntity.startsWith('#'))
|
|
212
|
+
{
|
|
213
|
+
return pMatch;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const tmpNumericalValue = parseInt(pEntity.substring(1), 10);
|
|
217
|
+
return String.fromCharCode(tmpNumericalValue);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
160
221
|
/**
|
|
161
222
|
* Concatenate a list of strings together. Non-strings are excluded.
|
|
162
223
|
*
|
|
@@ -177,19 +238,7 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
177
238
|
concatenateStringsInternal ()
|
|
178
239
|
{
|
|
179
240
|
const pParams = [ ...arguments ];
|
|
180
|
-
const
|
|
181
|
-
{
|
|
182
|
-
if (Array.isArray(p))
|
|
183
|
-
{
|
|
184
|
-
return p.flatMap(tmpArrayFlattener);
|
|
185
|
-
}
|
|
186
|
-
if (typeof p === 'object')
|
|
187
|
-
{
|
|
188
|
-
return Object.values(p);
|
|
189
|
-
}
|
|
190
|
-
return [ p ];
|
|
191
|
-
};
|
|
192
|
-
const tmpFlattenedArrays = pParams.flatMap(tmpArrayFlattener);
|
|
241
|
+
const tmpFlattenedArrays = this.fable.Utility.flattenArrayOfSolverInputs(pParams);
|
|
193
242
|
|
|
194
243
|
return this.concatenateStrings(...tmpFlattenedArrays);
|
|
195
244
|
}
|
|
@@ -216,19 +265,7 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
216
265
|
joinStringsInternal()
|
|
217
266
|
{
|
|
218
267
|
const [ pJoinOn, ...pParams ] = arguments;
|
|
219
|
-
const
|
|
220
|
-
{
|
|
221
|
-
if (Array.isArray(p))
|
|
222
|
-
{
|
|
223
|
-
return p.flatMap(tmpArrayFlattener);
|
|
224
|
-
}
|
|
225
|
-
if (typeof p === 'object')
|
|
226
|
-
{
|
|
227
|
-
return Object.values(p);
|
|
228
|
-
}
|
|
229
|
-
return [ p ];
|
|
230
|
-
};
|
|
231
|
-
const tmpFlattenedArrays = pParams.flatMap(tmpArrayFlattener);
|
|
268
|
+
const tmpFlattenedArrays = this.fable.Utility.flattenArrayOfSolverInputs(pParams);
|
|
232
269
|
|
|
233
270
|
return this.joinStrings(pJoinOn, ...tmpFlattenedArrays);
|
|
234
271
|
}
|
|
@@ -254,19 +291,7 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
254
291
|
concatenateStringsRawInternal (pValueObjectSetAddress)
|
|
255
292
|
{
|
|
256
293
|
const pParams = [ ...arguments ];
|
|
257
|
-
const
|
|
258
|
-
{
|
|
259
|
-
if (Array.isArray(p))
|
|
260
|
-
{
|
|
261
|
-
return p.flatMap(tmpArrayFlattener);
|
|
262
|
-
}
|
|
263
|
-
if (typeof p === 'object')
|
|
264
|
-
{
|
|
265
|
-
return Object.values(p);
|
|
266
|
-
}
|
|
267
|
-
return [ p ];
|
|
268
|
-
};
|
|
269
|
-
const tmpFlattenedArrays = pParams.flatMap(tmpArrayFlattener);
|
|
294
|
+
const tmpFlattenedArrays = this.fable.Utility.flattenArrayOfSolverInputs(pParams);
|
|
270
295
|
|
|
271
296
|
return this.concatenateStringsRaw(...tmpFlattenedArrays);
|
|
272
297
|
}
|
|
@@ -293,19 +318,7 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
293
318
|
joinStringsRawInternal ()
|
|
294
319
|
{
|
|
295
320
|
const [ pJoinOn, ...pParams ] = arguments;
|
|
296
|
-
const
|
|
297
|
-
{
|
|
298
|
-
if (Array.isArray(p))
|
|
299
|
-
{
|
|
300
|
-
return p.flatMap(tmpArrayFlattener);
|
|
301
|
-
}
|
|
302
|
-
if (typeof p === 'object')
|
|
303
|
-
{
|
|
304
|
-
return Object.values(p);
|
|
305
|
-
}
|
|
306
|
-
return [ p ];
|
|
307
|
-
};
|
|
308
|
-
const tmpFlattenedArrays = pParams.flatMap(tmpArrayFlattener);
|
|
321
|
+
const tmpFlattenedArrays = this.fable.Utility.flattenArrayOfSolverInputs(pParams);
|
|
309
322
|
|
|
310
323
|
return this.joinStringsRaw(pJoinOn, ...tmpFlattenedArrays);
|
|
311
324
|
}
|
|
@@ -45,8 +45,8 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
|
|
|
45
45
|
let tmpCharacter = pExpression[i];
|
|
46
46
|
|
|
47
47
|
// [ WHITESPACE ]
|
|
48
|
-
// 1. Space breaks tokens except when we're in an address that's been scoped by a {}
|
|
49
|
-
if ((tmpCharacter === ' ') && ((tmpCurrentTokenType !== 'StateAddress') && (tmpCurrentTokenType !== 'String')))
|
|
48
|
+
// 1. Space breaks tokens except when we're in an address that's been scoped by a {} or ""
|
|
49
|
+
if ((tmpCharacter === ' ' || tmpCharacter === '\t') && ((tmpCurrentTokenType !== 'StateAddress') && (tmpCurrentTokenType !== 'String')))
|
|
50
50
|
{
|
|
51
51
|
if (tmpCurrentToken.length > 0)
|
|
52
52
|
{
|
|
@@ -129,16 +129,26 @@
|
|
|
129
129
|
"Address": "fable.Utility.getInternalValueByHash"
|
|
130
130
|
},
|
|
131
131
|
|
|
132
|
+
"flatten": {
|
|
133
|
+
"Name": "flatten an array of values",
|
|
134
|
+
"Address": "fable.Utility.flattenArrayOfSolverInputs"
|
|
135
|
+
},
|
|
136
|
+
|
|
132
137
|
"findfirstvaluebyexactmatch": {
|
|
133
|
-
"Name": "find + map on array of objects
|
|
138
|
+
"Name": "find + map on array of objects",
|
|
134
139
|
"Address": "fable.Utility.findFirstValueByExactMatchInternal"
|
|
135
140
|
},
|
|
136
141
|
|
|
137
142
|
"findfirstvaluebystringincludes": {
|
|
138
|
-
"Name": "find + map on array of objects
|
|
143
|
+
"Name": "find + map on array of objects",
|
|
139
144
|
"Address": "fable.Utility.findFirstValueByStringIncludesInternal"
|
|
140
145
|
},
|
|
141
146
|
|
|
147
|
+
"resolvehtmlentities": {
|
|
148
|
+
"Name": "resolve HTML entities",
|
|
149
|
+
"Address": "fable.Utility.resolveHtmlEntities"
|
|
150
|
+
},
|
|
151
|
+
|
|
142
152
|
"concat": {
|
|
143
153
|
"Name": "concatenate an array of values and output a string",
|
|
144
154
|
"Address": "fable.DataFormat.concatenateStringsInternal"
|
|
@@ -159,15 +169,25 @@
|
|
|
159
169
|
"Address": "fable.DataFormat.joinStringsRawInternal"
|
|
160
170
|
},
|
|
161
171
|
|
|
172
|
+
"if": {
|
|
173
|
+
"Name": "perform a conditional operator on two values, and choose one of two outcomes based on the result",
|
|
174
|
+
"Address": "fable.Logic.checkIf"
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
"when": {
|
|
178
|
+
"Name": "perform a 'truthy' check on one value, and return one of two outcomes based on the result",
|
|
179
|
+
"Address": "fable.Logic.when"
|
|
180
|
+
},
|
|
181
|
+
|
|
162
182
|
"entryinset": {
|
|
163
183
|
"Name": "Entry in Set",
|
|
164
184
|
"Address": "fable.Math.entryInSet"
|
|
165
185
|
},
|
|
166
|
-
"
|
|
186
|
+
"smallestinset": {
|
|
167
187
|
"Name": "Smallest in Set",
|
|
168
188
|
"Address": "fable.Math.smallestInSet"
|
|
169
189
|
},
|
|
170
|
-
"
|
|
190
|
+
"largestinset": {
|
|
171
191
|
"Name": "Largest in Set",
|
|
172
192
|
"Address": "fable.Math.largestInSet"
|
|
173
193
|
},
|
package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Messaging.js
CHANGED
|
@@ -25,7 +25,7 @@ class ExpressionParserMessaging extends libExpressionParserOperationBase
|
|
|
25
25
|
{
|
|
26
26
|
let tmpVirtualSymbol = this.getOperationVirtualSymbolName(pToken);
|
|
27
27
|
|
|
28
|
-
if ((pToken.Type == 'Token.Constant') && (pToken.Value))
|
|
28
|
+
if ((pToken.Type == 'Token.Symbol' || pToken.Type == 'Token.Constant') && (pToken.Value))
|
|
29
29
|
{
|
|
30
30
|
return pToken.Value.toString();
|
|
31
31
|
}
|
|
@@ -166,9 +166,9 @@ class ExpressionParserMessaging extends libExpressionParserOperationBase
|
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
for (let i = 0; i <
|
|
169
|
+
for (let i = 0; i < pResultObject.PostfixSolveList.length; i++)
|
|
170
170
|
{
|
|
171
|
-
let tmpToken =
|
|
171
|
+
let tmpToken = pResultObject.PostfixSolveList[i];
|
|
172
172
|
console.log(`${i}: ${tmpToken.VirtualSymbolName} = (${tmpToken.LeftValue.Token}::${tmpToken.LeftValue.Value}) ${tmpToken.Operation.Token} (${tmpToken.RightValue.Token}::${tmpToken.RightValue.Value}) `)
|
|
173
173
|
}
|
|
174
174
|
|
package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js
CHANGED
|
@@ -243,6 +243,9 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
|
|
|
243
243
|
// This maps layer stack addresses (which match parenthesis virtual symbol names) to the resultant value for that layer stack.
|
|
244
244
|
// These values change as it solves but the last assignment is the proper assignment because math only reads forward in a line
|
|
245
245
|
tmpResults.PostfixLayerstackMap = {};
|
|
246
|
+
//FIXME: vet these - do we need a suffix version?
|
|
247
|
+
const unaryEligibleOperationTokens = [ '+', '-' ];
|
|
248
|
+
const unaryOperationPrefixTriggerTypes = [ 'Token.Operator', 'Token.Assignment' ];
|
|
246
249
|
for (let tmpSolveLayerIndex = 0; tmpSolveLayerIndex < tmpSolveLayerKeys.length; tmpSolveLayerIndex++)
|
|
247
250
|
{
|
|
248
251
|
let tmpSolveLayerTokens = tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];
|
|
@@ -253,11 +256,22 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
|
|
|
253
256
|
// There is a recursive way to do this, but given the short length of even the most complex equations we're favoring readability.
|
|
254
257
|
for (let i = 0; i < tmpSolveLayerTokens.length; i++)
|
|
255
258
|
{
|
|
259
|
+
const tmpToken = tmpSolveLayerTokens[i];
|
|
260
|
+
if (unaryEligibleOperationTokens.includes(tmpToken.Token) &&
|
|
261
|
+
// promote to unary if:
|
|
262
|
+
// 1. we are the first token in our group
|
|
263
|
+
// 2. we are prefixed by a token type that is incompatible with us being binary
|
|
264
|
+
(i == 0 || unaryOperationPrefixTriggerTypes.includes(tmpSolveLayerTokens[i - 1].Type)))
|
|
265
|
+
{
|
|
266
|
+
//FIXME: slow, but don't break the static data
|
|
267
|
+
tmpToken.Descriptor = JSON.parse(JSON.stringify(tmpToken.Descriptor));
|
|
268
|
+
tmpToken.Descriptor.Precedence = 1;
|
|
269
|
+
}
|
|
270
|
+
//FIXME: handle operators with dynamic precedence (ex. unary vs. bunary + and -)
|
|
256
271
|
// If the token is an operator and at the current precedence, add it to the postfix solve list and mutate the array.
|
|
257
272
|
if ((tmpSolveLayerTokens[i].Type === 'Token.Operator') &&
|
|
258
|
-
(
|
|
273
|
+
(tmpToken.Descriptor.Precedence === tmpPrecedence))
|
|
259
274
|
{
|
|
260
|
-
let tmpToken = tmpSolveLayerTokens[i];
|
|
261
275
|
// If there is a token and nothing else in this layer, then it's an error.
|
|
262
276
|
if (tmpSolveLayerTokens.length === 1)
|
|
263
277
|
{
|
|
@@ -265,6 +279,13 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
|
|
|
265
279
|
this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
|
|
266
280
|
return tmpResults.PostfixSolveList;
|
|
267
281
|
}
|
|
282
|
+
// The - at the beginning of an expression is a number line orientation modifier
|
|
283
|
+
else if ((i == 0) && (tmpToken.Token == '-' || tmpToken.Token == '+'))
|
|
284
|
+
{
|
|
285
|
+
tmpToken.VirtualSymbolName = `VNLO_${tmpVirtualSymbolIndex}`;
|
|
286
|
+
tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack] = tmpToken.VirtualSymbolName;
|
|
287
|
+
tmpVirtualSymbolIndex++;
|
|
288
|
+
}
|
|
268
289
|
// If the token is at the beginning of the expression and not a number line orientation modifier, it's an error.
|
|
269
290
|
else if ((i == 0) && ((tmpToken.Token != '+') || (tmpToken.Token != '-')))
|
|
270
291
|
{
|
|
@@ -280,13 +301,6 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
|
|
|
280
301
|
return tmpResults.PostfixSolveList;
|
|
281
302
|
}
|
|
282
303
|
|
|
283
|
-
// The - at the beginning of an expression is a number line orientation modifier
|
|
284
|
-
else if ((i == 0) && (tmpToken.Token == '-'))
|
|
285
|
-
{
|
|
286
|
-
tmpToken.VirtualSymbolName = `VNLO_${tmpVirtualSymbolIndex}`;
|
|
287
|
-
tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack] = tmpToken.VirtualSymbolName;
|
|
288
|
-
tmpVirtualSymbolIndex++;
|
|
289
|
-
}
|
|
290
304
|
// The - after an operator or an open parenthesis is also a number line orientation modifier
|
|
291
305
|
else if ((i > 0) && (tmpToken.Token == '-') && ((tmpSolveLayerTokens[i-1].Type === 'Token.Operator') || (tmpSolveLayerTokens[i-1].Token === '(')))
|
|
292
306
|
{
|
|
@@ -306,7 +320,8 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
|
|
|
306
320
|
}
|
|
307
321
|
|
|
308
322
|
// If the token is next to another operator it's a parsing error
|
|
309
|
-
else if ((tmpSolveLayerTokens[i-1].Type === 'Token.Operator') || (tmpSolveLayerTokens[i+1].Type === 'Token.Operator'))
|
|
323
|
+
else if (((tmpSolveLayerTokens[i-1].Type === 'Token.Operator') || (tmpSolveLayerTokens[i+1].Type === 'Token.Operator')) &&
|
|
324
|
+
(tmpSolveLayerTokens[i+1].Token != '-' && tmpSolveLayerTokens[i+1].Token != '+'))
|
|
310
325
|
{
|
|
311
326
|
tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.buildPostfixedSolveList found an operator at token index ${i} that is not surrounded by two values.`);
|
|
312
327
|
this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const libExpressionParserOperationBase = require('./Fable-Service-ExpressionParser-Base.js');
|
|
2
|
+
const libSetConcatArray = require('../Fable-SetConcatArray.js');
|
|
2
3
|
|
|
3
4
|
class ExpressionParserSolver extends libExpressionParserOperationBase
|
|
4
5
|
{
|
|
@@ -41,6 +42,7 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
|
|
|
41
42
|
|
|
42
43
|
for (let i = 0; i < pPostfixedExpression.length; i++)
|
|
43
44
|
{
|
|
45
|
+
// X = SUM(15, SUM(SIN(25), 10), (5 + 2), 3)
|
|
44
46
|
if (pPostfixedExpression[i].Operation.Type === 'Token.SolverInstruction')
|
|
45
47
|
{
|
|
46
48
|
continue;
|
|
@@ -89,7 +91,7 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
|
|
|
89
91
|
{
|
|
90
92
|
// TODO: This can be optimized. A lot. If necessary. Seems pretty fast honestly for even thousands of operations. Slowest part is arbitrary precision.
|
|
91
93
|
// An operator always has a left and right value.
|
|
92
|
-
let tmpFunctionAddress
|
|
94
|
+
let tmpFunctionAddress;
|
|
93
95
|
// Note: There are easier, passive ways of managing this state. But this is complex.
|
|
94
96
|
let tmpIsFunction = false;
|
|
95
97
|
if (tmpStepResultObject.ExpressionStep.Operation.Token in this.ExpressionParser.tokenMap)
|
|
@@ -106,39 +108,27 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
|
|
|
106
108
|
{
|
|
107
109
|
try
|
|
108
110
|
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (typeof(tmpStepResultObject.ExpressionStep.LeftValue.Value) === 'undefined')
|
|
111
|
+
let tmpResult;
|
|
112
|
+
const tmpFunction = tmpManifest.getValueAtAddress(tmpStepResultObject, tmpFunctionAddress);
|
|
113
|
+
if (typeof tmpFunction === 'function')
|
|
113
114
|
{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
tmpStepResultObject.ExpressionStep.LeftValue.Arguments = tmpStepResultObject.ExpressionStep.LeftValue.Value;
|
|
126
|
-
tmpStepResultObject.ExpressionStep.LeftValue.ArgumentString = 'ExpressionStep.LeftValue.Arguments';
|
|
127
|
-
}
|
|
128
|
-
else
|
|
129
|
-
{
|
|
130
|
-
// Allow for string-based math sets.
|
|
131
|
-
tmpStepResultObject.ExpressionStep.LeftValue.Arguments = tmpStepResultObject.ExpressionStep.LeftValue.Value.toString().split(',');
|
|
132
|
-
for (let j = 0; j < tmpStepResultObject.ExpressionStep.LeftValue.Arguments.length; j++)
|
|
115
|
+
let tmpFunctionBinding = null;
|
|
116
|
+
if (tmpFunctionAddress.includes('.'))
|
|
117
|
+
{
|
|
118
|
+
tmpFunctionBinding = tmpManifest.getValueAtAddress(tmpStepResultObject, tmpFunctionAddress.split('.').slice(0, -1).join('.'));
|
|
119
|
+
}
|
|
120
|
+
let tmpArguments = tmpStepResultObject.ExpressionStep.LeftValue.Value;
|
|
121
|
+
if (!(tmpArguments instanceof libSetConcatArray))
|
|
122
|
+
{
|
|
123
|
+
tmpArguments = [ tmpArguments ];
|
|
124
|
+
}
|
|
125
|
+
else
|
|
133
126
|
{
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
tmpStepResultObject.ExpressionStep.LeftValue.ArgumentString += ',';
|
|
137
|
-
}
|
|
138
|
-
tmpStepResultObject.ExpressionStep.LeftValue.ArgumentString += `ExpressionStep.LeftValue.Arguments[${j}]`;
|
|
127
|
+
tmpArguments = tmpArguments.values;
|
|
139
128
|
}
|
|
129
|
+
tmpResult = tmpFunction.apply(tmpFunctionBinding, tmpArguments);
|
|
140
130
|
}
|
|
141
|
-
tmpManifest.setValueAtAddress(tmpResults.VirtualSymbols, tmpStepResultObject.ExpressionStep.VirtualSymbolName,
|
|
131
|
+
tmpManifest.setValueAtAddress(tmpResults.VirtualSymbols, tmpStepResultObject.ExpressionStep.VirtualSymbolName, tmpResult);
|
|
142
132
|
tmpResults.LastResult = tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols, tmpStepResultObject.ExpressionStep.VirtualSymbolName);
|
|
143
133
|
//this.log.trace(` ---> Step ${i}: ${tmpResults.VirtualSymbols[tmpStepResultObject.ExpressionStep.VirtualSymbolName]}`)
|
|
144
134
|
}
|
|
@@ -60,6 +60,8 @@ class ExpressionParserValueMarshal extends libExpressionParserOperationBase
|
|
|
60
60
|
// }
|
|
61
61
|
if (!tmpValue)
|
|
62
62
|
{
|
|
63
|
+
tmpToken.Value = tmpValue;
|
|
64
|
+
tmpToken.Resolve = true;
|
|
63
65
|
tmpResults.ExpressionParserLog.push(`WARNING: ExpressionParser.substituteValuesInTokenizedObjects found no value for the symbol hash or address ${tmpToken.Token} at index ${i}`);
|
|
64
66
|
this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
|
|
65
67
|
continue;
|
|
@@ -74,17 +74,17 @@ class FableServiceFilePersistence extends libFableServiceBase
|
|
|
74
74
|
let tmpRecordFunction = (typeof(fRecordFunction) === 'function') ? fRecordFunction :
|
|
75
75
|
(pRecord) =>
|
|
76
76
|
{
|
|
77
|
-
this.fable.log(`CSV Reader received line ${pRecord}`);
|
|
77
|
+
this.fable.log.trace(`CSV Reader received line ${pRecord}`);
|
|
78
78
|
};
|
|
79
79
|
let tmpCompleteFunction = (typeof(fCompleteFunction) === 'function') ? fCompleteFunction :
|
|
80
80
|
() =>
|
|
81
81
|
{
|
|
82
|
-
this.fable.log(`CSV Read of ${pFilePath} complete.`);
|
|
82
|
+
this.fable.log.info(`CSV Read of ${pFilePath} complete.`);
|
|
83
83
|
};
|
|
84
84
|
let tmpErrorFunction = (typeof(fErrorFunction) === 'function') ? fErrorFunction :
|
|
85
85
|
(pError) =>
|
|
86
86
|
{
|
|
87
|
-
this.fable.log(`CSV Read of ${pFilePath} Error: ${pError}`, pError);
|
|
87
|
+
this.fable.log.error(`CSV Read of ${pFilePath} Error: ${pError}`, pError);
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
return this.lineReaderFactory(pFilePath,
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const libFableServiceBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
class FableServiceLogic extends libFableServiceBase
|
|
4
|
+
{
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../Fable.js')} pFable - The fable object
|
|
7
|
+
* @param {Record<string, any>} [pOptions] - The options object
|
|
8
|
+
* @param {string} [pServiceHash] - The hash of the service
|
|
9
|
+
*/
|
|
10
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
11
|
+
{
|
|
12
|
+
super(pFable, pOptions, pServiceHash);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Find the first value in an object that contains a specific value
|
|
17
|
+
*
|
|
18
|
+
* @param {string|number} pLeft - The left value to check
|
|
19
|
+
* @param {string} pComparisonOperator - The comparison operator to use
|
|
20
|
+
* @param {string|number} pRight - The right value to check
|
|
21
|
+
* @param {any} pOnTrue - The value to return if the comparison is true
|
|
22
|
+
* @param {any} [pOnFalse = ''] - The value to return if the comparison is false
|
|
23
|
+
* @return {any} - The selected value
|
|
24
|
+
*/
|
|
25
|
+
checkIf(pLeft, pComparisonOperator, pRight, pOnTrue, pOnFalse)
|
|
26
|
+
{
|
|
27
|
+
// precise numeric
|
|
28
|
+
// string (non-numeric)
|
|
29
|
+
let tmpMathLeft = this.fable.Math.parsePrecise(pLeft, null);
|
|
30
|
+
let tmpMathRight = this.fable.Math.parsePrecise(pRight, null);
|
|
31
|
+
let tmpCheckResult = false;
|
|
32
|
+
if (tmpMathLeft === null || tmpMathRight === null)
|
|
33
|
+
{
|
|
34
|
+
if (typeof pOnFalse === 'undefined')
|
|
35
|
+
{
|
|
36
|
+
pOnFalse = '';
|
|
37
|
+
}
|
|
38
|
+
switch (pComparisonOperator)
|
|
39
|
+
{
|
|
40
|
+
case '<':
|
|
41
|
+
case 'LT':
|
|
42
|
+
tmpCheckResult = pLeft < pRight;
|
|
43
|
+
break;
|
|
44
|
+
case '<=':
|
|
45
|
+
case 'LTE':
|
|
46
|
+
tmpCheckResult = pLeft <= pRight;
|
|
47
|
+
break;
|
|
48
|
+
case '>':
|
|
49
|
+
case 'GT':
|
|
50
|
+
tmpCheckResult = pLeft > pRight;
|
|
51
|
+
break;
|
|
52
|
+
case '>=':
|
|
53
|
+
case 'GTE':
|
|
54
|
+
tmpCheckResult = pLeft >= pRight;
|
|
55
|
+
break;
|
|
56
|
+
case '==':
|
|
57
|
+
tmpCheckResult = pLeft == pRight;
|
|
58
|
+
case '===':
|
|
59
|
+
tmpCheckResult = pLeft === pRight;
|
|
60
|
+
default:
|
|
61
|
+
this.fable.log.warn(`[FableServiceLogic.checkIf] Invalid comparison operator: ${pComparisonOperator}`);
|
|
62
|
+
tmpCheckResult = pLeft == pRight;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
{
|
|
67
|
+
if (typeof pOnFalse === 'undefined')
|
|
68
|
+
{
|
|
69
|
+
pOnFalse = '0';
|
|
70
|
+
}
|
|
71
|
+
switch (pComparisonOperator)
|
|
72
|
+
{
|
|
73
|
+
case '<':
|
|
74
|
+
case 'LT':
|
|
75
|
+
tmpCheckResult = this.fable.Math.ltPrecise(tmpMathLeft, tmpMathRight);
|
|
76
|
+
break;
|
|
77
|
+
case '<=':
|
|
78
|
+
case 'LTE':
|
|
79
|
+
tmpCheckResult = this.fable.Math.ltePrecise(tmpMathLeft, tmpMathRight);
|
|
80
|
+
break;
|
|
81
|
+
case '>':
|
|
82
|
+
case 'GT':
|
|
83
|
+
tmpCheckResult = this.fable.Math.gtPrecise(tmpMathLeft, tmpMathRight);
|
|
84
|
+
break;
|
|
85
|
+
case '>=':
|
|
86
|
+
case 'GTE':
|
|
87
|
+
tmpCheckResult = this.fable.Math.gtePrecise(tmpMathLeft, tmpMathRight);
|
|
88
|
+
break;
|
|
89
|
+
case '==':
|
|
90
|
+
tmpCheckResult = this.fable.Math.comparePreciseWithin(tmpMathLeft, tmpMathRight, '0.000001') == 0;
|
|
91
|
+
break;
|
|
92
|
+
case '===':
|
|
93
|
+
tmpCheckResult = this.fable.Math.comparePrecise(tmpMathLeft, tmpMathRight) == 0;
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
this.fable.log.warn(`[FableServiceLogic.checkIf] Invalid comparison operator: ${pComparisonOperator}`);
|
|
97
|
+
tmpCheckResult = pLeft == pRight ? pOnTrue : pOnFalse;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return tmpCheckResult ? pOnTrue : pOnFalse;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Find the first value in an object that contains a specific value
|
|
105
|
+
*
|
|
106
|
+
* @param {any} pCheckForTruthy - The object to check
|
|
107
|
+
* @param {any} pOnTrue - The value to return if the object is truthy
|
|
108
|
+
* @param {any} [pOnFalse = ''] - The value to return if the object is falsy
|
|
109
|
+
* @return {any} - The value from the object
|
|
110
|
+
*/
|
|
111
|
+
when(pCheckForTruthy, pOnTrue, pOnFalse = '')
|
|
112
|
+
{
|
|
113
|
+
if (!pCheckForTruthy)
|
|
114
|
+
{
|
|
115
|
+
return pOnFalse;
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(pCheckForTruthy) && pCheckForTruthy.length < 1)
|
|
118
|
+
{
|
|
119
|
+
return pOnFalse;
|
|
120
|
+
}
|
|
121
|
+
if (typeof pCheckForTruthy === 'object' && Object.keys(pCheckForTruthy).length < 1)
|
|
122
|
+
{
|
|
123
|
+
return pOnFalse;
|
|
124
|
+
}
|
|
125
|
+
return pOnTrue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = FableServiceLogic;
|