New Makefile for libsndfile-ocaml library integration; Makefiles chain modified.
[Faustine.git] / interpretor / preprocessor / faust-0.9.47mr3 / compiler / documentator / doc.cpp
1 /************************************************************************
2 ************************************************************************
3 FAUST compiler
4 Copyright (C) 2009 GRAME, Centre National de Creation Musicale
5 ---------------------------------------------------------------------
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 ************************************************************************
20 ************************************************************************/
21
22
23
24 /*****************************************************************************
25 ******************************************************************************
26
27
28 The Documentator Language
29
30
31 ******************************************************************************
32 *****************************************************************************/
33
34
35 /**
36 * @file doc.cpp
37 * @author Karim Barkati and Yann Orlarey
38 * @version 1.0
39 * @date 2009
40 * @brief Implementation of documentation trees support and printing.
41 */
42
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <sys/stat.h>
47 #include <time.h>
48 #include <algorithm>
49 #include <functional>
50
51 #include <iostream>
52 #include <fstream>
53 #include <sstream>
54
55 #include <map>
56 #include <string>
57 #include <vector>
58
59 #include "ppbox.hh"
60 #include "prim2.hh"
61 #include "doc.hh"
62 #include "eval.hh"
63 #include "errormsg.hh"
64 #include "doc_Text.hh"
65 #include "sigprint.hh"
66 #include "propagate.hh"
67 #include "enrobage.hh"
68 #include "drawschema.hh"
69 #include "names.hh"
70 #include "simplify.hh"
71 #include "privatise.hh"
72 #include "recursivness.hh"
73 #include "sourcereader.hh"
74 #include "lateq.hh"
75 #include "doc_compile.hh"
76 #include "doc_lang.hh"
77 #include "doc_notice.hh"
78 #include "doc_autodoc.hh"
79 #include "compatibility.hh"
80
81
82
83 #define MAXIDCHARS 5 ///< max numbers (characters) to represent ids (e.g. for directories).
84
85 using namespace std ;
86
87
88 /*****************************************************************************
89 Globals and prototyping
90 *****************************************************************************/
91
92 extern Tree gExpandedDefList;
93 extern map<Tree, set<Tree> > gMetaDataSet;
94 extern map<string, string> gDocMetadatasStringMap;
95 extern map<string, string> gDocMathStringMap;
96 extern bool gDetailsSwitch;
97 extern bool gStripDocSwitch;
98 extern string gFaustDirectory;
99 extern string gFaustSuperDirectory;
100 extern string gFaustSuperSuperDirectory;
101 extern string gMasterDocument;
102 extern string gMasterName;
103 extern SourceReader gReader;
104
105 extern string gDocName; ///< Contains the filename for out documentation.
106 static const char* gDocDevSuffix; ///< ".tex" (or .??? - used to choose output device).
107 static string gCurrentDir; ///< Room to save current directory name.
108 static const string gLatexheaderfilename = "latexheader.tex";
109
110 vector<Tree> gDocVector; ///< Contains <mdoc> parsed trees: DOCTXT, DOCEQN, DOCDGM.
111
112 static struct tm gCompilationDate;
113
114 bool gLstDependenciesSwitch = true; ///< mdoc listing management.
115 bool gLstMdocTagsSwitch = true; ///< mdoc listing management.
116 bool gLstDistributedSwitch = true; ///< mdoc listing management.
117
118 enum { langEN, langFR, langIT };
119 string gDocLang;
120
121 /* Printing functions */
122 static void printlatexheader(istream& latexheader, const string& faustversion, ostream& docout);
123 static void printfaustlistings(ostream& docout);
124 static void printfaustlisting(string& path, ostream& docout);
125 static void printlatexfooter(ostream& docout);
126 static void printdoccontent(const char* svgTopDir, const vector<Tree>& docVector, const string& faustversion, ostream& docout);
127 static void printfaustdocstamp(const string& faustversion, ostream& docout);
128 static void printDocEqn(Lateq* ltq, ostream& docout);
129 static void printDocDgm(const Tree expr, const char* svgTopDir, ostream& docout, int i);
130 static void printDocMetadata(const Tree expr, ostream& docout);
131
132 /* Primary sub-functions for <equation> handling */
133 static void prepareDocEqns( const vector<Tree>& docBoxes, vector<Lateq*>& docCompiledEqnsVector ); ///< Caller function.
134 static void collectDocEqns( const vector<Tree>& docBoxes, vector<Tree>& eqBoxes ); ///< step 0. Feed a vector.
135 static void mapEvalDocEqn( const vector<Tree>& eqBoxes, const Tree& env, vector<Tree>& evalEqBoxes ); ///< step 1. Evaluate boxes.
136 static void mapGetEqName( const vector<Tree>& evalEqBoxes, vector<string>& eqNames ); ///< step 2. Get boxes name.
137 static void calcEqnsNicknames( const vector<string>& eqNames, vector<string>& eqNicknames ); ///< step 3. Calculate nicknames.
138 static void mapPrepareEqSig( const vector<Tree>& evalEqBoxes, vector<int>& eqInputs, vector<int>& eqOutputs, vector<Tree>& eqSigs ); ///< step 4&5. Propagate and prepare signals.
139 static void mapSetSigNickname( const vector<string>& eqNicknames, const vector<int>& eqInputs, const vector<Tree>& eqSigs ); ///< step 6. Set signals nicknames.
140 static void collectEqSigs( const vector<Tree>& eqSigs, Tree& superEqList ); ///< step 7. Collect all signals in a superlist.
141 static void annotateSuperList( DocCompiler* DC, Tree superEqList ); ///< step 8. Annotate superlist.
142 //static void calcAndSetLtqNames( Tree superEqList ); ///< step 9.
143 static void mapCompileDocEqnSigs( const vector<Tree>& eqSigs, const vector<int>& eqInputs, const vector<int>& eqOutputs, DocCompiler* DC, vector<Lateq*>& docCompiledEqnsVector ); ///< step 10. Compile equations.
144
145 /* Secondary sub-functions for <equation> handling */
146 static string calcNumberedName(const char* base, int i);
147 static void getBoxInputsAndOutputs(const Tree t, int& numInputs, int& numOutputs);
148 static string calcDocEqnInitial(const string s);
149
150 /* Notice related functions */
151 static void initCompilationDate();
152 static struct tm* getCompilationDate();
153
154 /* Files functions */
155 static int cholddir ();
156 static int mkchdir(const char* dirname);
157 static int makedir(const char* dirname);
158 static void getCurrentDir();
159 static istream* openArchFile (const string& filename);
160 static char* legalFileName(const Tree t, int n, char* dst);
161 static string rmExternalDoubleQuotes(const string& s);
162 static void copyFaustSources(const char* projname, const vector<string>& pathnames);
163 vector<string>& docCodeSlicer(const string& faustfile, vector<string>& codeSlices);
164 static void printdocCodeSlices(const string& code, ostream& docout);
165 static bool doesFileBeginWithCode(const string& faustfile);
166
167 //static void declareAutoDoc();
168
169
170
171 /*****************************************************************************
172 Types of Documentation Elements
173 *****************************************************************************/
174
175 Sym DOCTXT = symbol ("DocTxt");
176 Tree docTxt(const char* name) { return tree( DOCTXT, tree(symbol(name)) ); }
177 bool isDocTxt(Tree t) { return t->node() == Node(DOCTXT); }
178 bool isDocTxt(Tree t0, const char** str)
179 {
180 Tree t1; Sym s;
181 if ( isTree(t0, DOCTXT, t1) && isSym(t1->node(), &s) ) {
182 *str = name(s);
183 return true;
184 } else {
185 return false;
186 }
187 }
188
189 Sym DOCEQN = symbol ("DocEqn");
190 Tree docEqn(Tree x) { return tree(DOCEQN, x); }
191 bool isDocEqn(Tree t, Tree& x) { return isTree(t, DOCEQN, x); }
192
193 Sym DOCDGM = symbol ("DocDgm");
194 Tree docDgm(Tree x) { return tree(DOCDGM, x); }
195 bool isDocDgm(Tree t, Tree& x) { return isTree(t, DOCDGM, x); }
196
197 Sym DOCNTC = symbol ("DocNtc");
198 Tree docNtc() { return tree(DOCNTC); }
199 bool isDocNtc(Tree t) { return isTree(t, DOCNTC); }
200
201 Sym DOCLST = symbol ("DocLst");
202 Tree docLst() { return tree(DOCLST); }
203 bool isDocLst(Tree t) { return isTree(t, DOCLST); }
204
205 Sym DOCMTD = symbol ("DocMtd");
206 Tree docMtd(Tree x) { return tree(DOCMTD, x); }
207 bool isDocMtd(Tree t, Tree& x) { return isTree(t, DOCMTD, x); }
208
209 //string getDocTxt(Tree t) { return hd(t)->branch(0); }
210
211
212
213 /*****************************************************************************
214 Main Printing Function for the Documentation
215 *****************************************************************************/
216
217
218 /**
219 * @brief The entry point to generate faust doc files.
220 *
221 * The entry point to generate the output LaTeX file, stored in the directory "<projname>-math/".
222 * This file eventually references images for diagrams, generated in SVG subdirectories.
223 * The device system was adapted from drawSchema's device system.
224 *
225 * @param[in] projname Basename of the new doc directory ("*-math").
226 * @param[in] docdev The doc device; only ".tex" is supported for the moment.
227 * @param[in] faustversion The current version of this Faust compiler.
228 */
229 void printDoc(const char* projname, const char* docdev, const char* faustversion)
230 {
231 gDocDevSuffix = docdev;
232
233 /** File stuff : create doc directories and a tex file. */
234 //cerr << "Documentator : printDoc : gFaustDirectory = '" << gFaustDirectory << "'" << endl;
235 //cerr << "Documentator : printDoc : gFaustSuperDirectory = '" << gFaustSuperDirectory << "'" << endl;
236 //cerr << "Documentator : printDoc : gFaustSuperSuperDirectory = '" << gFaustSuperSuperDirectory << "'" << endl;
237 //cerr << "Documentator : printDoc : gCurrentDir = '" << gCurrentDir << "'" << endl;
238
239 makedir(projname); // create a top directory to store files
240
241 string svgTopDir = subst("$0/svg", projname);
242 makedir(svgTopDir.c_str()); // create a directory to store svg-* subdirectories.
243
244 string cppdir = subst("$0/cpp", projname);
245 makedir(cppdir.c_str()); // create a cpp directory.
246
247 string pdfdir = subst("$0/pdf", projname);
248 makedir(pdfdir.c_str()); // create a pdf directory.
249
250 /* Copy all Faust source files into an 'src' sub-directory. */
251 vector<string> pathnames = gReader.listSrcFiles();
252 copyFaustSources(projname, pathnames);
253
254 string texdir = subst("$0/tex", projname);
255 mkchdir(texdir.c_str()); // create a directory and move into.
256
257 /** Create THE mathdoc tex file. */
258 ofstream docout(subst("$0.$1", gDocName, docdev).c_str());
259 cholddir(); // return to current directory
260
261 /** Init and load translation file. */
262 loadTranslationFile(gDocLang);
263
264 /** Simulate a default doc if no <mdoc> tag detected. */
265 if (gDocVector.empty()) { declareAutoDoc(); }
266
267 /** Printing stuff : in the '.tex' ouptut file, eventually including SVG files. */
268 printfaustdocstamp(faustversion, docout); ///< Faust version and compilation date (comment).
269 istream* latexheader = openArchFile(gLatexheaderfilename);
270 printlatexheader(*latexheader, faustversion, docout); ///< Static LaTeX header (packages and setup).
271 printdoccontent(svgTopDir.c_str(), gDocVector, faustversion, docout); ///< Generate math contents (main stuff!).
272 printlatexfooter(docout); ///< Static LaTeX footer.
273 }
274
275
276
277 /*****************************************************************************
278 LaTeX basic printing functions of the Documentation
279 *****************************************************************************/
280
281 /**
282 * Print a static LaTeX header.
283 *
284 * @param[in] latexheader The file containing the static part of the LaTeX header.
285 * @param[in] faustversion The current version of this Faust compiler.
286 * @param[out] docout The LaTeX output file to print into.
287 */
288 static void printlatexheader(istream& latexheader, const string& faustversion, ostream& docout)
289 {
290 string s;
291 while(getline(latexheader, s)) docout << s << endl;
292
293 /** Specific LaTeX macros for Faust */
294 docout << "\\newcommand{\\faustfilename}{" << gMasterDocument << "}" << endl;
295 docout << "\\newcommand{\\faustdocdir}{" << gMasterName << "-mdoc}" << endl;
296 docout << "\\newcommand{\\faustprogname}{" << gMasterName << "}" << endl;
297 docout << "\\newcommand{\\faustversion}{" << faustversion << "}" << endl;
298 char datebuf [150];
299 strftime (datebuf, 150, "%B %d, %Y", getCompilationDate());
300 docout << "\\newcommand{\\faustdocdate}{" << datebuf << "}" << endl;
301
302 docout << endl << "\\begin{document}" << endl;
303 }
304
305
306 /**
307 * @Brief Print a metadata set.
308 *
309 * Each metadata is a set, in order to handle multiple items,
310 * like multiple authors, even if most of metadatas have
311 * unique items.
312 *
313 * @param[in] expr Parsed metadata keyname, as boxes tree.
314 * @param[out] docout The output file to print into.
315 */
316 static void printDocMetadata(const Tree expr, ostream& docout)
317 {
318 if (gMetaDataSet.count(expr)) {
319 string sep = "";
320 set<Tree> mset = gMetaDataSet[expr];
321
322 for (set<Tree>::iterator j = mset.begin(); j != mset.end(); j++) {
323 docout << sep << rmExternalDoubleQuotes(tree2str(*j));
324 sep = ", ";
325 }
326 }
327 }
328
329
330 /**
331 * Print listings of each Faust code ".dsp" files,
332 * calling the 'printfaustlisting' function.
333 *
334 * @param[out] docout The LaTeX output file to print into.
335 */
336 static void printfaustlistings(ostream& docout)
337 {
338 if (gLstDependenciesSwitch) {
339 vector<string> pathnames = gReader.listSrcFiles();
340 for (unsigned int i=0; i< pathnames.size(); i++) {
341 printfaustlisting(pathnames[i], docout);
342 }
343 } else {
344 printfaustlisting(gMasterDocument, docout);
345 }
346 }
347
348
349 /**
350 * Print a listing of the Faust code, in a LaTeX "listing" environment.
351 * Strip content of <mdoc> tags.
352 *
353 * @param[in] faustfile The source file containing the Faust code.
354 * @param[out] docout The LaTeX output file to print into.
355 */
356 static void printfaustlisting(string& faustfile, ostream& docout)
357 {
358 string s;
359 ifstream src;
360
361 //cerr << "Documentator : printfaustlisting : Opening file '" << faustfile << "'" << endl;
362 src.open(faustfile.c_str(), ifstream::in);
363
364 docout << endl << "\\bigskip\\bigskip" << endl;
365 docout << "\\begin{lstlisting}[caption=\\texttt{" << filebasename(faustfile.c_str()) << "}]" << endl;
366
367 bool isInsideDoc = false;
368
369 if (faustfile != "" && src.good()) {
370 while(getline(src, s)) { /** We suppose there's only one <mdoc> tag per line. */
371 size_t foundopendoc = s.find("<mdoc>");
372 if (foundopendoc != string::npos && gStripDocSwitch) isInsideDoc = true;
373
374 if (isInsideDoc == false)
375 docout << s << endl;
376
377 size_t foundclosedoc = s.find("</mdoc>");
378 if (foundclosedoc != string::npos && gStripDocSwitch) isInsideDoc = false;
379 }
380 } else {
381 cerr << "ERROR : can't open faust source file " << faustfile << endl;
382 exit(1);
383 }
384
385 docout << "\\end{lstlisting}" << endl << endl;
386 }
387
388
389 /**
390 * Print the static LaTeX footer.
391 *
392 * @param[out] docout The LaTeX output file to print into.
393 */
394 static void printlatexfooter(ostream& docout)
395 {
396 docout << endl << "\\end{document}" << endl << endl;
397 }
398
399
400 /**
401 * Print a "doc stamp" in the LaTeX document :
402 * - the Faust version,
403 * - the date of doc compilation,
404 * - faust's web site URL.
405 *
406 * @param[in] faustversion The current version of this Faust compiler.
407 * @param[out] docout The LaTeX output file to print into.
408 */
409 static void printfaustdocstamp(const string& faustversion, ostream& docout)
410 {
411 char datebuf [150];
412 strftime (datebuf, 150, "%c", getCompilationDate());
413
414 docout << "%% This documentation was generated with Faust version " << faustversion << endl;
415 docout << "%% " << datebuf << endl;
416 docout << "%% http://faust.grame.fr" << endl << endl;
417 }
418
419
420
421 /*****************************************************************************
422 Main loop : launches prepare, evaluate, and print functions
423 *****************************************************************************/
424
425 /**
426 * @brief Main documentator loop.
427 *
428 * First loop on gDocVector, which contains the faust <mdoc> trees.
429 * Second loop for each of these <mdoc> trees, which contain parsed input expressions of 3 types :
430 * DOCEQN for <equation> tags, DOCDGM for <diagram> tags, and DOCTXT for direct LaTeX text (no tag).
431 * - DOCTXT expressions printing is trivial.
432 * - DOCDGM expressions printing calls 'printDocDgm' to generate SVG files and print LaTeX "figure" code.
433 * - DOCEQN expressions printing calls 'printDocEqn' after an important preparing work
434 * has been done by 'prepareDocEqns'.
435 *
436 * @param[in] projname Basename of the new doc directory ("*-math").
437 * @param[in] docVector Contains all <mdoc> parsed content (as boxes).
438 * @param[in] faustversion The current version of this Faust compiler.
439 * @param[out] docout The output file to print into.
440 **/
441 static void printdoccontent(const char* svgTopDir, const vector<Tree>& docVector, const string& faustversion, ostream& docout)
442 {
443 //cerr << endl << "Documentator : printdoccontent : " << docVector.size() << " <mdoc> tags read." << endl;
444
445 /** Equations need to be prepared (named and compiled) before printing. */
446 vector<Lateq*> docCompiledEqnsVector;
447 prepareDocEqns( docVector, docCompiledEqnsVector ); ///< Quite a lot of stuff there.
448 vector<Lateq*>::iterator eqn_it = docCompiledEqnsVector.begin();
449
450 int dgmIndex = 1; ///< For diagram directories numbering.
451
452 vector<string> docMasterCodeMap;
453 docMasterCodeMap = docCodeSlicer(gMasterDocument, docMasterCodeMap);
454
455 vector<Tree>::const_iterator doc;
456 vector<string>::const_iterator code;
457 code = docMasterCodeMap.begin();
458
459 if(doesFileBeginWithCode(gMasterDocument) && (! docMasterCodeMap.empty()) && gLstDistributedSwitch ) {
460 printdocCodeSlices(*code, docout);
461 code++;
462 }
463
464 /** First level printing loop, on docVector. */
465 for (doc=docVector.begin(); doc<docVector.end(); doc++, code++) {
466
467 Tree L = reverse(*doc);
468 //cerr << "Entering into <mdoc> parsing..." << endl;
469
470 /** Second level printing loop, on each <mdoc>. */
471 while (isList(L)) {
472 Tree expr;
473 if ( isDocEqn(hd(L), expr) ) { ///< After equations are well prepared and named.
474 printDocEqn(*eqn_it++, docout);
475 }
476 else if ( isDocDgm(hd(L), expr) ) {
477 printDocDgm(expr, svgTopDir, docout, dgmIndex++);
478 }
479 else if ( isDocMtd(hd(L), expr) ) {
480 printDocMetadata(expr, docout);
481 }
482 else if ( isDocTxt(hd(L)) ) {
483 docout << *hd(L)->branch(0); // Directly print registered doc text.
484 }
485 else if ( isDocNtc(hd(L)) ) {
486 printDocNotice(faustversion, docout);
487 }
488 else if ( isDocLst(hd(L)) ) {
489 printfaustlistings(docout);
490 }
491 else {
492 cerr << "ERROR : " << *hd(L) << " is not a valid documentation type." << endl;
493 }
494 L = tl(L);
495 }
496 //cerr << " ...end of <mdoc> parsing." << endl;
497
498 if ( code != docMasterCodeMap.end() && gLstDistributedSwitch ) {
499 printdocCodeSlices(*code, docout);
500 }
501 }
502 }
503
504
505
506 /*****************************************************************************
507 Primary sub-functions for <equation> handling
508 *****************************************************************************/
509
510 /**
511 * @brief Caller function for all steps of doc equations preparation.
512 *
513 * Note : many of the functions called put their result into their last argument
514 * in a "source / destination" manner,
515 * the "destination" being declared before the function call.
516 *
517 * @param[in] docBoxes The <mdoc> boxes to collect and prepare.
518 * @param[out] docCompiledEqnsVector The place to store compiled equations.
519 */
520 static void prepareDocEqns(const vector<Tree>& docBoxes, vector<Lateq*>& docCompiledEqnsVector)
521 {
522 vector<Tree> eqBoxes; collectDocEqns( docBoxes, eqBoxes ); ///< step 0. Feed a vector.
523
524 if(! eqBoxes.empty() ) {
525 vector<Tree> evalEqBoxes; mapEvalDocEqn( eqBoxes, gExpandedDefList, evalEqBoxes ); ///< step 1. Evaluate boxes.
526 vector<string> eqNames; mapGetEqName( evalEqBoxes, eqNames ); ///< step 2. Get boxes name.
527 vector<string> eqNicknames; calcEqnsNicknames( eqNames, eqNicknames ); ///< step 3. Calculate nicknames.
528
529 vector<int> eqInputs;
530 vector<int> eqOutputs;
531 vector<Tree> eqSigs; mapPrepareEqSig( evalEqBoxes, eqInputs, eqOutputs, eqSigs ); ///< step 4&5. Propagate and prepare signals.
532 mapSetSigNickname( eqNicknames, eqInputs, eqSigs ); ///< step 6. Set signals nicknames.
533 Tree superEqList; collectEqSigs( eqSigs, superEqList ); ///< step 7. Collect all signals in a superlist.
534
535 DocCompiler* DC = new DocCompiler(0, 0);
536 annotateSuperList( DC, superEqList ); ///< step 8. Annotate superEqList.
537 //calcAndSetLtqNames( superEqList ); ///< step 9. (directly in 10.)
538 mapCompileDocEqnSigs( eqSigs, eqInputs, eqOutputs, DC, docCompiledEqnsVector ); ///< step 10. Compile every signal.
539 }
540 }
541
542
543 /**
544 * #0. Collect every <equation> found in all <mdoc> faust comments.
545 *
546 * @param[in] docBoxes The <mdoc> boxes to filter.
547 * @param[out] eqBoxes The place to store only <equation> boxes.
548 */
549 static void collectDocEqns(const vector<Tree>& docBoxes, vector<Tree>& eqBoxes)
550 {
551 int nbdoceqn = 0;
552
553 for (vector<Tree>::const_iterator doc=docBoxes.begin(); doc<docBoxes.end(); doc++) {
554 Tree L = reverse(*doc);
555 Tree expr;
556 while (isList(L)) {
557 if ( isDocEqn(hd(L), expr) ) {
558 eqBoxes.push_back(expr);
559 nbdoceqn++;
560 }
561 L = tl(L);
562 }
563 }
564 //cerr << "Documentator : collectDocEqns : " << nbdoceqn << " <equation> tags found." << endl;
565 }
566
567
568 /**
569 * #1. Evaluate every doc <equation> (evaluation replaces abstractions by symbolic boxes).
570 *
571 * @param[in] eqBoxes The boxes to evaluate.
572 * @param[in] env The environment for the evaluation.
573 * @param[out] evalEqBoxes The place to store evaluated equations boxes.
574 */
575 static void mapEvalDocEqn(const vector<Tree>& eqBoxes, const Tree& env, vector<Tree>& evalEqBoxes)
576 {
577 //cerr << "###\n# Documentator : mapEvalDocEqn" << endl;
578
579 for ( vector<Tree>::const_iterator eq=eqBoxes.begin(); eq < eqBoxes.end(); eq++)
580 {
581 evalEqBoxes.push_back(evaldocexpr( *eq, env ));
582 }
583 //cerr << "Documentator : end of mapEvalDocEqn\n---" << endl;
584 }
585
586
587 /**
588 * #2. Get name if exists, else create one, and store it.
589 *
590 * @param[in] evalEqBoxes Evaluated box trees, eventually containing an equation name.
591 * @param[out] eqNames The place to store equations names.
592 */
593 static void mapGetEqName(const vector<Tree>& evalEqBoxes, vector<string>& eqNames)
594 {
595 //cerr << "###\n# Documentator : mapGetEqName" << endl;
596
597 int i = 1;
598 for( vector<Tree>::const_iterator eq = evalEqBoxes.begin(); eq < evalEqBoxes.end(); eq++, i++ ) {
599 Tree id;
600 string s;
601 int n,m; getBoxType(*eq, &n, &m); // eq name only for bd without inputs
602 if ( n==0 && getDefNameProperty(*eq, id) ) {
603 s = tree2str(id);
604 }
605 else {
606 s = calcNumberedName("doceqn-", i);
607 }
608 eqNames.push_back( s ) ;
609 }
610 //cerr << "Documentator : end of mapGetEqName\n---" << endl;
611 }
612
613
614 /**
615 * #3. Calculate a nickname for each equation and store it.
616 *
617 * @param[in] eqNames Equations names to parse.
618 * @param[out] eqNicknames The place to store calculated nicknames.
619 *
620 * @todo Should check unicity : check whether several names share the same initial,
621 * or else capture consonants for example.
622 */
623 static void calcEqnsNicknames(const vector<string>& eqNames, vector<string>& eqNicknames)
624 {
625 //cerr << "###\n# Documentator : calcEqnsNicknames" << endl;
626
627 vector<string> v;
628
629 for( vector<string>::const_iterator eq = eqNames.begin(); eq < eqNames.end(); eq++ ) {
630 string init = calcDocEqnInitial(*eq);
631 v.push_back(init);
632 /** Check duplicates */
633 // for( vector<string>::iterator it = v.begin(); it < v.end()-1; ++it ) {
634 // if (init == *it) {
635 // //cerr << "!! Warning Documentator : calcEqnsNicknames : duplicates \"" << init << "\"" << endl;
636 // }
637 // }
638 eqNicknames.push_back(init);
639 }
640
641 // for( vector<string>::const_iterator eq = eqNames.begin(); eq < eqNames.end(); eq++ ) {
642 // int c = 0;
643 // c = count_if(eqNames.begin(), eqNames.end(), bind2nd(equal_to<string>(), *eq));
644 // if (c > 0) {
645 // cerr << "- Duplicate nickname !! " << *eq << endl;
646 // } else {
647 // cerr << "(no duplicate) " << *eq << endl;
648 // }
649 // }
650
651 //cerr << "Documentator : end of calcEqnsNicknames\n---" << endl;
652 }
653
654
655 /**
656 * #4&5. Propagate and prepare every doc <equation>.
657 *
658 * Call boxPropagateSig, deBruijn2Sym, simplify, and privatise.
659 *
660 * @param[in] evalEqBoxes Equations boxes to propagate as signals.
661 * @param[out] eqSigs The place to store prepared signals.
662 */
663 static void mapPrepareEqSig(const vector<Tree>& evalEqBoxes, vector<int>& eqInputs, vector<int>& eqOutputs, vector<Tree>& eqSigs)
664 {
665 //cerr << "###\n# Documentator : mapPrepareEqSig" << endl;
666
667 for( vector<Tree>::const_iterator eq = evalEqBoxes.begin(); eq < evalEqBoxes.end(); eq++ ) {
668
669 int numInputs, numOutputs;
670 getBoxInputsAndOutputs(*eq, numInputs, numOutputs);
671 //cerr << numInputs <<" ins and " << numOutputs <<" outs" << endl;
672 eqInputs.push_back(numInputs);
673 eqOutputs.push_back(numOutputs);
674
675 Tree lsig1 = boxPropagateSig( nil, *eq , makeSigInputList(numInputs) );
676 //cerr << "output signals are : " << endl; printSignal(lsig1, stderr);
677
678 Tree lsig2 = deBruijn2Sym(lsig1); ///< Convert debruijn recursion into symbolic recursion
679 Tree lsig3 = simplify(lsig2); ///< Simplify by executing every computable operation
680 //Tree lsig4 = privatise(lsig3); ///< Un-share tables with multiple writers
681 Tree lsig4 = docTableConvertion(lsig3); ///< convert regular tables into special doctables
682 ///< (regular tables are difficult to translate to equations)
683
684 eqSigs.push_back(lsig4);
685 }
686 //cerr << "Documentator : end of mapPrepareEqSig\n---" << endl;
687 }
688
689
690 /**
691 * #6. Set signals nicknames.
692 *
693 * Do nothing for the moment !
694 * @param[in] eqNicknames Contains previously calculated nicknames.
695 * @param[out] eqSigs The signals to tag with a NICKNAMEPROPERTY.
696 */
697 static void mapSetSigNickname(const vector<string>& eqNicknames, const vector<int>& eqInputs, const vector<Tree>& eqSigs)
698 {
699 //cerr << "###\n# Documentator : mapSetSigNickname" << endl;
700
701 // Do nothing for the moment...
702 // for( unsigned int i=0; i < eqSigs.size(); i++ ) {
703 // if (eqInputs[i] == 0) // Only "generators" should be finally named with user equation (nick)name.
704 // setSigListNickName(eqSigs[i], eqNicknames[i]);
705 // }
706 //cerr << "Documentator : end of mapSetSigNickname\n---" << endl;
707 }
708
709
710 /**
711 * #7. Collect each prepared list of signals to construct a super list.
712 *
713 * @param[in] eqSigs Contains well-prepared and nicknamed signals.
714 * @param[out] superEqList The root where to 'cons' signals all together.
715 */
716 static void collectEqSigs(const vector<Tree>& eqSigs, Tree& superEqList)
717 {
718 //cerr << "###\n# Documentator : collectEqSigs" << endl;
719
720 superEqList = nil;
721
722 for( vector<Tree>::const_iterator it = eqSigs.begin(); it < eqSigs.end(); ++it ) {
723 superEqList = cons( *it, superEqList );
724 }
725 //printSignal(superEqList, stdout, 0);
726
727 //cerr << endl << "Documentator : end of collectEqSigs\n---" << endl;
728 }
729
730
731 /**
732 * #8. Annotate superEqList (to find candidate signals to be named later).
733 *
734 * @param[in] DC The signals compiler.
735 * @param[out] superEqList The super equations signal tree to annotate.
736 */
737 static void annotateSuperList(DocCompiler* DC, Tree superEqList)
738 {
739 DC->annotate(superEqList);
740 }
741
742
743 ///**
744 // * #9. Calculated and set lateq (LaTeX equation) names.
745 // * Note : Transfered into mapCompileDocEqnSigs (DocCompiler::compileMultiSignal).
746 // */
747 //static void calcAndSetLtqNames(Tree superEqList)
748 //{
749 //
750 //}
751
752
753 /**
754 * #10. Name and compile prepared doc <equation> signals.
755 *
756 * @param[in] eqSigs Contains well-prepared and nicknamed signals.
757 * @param[in] DC The signals compiler.
758 * @param[out] docCompiledEqnsVector The place to store each compiled Lateq* object.
759 */
760 static void mapCompileDocEqnSigs(const vector<Tree>& eqSigs, const vector<int>& eqInputs, const vector<int>& eqOutputs, DocCompiler* DC, vector<Lateq*>& docCompiledEqnsVector)
761 {
762 //cerr << "###\n# Documentator : mapCompileDocEqnSigs" << endl;
763
764 for( unsigned int i=0; i < eqSigs.size(); i++ ) {
765
766 // docCompiledEqnsVector.push_back( DC->compileMultiSignal(*it, 0) );
767 docCompiledEqnsVector.push_back( DC->compileLateq(eqSigs[i], new Lateq(eqInputs[i], eqOutputs[i])) );
768 }
769
770 //cerr << "Documentator : end of mapCompileDocEqnSigs\n---" << endl;
771 }
772
773
774
775 /*****************************************************************************
776 Secondary sub-functions for <equation> handling
777 *****************************************************************************/
778
779
780 /**
781 * Calculate an appropriate nickname for equations,
782 * from previous names.
783 *
784 * @param The string to parse.
785 * @return Essentially returns the initial character,
786 * except "Y" for "process", and "Z" for unnamed equations.
787 */
788 static string calcDocEqnInitial(const string s)
789 {
790 string nn;
791 if(s == "process")
792 nn = "Y";
793 else if (s.substr(0,6) == "doceqn")
794 nn = "Z";
795 else
796 nn += toupper(s[0]);
797 return nn;
798 }
799
800
801 /**
802 * Just get the number of inputs and the number of outputs of a box.
803 *
804 * @param[in] t The box tree to get inputs and outputs from.
805 * @param[out] numInputs The place to store the number of inputs.
806 * @param[out] numOutputs The place to store the number of outputs.
807 */
808 static void getBoxInputsAndOutputs(const Tree t, int& numInputs, int& numOutputs)
809 {
810 if (!getBoxType(t, &numInputs, &numOutputs)) {
811 cerr << "ERROR during the evaluation of t : " << boxpp(t) << endl;
812 exit(1);
813 }
814 //cerr << "Documentator : " << numInputs <<" inputs and " << numOutputs <<" outputs for box : " << boxpp(t) << endl;
815 }
816
817
818 /**
819 * Print doc equations, following the Lateq::println method.
820 *
821 * @param[in] ltq The object containing compiled LaTeX code of equations.
822 * @param[out] docout The output file to print into.
823 */
824 static void printDocEqn(Lateq* ltq, ostream& docout)
825 {
826 ltq->println(docout);
827 //cerr << "Documentator : printDocEqn : "; ltq->println(cerr); cerr << endl;
828 }
829
830
831 /*****************************************************************************
832 Sub-function for <diagram> handling
833 *****************************************************************************/
834
835 /**
836 * @brief Doc diagrams handling.
837 *
838 * Three steps :
839 * 1. evaluate expression
840 * 2. call svg drawing in the appropriate directory
841 * 3. print latex figure code with the appropriate directory reference
842 *
843 * @param[in] expr Parsed input expression, as boxes tree.
844 * @param[in] svgTopDir Basename of the new doc directory ("*-math/svg").
845 * @param[out] docout The output file to print into.
846 */
847 static void printDocDgm(const Tree expr, const char* svgTopDir, ostream& docout, int i)
848 {
849 /** 1. Evaluate expression. */
850 Tree docdgm = evaldocexpr(expr, gExpandedDefList);
851 if (gErrorCount > 0) {
852 cerr << "Total of " << gErrorCount << " errors during evaluation of : diagram docdgm = " << boxpp(docdgm) << ";\n";
853 exit(1);
854 }
855
856 /**
857 * 2. Draw the diagram after its evaluation, in SVG.
858 * Warning : pdflatex can't directly include SVG files !
859 */
860 char dgmid[MAXIDCHARS+1];
861 sprintf(dgmid, "%02d", i);
862 string thisdgmdir = subst("$0/svg-$1", svgTopDir, dgmid);
863 //cerr << "Documentator : printDocDgm : drawSchema in '" << gCurrentDir << "/" << thisdgmdir << "'" << endl;
864
865 drawSchema( docdgm, thisdgmdir.c_str(), "svg" );
866
867 /** 3. Print LaTeX figure code. */
868 char temp[1024];
869 const string dgmfilename = legalFileName(docdgm, 1024, temp);
870 //docout << "figure \\ref{figure" << i << "}";
871 docout << "\\begin{figure}[ht!]" << endl;
872 docout << "\t\\centering" << endl;
873 docout << "\t\\includegraphics[width=\\textwidth]{" << subst("../svg/svg-$0/", dgmid) << dgmfilename << "}" << endl;
874 docout << "\t\\caption{" << gDocMathStringMap["dgmcaption"] << " \\texttt{" << dgmfilename << "}}" << endl;
875 docout << "\t\\label{figure" << i << "}" << endl;
876 docout << "\\end{figure}" << endl << endl;
877
878 /** 4. Warn about naming interferences (in the notice). */
879 gDocNoticeFlagMap["nameconflicts"] = true;
880 gDocNoticeFlagMap["svgdir"] = true;
881 }
882
883
884
885 /*****************************************************************************
886 Other sub-functions
887 *****************************************************************************/
888
889
890 /**
891 * Slice faust code between "mdoc" sections.
892 *
893 * @param[in] faustfile Name of the input faust file to parse.
894 * @param[in] codeSlices The place to store code "slices".
895 */
896 vector<string>& docCodeSlicer(const string& faustfile, vector<string>& codeSlices)
897 {
898 string s;
899 ifstream src;
900 src.open(faustfile.c_str(), ifstream::in);
901 string tmp = "";
902
903 bool isInsideDoc = false;
904
905 if (faustfile != "" && src.good()) {
906 while(getline(src, s)) { /** Caution: we suppose there's only one <mdoc> tag per line! */
907 size_t foundopendoc = s.find("<mdoc>");
908
909 if (foundopendoc != string::npos) {
910 if (isInsideDoc == false) { /** A change has come. ;) */
911 if (! tmp.empty() ) {
912 codeSlices.push_back(tmp); }
913 tmp = "";
914 }
915 isInsideDoc = true;
916 }
917
918 if (isInsideDoc == false) {
919 tmp += s + '\n';
920 }
921
922 size_t foundclosedoc = s.find("</mdoc>");
923 if (foundclosedoc != string::npos) isInsideDoc = false;
924 }
925 } else {
926 cerr << "ERROR : can't open faust source file " << faustfile << endl;
927 exit(1);
928 }
929 return codeSlices;
930 }
931
932
933 /**
934 * Print faust code inside a listing environment.
935 *
936 * @param[in] code Faust code as a string (may contain '\n' characters).
937 * @param[out] docout The output file to print into.
938 */
939 static void printdocCodeSlices(const string& code, ostream& docout)
940 {
941 if ( ! code.empty() ) {
942 docout << endl << "\\begin{lstlisting}[numbers=none, frame=none, basicstyle=\\small\\ttfamily, backgroundcolor=\\color{yobg}]" << endl;
943 docout << code << endl;
944 docout << "\\end{lstlisting}" << endl << endl;
945 }
946 }
947
948
949 /**
950 * Test whether a file does begin with some faust code or not.
951 *
952 * @param[in] faustfile Name of the input faust file to parse.
953 */
954 static bool doesFileBeginWithCode(const string& faustfile)
955 {
956 string s;
957 ifstream src;
958 src.open(faustfile.c_str(), ifstream::in);
959
960 if (faustfile != "" && src.good()) {
961 getline(src, s);
962 size_t foundopendoc = s.find("<mdoc>");
963 if(int(foundopendoc)==0) {
964 return false;
965 } else {
966 return true;
967 }
968 } else {
969 cerr << "ERROR : can't open faust source file " << faustfile << endl;
970 exit(1);
971 }
972 }
973
974
975
976 //------------------------ dealing with files -------------------------
977
978
979 /**
980 * Create a new directory in the current one.
981 */
982 static int makedir(const char* dirname)
983 {
984 char buffer[FAUST_PATH_MAX];
985 gCurrentDir = getcwd (buffer, FAUST_PATH_MAX);
986
987 if ( gCurrentDir.c_str() != 0) {
988 int status = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
989 if (status == 0 || errno == EEXIST) {
990 return 0;
991 }
992 }
993 perror("makedir");
994 exit(errno);
995 }
996
997
998 /**
999 * Create a new directory in the current one,
1000 * then 'cd' into this new directory.
1001 *
1002 * @remark
1003 * The current directory is saved to be later restaured.
1004 */
1005 static int mkchdir(const char* dirname)
1006 {
1007 char buffer[FAUST_PATH_MAX];
1008 gCurrentDir = getcwd (buffer, FAUST_PATH_MAX);
1009
1010 if ( gCurrentDir.c_str() != 0) {
1011 int status = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
1012 if (status == 0 || errno == EEXIST) {
1013 if (chdir(dirname) == 0) {
1014 return 0;
1015 }
1016 }
1017 }
1018 perror("mkchdir");
1019 exit(errno);
1020 }
1021
1022
1023 /**
1024 * Switch back to the previously stored current directory
1025 */
1026 static int cholddir ()
1027 {
1028 if (chdir(gCurrentDir.c_str()) == 0) {
1029 return 0;
1030 } else {
1031 perror("cholddir");
1032 exit(errno);
1033 }
1034 }
1035
1036
1037 /**
1038 * Get current directory and store it in gCurrentDir.
1039 */
1040 static void getCurrentDir ()
1041 {
1042 char buffer[FAUST_PATH_MAX];
1043 gCurrentDir = getcwd (buffer, FAUST_PATH_MAX);
1044 }
1045
1046
1047 /**
1048 * Open architecture file.
1049 */
1050 static istream* openArchFile (const string& filename)
1051 {
1052 istream* file;
1053 getCurrentDir(); // Save the current directory.
1054 //cerr << "Documentator : openArchFile : Opening input file '" << filename << "'" << endl;
1055 if ( (file = open_arch_stream(filename.c_str())) ) {
1056 //cerr << "Documentator : openArchFile : Opening '" << filename << "'" << endl;
1057 } else {
1058 cerr << "ERROR : can't open architecture file " << filename << endl;
1059 exit(1);
1060 }
1061 cholddir(); // Return to current directory.
1062 return file;
1063 }
1064
1065
1066 /**
1067 * Transform the definition name property of tree <t> into a
1068 * legal file name. The resulting file name is stored in
1069 * <dst> a table of at least <n> chars. Returns the <dst> pointer
1070 * for convenience.
1071 */
1072 static char* legalFileName(const Tree t, int n, char* dst)
1073 {
1074 Tree id;
1075 int i=0;
1076 if (getDefNameProperty(t, id)) {
1077 const char* src = tree2str(id);
1078 for (i=0; isalnum(src[i]) && i<16; i++) {
1079 dst[i] = src[i];
1080 }
1081 }
1082 dst[i] = 0;
1083 if (strcmp(dst, "process") != 0) {
1084 // if it is not process add the hex address to make the name unique
1085 snprintf(&dst[i], n-i, "-%p", t);
1086 }
1087 return dst;
1088 }
1089
1090 /**
1091 * Simply concat a string with a number in a "%03d" format.
1092 * The number has MAXIDCHARS characters.
1093 **/
1094 static string calcNumberedName(const char* base, int i)
1095 {
1096 char nb[MAXIDCHARS+1];
1097 sprintf(nb, "%03d", i);
1098 return subst("$0$1", base, nb);
1099 }
1100
1101 /**
1102 * Remove the leading and trailing double quotes of a string
1103 * (but not those in the middle of the string)
1104 */
1105 static string rmExternalDoubleQuotes(const string& s)
1106 {
1107 size_t i = s.find_first_not_of("\"");
1108 size_t j = s.find_last_not_of("\"");
1109
1110 if ( (i != string::npos) & (j != string::npos) ) {
1111 return s.substr(i, 1+j-i);
1112 } else {
1113 return "";
1114 }
1115 }
1116
1117
1118 /**
1119 * Copy all Faust source files into an 'src' subdirectory.
1120 *
1121 * @param[in] projname Basename of the new doc directory ("*-math").
1122 * @param[in] pathnames The paths list of the source files to copy.
1123 */
1124 static void copyFaustSources(const char* projname, const vector<string>& pathnames)
1125 {
1126 string srcdir = subst("$0/src", projname);
1127 //cerr << "Documentator : copyFaustSources : Creating directory '" << srcdir << "'" << endl;
1128 makedir(srcdir.c_str()); // create a directory.
1129
1130 for (unsigned int i=0; i< pathnames.size(); i++) {
1131 ifstream src;
1132 ofstream dst;
1133 string faustfile = pathnames[i];
1134 string copy = subst("$0/$1", srcdir, filebasename(faustfile.c_str()));
1135 //cerr << "Documentator : copyFaustSources : Opening input file '" << faustfile << "'" << endl;
1136 //cerr << "Documentator : copyFaustSources : Opening output file '" << copy << "'" << endl;
1137 src.open(faustfile.c_str(), ifstream::in);
1138 dst.open(copy.c_str(), ofstream::out);
1139 string s;
1140 while ( getline(src,s) ) dst << s << endl;
1141 }
1142 }
1143
1144
1145 //------------------------ date managment -------------------------
1146
1147
1148 static void initCompilationDate()
1149 {
1150 time_t now;
1151
1152 time(&now);
1153 gCompilationDate = *localtime(&now);
1154 }
1155
1156 static struct tm* getCompilationDate()
1157 {
1158 initCompilationDate();
1159 return &gCompilationDate;
1160 }
1161