Homework 6


Announcements


  1. There are executables named graph and edge in ~bvz/cs461/hw/hw6 that you can use to check your output. The formatting of your output does not need to match mine, but you will probably need to have the same directives. A sample invocation of one of my executables would be:
    ./graph < fsm > fsm.dot
    
  2. You will be using dot rather than graphviz to produce a graph from your .dot files. See the body of the assignment for details on how to use dot.
  3. Make sure that when you create .dot files, that you create them with a .dot suffix. If you don't, then graphviz won't open them.

Introduction

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.dot
creates 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.

Translation Instructions

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.

  1. Every dot file has the following outer "shell":
    digraph {
      ... dot instructions
    }
    
  2. direction: The of a dot graph is specified using rankdir
    rankdir = LR;  // a horizontally laid out graph
    rankdir = TB;  // a vertically laid out graph
    
  3. node styles: nodestyle directives in dot take the form:
    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.

  4. node labels: you can assign a label to a node in dot using the notation
    nodename [ label = "node label" ];
    
    For example:
     
    CS102 [ label = "CS102\nC++" ];
    CS140 [ label = "CS140\nC++" ];
    CS160 [ label = "CS160\nC" ];
    
  5. edges: edges are specified one at a time in dot, with the from and to nodes separated by an arrow (->). Edges may include an optional label and style information. Here are several examples:
    1. Edges with labels
      start -> state1 [ label = "/" ];
      state1 -> state2 [ label = "*" ];
      state1 -> state5 [ label = "/" ];
      
    2. Edges without labels:
      CS102-> CS140;
      CS102-> CS160;
      
    3. Edges without labels but with style information:
      MATH251-> CS370 [ style = filled color = orange fontSize = 10 ];
      ECE313-> CS425 [ style = filled color = blue fontSize = 10 ];
      MATH142-> CS311 [ style = filled color = blue fontSize = 10 ];
      
    4. Edges with both labels and style information:
      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.

Sample Translations

Here are two sample translations from my language to a dot specification for both the "edges" and "full" version:

  1. edges version
    1. finite state machine and fsm-edges.dot
    2. CS pre-req graph and prereq-edges.dot.
  2. full version
    1. finite state machine and fsm.dot
    2. CS pre-req graph and prereq-edges.dot.

Implementation

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.

Submission Instructions

  1. Put your bison specification in a file named graph.yacc.
  2. Put your lex specification in a file named graph.lex.
  3. Create a makefile for your program and place it in a file named makefile.
  4. If you use any auxiliary C++ files, such as for classes for your abstract syntax tree, include the .cpp files and .h files in your submission.
  5. Create a README file that indicates whether you implemented the small or full version of the grammar.
  6. Submit all your files using the 461_submit script.