fable 3.0.132 → 3.0.134

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,28 +20,6 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
20
20
 
21
21
  let tmpDepthSolveList = Array.isArray(pDepthSolveList) ? pDepthSolveList : false;
22
22
 
23
- // // Although we have a `Token.Type == "Parenthesis"` option to check on, keeping these explicit means the solver won't
24
- // // allow users to pass in parenthesis in the wrong order.
25
- // // The linter does blow up as does the postfix, but, just in case we'll leave these explicit.
26
- // // It really doesn't hurt anything.
27
- // if (pLeftValue.Token === ')')
28
- // {
29
- // // We have found a close parenthesis which needs to pull the proper virtual symbol for the last operation on this stack.
30
- // // This ensures we are not expressing the parenthesis virtual symbols to the solver.
31
- // pLeftValue.VirtualSymbolName = pLayerStackMap[pLeftValue.SolveLayerStack];
32
- // this.log.error(`ERROR: ExpressionParser.getPosfixSolveListOperation found a close parenthesis in the left value of an operation.`);
33
- // }
34
- // else if (pRightValue.Token === '(')
35
- // {
36
- // // We have found a close parenthesis which needs to pull the proper virtual symbol for the last operation on this stack.
37
- // // This ensures we are not expressing the parenthesis virtual symbols to the solver.
38
- // pRightValue.VirtualSymbolName = pLayerStackMap[pRightValue.SolveLayerStack];
39
- // this.log.error(`ERROR: ExpressionParser.getPosfixSolveListOperation found a close parenthesis in the left value of an operation.`);
40
- // }
41
-
42
- // // Set the layer stack map virtual symbol name to the last operation performed on this stack.
43
- // pLayerStackMap[pOperation.SolveLayerStack] = pOperation.VirtualSymbolName;
44
-
45
23
  /* These two if blocks are very complex -- they basically provide a
46
24
  * way to deal with recursion that can be expressed to the user in
47
25
  * a meaningful way.
@@ -56,27 +34,13 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
56
34
  else
57
35
  {
58
36
  // We need to set the left value to a virtual symbol instead of the looked up value if it's already used in another operation
59
- tmpOperation.LeftValue = this.getTokenContainerObject(tmpOperation.LeftValue.VirtualSymbolName, 'Token.VirtualSymbol');
60
- // Now walk backwards and see if we need to update a previous symbol for a previously unparsed operator
61
- if (tmpDepthSolveList)
37
+ if ('LeftVirtualSymbolName' in tmpOperation.Operation)
62
38
  {
63
- for (let i = pDepthSolveIndex - 1; i >= 0; i--)
64
- {
65
- if ((tmpDepthSolveList[i].Type === 'Token.Operator') && (!tmpDepthSolveList[i].Parsed) &&
66
- // When walking backward, we only want to mutate if the .
67
- ('Descriptor' in tmpDepthSolveList[i]) && ('Descriptor' in tmpOperation.Operation) &&
68
- // Anything >3 does not have commutative properties
69
- (tmpDepthSolveList[i].Descriptor.Precedence > 3))
70
- {
71
- // If the symbol to its right is not the same as this operation
72
- if (tmpDepthSolveList[i+1].VirtualSymbolName !== tmpOperation.VirtualSymbolName)
73
- {
74
- // This is the recursive "shunting" being simulated
75
- tmpDepthSolveList[i+1].VirtualSymbolName = tmpOperation.VirtualSymbolName;
76
- }
77
- break;
78
- }
79
- }
39
+ tmpOperation.LeftValue = this.getTokenContainerObject(tmpOperation.Operation.LeftVirtualSymbolName, 'Token.VirtualSymbol');
40
+ }
41
+ else
42
+ {
43
+ tmpOperation.LeftValue = this.getTokenContainerObject(tmpOperation.LeftValue.VirtualSymbolName, 'Token.VirtualSymbol');
80
44
  }
81
45
  }
82
46
  if (!tmpOperation.RightValue.VirtualSymbolName)
@@ -86,31 +50,14 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
86
50
  else
87
51
  {
88
52
  // We need to set the right value to a virtual symbol instead of the looked up value if it's already used in another operation
89
- tmpOperation.RightValue = this.getTokenContainerObject(tmpOperation.RightValue.VirtualSymbolName, 'Token.VirtualSymbol');
90
- // Now walk forwards and see if we need to update an upcoming symbol for a previously unparsed operator
91
- if (tmpDepthSolveList)
53
+ //if ('LeftVirtualSymbolName' in tmpOperation.RightValue)
54
+ if ('RightVirtualSymbolName' in tmpOperation.Operation)
92
55
  {
93
- for (let i = pDepthSolveIndex + 1; i < tmpDepthSolveList.length; i++)
94
- {
95
- if ((tmpDepthSolveList[i].Type === 'Token.Operator') && (!tmpDepthSolveList[i].Parsed) &&
96
- // When walking forward, we only want to mutate if the precedence hasn't been solved.
97
- ('Descriptor' in tmpDepthSolveList[i]) && ('Descriptor' in tmpOperation.Operation) &&
98
- // Anything >3 does not have commutative properties
99
- (tmpDepthSolveList[i].Descriptor.Precedence > 3))
100
- {
101
- // If the symbol to its right is not the same as this operation
102
- if (tmpDepthSolveList[i-1].VirtualSymbolName !== tmpOperation.VirtualSymbolName)
103
- {
104
- // This is the recursive "shunting" being simulated
105
- tmpDepthSolveList[i-1].VirtualSymbolName = tmpOperation.VirtualSymbolName;
106
- }
107
- break;
108
- }
109
- else if ((tmpDepthSolveList[i].Type === 'Token.Operator') && (!tmpDepthSolveList[i].Parsed))
110
- {
111
- break;
112
- }
113
- }
56
+ tmpOperation.RightValue = this.getTokenContainerObject(tmpOperation.Operation.RightVirtualSymbolName, 'Token.VirtualSymbol');
57
+ }
58
+ else
59
+ {
60
+ tmpOperation.RightValue = this.getTokenContainerObject(tmpOperation.RightValue.VirtualSymbolName, 'Token.VirtualSymbol');
114
61
  }
115
62
  }
116
63
 
@@ -204,11 +151,6 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
204
151
  {
205
152
  // Set the type of this to be a function
206
153
  tmpResults.PostfixTokenObjects[i-1].Type = 'Token.Function';
207
- // tmpResults.PostfixTokenObjects[i-1].Descriptor = this.ExpressionParser.tokenMap[pTokenizedExpression[i-1]];
208
- // Rename the virtual symbol n ame to include the function
209
- // tmpResults.PostfixTokenObjects[i].VirtualSymbolName = `Fn_${tmpVirtualParenthesisIndex}_D_${tmpDepth}_${this.fable.DataFormat.cleanNonAlphaCharacters(tmpResults.PostfixTokenObjects[i-1].Token)}`;
210
- // The function and the parenthesis are at the same depth and virtual symbol
211
- // tmpResults.PostfixTokenObjects[i-1].VirtualSymbolName = tmpResults.PostfixTokenObjects[i].VirtualSymbolName;
212
154
  }
213
155
  }
214
156
 
@@ -242,24 +184,6 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
242
184
  tmpResults.PostfixTokenObjects[i].VirtualSymbolName = tmpSolveLayerStack.pop();
243
185
  tmpResults.PostfixTokenObjects[i].SolveLayerStack = tmpSolveLayerStack[tmpSolveLayerStack.length-1];
244
186
  }
245
-
246
- // 3. If it's a state address or Symbol, set depth
247
- // It was much more complex later on solving these as virtual symbols of their own.
248
- // We are saving the value resolution for the very end.
249
- else if ((tmpResults.PostfixTokenObjects[i].Type === 'Token.Symbol'))
250
- {
251
- // Set the depth of the current solution depth
252
- tmpResults.PostfixTokenObjects[i].Depth = tmpDepth;
253
- tmpResults.PostfixTokenObjects[i].SolveLayerStack = tmpSolveLayerStack[tmpSolveLayerStack.length-1];
254
- // Generate a virtual symbol name that's somewhat human readable
255
- //tmpResults.PostfixTokenObjects[i].VirtualSymbolName = `Sm_${tmpVirtualParenthesisIndex}_D_${tmpDepth}_${this.fable.DataFormat.cleanNonAlphaCharacters(tmpResults.PostfixTokenObjects[i].Token)}`;
256
-
257
- // We've used up this virtual symbol index so increment it
258
- // The reason we only use these once is to make sure if we use, say, sin(x) twice at the same depth we still have unique names for each virtual solution
259
- //tmpVirtualParenthesisIndex++;
260
- }
261
-
262
- // 4. If it's an operator or constant or comment, just set the depth
263
187
  else
264
188
  {
265
189
  tmpResults.PostfixTokenObjects[i].Depth = tmpDepth;
@@ -356,7 +280,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
356
280
  // The - at the beginning of an expression is a number line orientation modifier
357
281
  else if ((i == 0) && (tmpToken.Token == '-'))
358
282
  {
359
- tmpToken.VirtualSymbolName = `V_${tmpVirtualSymbolIndex}`;
283
+ tmpToken.VirtualSymbolName = `VNLO_${tmpVirtualSymbolIndex}`;
360
284
  tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack] = tmpToken.VirtualSymbolName;
361
285
  tmpVirtualSymbolIndex++;
362
286
  }
@@ -364,7 +288,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
364
288
  else if ((i > 0) && (tmpToken.Token == '-') && ((tmpSolveLayerTokens[i-1].Type === 'Token.Operator') || (tmpSolveLayerTokens[i-1].Token === '(')))
365
289
  {
366
290
  // The number line negation operator is a special case that generates a virtual constant (-1.0) and multiplies it by the next token
367
- tmpToken.VirtualSymbolName = `V_${tmpVirtualSymbolIndex}`;
291
+ tmpToken.VirtualSymbolName = `VNLO_${tmpVirtualSymbolIndex}`;
368
292
  tmpVirtualSymbolIndex++;
369
293
  }
370
294
  // The + at the beginning is also a number line orientation modifier ... THAT WE IGNORE
@@ -397,7 +321,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
397
321
  else if ((tmpSolveLayerTokens[i].Type === 'Token.Function') && (tmpPrecedence === 0))
398
322
  {
399
323
  let tmpToken = tmpSolveLayerTokens[i];
400
- tmpToken.VirtualSymbolName = `V_${tmpVirtualSymbolIndex}`;
324
+ tmpToken.VirtualSymbolName = `VFE_${tmpVirtualSymbolIndex}`;
401
325
  tmpVirtualSymbolIndex++;
402
326
  tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack] = tmpToken.VirtualSymbolName;
403
327
  }
@@ -405,156 +329,230 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
405
329
  }
406
330
  }
407
331
 
408
- // 5.2: Decorate the Parenthesis with Virtual Symbol Names
332
+ // 5.15 Generate Virtual Symbol Clusters for Functions and Parenthesis
409
333
  // ... this gets funny because of precedence of operations surrounding them, parenthesis and functions.
410
- let tmpParenthesisCacheLIFOStack = [];
334
+ let tmpFunctionCacheLIFOStack = [];
411
335
  for (let i = 0; i < tmpResults.PostfixTokenObjects.length; i++)
412
336
  {
413
337
  let tmpPostfixTokenObject = tmpResults.PostfixTokenObjects[i];
414
338
 
415
339
  if (tmpPostfixTokenObject.Type === 'Token.Parenthesis')
416
340
  {
417
- // This is just to track the parenthesis stack level in User feedback
341
+ // This is just to track the parenthesis stack level for User feedback
418
342
  tmpPostfixTokenObject.ParenthesisStack = tmpPostfixTokenObject.VirtualSymbolName;
419
343
 
420
- if (tmpPostfixTokenObject.Token === '(')
344
+ // At the beginning of the expression, this must be an open parenthesis to be legal.
345
+ if (i == 0)
421
346
  {
422
- // It's an open parenthesis. If the previous token was an operator, get its precedence.
423
- if (i > 0)
347
+ tmpPostfixTokenObject.IsFunction = false;
348
+ let tmpVirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
349
+ if (!tmpVirtualSymbolName)
424
350
  {
425
- if (tmpResults.PostfixTokenObjects[i-1].Type === 'Token.Operator')
426
- {
427
- tmpPostfixTokenObject.PreviousPrecedence = tmpResults.PostfixTokenObjects[i-1].Descriptor.Precedence;
428
- tmpPostfixTokenObject.IsFunction = false;
429
- tmpPostfixTokenObject.PreviousVirtualSymbolName = tmpResults.PostfixTokenObjects[i-1].VirtualSymbolName;
430
- tmpPostfixTokenObject.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
431
- }
432
- // This is a function, we will create a virtual symbol for the discrete parenthesis
433
- else if (tmpResults.PostfixTokenObjects[i-1].Type === 'Token.Function')
351
+ // ... this parenthesis group has no operators in it; make a virtual symbol name.
352
+ tmpVirtualSymbolName = `VP_${tmpVirtualSymbolIndex}`;
353
+ tmpVirtualSymbolIndex++;
354
+ }
355
+ tmpPostfixTokenObject.VirtualSymbolName = tmpVirtualSymbolName;
356
+ tmpFunctionCacheLIFOStack.push(tmpPostfixTokenObject);
357
+ }
358
+ // If it's an open parenthesis
359
+ else if (tmpPostfixTokenObject.Token === '(')
360
+ {
361
+ // ... supporting a function
362
+ if (tmpResults.PostfixTokenObjects[i-1].Type === 'Token.Function')
363
+ {
364
+ tmpPostfixTokenObject.IsFunction = true;
365
+ tmpPostfixTokenObject.Function = tmpResults.PostfixTokenObjects[i-1];
366
+ let tmpVirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
367
+ if (!tmpVirtualSymbolName)
434
368
  {
435
- tmpPostfixTokenObject.PreviousPrecedence = 0;
436
- tmpPostfixTokenObject.IsFunction = true;
437
- tmpPostfixTokenObject.PreviousVirtualSymbolName = tmpResults.PostfixTokenObjects[i-1].VirtualSymbolName;
438
- let tmpVirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
439
- if (!tmpVirtualSymbolName)
440
- {
441
- // This is a parenthesis group with no operators in it; make a virtual symbol name.
442
- tmpVirtualSymbolName = `V_${tmpVirtualSymbolIndex}`;
443
- tmpVirtualSymbolIndex++;
444
- }
445
- tmpPostfixTokenObject.VirtualSymbolName = tmpVirtualSymbolName;
446
-
447
- if (i > 1)
448
- {
449
- // Todo: This needs to be enhanced to deal with negations
450
- let tmpTokenBeforeFunction = tmpResults.PostfixTokenObjects[i-2];
451
- if (tmpTokenBeforeFunction.Type === 'Token.Operator')
452
- {
453
- tmpPostfixTokenObject.PreviousVirtualSymbolName = tmpResults.PostfixTokenObjects[i-2].VirtualSymbolName;
454
- tmpPostfixTokenObject.PreviousPrecedence = tmpResults.PostfixTokenObjects[i-2].Descriptor.Precedence;
455
- }
456
- }
369
+ // ... this parenthesis group has no operators in it; make a virtual symbol name.
370
+ tmpVirtualSymbolName = `VFP_${tmpVirtualSymbolIndex}`;
371
+ tmpVirtualSymbolIndex++;
457
372
  }
373
+ tmpPostfixTokenObject.VirtualSymbolName = tmpVirtualSymbolName;
458
374
  }
459
375
  else
460
376
  {
461
- tmpPostfixTokenObject.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
377
+ tmpPostfixTokenObject.IsFunction = false;
378
+ let tmpVirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
379
+ if (!tmpVirtualSymbolName)
380
+ {
381
+ // This is a parenthesis group with no operators in it; make a virtual symbol name.
382
+ tmpVirtualSymbolName = `VP_${tmpVirtualSymbolIndex}`;
383
+ tmpVirtualSymbolIndex++;
384
+ }
385
+ tmpPostfixTokenObject.VirtualSymbolName = tmpVirtualSymbolName;
462
386
  }
463
- tmpParenthesisCacheLIFOStack.push(tmpPostfixTokenObject);
387
+ tmpFunctionCacheLIFOStack.push(tmpPostfixTokenObject);
464
388
  }
465
389
  if (tmpPostfixTokenObject.Token === ')')
466
390
  {
467
- // There are three options for assigning this:
468
- let tmpOpenParenthesis = tmpParenthesisCacheLIFOStack.pop();
469
- // It's at the end of the tokens -- use the stack's identifier
470
- if (i >= tmpResults.PostfixTokenObjects.length - 1)
391
+ let tmpOpenParenthesis = tmpFunctionCacheLIFOStack.pop();
392
+ if (tmpOpenParenthesis.IsFunction)
471
393
  {
472
- if (tmpOpenParenthesis.IsFunction)
473
- {
474
- tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.PreviousVirtualSymbolName;
475
- }
476
- else
477
- {
478
- tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.VirtualSymbolName;
479
- }
394
+ tmpPostfixTokenObject.IsFunction = true;
395
+ tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.Function.VirtualSymbolName;
480
396
  }
481
397
  else
482
398
  {
483
- // The next token is an operator and we're a function
484
- let tmpPeekedNextToken = tmpResults.PostfixTokenObjects[i+1];
485
- if (tmpPeekedNextToken.Type == 'Token.Operator' && tmpOpenParenthesis.IsFunction)
486
- {
487
- // This is the most complex case -- the next token is an operator AND this is a function.
488
- // The following is just pointer math.
489
- // If the operater is at the same precedence or higher than the open parenthesis previous operator, use the previous operator's identifier
490
- // NOTE: This line of code is insanely complex
399
+ tmpPostfixTokenObject.IsFunction = false;
400
+ tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.VirtualSymbolName
401
+ }
402
+ }
403
+ }
404
+ }
405
+
406
+ // X. Postprocess the parenthesis groups to ensure they respect the order of operations for their boundaries
407
+ for (let tmpSolveLayerIndex = 0; tmpSolveLayerIndex < tmpSolveLayerKeys.length; tmpSolveLayerIndex++)
408
+ {
409
+ let tmpParenthesisStack = [];
410
+ let tmpLastOperator = false;
491
411
 
492
- //tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.PreviousVirtualSymbolName;
412
+ let tmpSolveLayerTokens = tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];
413
+ for (let i = 0; i < tmpSolveLayerTokens.length; i++)
414
+ {
415
+ let tmpPostfixTokenObject = tmpSolveLayerTokens[i];
493
416
 
494
- // If the next token has higher precedence than what's before the open parenthesis, use it for the open as well
495
- if (tmpPeekedNextToken.Descriptor.Precedence < tmpOpenParenthesis.PreviousPrecedence)
496
- {
497
- tmpOpenParenthesis.VirtualSymbolName = tmpPeekedNextToken.VirtualSymbolName;
498
- tmpPostfixTokenObject.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
499
- }
500
- // Otherwise use this one -- it is the higher precedence. And update the previous parenthesis operator's virtual symbol to be the peeked token's virtual symbol.
501
- else
417
+ // Keep track of the last operator
418
+ if (tmpPostfixTokenObject.Type === 'Token.Operator')
419
+ {
420
+ tmpLastOperator = tmpPostfixTokenObject;
421
+ }
422
+
423
+ // This is only important to do at the close parenthesis.
424
+ if (tmpPostfixTokenObject.Type === 'Token.Function')
425
+ {
426
+ tmpPostfixTokenObject.PreviousOperator = tmpLastOperator;
427
+ }
428
+ else if ((tmpPostfixTokenObject.Type === 'Token.Parenthesis') && (tmpPostfixTokenObject.Token === '(') && tmpPostfixTokenObject.IsFunction)
429
+ {
430
+ tmpParenthesisStack.push(tmpPostfixTokenObject);
431
+ if (tmpPostfixTokenObject.Function.PreviousOperator)
432
+ {
433
+ tmpPostfixTokenObject.PreviousOperator = tmpPostfixTokenObject.Function.PreviousOperator;
434
+ }
435
+ }
436
+ else if ((tmpPostfixTokenObject.Type === 'Token.Parenthesis') && (tmpPostfixTokenObject.Token === '('))
437
+ {
438
+ tmpPostfixTokenObject.PreviousOperator = tmpLastOperator;
439
+ tmpParenthesisStack.push(tmpPostfixTokenObject);
440
+ }
441
+ else if ((tmpPostfixTokenObject.Type === 'Token.Parenthesis') && (tmpPostfixTokenObject.Token === ')'))
442
+ {
443
+ // This is ultra complex, and binds the order of operations logic to the open parenthesis for the group
444
+ let tmpOpenParenthesis = tmpParenthesisStack.pop();
445
+ if (i < tmpSolveLayerTokens.length - 1)
446
+ {
447
+ for (let j = i + 1; j < tmpSolveLayerTokens.length; j++)
448
+ {
449
+ if (tmpSolveLayerTokens[j].Type === 'Token.Operator')
502
450
  {
503
- tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.PreviousVirtualSymbolName;
451
+ tmpOpenParenthesis.NextOperator = tmpSolveLayerTokens[j];
452
+ break;
504
453
  }
505
454
  }
506
- // The next token is an operator and it isn't a function
507
- else if (tmpPeekedNextToken.Type == 'Token.Operator' && ('PreviousPrecedence' in tmpOpenParenthesis))
455
+ }
456
+ if (tmpOpenParenthesis.PreviousOperator && tmpOpenParenthesis.NextOperator)
457
+ {
458
+ if (tmpOpenParenthesis.PreviousOperator.Descriptor.Precedence <= tmpOpenParenthesis.NextOperator.Descriptor.Precedence)
508
459
  {
509
- // This is the second most complex case -- the next token is an operator.
510
- // If the operater is at the same precedence or higher than the open parenthesis previous operator, use the previous operator's identifier
511
- // NOTE: This line of code is insanely complex
512
- if (tmpPeekedNextToken.Descriptor.Precedence <= tmpOpenParenthesis.PreviousPrecedence)
513
- {
514
- tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.PreviousVirtualSymbolName;
515
- }
516
- // Otherwise use this one -- it is the higher precedence. And update the previous parenthesis operator's virtual symbol to be the peeked token's virtual symbol.
517
- else
518
- {
519
- tmpPostfixTokenObject.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
520
- tmpOpenParenthesis.VirtualSymbolName = tmpPeekedNextToken.VirtualSymbolName;
521
- }
460
+ tmpOpenParenthesis.NextOperator.LeftVirtualSymbolName = tmpOpenParenthesis.PreviousOperator.VirtualSymbolName;
522
461
  }
523
462
  else
524
463
  {
525
- // If this is a function, dereference the function's previous virtual symbol name
526
- if (tmpOpenParenthesis.IsFunction)
527
- {
528
- tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.PreviousVirtualSymbolName;
529
- }
530
- else
531
- {
532
- tmpPostfixTokenObject.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
533
- }
464
+ tmpOpenParenthesis.PreviousOperator.RightVirtualSymbolName = tmpOpenParenthesis.NextOperator.VirtualSymbolName;
534
465
  }
535
466
  }
536
-
537
- // If there isn't an operator in the layer stack, push forward the assignment
538
- if (!tmpResults.PostfixLayerstackMap[tmpOpenParenthesis.ParenthesisStack])
467
+ }
468
+ else
469
+ {
470
+ if (!('SolveLayerStack' in tmpPostfixTokenObject))
539
471
  {
540
- tmpResults.PostfixLayerstackMap[tmpOpenParenthesis.ParenthesisStack] = tmpOpenParenthesis.VirtualSymbolName;
472
+ // Decorate the solve layer stack for the token
473
+ if (tmpParenthesisStack.length > 0)
474
+ {
475
+ tmpPostfixTokenObject.SolveLayerStack = tmpParenthesisStack[tmpParenthesisStack.length-1].SolveLayerStack;
476
+ }
477
+ else
478
+ {
479
+ tmpPostfixTokenObject.SolveLayerStack = 'Expression_Root';
480
+ }
541
481
  }
542
482
  }
543
483
  }
544
484
  }
545
485
 
486
+ // 5.2.9: Make sure the affinity of operators is respecting order of operations.
487
+ // Walk backwards and forwards, hoisting same value precedence backwards/forwards
488
+ // across each layer... the precedence change needs to be decreasing to matter
546
489
  for (let tmpSolveLayerIndex = 0; tmpSolveLayerIndex < tmpSolveLayerKeys.length; tmpSolveLayerIndex++)
547
490
  {
491
+ let tmpLastPrecedence = false;
492
+ let tmpFinalChainToken = false;
548
493
  let tmpSolveLayerTokens = tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];
549
494
 
550
- if (tmpSolveLayerTokens.length === 1)
495
+ for (let i = tmpSolveLayerTokens.length-1; i >= 0; i--)
496
+ {
497
+ let tmpToken = tmpSolveLayerTokens[i];
498
+
499
+ if (tmpToken.Type === 'Token.Operator')
500
+ {
501
+ if (!tmpFinalChainToken)
502
+ {
503
+ tmpFinalChainToken = tmpToken;
504
+ }
505
+ else if (tmpToken.Descriptor.Precedence > tmpLastPrecedence)
506
+ {
507
+ // This is less imporant than the last precedence, so hoist back the virtual value
508
+ tmpToken.RightVirtualSymbolName = tmpFinalChainToken.VirtualSymbolName;
509
+ //console.log(`Hoisting ${tmpToken.Token} back to ${tmpFinalChainToken.Token}`);
510
+ tmpFinalChainToken = tmpToken;
511
+ }
512
+ else if (tmpToken.Descriptor.Precedence < tmpLastPrecedence)
513
+ {
514
+ tmpFinalChainToken = tmpToken;
515
+ }
516
+ tmpLastPrecedence = tmpToken.Descriptor.Precedence;
517
+ }
518
+ }
519
+ let tmpDecreasingPrecedenceStack = [];
520
+ let tmpLastToken = false;
521
+ for (let i = tmpSolveLayerTokens.length-1; i >= 0; i--)
551
522
  {
552
- // This is just a simple value assignment -- use a simple addition virtual operation.
553
- // We often see these inside functions.
554
- let tmpAbstractAddToken = this.getTokenContainerObject('+');
555
- //let tmpVirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];
556
- tmpAbstractAddToken.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];
557
- tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractAddToken, this.getTokenContainerObject('0.0'), tmpSolveLayerTokens[0]));
523
+ let tmpToken = tmpSolveLayerTokens[i];
524
+
525
+ if (tmpToken.Type === 'Token.Operator')
526
+ {
527
+ if (!tmpLastToken)
528
+ {
529
+ tmpLastToken = tmpToken;
530
+ }
531
+ else if (tmpToken.Descriptor.Precedence > tmpLastPrecedence)
532
+ {
533
+ // Check and see if this needs to be resolved in the stack
534
+ if (tmpDecreasingPrecedenceStack.length > 0)
535
+ {
536
+ for (let j = tmpDecreasingPrecedenceStack.length-1; j >= 0; j--)
537
+ {
538
+ if (tmpDecreasingPrecedenceStack[j].Descriptor.Precedence >= tmpToken.Descriptor.Precedence)
539
+ {
540
+ //console.log(`Hoisting ${tmpDecreasingPrecedenceStack[j].Token} up to ${tmpToken.Token}`);
541
+ tmpDecreasingPrecedenceStack[j].LeftVirtualSymbolName = tmpToken.VirtualSymbolName;
542
+ tmpDecreasingPrecedenceStack.slice(j, 1);
543
+ break;
544
+ }
545
+ }
546
+ }
547
+ tmpLastToken = tmpToken;
548
+ }
549
+ else if (tmpToken.Descriptor.Precedence < tmpLastPrecedence)
550
+ {
551
+ tmpDecreasingPrecedenceStack.push(tmpLastToken);
552
+ tmpLastToken = tmpToken;
553
+ }
554
+ tmpLastPrecedence = tmpToken.Descriptor.Precedence;
555
+ }
558
556
  }
559
557
  }
560
558
 
@@ -563,6 +561,27 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
563
561
  {
564
562
  let tmpSolveLayerTokens = tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];
565
563
 
564
+ // If this is a layer with one value, presume it's an assignment.
565
+ if (tmpSolveLayerTokens.length === 1)
566
+ {
567
+ let tmpAbstractAssignToken = this.getTokenContainerObject('=');
568
+ tmpAbstractAssignToken.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];
569
+ // If this doesn't have a matching solvelayerstack, get the virtual symbol name from the parenthesis group it's in
570
+ if (!tmpAbstractAssignToken.VirtualSymbolName)
571
+ {
572
+ for (let i = 0; i < tmpResults.PostfixTokenObjects.length; i++)
573
+ {
574
+ if (tmpResults.PostfixTokenObjects[i].ParenthesisStack === tmpSolveLayerTokens[0].SolveLayerStack)
575
+ {
576
+ tmpAbstractAssignToken.VirtualSymbolName = tmpResults.PostfixTokenObjects[i].VirtualSymbolName;
577
+ break;
578
+ }
579
+ }
580
+ }
581
+ tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractAssignToken, tmpSolveLayerTokens[0], this.getTokenContainerObject('0.0')));
582
+ continue;
583
+ }
584
+
566
585
  // For each precedence level in the layer
567
586
  for (let tmpPrecedence = 0; tmpPrecedence <= this.ExpressionParser.tokenMaxPrecedence; tmpPrecedence++)
568
587
  {
@@ -580,7 +599,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
580
599
  {
581
600
  // The number line negation operator is a special case that generates a virtual constant (-1.0) and multiplies it by the next token
582
601
  // This is an abstract operation that isn't in the expression.
583
- let tmpAbstractMultiplyToken = this.getTokenContianerObject('*');
602
+ let tmpAbstractMultiplyToken = this.getTokenContainerObject('*');
584
603
  tmpAbstractMultiplyToken.VirtualSymbolName = tmpToken.VirtualSymbolName;
585
604
  tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractMultiplyToken, this.getTokenContainerObject('-1.0'), tmpSolveLayerTokens[i+1]));
586
605
  }
@@ -588,7 +607,7 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
588
607
  else if ((i > 0) && (tmpToken.Token == '-') && ((tmpSolveLayerTokens[i-1].Type === 'Token.Operator') || (tmpSolveLayerTokens[i-1].Token === '(')))
589
608
  {
590
609
  // The number line negation operator is a special case that generates a virtual constant (-1.0) and multiplies it by the next token
591
- let tmpAbstractMultiplyToken = this.getTokenContianerObject('*');
610
+ let tmpAbstractMultiplyToken = this.getTokenContainerObject('*');
592
611
  tmpAbstractMultiplyToken.VirtualSymbolName = tmpToken.VirtualSymbolName;
593
612
  tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractMultiplyToken, this.getTokenContainerObject('-1.0'), tmpSolveLayerTokens[i+1]));
594
613
  }
@@ -612,14 +631,13 @@ class ExpressionParserPostfix extends libExpressionParserOperationBase
612
631
  else if ((tmpSolveLayerTokens[i].Type === 'Token.Function') && (tmpPrecedence === 0))
613
632
  {
614
633
  let tmpToken = tmpSolveLayerTokens[i];
615
- // Not sure what to do with the other token.
616
634
  tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpToken, tmpSolveLayerTokens[i+1], this.getTokenContainerObject('0.0')));
617
635
  }
618
636
  }
619
637
  }
620
638
  }
621
639
 
622
- // Now set the assignment address.
640
+ // 7. Lastly set the assignment address.
623
641
  let tmpAssignmentInstruction = this.getPosfixSolveListOperation(this.getTokenContainerObject('Assign', 'Token.SolverInstruction'), this.getTokenContainerObject('DestinationHash', 'Token.SolverInstruction'), this.getTokenContainerObject('Resulting', 'Token.SolverInstruction'));
624
642
  tmpAssignmentInstruction.VirtualSymbolName = tmpResults.PostfixedAssignmentAddress;
625
643
  tmpResults.PostfixSolveList.push(tmpAssignmentInstruction);
@@ -68,6 +68,16 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
68
68
  tmpStepResultObject.ExpressionStep.RightValue.Value = tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols, tmpStepResultObject.ExpressionStep.RightValue.VirtualSymbolName);
69
69
  }
70
70
 
71
+ // Virtual Constants
72
+ if (tmpStepResultObject.ExpressionStep.LeftValue.Type === 'Token.Constant' && !('Value' in tmpStepResultObject.ExpressionStep.LeftValue))
73
+ {
74
+ tmpStepResultObject.ExpressionStep.LeftValue.Value = tmpStepResultObject.ExpressionStep.LeftValue.Token;
75
+ }
76
+ if (tmpStepResultObject.ExpressionStep.RightValue.Type === 'Token.Constant' && !('Value' in tmpStepResultObject.ExpressionStep.RightValue))
77
+ {
78
+ tmpStepResultObject.ExpressionStep.RightValue.Value = tmpStepResultObject.ExpressionStep.RightValue.Token;
79
+ }
80
+
71
81
  if (tmpStepResultObject.ExpressionStep.Operation.Type = 'Operator')
72
82
  {
73
83
  // TODO: This can be optimized. A lot. If necessary. Seems pretty fast honestly for even thousands of operations. Slowest part is arbitrary precision.
@@ -90,7 +100,7 @@ class ExpressionParserSolver extends libExpressionParserOperationBase
90
100
  }
91
101
  catch (pError)
92
102
  {
93
- tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ${i} with function ${tmpStepResultObject.ExpressionStep.Operation.Token}.`);
103
+ tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ${i} with function ${tmpStepResultObject.ExpressionStep.Operation.Token}: ${pError}`);
94
104
  this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
95
105
  return false;
96
106
  }
@@ -3,6 +3,7 @@
3
3
  {
4
4
  "Name": "Equals",
5
5
  "Token": "=",
6
+ "Function": "fable.Math.assignValue",
6
7
  "Precedence": 0,
7
8
  "Type": "Assignment"
8
9
  },
@@ -113,9 +113,18 @@ class FableServiceExpressionParser extends libFableServiceBase
113
113
  }
114
114
  catch(pError)
115
115
  {
116
- tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric value for the state address ${tmpToken.Token} at index ${i}`);
117
- this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
118
- tmpToken.Resolved = false;
116
+ // TODO: Should we allow this to be a function? Good god the complexity and beauty of that...
117
+ if (Array.isArray(tmpValue) || (typeof(tmpValue) === 'object'))
118
+ {
119
+ tmpToken.Resolved = true;
120
+ tmpToken.Value = tmpValue;
121
+ }
122
+ else
123
+ {
124
+ tmpResults.ExpressionParserLog.push(`INFO: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric, non-set value for the state address ${tmpToken.Token} at index ${i}`);
125
+ this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
126
+ tmpToken.Resolved = false;
127
+ }
119
128
  }
120
129
  }
121
130
  }
@@ -190,6 +199,16 @@ class FableServiceExpressionParser extends libFableServiceBase
190
199
  return this.Solver.solvePostfixedExpression(pPostfixedExpression, pDataDestinationObject, pResultObject, pManifest);
191
200
  }
192
201
 
202
+ /**
203
+ * Solves the given expression using the provided data and manifest.
204
+ *
205
+ * @param {string} pExpression - The expression to solve.
206
+ * @param {object} pDataSourceObject - (optional) The data source object (e.g. AppData).
207
+ * @param {object} pResultObject - (optional) The result object containing the full postfix expression list, internal variables and solver history.
208
+ * @param {object} pManifest - (optional) The manifest object for dereferencing variables.
209
+ * @param {object} pDataDestinationObject - (optional) The data destination object for where to marshal the result into.
210
+ * @returns {any} - The result of solving the expression.
211
+ */
193
212
  solve(pExpression, pDataSourceObject, pResultObject, pManifest, pDataDestinationObject)
194
213
  {
195
214
  let tmpResultsObject = (typeof(pResultObject) === 'object') ? pResultObject : {};