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