+/************************************************************************
+ ************************************************************************
+ FAUST compiler
+ Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ************************************************************************
+ ************************************************************************/
+
+/*****************************************************************************
+ HISTORY
+ 22/01/05 : corrected bug on bool signals cached in float variables
+ 2009-08-16 : First "doc" version (kb)
+ 2009-11-22 : Some clean up (kb)
+*****************************************************************************/
+
+
+
+
+#include <stdio.h>
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <math.h>
+
+#include "doc_compile.hh"
+#include "sigtype.hh"
+#include "floats.hh"
+#include "sigprint.hh"
+#include "sigtyperules.hh"
+#include "recursivness.hh"
+#include "simplify.hh"
+#include "privatise.hh"
+#include "prim2.hh"
+#include "xtended.hh"
+#include "compatibility.hh"
+#include "ppsig.hh"
+#include "names.hh"
+#include "doc.hh"
+#include "tlib.hh"
+#include "doc_notice.hh"
+
+
+extern bool gLessTempSwitch;
+extern int gMaxCopyDelay;
+extern map<string, string> gDocMathStringMap;
+
+extern bool getSigListNickName(Tree t, Tree& id);
+
+static const unsigned int MAX_RIGHT_MEMBER = 20;
+static const unsigned int MAX_SUB_EXPR = 10;
+
+
+/*****************************************************************************
+ getFreshID
+*****************************************************************************/
+
+map<string, int> DocCompiler::fIDCounters;
+
+string DocCompiler::getFreshID(const string& prefix)
+{
+ if (fIDCounters.find(prefix) == fIDCounters.end()) {
+ fIDCounters[prefix] = 1;
+ }
+ int n = fIDCounters[prefix];
+ fIDCounters[prefix] = n+1;
+
+ return subst("$0_{$1}", prefix, docT(n));
+}
+
+
+/*****************************************************************************
+ prepare
+*****************************************************************************/
+
+Tree DocCompiler::annotate(Tree LS)
+{
+ recursivnessAnnotation(LS); // Annotate LS with recursivness information
+ typeAnnotation(LS); // Annotate LS with type information
+ sharingAnalysis(LS); // annotate LS with sharing count
+ fOccMarkup.mark(LS); // annotate LS with occurences analysis
+
+ return LS;
+}
+
+/*****************************************************************************
+ compileLateq
+*****************************************************************************/
+
+Lateq* DocCompiler::compileLateq (Tree L, Lateq* compiledEqn)
+{
+ //cerr << "Documentator : compileLateq : L = "; printSignal(L, stdout, 0); cerr << endl;
+
+ fLateq = compiledEqn; ///< Dynamic field !
+ int priority = 0;
+
+ for (int i = 0; isList(L); L = tl(L), i++) {
+ Tree sig = hd(L);
+ Tree id;
+ if(getSigNickname(sig, id)) {
+ //cerr << "Documentator : compileLateq : NICKNAMEPROPERTY = " << tree2str(id) << endl;
+ fLateq->addOutputSigFormula(subst("$0(t) = $1", tree2str(id), CS(sig, priority), docT(i)));
+ } else {
+ //cerr << "Documentator : compileLateq : NO NICKNAMEPROPERTY" << endl;
+ if (fLateq->outputs() == 1) {
+ fLateq->addOutputSigFormula(subst("y(t) = $0", CS(sig, priority)));
+ gDocNoticeFlagMap["outputsig"] = true;
+ } else {
+ fLateq->addOutputSigFormula(subst("$0(t) = $1", getFreshID("y"), CS(sig, priority)));
+ gDocNoticeFlagMap["outputsigs"] = true;
+ }
+ }
+ }
+ return fLateq;
+}
+
+
+
+/*****************************************************************************
+ CS : compile a signal
+*****************************************************************************/
+
+/**
+ * Test if a signal is already compiled
+ * @param sig the signal expression to compile.
+ * @param name the string representing the compiled expression.
+ * @return true is already compiled
+ */
+bool DocCompiler::getCompiledExpression(Tree sig, string& cexp)
+{
+ return fCompileProperty.get(sig, cexp);
+}
+
+
+/**
+ * Set the string of a compiled expression is already compiled
+ * @param sig the signal expression to compile.
+ * @param cexp the string representing the compiled expression.
+ * @return the cexp (for commodity)
+ */
+string DocCompiler::setCompiledExpression(Tree sig, const string& cexp)
+{
+ fCompileProperty.set(sig, cexp);
+ return cexp;
+}
+
+
+/**
+ * Compile a signal
+ * @param sig the signal expression to compile.
+ * @return the C code translation of sig as a string
+ */
+string DocCompiler::CS (Tree sig, int priority)
+{
+ string code;
+
+ if (!getCompiledExpression(sig, code)) { // not compiled yet.
+ code = generateCode(sig, priority);
+ setCompiledExpression(sig, code);
+ }
+ return code;
+}
+
+
+
+/*****************************************************************************
+ generateCode : dispatch according to signal
+*****************************************************************************/
+
+
+/**
+ * @brief Main code generator dispatch.
+ *
+ * According to the type of the input signal, generateCode calls
+ * the appropriate generator with appropriate arguments.
+ *
+ * @param sig The signal expression to compile.
+ * @param priority The environment priority of the expression.
+ * @return <string> The LaTeX code translation of the signal.
+ */
+string DocCompiler::generateCode (Tree sig, int priority)
+{
+ int i;
+ double r;
+ Tree c, sel, x, y, z, u, label, ff, largs, type, name, file;
+
+ if ( getUserData(sig) ) { printGCCall(sig,"generateXtended"); return generateXtended (sig, priority); }
+ else if ( isSigInt(sig, &i) ) { printGCCall(sig,"generateNumber"); return generateNumber (sig, docT(i)); }
+ else if ( isSigReal(sig, &r) ) { printGCCall(sig,"generateNumber"); return generateNumber (sig, docT(r)); }
+ else if ( isSigInput(sig, &i) ) { printGCCall(sig,"generateInput"); return generateInput (sig, docT(i+1)); }
+ else if ( isSigOutput(sig, &i, x) ) { printGCCall(sig,"generateOutput"); return generateOutput (sig, docT(i+1), CS(x, priority)); }
+
+ else if ( isSigFixDelay(sig, x, y) ) { printGCCall(sig,"generateFixDelay"); return generateFixDelay (sig, x, y, priority); }
+ else if ( isSigPrefix(sig, x, y) ) { printGCCall(sig,"generatePrefix"); return generatePrefix (sig, x, y, priority); }
+ else if ( isSigIota(sig, x) ) { printGCCall(sig,"generateIota"); return generateIota (sig, x); }
+
+ else if ( isSigBinOp(sig, &i, x, y) ) { printGCCall(sig,"generateBinOp"); return generateBinOp (sig, i, x, y, priority); }
+ else if ( isSigFFun(sig, ff, largs) ) { printGCCall(sig,"generateFFun"); return generateFFun (sig, ff, largs, priority); }
+ else if ( isSigFConst(sig, type, name, file) ) { printGCCall(sig,"generateFConst"); return generateFConst (sig, tree2str(file), tree2str(name)); }
+ else if ( isSigFVar(sig, type, name, file) ) { printGCCall(sig,"generateFVar"); return generateFVar (sig, tree2str(file), tree2str(name)); }
+
+ // new special tables for documentation purposes
+
+ else if ( isSigDocConstantTbl(sig, x, y) ) { printGCCall(sig,"generateDocConstantTbl"); return generateDocConstantTbl (sig, x, y); }
+ else if ( isSigDocWriteTbl(sig,x,y,z,u) ) { printGCCall(sig,"generateDocWriteTbl"); return generateDocWriteTbl (sig, x, y, z, u); }
+ else if ( isSigDocAccessTbl(sig, x, y) ) { printGCCall(sig, "generateDocAccessTbl"); return generateDocAccessTbl(sig, x, y); }
+
+
+ else if ( isSigSelect2(sig, sel, x, y) ) { printGCCall(sig,"generateSelect2"); return generateSelect2 (sig, sel, x, y, priority); }
+ else if ( isSigSelect3(sig, sel, x, y, z) ) { printGCCall(sig,"generateSelect3"); return generateSelect3 (sig, sel, x, y, z, priority); }
+
+ else if ( isProj(sig, &i, x) ) { printGCCall(sig,"generateRecProj"); return generateRecProj (sig, x, i, priority); }
+
+ else if ( isSigIntCast(sig, x) ) { printGCCall(sig,"generateIntCast"); return generateIntCast (sig, x, priority); }
+ else if ( isSigFloatCast(sig, x) ) { printGCCall(sig,"generateFloatCast"); return generateFloatCast(sig, x, priority); }
+
+ else if ( isSigButton(sig, label) ) { printGCCall(sig,"generateButton"); return generateButton (sig, label); }
+ else if ( isSigCheckbox(sig, label) ) { printGCCall(sig,"generateCheckbox"); return generateCheckbox (sig, label); }
+ else if ( isSigVSlider(sig, label,c,x,y,z) ) { printGCCall(sig,"generateVSlider"); return generateVSlider (sig, label, c,x,y,z); }
+ else if ( isSigHSlider(sig, label,c,x,y,z) ) { printGCCall(sig,"generateHSlider"); return generateHSlider (sig, label, c,x,y,z); }
+ else if ( isSigNumEntry(sig, label,c,x,y,z) ) { printGCCall(sig,"generateNumEntry"); return generateNumEntry (sig, label, c,x,y,z); }
+
+ else if ( isSigVBargraph(sig, label,x,y,z) ) { printGCCall(sig,"generateVBargraph"); return CS(z, priority);}//generateVBargraph (sig, label, x, y, CS(z, priority)); }
+ else if ( isSigHBargraph(sig, label,x,y,z) ) { printGCCall(sig,"generateHBargraph"); return CS(z, priority);}//generateHBargraph (sig, label, x, y, CS(z, priority)); }
+ else if ( isSigAttach(sig, x, y) ) { printGCCall(sig,"generateAttach"); return generateAttach (sig, x, y, priority); }
+
+ else {
+ cerr << "Error in d signal, unrecognized signal : " << *sig << endl;
+ exit(1);
+ }
+ assert(0);
+ return "error in generate code";
+}
+
+
+/**
+ * Print calling information of generateCode, for debug purposes.
+ *
+ * @remark
+ * To turn printing on, turn the 'printCalls' boolean to true.
+ */
+void DocCompiler::printGCCall(Tree sig, const string& calledFunction)
+{
+ bool printCalls = false;
+ bool maskSigs = false;
+
+ if(printCalls) {
+ cerr << " -> generateCode calls " << calledFunction;
+ if(maskSigs) {
+ cerr << endl;
+ } else {
+ cerr << " on " << ppsig(sig) << endl;
+ }
+ }
+}
+
+
+/*****************************************************************************
+ NUMBERS
+*****************************************************************************/
+
+
+string DocCompiler::generateNumber (Tree sig, const string& exp)
+{
+ string ctype, vname;
+ Occurences* o = fOccMarkup.retrieve(sig);
+
+ // check for number occuring in delays
+ if (o->getMaxDelay()>0) {
+ getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
+ gDocNoticeFlagMap["recursigs"] = true;
+ //cerr << "- r : generateNumber : \"" << vname << "\"" << endl;
+ generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
+ }
+ return exp;
+}
+
+/*****************************************************************************
+ FOREIGN CONSTANTS
+*****************************************************************************/
+
+
+string DocCompiler::generateFConst (Tree sig, const string& file, const string& exp)
+{
+ string ctype, vname;
+ Occurences* o = fOccMarkup.retrieve(sig);
+
+ if (o->getMaxDelay()>0) {
+ getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
+ gDocNoticeFlagMap["recursigs"] = true;
+ //cerr << "- r : generateFConst : \"" << vname << "\"" << endl;
+ generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
+ }
+
+ if (exp == "fSamplingFreq") {
+ //gDocNoticeFlagMap["fsamp"] = true;
+ return "f_S";
+ }
+
+ return "\\mathrm{"+exp+"}";
+}
+
+/*****************************************************************************
+ FOREIGN VARIABLES
+*****************************************************************************/
+
+
+string DocCompiler::generateFVar (Tree sig, const string& file, const string& exp)
+{
+ string ctype, vname;
+ Occurences* o = fOccMarkup.retrieve(sig);
+
+ if (o->getMaxDelay()>0) {
+ getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
+ gDocNoticeFlagMap["recursigs"] = true;
+ //cerr << "- r : generateFVar : \"" << vname << "\"" << endl;
+ setVectorNameProperty(sig, vname);
+ generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
+ }
+ return generateCacheCode(sig, exp);
+}
+
+
+/*****************************************************************************
+ INPUTS - OUTPUTS
+*****************************************************************************/
+
+
+string DocCompiler::generateInput (Tree sig, const string& idx)
+{
+ if (fLateq->inputs() == 1) {
+ setVectorNameProperty(sig, "x");
+ fLateq->addInputSigFormula("x(t)");
+ gDocNoticeFlagMap["inputsig"] = true;
+ return generateCacheCode(sig, "x(t)");
+ } else {
+ setVectorNameProperty(sig, subst("x_{$0}", idx));
+ fLateq->addInputSigFormula(subst("x_{$0}(t)", idx));
+ gDocNoticeFlagMap["inputsigs"] = true;
+ return generateCacheCode(sig, subst("x_{$0}(t)", idx));
+ }
+}
+
+
+/** Unused for the moment ! */
+string DocCompiler::generateOutput (Tree sig, const string& idx, const string& arg)
+{
+ string dst;
+
+ if (fLateq->outputs() == 1) {
+ dst = subst("y(t)", idx);
+ gDocNoticeFlagMap["outputsig"] = true;
+ } else {
+ dst = subst("y_{$0}(t)", idx);
+ gDocNoticeFlagMap["outputsigs"] = true;
+ }
+
+ fLateq->addOutputSigFormula(subst("$0 = $1", dst, arg));
+ return dst;
+}
+
+
+/*****************************************************************************
+ BINARY OPERATION
+*****************************************************************************/
+
+/**
+ * Generate binary operations, managing priority parenthesis.
+ * ((a*b)+c) can be written (a*b+c) if priority(*) > priority(+)
+ * ((a*b)*c) can be writteb (a*b*c) if * is associative
+ * Associative operation should have a distinc priority from other operations.
+ * Non associative operations can share the same priority.
+ *
+ * @param sig The signal expression to treat.
+ * @param opcode The operation code, as described in gBinOpLateqTable.
+ * @param arg1 The first operand.
+ * @param arg2 The second operand.
+ * @param priority The priority of the environment of the expression.
+ *
+ * @return <string> The LaTeX code translation of the signal, cached.
+ *
+ * @remark The case of LaTeX frac{}{} is special.
+ *
+ * @todo Handle integer arithmetics, by testing arguments type,
+ * and printing dedicated operators (\oplus, \odot, \ominus, \oslash).
+ */
+
+/// associative operations are + * | & xor
+static bool associative (int opcode) {
+ return (opcode == kAdd) || (opcode == kMul) || (opcode == kAND) || (opcode == kOR) || (opcode == kXOR);
+}
+
+string DocCompiler::generateBinOp(Tree sig, int opcode, Tree arg1, Tree arg2, int priority)
+{
+ string s;
+ int thisPriority = gBinOpLateqTable[opcode]->fPriority;
+
+ /* Priority parenthesis handling. */
+ string lpar = "";
+ string rpar = "";
+ if ( (thisPriority < priority) || ((thisPriority == priority) && !associative(opcode)) ) {
+ // (a+b)*c or (a/b)/c need parenthesis
+ lpar = " \\left(";
+ rpar = "\\right) ";
+ }
+
+ Type t1 = getCertifiedSigType(arg1);
+ Type t2 = getCertifiedSigType(arg2);
+ bool intOpDetected = false;
+ if ( (t1->nature() == kInt) && (t2->nature() == kInt) ) {
+ intOpDetected = true;
+ }
+
+ string op;
+ if(!intOpDetected) {
+ op = gBinOpLateqTable[opcode]->fName;
+ } else {
+ switch (opcode) {
+ case kAdd:
+ op = "\\oplus";
+ gDocNoticeFlagMap["intplus"] = true;
+ break;
+ case kSub:
+ op = "\\ominus";
+ gDocNoticeFlagMap["intminus"] = true;
+ break;
+ case kMul:
+ op = "\\odot";
+ gDocNoticeFlagMap["intmult"] = true;
+ break;
+ case kDiv:
+ op = "\\oslash";
+ gDocNoticeFlagMap["intdiv"] = true;
+ gDocNoticeFlagMap["intcast"] = true; // "$normalize(int(i/j))$" in the notice.
+ break;
+ default:
+ op = gBinOpLateqTable[opcode]->fName;
+ break;
+ }
+ }
+
+ /* LaTeX frac{}{} handling VS general case. */
+ if ( (opcode == kDiv) && (!intOpDetected) ) {
+ s = subst("$0\\frac{$1}{$2}$3", lpar, CS(arg1, 0), CS(arg2, 0), rpar);
+ } else {
+ s = subst("$0$1 $2 $3$4", lpar, CS(arg1, thisPriority), op, CS(arg2, thisPriority), rpar);
+ }
+
+// if (opcode == kMul) {
+// gDocNoticeFlagMap["cdot"] = true;
+// }
+
+ return generateCacheCode(sig, s);
+}
+
+
+/*****************************************************************************
+ Primitive Operations
+*****************************************************************************/
+
+string DocCompiler::generateFFun(Tree sig, Tree ff, Tree largs, int priority)
+{
+ string code = ffname(ff);
+ code += '(';
+ string sep = "";
+ for (int i = 0; i< ffarity(ff); i++) {
+ code += sep;
+ code += CS(nth(largs, i), priority);
+ sep = ", ";
+ }
+ code += ')';
+
+ gDocNoticeFlagMap["foreignfun"] = true;
+
+ return "\\mathrm{ff"+code+"}";
+}
+
+
+/*****************************************************************************
+ CACHE CODE
+*****************************************************************************/
+
+void DocCompiler::getTypedNames(Type t, const string& prefix, string& ctype, string& vname)
+{
+ if (t->nature() == kInt) {
+ ctype = "int"; vname = subst("$0", getFreshID(prefix));
+ } else {
+ ctype = ifloat(); vname = subst("$0", getFreshID(prefix));
+ }
+}
+
+
+/**
+ * Test if exp is very simple that is it
+ * can't be considered a real component
+ * @param exp the signal we want to test
+ * @return true if it a very simple signal
+ */
+static bool isVerySimpleFormula(Tree sig)
+{
+ int i;
+ double r;
+ Tree type, name, file, label, c, x, y, z;
+
+ return isSigInt(sig, &i)
+ || isSigReal(sig, &r)
+ || isSigInput(sig, &i)
+ || isSigFConst(sig, type, name, file)
+ || isSigButton(sig, label)
+ || isSigCheckbox(sig, label)
+ || isSigVSlider(sig, label,c,x,y,z)
+ || isSigHSlider(sig, label,c,x,y,z)
+ || isSigNumEntry(sig, label,c,x,y,z)
+ ;
+}
+
+
+string DocCompiler::generateCacheCode(Tree sig, const string& exp)
+{
+ //cerr << "!! entering generateCacheCode with sig=\"" << ppsig(sig) << "\"" << endl;
+
+ string vname, ctype, code, vectorname;
+
+ int sharing = getSharingCount(sig);
+ Occurences* o = fOccMarkup.retrieve(sig);
+
+ // check reentrance
+ if (getCompiledExpression(sig, code)) {
+ //cerr << "!! generateCacheCode called a true getCompiledExpression" << endl;
+ return code;
+ }
+
+ // check for expression occuring in delays
+ if (o->getMaxDelay()>0) {
+ if (getVectorNameProperty(sig, vectorname)) {
+ return exp;
+ }
+ getTypedNames(getCertifiedSigType(sig), "r", ctype, vname);
+ gDocNoticeFlagMap["recursigs"] = true;
+ //cerr << "- r : generateCacheCode : vame=\"" << vname << "\", for sig=\"" << ppsig(sig) << "\"" << endl;
+ if (sharing>1) {
+ //cerr << " generateCacheCode calls generateDelayVec(generateVariableStore) on vame=\"" << vname << "\"" << endl;
+ return generateDelayVec(sig, generateVariableStore(sig,exp), ctype, vname, o->getMaxDelay());
+ } else {
+ //cerr << " generateCacheCode calls generateDelayVec(exp) on vame=\"" << vname << "\"" << endl;
+ return generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
+ }
+ }
+ else if (sharing == 1 || getVectorNameProperty(sig, vectorname) || isVerySimpleFormula(sig)) {
+ //cerr << "! generateCacheCode : sharing == 1 : return \"" << exp << "\"" << endl;
+ return exp;
+ }
+ else if (sharing > 1) {
+ //cerr << "! generateCacheCode : sharing > 1 : return \"" << exp << "\"" << endl;
+ return generateVariableStore(sig, exp);
+ }
+ else {
+ cerr << "Error in sharing count (" << sharing << ") for " << *sig << endl;
+ exit(1);
+ }
+
+ return "Error in generateCacheCode";
+}
+
+
+string DocCompiler::generateVariableStore(Tree sig, const string& exp)
+{
+ string vname, ctype;
+ Type t = getCertifiedSigType(sig);
+
+ switch (t->variability()) {
+
+ case kKonst :
+ getTypedNames(t, "k", ctype, vname); ///< "k" for constants.
+ fLateq->addConstSigFormula(subst("$0 = $1", vname, exp));
+ gDocNoticeFlagMap["constsigs"] = true;
+ return vname;
+
+ case kBlock :
+ getTypedNames(t, "p", ctype, vname); ///< "p" for "parameter".
+ fLateq->addParamSigFormula(subst("$0(t) = $1", vname, exp));
+ gDocNoticeFlagMap["paramsigs"] = true;
+ setVectorNameProperty(sig, vname);
+ return subst("$0(t)", vname);
+
+ case kSamp :
+ if(getVectorNameProperty(sig, vname)) {
+ return subst("$0(t)", vname);
+ } else {
+ getTypedNames(t, "s", ctype, vname);
+ //cerr << "- generateVariableStore : \"" << subst("$0(t) = $1", vname, exp) << "\"" << endl;
+ fLateq->addStoreSigFormula(subst("$0(t) = $1", vname, exp));
+ gDocNoticeFlagMap["storedsigs"] = true;
+ setVectorNameProperty(sig, vname);
+ return subst("$0(t)", vname);
+ }
+
+ default:
+ assert(0);
+ return "";
+ }
+}
+
+
+/*****************************************************************************
+ CASTING
+*****************************************************************************/
+
+
+string DocCompiler::generateIntCast(Tree sig, Tree x, int priority)
+{
+ gDocNoticeFlagMap["intcast"] = true;
+
+ return generateCacheCode(sig, subst("\\mathrm{int}\\left($0\\right)", CS(x, 0)));
+}
+
+
+/**
+ * @brief Don't generate float cast !
+ *
+ * It is just a kind of redirection.
+ * Calling generateCacheCode ensures to create a new
+ * variable name if the input signal expression is shared.
+ */
+string DocCompiler::generateFloatCast (Tree sig, Tree x, int priority)
+{
+ return generateCacheCode(sig, subst("$0", CS(x, priority)));
+}
+
+
+/*****************************************************************************
+ user interface elements
+*****************************************************************************/
+
+string DocCompiler::generateButton(Tree sig, Tree path)
+{
+ string vname = getFreshID("{u_b}");
+ string varname = vname + "(t)";
+ fLateq->addUISigFormula(getUIDir(path), prepareBinaryUI(varname, path));
+ gDocNoticeFlagMap["buttonsigs"] = true;
+ return generateCacheCode(sig, varname);
+}
+
+string DocCompiler::generateCheckbox(Tree sig, Tree path)
+{
+ string vname = getFreshID("{u_c}");
+ string varname = vname + "(t)";
+ fLateq->addUISigFormula(getUIDir(path), prepareBinaryUI(varname, path));
+ gDocNoticeFlagMap["checkboxsigs"] = true;
+ return generateCacheCode(sig, varname);
+}
+
+string DocCompiler::generateVSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
+{
+ string varname = getFreshID("{u_s}") + "(t)";
+ fLateq->addUISigFormula(getUIDir(path), prepareIntervallicUI(varname, path, cur, min, max));
+ gDocNoticeFlagMap["slidersigs"] = true;
+ return generateCacheCode(sig, varname);
+}
+
+string DocCompiler::generateHSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
+{
+ string varname = getFreshID("{u_s}") + "(t)";
+ fLateq->addUISigFormula(getUIDir(path), prepareIntervallicUI(varname, path, cur, min, max));
+ gDocNoticeFlagMap["slidersigs"] = true;
+ return generateCacheCode(sig, varname);
+}
+
+string DocCompiler::generateNumEntry(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
+{
+ string varname = getFreshID("{u_n}") + "(t)";
+ fLateq->addUISigFormula(getUIDir(path), prepareIntervallicUI(varname, path, cur, min, max));
+ gDocNoticeFlagMap["nentrysigs"] = true;
+ return generateCacheCode(sig, varname);
+}
+
+
+string DocCompiler::generateVBargraph(Tree sig, Tree path, Tree min, Tree max, const string& exp)
+{
+ string varname = getFreshID("{u_g}");
+
+ Type t = getCertifiedSigType(sig);
+ switch (t->variability()) {
+
+ case kKonst :
+ break;
+
+ case kBlock :
+ break;
+
+ case kSamp :
+ break;
+ }
+ return generateCacheCode(sig, varname);
+}
+
+
+string DocCompiler::generateHBargraph(Tree sig, Tree path, Tree min, Tree max, const string& exp)
+{
+ string varname = getFreshID("{u_g}");
+
+ Type t = getCertifiedSigType(sig);
+ switch (t->variability()) {
+
+ case kKonst :
+ break;
+
+ case kBlock :
+ break;
+
+ case kSamp :
+ break;
+ }
+ return generateCacheCode(sig, varname);
+}
+
+
+string DocCompiler::generateAttach (Tree sig, Tree x, Tree y, int priority)
+{
+ string vname;
+ string exp;
+
+ CS(y, priority);
+ exp = CS(x, priority);
+
+ if(getVectorNameProperty(x, vname)) {
+ setVectorNameProperty(sig, vname);
+ }
+
+ return generateCacheCode(sig, exp);
+}
+
+
+
+
+/*****************************************************************************
+ TABLES
+ (note : tables here are siplified versions different from the ones used to
+ generate c++ code)
+*****************************************************************************/
+
+/**
+ * Generate the equation of a constant table (its content is time constant).
+ * Returns the name of the table
+ */
+string DocCompiler::generateDocConstantTbl (Tree /*tbl*/, Tree size, Tree isig)
+{
+ string vname, ctype;
+ string init = CS(isig,0);
+
+ int n;
+ if (!isSigInt(size, &n)) {
+ cerr << "error in DocCompiler::generateDocConstantTbl() : "
+ << *size
+ << " is not an integer expression and can't be used as a table size' "
+ << endl;
+ }
+
+ // allocate a name v_i for the table
+ getTypedNames(getCertifiedSigType(isig), "v", ctype, vname);
+
+ // add a comment on tables in the notice
+ gDocNoticeFlagMap["tablesigs"] = true;
+
+ // add equation v[t] = isig(t)
+ fLateq->addRDTblSigFormula(subst("$0[t] = $1 \\condition{when $$t \\in [0,$2]$$} ", vname, init, T(n-1)));
+
+ // note that the name of the table can never be used outside an sigDocTableAccess
+ return vname;
+}
+
+
+/**
+ * tests if a charactere is a word separator
+ */
+static bool isSeparator(char c)
+{
+ bool w = ( ((c >= 'a') && (c <='z'))
+ || ((c >= 'A') && (c <='Z'))
+ || ((c >= '0') && (c <='9'))
+ );
+
+ return ! w;
+}
+
+
+/**
+ * Replaces the occurences of 't' in a formula with another character
+ */
+static string replaceTimeBy(const string& src, char r)
+{
+ string dst;
+ char pre = 0;
+ for (size_t i=0; i < src.size(); i++)
+ {
+ char x = src[i];
+ if ((x=='t') && isSeparator(pre) && ((i == src.size()-1) || isSeparator(src[i+1]))) {
+ dst.push_back(r);
+ } else {
+ dst.push_back(x);
+ }
+ pre = x;
+ }
+ return dst;
+}
+
+/**
+ * Generate the equation of a write table, which content is time dependent.
+ * It is basically a signal of vectors.
+ */
+string DocCompiler::generateDocWriteTbl (Tree /*tbl*/, Tree size, Tree isig, Tree widx, Tree wsig)
+{
+ string vname, ctype;
+ string init = CS(isig,0);
+ int n;
+ if (!isSigInt(size, &n)) {
+ cerr << "error in DocCompiler::generateDocWriteTbl() : "
+ << *size
+ << " is not an integer expression and can't be used as a table size' "
+ << endl;
+ }
+
+
+ // allocate a name w_i for the table
+ getTypedNames(getCertifiedSigType(isig), "w", ctype, vname);
+
+ // add a comment on tables in the notice
+ gDocNoticeFlagMap["tablesigs"] = true;
+
+ // describe the table equation
+ string ltqRWTableDef;
+ ltqRWTableDef += subst("$0(t)[i] = \n", vname);
+ ltqRWTableDef += "\\left\\{\\begin{array}{ll}\n";
+ ltqRWTableDef += subst("$0 & \\mbox{if \\,} t < 0 \\mbox{\\, and \\,} i \\in [0,$1] \\\\\n", replaceTimeBy(init,'i'), T(n-1));
+ ltqRWTableDef += subst("$0 & \\mbox{if \\,} i = $1 \\\\\n", CS(wsig,0), CS(widx,0));
+ ltqRWTableDef += subst("$0(t\\!-\\!1)[i] & \\mbox{otherwise} \\\\\n", vname);
+ ltqRWTableDef += "\\end{array}\\right.";
+
+ // add the table equation
+ fLateq->addRWTblSigFormula(ltqRWTableDef); //w(t) = initsig(t)
+
+ // note that the name of the table can never be used outside an sigDocTableAccess
+ return vname;
+}
+
+
+/**
+ * Generate the equation of a write table, which content is time dependent.
+ * It is basically a signal of vectors.
+ */
+string DocCompiler::generateDocAccessTbl (Tree sig, Tree tbl, Tree ridx)
+{
+ // the compilation of a table always returns its name
+ string vname = CS(tbl, 0);
+ string result = subst("$0[$1]", vname, CS(ridx,0) );
+
+ return generateCacheCode(sig, result);
+}
+
+bool DocCompiler::isShortEnough(string& s, unsigned int max)
+{
+ return (s.length() <= max);
+}
+
+
+
+/*****************************************************************************
+ RECURSIONS
+*****************************************************************************/
+
+
+/**
+ * Generate code for a projection of a group of mutually recursive definitions
+ */
+string DocCompiler::generateRecProj(Tree sig, Tree r, int i, int priority)
+{
+ string vname;
+ Tree var, le;
+
+ //cerr << "*** generateRecProj sig : \"" << ppsig(sig) << "\"" << endl;
+
+ if ( ! getVectorNameProperty(sig, vname)) {
+ assert(isRec(r, var, le));
+ //cerr << " generateRecProj has NOT YET a vname : " << endl;
+ //cerr << "--> generateRecProj calls generateRec on \"" << ppsig(sig) << "\"" << endl;
+ generateRec(r, var, le, priority);
+ assert(getVectorNameProperty(sig, vname));
+ //cerr << "<-- generateRecProj vname : \"" << subst("$0(t)", vname) << "\"" << endl;
+ } else {
+ //cerr << "(generateRecProj has already a vname : \"" << subst("$0(t)", vname) << "\")" << endl;
+ }
+ return subst("$0(t)", vname);
+}
+
+
+/**
+ * Generate code for a group of mutually recursive definitions
+ */
+void DocCompiler::generateRec(Tree sig, Tree var, Tree le, int priority)
+{
+ int N = len(le);
+
+ vector<bool> used(N);
+ vector<int> delay(N);
+ vector<string> vname(N);
+ vector<string> ctype(N);
+
+ // prepare each element of a recursive definition
+ for (int i=0; i<N; i++) {
+ Tree e = sigProj(i,sig); // recreate each recursive definition
+ if (fOccMarkup.retrieve(e)) {
+ // this projection is used
+ used[i] = true;
+ //cerr << "generateRec : used[" << i << "] = true" << endl;
+ getTypedNames(getCertifiedSigType(e), "r", ctype[i], vname[i]);
+ gDocNoticeFlagMap["recursigs"] = true;
+ //cerr << "- r : generateRec setVectorNameProperty : \"" << vname[i] << "\"" << endl;
+ setVectorNameProperty(e, vname[i]);
+ delay[i] = fOccMarkup.retrieve(e)->getMaxDelay();
+ } else {
+ // this projection is not used therefore
+ // we should not generate code for it
+ used[i] = false;
+ //cerr << "generateRec : used[" << i << "] = false" << endl;
+ }
+ }
+
+ // generate delayline for each element of a recursive definition
+ for (int i=0; i<N; i++) {
+ if (used[i]) {
+ generateDelayLine(ctype[i], vname[i], delay[i], CS(nth(le,i), priority));
+ }
+ }
+}
+
+
+/*****************************************************************************
+ PREFIX, DELAY A PREFIX VALUE
+*****************************************************************************/
+
+/**
+ * Generate LaTeX code for "prefix", a 1sample-delay explicitely initialized.
+ *
+ * @param sig The signal expression to treat.
+ * @param x The initial value for the delay line.
+ * @param e The value for the delay line, after initialization.
+ * @param priority The priority of the environment of the expression.
+ *
+ * @return <string> The LaTeX code translation of the signal, cached.
+ */
+string DocCompiler::generatePrefix (Tree sig, Tree x, Tree e, int priority)
+{
+ string var = getFreshID("m");
+ string exp0 = CS(x, priority);
+ string exp1 = CS(e, priority); // ensure exp1 is compiled to have a vector name
+ string vecname;
+
+ if (! getVectorNameProperty(e, vecname)) {
+ cerr << "No vector name for : " << ppsig(e) << endl;
+ assert(0);
+ }
+
+ string ltqPrefixDef;
+ ltqPrefixDef += subst("$0(t) = \n", var);
+ ltqPrefixDef += "\\left\\{\\begin{array}{ll}\n";
+ ltqPrefixDef += subst("$0 & \\mbox{, when \\,} t = 0\\\\\n", exp0);
+ ltqPrefixDef += subst("$0 & \\mbox{, when \\,} t > 0\n", subst("$0(t\\!-\\!1)", vecname));
+ ltqPrefixDef += "\\end{array}\\right.";
+
+ fLateq->addPrefixSigFormula(ltqPrefixDef);
+ gDocNoticeFlagMap["prefixsigs"] = true;
+
+ return generateCacheCode(sig, subst("$0(t)", var));
+}
+
+
+/*****************************************************************************
+ IOTA(n)
+*****************************************************************************/
+
+/**
+ * Generate a "iota" time function, n-cyclical.
+ */
+string DocCompiler::generateIota (Tree sig, Tree n)
+{
+ int size;
+ if (!isSigInt(n, &size)) { fprintf(stderr, "error in generateIota\n"); exit(1); }
+ //cout << "iota !" << endl;
+ return subst(" t \\bmod{$0} ", docT(size));
+}
+
+
+
+// a revoir en utilisant la lecture de table et en partageant la construction de la paire de valeurs
+
+
+/**
+ * Generate a select2 code
+ */
+string DocCompiler::generateSelect2 (Tree sig, Tree sel, Tree s1, Tree s2, int priority)
+{
+ string var = getFreshID("q");
+ string expsel = CS(sel, 0);
+ string exps1 = CS(s1, 0);
+ string exps2 = CS(s2, 0);
+
+ string ltqSelDef;
+ ltqSelDef += subst("$0(t) = \n", var);
+ ltqSelDef += "\\left\\{\\begin{array}{ll}\n";
+ ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 0\\\\\n", exps1, expsel);
+ ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 1\n", exps2, expsel);
+ ltqSelDef += "\\end{array}\\right.";
+
+ fLateq->addSelectSigFormula(ltqSelDef);
+ gDocNoticeFlagMap["selectionsigs"] = true;
+
+ //return generateCacheCode(sig, subst("$0(t)", var));
+ setVectorNameProperty(sig, var);
+ return subst("$0(t)", var);
+}
+
+
+/**
+ * Generate a select3 code
+ */
+string DocCompiler::generateSelect3 (Tree sig, Tree sel, Tree s1, Tree s2, Tree s3, int priority)
+{
+ string var = getFreshID("q");
+ string expsel = CS(sel, 0);
+ string exps1 = CS(s1, 0);
+ string exps2 = CS(s2, 0);
+ string exps3 = CS(s3, 0);
+
+ string ltqSelDef;
+ ltqSelDef += subst("$0(t) = \n", var);
+ ltqSelDef += "\\left\\{\\begin{array}{ll}\n";
+ ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 0\\\\\n", generateVariableStore(s1, exps1), expsel);
+ ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 1\\\\\n", generateVariableStore(s2, exps2), expsel);
+ ltqSelDef += subst("$0 & \\mbox{if \\,} $1 = 2\n", generateVariableStore(s3, exps3), expsel);
+ ltqSelDef += "\\end{array}\\right.";
+
+ fLateq->addSelectSigFormula(ltqSelDef);
+ gDocNoticeFlagMap["selectionsigs"] = true;
+
+ //return generateCacheCode(sig, subst("$0(t)", var));
+ setVectorNameProperty(sig, var);
+ return subst("$0(t)", var);
+}
+
+
+/**
+ * retrieve the type annotation of sig
+ * @param sig the signal we want to know the type
+ */
+string DocCompiler::generateXtended (Tree sig, int priority)
+{
+ xtended* p = (xtended*) getUserData(sig);
+ vector<string> args;
+ vector<Type> types;
+
+ for (int i=0; i<sig->arity(); i++) {
+ args.push_back(CS(sig->branch(i), 0));
+ types.push_back(getCertifiedSigType(sig->branch(i)));
+ }
+
+ if (p->needCache()) {
+ //cerr << "!! generateXtended : <needCache> : calls generateCacheCode(sig, p->generateLateq(fLateq, args, types))" << endl;
+ return generateCacheCode(sig, p->generateLateq(fLateq, args, types));
+ } else {
+ //cerr << "!! generateXtended : <do not needCache> : calls p->generateLateq(fLateq, args, types)" << endl;
+ return p->generateLateq(fLateq, args, types);
+ }
+}
+
+
+
+//------------------------------------------------------------------------------------------------
+
+
+/*****************************************************************************
+ vector name property
+*****************************************************************************/
+
+/**
+ * Set the vector name property of a signal, the name of the vector used to
+ * store the previous values of the signal to implement a delay.
+ * @param sig the signal expression.
+ * @param vecname the string representing the vector name.
+ * @return true is already compiled
+ */
+void DocCompiler::setVectorNameProperty(Tree sig, const string& vecname)
+{
+ fVectorProperty.set(sig, vecname);
+}
+
+
+/**
+ * Get the vector name property of a signal, the name of the vector used to
+ * store the previous values of the signal to implement a delay.
+ * @param sig the signal expression.
+ * @param vecname the string where to store the vector name.
+ * @return true if the signal has this property, false otherwise
+ */
+
+bool DocCompiler::getVectorNameProperty(Tree sig, string& vecname)
+{
+ return fVectorProperty.get(sig, vecname);
+}
+
+
+
+/*****************************************************************************
+ N-SAMPLE FIXED DELAY : sig = exp@delay
+
+ case 1-sample max delay :
+ Y(t-0) Y(t-1)
+ Temp Var gLessTempSwitch = false
+ V[0] V[1] gLessTempSwitch = true
+
+ case max delay < gMaxCopyDelay :
+ Y(t-0) Y(t-1) Y(t-2) ...
+ Temp V[0] V[1] ... gLessTempSwitch = false
+ V[0] V[1] V[2] ... gLessTempSwitch = true
+
+ case max delay >= gMaxCopyDelay :
+ Y(t-0) Y(t-1) Y(t-2) ...
+ Temp V[0] V[1] ...
+ V[0] V[1] V[2] ...
+
+
+*****************************************************************************/
+
+/**
+ * Generate code for accessing a delayed signal. The generated code depend of
+ * the maximum delay attached to exp and the gLessTempSwitch.
+ *
+ * @todo Priorités à revoir pour le parenthésage (associativité de - et /),
+ * avec gBinOpLateqTable dans binop.cpp.
+ */
+string DocCompiler::generateFixDelay (Tree sig, Tree exp, Tree delay, int priority)
+{
+ int d;
+ string vecname;
+
+ CS(exp, 0); // ensure exp is compiled to have a vector name
+
+ if (! getVectorNameProperty(exp, vecname)) {
+ cerr << "No vector name for : " << ppsig(exp) << endl;
+ assert(0);
+ }
+
+ if (isSigInt(delay, &d) && (d == 0)) {
+ //cerr << "@ generateFixDelay : d = " << d << endl;
+ return subst("$0(t)", vecname);
+ } else {
+ //cerr << "@ generateFixDelay : d = " << d << endl;
+ return subst("$0(t\\!-\\!$1)", vecname, CS(delay, 7));
+ }
+}
+
+
+/**
+ * Generate code for the delay mecchanism. The generated code depend of the
+ * maximum delay attached to exp and the "less temporaries" switch
+ */
+string DocCompiler::generateDelayVec(Tree sig, const string& exp, const string& ctype, const string& vname, int mxd)
+{
+ string s = generateDelayVecNoTemp(sig, exp, ctype, vname, mxd);
+ if (getCertifiedSigType(sig)->variability() < kSamp) {
+ return exp;
+ } else {
+ return s;
+ }
+}
+
+
+/**
+ * Generate code for the delay mecchanism without using temporary variables
+ */
+string DocCompiler::generateDelayVecNoTemp(Tree sig, const string& exp, const string& ctype, const string& vname, int mxd)
+{
+ assert(mxd > 0);
+
+ //cerr << " entering generateDelayVecNoTemp" << endl;
+
+ string vectorname;
+
+ // if generateVariableStore has already tagged sig, no definition is needed.
+ if(getVectorNameProperty(sig, vectorname)) {
+ return subst("$0(t)", vectorname);
+ } else {
+ fLateq->addRecurSigFormula(subst("$0(t) = $1", vname, exp));
+ setVectorNameProperty(sig, vname);
+ return subst("$0(t)", vname);
+ }
+}
+
+
+/**
+ * Generate code for the delay mecchanism without using temporary variables
+ */
+void DocCompiler::generateDelayLine(const string& ctype, const string& vname, int mxd, const string& exp)
+{
+ //assert(mxd > 0);
+ if (mxd == 0) {
+ fLateq->addRecurSigFormula(subst("$0(t) = $1", vname, exp));
+ } else {
+ fLateq->addRecurSigFormula(subst("$0(t) = $1", vname, exp));
+ }
+}
+
+
+
+
+/****************************************************************
+ User interface element utilities.
+ *****************************************************************/
+
+
+/**
+ * @brief Get the directory of a user interface element.
+ *
+ * Convert the input reversed path tree into a string.
+ * The name of the UI is stripped (the head of the path tree),
+ * the rest of the tree is a list of pointed pairs, where the names
+ * are contained by the tail of these pointed pairs.
+ * Metadatas (begining by '[') are stripped.
+ *
+ * @param[in] pathname The path tree to convert.
+ * @return <string> A directory-like string.
+ */
+string DocCompiler::getUIDir(Tree pathname)
+{
+ //cerr << "Documentator : getUIDir : print(pathname, stdout) = "; print(pathname, stdout); cerr << endl;
+ string s;
+ Tree dir = reverse(tl(pathname));
+ while (!isNil(dir)) {
+ string tmp = tree2str(tl(hd(dir)));
+ if ( (tmp[0] != '[') && (!tmp.empty()) ) {
+ s += tmp + '/';
+ }
+ dir = tl(dir);
+ }
+ return s;
+}
+
+
+/**
+ * @brief Prepare binary user interface elements (button, checkbox).
+ *
+ * - Format a LaTeX output string as a supertabular row with 3 columns :
+ * "\begin{supertabular}{lll}". @see Lateq::printHierarchy
+ * - The UI range is only a set of two values : {0, 1}.
+ * - The UI current value is automatically 0.
+ *
+ * @param[in] name The LaTeX name of the UI signal (eg. "{u_b}_{i}(t)").
+ * @param[in] path The path tree to parse.
+ * @return <string> The LaTeX output string.
+ */
+string DocCompiler::prepareBinaryUI(const string& name, Tree path)
+{
+ string label, unit;
+ getUIDocInfos(path, label, unit);
+ string s = "";
+ label = (label.size()>0) ? ("\\textsf{\""+label+"\"} ") : "";
+ unit = (unit.size()>0) ? ("\\ ("+unit+")") : "";
+ s += label + unit;
+ s += " & $" + name + "$";
+ s += " $\\in$ $\\left\\{\\,0, 1\\,\\right\\}$";
+ s += " & $(\\mbox{" + gDocMathStringMap["defaultvalue"] + "} = 0)$\\\\";
+ return s;
+}
+
+
+/**
+ * @brief Prepare "intervallic" user interface elements (sliders, nentry).
+ *
+ * - Format a LaTeX output string as a supertabular row with 3 columns :
+ * "\begin{supertabular}{lll}". @see Lateq::printHierarchy
+ * - The UI range is an bounded interval : [tmin, tmax].
+ * - The UI current value is tcur.
+ *
+ * @param[in] name The LaTeX name of the UI signal (eg. "{u_s}_{i}(t)").
+ * @param[in] path The path tree to parse.
+ * @param[in] tcur The current UI value tree to convert.
+ * @param[in] tmin The minimum UI value tree to convert.
+ * @param[in] tmax The maximum UI value tree to convert.
+ * @return <string> The LaTeX output string.
+ */
+string DocCompiler::prepareIntervallicUI(const string& name, Tree path, Tree tcur, Tree tmin, Tree tmax)
+{
+ string label, unit, cur, min, max;
+ getUIDocInfos(path, label, unit);
+ cur = docT(tree2float(tcur));
+ min = docT(tree2float(tmin));
+ max = docT(tree2float(tmax));
+
+ string s = "";
+ label = (label.size()>0) ? ("\\textsf{\""+label+"\"} ") : "";
+ unit = (unit.size()>0) ? ("\\ ("+unit+")") : "";
+ s += label + unit;
+ s += " & $" + name + "$";
+ s += " $\\in$ $\\left[\\," + min + ", " + max + "\\,\\right]$";
+ s += " & $(\\mbox{" + gDocMathStringMap["defaultvalue"] + "} = " + cur + ")$\\\\";
+ return s;
+}
+
+
+/**
+ * Get information on a user interface element for documentation.
+ *
+ * @param[in] path The UI full pathname to parse.
+ * @param[out] label The place to store the UI name.
+ * @param[out] unit The place to store the UI unit.
+ */
+void DocCompiler::getUIDocInfos(Tree path, string& label, string& unit)
+{
+ label = "";
+ unit = "";
+
+ map<string, set<string> > metadata;
+ extractMetadata(tree2str(hd(path)), label, metadata);
+
+ set<string> myunits = metadata["unit"];
+// for (set<string>::iterator i = myunits.begin(); i != myunits.end(); i++) {
+// cerr << "Documentator : getUIDocInfos : metadata[\"unit\"] = " << *i << endl;
+// }
+ for (map<string, set<string> >::iterator i = metadata.begin(); i != metadata.end(); i++) {
+ const string& key = i->first;
+ const set<string>& values = i->second;
+ for (set<string>::const_iterator j = values.begin(); j != values.end(); j++) {
+ if(key == "unit") unit += *j;
+ }
+ }
+}
+
+