fable 3.1.31 → 3.1.32

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.
@@ -18,683 +18,691 @@ function getExpressionParser()
18
18
  }
19
19
 
20
20
  suite
21
- (
22
- 'Expressions',
23
- function()
24
- {
25
- suite
26
- (
27
- 'Math Expressions',
28
- function()
29
- {
30
- test
21
+ (
22
+ 'Expressions',
23
+ function ()
24
+ {
25
+ suite
31
26
  (
32
- 'Exercise the Tokenizer',
33
- (fDone)=>
27
+ 'Math Expressions',
28
+ function ()
34
29
  {
35
- let tmpResultObject = {};
36
- let _Parser = getExpressionParser();
37
- let tokenizedResults = _Parser.tokenize('5 + 2', tmpResultObject);
38
- Expect(tokenizedResults.length).to.equal(3);
39
- Expect(tokenizedResults).to.deep.equal(['5', '+', '2']);
40
- Expect(tmpResultObject.RawExpression).to.equal('5 + 2');
41
- Expect(tmpResultObject.RawTokens).to.deep.equal(tokenizedResults);
42
- let complexTokenizedResults = _Parser.tokenize('5+3 - sqrt(75 / (3 + {Depth}) * Width)^ 3');
43
- Expect(complexTokenizedResults.length).to.equal(18);
44
- Expect(complexTokenizedResults).to.deep.equal(['5', '+', '3', '-', 'sqrt', '(', '75', '/', '(', '3', '+', '{Depth}', ')', '*', 'Width', ')', '^', '3']);
45
- let complexTokenizedResultsDoubleWidthOperators = _Parser.tokenize('SpecialValue ?= 5+3 - sqrt(75 / (3 + {Depth}) * Width)^ 3');
46
- Expect(complexTokenizedResultsDoubleWidthOperators.length).to.equal(20);
47
- Expect(complexTokenizedResultsDoubleWidthOperators).to.deep.equal(['SpecialValue', '?=', '5', '+', '3', '-', 'sqrt', '(', '75', '/', '(', '3', '+', '{Depth}', ')', '*', 'Width', ')', '^', '3']);
48
-
49
- let tokenizedWithSymbologyInQuotes = _Parser.tokenize('5 + 2 * "Hello World"');
50
- Expect(tokenizedWithSymbologyInQuotes.length).to.equal(5);
51
- Expect(tokenizedWithSymbologyInQuotes).to.deep.equal(['5', '+', '2', '*', '"Hello World"']);
52
- // TODO: refresh on the tokenization process and see if this is a valid test
53
- //Expect(tmpResultObject.RawExpression).to.equal('5 + 2');
54
- let tokenizedNegativeParams = _Parser.tokenize('SUM(10, -10)');
55
- //Expect(tokenizedNegativeParams.length).to.equal(6);
56
- Expect(tokenizedNegativeParams).to.deep.equal(['SUM', '(', '10', ',', '-', '10', ')']);
57
- let unbalancedParens = _Parser.tokenize('ROUND(5 + (2 * 10), 10))');
58
- Expect(unbalancedParens).to.deep.equal(['ROUND', '(', '5', '+', '(', '2', '*', '10', ')', ',', '10', ')', ')']);
59
- return fDone();
60
- }
61
- );
62
- test
63
- (
64
- 'Exercise the Linter',
65
- (fDone)=>
66
- {
67
- let tmpResultObject = {};
68
- let _Parser = getExpressionParser();
69
-
70
- let tokenizedResults = _Parser.tokenize('5 + 2', tmpResultObject);
71
- let lintedResults = _Parser.lintTokenizedExpression(tokenizedResults, tmpResultObject);
72
- Expect(lintedResults.length).to.equal(1);
73
- Expect(lintedResults[0]).to.contain('lintTokenizedExpression found no equality assignment in the tokenized')
74
-
75
- let complexTokenizedResults = _Parser.tokenize('Value = 5+3 - sqrt(75 / (3 + -{Depth}) * Width)^ 3');
76
- let complexLintedResults = _Parser.lintTokenizedExpression(complexTokenizedResults, tmpResultObject);
77
- Expect(complexLintedResults.length).to.equal(0);
78
-
79
- let brokenTokenizedResults = _Parser.tokenize('Value = 5+3 - sqrt(75 */ (3 + {Depth}) * Width)^ 3');
80
- let brokenLintedResults = _Parser.lintTokenizedExpression(brokenTokenizedResults, tmpResultObject);
81
- // The */ is invalid!!!!
82
- Expect(brokenLintedResults.length).to.equal(1);
83
-
84
- let unbalancedParensTokenizedResults = _Parser.tokenize('Value = ROUND(5 + (2 * 10), 10))');
85
- let unbalancedParensLintedResults = _Parser.lintTokenizedExpression(unbalancedParensTokenizedResults, tmpResultObject);
86
- // The unbalanced parens should be caught
87
- Expect(unbalancedParensLintedResults.length).to.equal(1);
88
- Expect(unbalancedParensLintedResults[0]).to.contain('unbalanced parenthesis');
89
- return fDone();
90
- }
91
- );
92
- test
93
- (
94
- 'Exercise Postfix Notation Conversion',
95
- (fDone)=>
96
- {
97
- let tmpResultObject = {};
98
-
99
- let _Parser = getExpressionParser();
100
-
101
- let tokenizedResults = _Parser.tokenize('= 5 + 2', tmpResultObject);
102
- let lintedResults = _Parser.lintTokenizedExpression(tokenizedResults, tmpResultObject);
103
- let postfixResults = _Parser.buildPostfixedSolveList(tokenizedResults, tmpResultObject);
104
- Expect(postfixResults.length).to.equal(2);
105
-
106
- let negativeConstantResults = _Parser.tokenize('Value = (-5 + 3)');
107
- let negativeConstantValueLintedResults = _Parser.lintTokenizedExpression(negativeConstantResults);
108
- let negativeConstantValueResults = _Parser.buildPostfixedSolveList(negativeConstantResults);
109
- Expect(negativeConstantValueResults.length).to.equal(3);
110
-
111
- let complexTokenizedResults = _Parser.tokenize('Value = 5+3 - sqrt(75 / (3 + -{Depth}) * Width)^ 3');
112
- let complexLintedResults = _Parser.lintTokenizedExpression(complexTokenizedResults);
113
- let complexPostfixResults = _Parser.buildPostfixedSolveList(complexTokenizedResults);
114
- Expect(complexPostfixResults.length).to.equal(9);
115
-
116
- return fDone();
117
- }
118
- );
119
- test
120
- (
121
- 'Exercise Variable Lookup and Substitution',
122
- (fDone)=>
123
- {
124
- let _Parser = getExpressionParser();
125
-
126
- let tmpResultObject = {};
127
- let tmpDataObject = {X: 5, Y: 3, Z: 75, Depth: 3, Width: 2};
128
- let tmpDestinationObject = {};
129
-
130
- let tmpArea = _Parser.solve('Area = X * Y * Z', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
131
-
132
- Expect(tmpArea).to.equal("1125");
133
-
134
- Expect(tmpDestinationObject.Area).to.equal("1125");
135
-
136
- return fDone();
137
- }
138
- );
139
- test
140
- (
141
- 'Exercise Marshaling to Value at a Hashed Address',
142
- (fDone)=>
143
- {
144
-
145
- let testFable = new libFable();
146
- testFable.AppData = {Pit: "Bottomless" };
147
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
148
-
149
- let tmpDataObject = {X: 5.867, Y: 3.1, Z: 75, Depth: 3, Width: 2};
150
- let tmpResultObject = {};
151
- let tmpDestinationObject = {};
152
-
153
- _Parser.solve('Area = X * Y * Z', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
154
- Expect(tmpDestinationObject.Area).to.equal("1364.0775");
155
-
156
- _Parser.solve('PitSize = getvalue("AppData.Pit")', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
157
- Expect(tmpDestinationObject.PitSize).to.equal("Bottomless");
158
-
159
- _Parser.solve('Value = -3', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
160
- Expect(tmpDestinationObject.Value).to.equal("-3");
161
-
162
- _Parser.solve('Value2 = (4 + -3)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
163
- Expect(tmpDestinationObject.Value2).to.equal("1");
164
-
165
- _Parser.solve('ValueN = 5+3 - sqrt(75 / (3 + -1) * 2)^ 3', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
166
- Expect(testFable.Math.ltPrecise(
167
- testFable.Math.absPrecise(
168
- testFable.Math.subtractPrecise(tmpDestinationObject.ValueN, "-641.5190528383289850727923780647")),
169
- '0.00000000000000001'))
170
- .to.equal(true, 'Expected complex expression to be with a small epsilon of the computed value');
171
-
172
- tmpDataObject.Temp = '24';
173
- tmpDataObject.HR = '20.5';
174
- _Parser.solve('2.71828182845905 ^ -0.282444', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
175
- testFable.ExpressionParser.Messaging.logFunctionSolve(tmpResultObject);
176
- _Parser.solve('EGS=ROUND(ROUND(0.0172834*2.71828182845905^(-0.0117685*Temp),5)*SQRT(ROUND(16.294-0.163*HR,1)/60),4)',
177
- tmpDataObject, tmpResultObject, false, tmpDestinationObject);
178
- _Parser.solve('EGSR=ROUND(ROUND(0.0172834*2.71828182845905^(((1-2)*0.0117685)*Temp),5)*SQRT(ROUND(16.294-0.163*HR,1)/60),4)',
179
- tmpDataObject, tmpResultObject, false, tmpDestinationObject);
180
- Expect(tmpDestinationObject.EGS).to.equal(tmpDestinationObject.EGSR);
181
- testFable.ExpressionParser.Messaging.logFunctionSolve(tmpResultObject);
182
- Expect(tmpDestinationObject.EGS).to.equal("0.0061");
183
-
184
- tmpDataObject.EJBinderGb1 = '15.5';
185
- tmpDataObject.EKPctBinderPb2 = '2.55555';
186
- _Parser.solve('EGse1 = Round((100 - EJBinderGb1) / EKPctBinderPb2), 2)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
187
- //TODO: we can consider solving and ignoring this paren, but for now, the error is fatal
188
- Expect(tmpDestinationObject.EGse1).to.not.exist;
189
- testFable.ExpressionParser.Messaging.logFunctionSolve(tmpResultObject);
190
-
191
- _Parser.solve('EGse1 = Round((100 - EJBinderGb1) / EKPctBinderPb2, 2)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
192
- Expect(tmpDestinationObject.EGse1).to.equal('33.07');
193
-
194
- _Parser.solve('EGse1 = Round((100 - EJBinderGb1) / EKPctBinderPb2), 2)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
195
- // ensure that this does mutate the output on error
196
- Expect(tmpDestinationObject.EGse1).to.not.exist;
197
-
198
- return fDone();
199
- }
200
- );
201
- test
202
- (
203
- 'Test iterative series',
204
- (fDone)=>
205
- {
206
- let testFable = new libFable();
207
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
208
-
209
- let tmpResult = _Parser.solve('Result = ITERATIVESERIES(Values, "Value", "Resultant", 1, "add")',
210
- {
211
- Values: [
212
- {Value: 10},
213
- {Value: 20},
214
- {Value: 5}
215
- ]
216
- });
217
-
218
- Expect(tmpResult[0].Resultant).to.equal("10");
219
- Expect(tmpResult[1].Resultant).to.equal("30");
220
- Expect(tmpResult[2].Resultant).to.equal("35");
221
-
222
- return fDone();
223
- }
224
- )
225
- test
226
- (
227
- 'Exercise End-to-End Expression Parsing',
228
- (fDone)=>
229
- {
230
- let _Parser = getExpressionParser();
231
-
232
- Expect(_Parser.solve('1 + 1')).to.equal("2");
233
- Expect(_Parser.solve('1 * 1')).to.equal("1");
234
- Expect(_Parser.solve('1 / 1')).to.equal("1");
235
- Expect(_Parser.solve('1 - 1')).to.equal("0");
236
- Expect(_Parser.solve('10 * 2')).to.equal("20");
237
-
238
- Expect(_Parser.solve('200 * 1.5')).to.equal("300");
239
- Expect(_Parser.solve('7 +13')).to.equal("20");
240
- Expect(_Parser.solve('7+14')).to.equal("21");
241
-
242
- Expect(_Parser.solve('Result = datemilliseconddifference("2023-08-10T05:00:00.000Z", "2023-08-11T05:00:00.000Z")')).to.equal("86400000");
243
- Expect(_Parser.solve('Result = datemilliseconddifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("86400000");
244
- Expect(_Parser.solve('Result = datemilliseconddifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:11:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("85740000");
245
- Expect(_Parser.solve('Result = datemilliseconddifference(StartDate, EndDate, 1)', {StartDate: "2023-08-10T05:11:00.000Z"})).to.equal('NaN');
246
- Expect(_Parser.solve('Result = dateseconddifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("86400");
247
- Expect(_Parser.solve('Result = datehourdifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("24");
248
- Expect(_Parser.solve('Result = dateminutedifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("1440");
249
- Expect(_Parser.solve('Result = datehourdifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("24");
250
- Expect(_Parser.solve('Result = datedaydifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("1");
251
- Expect(_Parser.solve('Result = dateweekdifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-05-22T05:00:00.000Z"})).to.equal("-11");
252
- Expect(_Parser.solve('Result = datemonthdifference(StartDate, EndDate)', {StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2021-01-11T05:00:00.000Z"})).to.equal("-30");
253
- Expect(_Parser.solve('Result = dateyeardifference(StartDate, EndDate)', {StartDate: "1986-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z"})).to.equal("37");
254
-
255
- Expect(_Parser.solve('5 + 2 * 10')).to.equal("25");
256
- Expect(_Parser.solve('3.5 + 5 + 10 * 10 / 5')).to.equal("28.5");
257
-
258
- Expect(_Parser.solve('3.5 + 5 + 10 * 10 / 5 - 1.5')).to.equal("27");
259
-
260
- Expect(_Parser.solve('(100 - 10)')).to.equal("90");
261
-
262
- Expect(_Parser.solve('100.55 - 1.55')).to.equal("99");
263
-
264
- Expect(_Parser.solve('5 + 2')).to.equal('7');
265
-
266
- Expect(_Parser.solve('50.22 + 2')).to.equal('52.22');
267
-
268
- Expect(_Parser.solve('5 ^ 2')).to.equal('25');
269
-
270
- Expect(_Parser.solve('Result = sqrt(100 * (C + 30)) + sin(Depth - Width) / 10', { "PR": 1.5, "Z": "20.036237", "C": -13, Depth: 100.203, Width: 10.5})).to.equal('41.32965489638783839821');
271
-
272
- Expect(_Parser.solve('((15000 * 2) / 4)^2 + 100 - 10 * (35 + 5)')).to.equal("56249700");
273
-
274
- Expect(_Parser.solve('1.5 * sqrt(8 * 2.423782342^2) / 10')).to.equal('1.02832375808904701855')
275
- Expect(_Parser.solve('1 * sqrt(16)')).to.equal('4');
276
-
277
- Expect(_Parser.solve('sin(rad(60))')).to.equal('0.8660254037844386');
278
-
279
- Expect(_Parser.solve('Result = 5+3 - sqrt(75 / (3 + Depth) * Width)^ 3', { "PR": 1.5, "Z": "20.036237", "C": -13, Depth: 100.203, Width: 10.5}))
280
- .to.equal('-13.078386362213538715906797395732300153182132216343566001917247')
281
-
282
- let tmpResult = _Parser.solve('Result = (160 * PR * Z) / (C / 100) * PR * Z + (160 * (1 - C / 100))', {C:-13,PR:1.5,Z:20.03})
283
- Expect(tmpResult).to.equal("-1110837.0769230769230769230307");
284
-
285
- let tmpResultPrecise = _Parser.solve('Result = (160 * PR * Z) / (C / 100) * PR * Z + (160 * (1 - C / 100))', {C:"-13",PR:"1.5",Z:"20.03"})
286
- Expect(tmpResultPrecise).to.equal("-1110837.0769230769230769230307");
287
-
288
- return fDone();
289
- }
290
- );
291
- test
292
- (
293
- 'Exercise End-to-End Expression Parsing with Sets',
294
- (fDone)=>
295
- {
296
- let _Parser = getExpressionParser();
297
-
298
- Expect(_Parser.solve('1 + 1')).to.equal("2");
299
- Expect(_Parser.solve("Volume = Width * Height * Depth", {"Width": 73.5, "Height": 28.8, "Depth": 200.5})).to.equal("424418.4");
300
- Expect(_Parser.solve("TotalCost = SUM(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("400");
301
- Expect(_Parser.solve("TotalCost = MEAN(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("80");
302
- Expect(_Parser.solve("TotalCost = MEDIAN(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("50");
303
- Expect(_Parser.solve("TotalCost = COUNT(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("5");
304
-
305
- _Parser.fable.AppData = {
306
- Teams: [
307
- { Team: 'Mariners', States: 'Washington', Score: 100 },
308
- { Team: 'Yankees', States: 'New York', Score: 200 },
309
- { Team: 'Mets', States: 'New York', Score: 50 },
310
- { Team: 'Giants', States: 'California', Score: 45 },
311
- { Team: 'Dodgers', States: 'California', Score: 5 },
312
- { Team: 'Astros', States: 'Texas', Score: 75 }
313
- ]
314
- };
315
-
316
- let tmpHistogramByReference = _Parser.solve(`aggregationhistogram("AppData.Teams", "States", "Score")`);
317
- Expect(tmpHistogramByReference['New York']).to.equal("250");
318
-
319
- let tmpHistogram = _Parser.solve(`aggregationhistogrambyobject(getvalue("AppData.Teams"), "States", "Score")`);
320
- Expect(tmpHistogram['New York']).to.equal("250");
321
-
322
- Expect(_Parser.solve("DateFromPartsExample = DATEFROMPARTS(2025, 4, 1)")).to.equal("2025-04-01T00:00:00.000Z");
323
- Expect(_Parser.solve("DateFromPartsExample = DATEADDDAYS(DATEFROMPARTS(2025, 4, 1, 13, 03, 51, 761),87)")).to.equal("2025-06-27T13:03:51.761Z");
324
-
325
- return fDone();
326
- }
327
- );
328
- test
329
- (
330
- 'Exercise Value Marshaling',
331
- (fDone)=>
332
- {
333
- let _Parser = getExpressionParser();
334
-
335
- Expect(_Parser.solve('1 + 1')).to.equal("2");
336
- Expect(_Parser.solve("Volume = Width * Height * Depth", {"Width": 73.5, "Height": 28.8, "Depth": 200.5})).to.equal("424418.4");
337
- Expect(_Parser.solve("TotalCost = SUM(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("400");
338
- Expect(_Parser.solve("TotalCost = MEAN(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("80");
339
- Expect(_Parser.solve("TotalCost = MEDIAN(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("50");
340
- Expect(_Parser.solve("TotalCost = COUNT(ItemCosts)", {"ItemCosts": [100,200,50,45,5]})).to.equal("5");
341
-
342
- return fDone();
343
- }
344
- );
345
- test
346
- (
347
- 'Exercise Rounding',
348
- (fDone)=>
349
- {
350
- let _Parser = getExpressionParser();
351
-
352
- let tmpSolveResults = {};
353
- let tmpDataObject = {X: 5.867, Y: 3.1, Z: 75, Depth: 3, Width: 2};
354
- // 1364.0775
355
- let tmpResult = _Parser.solve('Area = ROUND(X * Y * Z)', tmpDataObject, tmpSolveResults);
356
- Expect(tmpResult).to.equal("1364");
357
- tmpResult = _Parser.solve('Area = ROUND(X * Y * Z, 2)', tmpDataObject, tmpSolveResults);
358
- Expect(tmpResult).to.equal("1364.08");
359
- return fDone();
360
- }
361
- );
362
- test
363
- (
364
- 'Exercise Multi-parameter Functions',
365
- (fDone)=>
366
- {
367
- let _Parser = getExpressionParser();
368
-
369
- let tmpSolveResults = {};
370
- let tmpDataObject = {X: 5.867, Y: 3.5, Z: 75.248923423, Depth: 3, Width: 2};
371
- // 1364.0775
372
- let tmpResult = _Parser.solve('Area = ROUND(X * Y * Z)', tmpDataObject, tmpSolveResults);
373
- Expect(tmpResult).to.equal("1545");
374
- tmpResult = _Parser.solve('Area = ROUND(X * Y * Z, 3, 3)', tmpDataObject, tmpSolveResults);
375
- Expect(tmpResult).to.equal("1545.2");
376
- // Test the getvaluarray function]
377
- // TODO: Fix the return values for these expression return functions
378
- //tmpResult = _Parser.solve('NewSet = GETVALUEARRAY(X, Y, Z)', tmpDataObject, tmpSolveResults);
379
- //tmpResult = _Parser.solve('NewSetAverage = SUM(NewSet)', tmpDataObject, tmpSolveResults);
380
- //Expect(tmpResult).to.equal("84.115923423");
381
- return fDone();
382
- }
383
- );
384
- test
385
- (
386
- 'Exercise Complex Assignments',
387
- (fDone)=>
388
- {
389
- let testFable = new libFable();
390
-
391
- testFable.AppData = {Students: ["Kim","Jim", "Joan Jett", "Tank Girl"] };
392
-
393
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
394
-
395
- let tmpResultObject = {};
396
-
397
- let tmpSolveResults = {};
398
- let tmpDataObject = {X: 5.867, Y: 3.5, Z: 75.248923423, Depth: 3, Width: 2, Name: "Jerry"};
399
-
400
- _Parser.solve('Area = ROUND(X * Y * Z, 3, 3)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
401
-
402
- // Because the results object doesn't have a name in it, the null coalescence assignment operator should just work
403
- let tmpResult = _Parser.solve('Name ?= GETVALUE("AppData.Students[0]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
404
- Expect(tmpResultObject.Name).to.equal("Kim");
405
-
406
- tmpResult = _Parser.solve('Area = ROUND(X * Y * Z, 3, 3)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
407
- Expect(tmpResult).to.equal("1545.2");
408
-
409
- // Now that name is set, it shouldn't change.
410
- tmpResult = _Parser.solve('Name ?= GETVALUE("AppData.Students[1]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
411
- Expect(tmpResultObject.Name).to.equal("Kim");
412
-
413
- // Regular assignment should change it.
414
- tmpResult = _Parser.solve('Name = GETVALUE("AppData.Students[1]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
415
- Expect(tmpResultObject.Name).to.equal("Jim");
416
-
417
- // Regular assignment should change it.
418
- tmpResult = _Parser.solve('Name = GETVALUE("AppData.Students[3]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
419
- Expect(tmpResultObject.Name).to.equal("Tank Girl");
420
-
421
- // Regular assignment should change it (with an array out of bounds problem...)
422
- tmpResult = _Parser.solve('Name = GETVALUE("AppData.Students[4]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
423
- Expect(tmpResultObject.Name).to.equal(undefined);
424
-
425
- return fDone();
426
- }
427
- );
428
- test
429
- (
430
- 'Exercise Data Generation',
431
- (fDone)=>
432
- {
433
- let testFable = new libFable();
434
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
435
-
436
- let tmpDataObject = {};
437
- let tmpSolveResults = {};
438
- let tmpResultObject = {};
439
-
440
- let tmpResult;
441
- tmpResult = _Parser.solve('RandomIntValue = RANDOMINTEGER()', tmpDataObject, tmpSolveResults, false, tmpResultObject);
442
- Expect(tmpResult).to.not.be.NaN;
443
-
444
- tmpResult = _Parser.solve('RandomIntValueBetween = RANDOMINTEGERBETWEEN(10, 13)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
445
- Expect(parseInt(tmpResult)).to.be.at.least(10);
446
- Expect(parseInt(tmpResult)).to.be.at.most(13);
447
-
448
- tmpResult = _Parser.solve('RandomFloatValue = randomFloat()', tmpDataObject, tmpSolveResults, false, tmpResultObject);
449
- Expect(tmpResult).to.not.be.NaN;
450
-
451
- tmpResult = _Parser.solve('RandomFloatValueBetween = randomFloatBetween(10.5, 13.78)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
452
- Expect(parseFloat(tmpResult)).to.be.at.least(10.5);
453
- Expect(parseFloat(tmpResult)).to.be.at.most(13.78);
454
-
455
-
456
- return fDone();
457
- }
458
- );
459
- test
460
- (
461
- 'Complex Histogram Arithmatic',
462
- (fDone)=>
463
- {
464
- let testFable = new libFable();
465
-
466
- let testCityData = require('./data/cities.json');
467
- testFable.AppData = { Cities: testCityData };
468
-
469
- // let tmpDistribution = testFable.Math.histogramDistributionByExactValue(testFable.AppData.Cities, 'state');
470
-
471
- // Expect(tmpDistribution.Alabama).to.equal(12);
472
- // Expect(tmpDistribution.Colorado).to.equal(21);
473
- // Expect(tmpDistribution.Florida).to.equal(73);
474
- // Expect(tmpDistribution.Georgia).to.equal(18);
475
-
476
- // Now through the solver
477
-
478
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
479
-
480
- testFable.AppData.Value1 = "100"; // Comment
481
- testFable.AppData.Value2 = "-80.5"; // Comment
482
- testFable.AppData.Value3 = "10000"; // Comment
483
- testFable.AppData.Value4 = "-333.333"; // Comment
484
- testFable.AppData.Value5 = "0"; // Comment
485
-
486
- let tmpResultsObject = {};
487
- let tmpDestinationObject = {};
488
-
489
- _Parser.solve('DistributionResult = distributionhistogram("AppData.Cities", "state")', testFable, tmpResultsObject, false, tmpDestinationObject);
490
- _Parser.solve('AggregationResult = aggregationHistogram("AppData.Cities", "state", "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
491
- _Parser.solve('PopSum = sum(flatten(AppData.Cities[].population, AppData.Cities[].latitude))', testFable, tmpResultsObject, false, tmpDestinationObject);
492
-
493
- //_Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
494
- //Expect(tmpDestinationObject.MadeUpValueArray).to.equal('600');
495
- _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(AppData.Value1, AppData.Value2, AppData.Value3, AppData.Value4, AppData.Value5)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
496
- Expect(tmpDestinationObject.MadeUpValueArray).to.equal('1937.23');
497
-
498
- _Parser.solve('MadeUpValueArray = ROUND(AVG(cleanvaluearray(createarrayfromabsolutevalues(AppData.Value1, AppData.Value2, AppData.Value3, AppData.Value4, AppData.Value5), 1)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
499
- Expect(tmpDestinationObject.MadeUpValueArray).to.equal('2421.54');
500
-
501
- // _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(100, -80.5, 10000, -333.333, 0)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
502
- // Expect(tmpDestinationObject.MadeUpValueArray).to.equal('600');
30
+ test
31
+ (
32
+ 'Exercise the Tokenizer',
33
+ (fDone) =>
34
+ {
35
+ let tmpResultObject = {};
36
+ let _Parser = getExpressionParser();
37
+ let tokenizedResults = _Parser.tokenize('5 + 2', tmpResultObject);
38
+ Expect(tokenizedResults.length).to.equal(3);
39
+ Expect(tokenizedResults).to.deep.equal(['5', '+', '2']);
40
+ Expect(tmpResultObject.RawExpression).to.equal('5 + 2');
41
+ Expect(tmpResultObject.RawTokens).to.deep.equal(tokenizedResults);
42
+ let complexTokenizedResults = _Parser.tokenize('5+3 - sqrt(75 / (3 + {Depth}) * Width)^ 3');
43
+ Expect(complexTokenizedResults.length).to.equal(18);
44
+ Expect(complexTokenizedResults).to.deep.equal(['5', '+', '3', '-', 'sqrt', '(', '75', '/', '(', '3', '+', '{Depth}', ')', '*', 'Width', ')', '^', '3']);
45
+ let complexTokenizedResultsDoubleWidthOperators = _Parser.tokenize('SpecialValue ?= 5+3 - sqrt(75 / (3 + {Depth}) * Width)^ 3');
46
+ Expect(complexTokenizedResultsDoubleWidthOperators.length).to.equal(20);
47
+ Expect(complexTokenizedResultsDoubleWidthOperators).to.deep.equal(['SpecialValue', '?=', '5', '+', '3', '-', 'sqrt', '(', '75', '/', '(', '3', '+', '{Depth}', ')', '*', 'Width', ')', '^', '3']);
48
+
49
+ let tokenizedWithSymbologyInQuotes = _Parser.tokenize('5 + 2 * "Hello World"');
50
+ Expect(tokenizedWithSymbologyInQuotes.length).to.equal(5);
51
+ Expect(tokenizedWithSymbologyInQuotes).to.deep.equal(['5', '+', '2', '*', '"Hello World"']);
52
+ // TODO: refresh on the tokenization process and see if this is a valid test
53
+ //Expect(tmpResultObject.RawExpression).to.equal('5 + 2');
54
+ let tokenizedNegativeParams = _Parser.tokenize('SUM(10, -10)');
55
+ //Expect(tokenizedNegativeParams.length).to.equal(6);
56
+ Expect(tokenizedNegativeParams).to.deep.equal(['SUM', '(', '10', ',', '-', '10', ')']);
57
+ let unbalancedParens = _Parser.tokenize('ROUND(5 + (2 * 10), 10))');
58
+ Expect(unbalancedParens).to.deep.equal(['ROUND', '(', '5', '+', '(', '2', '*', '10', ')', ',', '10', ')', ')']);
59
+ return fDone();
60
+ }
61
+ );
62
+ test
63
+ (
64
+ 'Exercise the Linter',
65
+ (fDone) =>
66
+ {
67
+ let tmpResultObject = {};
68
+ let _Parser = getExpressionParser();
69
+
70
+ let tokenizedResults = _Parser.tokenize('5 + 2', tmpResultObject);
71
+ let lintedResults = _Parser.lintTokenizedExpression(tokenizedResults, tmpResultObject);
72
+ Expect(lintedResults.length).to.equal(1);
73
+ Expect(lintedResults[0]).to.contain('lintTokenizedExpression found no equality assignment in the tokenized')
74
+
75
+ let complexTokenizedResults = _Parser.tokenize('Value = 5+3 - sqrt(75 / (3 + -{Depth}) * Width)^ 3');
76
+ let complexLintedResults = _Parser.lintTokenizedExpression(complexTokenizedResults, tmpResultObject);
77
+ Expect(complexLintedResults.length).to.equal(0);
78
+
79
+ let brokenTokenizedResults = _Parser.tokenize('Value = 5+3 - sqrt(75 */ (3 + {Depth}) * Width)^ 3');
80
+ let brokenLintedResults = _Parser.lintTokenizedExpression(brokenTokenizedResults, tmpResultObject);
81
+ // The */ is invalid!!!!
82
+ Expect(brokenLintedResults.length).to.equal(1);
83
+
84
+ let unbalancedParensTokenizedResults = _Parser.tokenize('Value = ROUND(5 + (2 * 10), 10))');
85
+ let unbalancedParensLintedResults = _Parser.lintTokenizedExpression(unbalancedParensTokenizedResults, tmpResultObject);
86
+ // The unbalanced parens should be caught
87
+ Expect(unbalancedParensLintedResults.length).to.equal(1);
88
+ Expect(unbalancedParensLintedResults[0]).to.contain('unbalanced parenthesis');
89
+ return fDone();
90
+ }
91
+ );
92
+ test
93
+ (
94
+ 'Exercise Postfix Notation Conversion',
95
+ (fDone) =>
96
+ {
97
+ let tmpResultObject = {};
98
+
99
+ let _Parser = getExpressionParser();
100
+
101
+ let tokenizedResults = _Parser.tokenize('= 5 + 2', tmpResultObject);
102
+ let lintedResults = _Parser.lintTokenizedExpression(tokenizedResults, tmpResultObject);
103
+ let postfixResults = _Parser.buildPostfixedSolveList(tokenizedResults, tmpResultObject);
104
+ Expect(postfixResults.length).to.equal(2);
105
+
106
+ let negativeConstantResults = _Parser.tokenize('Value = (-5 + 3)');
107
+ let negativeConstantValueLintedResults = _Parser.lintTokenizedExpression(negativeConstantResults);
108
+ let negativeConstantValueResults = _Parser.buildPostfixedSolveList(negativeConstantResults);
109
+ Expect(negativeConstantValueResults.length).to.equal(3);
110
+
111
+ let complexTokenizedResults = _Parser.tokenize('Value = 5+3 - sqrt(75 / (3 + -{Depth}) * Width)^ 3');
112
+ let complexLintedResults = _Parser.lintTokenizedExpression(complexTokenizedResults);
113
+ let complexPostfixResults = _Parser.buildPostfixedSolveList(complexTokenizedResults);
114
+ Expect(complexPostfixResults.length).to.equal(9);
115
+
116
+ return fDone();
117
+ }
118
+ );
119
+ test
120
+ (
121
+ 'Exercise Variable Lookup and Substitution',
122
+ (fDone) =>
123
+ {
124
+ let _Parser = getExpressionParser();
125
+
126
+ let tmpResultObject = {};
127
+ let tmpDataObject = { X: 5, Y: 3, Z: 75, Depth: 3, Width: 2 };
128
+ let tmpDestinationObject = {};
129
+
130
+ let tmpArea = _Parser.solve('Area = X * Y * Z', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
131
+
132
+ Expect(tmpArea).to.equal("1125");
133
+ Expect(tmpDestinationObject.Area).to.equal("1125");
134
+
135
+
136
+ let tmpVariableAssignment = _Parser.solve('NewX = X', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
137
+ let tmpConstantAssignment = _Parser.solve('NewConst = 8.675309', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
138
+ let tmpStringAssignment = _Parser.solve('NewString = "Hello World!"', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
139
+
140
+ Expect(tmpDestinationObject.NewX).to.equal("5");
141
+ Expect(tmpDestinationObject.NewConst).to.equal("8.675309");
142
+ Expect(tmpDestinationObject.NewString).to.equal("Hello World!");
143
+
144
+ return fDone();
145
+ }
146
+ );
147
+ test
148
+ (
149
+ 'Exercise Marshaling to Value at a Hashed Address',
150
+ (fDone) =>
151
+ {
152
+
153
+ let testFable = new libFable();
154
+ testFable.AppData = { Pit: "Bottomless" };
155
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
156
+
157
+ let tmpDataObject = { X: 5.867, Y: 3.1, Z: 75, Depth: 3, Width: 2 };
158
+ let tmpResultObject = {};
159
+ let tmpDestinationObject = {};
160
+
161
+ _Parser.solve('Area = X * Y * Z', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
162
+ Expect(tmpDestinationObject.Area).to.equal("1364.0775");
163
+
164
+ _Parser.solve('PitSize = getvalue("AppData.Pit")', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
165
+ Expect(tmpDestinationObject.PitSize).to.equal("Bottomless");
166
+
167
+ _Parser.solve('Value = -3', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
168
+ Expect(tmpDestinationObject.Value).to.equal("-3");
169
+
170
+ _Parser.solve('Value2 = (4 + -3)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
171
+ Expect(tmpDestinationObject.Value2).to.equal("1");
172
+
173
+ _Parser.solve('ValueN = 5+3 - sqrt(75 / (3 + -1) * 2)^ 3', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
174
+ Expect(testFable.Math.ltPrecise(
175
+ testFable.Math.absPrecise(
176
+ testFable.Math.subtractPrecise(tmpDestinationObject.ValueN, "-641.5190528383289850727923780647")),
177
+ '0.00000000000000001'))
178
+ .to.equal(true, 'Expected complex expression to be with a small epsilon of the computed value');
179
+
180
+ tmpDataObject.Temp = '24';
181
+ tmpDataObject.HR = '20.5';
182
+ _Parser.solve('2.71828182845905 ^ -0.282444', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
183
+ testFable.ExpressionParser.Messaging.logFunctionSolve(tmpResultObject);
184
+ _Parser.solve('EGS=ROUND(ROUND(0.0172834*2.71828182845905^(-0.0117685*Temp),5)*SQRT(ROUND(16.294-0.163*HR,1)/60),4)',
185
+ tmpDataObject, tmpResultObject, false, tmpDestinationObject);
186
+ _Parser.solve('EGSR=ROUND(ROUND(0.0172834*2.71828182845905^(((1-2)*0.0117685)*Temp),5)*SQRT(ROUND(16.294-0.163*HR,1)/60),4)',
187
+ tmpDataObject, tmpResultObject, false, tmpDestinationObject);
188
+ Expect(tmpDestinationObject.EGS).to.equal(tmpDestinationObject.EGSR);
189
+ testFable.ExpressionParser.Messaging.logFunctionSolve(tmpResultObject);
190
+ Expect(tmpDestinationObject.EGS).to.equal("0.0061");
191
+
192
+ tmpDataObject.EJBinderGb1 = '15.5';
193
+ tmpDataObject.EKPctBinderPb2 = '2.55555';
194
+ _Parser.solve('EGse1 = Round((100 - EJBinderGb1) / EKPctBinderPb2), 2)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
195
+ //TODO: we can consider solving and ignoring this paren, but for now, the error is fatal
196
+ Expect(tmpDestinationObject.EGse1).to.not.exist;
197
+ testFable.ExpressionParser.Messaging.logFunctionSolve(tmpResultObject);
198
+
199
+ _Parser.solve('EGse1 = Round((100 - EJBinderGb1) / EKPctBinderPb2, 2)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
200
+ Expect(tmpDestinationObject.EGse1).to.equal('33.07');
201
+
202
+ _Parser.solve('EGse1 = Round((100 - EJBinderGb1) / EKPctBinderPb2), 2)', tmpDataObject, tmpResultObject, false, tmpDestinationObject);
203
+ // ensure that this does mutate the output on error
204
+ Expect(tmpDestinationObject.EGse1).to.not.exist;
205
+
206
+ return fDone();
207
+ }
208
+ );
209
+ test
210
+ (
211
+ 'Test iterative series',
212
+ (fDone) =>
213
+ {
214
+ let testFable = new libFable();
215
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
216
+
217
+ let tmpResult = _Parser.solve('Result = ITERATIVESERIES(Values, "Value", "Resultant", 1, "add")',
218
+ {
219
+ Values: [
220
+ { Value: 10 },
221
+ { Value: 20 },
222
+ { Value: 5 }
223
+ ]
224
+ });
225
+
226
+ Expect(tmpResult[0].Resultant).to.equal("10");
227
+ Expect(tmpResult[1].Resultant).to.equal("30");
228
+ Expect(tmpResult[2].Resultant).to.equal("35");
229
+
230
+ return fDone();
231
+ }
232
+ )
233
+ test
234
+ (
235
+ 'Exercise End-to-End Expression Parsing',
236
+ (fDone) =>
237
+ {
238
+ let _Parser = getExpressionParser();
239
+
240
+ Expect(_Parser.solve('1 + 1')).to.equal("2");
241
+ Expect(_Parser.solve('1 * 1')).to.equal("1");
242
+ Expect(_Parser.solve('1 / 1')).to.equal("1");
243
+ Expect(_Parser.solve('1 - 1')).to.equal("0");
244
+ Expect(_Parser.solve('10 * 2')).to.equal("20");
245
+
246
+ Expect(_Parser.solve('200 * 1.5')).to.equal("300");
247
+ Expect(_Parser.solve('7 +13')).to.equal("20");
248
+ Expect(_Parser.solve('7+14')).to.equal("21");
249
+
250
+ Expect(_Parser.solve('Result = datemilliseconddifference("2023-08-10T05:00:00.000Z", "2023-08-11T05:00:00.000Z")')).to.equal("86400000");
251
+ Expect(_Parser.solve('Result = datemilliseconddifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("86400000");
252
+ Expect(_Parser.solve('Result = datemilliseconddifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:11:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("85740000");
253
+ Expect(_Parser.solve('Result = datemilliseconddifference(StartDate, EndDate, 1)', { StartDate: "2023-08-10T05:11:00.000Z" })).to.equal('NaN');
254
+ Expect(_Parser.solve('Result = dateseconddifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("86400");
255
+ Expect(_Parser.solve('Result = datehourdifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("24");
256
+ Expect(_Parser.solve('Result = dateminutedifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("1440");
257
+ Expect(_Parser.solve('Result = datehourdifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("24");
258
+ Expect(_Parser.solve('Result = datedaydifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("1");
259
+ Expect(_Parser.solve('Result = dateweekdifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2023-05-22T05:00:00.000Z" })).to.equal("-11");
260
+ Expect(_Parser.solve('Result = datemonthdifference(StartDate, EndDate)', { StartDate: "2023-08-10T05:00:00.000Z", EndDate: "2021-01-11T05:00:00.000Z" })).to.equal("-30");
261
+ Expect(_Parser.solve('Result = dateyeardifference(StartDate, EndDate)', { StartDate: "1986-08-10T05:00:00.000Z", EndDate: "2023-08-11T05:00:00.000Z" })).to.equal("37");
262
+
263
+ Expect(_Parser.solve('5 + 2 * 10')).to.equal("25");
264
+ Expect(_Parser.solve('3.5 + 5 + 10 * 10 / 5')).to.equal("28.5");
265
+
266
+ Expect(_Parser.solve('3.5 + 5 + 10 * 10 / 5 - 1.5')).to.equal("27");
267
+
268
+ Expect(_Parser.solve('(100 - 10)')).to.equal("90");
269
+
270
+ Expect(_Parser.solve('100.55 - 1.55')).to.equal("99");
271
+
272
+ Expect(_Parser.solve('5 + 2')).to.equal('7');
273
+
274
+ Expect(_Parser.solve('50.22 + 2')).to.equal('52.22');
275
+
276
+ Expect(_Parser.solve('5 ^ 2')).to.equal('25');
277
+
278
+ Expect(_Parser.solve('Result = sqrt(100 * (C + 30)) + sin(Depth - Width) / 10', { "PR": 1.5, "Z": "20.036237", "C": -13, Depth: 100.203, Width: 10.5 })).to.equal('41.32965489638783839821');
279
+
280
+ Expect(_Parser.solve('((15000 * 2) / 4)^2 + 100 - 10 * (35 + 5)')).to.equal("56249700");
281
+
282
+ Expect(_Parser.solve('1.5 * sqrt(8 * 2.423782342^2) / 10')).to.equal('1.02832375808904701855')
283
+ Expect(_Parser.solve('1 * sqrt(16)')).to.equal('4');
284
+
285
+ Expect(_Parser.solve('sin(rad(60))')).to.equal('0.8660254037844386');
286
+
287
+ Expect(_Parser.solve('Result = 5+3 - sqrt(75 / (3 + Depth) * Width)^ 3', { "PR": 1.5, "Z": "20.036237", "C": -13, Depth: 100.203, Width: 10.5 }))
288
+ .to.equal('-13.078386362213538715906797395732300153182132216343566001917247')
289
+
290
+ let tmpResult = _Parser.solve('Result = (160 * PR * Z) / (C / 100) * PR * Z + (160 * (1 - C / 100))', { C: -13, PR: 1.5, Z: 20.03 })
291
+ Expect(tmpResult).to.equal("-1110837.0769230769230769230307");
292
+
293
+ let tmpResultPrecise = _Parser.solve('Result = (160 * PR * Z) / (C / 100) * PR * Z + (160 * (1 - C / 100))', { C: "-13", PR: "1.5", Z: "20.03" })
294
+ Expect(tmpResultPrecise).to.equal("-1110837.0769230769230769230307");
295
+
296
+ return fDone();
297
+ }
298
+ );
299
+ test
300
+ (
301
+ 'Exercise End-to-End Expression Parsing with Sets',
302
+ (fDone) =>
303
+ {
304
+ let _Parser = getExpressionParser();
305
+
306
+ Expect(_Parser.solve('1 + 1')).to.equal("2");
307
+ Expect(_Parser.solve("Volume = Width * Height * Depth", { "Width": 73.5, "Height": 28.8, "Depth": 200.5 })).to.equal("424418.4");
308
+ Expect(_Parser.solve("TotalCost = SUM(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("400");
309
+ Expect(_Parser.solve("TotalCost = MEAN(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("80");
310
+ Expect(_Parser.solve("TotalCost = MEDIAN(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("50");
311
+ Expect(_Parser.solve("TotalCost = COUNT(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("5");
312
+
313
+ _Parser.fable.AppData = {
314
+ Teams: [
315
+ { Team: 'Mariners', States: 'Washington', Score: 100 },
316
+ { Team: 'Yankees', States: 'New York', Score: 200 },
317
+ { Team: 'Mets', States: 'New York', Score: 50 },
318
+ { Team: 'Giants', States: 'California', Score: 45 },
319
+ { Team: 'Dodgers', States: 'California', Score: 5 },
320
+ { Team: 'Astros', States: 'Texas', Score: 75 }
321
+ ]
322
+ };
323
+
324
+ let tmpHistogramByReference = _Parser.solve(`aggregationhistogram("AppData.Teams", "States", "Score")`);
325
+ Expect(tmpHistogramByReference['New York']).to.equal("250");
326
+
327
+ let tmpHistogram = _Parser.solve(`aggregationhistogrambyobject(getvalue("AppData.Teams"), "States", "Score")`);
328
+ Expect(tmpHistogram['New York']).to.equal("250");
329
+
330
+ Expect(_Parser.solve("DateFromPartsExample = DATEFROMPARTS(2025, 4, 1)")).to.equal("2025-04-01T00:00:00.000Z");
331
+ Expect(_Parser.solve("DateFromPartsExample = DATEADDDAYS(DATEFROMPARTS(2025, 4, 1, 13, 03, 51, 761),87)")).to.equal("2025-06-27T13:03:51.761Z");
332
+
333
+ return fDone();
334
+ }
335
+ );
336
+ test
337
+ (
338
+ 'Exercise Value Marshaling',
339
+ (fDone) =>
340
+ {
341
+ let _Parser = getExpressionParser();
342
+
343
+ Expect(_Parser.solve('1 + 1')).to.equal("2");
344
+ Expect(_Parser.solve("Volume = Width * Height * Depth", { "Width": 73.5, "Height": 28.8, "Depth": 200.5 })).to.equal("424418.4");
345
+ Expect(_Parser.solve("TotalCost = SUM(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("400");
346
+ Expect(_Parser.solve("TotalCost = MEAN(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("80");
347
+ Expect(_Parser.solve("TotalCost = MEDIAN(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("50");
348
+ Expect(_Parser.solve("TotalCost = COUNT(ItemCosts)", { "ItemCosts": [100, 200, 50, 45, 5] })).to.equal("5");
349
+
350
+ return fDone();
351
+ }
352
+ );
353
+ test
354
+ (
355
+ 'Exercise Rounding',
356
+ (fDone) =>
357
+ {
358
+ let _Parser = getExpressionParser();
359
+
360
+ let tmpSolveResults = {};
361
+ let tmpDataObject = { X: 5.867, Y: 3.1, Z: 75, Depth: 3, Width: 2 };
362
+ // 1364.0775
363
+ let tmpResult = _Parser.solve('Area = ROUND(X * Y * Z)', tmpDataObject, tmpSolveResults);
364
+ Expect(tmpResult).to.equal("1364");
365
+ tmpResult = _Parser.solve('Area = ROUND(X * Y * Z, 2)', tmpDataObject, tmpSolveResults);
366
+ Expect(tmpResult).to.equal("1364.08");
367
+ return fDone();
368
+ }
369
+ );
370
+ test
371
+ (
372
+ 'Exercise Multi-parameter Functions',
373
+ (fDone) =>
374
+ {
375
+ let _Parser = getExpressionParser();
376
+
377
+ let tmpSolveResults = {};
378
+ let tmpDataObject = { X: 5.867, Y: 3.5, Z: 75.248923423, Depth: 3, Width: 2 };
379
+ // 1364.0775
380
+ let tmpResult = _Parser.solve('Area = ROUND(X * Y * Z)', tmpDataObject, tmpSolveResults);
381
+ Expect(tmpResult).to.equal("1545");
382
+ tmpResult = _Parser.solve('Area = ROUND(X * Y * Z, 3, 3)', tmpDataObject, tmpSolveResults);
383
+ Expect(tmpResult).to.equal("1545.2");
384
+ // Test the getvaluarray function]
385
+ // TODO: Fix the return values for these expression return functions
386
+ //tmpResult = _Parser.solve('NewSet = GETVALUEARRAY(X, Y, Z)', tmpDataObject, tmpSolveResults);
387
+ //tmpResult = _Parser.solve('NewSetAverage = SUM(NewSet)', tmpDataObject, tmpSolveResults);
388
+ //Expect(tmpResult).to.equal("84.115923423");
389
+ return fDone();
390
+ }
391
+ );
392
+ test
393
+ (
394
+ 'Exercise Complex Assignments',
395
+ (fDone) =>
396
+ {
397
+ let testFable = new libFable();
398
+
399
+ testFable.AppData = { Students: ["Kim", "Jim", "Joan Jett", "Tank Girl"] };
400
+
401
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
402
+
403
+ let tmpResultObject = {};
404
+
405
+ let tmpSolveResults = {};
406
+ let tmpDataObject = { X: 5.867, Y: 3.5, Z: 75.248923423, Depth: 3, Width: 2, Name: "Jerry" };
407
+
408
+ _Parser.solve('Area = ROUND(X * Y * Z, 3, 3)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
409
+
410
+ // Because the results object doesn't have a name in it, the null coalescence assignment operator should just work
411
+ let tmpResult = _Parser.solve('Name ?= GETVALUE("AppData.Students[0]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
412
+ Expect(tmpResultObject.Name).to.equal("Kim");
413
+
414
+ tmpResult = _Parser.solve('Area = ROUND(X * Y * Z, 3, 3)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
415
+ Expect(tmpResult).to.equal("1545.2");
416
+
417
+ // Now that name is set, it shouldn't change.
418
+ tmpResult = _Parser.solve('Name ?= GETVALUE("AppData.Students[1]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
419
+ Expect(tmpResultObject.Name).to.equal("Kim");
420
+
421
+ // Regular assignment should change it.
422
+ tmpResult = _Parser.solve('Name = GETVALUE("AppData.Students[1]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
423
+ Expect(tmpResultObject.Name).to.equal("Jim");
424
+
425
+ // Regular assignment should change it.
426
+ tmpResult = _Parser.solve('Name = GETVALUE("AppData.Students[3]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
427
+ Expect(tmpResultObject.Name).to.equal("Tank Girl");
428
+
429
+ // Regular assignment should change it (with an array out of bounds problem...)
430
+ tmpResult = _Parser.solve('Name = GETVALUE("AppData.Students[4]")', tmpDataObject, tmpSolveResults, false, tmpResultObject);
431
+ Expect(tmpResultObject.Name).to.equal(undefined);
432
+
433
+ return fDone();
434
+ }
435
+ );
436
+ test
437
+ (
438
+ 'Exercise Data Generation',
439
+ (fDone) =>
440
+ {
441
+ let testFable = new libFable();
442
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
443
+
444
+ let tmpDataObject = {};
445
+ let tmpSolveResults = {};
446
+ let tmpResultObject = {};
447
+
448
+ let tmpResult;
449
+ tmpResult = _Parser.solve('RandomIntValue = RANDOMINTEGER()', tmpDataObject, tmpSolveResults, false, tmpResultObject);
450
+ Expect(tmpResult).to.not.be.NaN;
451
+
452
+ tmpResult = _Parser.solve('RandomIntValueBetween = RANDOMINTEGERBETWEEN(10, 13)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
453
+ Expect(parseInt(tmpResult)).to.be.at.least(10);
454
+ Expect(parseInt(tmpResult)).to.be.at.most(13);
455
+
456
+ tmpResult = _Parser.solve('RandomFloatValue = randomFloat()', tmpDataObject, tmpSolveResults, false, tmpResultObject);
457
+ Expect(tmpResult).to.not.be.NaN;
458
+
459
+ tmpResult = _Parser.solve('RandomFloatValueBetween = randomFloatBetween(10.5, 13.78)', tmpDataObject, tmpSolveResults, false, tmpResultObject);
460
+ Expect(parseFloat(tmpResult)).to.be.at.least(10.5);
461
+ Expect(parseFloat(tmpResult)).to.be.at.most(13.78);
462
+
463
+
464
+ return fDone();
465
+ }
466
+ );
467
+ test
468
+ (
469
+ 'Complex Histogram Arithmatic',
470
+ (fDone) =>
471
+ {
472
+ let testFable = new libFable();
503
473
 
474
+ let testCityData = require('./data/cities.json');
475
+ testFable.AppData = { Cities: testCityData };
504
476
 
505
- _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(100, 10, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
506
- Expect(tmpDestinationObject.MadeUpValueArray).to.equal('550.83');
477
+ // let tmpDistribution = testFable.Math.histogramDistributionByExactValue(testFable.AppData.Cities, 'state');
507
478
 
508
- // _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(-5, 5, 0, -10, 10, -25, 25, -50, 50)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
509
- // Expect(tmpDestinationObject.MadeUpValueArray).to.equal('25');
479
+ // Expect(tmpDistribution.Alabama).to.equal(12);
480
+ // Expect(tmpDistribution.Colorado).to.equal(21);
481
+ // Expect(tmpDistribution.Florida).to.equal(73);
482
+ // Expect(tmpDistribution.Georgia).to.equal(18);
510
483
 
484
+ // Now through the solver
511
485
 
512
- Expect(tmpDestinationObject.DistributionResult.Alabama).to.equal(12);
513
- Expect(tmpDestinationObject.DistributionResult.Colorado).to.equal(21);
486
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
514
487
 
515
- Expect(tmpDestinationObject.AggregationResult.Alabama).to.equal('1279813');
516
- Expect(tmpDestinationObject.PopSum).to.equal(testCityData.reduce((sum, city) => testFable.Math.addPrecise(city.population, sum), '0'));
488
+ testFable.AppData.Value1 = "100"; // Comment
489
+ testFable.AppData.Value2 = "-80.5"; // Comment
490
+ testFable.AppData.Value3 = "10000"; // Comment
491
+ testFable.AppData.Value4 = "-333.333"; // Comment
492
+ testFable.AppData.Value5 = "0"; // Comment
517
493
 
518
- return fDone();
519
- }
520
- );
521
- test
522
- (
523
- 'plumbing histogram into aggregation',
524
- () =>
525
- {
526
- let testFable = new libFable();
494
+ let tmpResultsObject = {};
495
+ let tmpDestinationObject = {};
527
496
 
528
- let testCityData = require('./data/cities.json');
529
- testFable.AppData = { Cities: testCityData };
497
+ _Parser.solve('DistributionResult = distributionhistogram("AppData.Cities", "state")', testFable, tmpResultsObject, false, tmpDestinationObject);
498
+ _Parser.solve('AggregationResult = aggregationHistogram("AppData.Cities", "state", "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
499
+ _Parser.solve('PopSum = sum(flatten(AppData.Cities[].population, AppData.Cities[].latitude))', testFable, tmpResultsObject, false, tmpDestinationObject);
530
500
 
531
- // Now through the solver
501
+ //_Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
502
+ //Expect(tmpDestinationObject.MadeUpValueArray).to.equal('600');
503
+ _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(AppData.Value1, AppData.Value2, AppData.Value3, AppData.Value4, AppData.Value5)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
504
+ Expect(tmpDestinationObject.MadeUpValueArray).to.equal('1937.23');
532
505
 
533
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
534
- let tmpResultsObject = {};
535
- testFable.AppData.ResultsObject = tmpResultsObject;
536
- let tmpDestinationObject = {};
537
- testFable.AppData.DestinationObject = tmpDestinationObject;
506
+ _Parser.solve('MadeUpValueArray = ROUND(AVG(cleanvaluearray(createarrayfromabsolutevalues(AppData.Value1, AppData.Value2, AppData.Value3, AppData.Value4, AppData.Value5), 1)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
507
+ Expect(tmpDestinationObject.MadeUpValueArray).to.equal('2421.54');
538
508
 
539
- _Parser.solve('DistributionResult = distributionhistogram("AppData.Cities", "state")', testFable, tmpResultsObject, false, tmpDestinationObject);
540
- _Parser.solve('AggregationResult = aggregationHistogram("AppData.Cities", "state", "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
509
+ // _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(100, -80.5, 10000, -333.333, 0)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
510
+ // Expect(tmpDestinationObject.MadeUpValueArray).to.equal('600');
541
511
 
542
- Expect(tmpDestinationObject.DistributionResult.Alabama).to.equal(12);
543
- Expect(tmpDestinationObject.DistributionResult.Colorado).to.equal(21);
544
512
 
545
- Expect(tmpDestinationObject.AggregationResult.Alabama).to.equal('1279813');
513
+ _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(100, 10, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
514
+ Expect(tmpDestinationObject.MadeUpValueArray).to.equal('550.83');
546
515
 
547
- _Parser.solve('SumByAddress = SUM(FLATTEN(AppData.DestinationObject.AggregationResult))', testFable, tmpResultsObject, false, tmpDestinationObject);
548
- Expect(tmpDestinationObject.SumByAddress).to.equal(testCityData.reduce((sum, city) => testFable.Math.addPrecise(city.population, sum), '0'));
516
+ // _Parser.solve('MadeUpValueArray = ROUND(AVG(createarrayfromabsolutevalues(-5, 5, 0, -10, 10, -25, 25, -50, 50)),2)', testFable, tmpResultsObject, false, tmpDestinationObject);
517
+ // Expect(tmpDestinationObject.MadeUpValueArray).to.equal('25');
549
518
 
550
- _Parser.solve('Smallest = smallestinset(AppData.Cities[], "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
551
- Expect(tmpDestinationObject.Smallest.population).to.equal('36877');
552
519
 
553
- _Parser.solve('Largest = largestinset(AppData.Cities[], "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
554
- Expect(tmpDestinationObject.Largest.city).to.equal('New York');
520
+ Expect(tmpDestinationObject.DistributionResult.Alabama).to.equal(12);
521
+ Expect(tmpDestinationObject.DistributionResult.Colorado).to.equal(21);
555
522
 
556
- _Parser.solve('Tenth = entryinset(AppData.Cities[], "population", "-10")', testFable, tmpResultsObject, false, tmpDestinationObject);
557
- Expect(tmpDestinationObject.Tenth.city).to.equal('San Jose');
523
+ Expect(tmpDestinationObject.AggregationResult.Alabama).to.equal('1279813');
524
+ Expect(tmpDestinationObject.PopSum).to.equal(testCityData.reduce((sum, city) => testFable.Math.addPrecise(city.population, sum), '0'));
558
525
 
559
- _Parser.solve('TenthNum = entryinset(AppData.Cities[], "population", -10)', testFable, tmpResultsObject, false, tmpDestinationObject);
560
- Expect(tmpDestinationObject.TenthNum.city).to.equal('San Jose');
561
-
562
- _Parser.solve('TenthNumHacked = entryinset(AppData.Cities[], "population", (10 * -1))', testFable, tmpResultsObject, false, tmpDestinationObject);
563
- Expect(tmpDestinationObject.TenthNumHacked.city).to.equal('San Jose');
564
- }
565
- );
566
- }
567
- );
568
- suite
569
- (
570
- 'Data Expressions',
571
- function()
572
- {
573
- test
574
- (
575
- 'Concatenates Strings',
576
- () =>
577
- {
578
- let testFable = new libFable();
526
+ return fDone();
527
+ }
528
+ );
529
+ test
530
+ (
531
+ 'plumbing histogram into aggregation',
532
+ () =>
533
+ {
534
+ let testFable = new libFable();
579
535
 
580
- let testCityData = require('./data/cities.json');
581
- testFable.AppData =
582
- {
583
- Cities: testCityData.slice(0, 4),
584
- CityNames: testCityData.slice(0, 4).map((c) => c.city),
585
- Null: null,
586
- };
587
- testFable.AppData.CityNames[2] = { };
588
- testFable.AppData.Cities[2].city = { cat: '12345', dog: 'waffle' };
536
+ let testCityData = require('./data/cities.json');
537
+ testFable.AppData = { Cities: testCityData };
589
538
 
590
- // Now through the solver
539
+ // Now through the solver
591
540
 
592
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
593
- let tmpResultsObject = {};
594
- let tmpDestinationObject = {};
541
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
542
+ let tmpResultsObject = {};
543
+ testFable.AppData.ResultsObject = tmpResultsObject;
544
+ let tmpDestinationObject = {};
545
+ testFable.AppData.DestinationObject = tmpDestinationObject;
595
546
 
596
- _Parser.solve('Names = concat(AppData.Cities[].city)', testFable, tmpResultsObject, false, tmpDestinationObject);
597
- Expect(tmpDestinationObject.Names).to.equal('New YorkLos AngelesHouston');
547
+ _Parser.solve('DistributionResult = distributionhistogram("AppData.Cities", "state")', testFable, tmpResultsObject, false, tmpDestinationObject);
548
+ _Parser.solve('AggregationResult = aggregationHistogram("AppData.Cities", "state", "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
598
549
 
599
- _Parser.solve('Names2 = concat(AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
600
- Expect(tmpDestinationObject.Names2).to.equal('New YorkLos AngelesHouston');
550
+ Expect(tmpDestinationObject.DistributionResult.Alabama).to.equal(12);
551
+ Expect(tmpDestinationObject.DistributionResult.Colorado).to.equal(21);
601
552
 
602
- _Parser.solve('RawNames = concatRaw(AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
603
- Expect(tmpDestinationObject.RawNames).to.equal('New YorkLos Angeles[object Object]Houston');
553
+ Expect(tmpDestinationObject.AggregationResult.Alabama).to.equal('1279813');
604
554
 
605
- _Parser.solve('JoinedNames = join(", ", AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
606
- Expect(tmpDestinationObject.JoinedNames).to.equal('New York, Los Angeles, Houston');
555
+ _Parser.solve('SumByAddress = SUM(FLATTEN(AppData.DestinationObject.AggregationResult))', testFable, tmpResultsObject, false, tmpDestinationObject);
556
+ Expect(tmpDestinationObject.SumByAddress).to.equal(testCityData.reduce((sum, city) => testFable.Math.addPrecise(city.population, sum), '0'));
607
557
 
608
- _Parser.solve('RawJoinedNames = joinRaw(" ", AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
609
- Expect(tmpDestinationObject.RawJoinedNames).to.equal('New York Los Angeles [object Object] Houston');
558
+ _Parser.solve('Smallest = smallestinset(AppData.Cities[], "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
559
+ Expect(tmpDestinationObject.Smallest.population).to.equal('36877');
610
560
 
611
- _Parser.solve('NamesArgs = concat("cat", "dog", "waffle")', testFable, tmpResultsObject, false, tmpDestinationObject);
612
- Expect(tmpDestinationObject.NamesArgs).to.equal('catdogwaffle');
561
+ _Parser.solve('Largest = largestinset(AppData.Cities[], "population")', testFable, tmpResultsObject, false, tmpDestinationObject);
562
+ Expect(tmpDestinationObject.Largest.city).to.equal('New York');
613
563
 
614
- testFable.AppData.CityName = 'New York';
615
- _Parser.solve('RawNamesArgs = concatRaw(AppData.CityName, "arg name")', testFable, tmpResultsObject, false, tmpDestinationObject);
616
- Expect(tmpDestinationObject.RawNamesArgs).to.equal('New Yorkarg name');
564
+ _Parser.solve('Tenth = entryinset(AppData.Cities[], "population", "-10")', testFable, tmpResultsObject, false, tmpDestinationObject);
565
+ Expect(tmpDestinationObject.Tenth.city).to.equal('San Jose');
617
566
 
618
- _Parser.solve('JoinedNamesArgs = join(", ", AppData.CityNames[1], AppData.CityNames[2])', testFable, tmpResultsObject, false, tmpDestinationObject);
619
- Expect(tmpDestinationObject.JoinedNamesArgs).to.equal('Los Angeles');
567
+ _Parser.solve('TenthNum = entryinset(AppData.Cities[], "population", -10)', testFable, tmpResultsObject, false, tmpDestinationObject);
568
+ Expect(tmpDestinationObject.TenthNum.city).to.equal('San Jose');
620
569
 
621
- _Parser.solve('RawJoinedNamesArgs = joinRaw(" ", AppData.Cities[].city, "My Extra City")', testFable, tmpResultsObject, false, tmpDestinationObject);
622
- Expect(tmpDestinationObject.RawJoinedNamesArgs).to.equal('New York Los Angeles [object Object] Houston My Extra City');
570
+ _Parser.solve('TenthNumHacked = entryinset(AppData.Cities[], "population", (10 * -1))', testFable, tmpResultsObject, false, tmpDestinationObject);
571
+ Expect(tmpDestinationObject.TenthNumHacked.city).to.equal('San Jose');
572
+ }
573
+ );
623
574
  }
624
575
  );
625
- }
626
- );
627
- suite
628
- (
629
- 'Conditional Expressions',
630
- function()
631
- {
632
- test
576
+ suite
633
577
  (
634
- 'Conditional When',
635
- () =>
578
+ 'Data Expressions',
579
+ function ()
636
580
  {
637
- let testFable = new libFable();
638
-
639
- let testCityData = require('./data/cities.json');
640
- testFable.AppData =
641
- {
642
- Cities: testCityData.slice(0, 4),
643
- Null: null,
644
- };
645
-
646
- // Now through the solver
647
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
648
- let tmpResultsObject = {};
649
- let tmpDestinationObject = {};
650
-
651
- _Parser.solve('Name = When(AppData.Cities[0].city, AppData.Cities[0].city)', testFable, tmpResultsObject, false, tmpDestinationObject);
652
- Expect(tmpDestinationObject.Name).to.equal('New York');
653
-
654
- _Parser.solve('Overrun = When(AppData.Cities[10000000].city, AppData.Cities[10000000].city)', testFable, tmpResultsObject, false, tmpDestinationObject);
655
- Expect(tmpDestinationObject.Overrun).to.equal('');
656
-
657
- testFable.AppData.ECDMonth = 'January';
658
- testFable.AppData.ECDYear = '2023';
659
- _Parser.solve('EstimatedCompletionDate = ResolveHtmlEntities(When(AppData.ECDMonth, Join(", ", AppData.ECDMonth, AppData.ECDYear)))',
660
- testFable, tmpResultsObject, false, tmpDestinationObject);
661
- Expect(tmpDestinationObject.EstimatedCompletionDate).to.equal('January, 2023');
581
+ test
582
+ (
583
+ 'Concatenates Strings',
584
+ () =>
585
+ {
586
+ let testFable = new libFable();
587
+
588
+ let testCityData = require('./data/cities.json');
589
+ testFable.AppData =
590
+ {
591
+ Cities: testCityData.slice(0, 4),
592
+ CityNames: testCityData.slice(0, 4).map((c) => c.city),
593
+ Null: null,
594
+ };
595
+ testFable.AppData.CityNames[2] = {};
596
+ testFable.AppData.Cities[2].city = { cat: '12345', dog: 'waffle' };
597
+
598
+ // Now through the solver
599
+
600
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
601
+ let tmpResultsObject = {};
602
+ let tmpDestinationObject = {};
603
+
604
+ _Parser.solve('Names = concat(AppData.Cities[].city)', testFable, tmpResultsObject, false, tmpDestinationObject);
605
+ Expect(tmpDestinationObject.Names).to.equal('New YorkLos AngelesHouston');
606
+
607
+ _Parser.solve('Names2 = concat(AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
608
+ Expect(tmpDestinationObject.Names2).to.equal('New YorkLos AngelesHouston');
609
+
610
+ _Parser.solve('RawNames = concatRaw(AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
611
+ Expect(tmpDestinationObject.RawNames).to.equal('New YorkLos Angeles[object Object]Houston');
612
+
613
+ _Parser.solve('JoinedNames = join(", ", AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
614
+ Expect(tmpDestinationObject.JoinedNames).to.equal('New York, Los Angeles, Houston');
615
+
616
+ _Parser.solve('RawJoinedNames = joinRaw(" ", AppData.CityNames)', testFable, tmpResultsObject, false, tmpDestinationObject);
617
+ Expect(tmpDestinationObject.RawJoinedNames).to.equal('New York Los Angeles [object Object] Houston');
618
+
619
+ _Parser.solve('NamesArgs = concat("cat", "dog", "waffle")', testFable, tmpResultsObject, false, tmpDestinationObject);
620
+ Expect(tmpDestinationObject.NamesArgs).to.equal('catdogwaffle');
621
+
622
+ testFable.AppData.CityName = 'New York';
623
+ _Parser.solve('RawNamesArgs = concatRaw(AppData.CityName, "arg name")', testFable, tmpResultsObject, false, tmpDestinationObject);
624
+ Expect(tmpDestinationObject.RawNamesArgs).to.equal('New Yorkarg name');
625
+
626
+ _Parser.solve('JoinedNamesArgs = join(", ", AppData.CityNames[1], AppData.CityNames[2])', testFable, tmpResultsObject, false, tmpDestinationObject);
627
+ Expect(tmpDestinationObject.JoinedNamesArgs).to.equal('Los Angeles');
628
+
629
+ _Parser.solve('RawJoinedNamesArgs = joinRaw(" ", AppData.Cities[].city, "My Extra City")', testFable, tmpResultsObject, false, tmpDestinationObject);
630
+ Expect(tmpDestinationObject.RawJoinedNamesArgs).to.equal('New York Los Angeles [object Object] Houston My Extra City');
631
+ }
632
+ );
662
633
  }
663
634
  );
664
-
665
- test
635
+ suite
666
636
  (
667
- 'Conditional If',
668
- () =>
637
+ 'Conditional Expressions',
638
+ function ()
669
639
  {
670
- let testFable = new libFable();
671
-
672
- let testCityData = require('./data/cities.json');
673
- testFable.AppData =
674
- {
675
- Cities: testCityData.slice(0, 4),
676
- Null: null,
677
- };
678
-
679
- // Now through the solver
680
- let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
681
- let tmpResultsObject = {};
682
- let tmpDestinationObject = {};
683
-
684
- _Parser.solve('GTE = If(AppData.Cities[0].latitude, "<", "50", "west", "east")', testFable, tmpResultsObject, false, tmpDestinationObject);
685
- Expect(tmpDestinationObject.GTE).to.equal('west');
686
-
687
- _Parser.solve('Equals = If(AppData.Cities[0].city, "==", "New York", "yes", "no")', testFable, tmpResultsObject, false, tmpDestinationObject);
688
- Expect(tmpDestinationObject.Equals).to.equal('yes');
689
-
690
- _Parser.solve('EpsilonEquals = If("1.0000000001", "==", "1", "yes", "no")', testFable, tmpResultsObject, false, tmpDestinationObject);
691
- Expect(tmpDestinationObject.EpsilonEquals).to.equal('yes');
692
-
693
- _Parser.solve('PreciseEquals = If("1.0000000001", "===", "1", "yes", "no")', testFable, tmpResultsObject, false, tmpDestinationObject);
694
- Expect(tmpDestinationObject.PreciseEquals).to.equal('no');
640
+ test
641
+ (
642
+ 'Conditional When',
643
+ () =>
644
+ {
645
+ let testFable = new libFable();
646
+
647
+ let testCityData = require('./data/cities.json');
648
+ testFable.AppData =
649
+ {
650
+ Cities: testCityData.slice(0, 4),
651
+ Null: null,
652
+ };
653
+
654
+ // Now through the solver
655
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
656
+ let tmpResultsObject = {};
657
+ let tmpDestinationObject = {};
658
+
659
+ _Parser.solve('Name = When(AppData.Cities[0].city, AppData.Cities[0].city)', testFable, tmpResultsObject, false, tmpDestinationObject);
660
+ Expect(tmpDestinationObject.Name).to.equal('New York');
661
+
662
+ _Parser.solve('Overrun = When(AppData.Cities[10000000].city, AppData.Cities[10000000].city)', testFable, tmpResultsObject, false, tmpDestinationObject);
663
+ Expect(tmpDestinationObject.Overrun).to.equal('');
664
+
665
+ testFable.AppData.ECDMonth = 'January';
666
+ testFable.AppData.ECDYear = '2023';
667
+ _Parser.solve('EstimatedCompletionDate = ResolveHtmlEntities(When(AppData.ECDMonth, Join("&comma; ", AppData.ECDMonth, AppData.ECDYear)))',
668
+ testFable, tmpResultsObject, false, tmpDestinationObject);
669
+ Expect(tmpDestinationObject.EstimatedCompletionDate).to.equal('January, 2023');
670
+ }
671
+ );
672
+
673
+ test
674
+ (
675
+ 'Conditional If',
676
+ () =>
677
+ {
678
+ let testFable = new libFable();
679
+
680
+ let testCityData = require('./data/cities.json');
681
+ testFable.AppData =
682
+ {
683
+ Cities: testCityData.slice(0, 4),
684
+ Null: null,
685
+ };
686
+
687
+ // Now through the solver
688
+ let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
689
+ let tmpResultsObject = {};
690
+ let tmpDestinationObject = {};
691
+
692
+ _Parser.solve('GTE = If(AppData.Cities[0].latitude, "<", "50", "west", "east")', testFable, tmpResultsObject, false, tmpDestinationObject);
693
+ Expect(tmpDestinationObject.GTE).to.equal('west');
694
+
695
+ _Parser.solve('Equals = If(AppData.Cities[0].city, "==", "New York", "yes", "no")', testFable, tmpResultsObject, false, tmpDestinationObject);
696
+ Expect(tmpDestinationObject.Equals).to.equal('yes');
697
+
698
+ _Parser.solve('EpsilonEquals = If("1.0000000001", "==", "1", "yes", "no")', testFable, tmpResultsObject, false, tmpDestinationObject);
699
+ Expect(tmpDestinationObject.EpsilonEquals).to.equal('yes');
700
+
701
+ _Parser.solve('PreciseEquals = If("1.0000000001", "===", "1", "yes", "no")', testFable, tmpResultsObject, false, tmpDestinationObject);
702
+ Expect(tmpDestinationObject.PreciseEquals).to.equal('no');
703
+ }
704
+ );
695
705
  }
696
706
  );
697
- }
698
- );
699
- }
700
- );
707
+ }
708
+ );