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.
- package/package.json +1 -1
- package/server.js +48 -6
- package/static/index.html +9 -5
- package/static/linny-r.css +0 -14
- package/static/scripts/linny-r-ctrl.js +69 -4
- package/static/scripts/linny-r-gui.js +37 -26
- package/static/scripts/linny-r-model.js +136 -71
- package/static/scripts/linny-r-utils.js +38 -15
- package/static/scripts/linny-r-vm.js +265 -169
@@ -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
|
-
|
275
|
-
if(number
|
276
|
-
|
277
|
-
|
278
|
-
|
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
|
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)
|
308
|
-
|
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
|
313
|
-
// this same variable
|
314
|
-
this.trace(
|
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:
|
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
|
-
//
|
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)
|
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
|
536
|
-
//
|
537
|
-
//
|
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
|
563
|
-
//
|
564
|
-
// cannot be inferred at
|
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(
|
585
|
-
//
|
586
|
-
|
587
|
-
|
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
|
605
|
-
(this.
|
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
|
-
//
|
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].
|
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].
|
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].
|
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 &&
|
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 &&
|
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
|
-
|
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
|
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
|
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:
|
1006
|
-
//
|
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
|
-
|
1065
|
+
args = [stat, list, anchor1, offset1, anchor2, offset2];
|
1013
1066
|
if(Object.keys(wdict).length > 0) args.push(wdict);
|
1014
|
-
|
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,
|
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
|
1109
|
-
if(!(this.
|
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
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
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:
|
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
|
-
|
1157
|
-
|
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 =
|
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
|
-
|
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:
|
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
|
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 =
|
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,
|
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: `
|
1299
|
-
if(
|
1300
|
-
if(Array.isArray(
|
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,
|
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(
|
1318
|
-
if(
|
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:
|
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
|
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(
|
1335
|
-
this.is_static = this.is_static &&
|
1407
|
+
if(arg0 instanceof Expression) {
|
1408
|
+
this.is_static = this.is_static && arg0.isStatic;
|
1336
1409
|
}
|
1337
|
-
|
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
|
1346
|
-
// after optimizing a block) while no offset is defined to use
|
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:
|
1349
|
-
obj instanceof Dataset && attr &&
|
1350
|
-
//
|
1351
|
-
|
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.
|
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.
|
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.
|
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
|
-
|
5801
|
-
|
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
|
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
|
-
|
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
|
-
|
5826
|
-
|
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[
|
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
|
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(
|
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(
|
5892
|
-
// If a
|
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
|
5895
|
-
// expression that is being computed.
|
5896
|
-
if(
|
5897
|
-
|
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
|
-
//
|
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
|
-
//
|
5916
|
-
|
5917
|
-
VM.
|
5918
|
-
|
5919
|
-
|
5920
|
-
|
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
|
-
//
|
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,
|
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
|
-
|
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:
|
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
|
}
|