edits
[cdsspec-compiler.git] / src / edu / uci / eecs / specExtraction / InterfaceConstruct.java
1 package edu.uci.eecs.specExtraction;
2
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.regex.Matcher;
7 import java.util.regex.Pattern;
8
9 import edu.uci.eecs.specExtraction.SpecUtils.IntObj;
10 import edu.uci.eecs.specExtraction.SpecUtils.Primitive;
11 import edu.uci.eecs.utilParser.ParseException;
12 import edu.uci.eecs.utilParser.UtilParser;
13
14 /**
15  * <p>
16  * This class is a subclass of Construct. It represents a complete interface
17  * annotation.
18  * </p>
19  * 
20  * @author Peizhao Ou
21  * 
22  */
23 public class InterfaceConstruct extends Construct {
24         // The interface label of the current interface; If not specified, we use
25         // the actual interface name to represent this interface
26         private String name;
27         // Each interface interface will have an auto-generated struct that
28         // represents the return value and the arguments of that interface method
29         // call. We will mangle the interface label name to get this name in case we
30         // have other name conflict
31         private String structName;
32         public final Code preCondition;
33         public final Code justifyingCondition;
34         public final Code transition;
35         public final Code postCondition;
36         public final Code print;
37
38         // The ending line number of the specification annotation construct
39         public final int endLineNum;
40
41         // The ending line number of the function definition
42         private int endLineNumFunction;
43         // The function header of the corresponding interface --- The list of
44         // variable declarations that represent the RETURN value and
45         // arguments of the interface
46         private FunctionHeader funcHeader;
47
48         public final boolean autoGenPrint;
49
50         public InterfaceConstruct(File file, int beginLineNum, int endLineNum,
51                         ArrayList<String> annotations) throws WrongAnnotationException {
52                 super(file, beginLineNum);
53                 this.endLineNum = endLineNum;
54                 this.name = null;
55                 this.structName = null;
56                 this.preCondition = new Code();
57                 this.justifyingCondition = new Code();
58                 this.transition = new Code();
59                 this.postCondition = new Code();
60                 this.print = new Code();
61
62                 processAnnotations(annotations);
63
64                 autoGenPrint = print.isEmpty();
65         }
66
67         public FunctionHeader getFunctionHeader() {
68                 return this.funcHeader;
69         }
70
71         public boolean equals(Object other) {
72                 if (!(other instanceof InterfaceConstruct)) {
73                         return false;
74                 }
75                 InterfaceConstruct o = (InterfaceConstruct) other;
76                 if (o.name.equals(this.name))
77                         return true;
78                 else
79                         return false;
80         }
81
82         public String getName() {
83                 return this.name;
84         }
85
86         /**
87          * <p>
88          * This function will automatically generate the printing statements for
89          * supported types if the user has not defined the "@Print" primitive
90          * </p>
91          * 
92          * @return The auto-generated state printing statements
93          * @throws WrongAnnotationException
94          */
95         private Code generateAutoPrintFunction() {
96                 Code code = new Code();
97                 // For RET
98                 code.addLines(SpecUtils.generatePrintStatement(funcHeader.returnType,
99                                 SpecNaming.RET));
100                 // For arguments
101                 for (VariableDeclaration decl : funcHeader.args) {
102                         String type = decl.type;
103                         String name = decl.name;
104                         code.addLines(SpecUtils.generatePrintStatement(type, name));
105                 }
106                 return code;
107         }
108
109         /**
110          * <p>
111          * Assert that the interface primitive is valid; if not, throws an exception
112          * </p>
113          * 
114          * @param file
115          *            Current file
116          * @param primitive
117          *            The primitive string that we have extracted earlier
118          * @throws WrongAnnotationException
119          */
120         private void assertValidPrimitive(File file, Primitive primitive)
121                         throws WrongAnnotationException {
122                 int lineNum = primitive.beginLineNum;
123                 String name = primitive.name;
124                 if (!name.equals(SpecNaming.Interface)
125                                 && !name.equals(SpecNaming.Transition)
126                                 && !name.equals(SpecNaming.PreCondition)
127                                 && !name.equals(SpecNaming.JustifyingCondition)
128                                 && !name.equals(SpecNaming.SideEffect)
129                                 && !name.equals(SpecNaming.PostCondition)
130                                 && !name.equals(SpecNaming.PrintValue)) {
131                         WrongAnnotationException.err(file, lineNum, name
132                                         + " is NOT a valid CDSSpec interface primitive.");
133                 }
134         }
135
136         /**
137          * <p>
138          * Assert that the "@Interface" primitive has correct syntax; if not, throws
139          * an exception. If so, it basically checks whether content of the primitive
140          * is a valid word and then return interface label name.
141          * </p>
142          * 
143          * @param file
144          *            Current file
145          * @param lineNum
146          *            Current line number
147          * @param primitive
148          *            The primitive string that we have extracted earlier
149          * @throws WrongAnnotationException
150          */
151         private String extractInterfaceName(File file, Primitive primitive)
152                         throws WrongAnnotationException {
153                 int lineNum = primitive.beginLineNum;
154                 String name = primitive.name;
155                 if (primitive.contents.size() != 1)
156                         WrongAnnotationException.err(file, lineNum,
157                                         "The @Interface primitive: " + name + " has wrong syntax.");
158                 String line = primitive.contents.get(0);
159                 SpecUtils.matcherWord.reset(line);
160                 if (!SpecUtils.matcherWord.find())
161                         WrongAnnotationException.err(file, lineNum, name
162                                         + " is NOT a valid CDSSpec @Interface primitive.");
163                 return line;
164         }
165
166         private void processAnnotations(ArrayList<String> annotations)
167                         throws WrongAnnotationException {
168                 IntObj curIdx = new IntObj(0);
169                 Primitive primitive = null;
170
171                 while ((primitive = SpecUtils.extractPrimitive(file, beginLineNum,
172                                 annotations, curIdx)) != null) {
173                         String name = primitive.name;
174                         assertValidPrimitive(file, primitive);
175                         if (primitive.contents.size() == 0)
176                                 continue;
177                         if (name.equals(SpecNaming.Interface)) {
178                                 String interName = extractInterfaceName(file, primitive); 
179                                 setNames(interName);
180                         } else if (name.equals(SpecNaming.Transition)) {
181                                 this.transition.addLines(primitive.contents);
182                         } else if (name.equals(SpecNaming.PreCondition)) {
183                                 this.preCondition.addLines(primitive.contents);
184                         } else if (name.equals(SpecNaming.JustifyingCondition)) {
185                                 this.justifyingCondition.addLines(primitive.contents);
186                         } else if (name.equals(SpecNaming.PostCondition)) {
187                                 this.postCondition.addLines(primitive.contents);
188                         } else if (name.equals(SpecNaming.PrintValue)) {
189                                 this.print.addLines(primitive.contents);
190                         }
191                 }
192         }
193
194         /**
195          * <p>
196          * This function is called to extract all the declarations that should go to
197          * the corresponding value struct --- a C++ struct to be generated for this
198          * interface that contains the information of the return value and the
199          * arguments.
200          * </p>
201          * 
202          * @param line
203          *            The line that represents the interface declaration line
204          * @throws ParseException
205          */
206         public void processFunctionDeclaration(String line) throws ParseException {
207                 // FIXME: Currently we only allow the declaration to be one-liner
208                 funcHeader = UtilParser.parseFuncHeader(line);
209                 // Record the original declaration line
210                 funcHeader.setHeaderLine(line);
211
212                 // If users have not defined @Interface, we should use the function name
213                 // as the interface label
214                 if (name == null) {
215                         setNames(funcHeader.funcName.bareName);
216                 }
217
218                 // Once we have the compelte function declaration, we can auto-gen the
219                 // print-out statements if it's not defined
220                 if (autoGenPrint) {
221                         print.addLines(generateAutoPrintFunction());
222                 }
223         }
224
225         public String toString() {
226                 StringBuilder sb = new StringBuilder();
227                 sb.append(super.toString() + "\n");
228                 sb.append("@Interface: " + name + "\n");
229                 if (!transition.isEmpty())
230                         sb.append("@Transition:\n" + transition);
231                 if (!preCondition.isEmpty())
232                         sb.append("@PreCondition:\n" + preCondition);
233                 if (!postCondition.isEmpty())
234                         sb.append("@PostCondition:\n" + postCondition);
235                 if (!print.isEmpty())
236                         sb.append("@Print:\n" + print + "\n");
237                 sb.append(funcHeader);
238
239                 return sb.toString();
240         }
241
242         public int getEndLineNumFunction() {
243                 return endLineNumFunction;
244         }
245
246         public void setEndLineNumFunction(int endLineNumFunction) {
247                 this.endLineNumFunction = endLineNumFunction;
248         }
249
250         public String getStructName() {
251                 return structName;
252         }
253
254         private void setNames(String name) {
255                 this.name = name;
256                 if (name == null)
257                         return;
258                 structName = createStructName(name);
259         }
260         
261         static public String createStructName(String labelName) {
262                 return "__struct_" + labelName + "__";
263         }
264 }