linny-r 1.5.7 → 1.6.0
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/static/index.html +7 -3
- package/static/linny-r.css +19 -0
- package/static/scripts/linny-r-gui-actor-manager.js +3 -3
- package/static/scripts/linny-r-gui-chart-manager.js +12 -11
- package/static/scripts/linny-r-gui-controller.js +2 -1
- package/static/scripts/linny-r-gui-dataset-manager.js +17 -49
- package/static/scripts/linny-r-gui-documentation-manager.js +40 -20
- package/static/scripts/linny-r-gui-equation-manager.js +17 -4
- package/static/scripts/linny-r-gui-experiment-manager.js +41 -23
- package/static/scripts/linny-r-gui-expression-editor.js +26 -21
- package/static/scripts/linny-r-gui-monitor.js +4 -4
- package/static/scripts/linny-r-model.js +248 -77
- package/static/scripts/linny-r-utils.js +50 -11
- package/static/scripts/linny-r-vm.js +588 -292
@@ -33,7 +33,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
33
|
SOFTWARE.
|
34
34
|
*/
|
35
35
|
|
36
|
-
// CLASS Expression
|
36
|
+
// CLASS Expression
|
37
37
|
class Expression {
|
38
38
|
constructor(obj, attr, text) {
|
39
39
|
// Expressions are typically defined for some attribute of some
|
@@ -42,12 +42,22 @@ class Expression {
|
|
42
42
|
this.object = obj;
|
43
43
|
this.attribute = attr;
|
44
44
|
this.text = text;
|
45
|
+
// For method expressions only: the object to which they apply.
|
46
|
+
// This will be set dynamically by the VMI_push_method instruction.
|
47
|
+
this.method_object = null;
|
48
|
+
// Likewise, VMI_push_method may set the method object prefix if
|
49
|
+
// the specific entity needs to be inferred dynamically.
|
50
|
+
this.method_object_prefix = '';
|
45
51
|
// A stack for local time step (to allow lazy evaluation).
|
46
52
|
this.step = [];
|
47
53
|
// An operand stack for computation (elements must be numeric).
|
48
54
|
this.stack = [];
|
49
55
|
// NOTE: code = NULL indicates: not compiled yet.
|
50
56
|
this.code = null;
|
57
|
+
// Error message when last compiled.
|
58
|
+
this.compile_issue = '';
|
59
|
+
// Error message when last computed.
|
60
|
+
this.compute_issue = '';
|
51
61
|
// NOTE: Use a semaphore to prevent cyclic recursion.
|
52
62
|
this.compiling = false;
|
53
63
|
// While compiling, check whether any operand depends on time.
|
@@ -69,28 +79,67 @@ class Expression {
|
|
69
79
|
// by VMI_push_dataset_modifier (read and/or write) and by
|
70
80
|
// VMI_push_contextual_number (read only).
|
71
81
|
this.wildcard_vector_index = false;
|
82
|
+
// Method expressions are similar to wildcard expressions, but have
|
83
|
+
// not natural numbering scheme. By keeping a list of all objects
|
84
|
+
// to which a method has been applied, the index of such an object
|
85
|
+
// in this list serves as the vector number.
|
86
|
+
this.method_object_list = [];
|
72
87
|
// Special instructions can store results as cache properties to save
|
73
88
|
// (re)computation time; cache is cleared when expression is reset.
|
74
89
|
this.cache = {};
|
75
90
|
}
|
76
91
|
|
77
92
|
get isWildcardExpression() {
|
78
|
-
//
|
93
|
+
// Return TRUE if the owner is a dataset, and the attribute contains
|
79
94
|
// wildcards.
|
80
95
|
return this.object instanceof Dataset &&
|
81
96
|
this.object.isWildcardSelector(this.attribute);
|
82
97
|
}
|
83
98
|
|
99
|
+
get isMethod() {
|
100
|
+
// Return TRUE if the owner is the equations dataset, and the
|
101
|
+
// attribute starts with a colon.
|
102
|
+
return this.object === MODEL.equations_dataset &&
|
103
|
+
this.attribute.startsWith(':');
|
104
|
+
}
|
105
|
+
|
106
|
+
get noMethodObject() {
|
107
|
+
// Return TRUE if expression is a method that does not apply to
|
108
|
+
// any entity group.
|
109
|
+
return this.isMethod && !(this.eligible_prefixes &&
|
110
|
+
Object.keys(this.eligible_prefixes).length > 0);
|
111
|
+
}
|
112
|
+
|
113
|
+
matchWithEligiblePrefixes(pref) {
|
114
|
+
// Return the entity for which `pref` matches with an eligible prefix
|
115
|
+
// of this expression.
|
116
|
+
// NOTE: This expression must have been compiled to "know" its
|
117
|
+
// eligible prefixes.
|
118
|
+
this.compile();
|
119
|
+
// NOTE: Prevent infinite recursion, but do not generate a warning;
|
120
|
+
// the expression parser will do this.
|
121
|
+
if(this.compiling || !this.eligible_prefixes) return false;
|
122
|
+
return this.eligible_prefixes[pref.toLowerCase()] || false;
|
123
|
+
}
|
124
|
+
|
125
|
+
isEligible(prefix) {
|
126
|
+
// Return TRUE if `prefix` is an eligible prefix for this method.
|
127
|
+
if(this.eligible_prefixes) {
|
128
|
+
return this.eligible_prefixes[prefix.toLowerCase()] || false;
|
129
|
+
}
|
130
|
+
return false;
|
131
|
+
}
|
132
|
+
|
84
133
|
get variableName() {
|
85
|
-
// Return the name of the variable computed by this expression
|
134
|
+
// Return the name of the variable computed by this expression.
|
86
135
|
if(this.object === MODEL.equations_dataset) return 'equation ' + this.attribute;
|
87
136
|
if(this.object) return this.object.displayName + UI.OA_SEPARATOR + this.attribute;
|
88
137
|
return 'Unknown variable (no object)';
|
89
138
|
}
|
90
139
|
|
91
140
|
get timeStepDuration() {
|
92
|
-
//
|
93
|
-
// otherwise dt for the current model
|
141
|
+
// Return dt for dataset if this is a dataset modifier expression;
|
142
|
+
// otherwise dt for the current model.
|
94
143
|
if(this.object instanceof Dataset) {
|
95
144
|
return this.object.time_scale * VM.time_unit_values[this.object.time_unit];
|
96
145
|
}
|
@@ -98,47 +147,39 @@ class Expression {
|
|
98
147
|
}
|
99
148
|
|
100
149
|
get referencedEntities() {
|
101
|
-
//
|
102
|
-
|
103
|
-
const
|
104
|
-
el = [],
|
105
|
-
ml = [...this.text.matchAll(/\[(\{[^\}]+\}){0,1}([^\]]+)\]/g)];
|
106
|
-
for(let i = 0; i < ml.length; i++) {
|
107
|
-
const n = ml[i][2].trim();
|
108
|
-
let sep = n.lastIndexOf('|');
|
109
|
-
if(sep < 0) sep = n.lastIndexOf('@');
|
110
|
-
const
|
111
|
-
en = (sep < 0 ? n : n.substring(0, sep)),
|
112
|
-
e = MODEL.objectByName(en.trim());
|
113
|
-
if(e) addDistinct(e, el);
|
114
|
-
}
|
115
|
-
return el;
|
150
|
+
// Return a list of entities referenced in this expression.
|
151
|
+
return MODEL.entitiesInString(this.text);
|
116
152
|
}
|
117
|
-
|
153
|
+
|
118
154
|
update(parser) {
|
119
|
-
// Must be called after successful compilation by the expression parser
|
155
|
+
// Must be called after successful compilation by the expression parser.
|
120
156
|
this.text = parser.expr;
|
121
157
|
this.code = parser.code;
|
122
|
-
|
123
|
-
//
|
158
|
+
this.eligible_prefixes = parser.eligible_prefixes;
|
159
|
+
// NOTE: Overrule `is_static` to make that the "initial level" attribute
|
160
|
+
// is always evaluated for t=1.
|
124
161
|
this.is_static = (this.attribute === 'IL' ? true : parser.is_static);
|
125
162
|
this.is_level_based = parser.is_level_based;
|
126
163
|
this.reset();
|
127
164
|
}
|
128
165
|
|
129
166
|
reset(default_value=VM.NOT_COMPUTED) {
|
130
|
-
//
|
167
|
+
// Clear result of previous computation (if any).
|
168
|
+
this.method_object = null;
|
169
|
+
this.compile_issue = '';
|
170
|
+
this.compute_issue = '';
|
131
171
|
this.step.length = 0;
|
132
172
|
this.stack.length = 0;
|
133
173
|
this.wildcard_vectors = {};
|
134
174
|
this.wildcard_vector_index = false;
|
175
|
+
this.method_object_list.length = 0;
|
135
176
|
this.cache = {};
|
136
177
|
this.compile(); // if(!this.compiled) REMOVED to ensure correct isStatic!!
|
137
178
|
// Static expressions only need a vector with one element (having index 0)
|
138
179
|
if(this.is_static) {
|
139
|
-
// NOTE:
|
180
|
+
// NOTE: Empty expressions (i.e., no text) may default to different
|
140
181
|
// values: typically 0 for lower bounds, infinite for upper process
|
141
|
-
// bounds, etc., so this value must be passed as parameter
|
182
|
+
// bounds, etc., so this value must be passed as parameter.
|
142
183
|
this.vector.length = 1;
|
143
184
|
if(this.text.length === 0) {
|
144
185
|
this.vector[0] = default_value;
|
@@ -180,6 +221,7 @@ class Expression {
|
|
180
221
|
}
|
181
222
|
this.update(xp);
|
182
223
|
} else {
|
224
|
+
this.compile_issue = xp.error;
|
183
225
|
this.is_static = true;
|
184
226
|
this.vector.length = 0;
|
185
227
|
this.vector[0] = VM.INVALID;
|
@@ -271,9 +313,22 @@ class Expression {
|
|
271
313
|
|
272
314
|
chooseVector(number) {
|
273
315
|
// 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
|
-
|
316
|
+
// NOTE: Static wildcard and method expressions must also choose a vector!
|
317
|
+
if((typeof number !== 'number' ||
|
318
|
+
(this.isStatic && !this.isWildcardExpression)) &&
|
319
|
+
!this.isMethod) return this.vector;
|
320
|
+
// Method expressions are not "numbered" but differentiate by the
|
321
|
+
// entity to which they are applied. Their "vector number" is then
|
322
|
+
// inferred by looking up this entity in a method object list.
|
323
|
+
const mop = (this.method_object && this.method_object.identifier) ||
|
324
|
+
this.method_object_prefix || '';
|
325
|
+
if(mop) {
|
326
|
+
number = this.method_object_list.indexOf(mop);
|
327
|
+
if(number < 0) {
|
328
|
+
this.method_object_list.push(mop);
|
329
|
+
number = this.method_object_list.length - 1;
|
330
|
+
}
|
331
|
+
}
|
277
332
|
// Use the vector for the wildcard number (create it if necessary).
|
278
333
|
if(!this.wildcard_vectors.hasOwnProperty(number)) {
|
279
334
|
this.wildcard_vectors[number] = [];
|
@@ -358,8 +413,10 @@ class Expression {
|
|
358
413
|
this.wildcard_vector_index = false;
|
359
414
|
// If error, display the call stack (only once).
|
360
415
|
// NOTE: "undefined", "not computed" and "still computing" are NOT
|
361
|
-
// problematic unless they result in an error (stack over/underflow)
|
416
|
+
// problematic unless they result in an error (stack over/underflow).
|
362
417
|
if(v[t] <= VM.ERROR) {
|
418
|
+
// NOTE: Record the first issue that is detected.
|
419
|
+
if(!this.compute_issue) this.compute_issue = VM.errorMessage(v[t]);
|
363
420
|
MONITOR.showCallStack(t);
|
364
421
|
VM.logCallStack(t);
|
365
422
|
}
|
@@ -397,17 +454,19 @@ class Expression {
|
|
397
454
|
}
|
398
455
|
|
399
456
|
get asAttribute() {
|
400
|
-
//
|
401
|
-
// (special values as human-readable string), or the
|
457
|
+
// Return the result for the current time step if the model has been
|
458
|
+
// solved (with special values as human-readable string), or the
|
459
|
+
// expression as text.
|
402
460
|
if(!(MODEL.solved || this.isStatic)) return this.text;
|
403
461
|
const sv = VM.specialValue(this.result(MODEL.t))[1];
|
404
|
-
// NOTE: ?? is replaced by empty string
|
462
|
+
// NOTE: ?? is replaced by empty string to facilitate copy/paste to
|
463
|
+
// Excel-like spreadsheets, where an empty cell indicates "undefined".
|
405
464
|
if(sv === '\u2047') return '';
|
406
465
|
return sv;
|
407
466
|
}
|
408
467
|
|
409
468
|
push(value) {
|
410
|
-
//
|
469
|
+
// Push a numeric value onto the computation stack.
|
411
470
|
if(this.stack.length >= VM.MAX_STACK) {
|
412
471
|
this.trace('STACK OVERFLOW');
|
413
472
|
this.stack.push(VM.OVERFLOW);
|
@@ -419,7 +478,7 @@ class Expression {
|
|
419
478
|
}
|
420
479
|
|
421
480
|
top(no_check=false) {
|
422
|
-
//
|
481
|
+
// Return the top element of the stack, or FALSE if the stack was empty.
|
423
482
|
if(this.stack.length < 1) {
|
424
483
|
this.trace('TOP: UNDERFLOW');
|
425
484
|
this.stack = [VM.UNDERFLOW];
|
@@ -427,12 +486,12 @@ class Expression {
|
|
427
486
|
return false;
|
428
487
|
}
|
429
488
|
const top = this.stack[this.stack.length - 1];
|
430
|
-
// Check for errors, "undefined", "not computed", and "still computing"
|
489
|
+
// Check for errors, "undefined", "not computed", and "still computing".
|
431
490
|
if(top < VM.MINUS_INFINITY || top > VM.EXCEPTION) {
|
432
|
-
// If error or exception, ignore UNDEFINED if `no_check` is TRUE
|
491
|
+
// If error or exception, ignore UNDEFINED if `no_check` is TRUE.
|
433
492
|
if(no_check && top <= VM.UNDEFINED) return top;
|
434
493
|
// Otherwise, leave the special value on top of the stack, and
|
435
|
-
// return FALSE so that the VM instruction will not alter it
|
494
|
+
// return FALSE so that the VM instruction will not alter it.
|
436
495
|
this.trace(
|
437
496
|
VM.errorMessage(top) + ' at top of stack: ' + this.stack.toString());
|
438
497
|
return false;
|
@@ -441,26 +500,26 @@ class Expression {
|
|
441
500
|
}
|
442
501
|
|
443
502
|
pop(no_check=false) {
|
444
|
-
//
|
445
|
-
// element B from the stack, or FALSE if the stack contains fewer
|
446
|
-
// elements, or if A and/or B are error values
|
503
|
+
// Return the two top elements A and B as [A, B] after popping the
|
504
|
+
// top element B from the stack, or FALSE if the stack contains fewer
|
505
|
+
// than 2 elements, or if A and/or B are error values.
|
447
506
|
if(this.stack.length < 2) {
|
448
507
|
this.trace('POP: UNDERFLOW');
|
449
508
|
this.stack.push(VM.UNDERFLOW);
|
450
509
|
this.computed = true;
|
451
510
|
return false;
|
452
511
|
}
|
453
|
-
// Get the top two numbers on the stack as a list
|
512
|
+
// Get the top two numbers on the stack as a list.
|
454
513
|
const dyad = this.stack.slice(-2);
|
455
|
-
// Pop only the top one
|
514
|
+
// Pop only the top one.
|
456
515
|
this.stack.pop();
|
457
|
-
// Check whether either number is an error code
|
516
|
+
// Check whether either number is an error code.
|
458
517
|
let check = Math.min(dyad[0], dyad[1]);
|
459
518
|
if(check < VM.MINUS_INFINITY &&
|
460
519
|
// Exception: "array index out of bounds" error may also be
|
461
520
|
// ignored by using the | operator.
|
462
521
|
!(no_check && check === VM.ARRAY_INDEX)) {
|
463
|
-
// If error, leave the
|
522
|
+
// If error, leave the most severe error on top of the stack.
|
464
523
|
this.retop(check);
|
465
524
|
this.trace(VM.errorMessage(check) + ' in dyad: ' + dyad.toString());
|
466
525
|
return false;
|
@@ -482,38 +541,40 @@ class Expression {
|
|
482
541
|
this.trace(VM.errorMessage(check) + ' in dyad: ' + dyad.toString());
|
483
542
|
return false;
|
484
543
|
}
|
485
|
-
// No
|
544
|
+
// No issue(s)? Then return the dyad.
|
486
545
|
return dyad;
|
487
546
|
}
|
488
547
|
|
489
548
|
retop(value) {
|
490
|
-
//
|
491
|
-
// NOTE:
|
492
|
-
// follows a TOP or POP instruction
|
549
|
+
// Replace the top element of the stack by the new value.
|
550
|
+
// NOTE: Do not check the stack length, as this instruction typically
|
551
|
+
// follows a TOP or POP instruction.
|
493
552
|
this.stack[this.stack.length - 1] = value;
|
494
553
|
return true;
|
495
554
|
}
|
496
555
|
|
497
556
|
replaceAttribute(re, a1, a2) {
|
498
|
-
//
|
499
|
-
// match the regular expression `re
|
557
|
+
// Replace occurrences of attribute `a1` by `a2` for all variables
|
558
|
+
// that match the regular expression `re`.
|
500
559
|
let n = 0;
|
501
560
|
const matches = this.text.match(re);
|
502
561
|
if(matches) {
|
503
|
-
// Match is case-insensitive, so check each for matching case of
|
562
|
+
// Match is case-insensitive, so check each for matching case of
|
563
|
+
// attribute.
|
504
564
|
for(let i = 0; i < matches.length; i++) {
|
505
565
|
const
|
506
566
|
m = matches[i],
|
507
567
|
e = m.split('|');
|
508
|
-
// Let `ao` be attribute + offset (if any) without right bracket
|
568
|
+
// Let `ao` be attribute + offset (if any) without right bracket.
|
509
569
|
let ao = e.pop().slice(0, -1),
|
510
|
-
// Then also trim offset and spaces
|
570
|
+
// Then also trim offset and spaces.
|
511
571
|
a = ao.split('@')[0].trim();
|
512
|
-
// Check
|
572
|
+
// Check whether `a` (without bracket and without spaces) indeed
|
573
|
+
// matches `a1`.
|
513
574
|
if(a === a1) {
|
514
575
|
// If so, append new attribute plus offset plus right bracket...
|
515
576
|
e.push(ao.replace(a, a2) + ']');
|
516
|
-
// ... and replace the original match by the ensemble
|
577
|
+
// ... and replace the original match by the ensemble.
|
517
578
|
this.text = this.text.replace(m, e.join('|'));
|
518
579
|
n += 1;
|
519
580
|
}
|
@@ -564,13 +625,20 @@ class ExpressionParser {
|
|
564
625
|
constructor(text, owner=null, attribute='') {
|
565
626
|
// Setting TRACE to TRUE will log parsing information to the console.
|
566
627
|
this.TRACE = false;
|
567
|
-
// `text` is the expression string to be parsed.
|
568
|
-
this.expr = text;
|
569
628
|
// NOTE: When expressions for dataset modifiers or equations are
|
570
629
|
// parsed, `owner` is their dataset, and `attribute` is their name.
|
571
630
|
this.owner = owner;
|
572
631
|
this.owner_prefix = '';
|
573
632
|
this.attribute = attribute;
|
633
|
+
// `text` is the expression string to be parsed.
|
634
|
+
this.expr = text;
|
635
|
+
this.expansions = [];
|
636
|
+
// Initialize eligible entities as NULL so it will be initialized
|
637
|
+
// when the first method expression variable is parsed.
|
638
|
+
this.eligible_prefixes = null;
|
639
|
+
// When parsing a method expression, keep a list of all attributes
|
640
|
+
// used in variables.
|
641
|
+
this.method_attributes = [];
|
574
642
|
this.dataset = null;
|
575
643
|
this.dot = null;
|
576
644
|
this.selector = '';
|
@@ -652,7 +720,7 @@ class ExpressionParser {
|
|
652
720
|
// Set the above IF condition to FALSE to profile dynamic expressions.
|
653
721
|
console.log(`Expression for ${this.ownerName}: ${this.expr}\n${msg}`);
|
654
722
|
}
|
655
|
-
|
723
|
+
|
656
724
|
// The method parseVariable(name) checks whether `name` fits this pattern:
|
657
725
|
// {run}statistic$entity|attribute@offset_1:offset_2
|
658
726
|
// allowing spaces within {run} and around | and @ and :
|
@@ -667,8 +735,8 @@ class ExpressionParser {
|
|
667
735
|
// NOTE: this array is used as argument for the virtual machine instructions
|
668
736
|
// VMI_push_var, VMI_push_statistic and VMI_push_run_result.
|
669
737
|
parseVariable(name) {
|
670
|
-
//
|
671
|
-
name = name.replace(/\s+/g, ' ');
|
738
|
+
// Remove non-functional whitespace.
|
739
|
+
name = name.replace(/\s+/g, ' ').trim();
|
672
740
|
|
673
741
|
// For debugging, TRACE can be used to log to the console for
|
674
742
|
// specific expressions and/or variables, for example:
|
@@ -975,23 +1043,6 @@ class ExpressionParser {
|
|
975
1043
|
return false;
|
976
1044
|
}
|
977
1045
|
}
|
978
|
-
/*
|
979
|
-
// DEPRECATED -- Modeler can deal with this by smartly using AND
|
980
|
-
// clauses like "&x: &y:" to limit set to specific prefixes.
|
981
|
-
|
982
|
-
// Deal with "prefix inheritance" when pattern starts with a colon.
|
983
|
-
if(pat.startsWith(':') && this.owner_prefix) {
|
984
|
-
// Add a "must start with" AND condition to all OR clauses of the
|
985
|
-
// pattern.
|
986
|
-
// NOTE: Issues may occur when prefix contains &, ^ or #.
|
987
|
-
// @@TO DO: See if this can be easily prohibited.
|
988
|
-
const oc = pat.substring(1).split('|');
|
989
|
-
for(let i = 0; i < oc.length; i++) {
|
990
|
-
oc[i] = `~${this.owner_prefix}&${oc[i]}`;
|
991
|
-
}
|
992
|
-
pat = oc.join('|');
|
993
|
-
}
|
994
|
-
*/
|
995
1046
|
// NOTE: For patterns, assume that # *always* denotes the context-
|
996
1047
|
// sensitive number #, because if modelers wishes to include
|
997
1048
|
// ANY number, they can make their pattern less selective.
|
@@ -1118,7 +1169,7 @@ class ExpressionParser {
|
|
1118
1169
|
}
|
1119
1170
|
// A leading "!" denotes: pass variable reference instead of its value.
|
1120
1171
|
// NOTE: This also applies to the "dot", so [!.] is a valid variable.
|
1121
|
-
|
1172
|
+
const by_reference = name.startsWith('!');
|
1122
1173
|
if(by_reference) name = name.substring(1);
|
1123
1174
|
// When `name` is a single dot, it refers to the dataset for which the
|
1124
1175
|
// modifier expression is being parsed. Like all datasets, the "dot"
|
@@ -1134,11 +1185,163 @@ class ExpressionParser {
|
|
1134
1185
|
return false;
|
1135
1186
|
}
|
1136
1187
|
// Check whether name refers to a Linny-R entity defined by the model.
|
1188
|
+
|
1189
|
+
// NOTE: When parsing the expression of a "method", variables starting
|
1190
|
+
// with a colon may be special cases.
|
1191
|
+
if(this.attribute.startsWith(':') && name.startsWith(':')) {
|
1192
|
+
// When `name` identifies a method ":m" then this method can be
|
1193
|
+
// called "as is".
|
1194
|
+
const method = MODEL.equationByID(UI.nameToID(name));
|
1195
|
+
if(method) {
|
1196
|
+
// Check for auto-reference.
|
1197
|
+
if(method.selector === this.attribute) {
|
1198
|
+
this.error = 'Method cannot reference itself';
|
1199
|
+
return false;
|
1200
|
+
}
|
1201
|
+
if(attr) {
|
1202
|
+
// Methods are expressions and hence always return a number,
|
1203
|
+
// not an entity.
|
1204
|
+
this.error = 'Method cannot have an attribute';
|
1205
|
+
return false;
|
1206
|
+
}
|
1207
|
+
// NOTE: If it has no eligible prefixes yet, the method being
|
1208
|
+
// parsed "inherits" those of an "as is" method, or should
|
1209
|
+
// intersect its eligible prefixes with the "inherited" ones.
|
1210
|
+
method.expression.compile();
|
1211
|
+
if(method.expression.compiling) {
|
1212
|
+
this.error = 'Cannot resolve method "' + method.selector +
|
1213
|
+
'" because this would create a cyclic reference';
|
1214
|
+
return false;
|
1215
|
+
}
|
1216
|
+
const
|
1217
|
+
ep = {},
|
1218
|
+
mep = method.expression.eligible_prefixes,
|
1219
|
+
prefs = Object.keys(mep);
|
1220
|
+
// NOTE: Prefix keys will always be in lower case.
|
1221
|
+
for(let i = 0; i < prefs.length; i++) {
|
1222
|
+
const pref = prefs[i];
|
1223
|
+
if(this.eligible_prefixes === null || this.eligible_prefixes[pref]) {
|
1224
|
+
ep[pref] = true;
|
1225
|
+
}
|
1226
|
+
}
|
1227
|
+
this.eligible_prefixes = ep;
|
1228
|
+
// NOTE: The method may be dynamic and/or level-dependent.
|
1229
|
+
if(!method.expression.isStatic) {
|
1230
|
+
this.is_static = false;
|
1231
|
+
this.log('dynamic because dynamic method is used');
|
1232
|
+
}
|
1233
|
+
this.is_level_based = this.is_level_based ||
|
1234
|
+
method.expression.is_level_based;
|
1235
|
+
// Generate "call method" VM instruction with no additional
|
1236
|
+
// arguments; the method equation will be applied to the object
|
1237
|
+
// of the calling method.
|
1238
|
+
return [{meq: method}, anchor1, offset1, anchor2, offset2];
|
1239
|
+
}
|
1240
|
+
// If `name` does not identify a method, it must match the "tail"
|
1241
|
+
// of some prefixed entity "prefix: name", because a method can
|
1242
|
+
// only be used as [prefix: method name] in another expression.
|
1243
|
+
// When compiling a method, a list of eligible prefixes is made.
|
1244
|
+
// This should not be empty when a method reference is parsed.
|
1245
|
+
const
|
1246
|
+
tail = UI.PREFIXER + name.substring(1).trim(),
|
1247
|
+
ee = MODEL.entitiesEndingOn(tail, attr),
|
1248
|
+
ep = {};
|
1249
|
+
for(let i = 0; i < ee.length; i++) {
|
1250
|
+
const
|
1251
|
+
en = ee[i].displayName,
|
1252
|
+
pref = en.substring(0, en.length - tail.length).toLowerCase();
|
1253
|
+
if(this.eligible_prefixes === null || this.eligible_prefixes[pref]) {
|
1254
|
+
ep[pref] = true;
|
1255
|
+
}
|
1256
|
+
}
|
1257
|
+
this.eligible_prefixes = ep;
|
1258
|
+
const uca = attr.toUpperCase();
|
1259
|
+
// Capitalize `attr` if it is a standard entity attribute.
|
1260
|
+
if(VM.attribute_names[uca]) attr = uca;
|
1261
|
+
// Add attribute to method attribute list (for post-parsing check).
|
1262
|
+
this.method_attributes.push(attr);
|
1263
|
+
if(Object.keys(this.eligible_prefixes).length <= 0) {
|
1264
|
+
const n = name + (attr ? `|${attr}` : '');
|
1265
|
+
this.error =
|
1266
|
+
`No match for variable [${n}] in this method expression`;
|
1267
|
+
return false;
|
1268
|
+
}
|
1269
|
+
// NOTE: Some attributes make the method expression level-dependent.
|
1270
|
+
this.is_level_based = this.is_level_based ||
|
1271
|
+
VM.level_based_attr.indexOf(attr) >= 0;
|
1272
|
+
// NOTE: Postpone check whether method will make the expression
|
1273
|
+
// dynamic to after the expression has been parsed and the exact
|
1274
|
+
// set of eligible entities and the set of attributes is known.
|
1275
|
+
|
1276
|
+
// Colon-prefixed variables in method expressions are similar to
|
1277
|
+
// wildcard variables, so the same VM instruction is coded for,
|
1278
|
+
// except that the entity that is the object of the method will
|
1279
|
+
// be set (as `method_object` property) for expressions that "call"
|
1280
|
+
// the method. The distinction is indicated by passing the string
|
1281
|
+
// "MO" instead of the list of eligible entities.
|
1282
|
+
if(this.TRACE) console.log('TRACE: Variable', name,
|
1283
|
+
'references the method object. Attribute used:', attr);
|
1284
|
+
return [{n: name, ee: 'MO', a: attr, br: by_reference},
|
1285
|
+
anchor1, offset1, anchor2, offset2];
|
1286
|
+
}
|
1287
|
+
|
1288
|
+
// Special "method-parsing" cases will now have been handled.
|
1289
|
+
// The other cases apply also to normal expressions.
|
1137
1290
|
if(!obj) {
|
1138
|
-
//
|
1139
|
-
//
|
1291
|
+
// If variable name starts with a colon, then the owner prefix
|
1292
|
+
// should be added.
|
1140
1293
|
name = UI.colonPrefixedName(name, this.owner_prefix);
|
1141
|
-
//
|
1294
|
+
// Now check whether the variable appends a method.
|
1295
|
+
const
|
1296
|
+
parts = name.split(UI.PREFIXER),
|
1297
|
+
tail = parts.pop();
|
1298
|
+
if(parts.length > 0) {
|
1299
|
+
// Name contains at least one prefix => last part *could* be a
|
1300
|
+
// method name, so look it up after adding a leading colon.
|
1301
|
+
const method = MODEL.equationByID(UI.nameToID(':' + tail));
|
1302
|
+
// If tail matches with a method, the head must identify an
|
1303
|
+
// entity.
|
1304
|
+
if(method) {
|
1305
|
+
const
|
1306
|
+
en = parts.join(UI.PREFIXER),
|
1307
|
+
mep = method.expression.matchWithEligiblePrefixes(en);
|
1308
|
+
if(!mep) {
|
1309
|
+
if(method.expression.compiling) {
|
1310
|
+
this.error = `Cannot resolve "${en}", possibly because ` +
|
1311
|
+
`method "${method.selector}" creates a cyclic reference`;
|
1312
|
+
} else {
|
1313
|
+
this.error = 'Method "'+ method.selector +
|
1314
|
+
`" does not apply to "${en}"`;
|
1315
|
+
}
|
1316
|
+
return false;
|
1317
|
+
}
|
1318
|
+
// NOTE: The method may be dynamic and/or level-dependent.
|
1319
|
+
if(!method.expression.isStatic) {
|
1320
|
+
this.is_static = false;
|
1321
|
+
this.log('dynamic because dynamic method is used');
|
1322
|
+
}
|
1323
|
+
this.is_level_based = this.is_level_based ||
|
1324
|
+
method.expression.is_level_based;
|
1325
|
+
// NOTE: `en` may be an incomplete identification of the object
|
1326
|
+
// of the method, which can be completed only at execution time.
|
1327
|
+
// For example: [x: m] is a valid method call when "x: a: b"
|
1328
|
+
// and "x: c" identify model entities, and the expression of
|
1329
|
+
// method "m" contains variables [:a :b] and [:c] as operands.
|
1330
|
+
// These operands code as VMI_push_dataset_modifier instructions
|
1331
|
+
// with specifier {n: name, ee: "MO"} where, for the examples
|
1332
|
+
// above, "name" would be ":a :b" and ":c". By passing `en`
|
1333
|
+
// as the "method object prefix" to VMI_push_method, this
|
1334
|
+
// instruction can set the `method_object_prefix` attribute
|
1335
|
+
// of the expression so that it can be used by the VMI_push_
|
1336
|
+
// _dataset_modifier instruction to identify the method object
|
1337
|
+
// by assembling prefix + name (with its leading colon replaced
|
1338
|
+
// by the prefixer ": ").
|
1339
|
+
return [{meq: method, mo: en}, anchor1, offset1, anchor2, offset2];
|
1340
|
+
}
|
1341
|
+
}
|
1342
|
+
}
|
1343
|
+
if(!obj) {
|
1344
|
+
// Now check wildcard equations, as these are likely to be few
|
1142
1345
|
// (so a quick scan) and constitute a special case.
|
1143
1346
|
const
|
1144
1347
|
id = UI.nameToID(name),
|
@@ -1151,7 +1354,7 @@ class ExpressionParser {
|
|
1151
1354
|
// so this equation must be evaluated for that number.
|
1152
1355
|
return [
|
1153
1356
|
{d: w[0].dataset, s: w[1], x: w[0].expression},
|
1154
|
-
anchor1, offset1, anchor2, offset2];
|
1357
|
+
anchor1, offset1, anchor2, offset2];
|
1155
1358
|
}
|
1156
1359
|
// If no match, try to match the object ID with any type of entity.
|
1157
1360
|
obj = MODEL.objectByID(id);
|
@@ -1313,8 +1516,7 @@ class ExpressionParser {
|
|
1313
1516
|
// No explicit selector means that this variable is dynamic if
|
1314
1517
|
// the dataset has time series data, or if some of its modifier
|
1315
1518
|
// expressions are dynamic.
|
1316
|
-
if(obj.
|
1317
|
-
!obj.allModifiersAreStatic) {
|
1519
|
+
if(obj.mayBeDynamic) {
|
1318
1520
|
this.is_static = false;
|
1319
1521
|
this.log('dynamic because dataset without explicit selector is used');
|
1320
1522
|
}
|
@@ -1377,19 +1579,18 @@ class ExpressionParser {
|
|
1377
1579
|
// NOTE: `arg0` can now be a single value, a vector, or NULL.
|
1378
1580
|
if(arg0 === null) arg0 = obj.attributeValue(attr);
|
1379
1581
|
if(Array.isArray(arg0)) {
|
1380
|
-
if(obj instanceof Dataset) {
|
1381
|
-
|
1382
|
-
|
1383
|
-
this.log('dynamic because dataset vector is used');
|
1384
|
-
}
|
1582
|
+
if(obj instanceof Dataset && obj.mayBeDynamic) {
|
1583
|
+
this.is_static = false;
|
1584
|
+
this.log('dynamic because dataset vector is used');
|
1385
1585
|
} else if(VM.level_based_attr.indexOf(attr) >= 0) {
|
1386
1586
|
this.is_static = false;
|
1387
1587
|
this.log('dynamic because level-based attribute');
|
1388
|
-
} else {
|
1389
|
-
//
|
1588
|
+
} else if(new Set(arg0).size > 1) {
|
1589
|
+
// Not all values qre equal => dynamic.
|
1390
1590
|
this.is_static = false;
|
1391
|
-
this.log('
|
1392
|
-
console.log('ANOMALY: array for', obj.displayName, obj, attr, arg0);
|
1591
|
+
this.log('Dynamic because array contains different values');
|
1592
|
+
// console.log('ANOMALY: array for', obj.type, obj.displayName, obj, attr, arg0);
|
1593
|
+
// console.log('Expression for', this.ownerName, '; text =', this.expr);
|
1393
1594
|
}
|
1394
1595
|
if(this.TRACE) console.log('TRACE: arg[0] is a vector');
|
1395
1596
|
}
|
@@ -1441,10 +1642,10 @@ class ExpressionParser {
|
|
1441
1642
|
}
|
1442
1643
|
|
1443
1644
|
getSymbol() {
|
1444
|
-
//
|
1645
|
+
// Get the next substring in the expression that is a valid symbol
|
1445
1646
|
// while advancing the position-in-text (`pit`) and length-of-symbol
|
1446
1647
|
// (`los`), which are used to highlight the position of a syntax error
|
1447
|
-
// in the expression editor
|
1648
|
+
// in the expression editor.
|
1448
1649
|
let c, f, i, l, v;
|
1449
1650
|
this.prev_sym = this.sym;
|
1450
1651
|
this.sym = null;
|
@@ -1652,35 +1853,35 @@ class ExpressionParser {
|
|
1652
1853
|
}
|
1653
1854
|
|
1654
1855
|
compile() {
|
1655
|
-
//
|
1656
|
-
// NOTE:
|
1657
|
-
// become the code attribute of an expression object
|
1856
|
+
// Compile expression into array of VM instructions `code`.
|
1857
|
+
// NOTE: Always create a new code array instance, as it will typically
|
1858
|
+
// become the code attribute of an expression object.
|
1658
1859
|
if(DEBUGGING) console.log('COMPILING', this.ownerName, ':\n',
|
1659
1860
|
this.expr, '\ncontext number =', this.context_number);
|
1660
1861
|
this.code = [];
|
1661
|
-
// Position in text
|
1862
|
+
// Position in text.
|
1662
1863
|
this.pit = 0;
|
1663
|
-
// Length of symbol
|
1864
|
+
// Length of symbol.
|
1664
1865
|
this.los = 0;
|
1665
|
-
// Error message also serves as flag: stop compiling if not empty
|
1866
|
+
// Error message also serves as flag: stop compiling if not empty.
|
1666
1867
|
this.error = '';
|
1667
|
-
// `is_static` becomes FALSE when a time-dependent operand is detected
|
1868
|
+
// `is_static` becomes FALSE when a time-dependent operand is detected.
|
1668
1869
|
this.is_static = true;
|
1669
|
-
// `is_level_based` becomes TRUE when a level-based variable is detected
|
1870
|
+
// `is_level_based` becomes TRUE when a level-based variable is detected.
|
1670
1871
|
this.is_level_based = false;
|
1671
|
-
// `concatenating` becomes TRUE when a concatenation operator
|
1672
|
-
// is pushed, and FALSE when a reducing operator (min, max,
|
1673
|
-
// triangular) is pushed
|
1872
|
+
// `concatenating` becomes TRUE when a concatenation operator
|
1873
|
+
// (semicolon) is pushed, and FALSE when a reducing operator (min, max,
|
1874
|
+
// normal, weibull, triangular) is pushed.
|
1674
1875
|
this.concatenating = false;
|
1675
|
-
// An empty expression should return the "undefined" value
|
1876
|
+
// An empty expression should return the "undefined" value.
|
1676
1877
|
if(this.expr.trim() === '') {
|
1677
1878
|
this.code.push([VMI_push_number, VM.UNDEFINED]);
|
1678
1879
|
return;
|
1679
1880
|
}
|
1680
|
-
// Parse the expression using Edsger Dijkstra's shunting-yard algorithm
|
1681
|
-
// vmi = virtual machine instruction (a function)
|
1881
|
+
// Parse the expression using Edsger Dijkstra's shunting-yard algorithm.
|
1882
|
+
// vmi = virtual machine instruction (a function).
|
1682
1883
|
let vmi;
|
1683
|
-
// eot = end of text (index of last character in string)
|
1884
|
+
// eot = end of text (index of last character in string).
|
1684
1885
|
this.eot = this.expr.length - 1;
|
1685
1886
|
this.sym = null; // current symbol
|
1686
1887
|
this.prev_sym = null; // previous symbol
|
@@ -1693,27 +1894,27 @@ class ExpressionParser {
|
|
1693
1894
|
this.getSymbol();
|
1694
1895
|
if(this.error !== '') break;
|
1695
1896
|
if(this.sym === '(') {
|
1696
|
-
// Opening parenthesis is ALWAYS pushed onto the stack
|
1897
|
+
// Opening parenthesis is ALWAYS pushed onto the stack.
|
1697
1898
|
this.op_stack.push(this.sym);
|
1698
1899
|
} else if(this.sym === ')') {
|
1699
1900
|
// Closing parenthesis => pop all operators until its matching
|
1700
|
-
// opening parenthesis is found
|
1901
|
+
// opening parenthesis is found.
|
1701
1902
|
if(this.op_stack.indexOf('(') < 0) {
|
1702
1903
|
this.error = 'Unmatched \')\'';
|
1703
1904
|
} else if(this.prev_sym === '(' ||
|
1704
1905
|
OPERATOR_CODES.indexOf(this.prev_sym) >= 0) {
|
1705
|
-
// Parenthesis immediately after an operator => missing operand
|
1906
|
+
// Parenthesis immediately after an operator => missing operand.
|
1706
1907
|
this.error = 'Missing operand';
|
1707
1908
|
} else {
|
1708
|
-
// Pop all operators up to and including the matching parenthesis
|
1909
|
+
// Pop all operators up to and including the matching parenthesis.
|
1709
1910
|
vmi = null;
|
1710
1911
|
while(this.op_stack.length > 0 &&
|
1711
1912
|
this.op_stack[this.op_stack.length - 1] !== '(') {
|
1712
|
-
// Pop the operator
|
1913
|
+
// Pop the operator.
|
1713
1914
|
vmi = this.op_stack.pop();
|
1714
1915
|
this.codeOperation(vmi);
|
1715
1916
|
}
|
1716
|
-
// Also pop the opening parenthesis
|
1917
|
+
// Also pop the opening parenthesis.
|
1717
1918
|
this.op_stack.pop();
|
1718
1919
|
}
|
1719
1920
|
} else if(this.sym === VMI_if_else &&
|
@@ -1725,13 +1926,13 @@ class ExpressionParser {
|
|
1725
1926
|
this.op_stack[this.op_stack.length - 1] : null),
|
1726
1927
|
topprio = PRIORITIES[OPERATOR_CODES.indexOf(topop)],
|
1727
1928
|
symprio = PRIORITIES[OPERATOR_CODES.indexOf(this.sym)];
|
1728
|
-
// Pop all operators having a higher or equal priority than the
|
1729
|
-
// to be pushed EXCEPT when this priority equals 9, as monadic
|
1730
|
-
// bind right-to-left
|
1929
|
+
// Pop all operators having a higher or equal priority than the
|
1930
|
+
// one to be pushed EXCEPT when this priority equals 9, as monadic
|
1931
|
+
// operators bind right-to-left.
|
1731
1932
|
while(this.op_stack.length > 0 && OPERATOR_CODES.indexOf(topop) >= 0 &&
|
1732
1933
|
topprio >= symprio && symprio !== 9) {
|
1733
1934
|
// The stack may be emptied, but if it contains a (, this
|
1734
|
-
// parenthesis is unmatched
|
1935
|
+
// parenthesis is unmatched.
|
1735
1936
|
if(topop === '(') {
|
1736
1937
|
this.error = 'Missing \')\'';
|
1737
1938
|
} else {
|
@@ -1747,27 +1948,27 @@ class ExpressionParser {
|
|
1747
1948
|
}
|
1748
1949
|
}
|
1749
1950
|
|
1750
|
-
// NOTE:
|
1951
|
+
// NOTE: As of version 1.0.14, (a ? b : c) is implemented with
|
1751
1952
|
// "jump"-instructions so that only b OR c is evaluated instead
|
1752
|
-
// of both
|
1953
|
+
// of both.
|
1753
1954
|
if(this.sym === VMI_if_then) {
|
1754
1955
|
// Push index of JUMP-IF-FALSE instruction on if_stack so that
|
1755
1956
|
// later its dummy argument (NULL) can be replaced by the
|
1756
|
-
// index of the first instruction after the THEN part
|
1957
|
+
// index of the first instruction after the THEN part.
|
1757
1958
|
this.if_stack.push(this.code.length);
|
1758
1959
|
this.code.push([VMI_jump_if_false, null]);
|
1759
1960
|
} else if(this.sym === VMI_if_else) {
|
1760
1961
|
this.then_stack.push(this.code.length);
|
1761
1962
|
this.code.push([VMI_jump, null]);
|
1762
|
-
// NOTE:
|
1763
|
-
// start by popping the FALSE result of the IF condition
|
1963
|
+
// NOTE: If : is not omitted, the code for the ELSE part must
|
1964
|
+
// start by popping the FALSE result of the IF condition.
|
1764
1965
|
this.code.push([VMI_pop_false, null]);
|
1765
1966
|
}
|
1766
1967
|
// END of new code for IF-THEN-ELSE
|
1767
1968
|
|
1768
1969
|
this.op_stack.push(this.sym);
|
1769
1970
|
} else if(this.sym !== null) {
|
1770
|
-
// Symbol is an operand
|
1971
|
+
// Symbol is an operand.
|
1771
1972
|
if(CONSTANT_CODES.indexOf(this.sym) >= 0) {
|
1772
1973
|
this.code.push([this.sym, null]);
|
1773
1974
|
} else if(Array.isArray(this.sym)) {
|
@@ -1778,6 +1979,8 @@ class ExpressionParser {
|
|
1778
1979
|
this.code.push([VMI_push_statistic, this.sym]);
|
1779
1980
|
} else if(this.sym[0].hasOwnProperty('d')) {
|
1780
1981
|
this.code.push([VMI_push_dataset_modifier, this.sym]);
|
1982
|
+
} else if(this.sym[0].hasOwnProperty('meq')) {
|
1983
|
+
this.code.push([VMI_push_method, this.sym]);
|
1781
1984
|
} else if(this.sym[0].hasOwnProperty('ee')) {
|
1782
1985
|
this.code.push([VMI_push_wildcard_entity, this.sym]);
|
1783
1986
|
} else if(this.sym[0].hasOwnProperty('x')) {
|
@@ -1811,6 +2014,21 @@ class ExpressionParser {
|
|
1811
2014
|
this.error = 'Invalid parameter list';
|
1812
2015
|
}
|
1813
2016
|
}
|
2017
|
+
// When compiling a method, check for all eligible prefixes whether
|
2018
|
+
// they might make the expression dynamic.
|
2019
|
+
if(this.is_static && this.eligible_prefixes) {
|
2020
|
+
const epl = Object.keys(this.eligible_prefixes);
|
2021
|
+
for(let i = 0; i < epl.length; i++) {
|
2022
|
+
const ep = epl[i];
|
2023
|
+
for(let j = 0; j < ep.length; j++) {
|
2024
|
+
if(ep[j] instanceof Dataset && ep[j].mayBeDynamic) {
|
2025
|
+
this.is_static = false;
|
2026
|
+
this.log('dynamic because some modifiers of eligible datasets are dynamic');
|
2027
|
+
break;
|
2028
|
+
}
|
2029
|
+
}
|
2030
|
+
}
|
2031
|
+
}
|
1814
2032
|
if(this.TRACE || DEBUGGING) console.log('PARSED', this.ownerName, ':',
|
1815
2033
|
this.expr, this.code);
|
1816
2034
|
}
|
@@ -2741,7 +2959,7 @@ class VirtualMachine {
|
|
2741
2959
|
return prior_level;
|
2742
2960
|
}
|
2743
2961
|
|
2744
|
-
variablesLegend(
|
2962
|
+
variablesLegend() {
|
2745
2963
|
// Return a string with each variable code and full name on a
|
2746
2964
|
// separate line.
|
2747
2965
|
const
|
@@ -4743,8 +4961,12 @@ class VirtualMachine {
|
|
4743
4961
|
// than the standard, as it should not go beyond the end time plus
|
4744
4962
|
// look-ahead.
|
4745
4963
|
if(this.block_count < this.nr_of_blocks) return this.chunk_length;
|
4746
|
-
|
4747
|
-
|
4964
|
+
// Last block length equals remainder of simulation period divided
|
4965
|
+
// by block length.
|
4966
|
+
let rem = (MODEL.runLength - MODEL.look_ahead) % MODEL.block_length;
|
4967
|
+
// If this remainder equals 0, the last block is a full chunk.
|
4968
|
+
if(rem === 0) return this.chunk_length;
|
4969
|
+
// Otherwise, the last block if remainder + look-ahead time steps.
|
4748
4970
|
return rem + MODEL.look_ahead;
|
4749
4971
|
}
|
4750
4972
|
|
@@ -5529,96 +5751,99 @@ Solver status = ${json.status}`);
|
|
5529
5751
|
// automaton instruction has parameters x and a, where x is the computing
|
5530
5752
|
// expression and a the argument, which may be a single number or a list
|
5531
5753
|
// (array) of objects. When no arguments need to be passed, the second
|
5532
|
-
// parameter is
|
5754
|
+
// parameter is omitted.
|
5533
5755
|
|
5534
5756
|
function VMI_push_number(x, number) {
|
5535
|
-
//
|
5757
|
+
// Push a numeric constant on the VM stack.
|
5536
5758
|
if(DEBUGGING) console.log('push number = ' + number);
|
5537
5759
|
x.push(number);
|
5538
5760
|
}
|
5539
5761
|
|
5540
|
-
function VMI_push_time_step(x
|
5541
|
-
//
|
5542
|
-
// NOTE:
|
5543
|
-
// starts at 1), adjusted for the first time step of the simulation period
|
5762
|
+
function VMI_push_time_step(x) {
|
5763
|
+
// Push the current time step.
|
5764
|
+
// NOTE: This is the "local" time step for expression `x` (which always
|
5765
|
+
// starts at 1), adjusted for the first time step of the simulation period.
|
5544
5766
|
const t = x.step[x.step.length - 1] + MODEL.start_period - 1;
|
5545
5767
|
if(DEBUGGING) console.log('push absolute t = ' + t);
|
5546
5768
|
x.push(t);
|
5547
5769
|
}
|
5548
5770
|
|
5549
|
-
function VMI_push_delta_t(x
|
5550
|
-
//
|
5771
|
+
function VMI_push_delta_t(x) {
|
5772
|
+
// Push the duration of 1 time step (in hours).
|
5551
5773
|
const dt = MODEL.time_scale * VM.time_unit_values[MODEL.time_unit];
|
5552
5774
|
if(DEBUGGING) console.log('push delta-t = ' + dt);
|
5553
5775
|
x.push(dt);
|
5554
5776
|
}
|
5555
5777
|
|
5556
|
-
function VMI_push_relative_time(x
|
5557
|
-
//
|
5778
|
+
function VMI_push_relative_time(x) {
|
5779
|
+
// Push the "local" time step for expression `x`.
|
5780
|
+
// NOTE: Time step for optimization period always starts at 1.
|
5558
5781
|
const t = x.step[x.step.length - 1];
|
5559
5782
|
if(DEBUGGING) console.log('push relative t = ' + t);
|
5560
5783
|
x.push(t);
|
5561
5784
|
}
|
5562
5785
|
|
5563
|
-
function VMI_push_block_time(x
|
5564
|
-
//
|
5565
|
-
// adjusted for the first time step of the current block
|
5566
|
-
const
|
5567
|
-
|
5568
|
-
|
5786
|
+
function VMI_push_block_time(x) {
|
5787
|
+
// Push the "local" time step for expression `x` (which always starts
|
5788
|
+
// at 1) adjusted for the first time step of the current block.
|
5789
|
+
const
|
5790
|
+
lt = x.step[x.step.length - 1] - 1,
|
5791
|
+
bnr = Math.floor(lt / MODEL.block_length),
|
5792
|
+
t = lt - bnr * MODEL.block_length + 1;
|
5569
5793
|
if(DEBUGGING) console.log('push block time bt = ' + t);
|
5570
5794
|
x.push(t);
|
5571
5795
|
}
|
5572
5796
|
|
5573
|
-
function VMI_push_block_number(x
|
5574
|
-
//
|
5797
|
+
function VMI_push_block_number(x) {
|
5798
|
+
// Push the number of the block currently being optimized.
|
5799
|
+
// NOTE: Block numbering starts at 1.
|
5575
5800
|
const local_t = x.step[x.step.length - 1] - 1,
|
5576
5801
|
bnr = Math.floor(local_t / MODEL.block_length) + 1;
|
5577
5802
|
if(DEBUGGING) console.log('push current block number = ' + bnr);
|
5578
5803
|
x.push(bnr);
|
5579
5804
|
}
|
5580
5805
|
|
5581
|
-
function VMI_push_run_length(x
|
5582
|
-
//
|
5806
|
+
function VMI_push_run_length(x) {
|
5807
|
+
// Push the run length (excl. look-ahead!).
|
5583
5808
|
const n = MODEL.end_period - MODEL.start_period + 1;
|
5584
5809
|
if(DEBUGGING) console.log('push run length N = ' + n);
|
5585
5810
|
x.push(n);
|
5586
5811
|
}
|
5587
5812
|
|
5588
|
-
function VMI_push_block_length(x
|
5589
|
-
//
|
5813
|
+
function VMI_push_block_length(x) {
|
5814
|
+
// Push the block length.
|
5590
5815
|
if(DEBUGGING) console.log('push block length n = ' + MODEL.block_length);
|
5591
5816
|
x.push(MODEL.block_length);
|
5592
5817
|
}
|
5593
5818
|
|
5594
|
-
function VMI_push_look_ahead(x
|
5595
|
-
//
|
5819
|
+
function VMI_push_look_ahead(x) {
|
5820
|
+
// Push the look-ahead.
|
5596
5821
|
if(DEBUGGING) console.log('push look-ahead l = ' + MODEL.look_ahead);
|
5597
5822
|
x.push(MODEL.look_ahead);
|
5598
5823
|
}
|
5599
5824
|
|
5600
|
-
function VMI_push_round(x
|
5601
|
-
//
|
5825
|
+
function VMI_push_round(x) {
|
5826
|
+
// Push the current round number (a=1, z=26, etc.).
|
5602
5827
|
const r = VM.round_letters.indexOf(VM.round_sequence[VM.current_round]);
|
5603
5828
|
if(DEBUGGING) console.log('push round number R = ' + r);
|
5604
5829
|
x.push(r);
|
5605
5830
|
}
|
5606
5831
|
|
5607
|
-
function VMI_push_last_round(x
|
5608
|
-
//
|
5832
|
+
function VMI_push_last_round(x) {
|
5833
|
+
// Push the last round number (a=1, z=26, etc.).
|
5609
5834
|
const r = VM.round_letters.indexOf(VM.round_sequence[MODEL.rounds - 1]);
|
5610
5835
|
if(DEBUGGING) console.log('push last round number LR = ' + r);
|
5611
5836
|
x.push(r);
|
5612
5837
|
}
|
5613
5838
|
|
5614
|
-
function VMI_push_number_of_rounds(x
|
5615
|
-
//
|
5839
|
+
function VMI_push_number_of_rounds(x) {
|
5840
|
+
// Push the number of rounds (= length of round sequence).
|
5616
5841
|
if(DEBUGGING) console.log('push number of rounds NR = ' + MODEL.rounds);
|
5617
5842
|
x.push(MODEL.rounds);
|
5618
5843
|
}
|
5619
5844
|
|
5620
|
-
function VMI_push_run_number(x
|
5621
|
-
//
|
5845
|
+
function VMI_push_run_number(x) {
|
5846
|
+
// Push the number of the current run in the selected experiment (or 0).
|
5622
5847
|
const
|
5623
5848
|
sx = EXPERIMENT_MANAGER.selected_experiment,
|
5624
5849
|
nox = (sx ? ` (in ${sx.title})` : ' (no experiment)'),
|
@@ -5627,8 +5852,8 @@ function VMI_push_run_number(x, empty) {
|
|
5627
5852
|
x.push(xr);
|
5628
5853
|
}
|
5629
5854
|
|
5630
|
-
function VMI_push_number_of_runs(x
|
5631
|
-
//
|
5855
|
+
function VMI_push_number_of_runs(x) {
|
5856
|
+
// Push the number of runs in the current experiment (0 if no experiment).
|
5632
5857
|
const
|
5633
5858
|
sx = EXPERIMENT_MANAGER.selected_experiment,
|
5634
5859
|
nox = (sx ? `(in ${sx.title})` : '(no experiment)'),
|
@@ -5637,40 +5862,41 @@ function VMI_push_number_of_runs(x, empty) {
|
|
5637
5862
|
x.push(nx);
|
5638
5863
|
}
|
5639
5864
|
|
5640
|
-
function VMI_push_random(x
|
5641
|
-
//
|
5865
|
+
function VMI_push_random(x) {
|
5866
|
+
// Push a random number from the interval [0, 1).
|
5642
5867
|
const r = Math.random();
|
5643
5868
|
if(DEBUGGING) console.log('push random =', r);
|
5644
5869
|
x.push(r);
|
5645
5870
|
}
|
5646
5871
|
|
5647
|
-
function VMI_push_pi(x
|
5648
|
-
//
|
5872
|
+
function VMI_push_pi(x) {
|
5873
|
+
// Push the goniometric constant pi.
|
5649
5874
|
if(DEBUGGING) console.log('push pi');
|
5650
5875
|
x.push(Math.PI);
|
5651
5876
|
}
|
5652
5877
|
|
5653
|
-
function VMI_push_true(x
|
5654
|
-
//
|
5878
|
+
function VMI_push_true(x) {
|
5879
|
+
// Push the Boolean constant TRUE.
|
5655
5880
|
if(DEBUGGING) console.log('push TRUE');
|
5656
5881
|
x.push(1);
|
5657
5882
|
}
|
5658
5883
|
|
5659
|
-
function VMI_push_false(x
|
5660
|
-
//
|
5884
|
+
function VMI_push_false(x) {
|
5885
|
+
// Push the Boolean constant FALSE.
|
5661
5886
|
if(DEBUGGING) console.log('push FALSE');
|
5662
5887
|
x.push(0);
|
5663
5888
|
}
|
5664
5889
|
|
5665
|
-
function VMI_push_infinity(x
|
5666
|
-
//
|
5890
|
+
function VMI_push_infinity(x) {
|
5891
|
+
// Push the constant representing infinity for the solver.
|
5667
5892
|
if(DEBUGGING) console.log('push +INF');
|
5668
5893
|
x.push(VM.PLUS_INFINITY);
|
5669
5894
|
}
|
5670
5895
|
|
5671
5896
|
function valueOfIndexVariable(v) {
|
5672
|
-
// AUXILIARY FUNCTION for the VMI_push_(i, j or k) instructions
|
5673
|
-
//
|
5897
|
+
// AUXILIARY FUNCTION for the VMI_push_(i, j or k) instructions.
|
5898
|
+
// Return the value of the iterator index variable for the current
|
5899
|
+
// experiment.
|
5674
5900
|
if(MODEL.running_experiment) {
|
5675
5901
|
const
|
5676
5902
|
lead = v + '=',
|
@@ -5683,68 +5909,69 @@ function valueOfIndexVariable(v) {
|
|
5683
5909
|
return 0;
|
5684
5910
|
}
|
5685
5911
|
|
5686
|
-
function VMI_push_i(x
|
5687
|
-
//
|
5912
|
+
function VMI_push_i(x) {
|
5913
|
+
// Push the value of iterator index i.
|
5688
5914
|
const i = valueOfIndexVariable('i');
|
5689
5915
|
if(DEBUGGING) console.log('push i = ' + i);
|
5690
5916
|
x.push(i);
|
5691
5917
|
}
|
5692
5918
|
|
5693
|
-
function VMI_push_j(x
|
5694
|
-
//
|
5919
|
+
function VMI_push_j(x) {
|
5920
|
+
// Push the value of iterator index j.
|
5695
5921
|
const j = valueOfIndexVariable('j');
|
5696
5922
|
if(DEBUGGING) console.log('push j = ' + j);
|
5697
5923
|
x.push(j);
|
5698
5924
|
}
|
5699
5925
|
|
5700
|
-
function VMI_push_k(x
|
5701
|
-
//
|
5926
|
+
function VMI_push_k(x) {
|
5927
|
+
// Push the value of iterator index k.
|
5702
5928
|
const k = valueOfIndexVariable('k');
|
5703
5929
|
if(DEBUGGING) console.log('push k = ' + k);
|
5704
5930
|
x.push(k);
|
5705
5931
|
}
|
5706
5932
|
|
5707
5933
|
function pushTimeStepsPerTimeUnit(x, unit) {
|
5708
|
-
// AUXILIARY FUNCTION for the VMI_push_(time unit) instructions
|
5709
|
-
//
|
5934
|
+
// AUXILIARY FUNCTION for the VMI_push_(time unit) instructions.
|
5935
|
+
// Push the number of model time steps represented by 1 unit.
|
5936
|
+
// NOTE: This will typically be a real number -- no rounding.
|
5710
5937
|
const t = VM.time_unit_values[unit] / MODEL.time_scale /
|
5711
5938
|
VM.time_unit_values[MODEL.time_unit];
|
5712
5939
|
if(DEBUGGING) console.log(`push ${unit} = ${VM.sig4Dig(t)}`);
|
5713
5940
|
x.push(t);
|
5714
5941
|
}
|
5715
5942
|
|
5716
|
-
function VMI_push_year(x
|
5717
|
-
//
|
5943
|
+
function VMI_push_year(x) {
|
5944
|
+
// Push the number of time steps in one year.
|
5718
5945
|
pushTimeStepsPerTimeUnit(x, 'year');
|
5719
5946
|
}
|
5720
5947
|
|
5721
|
-
function VMI_push_week(x
|
5722
|
-
//
|
5948
|
+
function VMI_push_week(x) {
|
5949
|
+
// Push the number of time steps in one week.
|
5723
5950
|
pushTimeStepsPerTimeUnit(x, 'week');
|
5724
5951
|
}
|
5725
5952
|
|
5726
|
-
function VMI_push_day(x
|
5727
|
-
//
|
5953
|
+
function VMI_push_day(x) {
|
5954
|
+
// Push the number of time steps in one day.
|
5728
5955
|
pushTimeStepsPerTimeUnit(x, 'day');
|
5729
5956
|
}
|
5730
5957
|
|
5731
|
-
function VMI_push_hour(x
|
5732
|
-
//
|
5958
|
+
function VMI_push_hour(x) {
|
5959
|
+
// Push the number of time steps in one hour.
|
5733
5960
|
pushTimeStepsPerTimeUnit(x, 'hour');
|
5734
5961
|
}
|
5735
5962
|
|
5736
|
-
function VMI_push_minute(x
|
5737
|
-
//
|
5963
|
+
function VMI_push_minute(x) {
|
5964
|
+
// Push the number of time steps in one minute.
|
5738
5965
|
pushTimeStepsPerTimeUnit(x, 'minute');
|
5739
5966
|
}
|
5740
5967
|
|
5741
|
-
function VMI_push_second(x
|
5742
|
-
//
|
5968
|
+
function VMI_push_second(x) {
|
5969
|
+
// Push the number of time steps in one second.
|
5743
5970
|
pushTimeStepsPerTimeUnit(x, 'second');
|
5744
5971
|
}
|
5745
5972
|
|
5746
|
-
function VMI_push_contextual_number(x
|
5747
|
-
//
|
5973
|
+
function VMI_push_contextual_number(x) {
|
5974
|
+
// Push the numeric value of the context-sensitive number #.
|
5748
5975
|
const n = valueOfNumberSign(x);
|
5749
5976
|
if(DEBUGGING) {
|
5750
5977
|
console.log('push contextual number: # = ' + VM.sig2Dig(n));
|
@@ -5755,11 +5982,12 @@ function VMI_push_contextual_number(x, empty) {
|
|
5755
5982
|
/* VM instruction helper functions */
|
5756
5983
|
|
5757
5984
|
function valueOfNumberSign(x) {
|
5758
|
-
//
|
5759
|
-
// NOTE:
|
5760
|
-
// ending on digits, or
|
5761
|
-
// is the number its name or any of its prefixes ends on, but
|
5762
|
-
// more "creative" and can return the number context of nearby
|
5985
|
+
// Push the numeric value of the # sign for the context of expression `x`.
|
5986
|
+
// NOTE: This can be a wildcard match, an active experiment run selector
|
5987
|
+
// ending on digits, or the number context of an entity. The latter
|
5988
|
+
// typically is the number its name or any of its prefixes ends on, but
|
5989
|
+
// notes are more "creative" and can return the number context of nearby
|
5990
|
+
// entities.
|
5763
5991
|
let s = '!NO SELECTOR',
|
5764
5992
|
m = '!NO MATCH',
|
5765
5993
|
n = VM.UNDEFINED;
|
@@ -5788,7 +6016,7 @@ function valueOfNumberSign(x) {
|
|
5788
6016
|
}
|
5789
6017
|
}
|
5790
6018
|
// If selector contains no wildcards, get number context (typically
|
5791
|
-
// inferred from a number in the name of the object)
|
6019
|
+
// inferred from a number in the name of the object).
|
5792
6020
|
if(s.indexOf('*') < 0 && s.indexOf('?') < 0) {
|
5793
6021
|
const d = x.object.numberContext;
|
5794
6022
|
if(d) {
|
@@ -5798,7 +6026,7 @@ function valueOfNumberSign(x) {
|
|
5798
6026
|
}
|
5799
6027
|
}
|
5800
6028
|
}
|
5801
|
-
// For datasets, set the parent anchor to be the context-sensitive number
|
6029
|
+
// For datasets, set the parent anchor to be the context-sensitive number.
|
5802
6030
|
if(x.object instanceof Dataset) x.object.parent_anchor = n;
|
5803
6031
|
if(DEBUGGING) {
|
5804
6032
|
console.log(`context for # in expression for ${x.variableName}
|
@@ -5809,7 +6037,7 @@ function valueOfNumberSign(x) {
|
|
5809
6037
|
}
|
5810
6038
|
|
5811
6039
|
function relativeTimeStep(t, anchor, offset, dtm, x) {
|
5812
|
-
//
|
6040
|
+
// Return the relative time step, given t, anchor, offset,
|
5813
6041
|
// delta-t-multiplier and the expression being evaluated (to provide
|
5814
6042
|
// context for anchor #).
|
5815
6043
|
// NOTE: t = 1 corresponds with first time step of simulation period.
|
@@ -5829,7 +6057,7 @@ function relativeTimeStep(t, anchor, offset, dtm, x) {
|
|
5829
6057
|
if(DEBUGGING) {
|
5830
6058
|
console.log('Parent anchor', x.object.parent_anchor);
|
5831
6059
|
}
|
5832
|
-
// NOTE: For not array-type datasets, ^ is equivalent to
|
6060
|
+
// NOTE: For not array-type datasets, ^ is equivalent to #.
|
5833
6061
|
return x.object.parent_anchor;
|
5834
6062
|
}
|
5835
6063
|
return valueOfNumberSign(x) + offset;
|
@@ -5868,17 +6096,18 @@ function relativeTimeStep(t, anchor, offset, dtm, x) {
|
|
5868
6096
|
}
|
5869
6097
|
|
5870
6098
|
function twoOffsetTimeStep(t, a1, o1, a2, o2, dtm, x) {
|
5871
|
-
//
|
5872
|
-
// are anchor-offset shorthand for the debugging message,
|
5873
|
-
//
|
5874
|
-
//
|
5875
|
-
//
|
6099
|
+
// Return the list [rt, ao1, ao2] where `rt` is the time step, and
|
6100
|
+
// `ao1` and `ao2` are anchor-offset shorthand for the debugging message,
|
6101
|
+
// given `t`, the two anchors plus offsets, and the delta-t-multiplier.
|
6102
|
+
// NOTES:
|
6103
|
+
// (1) `dtm` will differ from 1 only for experiment results.
|
6104
|
+
// (2) Expression `x` is passed to provide context for evaluation of #.
|
5876
6105
|
let t1 = relativeTimeStep(t, a1, o1, dtm, x),
|
5877
6106
|
ao1 = [' @ ', a1, (o1 > 0 ? '+' : ''), (o1 ? o1 : ''),
|
5878
6107
|
' = ', t1].join(''),
|
5879
6108
|
ao2 = '';
|
5880
6109
|
if(o2 !== o1 || a2 !== a1) {
|
5881
|
-
// Two different offsets => use the midpoint as time (NO aggregation!)
|
6110
|
+
// Two different offsets => use the midpoint as time (NO aggregation!).
|
5882
6111
|
const t2 = relativeTimeStep(t, a2, o2, dtm, x);
|
5883
6112
|
ao2 = [' : ', a2, (o2 > 0 ? '+' : ''), (o2 ? o2 : ''), ' = ', t2].join('');
|
5884
6113
|
t1 = Math.floor((t1 + t2) / 2);
|
@@ -5890,51 +6119,51 @@ function twoOffsetTimeStep(t, a1, o1, a2, o2, dtm, x) {
|
|
5890
6119
|
/* VM instructions (continued) */
|
5891
6120
|
|
5892
6121
|
function VMI_push_var(x, args) {
|
5893
|
-
//
|
6122
|
+
// Push the value of the variable specified by `args`, being the list
|
5894
6123
|
// [obj, anchor1, offset1, anchor2, offset2] where `obj` can be a vector
|
5895
|
-
// or an expression, or a cluster unit balance specifier
|
6124
|
+
// or an expression, or a cluster unit balance specifier.
|
5896
6125
|
const
|
5897
6126
|
obj = args[0],
|
5898
|
-
// NOTE:
|
6127
|
+
// NOTE: Use the "local" time step for expression `x`.
|
5899
6128
|
tot = twoOffsetTimeStep(x.step[x.step.length - 1],
|
5900
6129
|
args[1], args[2], args[3], args[4], 1, x);
|
5901
6130
|
let t = tot[0];
|
5902
|
-
// Negative time step is evaluated as t = 0 (initial value), while t
|
5903
|
-
// optimization period is evaluated as its last time step
|
5904
|
-
// used in a self-referencing variable
|
6131
|
+
// Negative time step is evaluated as t = 0 (initial value), while t
|
6132
|
+
// beyond the optimization period is evaluated as its last time step
|
6133
|
+
// UNLESS t is used in a self-referencing variable.
|
5905
6134
|
const xv = obj.hasOwnProperty('xv');
|
5906
6135
|
if(!xv) {
|
5907
6136
|
t = Math.max(0, Math.min(
|
5908
6137
|
MODEL.end_period - MODEL.start_period + MODEL.look_ahead + 1, t));
|
5909
6138
|
}
|
5910
|
-
// Trace only now that time step t has been computed
|
6139
|
+
// Trace only now that time step t has been computed.
|
5911
6140
|
if(DEBUGGING) {
|
5912
6141
|
console.log('push var:', (xv ? '[SELF]' :
|
5913
6142
|
(obj instanceof Expression ? obj.text : '[' + obj.toString() + ']')),
|
5914
6143
|
tot[1] + ' ' + tot[2]);
|
5915
6144
|
}
|
5916
6145
|
if(Array.isArray(obj)) {
|
5917
|
-
// Object is a vector
|
6146
|
+
// Object is a vector.
|
5918
6147
|
let v = t < obj.length ? obj[t] : VM.UNDEFINED;
|
5919
|
-
// NOTE:
|
5920
|
-
// analysis, the value is multiplied by 1 + delta
|
6148
|
+
// NOTE: When the vector is the "active" parameter for sensitivity
|
6149
|
+
// analysis, the value is multiplied by 1 + delta %.
|
5921
6150
|
if(obj === MODEL.active_sensitivity_parameter) {
|
5922
|
-
// NOTE:
|
6151
|
+
// NOTE: Do NOT scale exceptional values.
|
5923
6152
|
if(v > VM.MINUS_INFINITY && v < VM.PLUS_INFINITY) {
|
5924
6153
|
v *= (1 + MODEL.sensitivity_delta * 0.01);
|
5925
6154
|
}
|
5926
6155
|
}
|
5927
6156
|
x.push(v);
|
5928
6157
|
} else if(xv) {
|
5929
|
-
// Variable references an earlier value computed for this expression `x
|
6158
|
+
// Variable references an earlier value computed for this expression `x`.
|
5930
6159
|
x.push(t >= 0 && t < x.vector.length ? x.vector[t] : obj.dv);
|
5931
6160
|
} else if(obj.hasOwnProperty('c') && obj.hasOwnProperty('u')) {
|
5932
|
-
// Object holds link lists for cluster balance computation
|
6161
|
+
// Object holds link lists for cluster balance computation.
|
5933
6162
|
x.push(MODEL.flowBalance(obj, t));
|
5934
6163
|
} else if(obj instanceof Expression) {
|
5935
6164
|
x.push(obj.result(t));
|
5936
6165
|
} else if(typeof obj === 'number') {
|
5937
|
-
// Object is a number
|
6166
|
+
// Object is a number.
|
5938
6167
|
x.push(obj);
|
5939
6168
|
} else {
|
5940
6169
|
console.log('ERROR: VMI_push_var object =', obj);
|
@@ -5943,17 +6172,17 @@ function VMI_push_var(x, args) {
|
|
5943
6172
|
}
|
5944
6173
|
|
5945
6174
|
function VMI_push_entity(x, args) {
|
5946
|
-
//
|
6175
|
+
// Push a special "entity reference" object based on `args`, being the
|
5947
6176
|
// list [obj, anchor1, offset1, anchor2, offset2] where `obj` has the
|
5948
|
-
// format {r: entity object, a: attribute}
|
5949
|
-
// The object that is pushed on the stack passes the entity, the
|
5950
|
-
// to use, and the time interval
|
6177
|
+
// format {r: entity object, a: attribute}.
|
6178
|
+
// The object that is pushed on the stack passes the entity, the
|
6179
|
+
// attribute to use, and the time interval.
|
5951
6180
|
const
|
5952
|
-
// NOTE:
|
6181
|
+
// NOTE: Use the "local" time step for expression `x`.
|
5953
6182
|
tot = twoOffsetTimeStep(x.step[x.step.length - 1],
|
5954
6183
|
args[1], args[2], args[3], args[4], 1, x),
|
5955
6184
|
er = {entity: args[0].r, attribute: args[0].a, t1: tot[0], t2: tot[1]};
|
5956
|
-
// Trace only now that time step t has been computed
|
6185
|
+
// Trace only now that time step t has been computed.
|
5957
6186
|
if(DEBUGGING) {
|
5958
6187
|
console.log(['push entity: ', er.entity.displayName, '|', er.attribute,
|
5959
6188
|
', t = ', er.t1, ' - ', er.t2].join(''));
|
@@ -5961,27 +6190,94 @@ function VMI_push_entity(x, args) {
|
|
5961
6190
|
x.push(er);
|
5962
6191
|
}
|
5963
6192
|
|
6193
|
+
function VMI_push_method(x, args) {
|
6194
|
+
// Push the result of the expression associated with the method (a
|
6195
|
+
// dataset modifier with a selector that starts with a colon).
|
6196
|
+
// The first element of the argument list specifies the method,
|
6197
|
+
// and possibly also the entity to be used as its object.
|
6198
|
+
// NOTE: Methods can only be called "as is" (without prefix) in a
|
6199
|
+
// method expression. The object of such "as is" method calls is
|
6200
|
+
// the object of the calling method expression `x`.
|
6201
|
+
const
|
6202
|
+
method = args[0].meq,
|
6203
|
+
mex = method.expression,
|
6204
|
+
// NOTE: If method object prefix is not specified in the first
|
6205
|
+
// argument, use the MOP of the calling method (if specified).
|
6206
|
+
mo_prefix = args[0].mo || x.method_object_prefix,
|
6207
|
+
// NOTE: Use the "local" time step for expression `x`.
|
6208
|
+
tot = twoOffsetTimeStep(x.step[x.step.length - 1],
|
6209
|
+
args[1], args[2], args[3], args[4], 1, x);
|
6210
|
+
if(!x.method_object && !mo_prefix) {
|
6211
|
+
console.log('ERROR: Undefined method object', x);
|
6212
|
+
x.push(VM.BAD_REF);
|
6213
|
+
return;
|
6214
|
+
}
|
6215
|
+
if(x.method_object) {
|
6216
|
+
// Set the method object to be used in VMI_push_wildcard_entity.
|
6217
|
+
mex.method_object = x.method_object;
|
6218
|
+
} else if(mex.isEligible(mo_prefix)) {
|
6219
|
+
mex.method_object_prefix = mo_prefix;
|
6220
|
+
} else {
|
6221
|
+
console.log('ERROR: ', mo_prefix, 'is not in eligible list of',
|
6222
|
+
method.selector, method.eligible_prefixes);
|
6223
|
+
x.push(VM.BAD_REF);
|
6224
|
+
return;
|
6225
|
+
}
|
6226
|
+
const
|
6227
|
+
t = tot[0],
|
6228
|
+
v = mex.result(t);
|
6229
|
+
// Clear the method object & prefix -- just to be neat.
|
6230
|
+
mex.method_object = null;
|
6231
|
+
mex.method_object_prefix = '';
|
6232
|
+
// Trace only now that time step t has been computed.
|
6233
|
+
if(DEBUGGING) {
|
6234
|
+
console.log('push method:', obj.displayName, method.selector,
|
6235
|
+
tot[1] + (tot[2] ? ':' + tot[2] : ''), 'value =', VM.sig4Dig(v));
|
6236
|
+
}
|
6237
|
+
x.push(v);
|
6238
|
+
}
|
6239
|
+
|
5964
6240
|
function VMI_push_wildcard_entity(x, args) {
|
5965
|
-
//
|
6241
|
+
// Push the value of (or reference to) an entity attribute, based on
|
5966
6242
|
// `args`, being the list [obj, anchor1, offset1, anchor2, offset2]
|
5967
6243
|
// where `obj` has the format {ee: list of eligible entities,
|
5968
6244
|
// n: name (with wildcard #), a: attribute, br: by reference (boolean)}
|
5969
|
-
|
5970
|
-
|
6245
|
+
let obj = null,
|
6246
|
+
nn = args[0].n;
|
5971
6247
|
const el = args[0].ee;
|
5972
|
-
|
5973
|
-
|
5974
|
-
|
5975
|
-
|
5976
|
-
|
5977
|
-
|
5978
|
-
|
5979
|
-
|
5980
|
-
|
5981
|
-
|
6248
|
+
// NOTE: Variables in method expressions that reference the object of
|
6249
|
+
// the method also code with this VM instruction, but then pass the
|
6250
|
+
// string "MO" instead of the list of eligible entities. This indicates
|
6251
|
+
// that the `method_object` property of expression `x` should be used.
|
6252
|
+
if(el === 'MO') {
|
6253
|
+
obj = x.method_object;
|
6254
|
+
if(!obj && x.method_object_prefix) {
|
6255
|
+
// Try to identity the object by the prefixed name.
|
6256
|
+
obj = MODEL.objectByName(x.method_object_prefix +
|
6257
|
+
UI.PREFIXER + nn.substring(1));
|
6258
|
+
}
|
6259
|
+
if(!obj) {
|
6260
|
+
console.log(`ERROR: Undefined method object`, x);
|
6261
|
+
x.push(VM.BAD_REF);
|
6262
|
+
return;
|
6263
|
+
}
|
6264
|
+
} else {
|
6265
|
+
// Select the first entity in `ee` that matches the wildcard vector
|
6266
|
+
// index of the expression `x` being executed.
|
6267
|
+
nn = nn.replace('#', x.wildcard_vector_index);
|
6268
|
+
for(let i = 0; !obj && i < el.length; i++) {
|
6269
|
+
if(el[i].name === nn) obj = el[i];
|
6270
|
+
}
|
6271
|
+
// If no match, then this indicates a bad reference.
|
6272
|
+
if(!obj) {
|
6273
|
+
console.log(`ERROR: no match for "${nn}" in eligible entity list`, el);
|
6274
|
+
x.push(VM.BAD_REF);
|
6275
|
+
return;
|
6276
|
+
}
|
5982
6277
|
}
|
5983
|
-
//
|
5984
|
-
//
|
6278
|
+
// Now `obj` should be an existing model entity.
|
6279
|
+
// If args[0] indicates "by reference", then VMI_push_entity can be
|
6280
|
+
// called with the appropriate parameters.
|
5985
6281
|
const attr = args[0].a || obj.defaultAttribute;
|
5986
6282
|
if(args[0].br) {
|
5987
6283
|
VMI_push_entity(x, {r: obj, a: attr});
|
@@ -6391,7 +6687,7 @@ function VMI_push_statistic(x, args) {
|
|
6391
6687
|
x.push(VM.UNDEFINED);
|
6392
6688
|
}
|
6393
6689
|
|
6394
|
-
function VMI_replace_undefined(x
|
6690
|
+
function VMI_replace_undefined(x) {
|
6395
6691
|
// Replaces one of the two top numbers on the stack by the other if the one
|
6396
6692
|
// is undefined
|
6397
6693
|
const d = x.pop(true); // TRUE denotes that "undefined" should be ignored as issue
|
@@ -6404,7 +6700,7 @@ function VMI_replace_undefined(x, empty) {
|
|
6404
6700
|
// NOTE: when the VM computes logical OR, AND and NOT, any non-zero number
|
6405
6701
|
// is interpreted as TRUE
|
6406
6702
|
|
6407
|
-
function VMI_or(x
|
6703
|
+
function VMI_or(x) {
|
6408
6704
|
// Performs a logical OR on the two top numbers on the stack
|
6409
6705
|
const d = x.pop();
|
6410
6706
|
if(d !== false) {
|
@@ -6413,7 +6709,7 @@ function VMI_or(x, empty) {
|
|
6413
6709
|
}
|
6414
6710
|
}
|
6415
6711
|
|
6416
|
-
function VMI_and(x
|
6712
|
+
function VMI_and(x) {
|
6417
6713
|
// Performs a logical AND on the two top numbers on the stack
|
6418
6714
|
const d = x.pop();
|
6419
6715
|
if(d !== false) {
|
@@ -6422,7 +6718,7 @@ function VMI_and(x, empty) {
|
|
6422
6718
|
}
|
6423
6719
|
}
|
6424
6720
|
|
6425
|
-
function VMI_not(x
|
6721
|
+
function VMI_not(x) {
|
6426
6722
|
// Performs a logical NOT on the top number of the stack
|
6427
6723
|
const d = x.top();
|
6428
6724
|
if(d !== false) {
|
@@ -6431,7 +6727,7 @@ function VMI_not(x, empty) {
|
|
6431
6727
|
}
|
6432
6728
|
}
|
6433
6729
|
|
6434
|
-
function VMI_abs(x
|
6730
|
+
function VMI_abs(x) {
|
6435
6731
|
// Replaces the top number of the stack by its absolute value
|
6436
6732
|
const d = x.top();
|
6437
6733
|
if(d !== false) {
|
@@ -6440,7 +6736,7 @@ function VMI_abs(x, empty) {
|
|
6440
6736
|
}
|
6441
6737
|
}
|
6442
6738
|
|
6443
|
-
function VMI_eq(x
|
6739
|
+
function VMI_eq(x) {
|
6444
6740
|
// Tests equality of the two top numbers on the stack
|
6445
6741
|
const d = x.pop();
|
6446
6742
|
if(d !== false) {
|
@@ -6449,7 +6745,7 @@ function VMI_eq(x, empty) {
|
|
6449
6745
|
}
|
6450
6746
|
}
|
6451
6747
|
|
6452
|
-
function VMI_ne(x
|
6748
|
+
function VMI_ne(x) {
|
6453
6749
|
// Tests inequality of the two top numbers on the stack
|
6454
6750
|
const d = x.pop();
|
6455
6751
|
if(d !== false) {
|
@@ -6458,7 +6754,7 @@ function VMI_ne(x, empty) {
|
|
6458
6754
|
}
|
6459
6755
|
}
|
6460
6756
|
|
6461
|
-
function VMI_lt(x
|
6757
|
+
function VMI_lt(x) {
|
6462
6758
|
// Tests whether second number on the stack is less than the top number
|
6463
6759
|
const d = x.pop();
|
6464
6760
|
if(d !== false) {
|
@@ -6467,7 +6763,7 @@ function VMI_lt(x, empty) {
|
|
6467
6763
|
}
|
6468
6764
|
}
|
6469
6765
|
|
6470
|
-
function VMI_gt(x
|
6766
|
+
function VMI_gt(x) {
|
6471
6767
|
// Tests whether second number on the stack is greater than the top number
|
6472
6768
|
const d = x.pop();
|
6473
6769
|
if(d !== false) {
|
@@ -6476,7 +6772,7 @@ function VMI_gt(x, empty) {
|
|
6476
6772
|
}
|
6477
6773
|
}
|
6478
6774
|
|
6479
|
-
function VMI_le(x
|
6775
|
+
function VMI_le(x) {
|
6480
6776
|
// Tests whether second number on the stack is less than, or equal to,
|
6481
6777
|
// the top number
|
6482
6778
|
const d = x.pop();
|
@@ -6486,7 +6782,7 @@ function VMI_le(x, empty) {
|
|
6486
6782
|
}
|
6487
6783
|
}
|
6488
6784
|
|
6489
|
-
function VMI_ge(x
|
6785
|
+
function VMI_ge(x) {
|
6490
6786
|
// Tests whether second number on the stack is greater than, or equal to,
|
6491
6787
|
// the top number
|
6492
6788
|
const d = x.pop();
|
@@ -6496,7 +6792,7 @@ function VMI_ge(x, empty) {
|
|
6496
6792
|
}
|
6497
6793
|
}
|
6498
6794
|
|
6499
|
-
function VMI_add(x
|
6795
|
+
function VMI_add(x) {
|
6500
6796
|
// Pops the top number on the stack and adds it to the new top number
|
6501
6797
|
const d = x.pop();
|
6502
6798
|
if(d !== false) {
|
@@ -6505,7 +6801,7 @@ function VMI_add(x, empty) {
|
|
6505
6801
|
}
|
6506
6802
|
}
|
6507
6803
|
|
6508
|
-
function VMI_sub(x
|
6804
|
+
function VMI_sub(x) {
|
6509
6805
|
// Pops the top number on the stack and subtracts it from the new
|
6510
6806
|
// top number
|
6511
6807
|
const d = x.pop();
|
@@ -6515,7 +6811,7 @@ function VMI_sub(x, empty) {
|
|
6515
6811
|
}
|
6516
6812
|
}
|
6517
6813
|
|
6518
|
-
function VMI_mul(x
|
6814
|
+
function VMI_mul(x) {
|
6519
6815
|
// Pops the top number on the stack and multiplies it with the new
|
6520
6816
|
// top number
|
6521
6817
|
const d = x.pop();
|
@@ -6525,7 +6821,7 @@ function VMI_mul(x, empty) {
|
|
6525
6821
|
}
|
6526
6822
|
}
|
6527
6823
|
|
6528
|
-
function VMI_div(x
|
6824
|
+
function VMI_div(x) {
|
6529
6825
|
// Pops the top number on the stack and divides the new top number
|
6530
6826
|
// by it. In case of division by zero, the top is replaced by #DIV0!
|
6531
6827
|
const d = x.pop();
|
@@ -6539,7 +6835,7 @@ function VMI_div(x, empty) {
|
|
6539
6835
|
}
|
6540
6836
|
}
|
6541
6837
|
|
6542
|
-
function VMI_mod(x
|
6838
|
+
function VMI_mod(x) {
|
6543
6839
|
// Pops the top number on the stack, divides the new top number by it
|
6544
6840
|
// (if non-zero, or it pushes error code #DIV0!), takes the fraction
|
6545
6841
|
// part, and multiplies this with the divider; in other words, it
|
@@ -6555,7 +6851,7 @@ function VMI_mod(x, empty) {
|
|
6555
6851
|
}
|
6556
6852
|
}
|
6557
6853
|
|
6558
|
-
function VMI_negate(x
|
6854
|
+
function VMI_negate(x) {
|
6559
6855
|
// Performs a negation on the top number of the stack
|
6560
6856
|
const d = x.top();
|
6561
6857
|
if(d !== false) {
|
@@ -6564,7 +6860,7 @@ function VMI_negate(x, empty) {
|
|
6564
6860
|
}
|
6565
6861
|
}
|
6566
6862
|
|
6567
|
-
function VMI_power(x
|
6863
|
+
function VMI_power(x) {
|
6568
6864
|
// Pops the top number on the stack and raises the new top number
|
6569
6865
|
// to its power
|
6570
6866
|
const d = x.pop();
|
@@ -6574,7 +6870,7 @@ function VMI_power(x, empty) {
|
|
6574
6870
|
}
|
6575
6871
|
}
|
6576
6872
|
|
6577
|
-
function VMI_sqrt(x
|
6873
|
+
function VMI_sqrt(x) {
|
6578
6874
|
// Replaces the top number of the stack by its square root, or by
|
6579
6875
|
// error code #VALUE! if the top number is negative
|
6580
6876
|
const d = x.top();
|
@@ -6588,7 +6884,7 @@ function VMI_sqrt(x, empty) {
|
|
6588
6884
|
}
|
6589
6885
|
}
|
6590
6886
|
|
6591
|
-
function VMI_sin(x
|
6887
|
+
function VMI_sin(x) {
|
6592
6888
|
// Replaces the top number X of the stack by sin(X)
|
6593
6889
|
const d = x.top();
|
6594
6890
|
if(d !== false) {
|
@@ -6597,7 +6893,7 @@ function VMI_sin(x, empty) {
|
|
6597
6893
|
}
|
6598
6894
|
}
|
6599
6895
|
|
6600
|
-
function VMI_cos(x
|
6896
|
+
function VMI_cos(x) {
|
6601
6897
|
// Replaces the top number X of the stack by cos(X)
|
6602
6898
|
const d = x.top();
|
6603
6899
|
if(d !== false) {
|
@@ -6606,7 +6902,7 @@ function VMI_cos(x, empty) {
|
|
6606
6902
|
}
|
6607
6903
|
}
|
6608
6904
|
|
6609
|
-
function VMI_atan(x
|
6905
|
+
function VMI_atan(x) {
|
6610
6906
|
// Replaces the top number X of the stack by atan(X)
|
6611
6907
|
const d = x.top();
|
6612
6908
|
if(d !== false) {
|
@@ -6615,7 +6911,7 @@ function VMI_atan(x, empty) {
|
|
6615
6911
|
}
|
6616
6912
|
}
|
6617
6913
|
|
6618
|
-
function VMI_ln(x
|
6914
|
+
function VMI_ln(x) {
|
6619
6915
|
// Replaces the top number X of the stack by ln(X), or by error
|
6620
6916
|
// code #VALUE! if X is negative
|
6621
6917
|
const d = x.top();
|
@@ -6629,7 +6925,7 @@ function VMI_ln(x, empty) {
|
|
6629
6925
|
}
|
6630
6926
|
}
|
6631
6927
|
|
6632
|
-
function VMI_exp(x
|
6928
|
+
function VMI_exp(x) {
|
6633
6929
|
// Replaces the top number X of the stack by exp(X)
|
6634
6930
|
const d = x.top();
|
6635
6931
|
if(d !== false) {
|
@@ -6638,7 +6934,7 @@ function VMI_exp(x, empty) {
|
|
6638
6934
|
}
|
6639
6935
|
}
|
6640
6936
|
|
6641
|
-
function VMI_log(x
|
6937
|
+
function VMI_log(x) {
|
6642
6938
|
// Pops the top number B from the stack and replaces the new top
|
6643
6939
|
// number A by A log B. NOTE: x = A log B <=> x = ln(B) / ln(A)
|
6644
6940
|
let d = x.pop();
|
@@ -6653,7 +6949,7 @@ function VMI_log(x, empty) {
|
|
6653
6949
|
}
|
6654
6950
|
}
|
6655
6951
|
|
6656
|
-
function VMI_round(x
|
6952
|
+
function VMI_round(x) {
|
6657
6953
|
// Replaces the top number X of the stack by round(X)
|
6658
6954
|
const d = x.top();
|
6659
6955
|
if(d !== false) {
|
@@ -6662,7 +6958,7 @@ function VMI_round(x, empty) {
|
|
6662
6958
|
}
|
6663
6959
|
}
|
6664
6960
|
|
6665
|
-
function VMI_int(x
|
6961
|
+
function VMI_int(x) {
|
6666
6962
|
// Replaces the top number X of the stack by its integer part
|
6667
6963
|
const d = x.top();
|
6668
6964
|
if(d !== false) {
|
@@ -6671,7 +6967,7 @@ function VMI_int(x, empty) {
|
|
6671
6967
|
}
|
6672
6968
|
}
|
6673
6969
|
|
6674
|
-
function VMI_fract(x
|
6970
|
+
function VMI_fract(x) {
|
6675
6971
|
// Replaces the top number X of the stack by its fraction part
|
6676
6972
|
const d = x.top();
|
6677
6973
|
if(d !== false) {
|
@@ -6680,7 +6976,7 @@ function VMI_fract(x, empty) {
|
|
6680
6976
|
}
|
6681
6977
|
}
|
6682
6978
|
|
6683
|
-
function VMI_exponential(x
|
6979
|
+
function VMI_exponential(x) {
|
6684
6980
|
// Replaces the top number X of the stack by a random number from the
|
6685
6981
|
// negative exponential distribution with parameter X (so X is the lambda,
|
6686
6982
|
// and the mean will be 1/X)
|
@@ -6692,7 +6988,7 @@ function VMI_exponential(x, empty) {
|
|
6692
6988
|
}
|
6693
6989
|
}
|
6694
6990
|
|
6695
|
-
function VMI_poisson(x
|
6991
|
+
function VMI_poisson(x) {
|
6696
6992
|
// Replaces the top number X of the stack by a random number from the
|
6697
6993
|
// poisson distribution with parameter X (so X is the mean value lambda)
|
6698
6994
|
const d = x.top();
|
@@ -6703,7 +6999,7 @@ function VMI_poisson(x, empty) {
|
|
6703
6999
|
}
|
6704
7000
|
}
|
6705
7001
|
|
6706
|
-
function VMI_binomial(x
|
7002
|
+
function VMI_binomial(x) {
|
6707
7003
|
// Replaces the top list (!) A of the stack by Bin(A[0], A[1]), i.e., a random
|
6708
7004
|
// number from the binomial distribution with n = A[0] and p = A[1]
|
6709
7005
|
const d = x.top();
|
@@ -6719,7 +7015,7 @@ function VMI_binomial(x, empty) {
|
|
6719
7015
|
}
|
6720
7016
|
}
|
6721
7017
|
|
6722
|
-
function VMI_normal(x
|
7018
|
+
function VMI_normal(x) {
|
6723
7019
|
// Replaces the top list (!) A of the stack by N(A[0], A[1]), i.e., a random
|
6724
7020
|
// number from the normal distribution with mu = A[0] and sigma = A[1]
|
6725
7021
|
const d = x.top();
|
@@ -6735,7 +7031,7 @@ function VMI_normal(x, empty) {
|
|
6735
7031
|
}
|
6736
7032
|
}
|
6737
7033
|
|
6738
|
-
function VMI_weibull(x
|
7034
|
+
function VMI_weibull(x) {
|
6739
7035
|
// Replaces the top list (!) A of the stack by Weibull(A[0], A[1]), i.e., a
|
6740
7036
|
// random number from the Weibull distribution with lambda = A[0] and k = A[1]
|
6741
7037
|
const d = x.top();
|
@@ -6751,7 +7047,7 @@ function VMI_weibull(x, empty) {
|
|
6751
7047
|
}
|
6752
7048
|
}
|
6753
7049
|
|
6754
|
-
function VMI_triangular(x
|
7050
|
+
function VMI_triangular(x) {
|
6755
7051
|
// Replaces the top list (!) A of the stack by Tri(A[0], A[1]), A[2]), i.e.,
|
6756
7052
|
// a random number from the triangular distribution with a = A[0], b = A[1],
|
6757
7053
|
// and c = A[2]. NOTE: if only 2 parameters are passed, c is assumed to equal
|
@@ -6769,7 +7065,7 @@ function VMI_triangular(x, empty) {
|
|
6769
7065
|
}
|
6770
7066
|
}
|
6771
7067
|
|
6772
|
-
function VMI_npv(x
|
7068
|
+
function VMI_npv(x) {
|
6773
7069
|
// Replaces the top list (!) A of the stack by the net present value (NPV)
|
6774
7070
|
// of the arguments in A. A[0] is the interest rate r, A[1] is the number of
|
6775
7071
|
// time periods n. If A has only 1 or 2 elements, the NPV is 0. If A has 3
|
@@ -6809,7 +7105,7 @@ function VMI_npv(x, empty) {
|
|
6809
7105
|
}
|
6810
7106
|
}
|
6811
7107
|
|
6812
|
-
function VMI_min(x
|
7108
|
+
function VMI_min(x) {
|
6813
7109
|
// Replaces the top list (!) A of the stack by the lowest value in this list
|
6814
7110
|
// NOTE: if A is not a list, A is left on the stack
|
6815
7111
|
const d = x.top();
|
@@ -6821,7 +7117,7 @@ function VMI_min(x, empty) {
|
|
6821
7117
|
}
|
6822
7118
|
}
|
6823
7119
|
|
6824
|
-
function VMI_max(x
|
7120
|
+
function VMI_max(x) {
|
6825
7121
|
// Replaces the top list (!) A of the stack by the highest value in this list
|
6826
7122
|
// NOTE: if A is not a list, A is left on the stack
|
6827
7123
|
const d = x.top();
|
@@ -6833,7 +7129,7 @@ function VMI_max(x, empty) {
|
|
6833
7129
|
}
|
6834
7130
|
}
|
6835
7131
|
|
6836
|
-
function VMI_concat(x
|
7132
|
+
function VMI_concat(x) {
|
6837
7133
|
// Pops the top number B from the stack, and then replaces the new top
|
6838
7134
|
// element A by [A, B] if A is a number, or adds B to A is A is a list
|
6839
7135
|
// of numbers (!) or
|
@@ -6878,20 +7174,20 @@ function VMI_jump_if_false(x, index) {
|
|
6878
7174
|
}
|
6879
7175
|
}
|
6880
7176
|
|
6881
|
-
function VMI_pop_false(x
|
7177
|
+
function VMI_pop_false(x) {
|
6882
7178
|
// Removes the top value from the stack, which should be 0 or
|
6883
7179
|
// VM.UNDEFINED (but this is not checked)
|
6884
7180
|
const r = x.stack.pop();
|
6885
7181
|
if(DEBUGGING) console.log(`POP-FALSE (${r})`);
|
6886
7182
|
}
|
6887
7183
|
|
6888
|
-
function VMI_if_then(x
|
7184
|
+
function VMI_if_then(x) {
|
6889
7185
|
// NO operation -- as of version 1.0.14, this function only serves as
|
6890
7186
|
// operator symbol, and its executions would indicate an error
|
6891
7187
|
console.log('WARNING: this IF-THEN instruction is obsolete!');
|
6892
7188
|
}
|
6893
7189
|
|
6894
|
-
function VMI_if_else(x
|
7190
|
+
function VMI_if_else(x) {
|
6895
7191
|
// NO operation -- as of version 1.0.14, this function only serves as
|
6896
7192
|
// operator symbol, and its executions would indicate an error
|
6897
7193
|
console.log('WARNING: this IF-THEN instruction is obsolete!');
|
@@ -7820,7 +8116,7 @@ const
|
|
7820
8116
|
|
7821
8117
|
// Each custom operator must have its own Virtual Machine instruction
|
7822
8118
|
|
7823
|
-
function VMI_profitable_units(x
|
8119
|
+
function VMI_profitable_units(x) {
|
7824
8120
|
// Replaces the argument list that should be at the top of the stack by the
|
7825
8121
|
// number of profitable units having a standard capacity (number), given the
|
7826
8122
|
// level (vector) of the process that represents multiple such units, the
|
@@ -7954,7 +8250,7 @@ DYNAMIC_SYMBOLS.push('npu');
|
|
7954
8250
|
LEVEL_BASED_CODES.push(VMI_profitable_units);
|
7955
8251
|
|
7956
8252
|
|
7957
|
-
function VMI_highest_cumulative_consecutive_deviation(x
|
8253
|
+
function VMI_highest_cumulative_consecutive_deviation(x) {
|
7958
8254
|
// Replaces the argument list that should be at the top of the stack by
|
7959
8255
|
// the HCCD (as in the function name) of the vector V that is passed as
|
7960
8256
|
// the first argument of this function. The HCCD value is computed by
|