Q1: How do I get the line numbers for each AST node? CUP gives ClassCastExceptions/NullPointerException etc.
A1: The problem is that CUP returns the value field of Token; not the Token itself. Really the best solution is to modify your scanner so that it stores the Token in the value field of Symbol. Here is approximate code in IC.lex:

"class" {: return new Symbol(...new Token(..line..)...) :}.

Then, in IC.cup you write something like:

terminal Token CLASS;
CLASS_DECL ::= CLASS:cl ID...
{: RESULT = new ICCLass(...cl.getLine(); :}


However, if you give approximate line numbers or information about the construct where the program occurred (e.g., "Error in class A"), that is fine too.

Here is another way, suggested by Eli Romm:
note that in class symbol there are 2 fields "left" and "right" that can be used to pass data to the cup. So, by adding this line to the Token constructor we can save the line number in the symbol this.left=line;. Then, if in the cup we have a rule like rule::=ID:data RP RP , we can access the line number of the ID token by dataleft. For example, rule::=ID:data RP RP: {: System.out.println(dataleft); :}

Q2: What if there are several methods named main in the code, some are static, some are not, some have the right signature some not.
A2: You only care about static methods that have the exact signature - ones thay accepts one argument of type string[] (the name of the argument is not important) and return void. Any method that does not match these criteria is not a main method. The code must have exactly one main method.

Q3: What do you mean by "verify that the library class has the correct name?"
A3: You compiler should parse libic.sig (either in the current library or in the path specified by -L), using the library parser. When you parse that file you should get an AST where the root node is a class node. Each class node know the name of the class (by a string). Just check that the string equals("Library"), which is what we expect to have in libic.sig.

Q4: The Library class. What is it? What should we do with it?
A4: Your compiler should always attempt to find the file libic.sig, either in the current directory or in the path specified by the -L option. It should then parse the file, build its AST and add the class you get to the main AST.
The easiest way to do this is by making a copy of the IC.cup file and making the class non terminal the start terminal (or just removing the rules for the main program and list of classes).
Please also see the last lines of Section 16 of the IC specification about the -L option.