Inferno
0.2
|
00001 /* 00002 * graph.cpp 00003 * 00004 * Created on: 18 Jul 2010 00005 * Author: jgraley 00006 */ 00007 00008 #include "tree/cpptree.hpp" 00009 #include "helpers/transformation.hpp" 00010 #include "sr/search_replace.hpp" 00011 #include "sr/soft_patterns.hpp" 00012 #include "common/trace.hpp" 00013 #include "common/read_args.hpp" 00014 #include "graph.hpp" 00015 #include "steps/inferno_patterns.hpp" 00016 #include <inttypes.h> 00017 00018 using namespace CPPTree; 00019 00020 // Graph Documantation 00021 // 00022 // The shapes and contents of the displayed nodes is explained in comments in the function 00023 // Graph::Name(), which may be found below. Ordinary tree nodes are always rectangles with 00024 // soft corners, so this function mainly deals with special nodes as used by Search and Replace. 00025 // The colours are defined in Graph::Colour, which categorises nodes (all nodes, including Special) 00026 // by whether they are derived from particular intermediate nodes, which are easily seen in 00027 // the code of the function. 00028 00029 00030 // TODO indicate pre-restriction by putting class name over the link. Only when type is not that 00031 // of the pointer, ie a non-trivial pre-estriction 00032 // TODO indicate Stuff restrictor by making it come out of the top of the circle (note that it will 00033 // be used on search, and usually search Stuff is coupled to replace Stuff, which will be below. 00034 // TODO force ranking to space out graph as 00035 // Primary: Stuff nodes, SearchReplace 00036 // Secondary: Normal nodes and special nodes that occupy space 00037 // Tertiary: CompareReplace and special nodes that do not occupy space 00038 00039 #define FS_SMALL "12" 00040 #define FS_MIDDLE "14" 00041 #define FS_LARGE "16" 00042 #define NS_SMALL "0.4" 00043 //#define FONT "Arial" 00044 00045 Graph::Graph( string of ) : 00046 outfile(of) 00047 { 00048 } 00049 00050 void Graph::operator()( Transformation *root ) 00051 { 00052 string s; 00053 s += Header(); 00054 s += MakeGraphTx(root); 00055 s += Footer(); 00056 Disburse( s ); 00057 } 00058 00059 string Graph::MakeGraphTx(Transformation *root) 00060 { 00061 string s; 00062 if( TransformationVector *tv = dynamic_cast<TransformationVector *>(root) ) 00063 { 00064 FOREACH( shared_ptr<Transformation> t, *tv ) 00065 s = MakeGraphTx( t.get() ) + s; // seem to have to pre-pend to get them appearing in the right order 00066 } 00067 else 00068 { 00069 unique_filter.Reset(); 00070 s += UniqueWalk( root, Id(root), false ); 00071 unique_filter.Reset(); 00072 s += UniqueWalk( root, Id(root), true ); 00073 } 00074 return s; 00075 } 00076 00077 00078 TreePtr<Node> Graph::operator()( TreePtr<Node> context, TreePtr<Node> root ) 00079 { 00080 (void)context; // Not needed!! 00081 00082 string s; 00083 s += Header(); 00084 unique_filter.Reset(); 00085 s += UniqueWalk( root, false ); 00086 unique_filter.Reset(); 00087 s += UniqueWalk( root, true ); 00088 s += Footer(); 00089 Disburse( s ); 00090 00091 return root; // no change 00092 } 00093 00094 00095 string Graph::Header() 00096 { 00097 string s; 00098 s += "digraph Inferno {\n"; // g is name of graph 00099 s += "graph [\n"; 00100 s += "rankdir = \"LR\"\n"; // left-to-right looks more like source code 00101 s += "size = \"14,20\"\n"; // make it smaller 00102 // s += "concentrate = \"true\"\n"; 00103 s += "];\n"; 00104 s += "node [\n"; 00105 #ifdef FONT 00106 s += "fontname = \"" FONT "\"\n"; 00107 #endif 00108 s += "];\n"; 00109 return s; 00110 } 00111 00112 00113 string Graph::Footer() 00114 { 00115 string s; 00116 s += "}\n"; 00117 return s; 00118 } 00119 00120 00121 void Graph::Disburse( string s ) 00122 { 00123 if( outfile.empty() ) 00124 { 00125 puts( s.c_str() ); 00126 } 00127 else 00128 { 00129 FILE *fp = fopen( outfile.c_str(), "wt" ); 00130 ASSERT( fp )( "Cannot open output file \"%s\"", outfile.c_str() ); 00131 fputs( s.c_str(), fp ); 00132 fclose( fp ); 00133 } 00134 } 00135 00136 00137 string Graph::UniqueWalk( TreePtr<Node> root, bool links_pass ) 00138 { 00139 string s; 00140 TRACE("Graph plotter traversing intermediate %s pass\n", links_pass ? "links" : "nodes"); 00141 ::UniqueWalk w( root ); 00142 FOREACH( TreePtr<Node> n, w ) 00143 { 00144 if( n ) 00145 s += links_pass ? DoNodeLinks(n) : DoNode(n); 00146 } 00147 return s; 00148 } 00149 00150 00151 string Graph::UniqueWalk( Transformation *sr, string id, bool links_pass ) 00152 { 00153 string s; 00154 s += links_pass ? DoTransformationLinks(sr, id) : DoTransformation(sr, id); 00155 00156 struct : public Filter 00157 { 00158 virtual bool IsMatch( TreePtr<Node> context, 00159 TreePtr<Node> root ) 00160 { 00161 return !( !dynamic_pointer_cast<TransformOfBase>(root) && 00162 !dynamic_pointer_cast<BuildIdentifierBase>(root) && 00163 dynamic_cast<Transformation*>(root.get()) ); 00164 } 00165 } no_tx_filter; 00166 00167 vector<string> labels; 00168 vector< TreePtr<Node> > links; 00169 (void)sr->GetGraphInfo( &labels, &links ); 00170 00171 FOREACH( TreePtr<Node> pattern, links ) 00172 { 00173 if( pattern ) 00174 { 00175 TRACE("Walking transform pattern ")(*pattern)("\n"); 00176 Walk w( pattern, &unique_filter, &no_tx_filter ); // return each node only once; do not recurse through transformations 00177 FOREACH( TreePtr<Node> n, w ) 00178 { 00179 s += links_pass ? DoNodeLinks(n) : DoNode(n); 00180 } 00181 } 00182 } 00183 return s; 00184 } 00185 00186 00187 string Graph::DoTransformation( Transformation *sr, 00188 string id ) 00189 { 00190 vector<string> labels; 00191 vector< TreePtr<Node> > links; 00192 sr->GetGraphInfo( &labels, &links ); 00193 00194 string name = sr->GetName(); 00195 int n; 00196 for( n=0; n<name.size(); n++ ) 00197 { 00198 if( name[n] == '<' ) 00199 { 00200 int nn; 00201 for( nn=n; nn<name.size(); nn++ ) 00202 { 00203 if( name[nn] == '>' ) 00204 { 00205 name = name.substr( 0, n ) + name.substr( nn+1 ); 00206 break; 00207 } 00208 } 00209 } 00210 } 00211 00212 string s; 00213 s += id; 00214 s += " [\n"; 00215 00216 s += "label = \"<fixed> " + Sanitise( name ); 00217 for( int i=0; i<labels.size(); i++ ) 00218 s += " | <" + labels[i] + "> " + labels[i]; 00219 s += "\"\n"; 00220 00221 s += "shape = \"record\"\n"; // nodes can be split into fields 00222 s += "style = \"filled\"\n"; 00223 s += "fontsize = \"" FS_MIDDLE "\"\n"; 00224 s += "];\n"; 00225 00226 return s; 00227 } 00228 00229 00230 string Graph::DoTransformationLinks( Transformation *sr, string id ) 00231 { 00232 vector<string> labels; 00233 vector< TreePtr<Node> > links; 00234 sr->GetGraphInfo( &labels, &links ); 00235 00236 string s; 00237 for( int i=0; i<labels.size(); i++ ) 00238 s += id + ":" + labels[i] + " -> " + Id(links[i].get()) + " [];\n"; 00239 00240 return s; 00241 } 00242 00243 00244 string Graph::Id( void *p ) 00245 { 00246 char s[20]; 00247 sprintf(s, "\"%p\"", p ); 00248 return s; 00249 } 00250 00251 00252 string Graph::SeqField( int i, int j ) 00253 { 00254 char s[20]; 00255 sprintf( s, "port%c%d", 'a'+i, j ); 00256 return s; 00257 } 00258 00259 00260 string Graph::Sanitise( string s, bool remove_tp ) 00261 { 00262 string o; 00263 if( remove_tp ) // get rid of TreePtr<> 00264 s = s.substr( 8, s.size()-9 ); 00265 int n = s.find("::"); 00266 if( n != string::npos ) 00267 s = s.substr( n+2 ); 00268 for( int i=0; i<s.size(); i++ ) 00269 { 00270 if( s[i] == '\"' ) 00271 { 00272 o += "\\\""; 00273 } 00274 else if( s[i] == '<' ) 00275 { 00276 o += "<"; 00277 } 00278 else if( s[i] == '>' ) 00279 { 00280 o += ">"; 00281 } 00282 else 00283 { 00284 o += s[i]; 00285 } 00286 } 00287 return o; 00288 } 00289 00290 00291 string Graph::Name( TreePtr<Node> sp, bool *bold, string *shape ) // TODO put stringize capabilities into the Property nodes as virtual methods 00292 { 00293 // This function does not deal directly with SearchReplace, CompareReplace, SlaveSearchReplace or 00294 // SlaveCompareReplace. These appear in sharp-cornered rectangles, with the name at the top and the 00295 // member TreePtr names below. These may be some combination of search, compare, replace and through 00296 // and their links are approximately to the right. 00297 *bold=true; 00298 if( dynamic_pointer_cast<StarBase>(sp) ) 00299 { 00300 // The Star node appears as a small circle with a * character inside it. * is chosen for its role in 00301 // filename wildcarding, which is semantically equiviant only when used in a Sequence. 00302 *shape = "circle"; 00303 return string("*"); 00304 } 00305 else if( TreePtr<StuffBase> stuff = dynamic_pointer_cast<StuffBase>(sp) ) 00306 { 00307 // The Stuff node appears as a small circle with a # character inside it. The terminus link emerges from the 00308 // right of the circle. If there is a recurse restriction the circle is egg-shaped and the restriction link 00309 // emerges from the top of the egg shape. # is chosen (as is the name Stuff) for its similarity to * because 00310 // the nodes are both able to wildcard multiple nodes in the tree. 00311 if( stuff->recurse_restriction ) 00312 *shape = "egg"; 00313 else 00314 *shape = "circle"; 00315 return string("#"); 00316 } 00317 else if( dynamic_pointer_cast<AnyNodeBase>(sp) ) 00318 { 00319 // The AnyNode node appears as a small circle with a ? sign in it. The terminus link emerges from the 00320 // right of the circle. ? implies the tendancy to match exactly one thing. 00321 *shape = "circle"; 00322 return string("?"); 00323 } 00324 else if( dynamic_pointer_cast<NotMatchBase>(sp) ) 00325 { 00326 // The NotMatch node appears as a small circle with an ! character inside it. The affected subtree is 00327 // on the right. 00328 // NOTE this and the next few special nodes are the nodes that control the action of the search engine in 00329 // Inferno search/replace. They are not the nodes that represent the operations in the program being processed. 00330 // Those nodes would appear as rounded rectangles with the name at the top. The nmes may be found in 00331 // src/tree/operator_db.txt 00332 *shape = "circle"; 00333 return string("!"); 00334 } 00335 else if( dynamic_pointer_cast<MatchAllBase>(sp) ) 00336 { 00337 // The MatchAll node appears as a small circle with an & character inside it. The affected subtrees are 00338 // on the right. 00339 *shape = "circle"; 00340 return string("&"); // note & is a wildcard in dot but not handled properly, this becomes "& ". At least some of the time. 00341 } 00342 else if( dynamic_pointer_cast<MatchAnyBase>(sp) ) 00343 { 00344 // The MatchAny node appears as a small circle with an | character inside it. The affected subtrees are 00345 // on the right. 00346 *shape = "circle"; 00347 return string("|"); 00348 } 00349 else if( dynamic_pointer_cast<MatchOddBase>(sp) ) 00350 { 00351 // The MatchOdd node appears as a small circle with an | character inside it. The affected subtrees are 00352 // on the right. 00353 *shape = "circle"; 00354 return string("^"); 00355 } 00356 else if( shared_ptr<TransformOfBase> tob = dynamic_pointer_cast<TransformOfBase>(sp) ) 00357 { 00358 // The TransformOf node appears as a slightly flattened hexagon, with the name of the specified 00359 // kind of Transformation class inside it. 00360 *shape = "hexagon"; 00361 return *(tob->transformation); 00362 } 00363 else if( dynamic_pointer_cast<PointerIsBase>(sp) ) 00364 { 00365 // The TransformOf node appears as a slightly flattened hexagon, with the name of the specified 00366 // kind of Transformation class inside it. 00367 *shape = "pentagon"; 00368 return string("pointer is"); 00369 } 00370 else if( shared_ptr<BuildIdentifierBase> smi = dynamic_pointer_cast<BuildIdentifierBase>(sp) ) 00371 { 00372 // The BuildIdentifier node appears as a parallelogram (rectangle pushed to the side) with 00373 // the printf format string that controls the name of the generated identifier inside it. 00374 // TODO indicate whether it's building instance, label or type identifier 00375 *shape = "parallelogram"; 00376 return smi->format; 00377 } 00378 else if( shared_ptr<IdentifierByNameBase> ibnb = dynamic_pointer_cast<IdentifierByNameBase>(sp) ) 00379 { 00380 // The IdentifierByNameBase node appears as a trapezium (rectangle narrower at the top) with 00381 // the string that must be matched inside it. 00382 // TODO indicate whether it's matching instance, label or type identifier 00383 *shape = "trapezium"; 00384 return ibnb->name; 00385 } 00386 else if( dynamic_pointer_cast<GreenGrassBase>(sp) ) 00387 { 00388 // The GreenGrass node appears as a small circle containing four vertical line characters, 00389 // like this: ||||. These are meant to represent the blades of grass. It was late and I was 00390 // tired. 00391 *shape = "circle"; 00392 return string("||||"); 00393 } 00394 else if( dynamic_pointer_cast<OverlayBase>(sp) ) 00395 { 00396 // The Overlay node is shown as a small triangle, with the through link on the right and the overlay link 00397 // coming out of the bottom. 00398 *shape = "triangle"; 00399 return string(""); 00400 } 00401 else if( dynamic_pointer_cast<InsertBase>(sp) ) 00402 { 00403 // The Insert node is shown as a small triangle containing a + symbol, with the insert link 00404 // coming out of the bottom 00405 *shape = "triangle"; 00406 return string("+"); 00407 } 00408 else if( dynamic_pointer_cast<EraseBase>(sp) ) 00409 { 00410 // The Erase node is shown as a small triangle containing a - symbol, with the erase link 00411 // coming out of the right 00412 *shape = "triangle"; 00413 return string("-"); 00414 } 00415 else 00416 { 00417 // All the other nodes are represented as a rectangle with curved corners. At the top of the rectangle, 00418 // in large font, is the name of the node's type OR the identifier name if the node is a kind of 00419 // SpecificIdentifier. All TreePtr<>, Sequence<> and Collection<> members are listed below in a 00420 // smaller font. The name of the pointed-to type is given (not the member's name, Inferno cannot deduce 00421 // this). 00422 // Collections appear once and are followed by {...} where the number of dots equals the number of 00423 // elements in the Collection. 00424 // Sequences appear once for each element in the sequence. Each appearance is followed by [i] where 00425 // i is the index, starting from 0. 00426 // All child pointers emerge from *approximately* the right of the corresponding member name. I cannot 00427 // for the life of me get GraphViz to make the lines begin *on* the right edge of the rectangle. They 00428 // always come from some way in from the right edge, and if they are angled up or down, they can appear 00429 // to be coming from the wrong place. 00430 *bold = false; 00431 *shape = "plaintext";//"record"; 00432 return sp->GetName(); 00433 } 00434 } 00435 00436 00437 // Colours are GraphVis colours as listed at http://www.graphviz.org/doc/info/colors.html 00438 string Graph::Colour( TreePtr<Node> n ) 00439 { 00440 if( dynamic_pointer_cast<Identifier>(n) ) 00441 return "gray60"; 00442 else if( dynamic_pointer_cast<Declaration>(n) ) 00443 return "seagreen1"; 00444 else if( dynamic_pointer_cast<MapOperand>(n) ) 00445 return "goldenrod2"; 00446 else if( dynamic_pointer_cast<Type>(n) ) 00447 return "plum"; 00448 else if( dynamic_pointer_cast<Literal>(n) ) 00449 return "goldenrod2"; 00450 else if( dynamic_pointer_cast<Expression>(n) ) 00451 return "chocolate1"; 00452 else if( dynamic_pointer_cast<Property>(n) ) 00453 return "olivedrab3"; 00454 else if( dynamic_pointer_cast<Statement>(n) ) 00455 return "brown1"; 00456 else if( dynamic_pointer_cast<Initialiser>(n) ) 00457 return "olivedrab3"; 00458 else if( dynamic_pointer_cast<Scope>(n) ) 00459 return "cyan"; 00460 else 00461 return ""; 00462 } 00463 00464 00465 string Graph::HTMLLabel( string name, TreePtr<Node> n ) 00466 { 00467 string s = "<<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\">\n"; 00468 s += " <TR>\n"; 00469 s += " <TD><FONT POINT-SIZE=\"" FS_LARGE ".0\">" + Sanitise(name) + "</FONT></TD>\n"; 00470 s += " <TD></TD>\n"; 00471 s += " </TR>\n"; 00472 vector< Itemiser::Element * > members = n->Itemise(); 00473 for( int i=0; i<members.size(); i++ ) 00474 { 00475 if( SequenceInterface *seq = dynamic_cast<SequenceInterface *>(members[i]) ) 00476 { 00477 for( int j=0; j<seq->size(); j++ ) 00478 { 00479 char c[20]; 00480 sprintf(c, "%d", j); 00481 s += " <TR>\n"; 00482 s += " <TD>" + Sanitise(*seq, true) + "[" + string(c) + "]</TD>\n"; 00483 s += " <TD PORT=\"" + SeqField( i, j ) + "\"></TD>\n"; 00484 s += " </TR>\n"; 00485 } 00486 } 00487 else if( TreePtrInterface *ptr = dynamic_cast<TreePtrInterface *>(members[i]) ) 00488 { 00489 if( *ptr ) 00490 { 00491 s += " <TR>\n"; 00492 s += " <TD>" + Sanitise(ptr->GetName(), true) + "</TD>\n"; 00493 s += " <TD PORT=\"" + SeqField( i ) + "\"></TD>\n"; 00494 s += " </TR>\n"; 00495 } 00496 } 00497 else if( CollectionInterface *col = dynamic_cast<CollectionInterface *>(members[i]) ) 00498 { 00499 s += " <TR>\n"; 00500 s += " <TD>" + Sanitise(*col, true) + "{"; 00501 for( int j=0; j<col->size(); j++ ) 00502 s += "."; 00503 s += "}</TD>\n"; 00504 s += " <TD PORT=\"" + SeqField( i ) + "\"></TD>\n"; 00505 s += " </TR>\n"; 00506 } 00507 else 00508 { 00509 ASSERT(0); 00510 } 00511 } 00512 s += "</TABLE>>\n"; 00513 return s; 00514 } 00515 00516 00517 string Graph::SimpleLabel( string name, TreePtr<Node> n ) 00518 { 00519 string s = "\"<fixed> " + Sanitise(name); 00520 vector< Itemiser::Element * > members = n->Itemise(); 00521 for( int i=0; i<members.size(); i++ ) 00522 { 00523 if( SequenceInterface *seq = dynamic_cast<SequenceInterface *>(members[i]) ) 00524 { 00525 for( int j=0; j<seq->size(); j++ ) 00526 { 00527 s += " | <" + SeqField( i, j ) + "> "; 00528 } 00529 } 00530 else if( TreePtrInterface *ptr = dynamic_cast<TreePtrInterface *>(members[i]) ) 00531 { 00532 s += " | <" + SeqField( i ) + "> "; 00533 } 00534 else // Collection 00535 { 00536 s += " | <" + SeqField( i ) + "> ";// \\{\\}"; 00537 } 00538 } 00539 s += "\"\n"; 00540 return s; 00541 } 00542 00543 00544 string Graph::DoNode( TreePtr<Node> n ) 00545 { 00546 if( !dynamic_pointer_cast<TransformOfBase>(n) && !dynamic_pointer_cast<BuildIdentifierBase>(n) ) // ignire the fact that these also derive from Transformation 00547 if( Transformation *rsb = dynamic_cast<Transformation *>(n.get()) ) 00548 return UniqueWalk( rsb, Id( n.get() ), false ); 00549 00550 string s; 00551 bool bold; 00552 string shape; 00553 s += Id(n.get()) + " [\n"; 00554 string name = Name(n, &bold, &shape); 00555 00556 s += "shape = \"" + shape + "\"\n"; 00557 if(shape == "record" || shape == "plaintext") 00558 { 00559 s += "label = " + HTMLLabel( name, n ); 00560 s += "style = \"rounded,filled\"\n"; 00561 s += "fontsize = \"" FS_SMALL "\"\n"; 00562 } 00563 else 00564 { 00565 s += "label = \"" + name + "\"\n";// TODO causes errors because links go to targets meant for records 00566 s += "style = \"filled\"\n"; 00567 00568 if( shape == "circle" || shape=="triangle" ) 00569 { 00570 s += "fixedsize = true\n"; 00571 s += "width = " NS_SMALL "\n"; 00572 s += "height = " NS_SMALL "\n"; 00573 s += "fontsize = \"" FS_LARGE "\"\n"; 00574 } 00575 else 00576 { 00577 s += "fontsize = \"" FS_MIDDLE "\"\n"; 00578 } 00579 } 00580 00581 string c = Colour(n); 00582 if(c!="") 00583 s += "fillcolor = " + c + "\n"; 00584 00585 s += "];\n"; 00586 return s; 00587 } 00588 00589 00590 string Graph::DoNodeLinks( TreePtr<Node> n ) 00591 { 00592 if( !dynamic_pointer_cast<TransformOfBase>(n) && !dynamic_pointer_cast<BuildIdentifierBase>(n) ) // ignire the fact that these also derive from Transformation 00593 if( Transformation *rsb = dynamic_cast<Transformation *>(n.get()) ) 00594 return UniqueWalk( rsb, Id( n.get() ), true ); 00595 00596 string s; 00597 TRACE("Itemising\n"); 00598 vector< Itemiser::Element * > members = n->Itemise(); 00599 TRACE("Doing links for ")(*n)(" size is %d\n", members.size() ); 00600 for( int i=0; i<members.size(); i++ ) 00601 { 00602 if( CollectionInterface *col = dynamic_cast<CollectionInterface *>(members[i]) ) 00603 { 00604 FOREACH( const TreePtrInterface &p, *col ) 00605 s += DoLink( n, SeqField(i), p, string(), &p ); 00606 } 00607 else if( SequenceInterface *seq = dynamic_cast<SequenceInterface *>(members[i]) ) 00608 { 00609 int j=0; 00610 FOREACH( const TreePtrInterface &p, *seq ) 00611 s += DoLink( n, SeqField(i, j++), p, string(), &p ); 00612 } 00613 else if( TreePtrInterface *ptr = dynamic_cast<TreePtrInterface *>(members[i]) ) 00614 { 00615 TRACE("TreePtr %d is @%p\n", i, ptr ); 00616 if( *ptr ) 00617 s += DoLink( n, SeqField(i), *ptr, string(), ptr ); 00618 } 00619 else 00620 { 00621 ASSERT(0)("got something from itemise that isnt a sequence, collection or a shared pointer"); 00622 } 00623 } 00624 return s; 00625 } 00626 00627 00628 bool Graph::IsRecord( TreePtr<Node> n ) 00629 { 00630 bool bold; 00631 string shape; 00632 bool fport, tport; 00633 Name( n, &bold, &shape ); 00634 return shape=="record" || shape=="plaintext"; 00635 } 00636 00637 string Graph::DoLink( TreePtr<Node> from, string field, TreePtr<Node> to, string atts, const TreePtrInterface *ptr ) 00638 { 00639 string s; 00640 s += Id(from.get()); 00641 if( field != "" && IsRecord(from) ) 00642 { 00643 s += ":" + field; 00644 atts = "dir = \"both\"\n"; 00645 atts = "arrowtail = \"dot\"\n"; 00646 // atts = "sametail = \"" + field + "\"\n"; 00647 if( ptr ) // is normal tree link 00648 if( shared_ptr<SpecialBase> sbs = dynamic_pointer_cast<SpecialBase>(to) ) // is to a special node 00649 if( typeid( *ptr ) != typeid( *(sbs->GetPreRestrictionArchitype()) ) ) // pre-restrictor is nontrivial 00650 { 00651 atts += "label = \"" + (**(sbs->GetPreRestrictionArchitype())).GetName() + "\"\n"; 00652 } 00653 } 00654 else if( dynamic_pointer_cast<StuffBase>(from) ) 00655 { 00656 if( field == "porta0" ) 00657 s+= ":n"; 00658 else 00659 s+= ":e"; 00660 } 00661 else if( dynamic_pointer_cast<OverlayBase>(from) ) 00662 { 00663 if( field == "portb0" ) 00664 s+= ":s"; 00665 else 00666 s+= ":e"; 00667 } 00668 else if( dynamic_pointer_cast<InsertBase>(from) ) 00669 { 00670 s+= ":s"; 00671 } 00672 00673 else if( dynamic_pointer_cast<EraseBase>(from) ) 00674 { 00675 s+= ":e"; 00676 } 00677 00678 s += " -> "; 00679 s += Id(to.get()); 00680 s += " ["+atts+"];\n"; 00681 return s; 00682 } 00683