edits
[cdsspec-compiler.git] / src / edu / uci / eecs / specExtraction / SpecUtils.java
1 package edu.uci.eecs.specExtraction;
2
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.regex.Matcher;
6 import java.util.regex.Pattern;
7
8 /**
9  * <p>
10  * This class contains a list of static utility functions for extracting
11  * specification annotations more conveniently.
12  * </p>
13  * 
14  * @author Peizhao Ou
15  * 
16  */
17 public class SpecUtils {
18
19         /**
20          * <p>
21          * This inner class defines a primitive --- a sub-annotation. For example,
22          * the "@DeclareState" in the global state is one primitive. We record the
23          * beginning line number of that primitive, the name of the primitive and a
24          * list of lines that belong to that primitive.
25          * </p>
26          * 
27          * @author Peizhao Ou
28          * 
29          */
30         public static class Primitive {
31                 public final int beginLineNum;
32                 public final String name;
33                 public final ArrayList<String> contents;
34
35                 public Primitive(String name, int beginLineNum) {
36                         this.name = name;
37                         this.beginLineNum = beginLineNum;
38                         contents = new ArrayList<String>();
39                 }
40
41                 public void addLine(String line) {
42                         contents.add(line);
43                 }
44
45                 public String toString() {
46                         return "@" + name + ":\n" + contents;
47                 }
48         };
49
50         /**
51          * <p>
52          * This is a customized wrap of integer so that we can use integer type as
53          * if they were in a C/C++ pass-by-reference fashion. It basically wraps an
54          * int primitive to allow read, write and increment its internal int value.
55          * </p>
56          * 
57          * @author Peizhao Ou
58          * 
59          */
60         public static class IntObj {
61                 private int value;
62
63                 public IntObj(int value) {
64                         this.value = value;
65                 }
66
67                 public void setVal(int value) {
68                         this.value = value;
69                 }
70
71                 public int getVal() {
72                         return this.value;
73                 }
74
75                 public void dec() {
76                         this.value--;
77                 }
78
79                 public void inc() {
80                         this.value++;
81                 }
82
83                 public String toString() {
84                         return Integer.toString(value);
85                 }
86         }
87
88         // A regular expression pattern that matches "@WORD:"
89         public static final Pattern regexpPrimitive = Pattern.compile("@(\\w+):");
90         // The matcher for the primitive regular expression
91         public static final Matcher matcherPrimitive = regexpPrimitive.matcher("");
92
93         // Match code after colon: ":" + Code + "$"
94         public static final Pattern regexpCode = Pattern.compile(":(.*)$");
95         public static final Matcher matcherCode = regexpCode.matcher("");
96
97         // Match a valid word: "^\w+$"
98         public static final Pattern regexpWord = Pattern.compile("^\\w+$");
99         public static final Matcher matcherWord = regexpWord.matcher("");
100
101         /**
102          * <p>
103          * This function will look for the first line that contains a primitive from
104          * the "curIdx", and then extracts all the non-empty lines of that primitive
105          * until it gets to the end of the list or the beginning of the next
106          * primitive. It returns a list of the actual code for that primitive. When
107          * we are done with the function call, the curIdx either points to the next
108          * primitive or the end of the annotation list.
109          * </p>
110          * 
111          * @param file
112          *            The file begin processing
113          * @param beginLineNum
114          *            The beginning line number of the first annotation lines
115          * @param annotations
116          *            The list of annotations
117          * @param curIdx
118          *            The current index (the index of the line that we are
119          *            processing). We will update the current index after calling
120          *            this function. Keep in mind that this is a customized wrap of
121          *            integer so that we can use it as if it is a C/C++ reference
122          * @return The primitive starting from the curIdx till either the end of the
123          *         annotations or the beginning line of the next primitive
124          * @throws WrongAnnotationException
125          */
126         public static Primitive extractPrimitive(File file, int beginLineNum, ArrayList<String> annotations, IntObj curIdx)
127                         throws WrongAnnotationException {
128                 if (curIdx.getVal() == annotations.size()) // The current index points
129                                                                                                         // to the end
130                         // of the list
131                         return null;
132
133                 String line = null;
134                 int curLineNum = -1;
135                 Primitive primitive = null;
136                 // Find the first primitive
137                 for (; curIdx.getVal() < annotations.size(); curIdx.inc()) {
138                         line = annotations.get(curIdx.getVal());
139                         curLineNum = curIdx.getVal() + beginLineNum;
140                         matcherPrimitive.reset(line);
141                         if (matcherPrimitive.find()) {
142                                 String name = matcherPrimitive.group(1);
143                                 primitive = new Primitive(name, curLineNum);
144                                 break;
145                         }
146                 }
147                 // Assert that we must have found one primitive
148                 if (primitive == null) {
149                         WrongAnnotationException.err(file, curLineNum,
150                                         "Something is wrong! We must have found one primitve here!\n");
151                 }
152
153                 // Process the current "primitive"
154                 // Deal with the first special line. E.g. @DeclareState: int x;
155                 String code = null;
156                 matcherCode.reset(line);
157                 if (matcherCode.find()) {
158                         code = matcherCode.group(1);
159                         String trimmedCode = trimSpace(trimTrailingCommentSymbol(code));
160                         if (!trimmedCode.equals("")) {
161                                 primitive.addLine(trimmedCode);
162                         }
163                 } else {
164                         WrongAnnotationException.err(file, curLineNum,
165                                         "The state annotation should have correct primitive syntax (sub-annotations)");
166                 }
167
168                 // Deal with other normal line. E.g. y = 1;
169                 curIdx.inc();
170                 ;
171                 for (; curIdx.getVal() < annotations.size(); curIdx.inc()) {
172                         curLineNum = beginLineNum + curIdx.getVal();
173                         line = annotations.get(curIdx.getVal());
174                         matcherPrimitive.reset(line);
175                         if (!matcherPrimitive.find()) {
176                                 // This is another line that we should add to the result
177                                 code = trimSpace(trimTrailingCommentSymbol(line));
178                                 if (!code.equals(""))
179                                         primitive.addLine(code);
180                         } else
181                                 // We get to the end of the current primitive
182                                 break;
183                 }
184
185                 if (primitive.contents.size() == 0) { // The content of the primitive is
186                                                                                                 // empty
187                         WrongAnnotationException.warning(file, curLineNum, "Primitive " + primitive.name + " is empty.");
188                 }
189                 return primitive;
190         }
191
192         /**
193          * 
194          * @param line
195          *            The line to be processed
196          * @return The string whose beginning and ending space have been trimmed
197          */
198         public static String trimSpace(String line) {
199                 // "^\s*(.*?)\s*$"
200                 Pattern regexp = Pattern.compile("^\\s*(.*?)\\s*$");
201                 Matcher matcher = regexp.matcher(line);
202                 if (matcher.find()) {
203                         return matcher.group(1);
204                 } else {
205                         return line;
206                 }
207         }
208
209         /**
210          * <p>
211          * It processed the line in a way that it removes the trailing C/C++ comment
212          * symbols "STAR SLASH"
213          * </p>
214          * 
215          * @param line
216          * @return
217          */
218         public static String trimTrailingCommentSymbol(String line) {
219                 Pattern regexp = Pattern.compile("(.*?)\\s*(\\*/)?\\s*$");
220                 Matcher matcher = regexp.matcher(line);
221                 if (matcher.find()) {
222                         return matcher.group(1);
223                 } else {
224                         return null;
225                 }
226         }
227
228         public static boolean isUserDefinedStruct(String type) {
229                 // FIXME: We only consider the type is either a one-level pointer or a
230                 // struct
231                 String bareType = trimSpace(type.replace('*', ' '));
232                 return !bareType.equals("int") && !bareType.matches("unsigned\\s+int") && !bareType.equals("unsigned")
233                                 && !bareType.equals("bool") && !bareType.equals("double") && !bareType.equals("float")
234                                 && !bareType.equals("void");
235         }
236
237         public static String getPlainType(String type) {
238                 // FIXME: We only consider the type is either a one-level pointer or a
239                 // struct
240                 String bareType = trimSpace(type.replace('*', ' '));
241                 return bareType;
242         }
243 }