fable 3.1.64 → 3.1.65

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.
Files changed (22) hide show
  1. package/docs/services/expression-parser-functions/README.md +9 -0
  2. package/docs/services/expression-parser-functions/beziercurvefit.md +57 -0
  3. package/docs/services/expression-parser-functions/bezierpoint.md +55 -0
  4. package/docs/services/expression-parser-functions/intercept.md +59 -0
  5. package/docs/services/expression-parser-functions/setvalue.md +55 -0
  6. package/docs/services/expression-parser-functions/slope.md +51 -0
  7. package/docs/services/expression-parser-functions/ternary.md +180 -0
  8. package/docs/services/expression-parser.md +72 -0
  9. package/example_applications/mathematical_playground/Math-Solver-Harness.js +108 -0
  10. package/example_applications/mathematical_playground/TestSuite-AcidTest.json +178 -0
  11. package/example_applications/mathematical_playground/TestSuite-Identities.json +147 -0
  12. package/example_applications/mathematical_playground/TestSuite-Precision.json +127 -0
  13. package/example_applications/mathematical_playground/TestSuite-Spreadsheet.json +198 -0
  14. package/package.json +1 -1
  15. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer-DirectiveMutation.js +143 -0
  16. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js +1 -1
  17. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +5 -4
  18. package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json +65 -1
  19. package/source/services/Fable-Service-ExpressionParser.js +1 -0
  20. package/source/services/Fable-Service-Logic.js +33 -0
  21. package/source/services/Fable-Service-Math.js +78 -0
  22. package/test/ExpressionParser_tests.js +290 -0
@@ -339,10 +339,153 @@ class ExpressionTokenizerDirectiveMutation extends libExpressionParserOperationB
339
339
  return tmpNewDirectiveDescription;
340
340
  }
341
341
 
342
+ rewriteTernaryOperators(pResultObject)
343
+ {
344
+ let tmpTokens = pResultObject.RawTokens;
345
+
346
+ // Scan right-to-left so nested ternaries (innermost first) are handled correctly.
347
+ for (let i = tmpTokens.length - 1; i >= 0; i--)
348
+ {
349
+ if (tmpTokens[i] !== '?')
350
+ {
351
+ continue;
352
+ }
353
+
354
+ // Found a ? token at index i.
355
+ // Walk left to find the start of the condition expression.
356
+ // The condition starts after the previous comma, open parenthesis, assignment operator, or beginning of tokens.
357
+ let tmpConditionStart = 0;
358
+ let tmpParenDepth = 0;
359
+ for (let j = i - 1; j >= 0; j--)
360
+ {
361
+ if (tmpTokens[j] === ')')
362
+ {
363
+ tmpParenDepth++;
364
+ }
365
+ else if (tmpTokens[j] === '(')
366
+ {
367
+ if (tmpParenDepth > 0)
368
+ {
369
+ tmpParenDepth--;
370
+ }
371
+ else
372
+ {
373
+ // We hit an unmatched open paren — condition starts after it
374
+ tmpConditionStart = j + 1;
375
+ break;
376
+ }
377
+ }
378
+ else if (tmpParenDepth === 0)
379
+ {
380
+ let tmpTokenDescriptor = this.ExpressionParser.tokenMap[tmpTokens[j]];
381
+ if (tmpTokenDescriptor && (tmpTokenDescriptor.Type === 'Assignment' || tmpTokens[j] === ','))
382
+ {
383
+ tmpConditionStart = j + 1;
384
+ break;
385
+ }
386
+ }
387
+ }
388
+
389
+ // Walk right from ? to find the matching :: at the same parenthesis depth.
390
+ let tmpSeparatorIndex = -1;
391
+ tmpParenDepth = 0;
392
+ for (let j = i + 1; j < tmpTokens.length; j++)
393
+ {
394
+ if (tmpTokens[j] === '(')
395
+ {
396
+ tmpParenDepth++;
397
+ }
398
+ else if (tmpTokens[j] === ')')
399
+ {
400
+ if (tmpParenDepth > 0)
401
+ {
402
+ tmpParenDepth--;
403
+ }
404
+ else
405
+ {
406
+ // Hit closing paren at our depth — no :: found in this group
407
+ break;
408
+ }
409
+ }
410
+ else if (tmpTokens[j] === '::' && tmpParenDepth === 0)
411
+ {
412
+ tmpSeparatorIndex = j;
413
+ break;
414
+ }
415
+ }
416
+
417
+ if (tmpSeparatorIndex === -1)
418
+ {
419
+ pResultObject.ExpressionParserLog.push(`ExpressionParser.rewriteTernaryOperators found a ? at token index ${i} with no matching :: separator.`);
420
+ this.log.warn(pResultObject.ExpressionParserLog[pResultObject.ExpressionParserLog.length - 1]);
421
+ continue;
422
+ }
423
+
424
+ // Walk right from :: to find the end of the false branch.
425
+ // The false branch ends at the next comma, close parenthesis, or end of tokens at the same depth.
426
+ let tmpFalseBranchEnd = tmpTokens.length;
427
+ tmpParenDepth = 0;
428
+ for (let j = tmpSeparatorIndex + 1; j < tmpTokens.length; j++)
429
+ {
430
+ if (tmpTokens[j] === '(')
431
+ {
432
+ tmpParenDepth++;
433
+ }
434
+ else if (tmpTokens[j] === ')')
435
+ {
436
+ if (tmpParenDepth > 0)
437
+ {
438
+ tmpParenDepth--;
439
+ }
440
+ else
441
+ {
442
+ // Unmatched close paren — false branch ends here
443
+ tmpFalseBranchEnd = j;
444
+ break;
445
+ }
446
+ }
447
+ else if (tmpParenDepth === 0)
448
+ {
449
+ if (tmpTokens[j] === ',')
450
+ {
451
+ tmpFalseBranchEnd = j;
452
+ break;
453
+ }
454
+ }
455
+ }
456
+
457
+ // Extract the three segments
458
+ let tmpConditionTokens = tmpTokens.slice(tmpConditionStart, i);
459
+ let tmpTrueBranchTokens = tmpTokens.slice(i + 1, tmpSeparatorIndex);
460
+ let tmpFalseBranchTokens = tmpTokens.slice(tmpSeparatorIndex + 1, tmpFalseBranchEnd);
461
+
462
+ // Build the replacement: ternary((condition), trueBranch, falseBranch)
463
+ // The condition is wrapped in its own parentheses to ensure correct
464
+ // precedence grouping when arithmetic appears on both sides of a comparison.
465
+ let tmpReplacementTokens = ['ternary', '(', '('];
466
+ tmpReplacementTokens = tmpReplacementTokens.concat(tmpConditionTokens);
467
+ tmpReplacementTokens.push(')');
468
+ tmpReplacementTokens.push(',');
469
+ tmpReplacementTokens = tmpReplacementTokens.concat(tmpTrueBranchTokens);
470
+ tmpReplacementTokens.push(',');
471
+ tmpReplacementTokens = tmpReplacementTokens.concat(tmpFalseBranchTokens);
472
+ tmpReplacementTokens.push(')');
473
+
474
+ // Splice the replacement into the token array
475
+ tmpTokens.splice(tmpConditionStart, tmpFalseBranchEnd - tmpConditionStart, ...tmpReplacementTokens);
476
+
477
+ // Adjust i to re-scan from the start of what we just inserted (in case of nested ternaries in the condition)
478
+ i = tmpConditionStart;
479
+ }
480
+ }
481
+
342
482
  parseDirectives(pResultObject)
343
483
  {
344
484
  let tmpResults = (typeof(pResultObject) === 'object') ? pResultObject : { ExpressionParserLog: [] };
345
485
 
486
+ // Rewrite ternary operators before directive parsing
487
+ this.rewriteTernaryOperators(tmpResults);
488
+
346
489
  tmpResults.SolverDirectives = this.defaultDirective;
347
490
  tmpResults.SolverDirectiveTokens = [];
348
491
 
@@ -215,7 +215,7 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
215
215
  {
216
216
  if (tmpCurrentToken.length > 0)
217
217
  {
218
- tmpResults.RawTokens.push(tmpTokenKey);
218
+ tmpResults.RawTokens.push(tmpCurrentToken);
219
219
  }
220
220
  tmpCurrentToken = '';
221
221
  tmpCurrentTokenType = false;
@@ -285,6 +285,11 @@
285
285
  "Address": "fable.Logic.when"
286
286
  },
287
287
 
288
+ "ternary": {
289
+ "Name": "numeric-aware ternary selection (used by ? :: operator desugaring)",
290
+ "Address": "fable.Logic.ternary"
291
+ },
292
+
288
293
  "entryinset": {
289
294
  "Name": "Entry in Set",
290
295
  "Address": "fable.Math.entryInSet"
@@ -465,10 +470,6 @@
465
470
  "Address": "fable.Math.interceptPrecise"
466
471
  },
467
472
 
468
- "polynomialregression": {
469
- "Name": "Perform an nth degree Polynomial Regression on a Set of X and Y Values",
470
- "Address": "fable.Math.polynomialRegression"
471
- },
472
473
  "leastsquares": {
473
474
  "Name": "Perform a Least Squares Regression on a Set of Independent Variable Vectors and a Dependent Variable Vector",
474
475
  "Address": "fable.Math.leastSquares"
@@ -46,7 +46,7 @@
46
46
  "Name": "Set Concatenate",
47
47
  "Token": ",",
48
48
  "Function": "fable.Math.setConcatenate",
49
- "Precedence": 5,
49
+ "Precedence": 6,
50
50
  "Type": "Operator"
51
51
  },
52
52
 
@@ -99,5 +99,69 @@
99
99
  "Function": "fable.Math.subtractPrecise",
100
100
  "Precedence": 4,
101
101
  "Type": "Operator"
102
+ },
103
+
104
+ ">":
105
+ {
106
+ "Name": "Greater Than",
107
+ "Token": ">",
108
+ "Function": "fable.Math.greaterThanOperator",
109
+ "Precedence": 5,
110
+ "Type": "Operator"
111
+ },
112
+ ">=":
113
+ {
114
+ "Name": "Greater Than or Equal",
115
+ "Token": ">=",
116
+ "Function": "fable.Math.greaterThanOrEqualOperator",
117
+ "Precedence": 5,
118
+ "Type": "Operator"
119
+ },
120
+ "<":
121
+ {
122
+ "Name": "Less Than",
123
+ "Token": "<",
124
+ "Function": "fable.Math.lessThanOperator",
125
+ "Precedence": 5,
126
+ "Type": "Operator"
127
+ },
128
+ "<=":
129
+ {
130
+ "Name": "Less Than or Equal",
131
+ "Token": "<=",
132
+ "Function": "fable.Math.lessThanOrEqualOperator",
133
+ "Precedence": 5,
134
+ "Type": "Operator"
135
+ },
136
+ "==":
137
+ {
138
+ "Name": "Equal",
139
+ "Token": "==",
140
+ "Function": "fable.Math.equalOperator",
141
+ "Precedence": 5,
142
+ "Type": "Operator"
143
+ },
144
+ "!=":
145
+ {
146
+ "Name": "Not Equal",
147
+ "Token": "!=",
148
+ "Function": "fable.Math.notEqualOperator",
149
+ "Precedence": 5,
150
+ "Type": "Operator"
151
+ },
152
+
153
+ "?":
154
+ {
155
+ "Name": "Ternary Condition",
156
+ "Token": "?",
157
+ "Precedence": 0,
158
+ "Type": "Ternary"
159
+ },
160
+ "::":
161
+ {
162
+ "Name": "Ternary Separator",
163
+ "Token": "::",
164
+ "Precedence": 0,
165
+ "Type": "Ternary"
102
166
  }
103
167
  }
@@ -112,6 +112,7 @@ class FableServiceExpressionParser extends libFableServiceBase
112
112
 
113
113
  // Wire each sub service into this instance of the solver.
114
114
  this.Tokenizer.connectExpressionParser(this);
115
+ this.Tokenizer.TokenizerDirectiveMutation.connectExpressionParser(this);
115
116
  this.Linter.connectExpressionParser(this);
116
117
  this.Postfix.connectExpressionParser(this);
117
118
  this.ValueMarshal.connectExpressionParser(this);
@@ -126,6 +126,39 @@ class FableServiceLogic extends libFableServiceBase
126
126
  }
127
127
  return pOnTrue;
128
128
  }
129
+
130
+ /**
131
+ * Numeric-aware ternary selection for the expression parser.
132
+ * Treats 0, "0", "", null, undefined, false, NaN, empty arrays, and empty objects as falsy.
133
+ * Used by the ternary operator desugaring (condition ? trueVal :: falseVal).
134
+ *
135
+ * @param {any} pCondition - The condition to evaluate
136
+ * @param {any} pOnTrue - The value to return if the condition is truthy
137
+ * @param {any} [pOnFalse = ''] - The value to return if the condition is falsy
138
+ * @return {any} - The selected value
139
+ */
140
+ ternary(pCondition, pOnTrue, pOnFalse = '')
141
+ {
142
+ // Standard JS falsy check
143
+ if (!pCondition)
144
+ {
145
+ return pOnFalse;
146
+ }
147
+ // Numeric zero as a string (from comparison operators returning "0")
148
+ if (pCondition === '0')
149
+ {
150
+ return pOnFalse;
151
+ }
152
+ if (Array.isArray(pCondition) && pCondition.length < 1)
153
+ {
154
+ return pOnFalse;
155
+ }
156
+ if (typeof pCondition === 'object' && Object.keys(pCondition).length < 1)
157
+ {
158
+ return pOnFalse;
159
+ }
160
+ return pOnTrue;
161
+ }
129
162
  }
130
163
 
131
164
  module.exports = FableServiceLogic;
@@ -460,6 +460,84 @@ class FableServiceMath extends libFableServiceBase
460
460
  return tmpLeftArbitraryValue.lte(tmpRightValue);
461
461
  }
462
462
 
463
+ /**
464
+ * Comparison operator: returns "1" if left > right, "0" otherwise.
465
+ * For use as an infix operator in the expression parser.
466
+ *
467
+ * @param {number|string} pLeftValue - The left value to compare.
468
+ * @param {number|string} pRightValue - The right value to compare.
469
+ * @returns {string} - "1" if left > right, "0" otherwise.
470
+ */
471
+ greaterThanOperator(pLeftValue, pRightValue)
472
+ {
473
+ return this.gtPrecise(pLeftValue, pRightValue) ? '1' : '0';
474
+ }
475
+
476
+ /**
477
+ * Comparison operator: returns "1" if left >= right, "0" otherwise.
478
+ * For use as an infix operator in the expression parser.
479
+ *
480
+ * @param {number|string} pLeftValue - The left value to compare.
481
+ * @param {number|string} pRightValue - The right value to compare.
482
+ * @returns {string} - "1" if left >= right, "0" otherwise.
483
+ */
484
+ greaterThanOrEqualOperator(pLeftValue, pRightValue)
485
+ {
486
+ return this.gtePrecise(pLeftValue, pRightValue) ? '1' : '0';
487
+ }
488
+
489
+ /**
490
+ * Comparison operator: returns "1" if left < right, "0" otherwise.
491
+ * For use as an infix operator in the expression parser.
492
+ *
493
+ * @param {number|string} pLeftValue - The left value to compare.
494
+ * @param {number|string} pRightValue - The right value to compare.
495
+ * @returns {string} - "1" if left < right, "0" otherwise.
496
+ */
497
+ lessThanOperator(pLeftValue, pRightValue)
498
+ {
499
+ return this.ltPrecise(pLeftValue, pRightValue) ? '1' : '0';
500
+ }
501
+
502
+ /**
503
+ * Comparison operator: returns "1" if left <= right, "0" otherwise.
504
+ * For use as an infix operator in the expression parser.
505
+ *
506
+ * @param {number|string} pLeftValue - The left value to compare.
507
+ * @param {number|string} pRightValue - The right value to compare.
508
+ * @returns {string} - "1" if left <= right, "0" otherwise.
509
+ */
510
+ lessThanOrEqualOperator(pLeftValue, pRightValue)
511
+ {
512
+ return this.ltePrecise(pLeftValue, pRightValue) ? '1' : '0';
513
+ }
514
+
515
+ /**
516
+ * Comparison operator: returns "1" if left == right, "0" otherwise.
517
+ * For use as an infix operator in the expression parser.
518
+ *
519
+ * @param {number|string} pLeftValue - The left value to compare.
520
+ * @param {number|string} pRightValue - The right value to compare.
521
+ * @returns {string} - "1" if left == right, "0" otherwise.
522
+ */
523
+ equalOperator(pLeftValue, pRightValue)
524
+ {
525
+ return this.comparePrecise(pLeftValue, pRightValue) == 0 ? '1' : '0';
526
+ }
527
+
528
+ /**
529
+ * Comparison operator: returns "1" if left != right, "0" otherwise.
530
+ * For use as an infix operator in the expression parser.
531
+ *
532
+ * @param {number|string} pLeftValue - The left value to compare.
533
+ * @param {number|string} pRightValue - The right value to compare.
534
+ * @returns {string} - "1" if left != right, "0" otherwise.
535
+ */
536
+ notEqualOperator(pLeftValue, pRightValue)
537
+ {
538
+ return this.comparePrecise(pLeftValue, pRightValue) != 0 ? '1' : '0';
539
+ }
540
+
463
541
  /**
464
542
  * Converts degrees to radians with arbitrary precision.
465
543
  *
@@ -2155,3 +2155,293 @@ suite
2155
2155
  );
2156
2156
  }
2157
2157
  );
2158
+
2159
+ suite
2160
+ (
2161
+ 'Comparison Operators',
2162
+ function ()
2163
+ {
2164
+ suite
2165
+ (
2166
+ 'Greater Than',
2167
+ function ()
2168
+ {
2169
+ test
2170
+ (
2171
+ 'Basic greater than comparisons.',
2172
+ (fDone) =>
2173
+ {
2174
+ let _Parser = getExpressionParser();
2175
+ Expect(_Parser.solve('Result = 5 > 3')).to.equal('1');
2176
+ Expect(_Parser.solve('Result = 3 > 5')).to.equal('0');
2177
+ Expect(_Parser.solve('Result = 5 > 5')).to.equal('0');
2178
+ return fDone();
2179
+ }
2180
+ );
2181
+ }
2182
+ );
2183
+ suite
2184
+ (
2185
+ 'Greater Than or Equal',
2186
+ function ()
2187
+ {
2188
+ test
2189
+ (
2190
+ 'Basic greater than or equal comparisons.',
2191
+ (fDone) =>
2192
+ {
2193
+ let _Parser = getExpressionParser();
2194
+ Expect(_Parser.solve('Result = 5 >= 3')).to.equal('1');
2195
+ Expect(_Parser.solve('Result = 3 >= 5')).to.equal('0');
2196
+ Expect(_Parser.solve('Result = 5 >= 5')).to.equal('1');
2197
+ return fDone();
2198
+ }
2199
+ );
2200
+ }
2201
+ );
2202
+ suite
2203
+ (
2204
+ 'Less Than',
2205
+ function ()
2206
+ {
2207
+ test
2208
+ (
2209
+ 'Basic less than comparisons.',
2210
+ (fDone) =>
2211
+ {
2212
+ let _Parser = getExpressionParser();
2213
+ Expect(_Parser.solve('Result = 3 < 5')).to.equal('1');
2214
+ Expect(_Parser.solve('Result = 5 < 3')).to.equal('0');
2215
+ Expect(_Parser.solve('Result = 5 < 5')).to.equal('0');
2216
+ return fDone();
2217
+ }
2218
+ );
2219
+ }
2220
+ );
2221
+ suite
2222
+ (
2223
+ 'Less Than or Equal',
2224
+ function ()
2225
+ {
2226
+ test
2227
+ (
2228
+ 'Basic less than or equal comparisons.',
2229
+ (fDone) =>
2230
+ {
2231
+ let _Parser = getExpressionParser();
2232
+ Expect(_Parser.solve('Result = 3 <= 5')).to.equal('1');
2233
+ Expect(_Parser.solve('Result = 5 <= 3')).to.equal('0');
2234
+ Expect(_Parser.solve('Result = 5 <= 5')).to.equal('1');
2235
+ return fDone();
2236
+ }
2237
+ );
2238
+ }
2239
+ );
2240
+ suite
2241
+ (
2242
+ 'Equal',
2243
+ function ()
2244
+ {
2245
+ test
2246
+ (
2247
+ 'Basic equality comparisons.',
2248
+ (fDone) =>
2249
+ {
2250
+ let _Parser = getExpressionParser();
2251
+ Expect(_Parser.solve('Result = 5 == 5')).to.equal('1');
2252
+ Expect(_Parser.solve('Result = 5 == 3')).to.equal('0');
2253
+ return fDone();
2254
+ }
2255
+ );
2256
+ }
2257
+ );
2258
+ suite
2259
+ (
2260
+ 'Not Equal',
2261
+ function ()
2262
+ {
2263
+ test
2264
+ (
2265
+ 'Basic inequality comparisons.',
2266
+ (fDone) =>
2267
+ {
2268
+ let _Parser = getExpressionParser();
2269
+ Expect(_Parser.solve('Result = 5 != 3')).to.equal('1');
2270
+ Expect(_Parser.solve('Result = 5 != 5')).to.equal('0');
2271
+ return fDone();
2272
+ }
2273
+ );
2274
+ }
2275
+ );
2276
+ suite
2277
+ (
2278
+ 'Comparison with Arithmetic',
2279
+ function ()
2280
+ {
2281
+ test
2282
+ (
2283
+ 'Arithmetic is evaluated before comparison.',
2284
+ (fDone) =>
2285
+ {
2286
+ let _Parser = getExpressionParser();
2287
+ // 2 + 3 = 5, 1 + 2 = 3, so 5 > 3 = 1
2288
+ Expect(_Parser.solve('Result = 2 + 3 > 1 + 2')).to.equal('1');
2289
+ // 1 + 1 = 2, 3 + 1 = 4, so 2 > 4 = 0
2290
+ Expect(_Parser.solve('Result = 1 + 1 > 3 + 1')).to.equal('0');
2291
+ // 10 - 5 = 5, 2 * 3 = 6, so 5 < 6 = 1
2292
+ Expect(_Parser.solve('Result = 10 - 5 < 2 * 3')).to.equal('1');
2293
+ return fDone();
2294
+ }
2295
+ );
2296
+ test
2297
+ (
2298
+ 'Comparison with variables.',
2299
+ (fDone) =>
2300
+ {
2301
+ let _Parser = getExpressionParser();
2302
+ let tmpData = { Height: 10, Width: 5 };
2303
+ let tmpDestination = {};
2304
+ _Parser.solve('Result = Height > Width', tmpData, {}, false, tmpDestination);
2305
+ Expect(tmpDestination.Result).to.equal('1');
2306
+ _Parser.solve('Result = Width > Height', tmpData, {}, false, tmpDestination);
2307
+ Expect(tmpDestination.Result).to.equal('0');
2308
+ _Parser.solve('Result = Height == Width', tmpData, {}, false, tmpDestination);
2309
+ Expect(tmpDestination.Result).to.equal('0');
2310
+ _Parser.solve('Result = Height != Width', tmpData, {}, false, tmpDestination);
2311
+ Expect(tmpDestination.Result).to.equal('1');
2312
+ return fDone();
2313
+ }
2314
+ );
2315
+ }
2316
+ );
2317
+ }
2318
+ );
2319
+
2320
+ suite
2321
+ (
2322
+ 'Ternary Operator',
2323
+ function ()
2324
+ {
2325
+ suite
2326
+ (
2327
+ 'Basic Ternary',
2328
+ function ()
2329
+ {
2330
+ test
2331
+ (
2332
+ 'True branch is selected when condition is truthy.',
2333
+ (fDone) =>
2334
+ {
2335
+ let _Parser = getExpressionParser();
2336
+ Expect(_Parser.solve('Result = 5 > 3 ? 100 :: 200')).to.equal('100');
2337
+ return fDone();
2338
+ }
2339
+ );
2340
+ test
2341
+ (
2342
+ 'False branch is selected when condition is falsy.',
2343
+ (fDone) =>
2344
+ {
2345
+ let _Parser = getExpressionParser();
2346
+ Expect(_Parser.solve('Result = 3 > 5 ? 100 :: 200')).to.equal('200');
2347
+ return fDone();
2348
+ }
2349
+ );
2350
+ test
2351
+ (
2352
+ 'Ternary with equality comparison.',
2353
+ (fDone) =>
2354
+ {
2355
+ let _Parser = getExpressionParser();
2356
+ Expect(_Parser.solve('Result = 5 == 5 ? 10 :: 20')).to.equal('10');
2357
+ Expect(_Parser.solve('Result = 5 == 3 ? 10 :: 20')).to.equal('20');
2358
+ return fDone();
2359
+ }
2360
+ );
2361
+ }
2362
+ );
2363
+ suite
2364
+ (
2365
+ 'Ternary with Variables',
2366
+ function ()
2367
+ {
2368
+ test
2369
+ (
2370
+ 'Ternary selects based on variable comparison.',
2371
+ (fDone) =>
2372
+ {
2373
+ let _Parser = getExpressionParser();
2374
+ let tmpData = { Height: 10, Width: 5, A: 42, B: 99 };
2375
+ let tmpDestination = {};
2376
+ _Parser.solve('SomeValue = Height > Width ? A :: B', tmpData, {}, false, tmpDestination);
2377
+ Expect(tmpDestination.SomeValue).to.equal('42');
2378
+ _Parser.solve('SomeValue = Width > Height ? A :: B', tmpData, {}, false, tmpDestination);
2379
+ Expect(tmpDestination.SomeValue).to.equal('99');
2380
+ return fDone();
2381
+ }
2382
+ );
2383
+ }
2384
+ );
2385
+ suite
2386
+ (
2387
+ 'Ternary with Arithmetic in Branches',
2388
+ function ()
2389
+ {
2390
+ test
2391
+ (
2392
+ 'Arithmetic expressions work in ternary branches.',
2393
+ (fDone) =>
2394
+ {
2395
+ let _Parser = getExpressionParser();
2396
+ let tmpData = { A: 10, B: 5 };
2397
+ let tmpDestination = {};
2398
+ // A > B is true, so A + 1 = 11
2399
+ _Parser.solve('Result = A > B ? A + 1 :: B + 1', tmpData, {}, false, tmpDestination);
2400
+ Expect(tmpDestination.Result).to.equal('11');
2401
+ // B > A is false, so B + 1 = 6
2402
+ _Parser.solve('Result = B > A ? A + 1 :: B + 1', tmpData, {}, false, tmpDestination);
2403
+ Expect(tmpDestination.Result).to.equal('6');
2404
+ return fDone();
2405
+ }
2406
+ );
2407
+ test
2408
+ (
2409
+ 'Arithmetic expressions work in the ternary condition.',
2410
+ (fDone) =>
2411
+ {
2412
+ let _Parser = getExpressionParser();
2413
+ // 2 + 3 = 5, 1 + 2 = 3, so 5 > 3 = true, result is 100
2414
+ Expect(_Parser.solve('Result = 2 + 3 > 1 + 2 ? 100 :: 200')).to.equal('100');
2415
+ // 1 + 1 = 2, 3 + 1 = 4, so 2 > 4 = false, result is 200
2416
+ Expect(_Parser.solve('Result = 1 + 1 > 3 + 1 ? 100 :: 200')).to.equal('200');
2417
+ return fDone();
2418
+ }
2419
+ );
2420
+ }
2421
+ );
2422
+ suite
2423
+ (
2424
+ 'Nested Ternary',
2425
+ function ()
2426
+ {
2427
+ test
2428
+ (
2429
+ 'Nested ternary with parenthesized inner ternary.',
2430
+ (fDone) =>
2431
+ {
2432
+ let _Parser = getExpressionParser();
2433
+ let tmpData = { A: 5, B: 3 };
2434
+ let tmpDestination = {};
2435
+ // A > 0 is true, then B > 0 is true, so result is 1
2436
+ _Parser.solve('Result = A > 0 ? (B > 0 ? 1 :: 2) :: 3', tmpData, {}, false, tmpDestination);
2437
+ Expect(tmpDestination.Result).to.equal('1');
2438
+ // A > 0 is true, then B > 10 is false, so result is 2
2439
+ _Parser.solve('Result = A > 0 ? (B > 10 ? 1 :: 2) :: 3', tmpData, {}, false, tmpDestination);
2440
+ Expect(tmpDestination.Result).to.equal('2');
2441
+ return fDone();
2442
+ }
2443
+ );
2444
+ }
2445
+ );
2446
+ }
2447
+ );