FAUST compiler  0.9.9.6b8
drawschema.cpp
Go to the documentation of this file.
00001 /************************************************************************
00002  ************************************************************************
00003     FAUST compiler
00004     Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
00005     ---------------------------------------------------------------------
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  ************************************************************************
00020  ************************************************************************/
00021 
00030 #include <stdio.h>
00031 #include <ctype.h>
00032 #include <sys/stat.h>
00033 #include <sys/types.h>
00034 #include <errno.h>
00035 #include <string.h>
00036 
00037 #include <ostream>
00038 #include <sstream>
00039 #include <set>
00040 #include <utility>
00041 #include <map>
00042 #include <stack>
00043 #include <string>
00044 
00045 #include "boxes.hh"
00046 #include "ppbox.hh"
00047 #include "prim2.hh"
00048 
00049 #include <vector>
00050 #include "devLib.h"
00051 #include "ppbox.hh"
00052 #include "xtended.hh"
00053 #include "occurrences.hh"
00054 #include "boxcomplexity.h"
00055 
00056 #include "schema.h"
00057 #include "drawschema.hh"
00058 #include "compatibility.hh"
00059 #include "names.hh"
00060 #include "description.hh"
00061 #include "property.hh"
00062 
00063 
00064 
00065 #if 0
00066 #define linkcolor "#b3d1dc"
00067 #define normalcolor "#ffeaa2"
00068 #define uicolor "#F1CFA1"
00069 #define slotcolor "#ffffd7"
00070 #define numcolor "#ffffff"
00071 #endif
00072 
00073 #if 0
00074 #define linkcolor "#F57900" 
00075 #define normalcolor "#4B71A1"
00076 #define uicolor "#47945E"
00077 #define slotcolor "#EDD400"
00078 #define numcolor "#4B71A1"
00079 #endif
00080 
00081 #if 0
00082 #define linkcolor "#47945E" 
00083 #define normalcolor "#4B71A1"
00084 #define uicolor "#f44800"
00085 #define slotcolor "#EDD400"
00086 #define numcolor "#f44800"
00087 #endif
00088 
00089 #if 0
00090 #define linkcolor "#47945E" 
00091 #define normalcolor "#4B71A1"
00092 #define uicolor "#816647"
00093 #define slotcolor "#EDD400"
00094 #define numcolor "#f44800"
00095 #endif
00096 
00097 #if 0
00098 #define linkcolor "#003366" 
00099 #define normalcolor "#4B71A1"
00100 #define uicolor "#816647"
00101 #define slotcolor "#EDD400"
00102 #define numcolor "#f44800"
00103 #endif
00104 
00105 #if 0
00106 #define linkcolor "#003366" 
00107 #define normalcolor "#4B71A1"
00108 #define uicolor "#477881"
00109 #define slotcolor "#816647"
00110 #define numcolor "#f44800"
00111 #endif
00112 
00113 
00114 #if 1
00115 #define linkcolor "#003366" 
00116 #define normalcolor "#4B71A1"
00117 #define uicolor "#477881"
00118 #define slotcolor "#47945E"
00119 #define numcolor "#f44800"
00120 #define invcolor "#ffffff"
00121 #endif
00122 
00123 using namespace std;
00124 
00125 // external parameters
00126 extern int gFoldThreshold;              // max diagram complexity before folding
00127 
00128 
00129 // internal state during drawing
00130 static Occurrences*     gOccurrences;
00131 static bool             sFoldingFlag;       // true with complex block-diagrams
00132 static stack<Tree>      gPendingExp;        // Expressions that need to be drawn
00133 static set<Tree>        gDrawnExp;          // Expressions drawn or scheduled so far
00134 static const char*      gDevSuffix;         // .svg or .ps used to choose output device
00135 static char             gCurrentDir[512];   // room to save current directory name
00136 static string           gSchemaFileName;    // name of schema file beeing generated
00137 static map<Tree,string> gBackLink;          // link to enclosing file for sub schema
00138 
00139 // prototypes of internal functions
00140 static void     writeSchemaFile(Tree bd);
00141 static schema*  generateDiagramSchema (Tree bd);
00142 static schema*  generateInsideSchema(Tree t);
00143 static void     scheduleDrawing(Tree t);
00144 static bool     pendingDrawing(Tree& t);
00145 static schema*  generateAbstractionSchema(schema* x, Tree t);
00146 static schema*  generateOutputSlotSchema(Tree a);
00147 static schema*  generateInputSlotSchema(Tree a);
00148 static schema*  generateBargraphSchema(Tree t);
00149 static schema*  generateUserInterfaceSchema(Tree t);
00150 static char*    legalFileName(Tree t, int n, char* dst);
00151 static int      cholddir ();
00152 static int      mkchdir(const char* dirname);
00153 
00154 
00155 
00156 
00162 void drawSchema(Tree bd, const char* projname, const char* dev)
00163 {
00164     gDevSuffix      = dev;
00165     sFoldingFlag    = boxComplexity(bd) > gFoldThreshold;
00166 
00167     mkchdir(projname);          // create a directory to store files
00168 
00169     scheduleDrawing(bd);        // schedule the initial drawing
00170 
00171     Tree t; while (pendingDrawing(t)) {
00172         writeSchemaFile(t);     // generate all the pending drawing
00173     }
00174 
00175     cholddir();                 // return to current directory
00176 }
00177 
00178 
00179 /************************************************************************
00180  ************************************************************************
00181                             IMPLEMENTATION
00182  ************************************************************************
00183  ************************************************************************/
00184 
00185 
00186 //------------------- to schedule and retreive drawing ------------------
00187 
00191 static void scheduleDrawing(Tree t)
00192 {
00193     if (gDrawnExp.find(t) == gDrawnExp.end()) {
00194         gDrawnExp.insert(t);
00195         gBackLink.insert(make_pair(t,gSchemaFileName)); // remember the enclosing filename
00196         gPendingExp.push(t);
00197     }
00198 }
00199 
00203 static bool pendingDrawing(Tree& t)
00204 {
00205     if (gPendingExp.empty()) return false;
00206     t = gPendingExp.top();
00207     gPendingExp.pop();
00208     return true;
00209 }
00210 
00211 
00212 
00213 //------------------------ dealing with files -------------------------
00214 
00220 static void writeSchemaFile(Tree bd)
00221 {
00222     Tree            id;
00223     schema*         ts;
00224 
00225     char            temp[1024];
00226 
00227     gOccurrences = new Occurrences(bd);
00228 
00229     bool hasname = getDefNameProperty(bd, id); 
00230 
00231     //assert(hasname);
00232     if (!hasname) {
00233         // create an arbitrary name 
00234         id = tree(Node(unique("diagram_")));
00235     }
00236 
00237     // generate legal file name for the schema
00238     stringstream s1; s1 << legalFileName(bd, 1024, temp) << "." << gDevSuffix;
00239     gSchemaFileName = s1.str();
00240 
00241     // generate the label of the schema
00242     stringstream s2; s2 << tree2str(id);
00243     string link = gBackLink[bd];
00244     ts = makeTopSchema(generateInsideSchema(bd), 20, s2.str(), link);
00245     // draw to the device defined by gDevSuffix
00246     if (strcmp(gDevSuffix, "svg") == 0) {
00247         SVGDev dev(s1.str().c_str(), ts->width(), ts->height());
00248         ts->place(0,0, kLeftRight);
00249         ts->draw(dev);
00250         { collector c; ts->collectTraits(c); c.draw(dev); }
00251     } else {
00252         PSDev dev(s1.str().c_str(), ts->width(), ts->height());
00253         ts->place(0,0, kLeftRight);
00254         ts->draw(dev);
00255         {
00256             collector c;
00257             ts->collectTraits(c);
00258             c.draw(dev);
00259         }
00260     }
00261 }
00262 
00263 
00264 
00269 static int mkchdir(const char* dirname)
00270 {
00271     //cerr << "mkchdir of " << dirname << endl;
00272     if (getcwd(gCurrentDir, 512) != 0) {
00273         int status = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
00274         if (status == 0 || errno == EEXIST) {
00275             if (chdir(dirname) == 0) {
00276                 return 0;
00277             }
00278         }
00279     }
00280     perror("mkchdir");
00281     exit(errno);
00282     //return errno;
00283 }
00284 
00285 
00289 static int cholddir ()
00290 {
00291     if (chdir(gCurrentDir) == 0) {
00292         return 0;
00293     } else {
00294         perror("cholddir");
00295         exit(errno);
00296     }
00297 }
00298 
00299 
00306 static char* legalFileName(Tree t, int n, char* dst)
00307 {
00308     Tree    id;
00309     int     i=0;
00310     if (getDefNameProperty(t, id)) {
00311         const char*     src = tree2str(id);
00312         for (i=0; isalnum(src[i]) && i<16; i++) {
00313             dst[i] = src[i];
00314         }
00315     }
00316     dst[i] = 0;
00317     if (strcmp(dst, "process") != 0) { 
00318         // if it is not process add the hex address to make the name unique
00319         snprintf(&dst[i], n-i, "-%p", t);
00320     }
00321     return dst;
00322 }
00323 
00324 
00325 
00326 //------------------------ generating the schema -------------------------
00327 
00328 
00333 Tree gInverter[6];
00334 
00335 static bool isInverter(Tree t)
00336 {
00337     // init gInverted table. For some reason doesn't work if done outside
00338     if (gInverter[0] == 0) {
00339         gInverter[0] = boxSeq(boxPar(boxWire(), boxInt(-1)),boxPrim2(sigMul));
00340         gInverter[1] = boxSeq(boxPar(boxInt(-1), boxWire()),boxPrim2(sigMul));
00341         gInverter[2] = boxSeq(boxPar(boxWire(), boxReal(-1.0)),boxPrim2(sigMul));
00342         gInverter[3] = boxSeq(boxPar(boxReal(-1.0), boxWire()),boxPrim2(sigMul));
00343         gInverter[4] = boxSeq(boxPar(boxInt(0), boxWire()),boxPrim2(sigSub));
00344         gInverter[5] = boxSeq(boxPar(boxReal(0.0), boxWire()),boxPrim2(sigSub));
00345     };
00346 
00347     //cerr << "isInverter " << t << '$' << boxpp(t) << endl;
00348     for (int i=0; i<6; i++) {
00349         if (t == gInverter[i]) return true;
00350     }
00351     return false;
00352 }
00353 
00354 
00360 property<bool> gPureRoutingProperty;
00361 
00362 static bool isPureRouting(Tree t)
00363 {
00364     bool    r;
00365     int     ID;
00366     Tree    x,y;
00367 
00368     if (gPureRoutingProperty.get(t,r)) {
00369         return r;
00370     } else if (    isBoxCut(t)
00371                 || isBoxWire(t)
00372                 || isInverter(t)
00373                 || isBoxSlot(t, &ID)
00374                 || (isBoxPar(t,x,y) && isPureRouting(x) && isPureRouting(y))
00375                 || (isBoxSeq(t,x,y) && isPureRouting(x) && isPureRouting(y))
00376                 || (isBoxSplit(t,x,y) && isPureRouting(x) && isPureRouting(y))
00377                 || (isBoxMerge(t,x,y) && isPureRouting(x) && isPureRouting(y))
00378               ) {
00379         gPureRoutingProperty.set(t,true);
00380         return true;
00381     } else {
00382         gPureRoutingProperty.set(t,false);
00383         return false;
00384     }
00385 }
00386 
00387 
00394 static schema* generateDiagramSchema(Tree t)
00395 {
00396     Tree    id;
00397     int     ins, outs;
00398 
00399     //cerr << t << " generateDiagramSchema " << boxpp(t)<< endl;
00400 
00401     if (getDefNameProperty(t, id)) {
00402         stringstream    s; s << tree2str(id);
00403         //cerr << t << "\tNAMED : " << s.str() << endl;
00404     }
00405 
00406     if ( sFoldingFlag && /*(gOccurrences->getCount(t) > 0) &&*/
00407             (boxComplexity(t) > 2) && getDefNameProperty(t, id)) {
00408         char    temp[1024];
00409         getBoxType(t, &ins, &outs);
00410         stringstream s, l;
00411         s << tree2str(id);
00412         l << legalFileName(t,1024,temp) << "." << gDevSuffix;
00413         scheduleDrawing(t);
00414         return makeBlockSchema(ins, outs, s.str(), linkcolor, l.str());
00415 
00416     } else  if (getDefNameProperty(t, id) && ! isPureRouting(t)) {
00417         // named case : not a slot, with a name
00418         // draw a line around the object with its name
00419         stringstream    s; s << tree2str(id);
00420         return makeDecorateSchema(generateInsideSchema(t), 10, s.str());
00421 
00422     } else {
00423         // normal case
00424         return generateInsideSchema(t);
00425     }
00426 }
00427 
00428 
00429 
00434 static schema* generateInsideSchema(Tree t)
00435 {
00436     Tree a, b, ff, l, type,name,file;
00437     int     i;
00438     double  r;
00439     prim0   p0;
00440     prim1   p1;
00441     prim2   p2;
00442     prim3   p3;
00443     prim4   p4;
00444     prim5   p5;
00445 
00446 
00447     xtended* xt = (xtended*)getUserData(t);
00448 
00449     if (xt)                         { return makeBlockSchema(xt->arity(), 1, xt->name(), normalcolor, ""); }
00450 
00451     else if (isInverter(t))         { return makeInverterSchema(invcolor); }
00452 
00453     else if (isBoxInt(t, &i))       { stringstream  s; s << i; return makeBlockSchema(0, 1, s.str(), numcolor, "" ); }
00454     else if (isBoxReal(t, &r))      { stringstream  s; s << r; return makeBlockSchema(0, 1, s.str(), numcolor, "" ); }
00455     else if (isBoxWire(t))          { return makeCableSchema(); }
00456     else if (isBoxCut(t))           { return makeCutSchema();  }
00457 
00458     else if (isBoxPrim0(t, &p0))    { return makeBlockSchema(0, 1, prim0name(p0), normalcolor, ""); }
00459     else if (isBoxPrim1(t, &p1))    { return makeBlockSchema(1, 1, prim1name(p1), normalcolor, ""); }
00460     else if (isBoxPrim2(t, &p2))    { return makeBlockSchema(2, 1, prim2name(p2), normalcolor, ""); }
00461     else if (isBoxPrim3(t, &p3))    { return makeBlockSchema(3, 1, prim3name(p3), normalcolor, ""); }
00462     else if (isBoxPrim4(t, &p4))    { return makeBlockSchema(4, 1, prim4name(p4), normalcolor, ""); }
00463     else if (isBoxPrim5(t, &p5))    { return makeBlockSchema(5, 1, prim5name(p5), normalcolor, ""); }
00464 
00465     else if (isBoxFFun(t, ff))                  { return makeBlockSchema(ffarity(ff), 1, ffname(ff), normalcolor, ""); }
00466     else if (isBoxFConst(t, type,name,file))    { return makeBlockSchema(0, 1, tree2str(name), normalcolor, ""); }
00467     else if (isBoxFVar (t, type, name,file))    { return makeBlockSchema(0, 1, tree2str(name), normalcolor, ""); }
00468 
00469     else if (isBoxButton(t))        { return generateUserInterfaceSchema(t); }
00470     else if (isBoxCheckbox(t))      { return generateUserInterfaceSchema(t); }
00471     else if (isBoxVSlider(t))       { return generateUserInterfaceSchema(t); }
00472     else if (isBoxHSlider(t))       { return generateUserInterfaceSchema(t); }
00473     else if (isBoxNumEntry(t))      { return generateUserInterfaceSchema(t); }
00474     else if (isBoxVBargraph(t))     { return generateBargraphSchema(t); }
00475     else if (isBoxHBargraph(t))     { return generateBargraphSchema(t); }
00476 
00477     // don't draw group rectangle when labels are empty (ie "")
00478     else if (isBoxVGroup(t,l,a))    {   stringstream s; s << "vgroup(" << extractName(l) << ")";
00479                                         schema* r = generateDiagramSchema(a);
00480                                         return makeDecorateSchema(r, 10, s.str()); }
00481     else if (isBoxHGroup(t,l,a))    {   stringstream s; s << "hgroup(" << extractName(l) << ")";
00482                                         schema* r = generateDiagramSchema(a);
00483                                         return makeDecorateSchema(r, 10, s.str()); }
00484     else if (isBoxTGroup(t,l,a))    {   stringstream s; s << "tgroup(" << extractName(l) << ")";
00485                                         schema* r = generateDiagramSchema(a);
00486                                         return makeDecorateSchema(r, 10, s.str()); }
00487 
00488     else if (isBoxSeq(t, a, b))     { return makeSeqSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
00489     else if (isBoxPar(t, a, b))     { return makeParSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
00490     else if (isBoxSplit(t, a, b))   { return makeSplitSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
00491     else if (isBoxMerge(t, a, b))   { return makeMergeSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
00492     else if (isBoxRec(t, a, b))     { return makeRecSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
00493 
00494     else if (isBoxSlot(t, &i))      { return generateOutputSlotSchema(t); }
00495     else if (isBoxSymbolic(t,a,b))  {
00496         Tree    id;
00497         if (getDefNameProperty(t, id)) {
00498             return generateAbstractionSchema(generateInputSlotSchema(a), b);
00499         } else {
00500             return makeDecorateSchema(generateAbstractionSchema(generateInputSlotSchema(a), b), 10, "Abstraction");
00501         }
00502     }
00503 
00504     else {
00505 
00506         fprintf(stderr, "Internal Error, box expression not recognized : "); print(t, stderr); fprintf(stderr, "\n");
00507         exit(1);
00508 
00509     }
00510 }
00511 
00515 static void UserInterfaceDescription(Tree box, string& d)
00516 {
00517     Tree    t1, label, cur, min, max, step;
00518     stringstream    fout;
00519     // user interface
00520          if (isBoxButton(box, label))   fout << "button(" << extractName(label) << ')';
00521     else if (isBoxCheckbox(box, label)) fout << "checkbox(" << extractName(label) << ')';
00522     else if (isBoxVSlider(box, label, cur, min, max, step))     {
00523         fout << "vslider("
00524              << extractName(label) << ", "
00525              << boxpp(cur) << ", "
00526              << boxpp(min) << ", "
00527              << boxpp(max) << ", "
00528              << boxpp(step)<< ')';
00529     }
00530     else if (isBoxHSlider(box, label, cur, min, max, step))     {
00531         fout << "hslider("
00532              << extractName(label) << ", "
00533              << boxpp(cur) << ", "
00534              << boxpp(min) << ", "
00535              << boxpp(max) << ", "
00536              << boxpp(step)<< ')';
00537     }
00538     else if (isBoxVGroup(box, label, t1)) {
00539         fout << "vgroup(" << extractName(label) << ", " << boxpp(t1, 0) << ')';
00540     }
00541     else if (isBoxHGroup(box, label, t1)) {
00542         fout << "hgroup(" << extractName(label) << ", " << boxpp(t1, 0) << ')';
00543     }
00544     else if (isBoxTGroup(box, label, t1)) {
00545         fout << "tgroup(" << extractName(label) << ", " << boxpp(t1, 0) << ')';
00546     }
00547     else if (isBoxHBargraph(box, label, min, max))  {
00548         fout << "hbargraph("
00549              << extractName(label) << ", "
00550              << boxpp(min) << ", "
00551              << boxpp(max) << ')';
00552     }
00553     else if (isBoxVBargraph(box, label, min, max))  {
00554         fout << "vbargraph("
00555              << extractName(label) << ", "
00556              << boxpp(min) << ", "
00557              << boxpp(max) << ')';
00558     }
00559     else if (isBoxNumEntry(box, label, cur, min, max, step))    {
00560         fout << "nentry("
00561              << extractName(label) << ", "
00562              << boxpp(cur) << ", "
00563              << boxpp(min) << ", "
00564              << boxpp(max) << ", "
00565              << boxpp(step)<< ')';
00566     }
00567     else {
00568         cerr << "INTERNAL ERROR : unknow user interface element " << endl;
00569         exit(0);
00570     }
00571     d = fout.str();
00572 }
00573 
00574 
00578 static schema*  generateUserInterfaceSchema(Tree t)
00579 {
00580     string s; UserInterfaceDescription(t,s);
00581     return makeBlockSchema(0, 1, s, uicolor, "");
00582 }
00583 
00584 
00588 static schema* generateBargraphSchema(Tree t)
00589 {
00590     string s; UserInterfaceDescription(t,s);
00591     return makeBlockSchema(1, 1, s, uicolor, "");
00592 }
00593 
00594 
00595 
00599 static schema* generateInputSlotSchema(Tree a)
00600 {
00601     Tree id; assert(getDefNameProperty(a, id));
00602     stringstream s; s << tree2str(id);
00603     return makeBlockSchema(1, 0, s.str(), slotcolor, "");
00604 }
00605 
00606 
00607 
00611 static schema* generateOutputSlotSchema(Tree a)
00612 {
00613     Tree id; assert(getDefNameProperty(a, id));
00614     stringstream s; s << tree2str(id);
00615     return makeBlockSchema(0, 1, s.str(), slotcolor, "");
00616 }
00617 
00618 
00619 
00624 static schema* generateAbstractionSchema(schema* x, Tree t)
00625 {
00626     Tree    a,b;
00627 
00628     while (isBoxSymbolic(t,a,b)) {
00629         x = makeParSchema(x, generateInputSlotSchema(a));
00630         t = b;
00631     }
00632     return makeSeqSchema(x, generateDiagramSchema(t));
00633 }
00634