1 /************************************************************************
2  ************************************************************************
3     FAUST compiler
4     Copyright (C) 2003-2018 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 /*****************************************************************************
23     HISTORY
24     22/01/05 : corrected bug on bool signals cached in float variables
25     2009-08-16 : First "doc" version (kb)
26     2009-11-22 : Some clean up (kb)
27 *****************************************************************************/
28 
29 #include <math.h>
30 #include <stdio.h>
31 #include <iostream>
32 #include <sstream>
33 #include <vector>
34 
35 #include "compatibility.hh"
36 #include "doc.hh"
37 #include "doc_compile.hh"
38 #include "doc_notice.hh"
39 #include "exception.hh"
40 #include "floats.hh"
41 #include "global.hh"
42 #include "names.hh"
43 #include "ppsig.hh"
44 #include "prim2.hh"
45 #include "privatise.hh"
46 #include "recursivness.hh"
47 #include "sigprint.hh"
48 #include "sigtype.hh"
49 #include "sigtyperules.hh"
50 #include "simplify.hh"
51 #include "tlib.hh"
52 #include "xtended.hh"
53 
54 extern bool getSigListNickName(Tree t, Tree& id);
55 
56 /*****************************************************************************
57                         getFreshID
58 *****************************************************************************/
59 
getFreshID(const string & prefix)60 string DocCompiler::getFreshID(const string& prefix)
61 {
62     if (gGlobal->gIDCounters.find(prefix) == gGlobal->gIDCounters.end()) {
63         gGlobal->gIDCounters[prefix] = 1;
64     }
65     int n                        = gGlobal->gIDCounters[prefix];
66     gGlobal->gIDCounters[prefix] = n + 1;
67 
68     return subst("$0_{$1}", prefix, docT(n));
69 }
70 
71 /*****************************************************************************
72                             prepare
73 *****************************************************************************/
74 
annotate(Tree LS)75 Tree DocCompiler::annotate(Tree LS)
76 {
77     recursivnessAnnotation(LS);                         // Annotate LS with recursivness information
78     typeAnnotation(LS, gGlobal->gLocalCausalityCheck);  // Annotate LS with type information
79     sharingAnalysis(LS);                                // annotate LS with sharing count
80     fOccMarkup.mark(LS);                                // annotate LS with occurences analysis
81 
82     return LS;
83 }
84 
85 /*****************************************************************************
86                             compileLateq
87 *****************************************************************************/
88 
compileLateq(Tree L,Lateq * compiledEqn)89 Lateq* DocCompiler::compileLateq(Tree L, Lateq* compiledEqn)
90 {
91     // cerr << "Documentator : compileLateq : L = "; printSignal(L, stdout, 0); cerr << endl;
92 
93     fLateq       = compiledEqn;  ///< Dynamic field !
94     int priority = 0;
95 
96     for (int i = 0; isList(L); L = tl(L), i++) {
97         Tree sig = hd(L);
98         Tree id;
99         if (getSigNickname(sig, id)) {
100             // cerr << "Documentator : compileLateq : NICKNAMEPROPERTY = " << tree2str(id) << endl;
101             fLateq->addOutputSigFormula(subst("$0(t) = $1", tree2str(id), CS(sig, priority), docT(i)));
102         } else {
103             // cerr << "Documentator : compileLateq : NO NICKNAMEPROPERTY" << endl;
104             if (fLateq->outputs() == 1) {
105                 fLateq->addOutputSigFormula(subst("y(t) = $0", CS(sig, priority)));
106                 gGlobal->gDocNoticeFlagMap["outputsig"] = true;
107             } else {
108                 fLateq->addOutputSigFormula(subst("$0(t) = $1", getFreshID("y"), CS(sig, priority)));
109                 gGlobal->gDocNoticeFlagMap["outputsigs"] = true;
110             }
111         }
112     }
113     return fLateq;
114 }
115 
116 /*****************************************************************************
117                              CS : compile a signal
118 *****************************************************************************/
119 
120 /**
121  * Test if a signal is already compiled
122  * @param	sig		the signal expression to compile.
123  * @param	name	the string representing the compiled expression.
124  * @return	true	is already compiled
125  */
getCompiledExpression(Tree sig,string & cexp)126 bool DocCompiler::getCompiledExpression(Tree sig, string& cexp)
127 {
128     return fCompileProperty.get(sig, cexp);
129 }
130 
131 /**
132  * Set the string of a compiled expression is already compiled
133  * @param	sig		the signal expression to compile.
134  * @param	cexp	the string representing the compiled expression.
135  * @return	the cexp (for commodity)
136  */
setCompiledExpression(Tree sig,const string & cexp)137 string DocCompiler::setCompiledExpression(Tree sig, const string& cexp)
138 {
139     fCompileProperty.set(sig, cexp);
140     return cexp;
141 }
142 
143 /**
144  * Compile a signal
145  * @param sig the signal expression to compile.
146  * @return the C code translation of sig as a string
147  */
CS(Tree sig,int priority)148 string DocCompiler::CS(Tree sig, int priority)
149 {
150     string code;
151 
152     if (!getCompiledExpression(sig, code)) {  // not compiled yet.
153         code = generateCode(sig, priority);
154         setCompiledExpression(sig, code);
155     }
156     return code;
157 }
158 
159 /*****************************************************************************
160                     generateCode : dispatch according to signal
161 *****************************************************************************/
162 
163 /**
164  * @brief Main code generator dispatch.
165  *
166  * According to the type of the input signal, generateCode calls
167  * the appropriate generator with appropriate arguments.
168  *
169  * @param	sig			The signal expression to compile.
170  * @param	priority	The environment priority of the expression.
171  * @return	<string>	The LaTeX code translation of the signal.
172  */
generateCode(Tree sig,int priority)173 string DocCompiler::generateCode(Tree sig, int priority)
174 {
175     int    i;
176     double r;
177     Tree   c, sel, x, y, z, u, label, ff, largs, type, name, file;
178 
179     if (getUserData(sig)) {
180         printGCCall(sig, "generateXtended");
181         return generateXtended(sig, priority);
182     } else if (isSigInt(sig, &i)) {
183         printGCCall(sig, "generateNumber");
184         return generateNumber(sig, docT(i));
185     } else if (isSigReal(sig, &r)) {
186         printGCCall(sig, "generateNumber");
187         return generateNumber(sig, docT(r));
188     } else if (isSigInput(sig, &i)) {
189         printGCCall(sig, "generateInput");
190         return generateInput(sig, docT(i + 1));
191     } else if (isSigOutput(sig, &i, x)) {
192         printGCCall(sig, "generateOutput");
193         return generateOutput(sig, docT(i + 1), CS(x, priority));
194     }
195 
196     else if (isSigDelay(sig, x, y)) {
197         printGCCall(sig, "generateDelay");
198         return generateDelay(sig, x, y, priority);
199     } else if (isSigPrefix(sig, x, y)) {
200         printGCCall(sig, "generatePrefix");
201         return generatePrefix(sig, x, y, priority);
202     } else if (isSigIota(sig, x)) {
203         printGCCall(sig, "generateIota");
204         return generateIota(sig, x);
205     }
206 
207     else if (isSigBinOp(sig, &i, x, y)) {
208         printGCCall(sig, "generateBinOp");
209         return generateBinOp(sig, i, x, y, priority);
210     } else if (isSigFFun(sig, ff, largs)) {
211         printGCCall(sig, "generateFFun");
212         return generateFFun(sig, ff, largs, priority);
213     } else if (isSigFConst(sig, type, name, file)) {
214         printGCCall(sig, "generateFConst");
215         return generateFConst(sig, tree2str(file), tree2str(name));
216     } else if (isSigFVar(sig, type, name, file)) {
217         printGCCall(sig, "generateFVar");
218         return generateFVar(sig, tree2str(file), tree2str(name));
219     }
220 
221     // new special tables for documentation purposes
222 
223     else if (isSigDocConstantTbl(sig, x, y)) {
224         printGCCall(sig, "generateDocConstantTbl");
225         return generateDocConstantTbl(sig, x, y);
226     } else if (isSigDocWriteTbl(sig, x, y, z, u)) {
227         printGCCall(sig, "generateDocWriteTbl");
228         return generateDocWriteTbl(sig, x, y, z, u);
229     } else if (isSigDocAccessTbl(sig, x, y)) {
230         printGCCall(sig, "generateDocAccessTbl");
231         return generateDocAccessTbl(sig, x, y);
232     }
233 
234     else if (isSigSelect2(sig, sel, x, y)) {
235         printGCCall(sig, "generateSelect2");
236         return generateSelect2(sig, sel, x, y, priority);
237     }
238 
239     else if (isProj(sig, &i, x)) {
240         printGCCall(sig, "generateRecProj");
241         return generateRecProj(sig, x, i, priority);
242     }
243 
244     else if (isSigIntCast(sig, x)) {
245         printGCCall(sig, "generateIntCast");
246         return generateIntCast(sig, x, priority);
247     } else if (isSigFloatCast(sig, x)) {
248         printGCCall(sig, "generateFloatCast");
249         return generateFloatCast(sig, x, priority);
250     }
251 
252     else if (isSigButton(sig, label)) {
253         printGCCall(sig, "generateButton");
254         return generateButton(sig, label);
255     } else if (isSigCheckbox(sig, label)) {
256         printGCCall(sig, "generateCheckbox");
257         return generateCheckbox(sig, label);
258     } else if (isSigVSlider(sig, label, c, x, y, z)) {
259         printGCCall(sig, "generateVSlider");
260         return generateVSlider(sig, label, c, x, y, z);
261     } else if (isSigHSlider(sig, label, c, x, y, z)) {
262         printGCCall(sig, "generateHSlider");
263         return generateHSlider(sig, label, c, x, y, z);
264     } else if (isSigNumEntry(sig, label, c, x, y, z)) {
265         printGCCall(sig, "generateNumEntry");
266         return generateNumEntry(sig, label, c, x, y, z);
267     }
268 
269     else if (isSigVBargraph(sig, label, x, y, z)) {
270         printGCCall(sig, "generateVBargraph");
271         return CS(z, priority);
272     }  // generateVBargraph 	(sig, label, x, y, CS(z, priority)); }
273     else if (isSigHBargraph(sig, label, x, y, z)) {
274         printGCCall(sig, "generateHBargraph");
275         return CS(z, priority);
276     }  // generateHBargraph 	(sig, label, x, y, CS(z, priority)); }
277     else if (isSigAttach(sig, x, y)) {
278         printGCCall(sig, "generateAttach");
279         return generateAttach(sig, x, y, priority);
280     } else if (isSigEnable(sig, x, y)) {
281         printGCCall(sig, "generateControl");
282         return generateControl(sig, x, y, priority);
283     }
284 
285     else {
286         stringstream error;
287         error << "ERROR in d signal, unrecognized signal : " << *sig << endl;
288         throw faustexception(error.str());
289     }
290     faustassert(0);
291     return "error in generate code";
292 }
293 
294 /**
295  * Print calling information of generateCode, for debug purposes.
296  *
297  * @remark
298  * To turn printing on, turn the 'printCalls' boolean to true.
299  */
printGCCall(Tree sig,const string & calledFunction)300 void DocCompiler::printGCCall(Tree sig, const string& calledFunction)
301 {
302     bool printCalls = false;
303     bool maskSigs   = false;
304 
305     if (printCalls) {
306         cerr << "  -> generateCode calls " << calledFunction;
307         if (maskSigs) {
308             cerr << endl;
309         } else {
310             cerr << " on " << ppsig(sig) << endl;
311         }
312     }
313 }
314 
315 /*****************************************************************************
316                                NUMBERS
317 *****************************************************************************/
318 
generateNumber(Tree sig,const string & exp)319 string DocCompiler::generateNumber(Tree sig, const string& exp)
320 {
321     string      ctype, vname;
322     Occurences* o = fOccMarkup.retrieve(sig);
323 
324     // check for number occuring in delays
325     if (o->getMaxDelay() > 0) {
326         getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
327         gGlobal->gDocNoticeFlagMap["recursigs"] = true;
328         // cerr << "- r : generateNumber : \"" << vname << "\"" << endl;
329         generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
330     }
331     return exp;
332 }
333 
334 /*****************************************************************************
335                                FOREIGN CONSTANTS
336 *****************************************************************************/
337 
generateFConst(Tree sig,const string & file,const string & exp)338 string DocCompiler::generateFConst(Tree sig, const string& file, const string& exp)
339 {
340     string      ctype, vname;
341     Occurences* o = fOccMarkup.retrieve(sig);
342 
343     if (o->getMaxDelay() > 0) {
344         getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
345         gGlobal->gDocNoticeFlagMap["recursigs"] = true;
346         // cerr << "- r : generateFConst : \"" << vname << "\"" << endl;
347         generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
348     }
349 
350     if (exp == "fSampleRate" || exp == "fSamplingFreq") {
351         // gGlobal->gDocNoticeFlagMap["fsamp"] = true;
352         return "f_S";
353     }
354 
355     return "\\mathrm{" + exp + "}";
356 }
357 
358 /*****************************************************************************
359                                FOREIGN VARIABLES
360 *****************************************************************************/
361 
generateFVar(Tree sig,const string & file,const string & exp)362 string DocCompiler::generateFVar(Tree sig, const string& file, const string& exp)
363 {
364     string      ctype, vname;
365     Occurences* o = fOccMarkup.retrieve(sig);
366 
367     if (o->getMaxDelay() > 0) {
368         getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
369         gGlobal->gDocNoticeFlagMap["recursigs"] = true;
370         // cerr << "- r : generateFVar : \"" << vname << "\"" << endl;
371         setVectorNameProperty(sig, vname);
372         generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
373     }
374     return generateCacheCode(sig, exp);
375 }
376 
377 /*****************************************************************************
378                                INPUTS - OUTPUTS
379 *****************************************************************************/
380 
generateInput(Tree sig,const string & idx)381 string DocCompiler::generateInput(Tree sig, const string& idx)
382 {
383     if (fLateq->inputs() == 1) {
384         setVectorNameProperty(sig, "x");
385         fLateq->addInputSigFormula("x(t)");
386         gGlobal->gDocNoticeFlagMap["inputsig"] = true;
387         return generateCacheCode(sig, "x(t)");
388     } else {
389         setVectorNameProperty(sig, subst("x_{$0}", idx));
390         fLateq->addInputSigFormula(subst("x_{$0}(t)", idx));
391         gGlobal->gDocNoticeFlagMap["inputsigs"] = true;
392         return generateCacheCode(sig, subst("x_{$0}(t)", idx));
393     }
394 }
395 
396 /** Unused for the moment ! */
generateOutput(Tree sig,const string & idx,const string & arg)397 string DocCompiler::generateOutput(Tree sig, const string& idx, const string& arg)
398 {
399     string dst;
400 
401     if (fLateq->outputs() == 1) {
402         dst                                     = subst("y(t)", idx);
403         gGlobal->gDocNoticeFlagMap["outputsig"] = true;
404     } else {
405         dst                                      = subst("y_{$0}(t)", idx);
406         gGlobal->gDocNoticeFlagMap["outputsigs"] = true;
407     }
408 
409     fLateq->addOutputSigFormula(subst("$0 = $1", dst, arg));
410     return dst;
411 }
412 
413 /*****************************************************************************
414                                BINARY OPERATION
415 *****************************************************************************/
416 
417 /**
418  * Generate binary operations, managing priority parenthesis.
419  * ((a*b)+c) can be written (a*b+c) if priority(*) > priority(+)
420  * ((a*b)*c) can be writteb (a*b*c) if * is associative
421  * Associative operation should have a distinc priority from other operations.
422  * Non associative operations can share the same priority.
423  *
424  * @param	sig			The signal expression to treat.
425  * @param	opcode		The operation code, as described in gBinOpLateqTable.
426  * @param	arg1		The first operand.
427  * @param	arg2		The second operand.
428  * @param	priority	The priority of the environment of the expression.
429  *
430  * @return	<string>	The LaTeX code translation of the signal, cached.
431  *
432  * @remark	The case of LaTeX frac{}{} is special.
433  *
434  * @todo	Handle integer arithmetics, by testing arguments type,
435  * and printing dedicated operators (\oplus, \odot, \ominus, \oslash).
436  */
437 
438 /// associative operations are + * | & xor
associative(int opcode)439 static bool associative(int opcode)
440 {
441     return (opcode == kAdd) || (opcode == kMul) || (opcode == kAND) || (opcode == kOR) || (opcode == kXOR);
442 }
443 
generateControl(Tree sig,Tree arg1,Tree arg2,int priority)444 string DocCompiler::generateControl(Tree sig, Tree arg1, Tree arg2, int priority)
445 {
446     return generateBinOp(sig, kMul, arg1, arg2, priority);
447 }
448 
generateBinOp(Tree sig,int opcode,Tree arg1,Tree arg2,int priority)449 string DocCompiler::generateBinOp(Tree sig, int opcode, Tree arg1, Tree arg2, int priority)
450 {
451     string s;
452     int    thisPriority = gBinOpLateqTable[opcode]->fPriority;
453 
454     /* Priority parenthesis handling. */
455     string lpar = "";
456     string rpar = "";
457     if ((thisPriority < priority) || ((thisPriority == priority) && !associative(opcode))) {
458         // (a+b)*c or (a/b)/c need parenthesis
459         lpar = " \\left(";
460         rpar = "\\right) ";
461     }
462 
463     Type t1            = getCertifiedSigType(arg1);
464     Type t2            = getCertifiedSigType(arg2);
465     bool intOpDetected = false;
466     if ((t1->nature() == kInt) && (t2->nature() == kInt)) {
467         intOpDetected = true;
468     }
469 
470     string op;
471     if (!intOpDetected) {
472         op = gBinOpLateqTable[opcode]->fName;
473     } else {
474         switch (opcode) {
475             case kAdd:
476                 op                                    = "\\oplus";
477                 gGlobal->gDocNoticeFlagMap["intplus"] = true;
478                 break;
479             case kSub:
480                 op                                     = "\\ominus";
481                 gGlobal->gDocNoticeFlagMap["intminus"] = true;
482                 break;
483             case kMul:
484                 op                                    = "\\odot";
485                 gGlobal->gDocNoticeFlagMap["intmult"] = true;
486                 break;
487             case kDiv:
488                 op                                    = "\\oslash";
489                 gGlobal->gDocNoticeFlagMap["intdiv"]  = true;
490                 gGlobal->gDocNoticeFlagMap["intcast"] = true;  // "$normalize(int(i/j))$" in the notice.
491                 break;
492             default:
493                 op = gBinOpLateqTable[opcode]->fName;
494                 break;
495         }
496     }
497 
498     /* LaTeX frac{}{} handling VS general case. */
499     if ((opcode == kDiv) && (!intOpDetected)) {
500         s = subst("$0\\frac{$1}{$2}$3", lpar, CS(arg1, 0), CS(arg2, 0), rpar);
501     } else {
502         s = subst("$0$1 $2 $3$4", lpar, CS(arg1, thisPriority), op, CS(arg2, thisPriority), rpar);
503     }
504 
505     //	if (opcode == kMul) {
506     //		gGlobal->gDocNoticeFlagMap["cdot"] = true;
507     //	}
508 
509     return generateCacheCode(sig, s);
510 }
511 
512 /*****************************************************************************
513                                Primitive Operations
514 *****************************************************************************/
515 
generateFFun(Tree sig,Tree ff,Tree largs,int priority)516 string DocCompiler::generateFFun(Tree sig, Tree ff, Tree largs, int priority)
517 {
518     string code = ffname(ff);
519     code += '(';
520     string sep = "";
521     for (int i = 0; i < ffarity(ff); i++) {
522         code += sep;
523         code += CS(nth(largs, i), priority);
524         sep = ", ";
525     }
526     code += ')';
527 
528     gGlobal->gDocNoticeFlagMap["foreignfun"] = true;
529 
530     return "\\mathrm{ff" + code + "}";
531 }
532 
533 /*****************************************************************************
534                                CACHE CODE
535 *****************************************************************************/
536 
getTypedNames(Type t,const string & prefix,string & ctype,string & vname)537 void DocCompiler::getTypedNames(Type t, const string& prefix, string& ctype, string& vname)
538 {
539     if (t->nature() == kInt) {
540         ctype = "int";
541         vname = subst("$0", getFreshID(prefix));
542     } else {
543         ctype = ifloat();
544         vname = subst("$0", getFreshID(prefix));
545     }
546 }
547 
548 /**
549  * Test if exp is very simple that is it
550  * can't be considered a real component
551  * @param exp the signal we want to test
552  * @return true if it a very simple signal
553  */
isVerySimpleFormula(Tree sig)554 static bool isVerySimpleFormula(Tree sig)
555 {
556     int    i;
557     double r;
558     Tree   type, name, file, label, c, x, y, z;
559 
560     return isSigInt(sig, &i) || isSigReal(sig, &r) || isSigInput(sig, &i) || isSigFConst(sig, type, name, file) ||
561            isSigButton(sig, label) || isSigCheckbox(sig, label) || isSigVSlider(sig, label, c, x, y, z) ||
562            isSigHSlider(sig, label, c, x, y, z) || isSigNumEntry(sig, label, c, x, y, z);
563 }
564 
generateCacheCode(Tree sig,const string & exp)565 string DocCompiler::generateCacheCode(Tree sig, const string& exp)
566 {
567     // cerr << "!! entering generateCacheCode with sig=\"" << ppsig(sig) << "\"" << endl;
568 
569     string vname, ctype, code, vectorname;
570 
571     int         sharing = getSharingCount(sig);
572     Occurences* o       = fOccMarkup.retrieve(sig);
573 
574     // check reentrance
575     if (getCompiledExpression(sig, code)) {
576         // cerr << "!! generateCacheCode called a true getCompiledExpression" << endl;
577         return code;
578     }
579 
580     // check for expression occuring in delays
581     if (o->getMaxDelay() > 0) {
582         if (getVectorNameProperty(sig, vectorname)) {
583             return exp;
584         }
585         getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
586         gGlobal->gDocNoticeFlagMap["recursigs"] = true;
587         // cerr << "- r : generateCacheCode : vame=\"" << vname << "\", for sig=\"" << ppsig(sig) << "\"" << endl;
588         if (sharing > 1) {
589             // cerr << "      generateCacheCode calls generateDelayVec(generateVariableStore) on vame=\"" << vname <<
590             // "\"" << endl;
591             return generateDelayVec(sig, generateVariableStore(sig, exp), ctype, vname, o->getMaxDelay());
592         } else {
593             // cerr << "      generateCacheCode calls generateDelayVec(exp) on vame=\"" << vname << "\"" << endl;
594             return generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
595         }
596     } else if (sharing == 1 || getVectorNameProperty(sig, vectorname) || isVerySimpleFormula(sig)) {
597         // cerr << "! generateCacheCode : sharing == 1 : return \"" << exp << "\"" << endl;
598         return exp;
599     } else if (sharing > 1) {
600         // cerr << "! generateCacheCode : sharing > 1 : return \"" << exp << "\"" << endl;
601         return generateVariableStore(sig, exp);
602     } else {
603         stringstream error;
604         error << "ERROR in sharing count (" << sharing << ") for " << *sig << endl;
605         throw faustexception(error.str());
606     }
607 
608     return "Error in generateCacheCode";
609 }
610 
generateVariableStore(Tree sig,const string & exp)611 string DocCompiler::generateVariableStore(Tree sig, const string& exp)
612 {
613     string vname, ctype;
614     Type   t = getCertifiedSigType(sig);
615 
616     switch (t->variability()) {
617         case kKonst:
618             getTypedNames(t, "k", ctype, vname);  ///< "k" for constants.
619             fLateq->addConstSigFormula(subst("$0 = $1", vname, exp));
620             gGlobal->gDocNoticeFlagMap["constsigs"] = true;
621             return vname;
622 
623         case kBlock:
624             getTypedNames(t, "p", ctype, vname);  ///< "p" for "parameter".
625             fLateq->addParamSigFormula(subst("$0(t) = $1", vname, exp));
626             gGlobal->gDocNoticeFlagMap["paramsigs"] = true;
627             setVectorNameProperty(sig, vname);
628             return subst("$0(t)", vname);
629 
630         case kSamp:
631             if (getVectorNameProperty(sig, vname)) {
632                 return subst("$0(t)", vname);
633             } else {
634                 getTypedNames(t, "s", ctype, vname);
635                 // cerr << "- generateVariableStore : \"" << subst("$0(t) = $1", vname, exp) << "\"" << endl;
636                 fLateq->addStoreSigFormula(subst("$0(t) = $1", vname, exp));
637                 gGlobal->gDocNoticeFlagMap["storedsigs"] = true;
638                 setVectorNameProperty(sig, vname);
639                 return subst("$0(t)", vname);
640             }
641 
642         default:
643             faustassert(0);
644             return "";
645     }
646 }
647 
648 /*****************************************************************************
649                                     CASTING
650 *****************************************************************************/
651 
generateIntCast(Tree sig,Tree x,int priority)652 string DocCompiler::generateIntCast(Tree sig, Tree x, int priority)
653 {
654     gGlobal->gDocNoticeFlagMap["intcast"] = true;
655 
656     return generateCacheCode(sig, subst("\\mathrm{int}\\left($0\\right)", CS(x, 0)));
657 }
658 
659 /**
660  * @brief Don't generate float cast !
661  *
662  * It is just a kind of redirection.
663  * Calling generateCacheCode ensures to create a new
664  * variable name if the input signal expression is shared.
665  */
generateFloatCast(Tree sig,Tree x,int priority)666 string DocCompiler::generateFloatCast(Tree sig, Tree x, int priority)
667 {
668     return generateCacheCode(sig, subst("$0", CS(x, priority)));
669 }
670 
671 /*****************************************************************************
672                             user interface elements
673 *****************************************************************************/
674 
generateButton(Tree sig,Tree path)675 string DocCompiler::generateButton(Tree sig, Tree path)
676 {
677     string vname   = getFreshID("{u_b}");
678     string varname = vname + "(t)";
679     fLateq->addUISigFormula(getUIDir(path), prepareBinaryUI(varname, path));
680     gGlobal->gDocNoticeFlagMap["buttonsigs"] = true;
681     return generateCacheCode(sig, varname);
682 }
683 
generateCheckbox(Tree sig,Tree path)684 string DocCompiler::generateCheckbox(Tree sig, Tree path)
685 {
686     string vname   = getFreshID("{u_c}");
687     string varname = vname + "(t)";
688     fLateq->addUISigFormula(getUIDir(path), prepareBinaryUI(varname, path));
689     gGlobal->gDocNoticeFlagMap["checkboxsigs"] = true;
690     return generateCacheCode(sig, varname);
691 }
692 
generateVSlider(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)693 string DocCompiler::generateVSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
694 {
695     string varname = getFreshID("{u_s}") + "(t)";
696     fLateq->addUISigFormula(getUIDir(path), prepareIntervallicUI(varname, path, cur, min, max));
697     gGlobal->gDocNoticeFlagMap["slidersigs"] = true;
698     return generateCacheCode(sig, varname);
699 }
700 
generateHSlider(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)701 string DocCompiler::generateHSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
702 {
703     string varname = getFreshID("{u_s}") + "(t)";
704     fLateq->addUISigFormula(getUIDir(path), prepareIntervallicUI(varname, path, cur, min, max));
705     gGlobal->gDocNoticeFlagMap["slidersigs"] = true;
706     return generateCacheCode(sig, varname);
707 }
708 
generateNumEntry(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)709 string DocCompiler::generateNumEntry(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
710 {
711     string varname = getFreshID("{u_n}") + "(t)";
712     fLateq->addUISigFormula(getUIDir(path), prepareIntervallicUI(varname, path, cur, min, max));
713     gGlobal->gDocNoticeFlagMap["nentrysigs"] = true;
714     return generateCacheCode(sig, varname);
715 }
716 
generateVBargraph(Tree sig,Tree path,Tree min,Tree max,const string & exp)717 string DocCompiler::generateVBargraph(Tree sig, Tree path, Tree min, Tree max, const string& exp)
718 {
719     string varname = getFreshID("{u_g}");
720 
721     Type t = getCertifiedSigType(sig);
722     switch (t->variability()) {
723         case kKonst:
724             break;
725 
726         case kBlock:
727             break;
728 
729         case kSamp:
730             break;
731     }
732     return generateCacheCode(sig, varname);
733 }
734 
generateHBargraph(Tree sig,Tree path,Tree min,Tree max,const string & exp)735 string DocCompiler::generateHBargraph(Tree sig, Tree path, Tree min, Tree max, const string& exp)
736 {
737     string varname = getFreshID("{u_g}");
738 
739     Type t = getCertifiedSigType(sig);
740     switch (t->variability()) {
741         case kKonst:
742             break;
743 
744         case kBlock:
745             break;
746 
747         case kSamp:
748             break;
749     }
750     return generateCacheCode(sig, varname);
751 }
752 
generateAttach(Tree sig,Tree x,Tree y,int priority)753 string DocCompiler::generateAttach(Tree sig, Tree x, Tree y, int priority)
754 {
755     string vname;
756     string exp;
757 
758     CS(y, priority);
759     exp = CS(x, priority);
760 
761     if (getVectorNameProperty(x, vname)) {
762         setVectorNameProperty(sig, vname);
763     }
764 
765     return generateCacheCode(sig, exp);
766 }
767 
768 /*****************************************************************************
769                                     TABLES
770  (note : tables here are siplified versions different from the ones used to
771   generate c++ code)
772 *****************************************************************************/
773 
774 /**
775  * Generate the equation of a constant table (its content is time constant).
776  * Returns the name of the table
777  */
generateDocConstantTbl(Tree,Tree size,Tree isig)778 string DocCompiler::generateDocConstantTbl(Tree /*tbl*/, Tree size, Tree isig)
779 {
780     string vname, ctype;
781     string init = CS(isig, 0);
782 
783     int n;
784     if (!isSigInt(size, &n)) {
785         cerr << "error in DocCompiler::generateDocConstantTbl() : " << *size
786              << " is not an integer expression and can't be used as a table size' " << endl;
787     }
788 
789     // allocate a name v_i for the table
790     getTypedNames(getCertifiedSigType(isig), "v", ctype, vname);
791 
792     // add a comment on tables in the notice
793     gGlobal->gDocNoticeFlagMap["tablesigs"] = true;
794 
795     // add equation v[t] = isig(t)
796     fLateq->addRDTblSigFormula(subst("$0[t] = $1 \\condition{when $$t \\in [0,$2]$$} ", vname, init, T(n - 1)));
797 
798     // note that the name of the table can never be used outside an sigDocTableAccess
799     return vname;
800 }
801 
802 /**
803  * tests if a charactere is a word separator
804  */
isSeparator(char c)805 static bool isSeparator(char c)
806 {
807     bool w = (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')));
808     return !w;
809 }
810 
811 /**
812  * Replaces the occurences of 't' in a formula with another character
813  */
replaceTimeBy(const string & src,char r)814 static string replaceTimeBy(const string& src, char r)
815 {
816     string dst;
817     char   pre = 0;
818     for (size_t i = 0; i < src.size(); i++) {
819         char x = src[i];
820         if ((x == 't') && isSeparator(pre) && ((i == src.size() - 1) || isSeparator(src[i + 1]))) {
821             dst.push_back(r);
822         } else {
823             dst.push_back(x);
824         }
825         pre = x;
826     }
827     return dst;
828 }
829 
830 /**
831  * Generate the equation of a write table, which content is time dependent.
832  * It is basically a signal of vectors.
833  */
generateDocWriteTbl(Tree,Tree size,Tree isig,Tree widx,Tree wsig)834 string DocCompiler::generateDocWriteTbl(Tree /*tbl*/, Tree size, Tree isig, Tree widx, Tree wsig)
835 {
836     string vname, ctype;
837     string init = CS(isig, 0);
838     int    n;
839     if (!isSigInt(size, &n)) {
840         cerr << "error in DocCompiler::generateDocWriteTbl() : " << *size
841              << " is not an integer expression and can't be used as a table size' " << endl;
842     }
843 
844     // allocate a name w_i for the table
845     getTypedNames(getCertifiedSigType(isig), "w", ctype, vname);
846 
847     // add a comment on tables in the notice
848     gGlobal->gDocNoticeFlagMap["tablesigs"] = true;
849 
850     // describe the table equation
851     string ltqRWTableDef;
852     ltqRWTableDef += subst("$0(t)[i] = \n", vname);
853     ltqRWTableDef += "\\left\\{\\begin{array}{ll}\n";
854     ltqRWTableDef += subst("$0 & \\mbox{if \\,} t < 0 \\mbox{\\, and \\,}  i \\in [0,$1] \\\\\n",
855                            replaceTimeBy(init, 'i'), T(n - 1));
856     ltqRWTableDef += subst("$0 & \\mbox{if \\,} i = $1 \\\\\n", CS(wsig, 0), CS(widx, 0));
857     ltqRWTableDef += subst("$0(t\\!-\\!1)[i] & \\mbox{otherwise} \\\\\n", vname);
858     ltqRWTableDef += "\\end{array}\\right.";
859 
860     // add the table equation
861     fLateq->addRWTblSigFormula(ltqRWTableDef);  // w(t) = initsig(t)
862 
863     // note that the name of the table can never be used outside an sigDocTableAccess
864     return vname;
865 }
866 
867 /**
868  * Generate the equation of a write table, which content is time dependent.
869  * It is basically a signal of vectors.
870  */
generateDocAccessTbl(Tree sig,Tree tbl,Tree ridx)871 string DocCompiler::generateDocAccessTbl(Tree sig, Tree tbl, Tree ridx)
872 {
873     // the compilation of a table always returns its name
874     string vname  = CS(tbl, 0);
875     string result = subst("$0[$1]", vname, CS(ridx, 0));
876 
877     return generateCacheCode(sig, result);
878 }
879 
isShortEnough(string & s,unsigned int max)880 bool DocCompiler::isShortEnough(string& s, unsigned int max)
881 {
882     return (s.length() <= max);
883 }
884 
885 /*****************************************************************************
886                                RECURSIONS
887 *****************************************************************************/
888 
889 /**
890  * Generate code for a projection of a group of mutually recursive definitions
891  */
generateRecProj(Tree sig,Tree r,int i,int priority)892 string DocCompiler::generateRecProj(Tree sig, Tree r, int i, int priority)
893 {
894     string vname;
895     Tree   var, le;
896 
897     // cerr << "*** generateRecProj sig : \"" << ppsig(sig) << "\"" << endl;
898 
899     if (!getVectorNameProperty(sig, vname)) {
900         faustassert(isRec(r, var, le));
901         // cerr << "    generateRecProj has NOT YET a vname : " << endl;
902         // cerr << "--> generateRecProj calls generateRec on \"" << ppsig(sig) << "\"" << endl;
903         generateRec(r, var, le, priority);
904         faustassert(getVectorNameProperty(sig, vname));
905         // cerr << "<-- generateRecProj vname : \"" << subst("$0(t)", vname) << "\"" << endl;
906     } else {
907         // cerr << "(generateRecProj has already a vname : \"" << subst("$0(t)", vname) << "\")" << endl;
908     }
909     return subst("$0(t)", vname);
910 }
911 
912 /**
913  * Generate code for a group of mutually recursive definitions
914  */
generateRec(Tree sig,Tree var,Tree le,int priority)915 void DocCompiler::generateRec(Tree sig, Tree var, Tree le, int priority)
916 {
917     int N = len(le);
918 
919     vector<bool>   used(N);
920     vector<int>    delay(N);
921     vector<string> vname(N);
922     vector<string> ctype(N);
923 
924     // prepare each element of a recursive definition
925     for (int i = 0; i < N; i++) {
926         Tree e = sigProj(i, sig);  // recreate each recursive definition
927         if (fOccMarkup.retrieve(e)) {
928             // this projection is used
929             used[i] = true;
930             // cerr << "generateRec : used[" << i << "] = true" << endl;
931             getTypedNames(getCertifiedSigType(e), "r", ctype[i], vname[i]);
932             gGlobal->gDocNoticeFlagMap["recursigs"] = true;
933             // cerr << "- r : generateRec setVectorNameProperty : \"" << vname[i] << "\"" << endl;
934             setVectorNameProperty(e, vname[i]);
935             delay[i] = fOccMarkup.retrieve(e)->getMaxDelay();
936         } else {
937             // this projection is not used therefore
938             // we should not generate code for it
939             used[i] = false;
940             // cerr << "generateRec : used[" << i << "] = false" << endl;
941         }
942     }
943 
944     // generate delayline for each element of a recursive definition
945     for (int i = 0; i < N; i++) {
946         if (used[i]) {
947             generateDelayLine(ctype[i], vname[i], delay[i], CS(nth(le, i), priority));
948         }
949     }
950 }
951 
952 /*****************************************************************************
953                                PREFIX, DELAY A PREFIX VALUE
954 *****************************************************************************/
955 
956 /**
957  * Generate LaTeX code for "prefix", a 1­sample-delay explicitely initialized.
958  *
959  * @param	sig			The signal expression to treat.
960  * @param	x			The initial value for the delay line.
961  * @param	e			The value for the delay line, after initialization.
962  * @param	priority	The priority of the environment of the expression.
963  *
964  * @return	<string>	The LaTeX code translation of the signal, cached.
965  */
generatePrefix(Tree sig,Tree x,Tree e,int priority)966 string DocCompiler::generatePrefix(Tree sig, Tree x, Tree e, int priority)
967 {
968     string var  = getFreshID("m");
969     string exp0 = CS(x, priority);
970     string exp1 = CS(e, priority);  // ensure exp1 is compiled to have a vector name
971     string vecname;
972 
973     if (!getVectorNameProperty(e, vecname)) {
974         cerr << "No vector name for : " << ppsig(e) << endl;
975         faustassert(0);
976     }
977 
978     string ltqPrefixDef;
979     ltqPrefixDef += subst("$0(t) = \n", var);
980     ltqPrefixDef += "\\left\\{\\begin{array}{ll}\n";
981     ltqPrefixDef += subst("$0 & \\mbox{, when \\,} t = 0\\\\\n", exp0);
982     ltqPrefixDef += subst("$0 & \\mbox{, when \\,} t > 0\n", subst("$0(t\\!-\\!1)", vecname));
983     ltqPrefixDef += "\\end{array}\\right.";
984 
985     fLateq->addPrefixSigFormula(ltqPrefixDef);
986     gGlobal->gDocNoticeFlagMap["prefixsigs"] = true;
987 
988     return generateCacheCode(sig, subst("$0(t)", var));
989 }
990 
991 /*****************************************************************************
992                                IOTA(n)
993 *****************************************************************************/
994 
995 /**
996  * Generate a "iota" time function, n-cyclical.
997  */
generateIota(Tree sig,Tree n)998 string DocCompiler::generateIota(Tree sig, Tree n)
999 {
1000     int size;
1001     if (!isSigInt(n, &size)) {
1002         throw faustexception("ERROR in generateIota\n");
1003     }
1004     // cout << "iota !" << endl;
1005     return subst(" t \\bmod{$0} ", docT(size));
1006 }
1007 
1008 // a revoir en utilisant la lecture de table et en partageant la construction de la paire de valeurs
1009 
1010 /**
1011  * Generate a select2 code
1012  */
generateSelect2(Tree sig,Tree sel,Tree s1,Tree s2,int priority)1013 string DocCompiler::generateSelect2(Tree sig, Tree sel, Tree s1, Tree s2, int priority)
1014 {
1015     string var    = getFreshID("q");
1016     string expsel = CS(sel, 0);
1017     string exps1  = CS(s1, 0);
1018     string exps2  = CS(s2, 0);
1019 
1020     string ltqSelDef;
1021     ltqSelDef += subst("$0(t) = \n", var);
1022     ltqSelDef += "\\left\\{\\begin{array}{ll}\n";
1023     ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 0\\\\\n", exps1, expsel);
1024     ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 1\n", exps2, expsel);
1025     ltqSelDef += "\\end{array}\\right.";
1026 
1027     fLateq->addSelectSigFormula(ltqSelDef);
1028     gGlobal->gDocNoticeFlagMap["selectionsigs"] = true;
1029 
1030     // return generateCacheCode(sig, subst("$0(t)", var));
1031     setVectorNameProperty(sig, var);
1032     return subst("$0(t)", var);
1033 }
1034 
1035 /**
1036  * Generate a select3 code
1037  */
generateSelect3(Tree sig,Tree sel,Tree s1,Tree s2,Tree s3,int priority)1038 string DocCompiler::generateSelect3(Tree sig, Tree sel, Tree s1, Tree s2, Tree s3, int priority)
1039 {
1040     string var    = getFreshID("q");
1041     string expsel = CS(sel, 0);
1042     string exps1  = CS(s1, 0);
1043     string exps2  = CS(s2, 0);
1044     string exps3  = CS(s3, 0);
1045 
1046     string ltqSelDef;
1047     ltqSelDef += subst("$0(t) = \n", var);
1048     ltqSelDef += "\\left\\{\\begin{array}{ll}\n";
1049     ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 0\\\\\n", generateVariableStore(s1, exps1), expsel);
1050     ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 1\\\\\n", generateVariableStore(s2, exps2), expsel);
1051     ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 2\n", generateVariableStore(s3, exps3), expsel);
1052     ltqSelDef += "\\end{array}\\right.";
1053 
1054     fLateq->addSelectSigFormula(ltqSelDef);
1055     gGlobal->gDocNoticeFlagMap["selectionsigs"] = true;
1056 
1057     // return generateCacheCode(sig, subst("$0(t)", var));
1058     setVectorNameProperty(sig, var);
1059     return subst("$0(t)", var);
1060 }
1061 
1062 /**
1063  * retrieve the type annotation of sig
1064  * @param sig the signal we want to know the type
1065  */
generateXtended(Tree sig,int priority)1066 string DocCompiler::generateXtended(Tree sig, int priority)
1067 {
1068     xtended*       p = (xtended*)getUserData(sig);
1069     vector<string> args;
1070     vector<Type>   types;
1071 
1072     for (int i = 0; i < sig->arity(); i++) {
1073         args.push_back(CS(sig->branch(i), 0));
1074         types.push_back(getCertifiedSigType(sig->branch(i)));
1075     }
1076 
1077     if (p->needCache()) {
1078         // cerr << "!! generateXtended : <needCache> : calls generateCacheCode(sig, p->generateLateq(fLateq, args,
1079         // types))" << endl;
1080         return generateCacheCode(sig, p->generateLateq(fLateq, args, types));
1081     } else {
1082         // cerr << "!! generateXtended : <do not needCache> : calls p->generateLateq(fLateq, args, types)" << endl;
1083         return p->generateLateq(fLateq, args, types);
1084     }
1085 }
1086 
1087 //------------------------------------------------------------------------------------------------
1088 
1089 /*****************************************************************************
1090                         vector name property
1091 *****************************************************************************/
1092 
1093 /**
1094  * Set the vector name property of a signal, the name of the vector used to
1095  * store the previous values of the signal to implement a delay.
1096  * @param sig the signal expression.
1097  * @param vecname the string representing the vector name.
1098  * @return true is already compiled
1099  */
setVectorNameProperty(Tree sig,const string & vecname)1100 void DocCompiler::setVectorNameProperty(Tree sig, const string& vecname)
1101 {
1102     fVectorProperty.set(sig, vecname);
1103 }
1104 
1105 /**
1106  * Get the vector name property of a signal, the name of the vector used to
1107  * store the previous values of the signal to implement a delay.
1108  * @param sig the signal expression.
1109  * @param vecname the string where to store the vector name.
1110  * @return true if the signal has this property, false otherwise
1111  */
1112 
getVectorNameProperty(Tree sig,string & vecname)1113 bool DocCompiler::getVectorNameProperty(Tree sig, string& vecname)
1114 {
1115     return fVectorProperty.get(sig, vecname);
1116 }
1117 
1118 /*****************************************************************************
1119                                N-SAMPLE FIXED DELAY : sig = exp@delay
1120 
1121     case 1-sample max delay :
1122         Y(t-0)	Y(t-1)
1123         Temp	Var						gGlobal->gLessTempSwitch = false
1124         V[0]	V[1]					gGlobal->gLessTempSwitch = true
1125 
1126     case max delay < gGlobal->gMaxCopyDelay :
1127         Y(t-0)	Y(t-1)	Y(t-2)  ...
1128         Temp	V[0]	V[1]	...		gGlobal->gLessTempSwitch = false
1129         V[0]	V[1]	V[2]	...		gGlobal->gLessTempSwitch = true
1130 
1131     case max delay >= gGlobal->gMaxCopyDelay :
1132         Y(t-0)	Y(t-1)	Y(t-2)  ...
1133         Temp	V[0]	V[1]	...
1134         V[0]	V[1]	V[2]	...
1135 
1136 *****************************************************************************/
1137 
1138 /**
1139  * Generate code for accessing a delayed signal. The generated code depend of
1140  * the maximum delay attached to exp and the gGlobal->gLessTempSwitch.
1141  *
1142  * @todo Priorités à revoir pour le parenthésage (associativité de - et /),
1143  * avec gBinOpLateqTable dans binop.cpp.
1144  */
generateDelay(Tree sig,Tree exp,Tree delay,int priority)1145 string DocCompiler::generateDelay(Tree sig, Tree exp, Tree delay, int priority)
1146 {
1147     int    d;
1148     string vecname;
1149 
1150     CS(exp, 0);  // ensure exp is compiled to have a vector name
1151 
1152     if (!getVectorNameProperty(exp, vecname)) {
1153         cerr << "No vector name for : " << ppsig(exp) << endl;
1154         faustassert(0);
1155     }
1156 
1157     if (isSigInt(delay, &d) && (d == 0)) {
1158         // cerr << "@ generateDelay : d = " << d << endl;
1159         return subst("$0(t)", vecname);
1160     } else {
1161         // cerr << "@ generateDelay : d = " << d << endl;
1162         return subst("$0(t\\!-\\!$1)", vecname, CS(delay, 7));
1163     }
1164 }
1165 
1166 /**
1167  * Generate code for the delay mecchanism. The generated code depend of the
1168  * maximum delay attached to exp and the "less temporaries" switch
1169  */
generateDelayVec(Tree sig,const string & exp,const string & ctype,const string & vname,int mxd)1170 string DocCompiler::generateDelayVec(Tree sig, const string& exp, const string& ctype, const string& vname, int mxd)
1171 {
1172     string s = generateDelayVecNoTemp(sig, exp, ctype, vname, mxd);
1173     if (getCertifiedSigType(sig)->variability() < kSamp) {
1174         return exp;
1175     } else {
1176         return s;
1177     }
1178 }
1179 
1180 /**
1181  * Generate code for the delay mecchanism without using temporary variables
1182  */
generateDelayVecNoTemp(Tree sig,const string & exp,const string & ctype,const string & vname,int mxd)1183 string DocCompiler::generateDelayVecNoTemp(Tree sig, const string& exp, const string& ctype, const string& vname,
1184                                            int mxd)
1185 {
1186     faustassert(mxd > 0);
1187     // cerr << "  entering generateDelayVecNoTemp" << endl;
1188     string vectorname;
1189 
1190     // if generateVariableStore has already tagged sig, no definition is needed.
1191     if (getVectorNameProperty(sig, vectorname)) {
1192         return subst("$0(t)", vectorname);
1193     } else {
1194         fLateq->addRecurSigFormula(subst("$0(t) = $1", vname, exp));
1195         setVectorNameProperty(sig, vname);
1196         return subst("$0(t)", vname);
1197     }
1198 }
1199 
1200 /**
1201  * Generate code for the delay mecchanism without using temporary variables
1202  */
generateDelayLine(const string & ctype,const string & vname,int mxd,const string & exp)1203 void DocCompiler::generateDelayLine(const string& ctype, const string& vname, int mxd, const string& exp)
1204 {
1205     // faustassert(mxd > 0);
1206     if (mxd == 0) {
1207         fLateq->addRecurSigFormula(subst("$0(t) = $1", vname, exp));
1208     } else {
1209         fLateq->addRecurSigFormula(subst("$0(t) = $1", vname, exp));
1210     }
1211 }
1212 
1213 /****************************************************************
1214             User interface element utilities.
1215  *****************************************************************/
1216 
1217 /**
1218  * @brief Get the directory of a user interface element.
1219  *
1220  * Convert the input reversed path tree into a string.
1221  * The name of the UI is stripped (the head of the path tree),
1222  * the rest of the tree is a list of pointed pairs, where the names
1223  * are contained by the tail of these pointed pairs.
1224  * Metadatas (begining by '[') are stripped.
1225  *
1226  * @param[in]	pathname	The path tree to convert.
1227  * @return		<string>	A directory-like string.
1228  */
getUIDir(Tree pathname)1229 string DocCompiler::getUIDir(Tree pathname)
1230 {
1231     // cerr << "Documentator : getUIDir : print(pathname, stdout) = "; print(pathname, stdout); cerr << endl;
1232     string s;
1233     Tree   dir = reverse(tl(pathname));
1234     while (!isNil(dir)) {
1235         string tmp = tree2str(tl(hd(dir)));
1236         if ((tmp[0] != '[') && (!tmp.empty())) {
1237             s += tmp + '/';
1238         }
1239         dir = tl(dir);
1240     }
1241     return s;
1242 }
1243 
1244 /**
1245  * @brief Prepare binary user interface elements (button, checkbox).
1246  *
1247  * - Format a LaTeX output string as a supertabular row with 3 columns :
1248  * "\begin{supertabular}{lll}". @see Lateq::printHierarchy
1249  * - The UI range is only a set of two values : {0, 1}.
1250  * - The UI current value is automatically 0.
1251  *
1252  * @param[in]	name		The LaTeX name of the UI signal (eg. "{u_b}_{i}(t)").
1253  * @param[in]	path		The path tree to parse.
1254  * @return		<string>	The LaTeX output string.
1255  */
prepareBinaryUI(const string & name,Tree path)1256 string DocCompiler::prepareBinaryUI(const string& name, Tree path)
1257 {
1258     string label, unit;
1259     getUIDocInfos(path, label, unit);
1260     string s = "";
1261     label    = (label.size() > 0) ? ("\\textsf{\"" + label + "\"} ") : "";
1262     unit     = (unit.size() > 0) ? ("\\ (" + unit + ")") : "";
1263     s += label + unit;
1264     s += " & $" + name + "$";
1265     s += " $\\in$ $\\left\\{\\,0, 1\\,\\right\\}$";
1266     s += " & $(\\mbox{" + gGlobal->gDocMathStringMap["defaultvalue"] + "} = 0)$\\\\";
1267     return s;
1268 }
1269 
1270 /**
1271  * @brief Prepare "intervallic" user interface elements (sliders, nentry).
1272  *
1273  * - Format a LaTeX output string as a supertabular row with 3 columns :
1274  * "\begin{supertabular}{lll}". @see Lateq::printHierarchy
1275  * - The UI range is an bounded interval : [tmin, tmax].
1276  * - The UI current value is tcur.
1277  *
1278  * @param[in]	name		The LaTeX name of the UI signal (eg. "{u_s}_{i}(t)").
1279  * @param[in]	path		The path tree to parse.
1280  * @param[in]	tcur		The current UI value tree to convert.
1281  * @param[in]	tmin		The minimum UI value tree to convert.
1282  * @param[in]	tmax		The maximum UI value tree to convert.
1283  * @return		<string>	The LaTeX output string.
1284  */
prepareIntervallicUI(const string & name,Tree path,Tree tcur,Tree tmin,Tree tmax)1285 string DocCompiler::prepareIntervallicUI(const string& name, Tree path, Tree tcur, Tree tmin, Tree tmax)
1286 {
1287     string label, unit, cur, min, max;
1288     getUIDocInfos(path, label, unit);
1289     cur = docT(tree2float(tcur));
1290     min = docT(tree2float(tmin));
1291     max = docT(tree2float(tmax));
1292 
1293     string s = "";
1294     label    = (label.size() > 0) ? ("\\textsf{\"" + label + "\"} ") : "";
1295     unit     = (unit.size() > 0) ? ("\\ (" + unit + ")") : "";
1296     s += label + unit;
1297     s += " & $" + name + "$";
1298     s += " $\\in$ $\\left[\\," + min + ", " + max + "\\,\\right]$";
1299     s += " & $(\\mbox{" + gGlobal->gDocMathStringMap["defaultvalue"] + "} = " + cur + ")$\\\\";
1300     return s;
1301 }
1302 
1303 /**
1304  * Get information on a user interface element for documentation.
1305  *
1306  * @param[in]	path	The UI full pathname to parse.
1307  * @param[out]	label	The place to store the UI name.
1308  * @param[out]	unit	The place to store the UI unit.
1309  */
getUIDocInfos(Tree path,string & label,string & unit)1310 void DocCompiler::getUIDocInfos(Tree path, string& label, string& unit)
1311 {
1312     label = "";
1313     unit  = "";
1314 
1315     map<string, set<string> > metadata;
1316     extractMetadata(tree2str(hd(path)), label, metadata);
1317 
1318     set<string> myunits = metadata["unit"];
1319     //	for (set<string>::iterator i = myunits.begin(); i != myunits.end(); i++) {
1320     //		cerr << "Documentator : getUIDocInfos : metadata[\"unit\"] = " << *i << endl;
1321     //	}
1322     for (map<string, set<string> >::iterator i = metadata.begin(); i != metadata.end(); i++) {
1323         const string&      key    = i->first;
1324         const set<string>& values = i->second;
1325         for (set<string>::const_iterator j = values.begin(); j != values.end(); j++) {
1326             if (key == "unit") unit += *j;
1327         }
1328     }
1329 }
1330