1 /************************************************************************
2 ************************************************************************
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.
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.
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 ************************************************************************/
22 /*****************************************************************************
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 *****************************************************************************/
38 #include "doc_compile.hh"
41 #include "sigprint.hh"
42 #include "sigtyperules.hh"
43 #include "recursivness.hh"
44 #include "simplify.hh"
45 #include "privatise.hh"
48 #include "compatibility.hh"
53 #include "doc_notice.hh"
56 extern bool gLessTempSwitch
;
57 extern int gMaxCopyDelay
;
58 extern map
<string
, string
> gDocMathStringMap
;
60 extern bool getSigListNickName(Tree t
, Tree
& id
);
62 static const unsigned int MAX_RIGHT_MEMBER
= 20;
63 static const unsigned int MAX_SUB_EXPR
= 10;
66 /*****************************************************************************
68 *****************************************************************************/
70 map
<string
, int> DocCompiler::fIDCounters
;
72 string
DocCompiler::getFreshID(const string
& prefix
)
74 if (fIDCounters
.find(prefix
) == fIDCounters
.end()) {
75 fIDCounters
[prefix
] = 1;
77 int n
= fIDCounters
[prefix
];
78 fIDCounters
[prefix
] = n
+1;
80 return subst("$0_{$1}", prefix
, docT(n
));
84 /*****************************************************************************
86 *****************************************************************************/
88 Tree
DocCompiler::annotate(Tree LS
)
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
98 /*****************************************************************************
100 *****************************************************************************/
102 Lateq
* DocCompiler::compileLateq (Tree L
, Lateq
* compiledEqn
)
104 //cerr << "Documentator : compileLateq : L = "; printSignal(L, stdout, 0); cerr << endl;
106 fLateq
= compiledEqn
; ///< Dynamic field !
109 for (int i
= 0; isList(L
); L
= tl(L
), i
++) {
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
)));
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;
121 fLateq
->addOutputSigFormula(subst("$0(t) = $1", getFreshID("y"), CS(sig
, priority
)));
122 gDocNoticeFlagMap
["outputsigs"] = true;
131 /*****************************************************************************
132 CS : compile a signal
133 *****************************************************************************/
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
141 bool DocCompiler::getCompiledExpression(Tree sig
, string
& cexp
)
143 return fCompileProperty
.get(sig
, cexp
);
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)
153 string
DocCompiler::setCompiledExpression(Tree sig
, const string
& cexp
)
155 fCompileProperty
.set(sig
, cexp
);
162 * @param sig the signal expression to compile.
163 * @return the C code translation of sig as a string
165 string
DocCompiler::CS (Tree sig
, int priority
)
169 if (!getCompiledExpression(sig
, code
)) { // not compiled yet.
170 code
= generateCode(sig
, priority
);
171 setCompiledExpression(sig
, code
);
178 /*****************************************************************************
179 generateCode : dispatch according to signal
180 *****************************************************************************/
184 * @brief Main code generator dispatch.
186 * According to the type of the input signal, generateCode calls
187 * the appropriate generator with appropriate arguments.
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.
193 string
DocCompiler::generateCode (Tree sig
, int priority
)
197 Tree c
, sel
, x
, y
, z
, u
, label
, ff
, largs
, type
, name
, file
;
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
)); }
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
); }
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
)); }
214 // new special tables for documentation purposes
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
); }
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
); }
224 else if ( isProj(sig
, &i
, x
) ) { printGCCall(sig
,"generateRecProj"); return generateRecProj (sig
, x
, i
, priority
); }
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
); }
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
); }
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
); }
240 cerr
<< "Error in d signal, unrecognized signal : " << *sig
<< endl
;
244 return "error in generate code";
249 * Print calling information of generateCode, for debug purposes.
252 * To turn printing on, turn the 'printCalls' boolean to true.
254 void DocCompiler::printGCCall(Tree sig
, const string
& calledFunction
)
256 bool printCalls
= false;
257 bool maskSigs
= false;
260 cerr
<< " -> generateCode calls " << calledFunction
;
264 cerr
<< " on " << ppsig(sig
) << endl
;
270 /*****************************************************************************
272 *****************************************************************************/
275 string
DocCompiler::generateNumber (Tree sig
, const string
& exp
)
278 Occurences
* o
= fOccMarkup
.retrieve(sig
);
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());
290 /*****************************************************************************
292 *****************************************************************************/
295 string
DocCompiler::generateFConst (Tree sig
, const string
& file
, const string
& exp
)
298 Occurences
* o
= fOccMarkup
.retrieve(sig
);
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());
307 if (exp
== "fSamplingFreq") {
308 //gDocNoticeFlagMap["fsamp"] = true;
312 return "\\mathrm{"+exp
+"}";
315 /*****************************************************************************
317 *****************************************************************************/
320 string
DocCompiler::generateFVar (Tree sig
, const string
& file
, const string
& exp
)
323 Occurences
* o
= fOccMarkup
.retrieve(sig
);
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());
332 return generateCacheCode(sig
, exp
);
336 /*****************************************************************************
338 *****************************************************************************/
341 string
DocCompiler::generateInput (Tree sig
, const string
& idx
)
343 if (fLateq
->inputs() == 1) {
344 setVectorNameProperty(sig
, "x");
345 fLateq
->addInputSigFormula("x(t)");
346 gDocNoticeFlagMap
["inputsig"] = true;
347 return generateCacheCode(sig
, "x(t)");
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
));
357 /** Unused for the moment ! */
358 string
DocCompiler::generateOutput (Tree sig
, const string
& idx
, const string
& arg
)
362 if (fLateq
->outputs() == 1) {
363 dst
= subst("y(t)", idx
);
364 gDocNoticeFlagMap
["outputsig"] = true;
366 dst
= subst("y_{$0}(t)", idx
);
367 gDocNoticeFlagMap
["outputsigs"] = true;
370 fLateq
->addOutputSigFormula(subst("$0 = $1", dst
, arg
));
375 /*****************************************************************************
377 *****************************************************************************/
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.
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.
392 * @return <string> The LaTeX code translation of the signal, cached.
394 * @remark The case of LaTeX frac{}{} is special.
396 * @todo Handle integer arithmetics, by testing arguments type,
397 * and printing dedicated operators (\oplus, \odot, \ominus, \oslash).
400 /// associative operations are + * | & xor
401 static bool associative (int opcode
) {
402 return (opcode
== kAdd
) || (opcode
== kMul
) || (opcode
== kAND
) || (opcode
== kOR
) || (opcode
== kXOR
);
405 string
DocCompiler::generateBinOp(Tree sig
, int opcode
, Tree arg1
, Tree arg2
, int priority
)
408 int thisPriority
= gBinOpLateqTable
[opcode
]->fPriority
;
410 /* Priority parenthesis handling. */
413 if ( (thisPriority
< priority
) || ((thisPriority
== priority
) && !associative(opcode
)) ) {
414 // (a+b)*c or (a/b)/c need parenthesis
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;
428 op
= gBinOpLateqTable
[opcode
]->fName
;
433 gDocNoticeFlagMap
["intplus"] = true;
437 gDocNoticeFlagMap
["intminus"] = true;
441 gDocNoticeFlagMap
["intmult"] = true;
445 gDocNoticeFlagMap
["intdiv"] = true;
446 gDocNoticeFlagMap
["intcast"] = true; // "$normalize(int(i/j))$" in the notice.
449 op
= gBinOpLateqTable
[opcode
]->fName
;
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
);
458 s
= subst("$0$1 $2 $3$4", lpar
, CS(arg1
, thisPriority
), op
, CS(arg2
, thisPriority
), rpar
);
461 // if (opcode == kMul) {
462 // gDocNoticeFlagMap["cdot"] = true;
465 return generateCacheCode(sig
, s
);
469 /*****************************************************************************
471 *****************************************************************************/
473 string
DocCompiler::generateFFun(Tree sig
, Tree ff
, Tree largs
, int priority
)
475 string code
= ffname(ff
);
478 for (int i
= 0; i
< ffarity(ff
); i
++) {
480 code
+= CS(nth(largs
, i
), priority
);
485 gDocNoticeFlagMap
["foreignfun"] = true;
487 return "\\mathrm{ff"+code
+"}";
491 /*****************************************************************************
493 *****************************************************************************/
495 void DocCompiler::getTypedNames(Type t
, const string
& prefix
, string
& ctype
, string
& vname
)
497 if (t
->nature() == kInt
) {
498 ctype
= "int"; vname
= subst("$0", getFreshID(prefix
));
500 ctype
= ifloat(); vname
= subst("$0", getFreshID(prefix
));
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
511 static bool isVerySimpleFormula(Tree sig
)
515 Tree type
, name
, file
, label
, c
, x
, y
, z
;
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
)
530 string
DocCompiler::generateCacheCode(Tree sig
, const string
& exp
)
532 //cerr << "!! entering generateCacheCode with sig=\"" << ppsig(sig) << "\"" << endl;
534 string vname
, ctype
, code
, vectorname
;
536 int sharing
= getSharingCount(sig
);
537 Occurences
* o
= fOccMarkup
.retrieve(sig
);
540 if (getCompiledExpression(sig
, code
)) {
541 //cerr << "!! generateCacheCode called a true getCompiledExpression" << endl;
545 // check for expression occuring in delays
546 if (o
->getMaxDelay()>0) {
547 if (getVectorNameProperty(sig
, vectorname
)) {
550 getTypedNames(getCertifiedSigType(sig
), "r", ctype
, vname
);
551 gDocNoticeFlagMap
["recursigs"] = true;
552 //cerr << "- r : generateCacheCode : vame=\"" << vname << "\", for sig=\"" << ppsig(sig) << "\"" << endl;
554 //cerr << " generateCacheCode calls generateDelayVec(generateVariableStore) on vame=\"" << vname << "\"" << endl;
555 return generateDelayVec(sig
, generateVariableStore(sig
,exp
), ctype
, vname
, o
->getMaxDelay());
557 //cerr << " generateCacheCode calls generateDelayVec(exp) on vame=\"" << vname << "\"" << endl;
558 return generateDelayVec(sig
, exp
, ctype
, vname
, o
->getMaxDelay());
561 else if (sharing
== 1 || getVectorNameProperty(sig
, vectorname
) || isVerySimpleFormula(sig
)) {
562 //cerr << "! generateCacheCode : sharing == 1 : return \"" << exp << "\"" << endl;
565 else if (sharing
> 1) {
566 //cerr << "! generateCacheCode : sharing > 1 : return \"" << exp << "\"" << endl;
567 return generateVariableStore(sig
, exp
);
570 cerr
<< "Error in sharing count (" << sharing
<< ") for " << *sig
<< endl
;
574 return "Error in generateCacheCode";
578 string
DocCompiler::generateVariableStore(Tree sig
, const string
& exp
)
581 Type t
= getCertifiedSigType(sig
);
583 switch (t
->variability()) {
586 getTypedNames(t
, "k", ctype
, vname
); ///< "k" for constants.
587 fLateq
->addConstSigFormula(subst("$0 = $1", vname
, exp
));
588 gDocNoticeFlagMap
["constsigs"] = true;
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
);
599 if(getVectorNameProperty(sig
, vname
)) {
600 return subst("$0(t)", vname
);
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
);
617 /*****************************************************************************
619 *****************************************************************************/
622 string
DocCompiler::generateIntCast(Tree sig
, Tree x
, int priority
)
624 gDocNoticeFlagMap
["intcast"] = true;
626 return generateCacheCode(sig
, subst("\\mathrm{int}\\left($0\\right)", CS(x
, 0)));
631 * @brief Don't generate float cast !
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.
637 string
DocCompiler::generateFloatCast (Tree sig
, Tree x
, int priority
)
639 return generateCacheCode(sig
, subst("$0", CS(x
, priority
)));
643 /*****************************************************************************
644 user interface elements
645 *****************************************************************************/
647 string
DocCompiler::generateButton(Tree sig
, Tree path
)
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
);
656 string
DocCompiler::generateCheckbox(Tree sig
, Tree path
)
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
);
665 string
DocCompiler::generateVSlider(Tree sig
, Tree path
, Tree cur
, Tree min
, Tree max
, Tree step
)
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
);
673 string
DocCompiler::generateHSlider(Tree sig
, Tree path
, Tree cur
, Tree min
, Tree max
, Tree step
)
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
);
681 string
DocCompiler::generateNumEntry(Tree sig
, Tree path
, Tree cur
, Tree min
, Tree max
, Tree step
)
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
);
690 string
DocCompiler::generateVBargraph(Tree sig
, Tree path
, Tree min
, Tree max
, const string
& exp
)
692 string varname
= getFreshID("{u_g}");
694 Type t
= getCertifiedSigType(sig
);
695 switch (t
->variability()) {
706 return generateCacheCode(sig
, varname
);
710 string
DocCompiler::generateHBargraph(Tree sig
, Tree path
, Tree min
, Tree max
, const string
& exp
)
712 string varname
= getFreshID("{u_g}");
714 Type t
= getCertifiedSigType(sig
);
715 switch (t
->variability()) {
726 return generateCacheCode(sig
, varname
);
730 string
DocCompiler::generateAttach (Tree sig
, Tree x
, Tree y
, int priority
)
736 exp
= CS(x
, priority
);
738 if(getVectorNameProperty(x
, vname
)) {
739 setVectorNameProperty(sig
, vname
);
742 return generateCacheCode(sig
, exp
);
748 /*****************************************************************************
750 (note : tables here are siplified versions different from the ones used to
752 *****************************************************************************/
755 * Generate the equation of a constant table (its content is time constant).
756 * Returns the name of the table
758 string
DocCompiler::generateDocConstantTbl (Tree
/*tbl*/, Tree size
, Tree isig
)
761 string init
= CS(isig
,0);
764 if (!isSigInt(size
, &n
)) {
765 cerr
<< "error in DocCompiler::generateDocConstantTbl() : "
767 << " is not an integer expression and can't be used as a table size' "
771 // allocate a name v_i for the table
772 getTypedNames(getCertifiedSigType(isig
), "v", ctype
, vname
);
774 // add a comment on tables in the notice
775 gDocNoticeFlagMap
["tablesigs"] = true;
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)));
780 // note that the name of the table can never be used outside an sigDocTableAccess
786 * tests if a charactere is a word separator
788 static bool isSeparator(char c
)
790 bool w
= ( ((c
>= 'a') && (c
<='z'))
791 || ((c
>= 'A') && (c
<='Z'))
792 || ((c
>= '0') && (c
<='9'))
800 * Replaces the occurences of 't' in a formula with another character
802 static string
replaceTimeBy(const string
& src
, char r
)
806 for (size_t i
=0; i
< src
.size(); i
++)
809 if ((x
=='t') && isSeparator(pre
) && ((i
== src
.size()-1) || isSeparator(src
[i
+1]))) {
820 * Generate the equation of a write table, which content is time dependent.
821 * It is basically a signal of vectors.
823 string
DocCompiler::generateDocWriteTbl (Tree
/*tbl*/, Tree size
, Tree isig
, Tree widx
, Tree wsig
)
826 string init
= CS(isig
,0);
828 if (!isSigInt(size
, &n
)) {
829 cerr
<< "error in DocCompiler::generateDocWriteTbl() : "
831 << " is not an integer expression and can't be used as a table size' "
836 // allocate a name w_i for the table
837 getTypedNames(getCertifiedSigType(isig
), "w", ctype
, vname
);
839 // add a comment on tables in the notice
840 gDocNoticeFlagMap
["tablesigs"] = true;
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.";
851 // add the table equation
852 fLateq
->addRWTblSigFormula(ltqRWTableDef
); //w(t) = initsig(t)
854 // note that the name of the table can never be used outside an sigDocTableAccess
860 * Generate the equation of a write table, which content is time dependent.
861 * It is basically a signal of vectors.
863 string
DocCompiler::generateDocAccessTbl (Tree sig
, Tree tbl
, Tree ridx
)
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) );
869 return generateCacheCode(sig
, result
);
872 bool DocCompiler::isShortEnough(string
& s
, unsigned int max
)
874 return (s
.length() <= max
);
879 /*****************************************************************************
881 *****************************************************************************/
885 * Generate code for a projection of a group of mutually recursive definitions
887 string
DocCompiler::generateRecProj(Tree sig
, Tree r
, int i
, int priority
)
892 //cerr << "*** generateRecProj sig : \"" << ppsig(sig) << "\"" << endl;
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;
902 //cerr << "(generateRecProj has already a vname : \"" << subst("$0(t)", vname) << "\")" << endl;
904 return subst("$0(t)", vname
);
909 * Generate code for a group of mutually recursive definitions
911 void DocCompiler::generateRec(Tree sig
, Tree var
, Tree le
, int priority
)
915 vector
<bool> used(N
);
916 vector
<int> delay(N
);
917 vector
<string
> vname(N
);
918 vector
<string
> ctype(N
);
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
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();
933 // this projection is not used therefore
934 // we should not generate code for it
936 //cerr << "generateRec : used[" << i << "] = false" << endl;
940 // generate delayline for each element of a recursive definition
941 for (int i
=0; i
<N
; i
++) {
943 generateDelayLine(ctype
[i
], vname
[i
], delay
[i
], CS(nth(le
,i
), priority
));
949 /*****************************************************************************
950 PREFIX, DELAY A PREFIX VALUE
951 *****************************************************************************/
954 * Generate LaTeX code for "prefix", a 1sample-delay explicitely initialized.
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.
961 * @return <string> The LaTeX code translation of the signal, cached.
963 string
DocCompiler::generatePrefix (Tree sig
, Tree x
, Tree e
, int priority
)
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
970 if (! getVectorNameProperty(e
, vecname
)) {
971 cerr
<< "No vector name for : " << ppsig(e
) << endl
;
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.";
982 fLateq
->addPrefixSigFormula(ltqPrefixDef
);
983 gDocNoticeFlagMap
["prefixsigs"] = true;
985 return generateCacheCode(sig
, subst("$0(t)", var
));
989 /*****************************************************************************
991 *****************************************************************************/
994 * Generate a "iota" time function, n-cyclical.
996 string
DocCompiler::generateIota (Tree sig
, Tree n
)
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
));
1006 // a revoir en utilisant la lecture de table et en partageant la construction de la paire de valeurs
1010 * Generate a select2 code
1012 string
DocCompiler::generateSelect2 (Tree sig
, Tree sel
, Tree s1
, Tree s2
, int priority
)
1014 string var
= getFreshID("q");
1015 string expsel
= CS(sel
, 0);
1016 string exps1
= CS(s1
, 0);
1017 string exps2
= CS(s2
, 0);
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.";
1026 fLateq
->addSelectSigFormula(ltqSelDef
);
1027 gDocNoticeFlagMap
["selectionsigs"] = true;
1029 //return generateCacheCode(sig, subst("$0(t)", var));
1030 setVectorNameProperty(sig
, var
);
1031 return subst("$0(t)", var
);
1036 * Generate a select3 code
1038 string
DocCompiler::generateSelect3 (Tree sig
, Tree sel
, Tree s1
, Tree s2
, Tree s3
, int priority
)
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);
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.";
1054 fLateq
->addSelectSigFormula(ltqSelDef
);
1055 gDocNoticeFlagMap
["selectionsigs"] = true;
1057 //return generateCacheCode(sig, subst("$0(t)", var));
1058 setVectorNameProperty(sig
, var
);
1059 return subst("$0(t)", var
);
1064 * retrieve the type annotation of sig
1065 * @param sig the signal we want to know the type
1067 string
DocCompiler::generateXtended (Tree sig
, int priority
)
1069 xtended
* p
= (xtended
*) getUserData(sig
);
1070 vector
<string
> args
;
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
)));
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
));
1082 //cerr << "!! generateXtended : <do not needCache> : calls p->generateLateq(fLateq, args, types)" << endl;
1083 return p
->generateLateq(fLateq
, args
, types
);
1089 //------------------------------------------------------------------------------------------------
1092 /*****************************************************************************
1093 vector name property
1094 *****************************************************************************/
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
1103 void DocCompiler::setVectorNameProperty(Tree sig
, const string
& vecname
)
1105 fVectorProperty
.set(sig
, vecname
);
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
1117 bool DocCompiler::getVectorNameProperty(Tree sig
, string
& vecname
)
1119 return fVectorProperty
.get(sig
, vecname
);
1124 /*****************************************************************************
1125 N-SAMPLE FIXED DELAY : sig = exp@delay
1127 case 1-sample max delay :
1129 Temp Var gLessTempSwitch = false
1130 V[0] V[1] gLessTempSwitch = true
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
1137 case max delay >= gMaxCopyDelay :
1138 Y(t-0) Y(t-1) Y(t-2) ...
1143 *****************************************************************************/
1146 * Generate code for accessing a delayed signal. The generated code depend of
1147 * the maximum delay attached to exp and the gLessTempSwitch.
1149 * @todo Priorités à revoir pour le parenthésage (associativité de - et /),
1150 * avec gBinOpLateqTable dans binop.cpp.
1152 string
DocCompiler::generateFixDelay (Tree sig
, Tree exp
, Tree delay
, int priority
)
1157 CS(exp
, 0); // ensure exp is compiled to have a vector name
1159 if (! getVectorNameProperty(exp
, vecname
)) {
1160 cerr
<< "No vector name for : " << ppsig(exp
) << endl
;
1164 if (isSigInt(delay
, &d
) && (d
== 0)) {
1165 //cerr << "@ generateFixDelay : d = " << d << endl;
1166 return subst("$0(t)", vecname
);
1168 //cerr << "@ generateFixDelay : d = " << d << endl;
1169 return subst("$0(t\\!-\\!$1)", vecname
, CS(delay
, 7));
1175 * Generate code for the delay mecchanism. The generated code depend of the
1176 * maximum delay attached to exp and the "less temporaries" switch
1178 string
DocCompiler::generateDelayVec(Tree sig
, const string
& exp
, const string
& ctype
, const string
& vname
, int mxd
)
1180 string s
= generateDelayVecNoTemp(sig
, exp
, ctype
, vname
, mxd
);
1181 if (getCertifiedSigType(sig
)->variability() < kSamp
) {
1190 * Generate code for the delay mecchanism without using temporary variables
1192 string
DocCompiler::generateDelayVecNoTemp(Tree sig
, const string
& exp
, const string
& ctype
, const string
& vname
, int mxd
)
1196 //cerr << " entering generateDelayVecNoTemp" << endl;
1200 // if generateVariableStore has already tagged sig, no definition is needed.
1201 if(getVectorNameProperty(sig
, vectorname
)) {
1202 return subst("$0(t)", vectorname
);
1204 fLateq
->addRecurSigFormula(subst("$0(t) = $1", vname
, exp
));
1205 setVectorNameProperty(sig
, vname
);
1206 return subst("$0(t)", vname
);
1212 * Generate code for the delay mecchanism without using temporary variables
1214 void DocCompiler::generateDelayLine(const string
& ctype
, const string
& vname
, int mxd
, const string
& exp
)
1218 fLateq
->addRecurSigFormula(subst("$0(t) = $1", vname
, exp
));
1220 fLateq
->addRecurSigFormula(subst("$0(t) = $1", vname
, exp
));
1227 /****************************************************************
1228 User interface element utilities.
1229 *****************************************************************/
1233 * @brief Get the directory of a user interface element.
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.
1241 * @param[in] pathname The path tree to convert.
1242 * @return <string> A directory-like string.
1244 string
DocCompiler::getUIDir(Tree pathname
)
1246 //cerr << "Documentator : getUIDir : print(pathname, stdout) = "; print(pathname, stdout); cerr << endl;
1248 Tree dir
= reverse(tl(pathname
));
1249 while (!isNil(dir
)) {
1250 string tmp
= tree2str(tl(hd(dir
)));
1251 if ( (tmp
[0] != '[') && (!tmp
.empty()) ) {
1261 * @brief Prepare binary user interface elements (button, checkbox).
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.
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.
1272 string
DocCompiler::prepareBinaryUI(const string
& name
, Tree path
)
1275 getUIDocInfos(path
, label
, unit
);
1277 label
= (label
.size()>0) ? ("\\textsf{\""+label
+"\"} ") : "";
1278 unit
= (unit
.size()>0) ? ("\\ ("+unit
+")") : "";
1280 s
+= " & $" + name
+ "$";
1281 s
+= " $\\in$ $\\left\\{\\,0, 1\\,\\right\\}$";
1282 s
+= " & $(\\mbox{" + gDocMathStringMap
["defaultvalue"] + "} = 0)$\\\\";
1288 * @brief Prepare "intervallic" user interface elements (sliders, nentry).
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.
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.
1302 string
DocCompiler::prepareIntervallicUI(const string
& name
, Tree path
, Tree tcur
, Tree tmin
, Tree tmax
)
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
));
1311 label
= (label
.size()>0) ? ("\\textsf{\""+label
+"\"} ") : "";
1312 unit
= (unit
.size()>0) ? ("\\ ("+unit
+")") : "";
1314 s
+= " & $" + name
+ "$";
1315 s
+= " $\\in$ $\\left[\\," + min
+ ", " + max
+ "\\,\\right]$";
1316 s
+= " & $(\\mbox{" + gDocMathStringMap
["defaultvalue"] + "} = " + cur
+ ")$\\\\";
1322 * Get information on a user interface element for documentation.
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.
1328 void DocCompiler::getUIDocInfos(Tree path
, string
& label
, string
& unit
)
1333 map
<string
, set
<string
> > metadata
;
1334 extractMetadata(tree2str(hd(path
)), label
, metadata
);
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;
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
;