./graph < fsm > fsm.dot
In this homework you are going to use Bison to write a translator from the graph language I gave you in homework 4, problem 8 to a graph specification language called dot. Using a program called dot, you can cause your .dot file to be laid out as a graph and printed in a number of formats. For example:
unix> dot -Tps -o fsm.ps fsm.dotcreates a postscript version of the graph and stores it in fsm.ps. The -o flag declares the output file and the -T file declares the format in which you would like the graph to be written. Other common file formats in which to write the output are png, -Tpng, and gif, -Tgif. As in homework 4, you may do either the edges version for 80 points or the full version for 100 points.
I am going to give you general translation instructions for dot. You will need to map these translation instructions to the specific productions in your grammar. If you have a question about how a translation should be performed, please try to write a small sample program first and run my executable on it, before sending email to piazza. You can also consult the dot manual.
digraph { ... dot instructions }
rankdir = LR; // a horizontally laid out graph rankdir = TB; // a vertically laid out graph
node [ attribute assignments ] nodename1 nodename2 ... nodenamen;For example:
node [ style = filled color = green shape = box ] CS102 CS140 CS160 CS302 CS311 CS312 CS360 CS361 CS365 CS400; node [ style = filled color = orange shape = ellipse ] MATH141 MATH142 MATH241 MATH251 ECE313;Note that you need to add a "style = filled" attribute assignment to the dot specification. If you fail to do so, then the node will not be filled.
nodename [ label = "node label" ];For example:
CS102 [ label = "CS102\nC++" ]; CS140 [ label = "CS140\nC++" ]; CS160 [ label = "CS160\nC" ];
start -> state1 [ label = "/" ]; state1 -> state2 [ label = "*" ]; state1 -> state5 [ label = "/" ];
CS102-> CS140; CS102-> CS160;
MATH251-> CS370 [ style = filled color = orange fontSize = 10 ]; ECE313-> CS425 [ style = filled color = blue fontSize = 10 ]; MATH142-> CS311 [ style = filled color = blue fontSize = 10 ];
MATH251-> CS370 [ label = "optional prereq" style = filled color = orange fontSize = 10 ]; MATH142-> CS311 [ label = "co-req" style = filled color = orange fontSize = 10 ];You will need to copy your edge style information to each edge that mentions that edge style. For example, if your specification reads:
edgestyle mathPrereq [color = orange, fontsize = 10]; ... MATH251 -> CS370 mathPrereq;your output will be:
MATH251 -> CS370 [style = filled color = orange, fontsize = 10];The line "edgestyle mathPrereq ..." will not appear in your dot specification.
Here are two sample translations from my language to a dot specification for both the "edges" and "full" version:
You may be able to output the dot specification at the same time that you parse your file but I am not sure. If you try to do it with a single pass, then you will need to store some information, such as the edge style information, in a global variable (e.g., an STL hash table) so that you can access it later. You may also have to do some clever hacking to produce the outer "shell", such as producing the "digraph {" preamble before you call your parser in main and the "}" postscript when you return from the parser.
I created classes for each node in my abstract syntax tree using the technique I described in class (i.e., a class for each non-terminal and subclasses for each production). I created the abstract syntax tree during the parse and then walked the syntax tree after I returned from parse and printed the dot specification.
With each class I associated a "print" method that knew how to translate from that production to a dot specification. As a very simple example, the "print" method for my color attribute was:
void colorAttribute::print() { printf(" style = filled color = %s", color.c_str()); }where color was an instance variable that stored the color from my specification.
My root node simply iterated through each of the major parts of the graph specification and called their print methods. Its pseudo-code looked like:
void graphNode::print() { // print the preamble cout << "digraph {" << endl; // print the direction, if there was one if direction was specified then graphDirection->print(); // enumerate the node styles for each nodestyle @in; nodeStyles nodestyle->print() // enumerate any labels associated with nonenames for each nodelabel @in; nodeLabels nodelabel->print() // enumerate the edgelists for each node for each adjacencyList @in; edgeList adjacencyList->print() // print the postscript cout << "}" << endl; }My main procedure that invoked the parser and then the root node's print method look like:
main() { yyparse(); root->print(); }I declared root as a graphNode * in the top section of my bison code.