linny-r 1.4.0 → 1.4.2

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.
@@ -270,53 +270,61 @@ class Expression {
270
270
  }
271
271
 
272
272
  chooseVector(number) {
273
- // Return the vector to use for computation (defaults to "own" vector)
274
- if(number === false || this.isStatic) return this.vector;
275
- if(number) {
276
- // Use the vector for the wildcard number (create it if necessary)
277
- if(!this.wildcard_vectors.hasOwnProperty(number)) {
278
- this.wildcard_vectors[number] = [];
273
+ // Return the vector to use for computation (defaults to "own" vector).
274
+ // NOTE: Static wildcard expressions must also choose a vector!
275
+ if(typeof number !== 'number' ||
276
+ (this.isStatic && !this.isWildcardExpression)) return this.vector;
277
+ // Use the vector for the wildcard number (create it if necessary).
278
+ if(!this.wildcard_vectors.hasOwnProperty(number)) {
279
+ this.wildcard_vectors[number] = [];
280
+ if(this.isStatic) {
281
+ this.wildcard_vectors[number][0] = VM.NOT_COMPUTED;
282
+ } else {
279
283
  MODEL.cleanVector(this.wildcard_vectors[number], VM.NOT_COMPUTED);
280
284
  }
281
- return this.wildcard_vectors[number];
282
285
  }
286
+ return this.wildcard_vectors[number];
283
287
  }
284
288
 
285
289
  compute(t, number=false) {
286
- // Executes the VM code for this expression for time step t
287
- // NOTE: `number` is passed (as integer) only if context for # is defined
290
+ // Executes the VM code for this expression for time step `t`.
291
+ // NOTE: `number` is passed only if context for # is defined.
288
292
  if(!this.compiled) this.compile();
289
- // Return FALSE if compilation resulted in error
293
+ // Return FALSE if compilation resulted in error.
290
294
  if(!this.compiled) return false;
291
- // Compute static expressions as if t = 0
295
+ // Compute static expressions as if t = 0.
292
296
  if(t < 0 || this.isStatic) t = 0;
293
- // Select the vector to use
297
+ // Select the vector to use.
294
298
  const v = this.chooseVector(number);
295
- // Check for potential error (that should NOT occur)
296
- if(!v || v.length === 0 || t >= v.length) {
299
+ // Check for potential error (that should NOT occur).
300
+ if(!Array.isArray(v) || v.length === 0 || t >= v.length) {
297
301
  const msg = 'ERROR: Undefined value during expression evaluation';
298
302
  UI.alert(msg);
299
303
  console.log(this.variableName, ':', this.text, '#', number, '@', t, v);
300
- // Throw exception to permit viewing the function call stack
304
+ // Throw exception to permit viewing the function call stack.
301
305
  throw msg;
302
306
  }
303
307
  // When called while already computing for time step t, signal this
304
308
  // as an error value.
305
309
  if(v[t] === VM.COMPUTING) v[t] = VM.CYCLIC;
306
- // Compute a value only once
307
- if(v[t] !== VM.NOT_COMPUTED) return true;
308
- // Provide selector context for # (number = FALSE => no wildcard match)
310
+ // Compute a value only once.
311
+ if(v[t] !== VM.NOT_COMPUTED) {
312
+ if(DEBUGGING) console.log('Already computed', this.variableName,
313
+ ':', this.text, '#', number, '@', t, v[t]);
314
+ return true;
315
+ }
316
+ // Provide selector context for # (number = FALSE => no wildcard match).
309
317
  this.wildcard_vector_index = number;
310
- // Push this expression onto the call stack
318
+ // Push this expression onto the call stack.
311
319
  VM.call_stack.push(this);
312
- // Push time step in case a VMI_push_var instruction references
313
- // this same variable
314
- this.trace('--START: ' + this.variableName);
320
+ // Push time step in case a VMI instruction for another expression
321
+ // references this same variable.
322
+ this.trace(`--START: ${this.variableName} (wvi = ${number})`);
315
323
  this.step.push(t);
316
- // NOTE: trace extression AFTER pushing the time step
324
+ // NOTE: Trace expression AFTER pushing the time step.
317
325
  this.trace(`"${this.text}"`);
318
326
  v[t] = VM.COMPUTING;
319
- // Execute the instructions
327
+ // Execute the instructions.
320
328
  let vmi = null,
321
329
  ok = true,
322
330
  cl = this.code.length;
@@ -325,13 +333,13 @@ class Expression {
325
333
  this.stack.length = 0;
326
334
  while(ok && this.program_counter < cl && v[t] === VM.COMPUTING) {
327
335
  vmi = this.code[this.program_counter];
328
- // Instructions are 2-element arrays [function, [arguments]];
329
- // the function is called with this expression as first parameter,
330
- // and the argument list as second parameter
336
+ // Instructions are 2-element arrays [function, [arguments]].
337
+ // The function is called with this expression as first parameter,
338
+ // and the argument list as second parameter.
331
339
  vmi[0](this, vmi[1]);
332
340
  this.program_counter++;
333
341
  }
334
- // Stack should now have length 1
342
+ // Stack should now have length 1.
335
343
  if(this.stack.length > 1) {
336
344
  v[t] = VM.OVERFLOW;
337
345
  } else if(this.stack.length < 1) {
@@ -340,19 +348,20 @@ class Expression {
340
348
  v[t] = this.stack.pop();
341
349
  }
342
350
  this.trace('RESULT = ' + VM.sig4Dig(v[t]));
343
- // Pop the time step
351
+ // Pop the time step.
344
352
  this.step.pop();
345
353
  this.trace('--STOP: ' + this.variableName);
346
- // Clear context for #
354
+ // Clear context for # for this expression (no stack needed, as
355
+ // wildcard expressions cannot reference themselves).
347
356
  this.wildcard_vector_index = false;
348
- // If error, display the call stack (only once)
357
+ // If error, display the call stack (only once).
349
358
  // NOTE: "undefined", "not computed" and "still computing" are NOT
350
359
  // problematic unless they result in an error (stack over/underflow)
351
360
  if(v[t] <= VM.ERROR) {
352
361
  MONITOR.showCallStack(t);
353
362
  VM.logCallStack(t);
354
363
  }
355
- // Always pop the expression from the call stack
364
+ // Always pop the expression from the call stack.
356
365
  VM.call_stack.pop(this);
357
366
  return true;
358
367
  }
@@ -365,7 +374,10 @@ class Expression {
365
374
  // "initial value" (these follow from the variables used in the expression)
366
375
  // Select the vector to use
367
376
  const v = this.chooseVector(number);
368
- if(!v) return VM.UNDEFINED;
377
+ if(!Array.isArray(v)) {
378
+ console.log('ANOMALY: No vector for result(t)');
379
+ return VM.UNDEFINED;
380
+ }
369
381
  if(t < 0 || this.isStatic) t = 0;
370
382
  if(t >= v.length) return VM.UNDEFINED;
371
383
  if(v[t] === VM.NOT_COMPUTED || v[t] === VM.COMPUTING) {
@@ -532,12 +544,9 @@ class Expression {
532
544
  // using the variable [partial load 1] to compute the partial load for
533
545
  // process P1.
534
546
  // NOTES:
535
- // (1) This will (for now) NOT apply recursively, so [partial load #] cannot
536
- // be used in some other wildcard equation like, for example, "percent load ??"
537
- // having expression "100 * [partial load #]". This restriction follows from
538
- // the present limitation that only ONE context-sensitive number exists, which
539
- // makes that "nested" wildcard expressions can always be rewritten as a single
540
- // expression.
547
+ // (1) This applies recursively, so [partial load #] can be used in some other
548
+ // wildcard equation like, for example, "percent load ??" having expression
549
+ // "100 * [partial load #]".
541
550
  // (2) The # may be used in patterns, so when a model comprises processes
542
551
  // P1 and P2, and products Q2 and Q3, and a wildcard equation "total level ??"
543
552
  // with expression "[SUM$#|L]", then [total level 1] will evaluate as the level
@@ -545,6 +554,8 @@ class Expression {
545
554
 
546
555
  class ExpressionParser {
547
556
  constructor(text, owner=null, attribute='') {
557
+ // Setting TRACE to TRUE will log parsing information to the console.
558
+ this.TRACE = false;
548
559
  // `text` is the expression string to be parsed.
549
560
  this.expr = text;
550
561
  // NOTE: When expressions for dataset modifiers or equations are
@@ -557,11 +568,12 @@ class ExpressionParser {
557
568
  this.selector = '';
558
569
  this.context_number = '';
559
570
  this.wildcard_selector = false;
560
- this.wildcard_equation = false;
561
571
  // Always infer the value for the context-sensitive number #.
562
- // NOTE: this will be "?" if `owner` is a dataset modifier with
563
- // wildcards in its selector; this indicates that the value of #
564
- // cannot be inferred at compile time.
572
+ // NOTE: This this will always be a string. Three possible cases:
573
+ // (1) a question mark "?" if `owner` is a dataset and `attribute`
574
+ // wildcards in its selector; this indicates that the value of # cannot be inferred at
575
+ // compile time.
576
+ //
565
577
  if(owner) {
566
578
  this.context_number = owner.numberContext;
567
579
  // NOTE: The owner prefix includes the trailing colon+space.
@@ -578,19 +590,34 @@ class ExpressionParser {
578
590
  if(owner instanceof Dataset) {
579
591
  this.dataset = owner;
580
592
  // The attribute (if specified) is a dataset modifier selector.
593
+ // This may be the name of an equation; this can be tested by
594
+ // checking whether the owner is the equations dataset.
581
595
  this.selector = attribute;
582
- // Record whether this selector contains wildcards.
596
+ // Record whether this selector contains wildcards (? and/or *
597
+ // for selectors, ?? for equations).
583
598
  this.wildcard_selector = owner.isWildcardSelector(attribute);
584
- if(owner === MODEL.equations_dataset) {
585
- // Reocrd that the owner is a wildcard equation.
586
- this.wildcard_equation = this.wildcard_selector;
587
- } else {
599
+ if(this.wildcard_selector) {
600
+ // NOTE: Wildcard selectors override the context number that
601
+ // may have been inferred from the dataset name.
602
+ this.context_number = '?';
603
+ } else {
604
+ // Normal selectors may have a "tail number". If so, this
605
+ // overrides the tail number of the dataset.
606
+ const tn = UI.tailNumber(attribute);
607
+ if(tn) this.context_number = tn;
608
+ }
609
+ if(owner !== MODEL.equations_dataset) {
588
610
  // For "normal" modifier expressions, the "dot" (.) can be used
589
611
  // to refer to the dataset of the modifier.
590
612
  this.dot = this.dataset;
591
613
  }
592
614
  }
593
615
  }
616
+ // Ensure that context number is either '?' or a number or FALSE.
617
+ if(this.context_number !== '?') {
618
+ this.context_number = parseInt(this.context_number);
619
+ if(isNaN(this.context_number)) this.context_number = false;
620
+ }
594
621
  // Immediately compile; this may generate warnings
595
622
  this.compile();
596
623
  }
@@ -601,14 +628,18 @@ class ExpressionParser {
601
628
  let n = this.owner.displayName;
602
629
  if(this.attribute) n += '|' + this.attribute;
603
630
  if(this.wildcard_selector) {
604
- n += ' [wildcard ' + (this.wildcard_equation ? 'equation' : 'modifier') +
605
- (this.context_number ? ' # = ' + this.context_number : '') + ']';
631
+ n = [n, ' [wildcard ',
632
+ (this.dataset === MODEL.equations_dataset ?
633
+ 'equation' : 'modifier'),
634
+ (this.context_number !== false ?
635
+ ' # = ' + this.context_number : ''),
636
+ ']'].join('');
606
637
  }
607
638
  return n;
608
639
  }
609
640
 
610
641
  log(msg) {
611
- // FOR TRACING & DEBUGGING: Logs a message to the console.
642
+ // NOTE: This method is used only to profile dynamic expressions.
612
643
  if(true) return;
613
644
  // Set the above IF condition to FALSE to profile dynamic expressions.
614
645
  console.log(`Expression for ${this.ownerName}: ${this.expr}\n${msg}`);
@@ -630,6 +661,14 @@ class ExpressionParser {
630
661
  parseVariable(name) {
631
662
  // Reduce whitespace to single space.
632
663
  name = name.replace(/\s+/g, ' ');
664
+
665
+ // For debugging, TRACE can be used to log to the console for
666
+ // specific expressions and/or variables, for example:
667
+ // this.TRACE = name.endsWith('losses') || this.ownerName.endsWith('losses');
668
+ if(this.TRACE) console.log(
669
+ `TRACE: Parsing variable "${name}" in expression for`,
670
+ this.ownerName, ' --> ', this.expr);
671
+
633
672
  // Initialize possible components.
634
673
  let obj = null,
635
674
  attr = '',
@@ -640,6 +679,7 @@ class ExpressionParser {
640
679
  anchor2 = '',
641
680
  offset2 = 0,
642
681
  msg = '',
682
+ arg0 = null,
643
683
  args = null,
644
684
  s = name.split('@');
645
685
  if(s.length > 1) {
@@ -673,14 +713,14 @@ class ExpressionParser {
673
713
  // t (current time step, this is the default),
674
714
  if('#cfijklnprst'.includes(offs[0].charAt(0))) {
675
715
  anchor1 = offs[0].charAt(0);
676
- offset1 = safeStrToInt(offs[0].substr(1));
716
+ offset1 = safeStrToInt(offs[0].substring(1));
677
717
  } else {
678
718
  offset1 = safeStrToInt(offs[0]);
679
719
  }
680
720
  if(offs.length > 1) {
681
721
  if('#cfijklnprst'.includes(offs[1].charAt(0))) {
682
722
  anchor2 = offs[1].charAt(0);
683
- offset2 = safeStrToInt(offs[1].substr(1));
723
+ offset2 = safeStrToInt(offs[1].substring(1));
684
724
  } else {
685
725
  offset2 = safeStrToInt(offs[1]);
686
726
  }
@@ -691,7 +731,7 @@ class ExpressionParser {
691
731
  }
692
732
  // Check whether # anchor is meaningful for this expression
693
733
  if((anchor1 === '#' || anchor2 === '#') &&
694
- !(this.wildcard_selector || this.context_number)) {
734
+ !(this.wildcard_selector || this.context_number !== false)) {
695
735
  // Log debugging information for this error
696
736
  console.log(this.owner.displayName, this.owner.type, this.selector);
697
737
  this.error = 'Anchor # is undefined in this context';
@@ -717,7 +757,7 @@ class ExpressionParser {
717
757
  };
718
758
  // NOTE: name should then be in the experiment's variable list
719
759
  name = s[1].trim();
720
- s = s[0].substr(1);
760
+ s = s[0].substring(1);
721
761
  // Check for scaling method
722
762
  // NOTE: simply ignore $ unless it indicates a valid method
723
763
  const msep = s.indexOf('$');
@@ -805,7 +845,8 @@ class ExpressionParser {
805
845
  if(x.x) {
806
846
  // Look up name in experiment outcomes list
807
847
  x.v = x.x.resultIndex(name);
808
- if(x.v < 0 && name.indexOf('#') >= 0 && this.context_number) {
848
+ if(x.v < 0 && name.indexOf('#') >= 0 &&
849
+ typeof this.context_number === 'number') {
809
850
  // Variable name may be parametrized with #, but not in
810
851
  // expressions for wildcard selectors
811
852
  name = name.replace('#', this.context_number);
@@ -819,9 +860,10 @@ class ExpressionParser {
819
860
  // Check outcome list of ALL experiments
820
861
  for(let i = 0; i < MODEL.experiments.length; i++) {
821
862
  let xri = MODEL.experiments[i].resultIndex(name);
822
- if(xri < 0 && name.indexOf('#') >= 0 && this.context_number) {
863
+ if(xri < 0 && name.indexOf('#') >= 0 &&
864
+ typeof this.context_number === 'number') {
823
865
  // Variable name may be parametrized with #, but not in
824
- // expressions for wildcard selectors
866
+ // expressions for wildcard selectors.
825
867
  name = name.replace('#', this.context_number);
826
868
  xri = MODEL.experiments[i].resultIndex(name);
827
869
  }
@@ -859,6 +901,7 @@ class ExpressionParser {
859
901
  // For experiment run results, default anchor is 't'.
860
902
  if(!anchor1) anchor1 = 't';
861
903
  if(!anchor2) anchor2 = 't';
904
+ if(this.TRACE) console.log('TRACE: Variable is run result. x =', x);
862
905
  // NOTE: compiler will recognize `x` to indicate "push run results".
863
906
  return [x, anchor1, offset1, anchor2, offset2];
864
907
  }
@@ -899,7 +942,7 @@ class ExpressionParser {
899
942
  let pat = name.split('$');
900
943
  if(pat.length > 1 &&
901
944
  VM.statistic_operators.indexOf(pat[0].toUpperCase()) >= 0) {
902
- // For statistics, default anchor is 't'.
945
+ // For statistics, the default anchor is 't'.
903
946
  if(!anchor1) anchor1 = 't';
904
947
  if(!anchor2) anchor2 = 't';
905
948
  // Check whether unit balance for clusters is asked for.
@@ -916,27 +959,37 @@ class ExpressionParser {
916
959
  // NOTE: The "dot" dataset is not level-dependent, and statistics
917
960
  // over its vector do NOT make the expression dynamic.
918
961
  if(this.dot) {
919
- return [stat, [this.dot.vector], anchor1, offset1, anchor2, offset2];
962
+ args = [stat, [this.dot.vector], anchor1, offset1, anchor2, offset2];
963
+ if(this.TRACE) console.log('TRACE: Variable is a statistic:', args);
964
+ return args;
920
965
  } else {
921
966
  this.error = UI.ERROR.NO_DATASET_DOT;
922
967
  return false;
923
968
  }
924
969
  }
970
+ /*
971
+ // DEPRECATED -- Modeler can deal with this by smartly using AND
972
+ // clauses like "&x: &y:" to limit set to specific prefixes.
973
+
925
974
  // Deal with "prefix inheritance" when pattern starts with a colon.
926
- if(pat.startsWith(':')) {
975
+ if(pat.startsWith(':') && this.owner_prefix) {
927
976
  // Add a "must start with" AND condition to all OR clauses of the
928
977
  // pattern.
929
- // NOTE: Issues may occur when prefix contains &, ^ or # (@@TO DO?)
978
+ // NOTE: Issues may occur when prefix contains &, ^ or #.
979
+ // @@TO DO: See if this can be easily prohibited.
930
980
  const oc = pat.substring(1).split('|');
931
981
  for(let i = 0; i < oc.length; i++) {
932
982
  oc[i] = `~${this.owner_prefix}&${oc[i]}`;
933
983
  }
934
984
  pat = oc.join('|');
935
985
  }
986
+ */
936
987
  // NOTE: For patterns, assume that # *always* denotes the context-
937
988
  // sensitive number #, because if modelers wishes to include
938
989
  // ANY number, they can make their pattern less selective.
939
- if(this.context_number) pat = pat.replace('#', this.context_number);
990
+ if(typeof this.context_number === 'number') {
991
+ pat = pat.replace('#', this.context_number);
992
+ }
940
993
  // By default, consider all entity types.
941
994
  let et = VM.entity_letters,
942
995
  patstr = pat;
@@ -956,7 +1009,7 @@ class ExpressionParser {
956
1009
  // returned list to the specified entity types.
957
1010
  ewa = MODEL.entitiesWithAttribute(attr, et);
958
1011
  // Create list of expression objects for the matching entities.
959
- // Also create a "dict" with for each matching wildcard number
1012
+ // Also create a "dict" with, for each matching wildcard number,
960
1013
  // the matching entities as a separate list. This will permit
961
1014
  // narrowing the selection at run time, based on the expression's
962
1015
  // wildcard number.
@@ -1002,16 +1055,18 @@ class ExpressionParser {
1002
1055
  }
1003
1056
  }
1004
1057
  if(list.length > 0) {
1005
- // NOTE: statistic MAY make expression level-based
1006
- // NOTE: assume NOT when offset has been specified, as this suggests
1007
- // that modelers know what they're doing
1058
+ // NOTE: Statistic MAY make expression level-based.
1059
+ // Assume that this is NOT so when an offset has been specified,
1060
+ // as this suggests that modelers know what they're doing.
1008
1061
  this.is_level_based = this.is_level_based ||
1009
1062
  VM.level_based_attr.indexOf(attr) >= 0 &&
1010
1063
  anchor1 === 't' && offset1 === 0 &&
1011
1064
  anchor2 === 't' && offset2 === 0;
1012
- const args = [stat, list, anchor1, offset1, anchor2, offset2];
1065
+ args = [stat, list, anchor1, offset1, anchor2, offset2];
1013
1066
  if(Object.keys(wdict).length > 0) args.push(wdict);
1014
- // NOTE: Compiler will recognize 6- or 7-element list as a "statistic"
1067
+ if(this.TRACE) console.log('TRACE: Variable is a statistic:', args);
1068
+ // NOTE: Compiler will recognize 6- or 7-element list as a
1069
+ // sign to use the VMI_push_statistic instruction.
1015
1070
  return args;
1016
1071
  }
1017
1072
  this.error = `No entities that match pattern "${patstr}"` +
@@ -1045,6 +1100,7 @@ class ExpressionParser {
1045
1100
  this.log('dynamic because of self-reference');
1046
1101
  if(('cips'.indexOf(anchor1) >= 0 || anchor1 === 't' && offset1 < 0) &&
1047
1102
  ('cips'.indexOf(anchor2) >= 0 ||anchor2 === 't' && offset2 < 0)) {
1103
+ if(this.TRACE) console.log('TRACE: Variable is a self-reference.');
1048
1104
  // The `xv` attribute will be recognized by VMI_push_var to denote
1049
1105
  // "use the vector of the expression for which this VMI is code".
1050
1106
  return [{xv: true, dv: this.dataset.defaultValue},
@@ -1080,6 +1136,9 @@ class ExpressionParser {
1080
1136
  id = UI.nameToID(name),
1081
1137
  w = MODEL.wildcardEquationByID(id);
1082
1138
  if(w) {
1139
+ if(this.TRACE) console.log('TRACE: Variable is a wildcard equation:',
1140
+ w[0], '-- number is', w[1], '\nTRACE: Equation expression: ',
1141
+ w[0].expression.text);
1083
1142
  // Variable matches wildcard equation w[0] with number w[1],
1084
1143
  // so this equation must be evaluated for that number.
1085
1144
  return [
@@ -1089,11 +1148,13 @@ class ExpressionParser {
1089
1148
  // If no match, try to match the object ID with any type of entity.
1090
1149
  obj = MODEL.objectByID(id);
1091
1150
  }
1092
- // If not, retry if wildcards can be substituted.
1151
+ // If not, try whether wildcards can be substituted.
1093
1152
  if(!obj && name.indexOf('#') >= 0) {
1094
- if(this.context_number) {
1153
+ if(typeof this.context_number === 'number') {
1095
1154
  obj = MODEL.objectByName(name.replace('#', this.context_number));
1096
1155
  }
1156
+ if(obj && TRACE) console.log('TRACE: Matched ', name,
1157
+ 'with entity:', obj.displayName);
1097
1158
  if(!obj) {
1098
1159
  // If immediate substitution of # does not identify an entity,
1099
1160
  // then name may still refer to a wildcard equation.
@@ -1104,9 +1165,9 @@ class ExpressionParser {
1104
1165
  } else {
1105
1166
  obj = MODEL.equationByID(UI.nameToID(wcname));
1106
1167
  if(obj) {
1107
- // Special case: parsed variable references a wildcard
1108
- // equation (so `obj` is an instance of DatasetModifier).
1109
- if(!(this.wildcard_equation || this.context_number)) {
1168
+ // Special case: the parsed variable references a wildcard
1169
+ // equation, so now `obj` is an instance of DatasetModifier.
1170
+ if(!(this.wildcard_selector || this.context_number)) {
1110
1171
  msg = UI.ERROR.NO_NUMBER_CONTEXT;
1111
1172
  } else {
1112
1173
  // Acceptable reference to a wildcard equation.
@@ -1117,23 +1178,13 @@ class ExpressionParser {
1117
1178
  // NOTE: The referenced expression may be level-dependent.
1118
1179
  this.is_level_based = this.is_level_based ||
1119
1180
  obj.expression.is_level_based;
1120
- // NOTE: Pass a question mark "?" as selector when one
1121
- // wildcard equation references another; otherwise, pass
1122
- // the context number as integer.
1123
- let cnr;
1124
- if(this.wildcard_equation) {
1125
- cnr = '?';
1126
- } else {
1127
- cnr = parseInt(this.context_number);
1128
- // NOTE: Let script break if context number is not numeric.
1129
- if(isNaN(cnr) ) throw ['FATAL ERROR in expression for ',
1130
- this.ownerName, ' while parsing variable "', name,
1131
- '"\nContext number "', this.context_number,
1132
- '" is not a number\nExpression: ', this.expr].join('');
1133
- }
1181
+ if(this.TRACE) console.log('TRACE: Variable ', name,
1182
+ 'is a wildcard equation:', obj.displayName,
1183
+ '-- number is:', this.context_number,
1184
+ '\nTRACE: Expression:', obj.expression.text);
1134
1185
  // Use the context number as "selector" parameter of the VMI.
1135
1186
  return [
1136
- {d: obj.dataset, s: cnr, x: obj.expression},
1187
+ {d: obj.dataset, s: this.context_number, x: obj.expression},
1137
1188
  anchor1, offset1, anchor2, offset2];
1138
1189
  }
1139
1190
  }
@@ -1153,8 +1204,10 @@ class ExpressionParser {
1153
1204
  // at run time the VM can match with the value of #.
1154
1205
  // NOTE: Also pass whether the entity should be pushed
1155
1206
  // "by reference".
1156
- return [
1157
- {ee: ame, n: name, a: uca, br: by_reference},
1207
+ if(this.TRACE) console.log('TRACE: Variable', name,
1208
+ 'matches with tail-numbered entities:', ame,
1209
+ '\nTRACE: Attribute used:', uca);
1210
+ return [{n: name, ee: ame, a: uca, br: by_reference},
1158
1211
  anchor1, offset1, anchor2, offset2];
1159
1212
  }
1160
1213
  // Wildcard selector, but no number context for #.
@@ -1205,9 +1258,12 @@ class ExpressionParser {
1205
1258
  }
1206
1259
  // If `obj` is a dataset *modifier*, it must be a "normal" equation...
1207
1260
  if(obj instanceof DatasetModifier) {
1261
+ if(this.TRACE) console.log('TRACE: Dataset modifier "' + obj.displayName +
1262
+ '" mapped to dataset:', obj.dataset.name,
1263
+ 'and selector:', obj.selector);
1208
1264
  // ... so "map" it onto the equations dataset + selector...
1209
1265
  attr = obj.selector;
1210
- obj = MODEL.equations_dataset;
1266
+ obj = obj.dataset;
1211
1267
  }
1212
1268
  // ... so now it will be processed the same way dataset modifiers
1213
1269
  // are processed, especially when they have a tail number.
@@ -1224,30 +1280,40 @@ class ExpressionParser {
1224
1280
 
1225
1281
  // If "by reference", return the object itself plus its attribute
1226
1282
  if(by_reference) {
1283
+ if(this.TRACE) console.log('TRACE: Variable is a reference to',
1284
+ obj.displayName, '. Attribute:', attr);
1227
1285
  return [{r: obj, a: attr}, anchor1, offset1, anchor2, offset2];
1228
1286
  }
1229
1287
  if(obj === this.dataset && attr === '' && !obj.array) {
1230
1288
  // When dataset modifier expression refers to its dataset without
1231
1289
  // selector, then this is equivalent to [.] (use the series data
1232
1290
  // vector) unless it is an array, since then the series data is
1233
- // not a time-scaled vector => special case
1234
- args = obj.vector;
1291
+ // not a time-scaled vector => special case.
1292
+ if(this.TRACE) console.log(
1293
+ 'TRACE: Dataset without selector, no array:', obj.displayName,
1294
+ 'Use vector:', obj.vector);
1295
+ arg0 = obj.vector;
1235
1296
  } else if(attr === '') {
1236
1297
  // For all other variables, assume default attribute if none specified
1237
1298
  attr = obj.defaultAttribute;
1238
1299
  // For a dataset, check whether the VMI_push_dataset_modifier should be
1239
1300
  // used. This is the case for array-type datasets, and for datasets
1240
1301
  // having modifiers UNLESS the modeler used a vertical bar to indicate
1241
- // "use the data"
1302
+ // "use the data".
1242
1303
  if(obj instanceof Dataset &&
1243
1304
  (obj.array || (!use_data && obj.selectorList.length > 0))) {
1305
+ // No explicit selector means that this variable is dynamic if
1306
+ // the dataset has time series data, or if some of its modifier
1307
+ // expressions are dynamic.
1244
1308
  if(obj.data.length > 1 || (obj.data.length > 0 && !obj.periodic) ||
1245
1309
  !obj.allModifiersAreStatic) {
1246
- // No explicit selector => dynamic unless no time series data, and
1247
- // ALL modifier expressions are static
1248
1310
  this.is_static = false;
1249
1311
  this.log('dynamic because dataset without explicit selector is used');
1250
1312
  }
1313
+ if(this.TRACE) console.log(
1314
+ 'TRACE: Dataset without explicit selector:',
1315
+ (obj.array ? 'array' : 'has modifiers'), obj.displayName,
1316
+ '\nTRACE: Use VMI_push_dataset_modifier; use-data flag:', use_data);
1251
1317
  // NOTE: Also pass the "use data" flag so that experiment selectors
1252
1318
  // will be ignored if the modeler coded the vertical bar.
1253
1319
  return [{d: obj, ud: use_data}, anchor1, offset1, anchor2, offset2];
@@ -1269,7 +1335,7 @@ class ExpressionParser {
1269
1335
  this.is_static = false;
1270
1336
  this.log('dynamic because dataset modifier expression is dynamic');
1271
1337
  }
1272
- // NOTE: a single match may be due to wildcard(s) in the modifier,
1338
+ // NOTE: A single match may be due to wildcard(s) in the modifier,
1273
1339
  // e.g., a variable [dataset|abc] matches with a modifier having
1274
1340
  // wildcard selector "a?b", or [dataset|a12] matches with "a*".
1275
1341
  // In such cases, if the selector matches an integer like "a12"
@@ -1278,26 +1344,31 @@ class ExpressionParser {
1278
1344
  // for [datset34|a12], the number context is '12' and not '34').
1279
1345
  let mcn = matchingNumber(attr, m.selector);
1280
1346
  if(mcn === false) {
1281
- // NOTE: When no matching number if found, `attr` may still
1347
+ // NOTE: When no matching number is found, `attr` may still
1282
1348
  // contain a ?? wildcard. If it indeed identifies a wildcard
1283
1349
  // equation, then "?" should be passed to the VM instruction.
1284
1350
  if(obj === MODEL.equations_dataset && attr.indexOf('??') >= 0) {
1285
1351
  mcn = '?';
1286
1352
  } else {
1287
1353
  // Ensure that `mcn` is either an integer value or FALSE.
1288
- mcn = endsWithDigits(obj.name) || this.context_number || false;
1354
+ mcn = parseInt(UI.tailNumber(obj.name)) || this.context_number;
1289
1355
  }
1290
1356
  }
1291
- // Pass the dataset, the context number # (or FALSE) in place, and the
1292
- // modifier expression
1357
+ // Pass the dataset, the context number # (or FALSE) in place,
1358
+ // and the modifier expression.
1359
+ if(this.TRACE) console.log('TRACE: Variable is',
1360
+ (m.dataset === MODEL.equations_dataset ?
1361
+ 'an equation: ' + m.selector :
1362
+ 'a dataset with explicit selector: ' + m.displayName),
1363
+ '\nTRACE: Context number:', mcn, ' Expression:', m.expression.text);
1293
1364
  return [
1294
1365
  {d: m.dataset, s: mcn, x: m.expression},
1295
1366
  anchor1, offset1, anchor2, offset2];
1296
1367
  }
1297
1368
  }
1298
- // NOTE: `args` can now be a single value, a vector, or NULL.
1299
- if(args === null) args = obj.attributeValue(attr);
1300
- if(Array.isArray(args)) {
1369
+ // NOTE: `arg0` can now be a single value, a vector, or NULL.
1370
+ if(arg0 === null) arg0 = obj.attributeValue(attr);
1371
+ if(Array.isArray(arg0)) {
1301
1372
  if(obj instanceof Dataset) {
1302
1373
  if(obj.data.length > 1 || obj.data.length > 0 && !obj.periodic) {
1303
1374
  this.is_static = false;
@@ -1307,23 +1378,25 @@ class ExpressionParser {
1307
1378
  this.is_static = false;
1308
1379
  this.log('dynamic because level-based attribute');
1309
1380
  } else {
1310
- // Unusual (?) combi, so let's assume dynamic
1381
+ // Unusual (?) combi, so let's assume dynamic.
1311
1382
  this.is_static = false;
1312
1383
  this.log('probably dynamic -- check below:');
1313
- console.log(obj.displayName, obj, attr, args);
1384
+ console.log('ANOMALY: array for', obj.displayName, obj, attr, arg0);
1314
1385
  }
1386
+ if(this.TRACE) console.log('TRACE: arg[0] is a vector');
1315
1387
  }
1316
- // If not a single value or vector, it must be an expression
1317
- if(args === null) args = obj.attributeExpression(attr);
1318
- if(args === null) {
1319
- // Only NOW check whether unit balance for clusters is asked for
1388
+ // If not a single value or vector, it must be an expression.
1389
+ if(arg0 === null) arg0 = obj.attributeExpression(attr);
1390
+ if(arg0 === null) {
1391
+ // Only NOW check whether unit balance for clusters is asked for.
1320
1392
  if(cluster_balance_unit !== false && obj instanceof Cluster) {
1321
- // NOTE: cluster balance ALWAYS makes expression level-based
1393
+ // NOTE: Cluster balance ALWAYS makes expression level-based
1394
+ // and dynamic.
1322
1395
  this.is_level_based = true;
1323
- // @TO DO: rethink whether this will indeed also make this expression
1324
- // dynamic
1325
1396
  this.is_static = false;
1326
- this.log('dynamic because cluster balance is believed to be dynamic');
1397
+ this.log('dynamic because cluster balance is level-based');
1398
+ if(this.TRACE) console.log('TRACE: Variable is a balance:',
1399
+ cluster_balance_unit, 'for cluster', obj.displayName);
1327
1400
  // NOTE: VM instructions VMI_push_var will recognize this special case
1328
1401
  return [{c: obj, u: cluster_balance_unit},
1329
1402
  anchor1, offset1, anchor2, offset2];
@@ -1331,26 +1404,31 @@ class ExpressionParser {
1331
1404
  // Fall-through: invalid attribute for this object
1332
1405
  msg = `${obj.type} entities have no attribute "${attr}"`;
1333
1406
  } else {
1334
- if(args instanceof Expression) {
1335
- this.is_static = this.is_static && args.isStatic;
1407
+ if(arg0 instanceof Expression) {
1408
+ this.is_static = this.is_static && arg0.isStatic;
1336
1409
  }
1337
- args = [args, anchor1, offset1, anchor2, offset2];
1410
+ if(this.TRACE) console.log('TRACE: arg[0] is the expression for',
1411
+ arg0.variableName, '\nTRACE: Expression:', arg0.text);
1412
+ args = [arg0, anchor1, offset1, anchor2, offset2];
1338
1413
  }
1339
1414
  if(msg) {
1340
1415
  this.error = msg;
1341
1416
  return false;
1342
1417
  }
1343
1418
  // Now `args` should be a valid argument for a VM instruction that
1344
- // pushes an operand on the evaluation stack
1345
- // Check whether the attribute is level-based (i.e., can be computed only
1346
- // after optimizing a block) while no offset is defined to use prior data
1419
+ // pushes an operand on the evaluation stack.
1420
+ // Check whether the attribute is level-based (i.e., can be computed
1421
+ // only after optimizing a block) while no offset is defined to use
1422
+ // prior data.
1347
1423
  this.is_level_based = this.is_level_based ||
1348
- // NOTE: dataset modifier expressions may be level_based
1349
- obj instanceof Dataset && attr && args[0].is_level_based ||
1350
- // NOTE: assume NOT level_based if anchor & offset are specified
1351
- VM.level_based_attr.indexOf(attr) >= 0 &&
1424
+ // NOTE: Dataset modifier expressions may be level-based.
1425
+ (obj instanceof Dataset && attr && arg0.is_level_based) ||
1426
+ // Assume NOT level-based if anchor & offset are specified.
1427
+ // NOTE: This is based on the assumption that advanced modelers
1428
+ // know what they are doing.
1429
+ (VM.level_based_attr.indexOf(attr) >= 0 &&
1352
1430
  anchor1 === 't' && offset1 === 0 &&
1353
- anchor2 === 't' && offset2 === 0;
1431
+ anchor2 === 't' && offset2 === 0);
1354
1432
  return args;
1355
1433
  }
1356
1434
 
@@ -1376,7 +1454,7 @@ class ExpressionParser {
1376
1454
  this.los = 1;
1377
1455
  this.error = 'Missing closing bracket \']\'';
1378
1456
  } else {
1379
- v = this.expr.substr(this.pit + 1, i - 1 - this.pit);
1457
+ v = this.expr.substring(this.pit + 1, i);
1380
1458
  this.pit = i + 1;
1381
1459
  // NOTE: Enclosing brackets are also part of this symbol
1382
1460
  this.los = v.length + 2;
@@ -1394,7 +1472,7 @@ class ExpressionParser {
1394
1472
  this.los = 1;
1395
1473
  this.error = 'Unmatched quote';
1396
1474
  } else {
1397
- v = this.expr.substr(this.pit + 1, i - 1 - this.pit);
1475
+ v = this.expr.substring(this.pit + 1, i);
1398
1476
  this.pit = i + 1;
1399
1477
  // NOTE: Enclosing quotes are also part of this symbol
1400
1478
  this.los = v.length + 2;
@@ -1447,7 +1525,7 @@ class ExpressionParser {
1447
1525
  this.los++;
1448
1526
  }
1449
1527
  // ... but trim spaces from the symbol
1450
- v = this.expr.substr(this.pit, this.los).trim();
1528
+ v = this.expr.substring(this.pit, this.pit + this.los).trim();
1451
1529
  // Ignore case
1452
1530
  l = v.toLowerCase();
1453
1531
  if(l === '#') {
@@ -1688,6 +1766,7 @@ class ExpressionParser {
1688
1766
  // Either a statistic, a dataset (array-type or with modifier),
1689
1767
  // an experiment run result, or a variable.
1690
1768
  if(this.sym.length >= 6) {
1769
+ // 6 or 7 arguments indicates a statistic.
1691
1770
  this.code.push([VMI_push_statistic, this.sym]);
1692
1771
  } else if(this.sym[0].hasOwnProperty('d')) {
1693
1772
  this.code.push([VMI_push_dataset_modifier, this.sym]);
@@ -1724,7 +1803,7 @@ class ExpressionParser {
1724
1803
  this.error = 'Invalid parameter list';
1725
1804
  }
1726
1805
  }
1727
- if(DEBUGGING) console.log('PARSED', this.ownerName, ':',
1806
+ if(this.TRACE || DEBUGGING) console.log('PARSED', this.ownerName, ':',
1728
1807
  this.expr, this.code);
1729
1808
  }
1730
1809
 
@@ -5174,7 +5253,7 @@ Solver status = ${json.status}`);
5174
5253
  // Show the reset button (GUI only)
5175
5254
  UI.readyToReset();
5176
5255
  // If receiver is active, report results
5177
- if(RECEIVER.solving) RECEIVER.report();
5256
+ if(RECEIVER.solving || MODEL.report_results) RECEIVER.report();
5178
5257
  // If experiment is active, signal the manager
5179
5258
  if(MODEL.running_experiment) EXPERIMENT_MANAGER.processRun();
5180
5259
  // Warn modeler if any issues occurred
@@ -5796,10 +5875,9 @@ function VMI_push_wildcard_entity(x, args) {
5796
5875
  // n: name (with wildcard #), a: attribute, br: by reference (boolean)}
5797
5876
  // First select the first entity in `ee` that matches the wildcard vector
5798
5877
  // index of the expression `x` being executed.
5799
- const
5800
- el = args[0].ee,
5801
- nn = args[0].n.replace('#', x.wildcard_vector_index);
5802
- let obj = null;
5878
+ const el = args[0].ee;
5879
+ let nn = args[0].n.replace('#', x.wildcard_vector_index),
5880
+ obj = null;
5803
5881
  for(let i = 0; !obj && i < el.length; i++) {
5804
5882
  if(el[i].name === nn) obj = el[i];
5805
5883
  }
@@ -5809,12 +5887,11 @@ function VMI_push_wildcard_entity(x, args) {
5809
5887
  x.push(VM.BAD_REF);
5810
5888
  return;
5811
5889
  }
5812
- // Otherwise, if `br` indicates "by reference", then VMI_push_entity can
5813
- // be called with the appropriate parameters.
5890
+ // Otherwise, if args[0] indicates "by reference", then VMI_push_entity
5891
+ // can be called with the appropriate parameters.
5814
5892
  const attr = args[0].a || obj.defaultAttribute;
5815
5893
  if(args[0].br) {
5816
- args[0] = {r: obj, a: attr};
5817
- VMI_push_entity(x, args);
5894
+ VMI_push_entity(x, {r: obj, a: attr});
5818
5895
  return;
5819
5896
  }
5820
5897
  // Otherwise, if the entity is a dataset modifier, this must be an
@@ -5822,8 +5899,9 @@ function VMI_push_wildcard_entity(x, args) {
5822
5899
  // push the result of this equation using the wildcard vector index
5823
5900
  // of the expression that is being computed.
5824
5901
  if(obj instanceof DatasetModifier) {
5825
- args[0] = {d: obj.dataset, s: x.wildcard_vector_index, x: obj.expression};
5826
- VMI_push_dataset_modifier(x, args);
5902
+ VMI_push_dataset_modifier(x,
5903
+ [{d: obj.dataset, s: x.wildcard_vector_index, x: obj.expression},
5904
+ args[1], args[2], args[3], args[4]]);
5827
5905
  return;
5828
5906
  }
5829
5907
  // Otherwise, it can be a vector type attribute or an expression.
@@ -5836,8 +5914,7 @@ function VMI_push_wildcard_entity(x, args) {
5836
5914
  return;
5837
5915
  }
5838
5916
  // Otherwise, VMI_push_var can be called with `v` as first argument.
5839
- args[0] = v;
5840
- VMI_push_var(x, args);
5917
+ VMI_push_var(x, [v, args[1], args[2], args[3], args[4]]);
5841
5918
  }
5842
5919
 
5843
5920
  function VMI_push_dataset_modifier(x, args) {
@@ -5849,7 +5926,7 @@ function VMI_push_dataset_modifier(x, args) {
5849
5926
  // the running experiment UNLESS the field `ud` ("use data") is defined
5850
5927
  // for the first argument, and evaluates as TRUE.
5851
5928
  // NOTE: Ensure that number 0 is not interpreted as FALSE.
5852
- let ms = (args[0].s === 0 || args[0].s) ? args[0].s : false;
5929
+ let wcnr = (args[0].s === undefined ? false : args[0].s);
5853
5930
  const
5854
5931
  ds = args[0].d,
5855
5932
  ud = args[0].ud || false,
@@ -5857,12 +5934,15 @@ function VMI_push_dataset_modifier(x, args) {
5857
5934
  // NOTE: Use the "local" time step for expression x, i.e., the top
5858
5935
  // value of the expression's time step stack `x.step`.
5859
5936
  tot = twoOffsetTimeStep(x.step[x.step.length - 1],
5860
- args[1], args[2], args[3], args[4], 1, x);
5937
+ args[1], args[2], args[3], args[4], 1, x),
5938
+ // Record whether either anchor uses the context-sensitive number.
5939
+ hashtag_index = (args[1] === '#' || args[3] === '#');
5861
5940
  // NOTE: Sanity check to facilitate debugging; if no dataset is provided,
5862
5941
  // the script will still break at the LET statement below.
5863
5942
  if(!ds) console.log('ERROR: VMI_push_dataset_modifier without dataset',
5864
5943
  x.variableName, x.code);
5865
5944
  let t = tot[0],
5945
+ // By default, use the vector of the dataset to compute the value.
5866
5946
  obj = ds.vector;
5867
5947
 
5868
5948
  if(ds.array) {
@@ -5874,7 +5954,7 @@ function VMI_push_dataset_modifier(x, args) {
5874
5954
  t = t % obj.length;
5875
5955
  if(t < 0) t += obj.length;
5876
5956
  }
5877
- if(args[1] === '#' || args[3] === '#') {
5957
+ if(hashtag_index) {
5878
5958
  // NOTE: Add 1 because (parent) anchors are 1-based.
5879
5959
  ds.parent_anchor = t + 1;
5880
5960
  if(DEBUGGING) {
@@ -5888,55 +5968,71 @@ function VMI_push_dataset_modifier(x, args) {
5888
5968
  t = Math.max(0, Math.min(
5889
5969
  MODEL.end_period - MODEL.start_period + MODEL.look_ahead + 1, t));
5890
5970
  }
5891
- if(ms !== false) {
5892
- // If a modifier selector is specified, use the associated expression.
5971
+ if(wcnr !== false || ds === MODEL.equations_dataset) {
5972
+ // If a wildcard number is specified, or when a normal (not-wildcard)
5973
+ // equation is referenced, use the modifier expression to calculate
5974
+ // the value to push.
5893
5975
  obj = mx;
5894
- // If '?' is passed as selector, use the wildcard vector index of the
5895
- // expression that is being computed.
5896
- if(ms === '?') ms = x.wildcard_vector_index;
5897
- } else if(!ud) {
5976
+ // If '?' is passed as wildcard number, use the wildcard vector index
5977
+ // of the expression that is being computed (this may be FALSE).
5978
+ if(wcnr === '?') {
5979
+ wcnr = x.wildcard_vector_index;
5980
+ }
5981
+ } else if(!ud ) {
5898
5982
  // In no selector and not "use data", check whether a running experiment
5899
5983
  // defines the expression to use. If not, `obj` will be the dataset
5900
5984
  // vector (so same as when "use data" is set).
5901
5985
  obj = ds.activeModifierExpression;
5902
5986
  }
5987
+ if(!obj) {
5988
+ console.log('ANOMALY: no object. obj, wcnr, args, x', obj, wcnr, args, x);
5989
+ }
5903
5990
  // Now determine what value `v` should be pushed onto the expression stack.
5904
5991
  // By default, use the dataset default value.
5905
5992
  let v = ds.defaultValue,
5906
5993
  // NOTE: `obstr` is used only when debugging, to log `obj` in human-
5907
5994
  // readable format.
5908
- obstr = (obj instanceof Expression ?
5909
- obj.text : '[' + obj.toString() + ']');
5995
+ obstr = (obj instanceof Expression ? obj.text : `[${obj.toString()}]`);
5910
5996
  if(Array.isArray(obj)) {
5911
- // Object is a vector.
5997
+ // `obj` is a vector.
5912
5998
  if(t >= 0 && t < obj.length) {
5913
5999
  v = obj[t];
5914
6000
  } else if(ds.array && t >= obj.length) {
5915
- // Set error value if array index is out of bounds.
5916
- v = VM.ARRAY_INDEX;
5917
- VM.out_of_bounds_array = ds.displayName;
5918
- VM.out_of_bounds_msg = `Index ${VM.sig2Dig(t + 1)} not in array dataset ` +
5919
- `${ds.displayName}, which has length ${obj.length}`;
5920
- console.log(VM.out_of_bounds_msg);
6001
+ // Ensure that value of t is human-readable.
6002
+ // NOTE: Add 1 to compensate for earlier t-- to make `t` zero-based.
6003
+ const index = VM.sig2Dig(t + 1);
6004
+ // Special case: index is undefined because # was undefined.
6005
+ if(hashtag_index && index === '\u2047') {
6006
+ // In such cases, return the default value of the dataset.
6007
+ v = ds.default_value;
6008
+ } else {
6009
+ // Set error value to indicate that array index is out of bounds.
6010
+ v = VM.ARRAY_INDEX;
6011
+ VM.out_of_bounds_array = ds.displayName;
6012
+ VM.out_of_bounds_msg = `Index ${index} not in array dataset ` +
6013
+ `${ds.displayName}, which has length ${obj.length}`;
6014
+ console.log(VM.out_of_bounds_msg);
6015
+ }
5921
6016
  }
5922
6017
  // Fall through: no change to `v` => dataset default value is pushed.
5923
6018
  } else {
5924
- // Object is an expression.
6019
+ // `obj` is an expression.
5925
6020
  // NOTE: Readjust `t` when `obj` is an expression for an *array-type*
5926
6021
  // dataset modifier.
5927
6022
  if(obj.object instanceof Dataset && obj.object.array) t++;
5928
6023
  // Pass modifier selector (if specified; may be FALSE) so that result
5929
- // will be recomputed with this selector as context for #
5930
- v = obj.result(t, ms);
6024
+ // will be recomputed with this selector as context for #.
6025
+ v = obj.result(t, wcnr);
5931
6026
  }
5932
- // Trace only now that time step t has been computed
6027
+ // Trace only now that time step t has been computed.
5933
6028
  if(DEBUGGING) {
5934
6029
  console.log('push dataset modifier:', obstr,
5935
- tot[1] + (tot[2] ? ':' + tot[2] : ''), 'value =', VM.sig4Dig(v));
5936
- console.log(' --', x.text, ' for owner ', x.object.displayName, x.attribute);
6030
+ tot[1] + (tot[2] ? ':' + tot[2] : ''), 'value =', VM.sig4Dig(v),
6031
+ '\nExpression: ', x.text, '\nVariable:', x.variableName,
6032
+ 'Context number:', wcnr);
5937
6033
  }
5938
- // NOTE: if value is exceptional ("undefined", etc.), use default value
5939
- if(v >= VM.PLUS_INFINITY) v = ds.defaultValue;
6034
+ // NOTE: If value is exceptional ("undefined", etc.), use default value.
6035
+ // DEPRECATED !! if(v >= VM.PLUS_INFINITY) v = ds.defaultValue;
5940
6036
  // Finally, push the value onto the expression stack
5941
6037
  x.push(v);
5942
6038
  }