-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTemporal.cpp
738 lines (663 loc) · 29.4 KB
/
Temporal.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
///
/// Langulus::Flow
/// Copyright (c) 2017 Dimo Markov <[email protected]>
/// Part of the Langulus framework, see https://langulus.com
///
/// SPDX-License-Identifier: GPL-3.0-or-later
///
#include "Time.inl"
#include "Code.inl"
#include "Resolvable.inl"
#include "inner/Missing.hpp"
#include "inner/Fork.hpp"
#include "Temporal.hpp"
#if 0
#define VERBOSE_TEMPORAL(...) Logger::Verbose(*this, ": ", __VA_ARGS__)
#define VERBOSE_TEMPORAL_TAB(...) const auto tab = Logger::VerboseTab(*this, ": ", __VA_ARGS__)
#else
#define VERBOSE_TEMPORAL(...) LANGULUS(NOOP)
#define VERBOSE_TEMPORAL_TAB(...) LANGULUS(NOOP)
#endif
using namespace Langulus::Flow;
/// Default constructor, add the initial missing future point
/// @param environment - the initial flow environment
Temporal::Temporal() {
mPriorityStack << Inner::MissingFuture {};
}
/// Construct as a sub-flow
/// @attention assumes parent is a valid pointer
/// @param parent - the parent flow
Temporal::Temporal(Temporal* parent)
: mParent {parent} {
mPriorityStack << Inner::MissingFuture {};
}
/// Serialize temporal as Code
Temporal::operator Code() const {
return IdentityOf(this);
}
/// Serialize temporal as debug string
Temporal::operator Text() const {
return IdentityOf(this);
}
/// Reset progress for the priority stack
void Temporal::Reset() {
mStart = mNow = {};
ResetInner(mPriorityStack);
}
/// Reset progress for all verbs inside a scope
/// @param scope - scope to reset
void Temporal::ResetInner(Many& scope) {
scope.ForEach(
[&](Many& m) {
if (m.IsDense())
ResetInner(m);
},
[&](Inner::Missing& missing) {
if (missing.mContent.IsDense())
ResetInner(missing.mContent);
},
[&](Trait& trait) {
if (trait.IsDense())
ResetInner(static_cast<Many&>(trait));
},
[&](Construct& construct) {
ResetInner(construct.GetDescriptor());
},
[&](Neat& neat) {
neat.ForEachTrait([this](Trait& trait) {
ResetInner(static_cast<Many&>(trait));
});
neat.ForEachConstruct([this](Construct& con) {
Many wrapper {con};
ResetInner(wrapper);
});
neat.ForEachTail([this](Many& stuff) {
ResetInner(stuff);
});
},
[&](A::Verb& constVerb) {
ResetInner(constVerb.GetSource());
ResetInner(constVerb.GetArgument());
constVerb.Undo();
}
);
}
/// Compare two flows
/// @param other - the flow to compare with
/// @return true if both flows are the same
bool Temporal::operator == (const Temporal& other) const {
return mFrequencyStack == other.mFrequencyStack
and mTimeStack == other.mTimeStack
and mPriorityStack == other.mPriorityStack;
}
/// Check if flow contains anything
/// @return true if flow contains something
bool Temporal::IsValid() const {
return mPriorityStack or mTimeStack or mFrequencyStack;
}
/// Dump the contents of the flow to the log in a pretty, colorized and
/// easily readable way
void Temporal::Dump() const {
if (mPriorityStack)
Logger::Verbose(mPriorityStack);
if (mTimeStack)
Logger::Verbose(mTimeStack);
if (mFrequencyStack)
Logger::Verbose(mFrequencyStack);
}
/// Get the accumulated running time across all Updates
/// @return the time
Langulus::Time Temporal::GetUptime() const {
return mNow - mStart;
}
/// Advance the flow - moves time forward, executes stacks
/// @param dt - delta time
/// @param sideffects - any side effects produced by executing
/// @return true if no exit was requested
bool Temporal::Update(Time dt, Many& sideffects) {
if (mStart == mNow) {
// We're at the beginning of time - execute the priority stack
VERBOSE_TEMPORAL(Logger::Purple,
"Flow before execution: ", mPriorityStack);
Many unusedContext;
Execute(mPriorityStack, unusedContext, sideffects, false);
VERBOSE_TEMPORAL(Logger::Purple,
"Flow after execution: ", mPriorityStack);
}
// Avoid updating anything else, if no time had passed
if (not dt)
return true;
// Advance the global cycler for the flow
mNow += dt;
// Execute flows that occur periodically
for (auto pair : mFrequencyStack) {
pair.mValue.mNow += dt;
auto ticks = pair.mValue.GetUptime().Seconds() / mRatePeriod.Seconds();
while (ticks >= pair.mKey) {
// Time to execute the periodic flow
pair.mValue.Reset();
pair.mValue.Update({}, sideffects);
ticks -= pair.mKey;
}
// Make sure any leftover time is returned to the periodic flow
pair.mValue.mNow = pair.mValue.mStart + mRatePeriod * ticks;
}
// Execute flows that occur after a given point in time
const auto ticks = GetUptime().Seconds() / mTimePeriod.Seconds();
for (auto pair : mTimeStack) {
if (pair.mKey > ticks) {
// The time stack is sorted, so no point in continuing
break;
}
// Always update all time points before the tick count
// They might have periodic flows inside
pair.mValue.Update(dt, sideffects);
}
return true;
}
/// Merge a flow
/// @param other - the flow to merge with this one
void Temporal::Merge(const Temporal& other) {
// Concatenate priority stacks
mPriorityStack += other.mPriorityStack;
// Merge time stacks
for (auto pair : other.mTimeStack) {
auto found = mTimeStack.FindIt(pair.mKey);
if (not found) {
// New time point required
mTimeStack.Insert(pair.mKey, this);
found = mTimeStack.FindIt(pair.mKey);
}
found.GetValue().Merge(pair.mValue);
};
// Merge frequency stacks
for (auto pair : other.mFrequencyStack) {
auto found = mFrequencyStack.FindIt(pair.mKey);
if (not found) {
// New time point required
mFrequencyStack.Insert(pair.mKey, this);
found = mFrequencyStack.FindIt(pair.mKey);
}
found.GetValue().Merge(pair.mValue);
};
}
/// Push a scope of verbs and data to the flow
/// The following rules are used to place the data:
/// 1. Data is always inserted at future missing points (??) - there is
/// always at least one such point in any flow (at the back of the
/// main scope)
/// 2. If inserted data has a past missing point (?), that point will be
/// filled with whatever data is already available at the place of
/// insertion
/// 3. Future and past points might have a filter, which decides what
/// kind of data can be inserted at that point
/// 4. Future and past points might have priority, which decides what
/// kind of verbs are allowed inside. Priorities are set when a
/// verb is inserted. A verb of higher-or-equal priority can never be
/// inserted in a point of lower priority. A verb of higher-or-equal
/// priority can only wrap lower-or-equal priority scopes in itself.
/// 5. Future and past points might have branches, which forces shallow
/// duplication of missing future/past content when linking
/// 6. Verbs with different frequency and time charge go to the
/// corresponding stacks, and are stripped from such properties;
/// from there on, they're handled conventionally, by the
/// aforementioned rules in the context of that stack
/// @attention assumes argument is a valid scope
/// @param scope - the scope to analyze and push
/// @return true if the flow changed
Many Temporal::PushInner(Many scope) {
VERBOSE_TEMPORAL_TAB("Pushing: ", scope);
// Compile pushed scope to an intermediate format
auto compiled = Compile(scope, Inner::NoPriority);
VERBOSE_TEMPORAL("Compiled to: ", compiled);
// Link new scope with the available stacks
try { Link(compiled); }
catch (...) { return {}; }
if (mPriorityStack)
VERBOSE_TEMPORAL(Logger::Purple, "Priority flow: ", mPriorityStack);
if (mTimeStack)
VERBOSE_TEMPORAL(Logger::Purple, "Time flow: ", mTimeStack);
if (mFrequencyStack)
VERBOSE_TEMPORAL(Logger::Purple, "Frequency flow: ", mFrequencyStack);
// Execute the new scope and return any side effects
Many sideffects;
Update({}, sideffects);
return Abandon(sideffects);
}
/// Compiles a scope into an intermediate form, used by the flow
/// @attention assumes argument is a valid scope
/// @param scope - the scope to compile
/// @param priority - the priority to set for any missing point created
/// for the provided scope.
/// @return the compiled scope
Many Temporal::Compile(const Many& scope, Real priority) {
Many result;
if (scope.IsOr())
result.MakeOr();
if (scope.IsPast()) {
// Convert the scope to a MissingPast intermediate format
result = Inner::MissingPast {scope, priority};
return Abandon(result);
}
else if (scope.IsFuture()) {
// Convert the scope to a MissingFuture intermediate format
result = Inner::MissingFuture {scope, priority};
return Abandon(result);
}
else if (scope.IsDeep()) {
if (scope.IsSparse()) {
// Sparse scopes are always inserted, even if empty. They act
// as handles, that can change context externally. They are
// never compiled, because that would require fiddling with
// the contents of the handle.
// @attention any sparse element inside a flow will cause that
// flow to become impure, as in, it can be affected by
// external factors, and is no longer purely functional.
scope.ForEach([&](const Many& subscope) {
result << &subscope;
});
}
else {
// Nest dense deep scopes
scope.ForEach([&](const Many& subscope) {
result << Compile(subscope, priority);
});
}
return Abandon(result);
}
const auto done = scope.ForEach(
[&](const Trait& subscope) {
// Compile traits
result << Trait::From(
subscope.GetTrait(),
Compile(subscope, priority)
);
},
[&](const Construct& subscope) {
// Compile constructs
result << Construct {
subscope.GetType(),
Compile(subscope.GetDescriptor(), priority),
subscope.GetCharge()
};
},
[&](const A::Verb& subscope) {
// Compile verbs
auto v = Verb::FromMeta(
subscope.GetVerb(),
Compile(subscope.GetArgument(), subscope.GetPriority()),
subscope.GetCharge(),
subscope.GetVerbState()
).SetSource(
Compile(subscope.GetSource(), subscope.GetPriority())
);
result << Abandon(v);
}
);
if (not done) {
// Just propagate content
result = scope;
}
return Abandon(result);
}
/// Compiles a neat descriptor into an intermediate form, used by the flow
/// @attention assumes argument is a valid scope
/// @param neat - the Neat to compile
/// @param priority - the priority to set for any missing point created
/// for the provided scope.
/// @return the compiled scope
Many Temporal::Compile(const Neat& neat, Real priority) {
Neat result;
neat.ForEachTrait([&](const Trait& subscope) {
// Compile traits
result << Trait::From(
subscope.GetTrait(),
Compile(subscope, priority)
);
});
neat.ForEachConstruct([&](const Construct& subscope) {
// Compile constructs
result << Construct {
subscope.GetType(),
Compile(subscope.GetDescriptor(), priority),
subscope.GetCharge()
};
});
neat.ForEachTail([&](const Many& group) {
// Compile anything else
result << Compile(group, priority);
});
return Abandon(result);
}
/// Links the missing past points of the provided scope, with the missing
/// future point (or any nested future points inside). Anything could be
/// pushed to provided future point as a fallback, as long as state and
/// filters allows it!
/// @attention assumes argument is a valid scope
/// @param scope - the scope to link
/// @param future - [in/out] the future point to place inside
/// @return true if scope was linked successfully
/*bool Temporal::PushFutures(const Many& scope, Inner::MissingFuture& future) {
// Attempt linking to the contents first
if (PushFutures(scope, future.mContent))
return true;
//
// If reached, then future point is flat and boring, fallback by
// directly linking against it
VERBOSE_TEMPORAL_TAB("Linking to: ", future);
return future.Push(scope);
}*/
/// Links the missing past points of the provided scope, with the missing
/// future points of the provided stack. But anything new could go into
/// old future points, as long as state and filters allows it!
/// @attention assumes argument is a valid scope
/// @param scope - the scope to link
/// @param stack - [in/out] the stack to link with
/// @return true if scope was linked successfully
bool Temporal::PushFutures(const Many& scope, Many& stack) {
bool atLeastOneSuccess = false;
if (stack.IsDeep() and stack.IsDense()) {
// Nest deep stack, if dense
stack.ForEachRev([&](Many& substack) {
atLeastOneSuccess |= PushFutures(scope, substack);
// Continue linking only if the stack is branched
return not (stack.IsOr() and atLeastOneSuccess);
});
return atLeastOneSuccess;
}
// Iterate backwards - the last future points are always most
// relevant for linking. Lets start, by scanning all future points
// in the available stack. Scope will be cloned for each encountered
// branch
stack.ForEachRev(
[&](Trait& substack) {
atLeastOneSuccess |= PushFutures(scope, substack);
// Continue linking only if the stack is branched
return not (stack.IsOr() and atLeastOneSuccess);
},
[&](Construct& substack) {
atLeastOneSuccess |= PushFutures(scope, substack.GetDescriptor());
// Continue linking only if the stack is branched
return not (stack.IsOr() and atLeastOneSuccess);
},
[&](Verb& substack) -> LoopControl {
if (PushFutures(scope, substack.GetArgument())) {
atLeastOneSuccess = true;
// Continue linking only if the stack is branched
return not stack.IsOr();
}
if (PushFutures(scope, substack.GetSource())) {
atLeastOneSuccess = true;
// Continue linking only if the stack is branched
return not stack.IsOr();
}
return Loop::Continue;
},
[&](Inner::MissingFuture& future) {
VERBOSE_TEMPORAL_TAB("Pushing ", scope, " to ", future);
atLeastOneSuccess |= future.Push(scope);
// Continue linking only if the stack is branched
return not (stack.IsOr() and atLeastOneSuccess);
}
);
return atLeastOneSuccess;
}
/// Links the missing past points of the provided scope, with the missing
/// future points of the provided Neat. But anything new could go into
/// old future points, as long as state and filters allows it!
/// @attention assumes argument is a valid scope
/// @param scope - the scope to link
/// @param stack - [in/out] the neat stack to link with
/// @return true if scope was linked successfully
bool Temporal::PushFutures(const Many& scope, Neat& stack) {
bool atLeastOneSuccess = false;
stack.ForEach([&](Many& substack) {
atLeastOneSuccess |= PushFutures(scope, substack);
});
return atLeastOneSuccess;
}
/// Experimental
/// Experimental
/// Experimental
void Temporal::Link(const Many& scope) {
if (scope.IsOr())
TODO();
if (scope.IsDeep()) {
if (scope.IsSparse()) {
// Sparse blocks are always pushed directly without linking
// anything, because that would require changing data behind
// the handle. This allows for specifying contexts externally,
// but also makes the flow impure, because it allows it to be
// affacted by external influence.
scope.ForEach([&](const Many& sub) {
VERBOSE_TEMPORAL_TAB("Pushing sparse block ", sub, " to ", mPriorityStack);
LANGULUS_ASSERT(
PushFutures(&sub, mPriorityStack),
Flow, "Couldn't push to future"
);
});
}
else {
// Nest-link dense deep scope
scope.ForEach([&](const Many& sub) {
Link(sub);
});
}
}
else {
// Handle shallow scope
scope.ForEach(
[&](const Trait& t) {
// Forward to all future points in the priority stack
TMany<Trait> local = t;
LANGULUS_ASSERT(
PushFutures(local, mPriorityStack),
Flow, "Couldn't push to future"
);
},
[&](const Construct& c) {
// Forward to all future points in the priority stack
TMany<Construct> local = c;
LANGULUS_ASSERT(
PushFutures(local, mPriorityStack),
Flow, "Couldn't push to future"
);
},
[&](const Verb& v) {
if (v.IsVerb<Verbs::Do>()) {
// "Do" verbs act as context/mass/rate/time setters
// Don't push them, but use them to set environment for
// any sub-verbs
LinkRelative(v.GetArgument(), v);
}
else if (v.GetTime()) {
// Verb is timed, forward it to the time stack
TMany<Verb> local = v;
local[0].SetTime(0);
auto found = mTimeStack.FindIt(v.GetTime());
if (not found) {
mTimeStack.Insert(v.GetTime(), this);
found = mTimeStack.FindIt(v.GetTime());
}
found.GetValue().LinkRelative(local, v);
}
else if (v.GetRate()) {
// Verb is rated, forward it to the frequency stack
TMany<Verb> local = v;
local[0].SetRate(0);
auto found = mFrequencyStack.FindIt(v.GetRate());
if (not found) {
mFrequencyStack.Insert(v.GetRate(), this);
found = mFrequencyStack.FindIt(v.GetRate());
}
LANGULUS_ASSERT(
found.GetValue().PushFutures(local, found.GetValue().mPriorityStack),
Flow, "Couldn't push to future"
);
}
else {
// Forward it to the priority stack
TMany<Verb> local = v;
LANGULUS_ASSERT(
PushFutures(local, mPriorityStack),
Flow, "Couldn't push to future"
);
}
}
);
}
}
/// Experimental
/// Experimental
/// Experimental
void Temporal::LinkRelative(const Many& scope, const Verb& override) {
if (scope.IsOr())
TODO();
if (scope.IsDeep()) {
// Nest deep scope
scope.ForEach([&](const Many& sub) {
LinkRelative(sub, override);
});
}
else {
// Handle shallow scope
scope.ForEach(
[&](const Trait& t) {
TMany<Trait> local = t;
// Forward to future point in appropriate stack,
// according to the override verb
if (override.GetTime()) {
// Trait is timed, forward it to the time stack
auto found = mTimeStack.FindIt(override.GetTime());
if (not found) {
mTimeStack.Insert(override.GetTime(), this);
found = mTimeStack.FindIt(override.GetTime());
}
LANGULUS_ASSERT(
found.GetValue().PushFutures(local, found.GetValue().mPriorityStack),
Flow, "Couldn't push to future"
);
}
else if (override.GetRate()) {
// Verb is rated, forward it to the frequency stack
auto found = mFrequencyStack.FindIt(override.GetRate());
if (not found) {
mFrequencyStack.Insert(override.GetRate(), this);
found = mFrequencyStack.FindIt(override.GetRate());
}
LANGULUS_ASSERT(
found.GetValue().PushFutures(local, found.GetValue().mPriorityStack),
Flow, "Couldn't push to future"
);
}
else {
// Forward it to the priority stack
LANGULUS_ASSERT(
PushFutures(local, mPriorityStack),
Flow, "Couldn't push to future"
);
}
},
[&](const Construct& c) {
TMany<Construct> local = c;
// Forward to future point in appropriate stack,
// according to the override verb
if (override.GetTime()) {
// Trait is timed, forward it to the time stack
auto found = mTimeStack.FindIt(override.GetTime());
if (not found) {
mTimeStack.Insert(override.GetTime(), this);
found = mTimeStack.FindIt(override.GetTime());
}
LANGULUS_ASSERT(
found.GetValue().PushFutures(local, found.GetValue().mPriorityStack),
Flow, "Couldn't push to future"
);
}
else if (override.GetRate()) {
// Verb is rated, forward it to the frequency stack
auto found = mFrequencyStack.FindIt(override.GetRate());
if (not found) {
mFrequencyStack.Insert(override.GetRate(), this);
found = mFrequencyStack.FindIt(override.GetRate());
}
LANGULUS_ASSERT(
found.GetValue().PushFutures(local, found.GetValue().mPriorityStack),
Flow, "Couldn't push to future"
);
}
else {
// Forward it to the priority stack
LANGULUS_ASSERT(
PushFutures(local, mPriorityStack),
Flow, "Couldn't push to future"
);
}
},
[&](const Verb& v) {
// Multiply verb energy and merge contexts
const Verb localOverride = v * override;
if (v.IsVerb<Verbs::Do>()) {
// "Do" verbs act as context/mass/rate/time setters
// Don't push them, but use them to set environment for
// any sub-verbs
LinkRelative(v.GetArgument(), localOverride);
}
else if (localOverride.GetTime()) {
// Verb is timed, forward it to the time stack
const auto time = localOverride.GetTime();
TMany<Verb> local = v;
local[0].SetTime(0);
auto found = mTimeStack.FindIt(time);
if (not found) {
mTimeStack.Insert(time, this);
found = mTimeStack.FindIt(time);
}
found.GetValue().LinkRelative(local, localOverride);
}
else if (localOverride.GetRate()) {
// Verb is rated, forward it to the frequency stack
const auto rate = localOverride.GetRate();
TMany<Verb> local = v;
local[0].SetRate(0);
if (not local[0].GetSource())
local[0].SetSource(localOverride.GetSource());
auto found = mFrequencyStack.FindIt(rate);
if (not found) {
mFrequencyStack.Insert(rate, this);
found = mFrequencyStack.FindIt(rate);
}
LANGULUS_ASSERT(
found.GetValue().PushFutures(local, found.GetValue().mPriorityStack),
Flow, "Couldn't push to future"
);
}
else {
// Forward it to the priority stack
// Collapse all verb charges at this point
TMany<Verb> local = v;
local[0].SetMass(localOverride.GetMass());
local[0].SetPriority(localOverride.GetPriority());
// Always push any valid source to the future, so that
// missing past can get satisfied by it
if (override.GetSource()) {
LANGULUS_ASSERT(
PushFutures(override.GetSource(), mPriorityStack),
Flow, "Couldn't push to future"
);
}
/*
if (not local[0].GetSource()) {
// Directly substitute missing sources
//TODO fill missing only to allow for explicit stateless execution?
local[0].SetSource(localOverride.GetSource());
}*/
LANGULUS_ASSERT(
PushFutures(local, mPriorityStack),
Flow, "Couldn't push to future"
);
}
}
);
}
}