X-Git-Url: https://scm.cri.ensmp.fr/git/Faustine.git/blobdiff_plain/1059e1cc0c2ecfa237406949aa26155b6a5b9154..66f23d4fabf89ad09adbd4dfc15ac6b5b2b7da83:/interpreter/preprocessor/faust-0.9.47mr3/compiler/documentator/doc.cpp diff --git a/interpreter/preprocessor/faust-0.9.47mr3/compiler/documentator/doc.cpp b/interpreter/preprocessor/faust-0.9.47mr3/compiler/documentator/doc.cpp new file mode 100644 index 0000000..00c956e --- /dev/null +++ b/interpreter/preprocessor/faust-0.9.47mr3/compiler/documentator/doc.cpp @@ -0,0 +1,1161 @@ +/************************************************************************ + ************************************************************************ + 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#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" + + + +#define MAXIDCHARS 5 ///< max numbers (characters) to represent ids (e.g. for directories). + +using namespace std ; + + +/***************************************************************************** + Globals and prototyping + *****************************************************************************/ + +extern Tree gExpandedDefList; +extern map > gMetaDataSet; +extern map gDocMetadatasStringMap; +extern map 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. +static const char* gDocDevSuffix; ///< ".tex" (or .??? - used to choose output device). +static string gCurrentDir; ///< Room to save current directory name. +static const string gLatexheaderfilename = "latexheader.tex"; + +vector gDocVector; ///< Contains parsed trees: DOCTXT, DOCEQN, DOCDGM. + +static struct tm gCompilationDate; + +bool gLstDependenciesSwitch = true; ///< mdoc listing management. +bool gLstMdocTagsSwitch = true; ///< mdoc listing management. +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& 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 handling */ +static void prepareDocEqns( const vector& docBoxes, vector& docCompiledEqnsVector ); ///< Caller function. +static void collectDocEqns( const vector& docBoxes, vector& eqBoxes ); ///< step 0. Feed a vector. +static void mapEvalDocEqn( const vector& eqBoxes, const Tree& env, vector& evalEqBoxes ); ///< step 1. Evaluate boxes. +static void mapGetEqName( const vector& evalEqBoxes, vector& eqNames ); ///< step 2. Get boxes name. +static void calcEqnsNicknames( const vector& eqNames, vector& eqNicknames ); ///< step 3. Calculate nicknames. +static void mapPrepareEqSig( const vector& evalEqBoxes, vector& eqInputs, vector& eqOutputs, vector& eqSigs ); ///< step 4&5. Propagate and prepare signals. +static void mapSetSigNickname( const vector& eqNicknames, const vector& eqInputs, const vector& eqSigs ); ///< step 6. Set signals nicknames. +static void collectEqSigs( const vector& 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& eqSigs, const vector& eqInputs, const vector& eqOutputs, DocCompiler* DC, vector& docCompiledEqnsVector ); ///< step 10. Compile equations. + +/* Secondary sub-functions for 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& pathnames); +vector& docCodeSlicer(const string& faustfile, vector& 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 "-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. + */ +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 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 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. + */ +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. + */ +static void printDocMetadata(const Tree expr, ostream& docout) +{ + if (gMetaDataSet.count(expr)) { + string sep = ""; + set mset = gMetaDataSet[expr]; + + for (set::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. + */ +static void printfaustlistings(ostream& docout) +{ + if (gLstDependenciesSwitch) { + vector 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 tags. + * + * @param[in] faustfile The source file containing the Faust code. + * @param[out] docout The LaTeX output file to print into. + */ +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 tag per line. */ + size_t foundopendoc = s.find(""); + if (foundopendoc != string::npos && gStripDocSwitch) isInsideDoc = true; + + if (isInsideDoc == false) + docout << s << endl; + + size_t foundclosedoc = s.find(""); + 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. + */ +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. + */ +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 trees. + * Second loop for each of these trees, which contain parsed input expressions of 3 types : + * DOCEQN for tags, DOCDGM for 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 parsed content (as boxes). + * @param[in] faustversion The current version of this Faust compiler. + * @param[out] docout The output file to print into. + **/ +static void printdoccontent(const char* svgTopDir, const vector& docVector, const string& faustversion, ostream& docout) +{ + //cerr << endl << "Documentator : printdoccontent : " << docVector.size() << " tags read." << endl; + + /** Equations need to be prepared (named and compiled) before printing. */ + vector docCompiledEqnsVector; + prepareDocEqns( docVector, docCompiledEqnsVector ); ///< Quite a lot of stuff there. + vector::iterator eqn_it = docCompiledEqnsVector.begin(); + + int dgmIndex = 1; ///< For diagram directories numbering. + + vector docMasterCodeMap; + docMasterCodeMap = docCodeSlicer(gMasterDocument, docMasterCodeMap); + + vector::const_iterator doc; + vector::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 parsing..." << endl; + + /** Second level printing loop, on each . */ + 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 parsing." << endl; + + if ( code != docMasterCodeMap.end() && gLstDistributedSwitch ) { + printdocCodeSlices(*code, docout); + } + } +} + + + +/***************************************************************************** + Primary sub-functions for 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 boxes to collect and prepare. + * @param[out] docCompiledEqnsVector The place to store compiled equations. + */ +static void prepareDocEqns(const vector& docBoxes, vector& docCompiledEqnsVector) +{ + vector eqBoxes; collectDocEqns( docBoxes, eqBoxes ); ///< step 0. Feed a vector. + + if(! eqBoxes.empty() ) { + vector evalEqBoxes; mapEvalDocEqn( eqBoxes, gExpandedDefList, evalEqBoxes ); ///< step 1. Evaluate boxes. + vector eqNames; mapGetEqName( evalEqBoxes, eqNames ); ///< step 2. Get boxes name. + vector eqNicknames; calcEqnsNicknames( eqNames, eqNicknames ); ///< step 3. Calculate nicknames. + + vector eqInputs; + vector eqOutputs; + vector 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 found in all faust comments. + * + * @param[in] docBoxes The boxes to filter. + * @param[out] eqBoxes The place to store only boxes. + */ +static void collectDocEqns(const vector& docBoxes, vector& eqBoxes) +{ + int nbdoceqn = 0; + + for (vector::const_iterator doc=docBoxes.begin(); doc tags found." << endl; +} + + +/** + * #1. Evaluate every doc (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. + */ +static void mapEvalDocEqn(const vector& eqBoxes, const Tree& env, vector& evalEqBoxes) +{ + //cerr << "###\n# Documentator : mapEvalDocEqn" << endl; + + for ( vector::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. + */ +static void mapGetEqName(const vector& evalEqBoxes, vector& eqNames) +{ + //cerr << "###\n# Documentator : mapGetEqName" << endl; + + int i = 1; + for( vector::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. + */ +static void calcEqnsNicknames(const vector& eqNames, vector& eqNicknames) +{ + //cerr << "###\n# Documentator : calcEqnsNicknames" << endl; + + vector v; + + for( vector::const_iterator eq = eqNames.begin(); eq < eqNames.end(); eq++ ) { + string init = calcDocEqnInitial(*eq); + v.push_back(init); + /** Check duplicates */ +// for( vector::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::const_iterator eq = eqNames.begin(); eq < eqNames.end(); eq++ ) { +// int c = 0; +// c = count_if(eqNames.begin(), eqNames.end(), bind2nd(equal_to(), *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 . + * + * Call boxPropagateSig, deBruijn2Sym, simplify, and privatise. + * + * @param[in] evalEqBoxes Equations boxes to propagate as signals. + * @param[out] eqSigs The place to store prepared signals. + */ +static void mapPrepareEqSig(const vector& evalEqBoxes, vector& eqInputs, vector& eqOutputs, vector& eqSigs) +{ + //cerr << "###\n# Documentator : mapPrepareEqSig" << endl; + + for( vector::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. + */ +static void mapSetSigNickname(const vector& eqNicknames, const vector& eqInputs, const vector& 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. + */ +static void collectEqSigs(const vector& eqSigs, Tree& superEqList) +{ + //cerr << "###\n# Documentator : collectEqSigs" << endl; + + superEqList = nil; + + for( vector::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. + */ +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 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. + */ +static void mapCompileDocEqnSigs(const vector& eqSigs, const vector& eqInputs, const vector& eqOutputs, DocCompiler* DC, vector& 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 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. + */ +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. + */ +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. + */ +static void printDocEqn(Lateq* ltq, ostream& docout) +{ + ltq->println(docout); + //cerr << "Documentator : printDocEqn : "; ltq->println(cerr); cerr << endl; +} + + +/***************************************************************************** + Sub-function for 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. + */ +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". + */ +vector& docCodeSlicer(const string& faustfile, vector& 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 tag per line! */ + size_t foundopendoc = s.find(""); + + 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(""); + 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. + */ +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. + */ +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(""); + 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. + */ +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. + */ +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 + */ +static int cholddir () +{ + if (chdir(gCurrentDir.c_str()) == 0) { + return 0; + } else { + perror("cholddir"); + exit(errno); + } +} + + +/** + * Get current directory and store it in gCurrentDir. + */ +static void getCurrentDir () +{ + char buffer[FAUST_PATH_MAX]; + gCurrentDir = getcwd (buffer, FAUST_PATH_MAX); +} + + +/** + * Open architecture file. + */ +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 into a + * legal file name. The resulting file name is stored in + * a table of at least chars. Returns the pointer + * for convenience. + */ +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. + **/ +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) + */ +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. + */ +static void copyFaustSources(const char* projname, const vector& 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; +} +