Logo Search packages:      
Sourcecode: faust version File versions  Download package

doc.cpp

Go to the documentation of this file.
/************************************************************************
 ************************************************************************
 FAUST compiler
 Copyright (C) 2009 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.
 ************************************************************************
 ************************************************************************/



/*****************************************************************************
 ******************************************************************************
 
 
                                    The Documentator Language
 
 
 ******************************************************************************
 *****************************************************************************/


/**
 * @file doc.cpp
 * @author Karim Barkati and Yann Orlarey
 * @version 1.0
 * @date 2009
 * @brief Implementation of documentation trees support and printing.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <algorithm>
#include <functional>

#include <iostream>
#include <fstream>
#include <sstream>

#include <map>
#include <string>
#include <vector>

#include "ppbox.hh"
#include "prim2.hh"
#include "doc.hh"
#include "eval.hh"
#include "errormsg.hh"
#include "doc_Text.hh"
#include "sigprint.hh"
#include "propagate.hh"
#include "enrobage.hh"
#include "drawschema.hh"
#include "names.hh"
#include "simplify.hh"
#include "privatise.hh"
#include "recursivness.hh"
#include "sourcereader.hh"
#include "lateq.hh"
#include "doc_compile.hh"
#include "doc_lang.hh"
#include "doc_notice.hh"
#include "doc_autodoc.hh"
#include "compatibility.hh"



00083 #define MAXIDCHARS 5                      ///< max numbers (characters) to represent ids (e.g. for directories).

using namespace std ;


/*****************************************************************************
                                    Globals and prototyping
 *****************************************************************************/

extern Tree                               gExpandedDefList;
extern map<Tree, set<Tree> >  gMetaDataSet;
extern map<string, string>          gDocMetadatasStringMap;
extern map<string, string>          gDocMathStringMap;
extern bool                         gDetailsSwitch;
extern bool                         gStripDocSwitch;
extern string                             gFaustDirectory;
extern string                             gFaustSuperDirectory;
extern string                             gFaustSuperSuperDirectory;
extern string                             gMasterDocument;
extern string                             gMasterName;
extern SourceReader                       gReader;

extern string                             gDocName;                     ///< Contains the filename for out documentation.
00106 static const char*                        gDocDevSuffix;                ///< ".tex" (or .??? - used to choose output device).
00107 static string                             gCurrentDir;                  ///< Room to save current directory name.
static const string                       gLatexheaderfilename = "latexheader.tex";

00110 vector<Tree>                              gDocVector;                   ///< Contains <mdoc> parsed trees: DOCTXT, DOCEQN, DOCDGM.

static struct tm                    gCompilationDate;

00114 bool                                      gLstDependenciesSwitch  = true; ///< mdoc listing management.
00115 bool                                      gLstMdocTagsSwitch            = true; ///< mdoc listing management.
00116 bool                                      gLstDistributedSwitch   = true; ///< mdoc listing management.

enum { langEN, langFR, langIT };
string                        gDocLang;

/* Printing functions */
static void       printlatexheader(istream& latexheader, const string& faustversion, ostream& docout);
static void       printfaustlistings(ostream& docout);
static void       printfaustlisting(string& path, ostream& docout);
static void       printlatexfooter(ostream& docout);
static void       printdoccontent(const char* svgTopDir, const vector<Tree>& docVector, const string& faustversion, ostream& docout);
static void       printfaustdocstamp(const string& faustversion, ostream& docout);
static void       printDocEqn(Lateq* ltq, ostream& docout);
static void       printDocDgm(const Tree expr, const char* svgTopDir, ostream& docout, int i);
static void       printDocMetadata(const Tree expr, ostream& docout);

/* Primary sub-functions for <equation> handling */
static void prepareDocEqns( const vector<Tree>& docBoxes, vector<Lateq*>& docCompiledEqnsVector );          ///< Caller function.
static void collectDocEqns( const vector<Tree>& docBoxes, vector<Tree>& eqBoxes );                                ///< step 0. Feed a vector.
static void mapEvalDocEqn( const vector<Tree>& eqBoxes, const Tree& env, vector<Tree>& evalEqBoxes ); ///< step 1. Evaluate boxes.
static void mapGetEqName( const vector<Tree>& evalEqBoxes, vector<string>& eqNames );                             ///< step 2. Get boxes name.
static void calcEqnsNicknames( const vector<string>& eqNames, vector<string>& eqNicknames );                ///< step 3. Calculate nicknames.
static void mapPrepareEqSig( const vector<Tree>& evalEqBoxes, vector<int>& eqInputs, vector<int>& eqOutputs, vector<Tree>& eqSigs );      ///< step 4&5. Propagate and prepare signals.
static void mapSetSigNickname( const vector<string>& eqNicknames, const vector<int>& eqInputs, const vector<Tree>& eqSigs );  ///< step 6. Set signals nicknames.
static void collectEqSigs( const vector<Tree>& eqSigs, Tree& superEqList );                                             ///< step 7. Collect all signals in a superlist.
static void annotateSuperList( DocCompiler* DC, Tree superEqList );                                               ///< step 8. Annotate superlist.
//static void     calcAndSetLtqNames( Tree superEqList );         ///< step 9. 
static void mapCompileDocEqnSigs( const vector<Tree>& eqSigs, const vector<int>& eqInputs, const vector<int>& eqOutputs, DocCompiler* DC, vector<Lateq*>& docCompiledEqnsVector );  ///< step 10. Compile equations.

/* Secondary sub-functions for <equation> handling */
static string     calcNumberedName(const char* base, int i);
static void       getBoxInputsAndOutputs(const Tree t, int& numInputs, int& numOutputs);
static string     calcDocEqnInitial(const string s);

/* Notice related functions */
static void       initCompilationDate();
static struct tm* getCompilationDate();

/* Files functions */
static int        cholddir ();
static int        mkchdir(const char* dirname);
static int        makedir(const char* dirname);
static void       getCurrentDir();
static istream* openArchFile (const string& filename);
static char*      legalFileName(const Tree t, int n, char* dst);
static string     rmExternalDoubleQuotes(const string& s);
static void       copyFaustSources(const char* projname, const vector<string>& pathnames);
vector<string>& docCodeSlicer(const string& faustfile, vector<string>& codeSlices);
static void       printdocCodeSlices(const string& code, ostream& docout);
static bool       doesFileBeginWithCode(const string& faustfile);

//static void           declareAutoDoc();



/*****************************************************************************
                              Types of Documentation Elements
 *****************************************************************************/

Sym DOCTXT = symbol ("DocTxt");
Tree docTxt(const char* name)       { return tree( DOCTXT, tree(symbol(name)) ); }
bool isDocTxt(Tree t)                     { return t->node() == Node(DOCTXT); }
bool isDocTxt(Tree t0, const char** str)
{
      Tree t1; Sym s;
      if ( isTree(t0, DOCTXT, t1) && isSym(t1->node(), &s) ) {
            *str = name(s);
            return true;
      } else {
            return false;
      }
}

Sym DOCEQN = symbol ("DocEqn");
Tree docEqn(Tree x)                       { return tree(DOCEQN, x);           }
bool isDocEqn(Tree t, Tree& x)            { return isTree(t, DOCEQN, x);      }

Sym DOCDGM = symbol ("DocDgm");
Tree docDgm(Tree x)                       { return tree(DOCDGM, x);           }
bool isDocDgm(Tree t, Tree& x)            { return isTree(t, DOCDGM, x);      }

Sym DOCNTC = symbol ("DocNtc");
Tree docNtc()                                   { return tree(DOCNTC);              }
bool isDocNtc(Tree t)                     { return isTree(t, DOCNTC);   }

Sym DOCLST = symbol ("DocLst");
Tree docLst()                                   { return tree(DOCLST);              }
bool isDocLst(Tree t)                     { return isTree(t, DOCLST);   }

Sym DOCMTD = symbol ("DocMtd");
Tree docMtd(Tree x)                       { return tree(DOCMTD, x);           }
bool isDocMtd(Tree t, Tree& x)            { return isTree(t, DOCMTD, x);      }

//string getDocTxt(Tree t)                { return hd(t)->branch(0); }



/*****************************************************************************
                        Main Printing Function for the Documentation
 *****************************************************************************/


/**
 * @brief The entry point to generate faust doc files.
 *
 * The entry point to generate the output LaTeX file, stored in the directory "<projname>-math/".
 * This file eventually references images for diagrams, generated in SVG subdirectories.
 * The device system was adapted from drawSchema's device system.
 *
 * @param[in]     projname          Basename of the new doc directory ("*-math").
 * @param[in]     docdev                  The doc device; only ".tex" is supported for the moment.
 * @param[in]     faustversion      The current version of this Faust compiler.
 */
00229 void printDoc(const char* projname, const char* docdev, const char* faustversion)
{
      gDocDevSuffix = docdev;
      
      /** File stuff : create doc directories and a tex file. */
      //cerr << "Documentator : printDoc : gFaustDirectory = '" << gFaustDirectory << "'" << endl;
      //cerr << "Documentator : printDoc : gFaustSuperDirectory = '" << gFaustSuperDirectory << "'" << endl;
      //cerr << "Documentator : printDoc : gFaustSuperSuperDirectory = '" << gFaustSuperSuperDirectory << "'" << endl;
      //cerr << "Documentator : printDoc : gCurrentDir = '" << gCurrentDir << "'" << endl;
      
      makedir(projname);                  // create a top directory to store files
      
      string svgTopDir = subst("$0/svg", projname);
      makedir(svgTopDir.c_str()); // create a directory to store svg-* subdirectories.
      
      string cppdir = subst("$0/cpp", projname);
      makedir(cppdir.c_str());      // create a cpp directory.
      
      string pdfdir = subst("$0/pdf", projname);
      makedir(pdfdir.c_str());      // create a pdf directory.
      
      /* Copy all Faust source files into an 'src' sub-directory. */
      vector<string> pathnames = gReader.listSrcFiles();
      copyFaustSources(projname, pathnames);
      
      string texdir = subst("$0/tex", projname);
      mkchdir(texdir.c_str());      // create a directory and move into.

       /** Create THE mathdoc tex file. */
      ofstream docout(subst("$0.$1", gDocName, docdev).c_str());
      cholddir();                         // return to current directory
      
      /** Init and load translation file. */
      loadTranslationFile(gDocLang);
      
      /** Simulate a default doc if no <mdoc> tag detected. */
      if (gDocVector.empty()) { declareAutoDoc(); }   
      
      /** Printing stuff : in the '.tex' ouptut file, eventually including SVG files. */
      printfaustdocstamp(faustversion, docout);                               ///< Faust version and compilation date (comment).
      istream* latexheader = openArchFile(gLatexheaderfilename);
      printlatexheader(*latexheader, faustversion, docout);                               ///< Static LaTeX header (packages and setup).
      printdoccontent(svgTopDir.c_str(), gDocVector, faustversion, docout);         ///< Generate math contents (main stuff!).
      printlatexfooter(docout);                                                           ///< Static LaTeX footer.
}



/*****************************************************************************
                  LaTeX basic printing functions of the Documentation
 *****************************************************************************/

/** 
 * Print a static LaTeX header. 
 *
 * @param[in]     latexheader       The file containing the static part of the LaTeX header.
 * @param[in]     faustversion      The current version of this Faust compiler.
 * @param[out]    docout                  The LaTeX output file to print into.
 */
00288 static void printlatexheader(istream& latexheader, const string& faustversion, ostream& docout)
{     
      string      s;
      while(getline(latexheader, s)) docout << s << endl;
      
      /** Specific LaTeX macros for Faust */
      docout << "\\newcommand{\\faustfilename}{" << gMasterDocument << "}" << endl;
      docout << "\\newcommand{\\faustdocdir}{" << gMasterName << "-mdoc}" << endl;
      docout << "\\newcommand{\\faustprogname}{" << gMasterName << "}" << endl;
      docout << "\\newcommand{\\faustversion}{" << faustversion << "}" << endl;
      char datebuf [150];
      strftime (datebuf, 150, "%B %d, %Y", getCompilationDate());
      docout << "\\newcommand{\\faustdocdate}{" << datebuf << "}" << endl;
      
      docout << endl << "\\begin{document}" << endl;
}


/**
 * @Brief Print a metadata set.
 * 
 * Each metadata is a set, in order to handle multiple items,
 * like multiple authors, even if most of metadatas have
 * unique items.
 * 
 * @param[in]     expr        Parsed metadata keyname, as boxes tree.
 * @param[out]    docout            The output file to print into.
 */
00316 static void printDocMetadata(const Tree expr, ostream& docout)
{
      if (gMetaDataSet.count(expr)) {
            string sep = "";
            set<Tree> mset = gMetaDataSet[expr];
            
            for (set<Tree>::iterator j = mset.begin(); j != mset.end(); j++) {
                  docout << sep << rmExternalDoubleQuotes(tree2str(*j));
                  sep = ", ";
            }
      }
}


/** 
 * Print listings of each Faust code ".dsp" files,
 * calling the 'printfaustlisting' function.
 *
 * @param[out]    docout            The LaTeX output file to print into.
 */
00336 static void printfaustlistings(ostream& docout)
{     
      if (gLstDependenciesSwitch) {
            vector<string> pathnames = gReader.listSrcFiles();
            for (unsigned int i=0; i< pathnames.size(); i++) {
                  printfaustlisting(pathnames[i], docout);
            }
      } else {
            printfaustlisting(gMasterDocument, docout);
      }
}


/** 
 * Print a listing of the Faust code, in a LaTeX "listing" environment.
 * Strip content of <mdoc> tags.
 *
 * @param[in]     faustfile   The source file containing the Faust code.
 * @param[out]    docout            The LaTeX output file to print into.
 */
00356 static void printfaustlisting(string& faustfile, ostream& docout)
{     
      string      s;
      ifstream src;
      
      //cerr << "Documentator : printfaustlisting : Opening file '" << faustfile << "'" << endl;
      src.open(faustfile.c_str(), ifstream::in);
      
      docout << endl << "\\bigskip\\bigskip" << endl;
      docout << "\\begin{lstlisting}[caption=\\texttt{" << filebasename(faustfile.c_str()) << "}]" << endl;

      bool isInsideDoc = false;
      
      if (faustfile != "" && src.good()) {
            while(getline(src, s)) { /** We suppose there's only one <mdoc> tag per line. */
                  size_t foundopendoc  = s.find("<mdoc>");
                  if (foundopendoc != string::npos && gStripDocSwitch) isInsideDoc = true;
                  
                  if (isInsideDoc == false)
                        docout << s << endl;
                  
                  size_t foundclosedoc = s.find("</mdoc>");
                  if (foundclosedoc != string::npos && gStripDocSwitch) isInsideDoc = false;
            }
      } else {
            cerr << "ERROR : can't open faust source file " << faustfile << endl;
            exit(1);
      }
      
      docout << "\\end{lstlisting}" << endl << endl;
}


/** 
 * Print the static LaTeX footer. 
 *
 * @param[out]    docout            The LaTeX output file to print into.
 */
00394 static void printlatexfooter(ostream& docout)
{
      docout << endl << "\\end{document}" << endl << endl;
}


/** 
 * Print a "doc stamp" in the LaTeX document :
 * - the Faust version,
 * - the date of doc compilation,
 * - faust's web site URL.
 *
 * @param[in]     faustversion      The current version of this Faust compiler.
 * @param[out]    docout                  The LaTeX output file to print into.
 */
00409 static void printfaustdocstamp(const string& faustversion, ostream& docout)
{
      char datebuf [150];
      strftime (datebuf, 150, "%c", getCompilationDate());
      
      docout << "%% This documentation was generated with Faust version " << faustversion << endl;
      docout << "%% " << datebuf << endl;
      docout << "%% http://faust.grame.fr" << endl << endl;
}



/*****************************************************************************
                  Main loop : launches prepare, evaluate, and print functions
 *****************************************************************************/

/**
 * @brief Main documentator loop.
 *
 * First loop on gDocVector, which contains the faust <mdoc> trees.
 * Second loop for each of these <mdoc> trees, which contain parsed input expressions of 3 types :
 * DOCEQN for <equation> tags, DOCDGM for <diagram> tags, and DOCTXT for direct LaTeX text (no tag).
 * - DOCTXT expressions printing is trivial.
 * - DOCDGM expressions printing calls 'printDocDgm' to generate SVG files and print LaTeX "figure" code.
 * - DOCEQN expressions printing calls 'printDocEqn' after an important preparing work 
 *   has been done by 'prepareDocEqns'.
 *
 * @param[in]     projname          Basename of the new doc directory ("*-math").
 * @param[in]     docVector         Contains all <mdoc> parsed content (as boxes).
 * @param[in]     faustversion      The current version of this Faust compiler.
 * @param[out]    docout                  The output file to print into.
 **/
00441 static void printdoccontent(const char* svgTopDir, const vector<Tree>& docVector, const string& faustversion, ostream& docout)
{
      //cerr << endl << "Documentator : printdoccontent : " << docVector.size() << " <mdoc> tags read." << endl;
      
      /** Equations need to be prepared (named and compiled) before printing. */
      vector<Lateq*>  docCompiledEqnsVector;
      prepareDocEqns( docVector, docCompiledEqnsVector ); ///< Quite a lot of stuff there.
      vector<Lateq*>::iterator eqn_it = docCompiledEqnsVector.begin();
      
      int dgmIndex = 1;             ///< For diagram directories numbering.

      vector<string> docMasterCodeMap;
      docMasterCodeMap = docCodeSlicer(gMasterDocument, docMasterCodeMap);
      
      vector<Tree>::const_iterator doc;
      vector<string>::const_iterator code;
      code = docMasterCodeMap.begin();
      
      if(doesFileBeginWithCode(gMasterDocument) && (! docMasterCodeMap.empty()) && gLstDistributedSwitch ) {
            printdocCodeSlices(*code, docout);
            code++;
      }
      
      /** First level printing loop, on docVector. */
      for (doc=docVector.begin(); doc<docVector.end(); doc++, code++) {
            
            Tree L = reverse(*doc);
            //cerr << "Entering into <mdoc> parsing..." << endl; 
            
            /** Second level printing loop, on each <mdoc>. */
            while (isList(L)) {
                  Tree expr;
                  if ( isDocEqn(hd(L), expr) ) { ///< After equations are well prepared and named.
                        printDocEqn(*eqn_it++, docout);
                  }
                  else if ( isDocDgm(hd(L), expr) ) { 
                        printDocDgm(expr, svgTopDir, docout, dgmIndex++);
                  }
                  else if ( isDocMtd(hd(L), expr) ) { 
                        printDocMetadata(expr, docout);
                  }
                  else if ( isDocTxt(hd(L)) ) { 
                        docout << *hd(L)->branch(0); // Directly print registered doc text.
                  }
                  else if ( isDocNtc(hd(L)) ) { 
                        printDocNotice(faustversion, docout);
                  }
                  else if ( isDocLst(hd(L)) ) { 
                        printfaustlistings(docout);
                  }
                  else { 
                        cerr << "ERROR : " << *hd(L) << " is not a valid documentation type." << endl; 
                  }
                  L = tl(L);
            }
            //cerr << " ...end of <mdoc> parsing." << endl; 
            
            if ( code != docMasterCodeMap.end() && gLstDistributedSwitch ) {
                  printdocCodeSlices(*code, docout);
            }
      }
}



/*****************************************************************************
                  Primary sub-functions for <equation> handling
 *****************************************************************************/

/**
 * @brief Caller function for all steps of doc equations preparation.
 *  
 * Note : many of the functions called put their result into their last argument
 * in a "source / destination" manner, 
 * the "destination" being declared before the function call. 
 *
 * @param[in]     docBoxes                      The <mdoc> boxes to collect and prepare.
 * @param[out]    docCompiledEqnsVector   The place to store compiled equations.
 */
00520 static void prepareDocEqns(const vector<Tree>& docBoxes, vector<Lateq*>& docCompiledEqnsVector)
{     
      vector<Tree>      eqBoxes;          collectDocEqns( docBoxes, eqBoxes );            ///< step 0. Feed a vector.
      
      if(! eqBoxes.empty() ) {
            vector<Tree>      evalEqBoxes;      mapEvalDocEqn( eqBoxes, gExpandedDefList, evalEqBoxes );    ///< step 1. Evaluate boxes.
            vector<string>    eqNames;          mapGetEqName( evalEqBoxes, eqNames );           ///< step 2. Get boxes name.
            vector<string>    eqNicknames;      calcEqnsNicknames( eqNames, eqNicknames );      ///< step 3. Calculate nicknames.
            
            vector<int>       eqInputs;
            vector<int>       eqOutputs;
            vector<Tree>      eqSigs;                 mapPrepareEqSig( evalEqBoxes, eqInputs, eqOutputs, eqSigs );      ///< step 4&5. Propagate and prepare signals.
            mapSetSigNickname( eqNicknames, eqInputs, eqSigs );                                                   ///< step 6. Set signals nicknames.
            Tree              superEqList;      collectEqSigs( eqSigs, superEqList );           ///< step 7. Collect all signals in a superlist.
            
            DocCompiler* DC = new DocCompiler(0, 0);
            annotateSuperList( DC, superEqList );                                                           ///< step 8. Annotate superEqList.
            //calcAndSetLtqNames( superEqList );                                                            ///< step 9. (directly in 10.)
            mapCompileDocEqnSigs( eqSigs, eqInputs, eqOutputs, DC, docCompiledEqnsVector );           ///< step 10. Compile every signal.
      }
}


/**
 * #0. Collect every <equation> found in all <mdoc> faust comments.
 *
 * @param[in]     docBoxes    The <mdoc> boxes to filter.
 * @param[out]    eqBoxes           The place to store only <equation> boxes.
 */
00549 static void collectDocEqns(const vector<Tree>& docBoxes, vector<Tree>& eqBoxes)
{
      int nbdoceqn = 0;
      
      for (vector<Tree>::const_iterator doc=docBoxes.begin(); doc<docBoxes.end(); doc++) {
            Tree L = reverse(*doc);
            Tree expr;
            while (isList(L)) {
                  if ( isDocEqn(hd(L), expr) ) {
                        eqBoxes.push_back(expr);
                        nbdoceqn++;
                  }
                  L = tl(L);
            }
      }
      //cerr << "Documentator : collectDocEqns : " << nbdoceqn << " <equation> tags found." << endl;
}


/**
 * #1. Evaluate every doc <equation> (evaluation replaces abstractions by symbolic boxes).
 *
 * @param[in]     eqBoxes           The boxes to evaluate.
 * @param[in]     env               The environment for the evaluation.
 * @param[out]    evalEqBoxes The place to store evaluated equations boxes.
 */
00575 static void mapEvalDocEqn(const vector<Tree>& eqBoxes, const Tree& env, vector<Tree>& evalEqBoxes)
{
      //cerr << "###\n# Documentator : mapEvalDocEqn" << endl;
      
      for ( vector<Tree>::const_iterator eq=eqBoxes.begin(); eq < eqBoxes.end(); eq++)
      {
            evalEqBoxes.push_back(evaldocexpr( *eq, env ));
      }
      //cerr << "Documentator : end of mapEvalDocEqn\n---" << endl;
}


/**
 * #2. Get name if exists, else create one, and store it.
 *
 * @param[in]     evalEqBoxes Evaluated box trees, eventually containing an equation name.
 * @param[out]    eqNames           The place to store equations names.
 */
00593 static void mapGetEqName(const vector<Tree>& evalEqBoxes, vector<string>& eqNames)
{
      //cerr << "###\n# Documentator : mapGetEqName" << endl;
      
      int i = 1;
      for( vector<Tree>::const_iterator eq = evalEqBoxes.begin(); eq < evalEqBoxes.end(); eq++, i++ ) {
            Tree id;
            string s;
            int n,m; getBoxType(*eq, &n, &m); // eq name only for bd without inputs
            if ( n==0 && getDefNameProperty(*eq, id) ) {
                  s = tree2str(id);
            }
            else {
                  s = calcNumberedName("doceqn-", i);
            }           
            eqNames.push_back( s ) ;
      }
      //cerr << "Documentator : end of mapGetEqName\n---" << endl;
}


/**
 * #3. Calculate a nickname for each equation and store it.
 *
 * @param[in]     eqNames           Equations names to parse.
 * @param[out]    eqNicknames The place to store calculated nicknames.
 *
 * @todo Should check unicity : check whether several names share the same initial,
 * or else capture consonants for example.
 */
00623 static void calcEqnsNicknames(const vector<string>& eqNames, vector<string>& eqNicknames)
{
      //cerr << "###\n# Documentator : calcEqnsNicknames" << endl;
      
      vector<string> v;
      
      for( vector<string>::const_iterator eq = eqNames.begin(); eq < eqNames.end(); eq++ ) {
            string init = calcDocEqnInitial(*eq);
            v.push_back(init);
            /** Check duplicates */
//          for( vector<string>::iterator it = v.begin(); it < v.end()-1; ++it ) {
//                if (init == *it) {
//                      //cerr << "!! Warning Documentator : calcEqnsNicknames : duplicates \"" << init << "\"" << endl;
//                }
//          }
            eqNicknames.push_back(init);
      }
      
//    for( vector<string>::const_iterator eq = eqNames.begin(); eq < eqNames.end(); eq++ ) {
//          int c = 0;
//          c = count_if(eqNames.begin(), eqNames.end(), bind2nd(equal_to<string>(), *eq));
//          if (c > 0) { 
//                cerr << "- Duplicate nickname !! " << *eq << endl; 
//          } else {
//                cerr << "(no duplicate) " << *eq << endl;
//          }
//    }
      
      //cerr << "Documentator : end of calcEqnsNicknames\n---" << endl;
}


/**
 * #4&5. Propagate and prepare every doc <equation>.
 *
 * Call boxPropagateSig, deBruijn2Sym, simplify, and privatise.
 *
 * @param[in]     evalEqBoxes       Equations boxes to propagate as signals.
 * @param[out]    eqSigs                  The place to store prepared signals.
 */
00663 static void mapPrepareEqSig(const vector<Tree>& evalEqBoxes, vector<int>& eqInputs, vector<int>& eqOutputs, vector<Tree>& eqSigs)
{
      //cerr << "###\n# Documentator : mapPrepareEqSig" << endl;
      
      for( vector<Tree>::const_iterator eq = evalEqBoxes.begin(); eq < evalEqBoxes.end(); eq++ ) {
            
            int numInputs, numOutputs;
            getBoxInputsAndOutputs(*eq, numInputs, numOutputs);
            //cerr << numInputs <<" ins and " << numOutputs <<" outs" << endl;
            eqInputs.push_back(numInputs);
            eqOutputs.push_back(numOutputs);
            
            Tree lsig1 = boxPropagateSig( nil, *eq , makeSigInputList(numInputs) );
            //cerr << "output signals are : " << endl;  printSignal(lsig1, stderr);
            
            Tree lsig2 = deBruijn2Sym(lsig1);   ///< Convert debruijn recursion into symbolic recursion
            Tree lsig3 = simplify(lsig2);       ///< Simplify by executing every computable operation
        //Tree lsig4 = privatise(lsig3);        ///< Un-share tables with multiple writers
        Tree lsig4 = docTableConvertion(lsig3);       ///< convert regular tables into special doctables
                                                    ///< (regular tables are difficult to translate to equations)

            eqSigs.push_back(lsig4);
      }
      //cerr << "Documentator : end of mapPrepareEqSig\n---" << endl;
}


/**
 * #6. Set signals nicknames.
 *
 * Do nothing for the moment !
 * @param[in]     eqNicknames       Contains previously calculated nicknames.
 * @param[out]    eqSigs                  The signals to tag with a NICKNAMEPROPERTY.
 */
00697 static void mapSetSigNickname(const vector<string>& eqNicknames, const vector<int>& eqInputs, const vector<Tree>& eqSigs)
{
      //cerr << "###\n# Documentator : mapSetSigNickname" << endl;

//    Do nothing for the moment...
//    for( unsigned int i=0; i < eqSigs.size(); i++ ) {
//          if (eqInputs[i] == 0) // Only "generators" should be finally named with user equation (nick)name.
//                setSigListNickName(eqSigs[i], eqNicknames[i]);
//    }
      //cerr << "Documentator : end of mapSetSigNickname\n---" << endl;
}


/**
 * #7. Collect each prepared list of signals to construct a super list.
 *
 * @param[in]     eqSigs                  Contains well-prepared and nicknamed signals.
 * @param[out]    superEqList       The root where to 'cons' signals all together.
 */
00716 static void collectEqSigs(const vector<Tree>& eqSigs, Tree& superEqList)
{
      //cerr << "###\n# Documentator : collectEqSigs" << endl;
      
      superEqList = nil;
      
      for( vector<Tree>::const_iterator it = eqSigs.begin(); it < eqSigs.end(); ++it ) {
            superEqList = cons( *it, superEqList );
      }
      //printSignal(superEqList, stdout, 0);
      
      //cerr << endl << "Documentator : end of collectEqSigs\n---" << endl;
}


/**
 * #8. Annotate superEqList (to find candidate signals to be named later).
 *
 * @param[in]     DC                The signals compiler.
 * @param[out]    superEqList The super equations signal tree to annotate. 
 */
00737 static void annotateSuperList(DocCompiler* DC, Tree superEqList)
{
      DC->annotate(superEqList);
}


///**
// * #9. Calculated and set lateq (LaTeX equation) names.
// * Note : Transfered into mapCompileDocEqnSigs (DocCompiler::compileMultiSignal).
// */
//static void     calcAndSetLtqNames(Tree superEqList)
//{
//    
//}


/**
 * #10. Name and compile prepared doc <equation> signals.
 *
 * @param[in]     eqSigs                              Contains well-prepared and nicknamed signals.
 * @param[in]     DC                                  The signals compiler.
 * @param[out]    docCompiledEqnsVector   The place to store each compiled Lateq* object.
 */
00760 static void mapCompileDocEqnSigs(const vector<Tree>& eqSigs, const vector<int>& eqInputs, const vector<int>& eqOutputs, DocCompiler* DC, vector<Lateq*>& docCompiledEqnsVector)
{
      //cerr << "###\n# Documentator : mapCompileDocEqnSigs" << endl;
      
      for( unsigned int i=0; i < eqSigs.size(); i++ ) {
            
            //          docCompiledEqnsVector.push_back( DC->compileMultiSignal(*it, 0) );
            docCompiledEqnsVector.push_back( DC->compileLateq(eqSigs[i], new Lateq(eqInputs[i], eqOutputs[i])) );
      }
      
      //cerr << "Documentator : end of mapCompileDocEqnSigs\n---" << endl;
}



/*****************************************************************************
                        Secondary sub-functions for <equation> handling
 *****************************************************************************/


/**
 * Calculate an appropriate nickname for equations,
 * from previous names.
 *
 * @param   The string to parse.
 * @return  Essentially returns the initial character, 
 * except "Y" for "process", and "Z" for unnamed equations.
 */
00788 static string calcDocEqnInitial(const string s)
{
      string nn;
      if(s == "process")
            nn = "Y";
      else if (s.substr(0,6) == "doceqn")
            nn = "Z";
      else
            nn += toupper(s[0]);
      return nn;
}


/**
 * Just get the number of inputs and the number of outputs of a box.
 *
 * @param[in]     t                 The box tree to get inputs and outputs from.
 * @param[out]    numInputs   The place to store the number of inputs.
 * @param[out]    numOutputs  The place to store the number of outputs.
 */
00808 static void getBoxInputsAndOutputs(const Tree t, int& numInputs, int& numOutputs)
{
      if (!getBoxType(t, &numInputs, &numOutputs)) {
            cerr << "ERROR during the evaluation of t : " << boxpp(t) << endl;
            exit(1);
      }
      //cerr << "Documentator : " << numInputs <<" inputs and " << numOutputs <<" outputs for box : " << boxpp(t) << endl;
}


/**
 * Print doc equations, following the Lateq::println method.
 *
 * @param[in]     ltq         The object containing compiled LaTeX code of equations.
 * @param[out]    docout      The output file to print into.
 */
00824 static void printDocEqn(Lateq* ltq, ostream& docout) 
{
      ltq->println(docout);
      //cerr << "Documentator : printDocEqn : "; ltq->println(cerr); cerr << endl;
}


/*****************************************************************************
                              Sub-function for <diagram> handling
 *****************************************************************************/

/**
 * @brief Doc diagrams handling.
 * 
 * Three steps :
 * 1. evaluate expression
 * 2. call svg drawing in the appropriate directory
 * 3. print latex figure code with the appropriate directory reference
 *
 * @param[in]     expr        Parsed input expression, as boxes tree.
 * @param[in]     svgTopDir   Basename of the new doc directory ("*-math/svg").
 * @param[out]    docout            The output file to print into.
 */
00847 static void printDocDgm(const Tree expr, const char* svgTopDir, ostream& docout, int i)
{
      /** 1. Evaluate expression. */
      Tree docdgm = evaldocexpr(expr, gExpandedDefList);
      if (gErrorCount > 0) {
            cerr << "Total of " << gErrorCount << " errors during evaluation of : diagram docdgm = " << boxpp(docdgm) << ";\n";
            exit(1);
      }
      
      /**
       * 2. Draw the diagram after its evaluation, in SVG.
       * Warning : pdflatex can't directly include SVG files !
       */
      char dgmid[MAXIDCHARS+1];
      sprintf(dgmid, "%02d", i);
      string thisdgmdir = subst("$0/svg-$1", svgTopDir, dgmid);
      //cerr << "Documentator : printDocDgm : drawSchema in '" << gCurrentDir << "/" << thisdgmdir << "'" << endl;
      
      drawSchema( docdgm, thisdgmdir.c_str(), "svg" );
      
      /** 3. Print LaTeX figure code. */
      char temp[1024];
      const string dgmfilename = legalFileName(docdgm, 1024, temp);
      //docout << "figure \\ref{figure" << i << "}";
      docout << "\\begin{figure}[ht!]" << endl;
      docout << "\t\\centering" << endl;
      docout << "\t\\includegraphics[width=\\textwidth]{" << subst("../svg/svg-$0/", dgmid) << dgmfilename << "}" << endl;
      docout << "\t\\caption{" << gDocMathStringMap["dgmcaption"] << " \\texttt{" << dgmfilename << "}}" << endl;
      docout << "\t\\label{figure" << i << "}" << endl;
      docout << "\\end{figure}" << endl << endl;
      
      /** 4. Warn about naming interferences (in the notice). */
      gDocNoticeFlagMap["nameconflicts"] = true;
      gDocNoticeFlagMap["svgdir"] = true;
}



/*****************************************************************************
                                          Other sub-functions
 *****************************************************************************/


/**
 * Slice faust code between "mdoc" sections.
 *
 * @param[in]     faustfile   Name of the input faust file to parse.
 * @param[in]     codeSlices  The place to store code "slices".
 */
00896 vector<string>& docCodeSlicer(const string& faustfile, vector<string>& codeSlices)
{
      string      s;
      ifstream src;
      src.open(faustfile.c_str(), ifstream::in);
      string tmp = "";
      
      bool isInsideDoc = false;
      
      if (faustfile != "" && src.good()) {
            while(getline(src, s)) { /** Caution: we suppose there's only one <mdoc> tag per line! */
                  size_t foundopendoc  = s.find("<mdoc>");
                  
                  if (foundopendoc != string::npos) { 
                        if (isInsideDoc == false) { /** A change has come. ;) */
                              if (! tmp.empty() ) {
                                    codeSlices.push_back(tmp); }
                              tmp = "";
                        }
                        isInsideDoc = true;  
                  }
                  
                  if (isInsideDoc == false) {
                        tmp += s + '\n';
                  }
                  
                  size_t foundclosedoc = s.find("</mdoc>");
                  if (foundclosedoc != string::npos) isInsideDoc = false;
            }
      } else {
            cerr << "ERROR : can't open faust source file " << faustfile << endl;
            exit(1);
      }
      return codeSlices;
}
                  

/**
 * Print faust code inside a listing environment.
 * 
 * @param[in]     code        Faust code as a string (may contain '\n' characters).
 * @param[out]    docout            The output file to print into.
 */
00939 static void printdocCodeSlices(const string& code, ostream& docout)
{
      if ( ! code.empty() ) {
            docout << endl << "\\begin{lstlisting}[numbers=none, frame=none, basicstyle=\\small\\ttfamily, backgroundcolor=\\color{yobg}]" << endl;
            docout << code << endl;
            docout << "\\end{lstlisting}" << endl << endl;
      }
}


/**
 * Test whether a file does begin with some faust code or not.
 *
 * @param[in]     faustfile   Name of the input faust file to parse.
 */
00954 static bool doesFileBeginWithCode(const string& faustfile)
{
      string      s;
      ifstream src;
      src.open(faustfile.c_str(), ifstream::in);
      
      if (faustfile != "" && src.good()) {
            getline(src, s);
            size_t foundopendoc = s.find("<mdoc>");
            if(int(foundopendoc)==0) {
                  return false;
            } else {
                  return true;
            }
      } else {
            cerr << "ERROR : can't open faust source file " << faustfile << endl;
            exit(1);
      }
}     



//------------------------ dealing with files -------------------------


/**
 * Create a new directory in the current one.
 */
00982 static int makedir(const char* dirname)
{
      char  buffer[FAUST_PATH_MAX];
      gCurrentDir = getcwd (buffer, FAUST_PATH_MAX);
      
      if ( gCurrentDir.c_str() != 0) {
            int status = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
            if (status == 0 || errno == EEXIST) {
                  return 0;
            }
      }
      perror("makedir");
      exit(errno);
}


/**
 * Create a new directory in the current one, 
 * then 'cd' into this new directory.
 * 
 * @remark
 * The current directory is saved to be later restaured.
 */
01005 static int mkchdir(const char* dirname)
{
      char  buffer[FAUST_PATH_MAX];
      gCurrentDir = getcwd (buffer, FAUST_PATH_MAX);

      if ( gCurrentDir.c_str() != 0) {
            int status = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
            if (status == 0 || errno == EEXIST) {
                  if (chdir(dirname) == 0) {
                        return 0;
                  }
            }
      }
      perror("mkchdir");
      exit(errno);
}


/**
 * Switch back to the previously stored current directory
 */
01026 static int cholddir ()
{
      if (chdir(gCurrentDir.c_str()) == 0) {
            return 0;
      } else {
            perror("cholddir");
            exit(errno);
      }
}


/**
 * Get current directory and store it in gCurrentDir.
 */
01040 static void getCurrentDir ()
{
      char  buffer[FAUST_PATH_MAX];
    gCurrentDir = getcwd (buffer, FAUST_PATH_MAX);
}


/**
 * Open architecture file.
 */
01050 static istream* openArchFile (const string& filename)
{
      istream* file;
      getCurrentDir();  // Save the current directory.
      //cerr << "Documentator : openArchFile : Opening input file  '" << filename << "'" << endl;
      if ( (file = open_arch_stream(filename.c_str())) ) {
            //cerr << "Documentator : openArchFile : Opening '" << filename << "'" << endl;
      } else {
            cerr << "ERROR : can't open architecture file " << filename << endl;
            exit(1);
      }
      cholddir();             // Return to current directory.
      return file;
}


/**
 * Transform the definition name property of tree <t> into a
 * legal file name.  The resulting file name is stored in
 * <dst> a table of at least <n> chars. Returns the <dst> pointer
 * for convenience.
 */
01072 static char* legalFileName(const Tree t, int n, char* dst)
{
      Tree  id;
      int   i=0;
      if (getDefNameProperty(t, id)) {
            const char*       src = tree2str(id);
            for (i=0; isalnum(src[i]) && i<16; i++) {
                  dst[i] = src[i];
            }
      }
      dst[i] = 0;
      if (strcmp(dst, "process") != 0) { 
            // if it is not process add the hex address to make the name unique
            snprintf(&dst[i], n-i, "-%p", t);
      }
      return dst;
}

/**
 * Simply concat a string with a number in a "%03d" format.
 * The number has MAXIDCHARS characters. 
 **/
01094 static string calcNumberedName(const char* base, int i)
{
      char nb[MAXIDCHARS+1];
      sprintf(nb, "%03d", i);
      return subst("$0$1", base, nb);
}

/**
 * Remove the leading and trailing double quotes of a string
 * (but not those in the middle of the string)
 */
01105 static string rmExternalDoubleQuotes(const string& s)
{
    size_t i = s.find_first_not_of("\"");
    size_t j = s.find_last_not_of("\"");
      
    if ( (i != string::npos) & (j != string::npos) ) {
        return s.substr(i, 1+j-i);
    } else {
        return "";
    }
}


/** 
 * Copy all Faust source files into an 'src' subdirectory.
 *
 * @param[in]     projname          Basename of the new doc directory ("*-math").
 * @param[in]     pathnames         The paths list of the source files to copy.
 */
01124 static void copyFaustSources(const char* projname, const vector<string>& pathnames)
{
      string srcdir = subst("$0/src", projname);
      //cerr << "Documentator : copyFaustSources : Creating directory '" << srcdir << "'" << endl;
      makedir(srcdir.c_str());      // create a directory.
            
      for (unsigned int i=0; i< pathnames.size(); i++) {
            ifstream src;
            ofstream dst;
            string faustfile = pathnames[i];
            string copy = subst("$0/$1", srcdir, filebasename(faustfile.c_str()));
            //cerr << "Documentator : copyFaustSources : Opening input file  '" << faustfile << "'" << endl;
            //cerr << "Documentator : copyFaustSources : Opening output file '" << copy << "'" << endl;
            src.open(faustfile.c_str(), ifstream::in);
            dst.open(copy.c_str(), ofstream::out);
            string      s;
            while ( getline(src,s) ) dst << s << endl;
      }
}


//------------------------ date managment -------------------------


static void initCompilationDate()
{
      time_t now;
      
      time(&now);
      gCompilationDate = *localtime(&now);
}

static struct tm* getCompilationDate()
{
      initCompilationDate();
      return &gCompilationDate;
}


Generated by  Doxygen 1.6.0   Back to index