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 1sample-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