1 /************************************************************************
2  ************************************************************************
3     FAUST compiler
4     Copyright (C) 2017 GRAME, Centre National de Creation Musicale
5     ---------------------------------------------------------------------
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  ************************************************************************
20  ************************************************************************/
21 
22 #ifndef _RUST_INSTRUCTIONS_H
23 #define _RUST_INSTRUCTIONS_H
24 
25 #include <regex>
26 
27 #include "text_instructions.hh"
28 #include "Text.hh"
29 
30 using namespace std;
31 
makeNameSingular(const string & name)32 inline string makeNameSingular(const string& name) {
33     string result = name;
34     result = std::regex_replace(result, std::regex("inputs"), "input");
35     result = std::regex_replace(result, std::regex("outputs"), "output");
36     return result;
37 }
38 
39 // Visitor used to initialize fields into the DSP constructor
40 struct RustInitFieldsVisitor : public DispatchVisitor {
41     std::ostream* fOut;
42     int           fTab;
43 
RustInitFieldsVisitorRustInitFieldsVisitor44     RustInitFieldsVisitor(std::ostream* out, int tab = 0) : fOut(out), fTab(tab) {}
45 
visitRustInitFieldsVisitor46     virtual void visit(DeclareVarInst* inst)
47     {
48         tab(fTab, *fOut);
49         *fOut << inst->fAddress->getName() << ": ";
50         ZeroInitializer(fOut, inst->fType);
51         if (inst->fAddress->getAccess() & Address::kStruct) *fOut << ",";
52     }
53 
54     // Generate zero intialisation code for simple int/real scalar and arrays types
ZeroInitializerRustInitFieldsVisitor55     static void ZeroInitializer(std::ostream* fOut, Typed* typed)
56     {
57         Typed::VarType type       = typed->getType();
58         ArrayTyped*    array_type = dynamic_cast<ArrayTyped*>(typed);
59 
60         if (array_type) {
61             if (isIntPtrType(type)) {
62                 *fOut << "[0;" << array_type->fSize << "]";
63             } else if (isRealPtrType(type)) {
64                 *fOut << "[0.0;" << array_type->fSize << "]";
65             }
66         } else {
67             if (isIntType(type)) {
68                 *fOut << "0";
69             } else if (isRealType(type)) {
70                 *fOut << "0.0";
71             }
72         }
73     }
74 };
75 
76 class RustInstVisitor : public TextInstVisitor {
77    private:
78     /*
79      Global functions names table as a static variable in the visitor
80      so that each function prototype is generated as most once in the module.
81      */
82     static map<string, bool> gFunctionSymbolTable;
83     map<string, string>      fMathLibTable;
84 
85    public:
86     using TextInstVisitor::visit;
87 
RustInstVisitor(std::ostream * out,const string & structname,int tab=0)88     RustInstVisitor(std::ostream* out, const string& structname, int tab = 0)
89         : TextInstVisitor(out, ".", new RustStringTypeManager(xfloat(), "&"), tab)
90     {
91         fTypeManager->fTypeDirectTable[Typed::kObj]     = "";
92         fTypeManager->fTypeDirectTable[Typed::kObj_ptr] = "";
93 
94         // Integer version
95         fMathLibTable["abs"]   = "i32::abs";
96         fMathLibTable["min_i"] = "std::cmp::min";
97         fMathLibTable["max_i"] = "std::cmp::max";
98 
99         // Float version
100         fMathLibTable["fabsf"]      = "F32::abs";
101         fMathLibTable["acosf"]      = "F32::acos";
102         fMathLibTable["asinf"]      = "F32::asin";
103         fMathLibTable["atanf"]      = "F32::atan";
104         fMathLibTable["atan2f"]     = "F32::atan2";
105         fMathLibTable["ceilf"]      = "F32::ceil";
106         fMathLibTable["cosf"]       = "F32::cos";
107         fMathLibTable["expf"]       = "F32::exp";
108         fMathLibTable["floorf"]     = "F32::floor";
109         fMathLibTable["fmodf"]      = "libm::fmodf";
110         fMathLibTable["logf"]       = "F32::log";
111         fMathLibTable["log10f"]     = "F32::log10";
112         fMathLibTable["max_f"]      = "F32::max";
113         fMathLibTable["min_f"]      = "F32::min";
114         fMathLibTable["powf"]       = "F32::powf";
115         fMathLibTable["remainderf"] = "F32::rem_euclid";
116         //fMathLibTable["rintf"]      = "linux_api_math::rintf"; // TODO
117         fMathLibTable["rintf"]      = "F32::round";
118         fMathLibTable["roundf"]     = "F32::round";
119         fMathLibTable["sinf"]       = "F32::sin";
120         fMathLibTable["sqrtf"]      = "F32::sqrt";
121         fMathLibTable["tanf"]       = "F32::tan";
122 
123         // Additional hyperbolic math functions
124         fMathLibTable["acoshf"]     = "F32::acosh";
125         fMathLibTable["asinhf"]     = "F32::asinh";
126         fMathLibTable["atanhf"]     = "F32::atanh";
127         fMathLibTable["coshf"]      = "F32::cosh";
128         fMathLibTable["sinhf"]      = "F32::sinh";
129         fMathLibTable["tanhf"]      = "F32::tanh";
130 
131         fMathLibTable["isnanf"]     = "F32::is_nan";
132         fMathLibTable["isinff"]     = "F32::is_infinite";
133         fMathLibTable["copysignf"]  = "F32::copysign";
134 
135         // Double version
136         fMathLibTable["fabs"]      = "F64::abs";
137         fMathLibTable["acos"]      = "F64::acos";
138         fMathLibTable["asin"]      = "F64::asin";
139         fMathLibTable["atan"]      = "F64::atan";
140         fMathLibTable["atan2"]     = "F64::atan2";
141         fMathLibTable["ceil"]      = "F64::ceil";
142         fMathLibTable["cos"]       = "F64::cos";
143         fMathLibTable["exp"]       = "F64::exp";
144         fMathLibTable["floor"]     = "F64::floor";
145         fMathLibTable["fmod"]      = "libm::fmod";
146         fMathLibTable["log"]       = "F64::log";
147         fMathLibTable["log10"]     = "F64::log10";
148         fMathLibTable["max_"]      = "F64::max";
149         fMathLibTable["min_"]      = "F64::min";
150         fMathLibTable["pow"]       = "F64::powf";
151         fMathLibTable["remainder"] = "F64::rem_euclid";
152         //fMathLibTable["rint"]      = "linux_api_math::rint";  // TODO
153         fMathLibTable["rint"]      = "F64::round";
154         fMathLibTable["round"]     = "F64::round";
155         fMathLibTable["sin"]       = "F64::sin";
156         fMathLibTable["sqrt"]      = "F64::sqrt";
157         fMathLibTable["tan"]       = "F64::tan";
158 
159         // Additional hyperbolic math functions
160         fMathLibTable["acosh"]     = "F64::acosh";
161         fMathLibTable["asinh"]     = "F64::asinh";
162         fMathLibTable["atanh"]     = "F64::atanh";
163         fMathLibTable["cosh"]      = "F64::cosh";
164         fMathLibTable["sinh"]      = "F64::sinh";
165         fMathLibTable["tanh"]      = "F64::tanh";
166 
167         fMathLibTable["isnan"]     = "F64::is_nan";
168         fMathLibTable["isinf"]     = "F64::is_infinite";
169         fMathLibTable["copysign"]  = "F64::copysign";
170     }
171 
~RustInstVisitor()172     virtual ~RustInstVisitor() {}
173 
visit(DeclareVarInst * inst)174     virtual void visit(DeclareVarInst* inst)
175     {
176         if (inst->fAddress->getAccess() & Address::kStaticStruct) {
177             *fOut << "static mut ";
178         }
179 
180         if (inst->fAddress->getAccess() & Address::kStack || inst->fAddress->getAccess() & Address::kLoop) {
181             *fOut << "let mut ";
182         }
183 
184         // If type is kNoType, only generate the name, otherwise a typed expression
185         if (inst->fType->getType() == Typed::VarType::kNoType) {
186             *fOut << inst->fAddress->getName();
187         } else {
188             *fOut << fTypeManager->generateType(inst->fType, inst->fAddress->getName());
189         }
190 
191         if (inst->fValue) {
192             *fOut << " = ";
193             inst->fValue->accept(this);
194         } else if (inst->fAddress->getAccess() & Address::kStaticStruct) {
195             *fOut << " = ";
196             RustInitFieldsVisitor::ZeroInitializer(fOut, inst->fType);
197         }
198 
199         EndLine((inst->fAddress->getAccess() & Address::kStruct) ? ',' : ';');
200     }
201 
visit(DeclareBufferIterators * inst)202     virtual void visit(DeclareBufferIterators* inst)
203     {
204         /* Generates an expression like:
205         let (outputs0, outputs1) = if let [outputs0, outputs1, ..] = outputs {
206             let outputs0 = outputs0[..count as usize].iter_mut();
207             let outputs1 = outputs1[..count as usize].iter_mut();
208             (outputs0, outputs1)
209         } else {
210             panic!("wrong number of outputs");
211         };
212         */
213 
214         // Don't generate if no channels
215         if (inst->fNumChannels == 0) return;
216 
217         std::string name = inst->fBufferName2;
218 
219         // Build pattern matching + if let line
220         *fOut << "let (";
221         for (int i = 0; i < inst->fNumChannels; ++i) {
222             if (i > 0) {
223                 *fOut << ", ";
224             }
225             *fOut << name << i;
226         }
227         *fOut << ") = if let [";
228         for (int i = 0; i < inst->fNumChannels; ++i) {
229             *fOut << name << i << ", ";
230         }
231         *fOut << "..] = " << name << " {";
232 
233         // Build fixed size iterator variables
234         fTab++;
235         for (int i = 0; i < inst->fNumChannels; ++i) {
236             tab(fTab, *fOut);
237             *fOut << "let " << name << i << " = " << name << i << "[..count as usize]";
238             if (inst->fMutable) {
239                 *fOut << ".iter_mut();";
240             } else {
241                 *fOut << ".iter();";
242             }
243         }
244 
245         // Build return tuple
246         tab(fTab, *fOut);
247         *fOut << "(";
248         for (int i = 0; i < inst->fNumChannels; ++i) {
249             if (i > 0) {
250                 *fOut << ", ";
251             }
252             *fOut << name << i;
253         }
254         *fOut << ")";
255 
256         // Build else branch
257         fTab--;
258         tab(fTab, *fOut);
259         *fOut << "} else {";
260 
261         fTab++;
262         tab(fTab, *fOut);
263         *fOut << "panic!(\"wrong number of " << name << "\");";
264 
265         fTab--;
266         tab(fTab, *fOut);
267         *fOut << "};";
268         tab(fTab, *fOut);
269     }
270 
visit(DeclareFunInst * inst)271     virtual void visit(DeclareFunInst* inst)
272     {
273         // Already generated
274         if (gFunctionSymbolTable.find(inst->fName) != gFunctionSymbolTable.end()) {
275             return;
276         } else {
277             gFunctionSymbolTable[inst->fName] = true;
278         }
279 
280         // Only generates additional functions
281         if (fMathLibTable.find(inst->fName) == fMathLibTable.end()) {
282             // Prototype
283             // Since functions are attached to a trait they must not be prefixed with "pub".
284             // In case we need a mechanism to attach functions to both traits and normal
285             // impls, we need a mechanism to forward the information whether to use "pub"
286             // or not. In the worst case, we have to prefix the name string like "pub fname",
287             // and handle the prefix here.
288             *fOut << "fn " << inst->fName;
289             generateFunDefArgs(inst);
290             generateFunDefBody(inst);
291         }
292     }
293 
generateFunDefBody(DeclareFunInst * inst)294     virtual void generateFunDefBody(DeclareFunInst* inst)
295     {
296         *fOut << ") -> " << fTypeManager->generateType(inst->fType->fResult);
297         if (inst->fCode->fCode.size() == 0) {
298             *fOut << ";" << endl;  // Pure prototype
299         } else {
300             // Function body
301             *fOut << " {";
302             fTab++;
303             tab(fTab, *fOut);
304             inst->fCode->accept(this);
305             fTab--;
306             back(1, *fOut);
307             *fOut << "}";
308             tab(fTab, *fOut);
309         }
310     }
311 
visit(RetInst * inst)312     virtual void visit(RetInst* inst)
313     {
314         if (inst->fResult) {
315             inst->fResult->accept(this);
316         } else {
317             *fOut << "return";
318             EndLine();
319         }
320     }
321 
visit(NamedAddress * named)322     virtual void visit(NamedAddress* named)
323     {
324         if (named->getAccess() & Address::kStruct) {
325             if (named->getAccess() & Address::kReference && named->getAccess() & Address::kMutable) {
326                 *fOut << "&mut self.";
327             } else {
328                 *fOut << "self.";
329             }
330         } else if (named->getAccess() & Address::kStaticStruct) {
331             if (named->getAccess() & Address::kReference && named->getAccess() & Address::kMutable) {
332                 *fOut << "&mut ";
333             }
334         }
335         *fOut << named->getName();
336     }
337 
visit(IndexedAddress * indexed)338     virtual void visit(IndexedAddress* indexed)
339     {
340         indexed->fAddress->accept(this);
341         if (dynamic_cast<Int32NumInst*>(indexed->fIndex)) {
342             *fOut << "[";
343             indexed->fIndex->accept(this);
344             *fOut << "]";
345         } else {
346             // Array index expression casted to 'usize' type
347             *fOut << "[";
348             indexed->fIndex->accept(this);
349             *fOut << " as usize]";
350         }
351     }
352 
visit(LoadVarInst * inst)353     virtual void visit(LoadVarInst* inst)
354     {
355         if (inst->fAddress->getAccess() & Address::kStaticStruct) {
356             *fOut << "unsafe { ";
357         }
358         inst->fAddress->accept(this);
359         if (inst->fAddress->getAccess() & Address::kStaticStruct) {
360             *fOut << " }";
361         }
362     }
363 
visit(LoadVarAddressInst * inst)364     virtual void visit(LoadVarAddressInst* inst)
365     {
366         *fOut << "&";
367         inst->fAddress->accept(this);
368     }
369 
visit(FloatArrayNumInst * inst)370     virtual void visit(FloatArrayNumInst* inst)
371     {
372         char sep = '[';
373         for (size_t i = 0; i < inst->fNumTable.size(); i++) {
374             *fOut << sep << checkFloat(inst->fNumTable[i]);
375             sep = ',';
376         }
377         *fOut << ']';
378     }
379 
visit(Int32ArrayNumInst * inst)380     virtual void visit(Int32ArrayNumInst* inst)
381     {
382         char sep = '[';
383         for (size_t i = 0; i < inst->fNumTable.size(); i++) {
384             *fOut << sep << inst->fNumTable[i];
385             sep = ',';
386         }
387         *fOut << ']';
388     }
389 
visit(DoubleArrayNumInst * inst)390     virtual void visit(DoubleArrayNumInst* inst)
391     {
392         char sep = '[';
393         for (size_t i = 0; i < inst->fNumTable.size(); i++) {
394             *fOut << sep << checkDouble(inst->fNumTable[i]);
395             sep = ',';
396         }
397         *fOut << ']';
398     }
399 
visit(BinopInst * inst)400     virtual void visit(BinopInst* inst)
401     {
402         // Special case for 'logical right-shift'
403         if (strcmp(gBinOpTable[inst->fOpcode]->fName, ">>>") == 0) {
404             TypingVisitor typing;
405             inst->fInst1->accept(&typing);
406             *fOut << "(((";
407             inst->fInst1->accept(this);
408             if (isInt64Type(typing.fCurType)) {
409                 *fOut << " as u64)";
410             } else if (isInt32Type(typing.fCurType)) {
411                 *fOut << " as u32)";
412             } else {
413                 faustassert(false);
414             }
415             *fOut << " >> ";
416             inst->fInst2->accept(this);
417             *fOut << ")";
418             if (isInt64Type(typing.fCurType)) {
419                 *fOut << " as i64)";
420             } else if (isInt32Type(typing.fCurType)) {
421                 *fOut << " as i32)";
422             } else {
423                 faustassert(false);
424             }
425         } else {
426             TextInstVisitor::visit(inst);
427         }
428     }
429 
visit(::CastInst * inst)430     virtual void visit(::CastInst* inst)
431     {
432         *fOut << "(";
433         inst->fInst->accept(this);
434         *fOut << " as " << fTypeManager->generateType(inst->fType);
435         *fOut << ")";
436     }
437 
visit(BitcastInst * inst)438     virtual void visit(BitcastInst* inst) { faustassert(false); }
439 
visit(FunCallInst * inst)440     virtual void visit(FunCallInst* inst)
441     {
442         if (fMathLibTable.find(inst->fName) != fMathLibTable.end()) {
443             generateFunCall(inst, fMathLibTable[inst->fName]);
444         } else {
445             generateFunCall(inst, inst->fName);
446         }
447     }
448 
generateFunCall(FunCallInst * inst,const std::string & fun_name)449     virtual void generateFunCall(FunCallInst* inst, const std::string& fun_name)
450     {
451         if (inst->fMethod) {
452             list<ValueInst*>::const_iterator it = inst->fArgs.begin();
453             // Compile object arg
454             (*it)->accept(this);
455             // Compile parameters
456             *fOut << fObjectAccess;
457             // Hack for 1 FIR generated names
458             if (startWith(fun_name, "instanceInit")) {
459                 *fOut << "instance_init" << fun_name.substr(12) << "(";
460             } else {
461                 *fOut << fun_name << "(";
462             }
463             generateFunCallArgs(++it, inst->fArgs.end(), int(inst->fArgs.size()) - 1);
464         } else {
465             *fOut << fun_name << "(";
466             // Compile parameters
467             generateFunCallArgs(inst->fArgs.begin(), inst->fArgs.end(), int(inst->fArgs.size()));
468             // Hack for 'log' function that needs a base
469             if (fun_name == "F32::log") {
470                 *fOut << ", std::f32::consts::E";
471             } else if (fun_name == "F64::log") {
472                 *fOut << ", std::f64::consts::E";
473             }
474         }
475         *fOut << ")";
476     }
477 
visit(Select2Inst * inst)478     virtual void visit(Select2Inst* inst)
479     {
480         *fOut << "if (";
481         inst->fCond->accept(this);
482         // Force 'cond' to bool type
483         *fOut << " as i32 != 0) { ";
484         inst->fThen->accept(this);
485         *fOut << " } else { ";
486         inst->fElse->accept(this);
487         *fOut << " }";
488     }
489 
visit(IfInst * inst)490     virtual void visit(IfInst* inst)
491     {
492         *fOut << "if (";
493         inst->fCond->accept(this);
494         // Force 'cond' to bool type
495         *fOut << " as i32 == 1) { ";
496         fTab++;
497         tab(fTab, *fOut);
498         inst->fThen->accept(this);
499         fTab--;
500         back(1, *fOut);
501         if (inst->fElse->fCode.size() > 0) {
502             *fOut << "} else {";
503             fTab++;
504             tab(fTab, *fOut);
505             inst->fElse->accept(this);
506             fTab--;
507             back(1, *fOut);
508             *fOut << "}";
509         } else {
510             *fOut << "}";
511         }
512         tab(fTab, *fOut);
513     }
514 
visit(ForLoopInst * inst)515     virtual void visit(ForLoopInst* inst)
516     {
517         // Don't generate empty loops...
518         if (inst->fCode->size() == 0) return;
519 
520         inst->fInit->accept(this);
521         *fOut << "loop {";
522         fTab++;
523         tab(fTab, *fOut);
524         inst->fCode->accept(this);
525         inst->fIncrement->accept(this);
526         *fOut << "if ";
527         inst->fEnd->accept(this);
528         *fOut << " { continue; } else { break; }";
529         fTab--;
530         tab(fTab, *fOut);
531         *fOut << "}";
532         tab(fTab, *fOut);
533     }
534 
visit(SimpleForLoopInst * inst)535     virtual void visit(SimpleForLoopInst* inst)
536     {
537         // Don't generate empty loops...
538         if (inst->fCode->size() == 0) return;
539 
540         *fOut << "for " << inst->getName() << " in ";
541         if (inst->fReverse) {
542             *fOut << "(";
543             inst->fLowerBound->accept(this);
544             *fOut << "..=";
545             inst->fUpperBound->accept(this);
546             *fOut << ").rev()";
547         } else {
548             inst->fLowerBound->accept(this);
549             *fOut << "..";
550             inst->fUpperBound->accept(this);
551         }
552         *fOut << " {";
553         fTab++;
554         tab(fTab, *fOut);
555         inst->fCode->accept(this);
556         fTab--;
557         back(1, *fOut);
558         *fOut << "}";
559         tab(fTab, *fOut);
560     }
561 
visit(IteratorForLoopInst * inst)562     virtual void visit(IteratorForLoopInst* inst)
563     {
564         // Don't generate empty loops...
565         if (inst->fCode->size() == 0) return;
566 
567         *fOut << "let zipped_iterators = ";
568         for (std::size_t i = 0; i < inst->fIterators.size(); ++i) {
569             if (i == 0) {
570                 inst->fIterators[i]->accept(this);
571             } else {
572                 *fOut << ".zip(";
573                 inst->fIterators[i]->accept(this);
574                 *fOut << ")";
575             }
576         }
577         *fOut << ";";
578         tab(fTab, *fOut);
579 
580         *fOut << "for ";
581         for (std::size_t i = 0; i < inst->fIterators.size() - 1; ++i) {
582             *fOut << "(";
583         }
584         *fOut << makeNameSingular(inst->fIterators[0]->getName());
585         for (std::size_t i = 1; i < inst->fIterators.size(); ++i) {
586             *fOut << ", " << makeNameSingular(inst->fIterators[i]->getName()) << ")";
587         }
588         *fOut << " in zipped_iterators {";
589         fTab++;
590         tab(fTab, *fOut);
591         inst->fCode->accept(this);
592         fTab--;
593         back(1, *fOut);
594         *fOut << "}";
595         tab(fTab, *fOut);
596     }
597 
visit(::SwitchInst * inst)598     virtual void visit(::SwitchInst* inst)
599     {
600         *fOut << "match (";
601         inst->fCond->accept(this);
602         *fOut << ") {";
603         fTab++;
604         tab(fTab, *fOut);
605         for (const auto& it : inst->fCode) {
606             if (it.first == -1) {  // -1 used to code "default" case
607                 *fOut << "_ => {";
608             } else {
609                 *fOut << it.first << " => {";
610             }
611             fTab++;
612             tab(fTab, *fOut);
613             (it.second)->accept(this);
614             fTab--;
615             back(1, *fOut);
616             *fOut << "},";
617             tab(fTab, *fOut);
618         }
619         fTab--;
620         back(1, *fOut);
621         *fOut << "} ";
622         tab(fTab, *fOut);
623     }
624 
cleanup()625     static void cleanup() { gFunctionSymbolTable.clear(); }
626 };
627 
628 /**
629  * Helper visitor that allows to build a parameter lookup table.
630  */
631 class UserInterfaceParameterMapping : public InstVisitor {
632    private:
633     map<string, int>      fParameterLookup;
634     int                   fParameterIndex;
635 
636    public:
637     using InstVisitor::visit;
638 
UserInterfaceParameterMapping()639     UserInterfaceParameterMapping()
640         : InstVisitor(), fParameterLookup{}, fParameterIndex{0}
641     {}
642 
~UserInterfaceParameterMapping()643     virtual ~UserInterfaceParameterMapping() {}
644 
getParameterLookup()645     map<string, int> getParameterLookup() {
646         return fParameterLookup;
647     }
648 
visit(BlockInst * inst)649     virtual void visit(BlockInst* inst)
650     {
651         // BlockInst visitor is unimplemented in base class, so we need a trivial implementation
652         // to actually visit the user interface statements in the BlockInst.
653         for (const auto& it : inst->fCode) {
654             it->accept(this);
655         }
656     }
657 
visit(AddMetaDeclareInst * inst)658     virtual void visit(AddMetaDeclareInst* inst)
659     {
660         // Only store fZone's value if it is not the 0 / nullptr special case
661         if (inst->fZone != "0") {
662             if (fParameterLookup.find(inst->fZone) == fParameterLookup.end()) {
663                 fParameterLookup[inst->fZone] = fParameterIndex++;
664             }
665         }
666     }
667 
visit(AddButtonInst * inst)668     virtual void visit(AddButtonInst* inst)
669     {
670         if (fParameterLookup.find(inst->fZone) == fParameterLookup.end()) {
671             fParameterLookup[inst->fZone] = fParameterIndex++;
672         }
673     }
674 
visit(AddSliderInst * inst)675     virtual void visit(AddSliderInst* inst)
676     {
677         if (fParameterLookup.find(inst->fZone) == fParameterLookup.end()) {
678             fParameterLookup[inst->fZone] = fParameterIndex++;
679         }
680     }
681 
visit(AddBargraphInst * inst)682     virtual void visit(AddBargraphInst* inst)
683     {
684         if (fParameterLookup.find(inst->fZone) == fParameterLookup.end()) {
685             fParameterLookup[inst->fZone] = fParameterIndex++;
686         }
687     }
688 
689 };
690 
691 /**
692  * Visitor for building user interface instructions based on the parameter lookup table.
693  */
694 class RustUIInstVisitor : public TextInstVisitor {
695    private:
696     map<string, int>      fParameterLookup;
697 
getParameterIndex(string name)698     int getParameterIndex(string name) {
699         auto parameterIndex = fParameterLookup.find(name);
700         if (parameterIndex == fParameterLookup.end()) {
701             throw runtime_error("Parameter '" + name + "' is unknown");
702         } else {
703             return parameterIndex->second;
704         }
705     }
706 
707    public:
708     using TextInstVisitor::visit;
709 
RustUIInstVisitor(std::ostream * out,const string & structname,map<string,int> parameterLookup,int tab=0)710     RustUIInstVisitor(std::ostream* out, const string& structname, map<string, int> parameterLookup, int tab = 0)
711         : TextInstVisitor(out, ".", new RustStringTypeManager(xfloat(), "&"), tab),
712           fParameterLookup{parameterLookup}
713     {}
714 
~RustUIInstVisitor()715     virtual ~RustUIInstVisitor()
716     {}
717 
visit(AddMetaDeclareInst * inst)718     virtual void visit(AddMetaDeclareInst* inst)
719     {
720         // Special case
721         if (inst->fZone == "0") {
722             *fOut << "ui_interface.declare(None, " << quote(inst->fKey) << ", " << quote(inst->fValue)
723                   << ")";
724         } else {
725             *fOut << "ui_interface.declare(Some(ParamIndex(" << getParameterIndex(inst->fZone) << ")), " << quote(inst->fKey) << ", "
726                   << quote(inst->fValue) << ")";
727         }
728         EndLine();
729     }
730 
visit(OpenboxInst * inst)731     virtual void visit(OpenboxInst* inst)
732     {
733         string name;
734         switch (inst->fOrient) {
735             case OpenboxInst::kVerticalBox:
736                 name = "ui_interface.open_vertical_box(";
737                 break;
738             case OpenboxInst::kHorizontalBox:
739                 name = "ui_interface.open_horizontal_box(";
740                 break;
741             case OpenboxInst::kTabBox:
742                 name = "ui_interface.open_tab_box(";
743                 break;
744         }
745         *fOut << name << quote(inst->fName) << ")";
746         EndLine();
747     }
748 
visit(CloseboxInst * inst)749     virtual void visit(CloseboxInst* inst)
750     {
751         *fOut << "ui_interface.close_box();";
752         tab(fTab, *fOut);
753     }
754 
visit(AddButtonInst * inst)755     virtual void visit(AddButtonInst* inst)
756     {
757         if (inst->fType == AddButtonInst::kDefaultButton) {
758             *fOut << "ui_interface.add_button(" << quote(inst->fLabel) << ", ParamIndex(" << getParameterIndex(inst->fZone) << "))";
759         } else {
760             *fOut << "ui_interface.add_check_button(" << quote(inst->fLabel) << ", ParamIndex(" << getParameterIndex(inst->fZone) << "))";
761         }
762         EndLine();
763     }
764 
visit(AddSliderInst * inst)765     virtual void visit(AddSliderInst* inst)
766     {
767         string name;
768         switch (inst->fType) {
769             case AddSliderInst::kHorizontal:
770                 name = "ui_interface.add_horizontal_slider";
771                 break;
772             case AddSliderInst::kVertical:
773                 name = "ui_interface.add_vertical_slider";
774                 break;
775             case AddSliderInst::kNumEntry:
776                 name = "ui_interface.add_num_entry";
777                 break;
778         }
779         *fOut << name << "(" << quote(inst->fLabel) << ", "
780               << "ParamIndex(" << getParameterIndex(inst->fZone) << "), " << checkReal(inst->fInit) << ", " << checkReal(inst->fMin) << ", "
781               << checkReal(inst->fMax) << ", " << checkReal(inst->fStep) << ")";
782         EndLine();
783     }
784 
visit(AddBargraphInst * inst)785     virtual void visit(AddBargraphInst* inst)
786     {
787         string name;
788         switch (inst->fType) {
789             case AddBargraphInst::kHorizontal:
790                 name = "ui_interface.add_horizontal_bargraph";
791                 break;
792             case AddBargraphInst::kVertical:
793                 name = "ui_interface.add_vertical_bargraph";
794                 break;
795         }
796         *fOut << name << "(" << quote(inst->fLabel) << ", ParamIndex(" << getParameterIndex(inst->fZone) << "), " << checkReal(inst->fMin)
797               << ", " << checkReal(inst->fMax) << ")";
798         EndLine();
799     }
800 
visit(AddSoundfileInst * inst)801     virtual void visit(AddSoundfileInst* inst)
802     {
803         // Not supported for now
804         throw faustexception("ERROR : 'soundfile' primitive not yet supported for Rust\n");
805     }
806 };
807 
808 #endif
809