SCDoc: Use proper static string constants instead of comparing string literals.
[supercollider.git] / SCClassLibrary / JITLib / Patterns / Pdef.sc
blob26051ca0c54ce127ed7365d6a42208a83dc85c24
1 // contains numerical / value patterns
3 PatternProxy : Pattern {
4         var <source, <pattern, <>envir;
5         var >clock, quant, <>condition=true, reset;
7                                 // quant new pattern insertion. can be [quant, phase, timingOffset]
8                                 // in EventPatternProxy it can be [quant, phase, timingOffset, onset]
10         classvar <>defaultQuant, defaultEnvir;
12         *new { arg source;
13                 ^super.new.source_(source)
14         }
16         *default { ^1 } // safe for duration patterns
17         *defaultValue { ^1 }
19         clock { ^clock ? TempoClock.default }
21         quant { ^quant ??  { this.class.defaultQuant } }
22         quant_ { arg val; quant = val }
24         constrainStream { arg stream;
25                 if(quant.isNil or: { stream.isNil }) { ^pattern.asStream };
26                 ^Pseq([PfinQuant(EmbedOnce(stream), quant, clock), pattern]).asStream
27         }
29         source_ { arg obj;
30                 var pat = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
31                 if (obj.isNil) { pat = this.class.default };
32                 pattern = pat;
33                 source = obj; // keep original here.
34         }
36         setSourceLikeInPbind { arg obj;
37                 var pat = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
38                 if (obj.isNil) { pat = this.class.default };
39                 pattern = pat.fin(inf);
40                 source = obj; // keep original here.
41         }
43         defaultEvent {
44                 if(envir.isNil) { envir = this.class.event };
45                 ^if(envir[\independent] === true) { (parent:envir) } { envir }
46         }
48         convertFunction { arg func;
49                         ^Prout {
50                                 var inval = func.def.prototypeFrame !? { inval = this.defaultEvent };
51                                 func.value( inval ).embedInStream(inval)
52                         };
53         }
55         *parallelise { arg list; ^Ptuple(list) }
57         pattern_ { arg pat; this.source_(pat) }
59         timingOffset_ { arg val; quant = quant.instill(2, val) }
60         timingOffset { arg val; ^quant.obtain(2) }
61         phase_ { arg val; quant = quant.instill(1, val) }
62         phase { arg val; ^quant.obtain(1) }
63         quantBeat_ { arg val; quant = quant.instill(0, val) }
64         quantBeat { arg val; ^quant.obtain(0) }
66         set { arg ... args;
67                 if(envir.isNil) { this.envir = this.class.event };
68                 args.pairsDo { arg key, val; envir.put(key, val) };
69         }
71         unset { arg ... args;
72                 if(envir.notNil) { args.do { arg key; envir.removeAt(key) } };
73         }
75         get { arg key;
76                 ^if(envir.notNil) { envir[key] } { nil };
77         }
79         place { arg ... args;
80                 if(envir.isNil) { this.envir = this.class.event };
81                 args.pairsDo { arg key, val; envir.place(key, val) }; // place is defined in the event.
82         }
84         isEventPattern { ^false }
86         receiveEvent { ^nil }
88         embed { |val|
89                 ^if(val.notNil) { Pchain(this, val) } { this }.embedInStream
90         }
92         embedInStream { arg inval;
94                 var outval, count = 0;
95                 var pat = pattern;
96                 var test = condition;
97                 var resetTest = reset;
98                 var stream = pattern.asStream;
100                 while {
101                         this.receiveEvent(inval); // used in subclass
103                         if(
104                                 (reset !== resetTest)
105                                 or: { pat !== pattern and: { test.value(outval, count) } }
106                         ) {
107                                                 pat = pattern;
108                                                 test = condition;
109                                                 resetTest = reset;
110                                                 count = 0;
111                                                 stream = this.constrainStream(stream);
112                         };
113                         outval = stream.next(inval);
114                         count = count + 1;
115                         outval.notNil
116                 }{
117                         inval = outval.yield;
118                 };
119                 ^inval
120         }
122         endless {
124                 ^Prout { arg inval;
126                         var outval, count = 0;
127                         var pat = pattern;
128                         var test = condition;
129                         var resetTest = reset;
130                         var stream = pattern.asStream;
131                         var default = this.class.defaultValue;
133                         loop {
134                                 this.receiveEvent(inval); // used in subclass
136                                 if(
137                                         (reset !== resetTest)
138                                         or: { pat !== pattern and: { test.value(outval, count) } }
139                                 ) {
140                                                         pat = pattern;
141                                                         test = condition;
142                                                         resetTest = reset;
143                                                         count = 0;
144                                                         stream = this.constrainStream(stream);
145                                 };
146                                 outval = stream.next(inval) ? default;
147                                 count = count + 1;
148                                 inval = outval.yield;
149                         }
150                 }
152         }
154         count { arg n=1;
155                 condition = { |val,i| i % n == 0 }
156         }
158         reset { reset = reset ? 0 + 1 }
160         sched { arg func;
161                 if(quant.isNil)
162                         { func.value }
163                         { this.clock.schedAbs(quant.nextTimeOnGrid(this.clock), { func.value; nil }) }
164         }
166         storeArgs { ^[pattern] }
169         // these following methods are factored out for the benefit of subclasses
170         // they only work for Pdef/Tdef/Pdefn.
173         *removeAll {
174                 this.clear;
175                 this.all.makeEmpty;
176         }
178         *clear {
179                 this.all.do { arg pat; pat.clear }
180         }
182         clear {
183                 this.stop;
184                 this.source = nil;
185                 ^nil
186         }
188         remove {
189                 if(this.class.hasGlobalDictionary) { this.class.all.removeAt(this.key) };
190                 this.clear;
191         }
193         // backward compatibility
194         *basicNew { arg source;
195                 ^super.new.init(source)
196         }
198         // global storage
200         *at { arg key;
201                 ^if(this.hasGlobalDictionary) { this.all.at(key) } { nil }
202         }
204         repositoryArgs { ^[this.key, this.source] }
206         *postRepository { arg keys, stream;
207                 if(this.hasGlobalDictionary.not) { Error("This class has no global repository.").throw };
208                 keys = keys ?? { this.all.keys };
209                 stream = stream ? Post;
210                 keys.do { arg key;
211                         var item;
212                         item = this.all[key];
213                         if(item.notNil and: { item.source !== this.default }) {
214                                 stream << item.class.name << "(" <<<* item.repositoryArgs << ")";
215                                 if(item.envir.notNil and: { item.envir.notEmpty }) {
216                                         stream << ".set(" <<<* item.envir.asKeyValuePairs << ")"
217                                 };
219                                 stream << ";\n"
220                         };
221                 };
222         }
224         *event { arg proxyClass = PatternProxy;
225                 var event, res;
226                 if(defaultEnvir.isNil) {
227                         defaultEnvir = Event.default;
228                         defaultEnvir.parent = defaultEnvir.parent.copy.putAll(
229                                 (
230                                 forward: #{ 1 },
231                                 proxyClass: proxyClass,
232                                 get: {|e, key|
233                                         var x = e.at(key);
234                                         if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x); };
235                                         x
236                                 },
237                                 place: { |e, key, val|
238                                         var x = e.at(key);
239                                         if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x) };
240                                         x.source_(val)
241                                 }
242                                 )
243                         );
244                 };
245                 event = defaultEnvir.copy;
246         //      event[\self] = event; // this risks a crash on storeOn.
247                 ^event
248         }
250         *hasGlobalDictionary { ^false }
254 Pdefn : PatternProxy {
255         var <key;
256         classvar <>all;
258         *initClass {
259                 all = IdentityDictionary.new;
260         }
261         *new { arg key, item;
262                 var res = this.at(key);
263                 if(res.isNil) {
264                         res = super.new(item).prAdd(key);
265                 } {
266                         if(item.notNil) { res.source = item }
267                 }
268                 ^res
270         }
272         map { arg ... args;
273                 if(envir.isNil) { this.envir = () };
274                 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
275         }
277         storeArgs { ^[key] } // assume it was created globally
279         prAdd { arg argKey;
280                 key = argKey;
281                 all.put(argKey, this);
282         }
283         
284         *hasGlobalDictionary { ^true }
289 // contains time patterns (tasks)
291 TaskProxy : PatternProxy {
292         var <player, <>playQuant;
293         classvar <>defaultQuant=1.0;
295         storeArgs { ^[source] }
297         source_ { arg obj;
298                         pattern = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
299                         if (obj.isNil) { pattern = this.class.default; source = obj; };
300                         this.wakeUp;
301                         source = obj;
302         }
304         convertFunction { arg func;
305                         ^Prout { |inevent|
306                                         var inval = func.def.prototypeFrame !? { this.defaultEvent };
307                                         if(inevent.isNumber or: {inevent.isNil} or: { inval.isNil }) {
308                                                 inevent = inval
309                                         } {
310                                                 inevent.copy.parent_(inval);
311                                         };
312                                         func.value(inevent)
313                         }
314         }
317         isEventPattern { ^true }
319         *default { ^Pn(this.defaultValue, 1) }
321         constrainStream { arg str;
322                 ^if(this.quant.notNil and: { str.notNil }) {
323                         Pseq([
324                                 EmbedOnce(Pconst(thisThread.clock.timeToNextBeat(this.quant), str, 0.001)),
325                                 pattern
326                         ])
327                 } { pattern }.asStream
328         }
330         align { arg argQuant;
331                 quant = argQuant;
332                 this.source = this.source.copy;
333         }
336         ////////// playing interface //////////
338         playOnce { arg argClock, doReset = (false), quant;
339                 var clock = argClock ? this.clock;
340                 ^PauseStream.new(this.asProtected.asStream).play(clock, doReset, quant ? this.quant)
341         }
343         play { arg argClock, doReset=false, quant;
344                 playQuant = quant ? this.quant;
345                 if(player.isNil) {
346                         player = this.playOnce(argClock, doReset, playQuant);
347                 } {
348                                 // resets  when stream has ended or after pause/cmd-period:
349                         if (player.streamHasEnded or: { player.wasStopped }) { doReset = true };
351                         if(player.isPlaying.not) {
352                                 player.play(argClock, doReset, playQuant);
353                         } {
354                                 if (doReset) { player.reset };
355                         }
356                 }
357         }
358         wakeUp {
359                         if(this.isPlaying) { this.play(quant:playQuant) }       }
360         asProtected {
361                 ^Pprotect(this, { if(this.player.notNil) { this.player.streamError } })
362         }
364         // check playing states:
365         isPlaying { ^player.notNil and: { player.wasStopped.not } }
366         isActive { ^this.isPlaying and: { player.streamHasEnded.not } }
367         hasSource { ^source.notNil }
368         hasEnvir { ^envir.notNil }
369         hasPlayer { ^player.notNil }
370         hasEnded { ^player.isNil or: { player.streamHasEnded } }
371         isPaused { ^player.isNil or: { player.wasStopped  } }
372         canPause { ^player.notNil and: { player.canPause } }
374         fork { arg clock, quant, event;
375                 ^Routine { this.embedInStream(event) }.play(clock ? thisThread.clock, quant)
376         }
378         stop { player.stop; player = nil; }
380         pause { if(player.notNil) { this.sched { player.pause } } }
381         resume { arg clock, quant;
382                 player !? { player.resume(clock ? this.clock, quant ? this.quant) }
383         }
387 Tdef : TaskProxy {
388         var <key;
389         classvar <>all;
392         *initClass {
393                 all = IdentityDictionary.new;
394         }
396         *new { arg key, item;
397                 var res = this.at(key);
398                 if(res.isNil) {
399                         res = super.new(item).prAdd(key);
400                 } {
401                         if(item.notNil) { res.source = item }
402                 }
403                 ^res
405         }
407         storeArgs { ^[key] }
409         prAdd { arg argKey;
410                 key = argKey;
411                 all.put(argKey, this);
412         }
413         
414         *hasGlobalDictionary { ^true }
421 // contains event patterns
423 EventPatternProxy : TaskProxy {
424         var <>fadeTime;
425         classvar <>defaultQuant=1.0;
427         storeArgs { ^[source] }
429         source_ { arg obj;
430                 if(obj.isKindOf(Function)) // allow functions to be passed in
431                         { pattern = PlazyEnvirN(obj) }
432                         { if (obj.isNil)
433                                 { pattern = this.class.default }
434                                 { pattern = obj }
435                         };
436                 envir !? { pattern = pattern <> envir };
437                 this.wakeUp;
438                 source = obj;
439         }
441         envir_ { arg dict;
442                 envir = dict;
443                 this.source = source;
444         }
446         *defaultValue { ^Event.silent }
448         embedInStream { arg inval, cleanup;
450                 var outval, count=0;
451                 var pat = pattern;
452                 var test = condition;
453                 var resetTest = reset;
454                 var stream = pattern.asStream;
456                 cleanup ?? { cleanup = EventStreamCleanup.new };
458                 while {
459                         this.receiveEvent(inval);
460                         if(
461                                 (reset !== resetTest)
462                                 or: { pat !== pattern and: { test.value(outval, count) } }
463                         ) {
464                                                 pat = pattern;
465                                                 test = condition;
466                                                 resetTest = reset;
467                                                 count = 0;
468                                                 // inval is the next event that will be yielded
469                                                 // constrainStream may add some values to it so IT MUST BE YIELDED
470                                                 stream = this.constrainStream(stream, inval, cleanup);
471                                                 cleanup = EventStreamCleanup.new;
472                         };
473                         outval = stream.next(inval);
474                         count = count + 1;
475                         outval.notNil
476                 }{
477                         outval = cleanup.update(outval);
478                         inval = outval.yield;
479                         if(inval.isNil) { ^nil.alwaysYield }
480                 };
481                 ^inval
483         }
485         endless {
487                 ^Prout { arg inval;
489                         var outval;
490                         var cleanup = EventStreamCleanup.new;
491                         var count = 0;
492                         var pat = pattern;
493                         var test = condition;
494                         var resetTest = reset;
495                         var stream = pattern.asStream;
496                         var default = this.class.defaultValue;
498                         loop {
499                                 this.receiveEvent(inval);
500                                 if(
501                                         (reset !== resetTest)
502                                         or: { pat !== pattern and: { test.value(outval, count) } }
503                                 ) {
504                                                         pat = pattern;
505                                                         test = condition;
506                                                         resetTest = reset;
507                                                         count = 0;
508                                                         // inval is the next event that will be yielded
509                                                         // constrainStream may add some values to it so IT MUST BE YIELDED
510                                                         stream = this.constrainStream(stream, inval, cleanup);
511                                                         cleanup = EventStreamCleanup.new;
512                                 };
513                                 outval = stream.next(inval) ? default;
514                                 count = count + 1;
515                                 outval = cleanup.update(outval);
516                                 inval = outval.yield;
517                         }
518                 }
520         }
523         constrainStream { arg str, inval, cleanup;
524                 var delta, tolerance, new;
525                 var quantBeat, catchUp, deltaTillCatchUp, forwardTime, quant = this.quant;
527                 ^if(quant.notNil) {
528                         quantBeat = this.quantBeat ? 0;
529                         catchUp = this.outset;
531                         delta = thisThread.clock.timeToNextBeat(quant);
532                         tolerance = quantBeat % delta % 0.125;
533                         if(catchUp.notNil) {
534                                 deltaTillCatchUp = thisThread.clock.timeToNextBeat(catchUp);
535                                 new = pattern.asStream;
536                                 forwardTime = quantBeat - delta + deltaTillCatchUp;
537                                 delta = new.fastForward(forwardTime, tolerance) + deltaTillCatchUp;
538                         } {
539                                 new = pattern
540                         };
542                         if(fadeTime.isNil) {
543                                 if(delta == 0) {
544                                         cleanup.exit(inval);
545                                         new
546                                 } {
547                                         Pseq([EmbedOnce(Pfindur(delta, str, tolerance).asStream(cleanup)), new])
548                                 }
549                         }{
551                                 Ppar([
552                                         EmbedOnce(PfadeOut(str, fadeTime, delta, tolerance).asStream(cleanup)),
553                                         PfadeIn(new, fadeTime, delta, tolerance)
554                                 ])
555                         }
556                 } {
557                         cleanup.exit(inval);
558                         pattern
559                 }.asStream
560         }
562         *parallelise { arg list; ^Ppar(list) }
564         outset_ { arg val; quant = quant.instill(3, val) }
565         outset { arg val; ^this.quant.obtain(3) }
568         // branching from another thread
570         fork { arg argClock, quant, protoEvent; // usual fork arg order: clock, quant, ...
571                 argClock = argClock ? thisThread.clock;
572                 ^EventStreamPlayer(this.asStream, protoEvent).play(argClock, true, quant)
573         }
575         // playing one instance //
577         playOnce { arg argClock, protoEvent, quant;
578                 ^this.fork(argClock ? this.clock, quant ? this.quant, protoEvent)
579         }
582         ////////// playing interface //////////
584         // start playing //
586         play { arg argClock, protoEvent, quant, doReset=false;
587                 playQuant = quant ? this.quant;
588                 if(player.isNil) {
589                         player = EventStreamPlayer(this.asProtected.asStream, protoEvent);
590                         player.play(argClock, doReset, playQuant);
591                 } {
592                                 // resets  when stream has ended or after pause/cmd-period:
593                         if(player.streamHasEnded or: { player.wasStopped }) { doReset = true };
594                         protoEvent !? { player.event = protoEvent };
595                         if(player.isPlaying.not) {
596                                 player.play(argClock, doReset, playQuant);
597                         } {
598                                 if(doReset) { player.reset };
599                         }
600                 }
601         }
606 Pdef : EventPatternProxy {
607         var <key;
609         classvar <>all;
611         storeArgs { ^[key] }
613         *new { arg key, item;
614                 var res = this.at(key);
615                 if(res.isNil) {
616                         res = super.new(item).prAdd(key);
617                 } {
618                         if(item.notNil) { res.source = item }
619                 }
620                 ^res
622         }
624         map { arg ... args;
625                 if(envir.isNil) { this.envir = () };
626                 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
627         }
629         prAdd { arg argKey;
630                 key = argKey;
631                 all.put(argKey, this);
632         }
633         
634         *hasGlobalDictionary { ^true }
636         *initClass {
637                 var phraseEventFunc;
639                 all = IdentityDictionary.new;
640                 Class.initClassTree(Event);
642                 phraseEventFunc = {
643                         var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest;
645                                 embeddingLevel = ~embeddingLevel ? 0; // infinite recursion catch
646                                 freq = ~freq.value;
647                                 rest = freq.isKindOf(Symbol); // check for outer rests
648                                 if(rest) { ~freq = freq };
649                                 pat = (~repository ? all).at(~instrument);
651                                 if(pat.notNil and: { embeddingLevel < 8 })
652                                 {
653                                         pat = pat.pattern; // optimization. outer pattern takes care for replacement
654                                         // preserve information from outer pattern, but not delta.
656                                         recursionLevel = ~recursionLevel;
657                                         if(~transparency.isNil or:
658                                                 { ~transparency > (recursionLevel ? 0) }
659                                         ) {
660                                                 outerEvent = currentEnvironment.copy
661                                         } {
662                                                 outerEvent = Event.default;
663                                                 outerEvent.use {
664                                                         ~type = \phrase;
665                                                         ~recursionLevel = recursionLevel;
666                                                 }
667                                         };
669                                         if(recursionLevel.notNil) {
670                                                 if(recursionLevel > 0) {
671                                                         // in recursion, some inner values have to be overridden
672                                                         instrument = ~instrument;
673                                                         pat = pat.collect { |inval|
674                                                                         inval.use {
675                                                                                         ~instrument = instrument;
676                                                                                         ~parent = outerEvent;
677                                                                                         ~recursionLevel = recursionLevel - 1;
678                                                                                 };
679                                                                                 inval
680                                                         };
681                                                 } {
682                                                         // play pattern in the ordinary way
683                                                         ~type = \note;
684                                                 };
685                                         } {     // avoid recursion, if instrument not set.
686                                                 outerEvent.put(\embeddingLevel, embeddingLevel + 1);
687                                                 outerEvent.parent_(Event.parentEvents.default);
688                                         };
689                                         // maybe add a Pprotect here.
690                                         // pat.asProtected
691                                         pat = Pfindur(~sustain.value, pat);
692                                         outerEvent.put(\delta, nil); // block delta modification by Ppar
693                                         outerEvent.put(\instrument, ~synthDef ? \default);
695                                         pat.play(thisThread.clock, outerEvent, 0.0);
696                                 } {
697                                         ~type = \note;
698                                         ~play.value;
699                                 }
701                 };
703                 Event.addEventType(\phrase, phraseEventFunc);
704         }
710 PbindProxy : Pattern {
711         var <>pairs, <source;
713         *new { arg ... pairs;
714                 ^super.newCopyArgs(pairs).init
715         }
716         init {
717                 forBy(0, pairs.size-1, 2) { arg i;
718                         pairs[i+1] = PatternProxy(pairs[i+1])
719                 };
720                 source = EventPatternProxy(Pbind(*pairs));
721         }
722         embedInStream { arg inval;
723                 ^source.embedInStream(inval)
724         }
725         find { arg key;
726                 pairs.pairsDo { |u,x,i| if(u === key) { ^i } }; ^nil
727         }
728         quant_ { arg val;
729                 pairs.pairsDo { arg key, item; item.quant = val }; // maybe use ref later
730                 source.quant = val;
731         }
732         quant { ^source.quant }
733         envir { ^source.envir }
734         envir_ { arg envir; source.envir_(envir) }
736         at { arg key; var i; i = this.find(key); ^if(i.notNil) { pairs[i+1] } { nil } }
738         // does not yet work with adding arrayed keys/values
739         set { arg ... args; // key, val ...
740                 var changedPairs=false, quant;
741                 quant = this.quant;
742                 args.pairsDo { |key, val|
743                         var i, remove;
744                         i = this.find(key);
745                         if(i.notNil)
746                         {
747                                 if(val.isNil) {
748                                         pairs.removeAt(i);
749                                         pairs.removeAt(i);
750                                         changedPairs = true;
751                                 }{
752                                         pairs[i+1].setSourceLikeInPbind(val)
753                                 };
754                         }{
755                                 pairs = pairs ++ [key, PatternProxy.new.setSourceLikeInPbind(val).quant_(quant)];
756                                 // fin(inf) is a way to stream symbols endlessly
757                                 changedPairs = true;
758                         };
760                 };
761                 if(changedPairs) { source.source =  Pbind(*pairs) };
763         }
765         storeArgs {
766                 var result = Array(pairs.size);
767                 pairs.pairsDo { |key, value|
768                         result.add(key).add(value.source)
769                 };
770                 ^result
771         }
775 Pbindef : Pdef {
776         *new { arg ... pairs;
777                 var key, pat, src;
778                 key = pairs.removeAt(0);
779                 pat = super.new(key);
780                 src = pat.source;
781                 if(pairs.isEmpty.not) {
782                         if(src.class === PbindProxy) {
783                                 src.set(*pairs);
784                                 pat.wakeUp;
785                         } {
786                                 if(src.isKindOf(Pbind))
787                                 {
788                                         src.patternpairs.pairsDo { |key, pat|
789                                                 if(pairs.includes(key).not) {
790                                                         pairs = pairs.add(key);
791                                                         pairs = pairs.add(pat);
792                                                 }
793                                         }
794                                 };
796                                 src = PbindProxy.new(*pairs).quant_(pat.quant);
797                                 pat.source = src
798                         };
799                 };
801                 ^pat
803         }
805         storeArgs { ^[key]++pattern.storeArgs }
806         repositoryArgs { ^this.storeArgs }
807         quant_ { arg val; super.quant = val; source.quant = val }
808         
809         *hasGlobalDictionary { ^true }
817 // general purpose lookup stream
819 Pdict : Pattern {
820         var <>dict, <>which, <>repeats, <>default;
821         *new { arg dict, which, repeats=inf, default;
822                 ^super.newCopyArgs(dict, which, repeats, default);
823         }
824         storeArgs { ^[dict,which,repeats,default ] }
825         embedInStream { arg inval;
826                 var keyStream, key;
827                 keyStream = which.asStream;
828                 repeats.value(inval).do({
829                         key = keyStream.next(inval);
830                         if(key.isNil) { ^inval };
831                         inval = (dict.at(key) ? default).embedInStream(inval);
832                 });
833                 ^inval
834         }