1 package edu.uci.eecs.specExtraction;
3 import java.io.BufferedReader;
5 import java.io.FileNotFoundException;
6 import java.io.FileReader;
7 import java.io.IOException;
8 import java.io.LineNumberReader;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.regex.Matcher;
14 import java.util.regex.Pattern;
16 import edu.uci.eecs.codeGenerator.CodeGeneratorUtils;
17 import edu.uci.eecs.codeGenerator.Environment;
18 import edu.uci.eecs.utilParser.ParseException;
22 * This class represents the specification extractor of the specification. The
23 * main function of this class is to read C/C++11 source files and extract the
24 * corresponding specifications, and record corresponding information such as
25 * location, e.g., the file name and the line number, to help the code
32 public class SpecExtractor {
33 public final HashMap<File, ArrayList<InterfaceConstruct>> interfaceListMap;
34 public final HashMap<File, ArrayList<OPConstruct>> OPListMap;
35 public final HashSet<String> OPLabelSet;
36 // Note that we only allow one entry per file at most
37 public final HashMap<File, EntryConstruct> entryMap;
39 public final HashSet<String> headerFiles;
41 // In the generated header file, we need to forward the user-defined
42 public final HashSet<String> forwardClass;
44 private GlobalConstruct globalConstruct;
46 public SpecExtractor() {
47 interfaceListMap = new HashMap<File, ArrayList<InterfaceConstruct>>();
48 OPListMap = new HashMap<File, ArrayList<OPConstruct>>();
49 OPLabelSet = new HashSet<String>();
50 entryMap = new HashMap<File, EntryConstruct>();
51 headerFiles = new HashSet<String>();
52 forwardClass = new HashSet<String>();
53 globalConstruct = null;
56 private void addInterfaceConstruct(InterfaceConstruct construct) {
57 ArrayList<InterfaceConstruct> list = interfaceListMap
60 list = new ArrayList<InterfaceConstruct>();
61 interfaceListMap.put(construct.file, list);
66 private void addOPConstruct(OPConstruct construct) {
67 ArrayList<OPConstruct> list = OPListMap.get(construct.file);
69 list = new ArrayList<OPConstruct>();
70 OPListMap.put(construct.file, list);
75 private void addEntryConstruct(File file, EntryConstruct construct)
76 throws WrongAnnotationException {
77 EntryConstruct old = entryMap.get(file);
79 entryMap.put(file, construct);
80 else { // Error processing
81 String errMsg = "Multiple @Entry annotations in the same file.\n\t Other @Entry at Line "
82 + old.beginLineNum + ".";
83 WrongAnnotationException.err(file, construct.beginLineNum, errMsg);
87 public GlobalConstruct getGlobalConstruct() {
88 return this.globalConstruct;
93 * A print out function for the purpose of debugging. Note that we better
94 * call this function after having called the checkSemantics() function to
95 * check annotation consistency.
98 public void printAnnotations() {
100 .println("/********** Print out of specification extraction **********/");
101 System.out.println("// Extracted header files");
102 for (String header : headerFiles)
103 System.out.println(header);
105 System.out.println("// Global State Construct");
106 if (globalConstruct != null)
107 System.out.println(globalConstruct);
109 for (File file : interfaceListMap.keySet()) {
110 ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
111 System.out.println("// Interface in file: " + file.getName());
112 for (InterfaceConstruct construct : list) {
113 System.out.println(construct);
114 System.out.println("EndLineNumFunc: "
115 + construct.getEndLineNumFunction());
119 for (File file : OPListMap.keySet()) {
120 System.out.println("// Ordering points in file: " + file.getName());
121 ArrayList<OPConstruct> list = OPListMap.get(file);
122 for (OPConstruct construct : list)
123 System.out.println(construct);
126 for (File file : entryMap.keySet()) {
127 System.out.println("// Entry in file: " + file.getName());
128 System.out.println(entryMap.get(file));
134 * Perform basic semantics checking of the extracted specification.
138 * @throws WrongAnnotationException
140 public void checkSemantics() throws WrongAnnotationException {
141 String errMsg = null;
143 // Assert that we have defined and only defined one global state
145 if (globalConstruct == null) {
146 errMsg = "Spec error: There should be one global state annotation.\n";
147 throw new WrongAnnotationException(errMsg);
150 // Assert that the interface constructs have unique label name
151 HashMap<String, InterfaceConstruct> interfaceMap = new HashMap<String, InterfaceConstruct>();
152 for (File f : interfaceListMap.keySet()) {
153 ArrayList<InterfaceConstruct> list = interfaceListMap.get(f);
155 for (InterfaceConstruct construct : list) {
156 InterfaceConstruct existingConstruct = interfaceMap
157 .get(construct.getName());
158 if (existingConstruct != null) { // Error
159 errMsg = "Interface labels duplication with: \""
160 + construct.getName() + "\" in File \""
161 + existingConstruct.file.getName()
162 + "\", Line " + existingConstruct.beginLineNum
164 WrongAnnotationException.err(construct.file,
165 construct.beginLineNum, errMsg);
167 interfaceMap.put(construct.getName(), construct);
173 // Process ordering point labels
174 for (File file : OPListMap.keySet()) {
175 ArrayList<OPConstruct> list = OPListMap.get(file);
176 for (OPConstruct construct : list) {
177 if (construct.type == OPType.OPCheck
178 || construct.type == OPType.PotentialOP) {
179 String label = construct.label;
180 OPLabelSet.add(label);
189 * This function applies on a String (a plain line of text) to check whether
190 * the current line is a C/C++ header include statement. If it is, it
191 * extracts the header file name and store it, and returns true; otherwise,
196 * The line of text to be processed
197 * @return Returns true if the current line is a C/C++ header include
200 public boolean extractHeaders(String line) {
201 // "^( |\t)*#include( |\t)+("|<)([a-zA-Z_0-9\-\.])+("|>)"
202 Pattern regexp = Pattern
203 .compile("^( |\\t)*(#include)( |\\t)+(\"|<)([a-zA-Z_0-9\\-\\.]+)(\"|>)");
204 Matcher matcher = regexp.matcher(line);
207 if (matcher.find()) {
208 String header = null;
209 String braceSymbol = matcher.group(4);
210 if (braceSymbol.equals("<"))
211 header = "<" + matcher.group(5) + ">";
213 header = "\"" + matcher.group(5) + "\"";
214 if (!SpecNaming.isPreIncludedHeader(header)) {
215 headerFiles.add(header);
224 * A sub-routine to extract the construct from beginning till end. When
225 * called, we have already match the beginning of the construct. We will
226 * call this sub-routine when we extract the interface construct and the
227 * global state construct.
231 * The side effect of this function is that the lineReader has just read the
232 * end of the construct, meaning that the caller can get the end line number
233 * by calling lineReader.getLineNumber().
237 * The file that we are processing
239 * The LineNumberReader that we are using when processing the
242 * The file that we are processing
244 * The current line that we are processing. It should be the
245 * beginning line of the annotation construct.
246 * @param beginLineNum
247 * The beginning line number of the interface construct
249 * @return Returns the annotation string list of the current construct
250 * @throws WrongAnnotationException
252 private ArrayList<String> extractTillConstructEnd(File file,
253 LineNumberReader lineReader, String curLine, int beginLineNum)
254 throws WrongAnnotationException {
255 ArrayList<String> annotations = new ArrayList<String>();
256 annotations.add(curLine);
257 // System.out.println(curLine);
258 // Initial settings for matching lines
260 Pattern regexpEnd = Pattern.compile("\\*/( |\\t)*$");
261 Matcher matcher = regexpEnd.matcher(curLine);
262 if (matcher.find()) { // The beginning line is also the end line
263 annotations.add(curLine);
268 while ((line = lineReader.readLine()) != null) {
270 // System.out.println(line);
272 matcher.reset(line); // reset the input
273 annotations.add(line);
277 WrongAnnotationException
280 "The interface annotation should have the matching closing symbol closing \"*/\"");
281 } catch (IOException e) {
290 * A sub-routine to extract the global construct. When called, we have
291 * already match the beginning of the construct.
295 * The file that we are processing
297 * The LineNumberReader that we are using when processing the
300 * The current line that we are processing. It should be the
301 * beginning line of the annotation construct.
302 * @param beginLineNum
303 * The beginning line number of the interface construct
305 * @throws WrongAnnotationException
307 private void extractGlobalConstruct(File file, LineNumberReader lineReader,
308 String curLine, int beginLineNum) throws WrongAnnotationException {
309 ArrayList<String> annotations = extractTillConstructEnd(file,
310 lineReader, curLine, beginLineNum);
311 GlobalConstruct construct = new GlobalConstruct(file, beginLineNum,
313 if (globalConstruct != null) { // Check if we have seen a global state
315 File otherDefinitionFile = globalConstruct.file;
316 int otherDefinitionLine = globalConstruct.beginLineNum;
317 String errMsg = "Multiple definition of global state.\n"
318 + "\tAnother definition is in File \""
319 + otherDefinitionFile.getName() + "\" (Line "
320 + otherDefinitionLine + ").";
321 WrongAnnotationException.err(file, beginLineNum, errMsg);
323 globalConstruct = construct;
328 * The current file we are processing
330 * Call this function when the lineReader will read the beginning
331 * of the definition right away
332 * @param startingLine
333 * The line that we should start processing
334 * @return The line number of the ending line of the interfae definition. If
335 * returning -1, it means the curl symbols in the interface do not
337 * @throws WrongAnnotationException
339 private int findEndLineNumFunction(File file, LineNumberReader lineReader,
340 String startingLine) throws WrongAnnotationException {
341 String line = startingLine;
342 // FIXME: We assume that in the string of the code, there does not exist
343 // the symbol '{' & '{'
345 boolean foundFirstCurl = false;
346 int unmatchedCnt = 0;
349 // System.out.println(line);
351 // Extract the one-liner construct first
352 extractOneLineConstruct(file, lineReader.getLineNumber(), line);
354 for (int i = 0; i < line.length(); i++) {
355 char ch = line.charAt(i);
357 foundFirstCurl = true;
359 } else if (ch == '}') {
362 // The current line is the end of the function
363 if (foundFirstCurl && unmatchedCnt == 0) {
364 int endLineNumFunction = lineReader.getLineNumber();
365 return endLineNumFunction;
368 } while ((line = lineReader.readLine()) != null);
369 } catch (IOException e) {
372 // -1 means the curl symbols in the interface do not match
378 * A sub-routine to extract the interface construct. When called, we have
379 * already match the beginning of the construct, and we also need to find
380 * the ending line number of the closing brace of the corresponding
385 * The file that we are processing
387 * The LineNumberReader that we are using when processing the
390 * The current line that we are processing. It should be the
391 * beginning line of the annotation construct.
392 * @param beginLineNum
393 * The beginning line number of the interface construct
395 * @throws WrongAnnotationException
396 * @throws IOException
397 * @throws ParseException
399 private void extractInterfaceConstruct(File file,
400 LineNumberReader lineReader, String curLine, int beginLineNum)
401 throws WrongAnnotationException, IOException, ParseException {
402 ArrayList<String> annotations = extractTillConstructEnd(file,
403 lineReader, curLine, beginLineNum);
404 int endLineNum = lineReader.getLineNumber();
405 InterfaceConstruct construct = new InterfaceConstruct(file,
406 beginLineNum, endLineNum, annotations);
407 addInterfaceConstruct(construct);
409 // Process the corresponding interface function declaration header
414 line = lineReader.readLine();
415 lineNum = lineReader.getLineNumber();
416 construct.processFunctionDeclaration(line);
418 // Record those user-defined struct
420 String returnType = construct.getFunctionHeader().returnType;
421 if (SpecUtils.isUserDefinedStruct(returnType))
422 forwardClass.add(SpecUtils.getPlainType(returnType));
424 for (VariableDeclaration decl : construct.getFunctionHeader().args) {
425 if (SpecUtils.isUserDefinedStruct(decl.type))
426 forwardClass.add(SpecUtils.getPlainType(decl.type));
429 } catch (IOException e) {
430 errMsg = "Spec error in file \""
434 + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
435 System.out.println(errMsg);
437 } catch (ParseException e) {
438 errMsg = "Spec error in file \""
442 + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
443 System.out.println(errMsg);
447 // Now we find the end of the interface definition
448 int endLineNumFunction = findEndLineNumFunction(file, lineReader, line);
449 construct.setEndLineNumFunction(endLineNumFunction);
450 if (endLineNumFunction == -1) {
451 WrongAnnotationException
452 .err(file, beginLineNum,
453 "The interface definition does NOT have matching curls '}'");
459 * A sub-routine to extract the ordering point construct. When called, we
460 * have already match the beginning of the construct.
464 * The file that we are processing
465 * @param beginLineNum
466 * The beginning line number of the interface construct
469 * The current line that we are processing. It should be the
470 * beginning line of the annotation construct.
472 * The type of ordering point construct we are processing
473 * @throws WrongAnnotationException
475 private void extractOPConstruct(File file, int beginLineNum,
476 String curLine, OPType type) throws WrongAnnotationException {
477 String condition = null;
480 // "(\(\s?(\w+)\s?\))?\s:\s?(.+)\*/\s?$"
481 Pattern regexp = Pattern
482 .compile("(\\(\\s*(\\w+)\\s*\\))?\\s*:\\s*(.+)\\*/\\s*$");
483 Matcher matcher = regexp.matcher(curLine);
484 if (matcher.find()) {
485 label = matcher.group(2);
486 condition = matcher.group(3);
488 WrongAnnotationException
491 "Wrong syntax for the ordering point construct. You might need a colon before the condition.");
493 OPConstruct op = new OPConstruct(file, beginLineNum, type, label,
500 * A sub-routine to extract the entry construct. When called, we have
501 * already match the beginning of the construct.
505 * The file that we are processing
506 * @param beginLineNum
507 * The beginning line number of the interface construct
510 * Current line being processed
511 * @throws WrongAnnotationException
513 public void extractEntryConstruct(File file, int beginLineNum,
514 String curLine) throws WrongAnnotationException {
515 addEntryConstruct(file, new EntryConstruct(file, beginLineNum, curLine));
520 * A sub-routine to extract those annotation constructs that take only one
521 * line --- Entry, OPDefine, PotentialOP, OPCheck, OPClear and OPClearDefin.
525 * The file that we are processing
526 * @param beginLineNum
527 * The beginning line number of the interface construct
530 * The current line that we are processing. It should be the
531 * beginning line of the annotation construct.
532 * @throws WrongAnnotationException
534 private void extractOneLineConstruct(File file, int beginLineNum,
535 String curLine) throws WrongAnnotationException {
536 // "/\*\*\s*@(Entry|OPDefine|PotentialOP|OPCheck|OPClear|OPClearDefine)"
537 Pattern regexpBegin = Pattern.compile("/\\*\\*\\s*@(\\w+)");
538 Matcher matcher = regexpBegin.matcher(curLine);
539 matcher.reset(curLine);
540 if (matcher.find()) {
541 String name = matcher.group(1);
542 if (name.equals("Entry"))
543 extractEntryConstruct(file, beginLineNum, curLine);
544 else if (name.equals("OPDefine") || name.equals("PotentialOP")
545 || name.equals("OPCheck") || name.equals("OPClear")
546 || name.equals("OPClearDefine"))
547 extractOPConstruct(file, beginLineNum, curLine,
548 OPType.valueOf(name));
554 * This function will process a given C/C++ file ( .h, .c or .cc). It will
555 * extract all the headers included in that file, and all the annotation
556 * constructs specified in that file. We then will store the information in
557 * the corresponding containers.
561 * The basic idea is to read the file line by line, and then use regular
562 * expression to match the specific annotations or the header files.
566 * The file object of the corresponding file to be processed
567 * @throws WrongAnnotationException
568 * @throws ParseException
570 public void extractConstruct(File file) throws WrongAnnotationException,
572 BufferedReader br = null;
573 LineNumberReader lineReader = null;
575 // Initial settings for processing the lines
576 br = new BufferedReader(new FileReader(file));
577 lineReader = new LineNumberReader(br);
578 // "/\*\*\s*@(DeclareState|Interface)"
579 Pattern regexpBegin = Pattern
580 .compile("/\\*\\*\\s*@(DeclareState|Interface)");
581 Matcher matcher = regexpBegin.matcher("");
584 while ((line = lineReader.readLine()) != null) {
585 // Start to process the line
587 // First try to process the line to see if it's a header file
589 boolean succ = extractHeaders(line);
590 if (succ) // It's a header line and we successfully extract it
593 int beginLineNum = lineReader.getLineNumber();
594 // Extract the one-liner construct first
595 extractOneLineConstruct(file, beginLineNum, line);
597 // Now we process the line to see if it's an annotation (State
599 matcher.reset(line); // reset the input
600 if (matcher.find()) { // Found the beginning line
601 // The matching annotation name
602 String constructName = matcher.group(1);
604 // Process each annotation accordingly
605 if (constructName.equals(SpecNaming.DeclareState)) {
606 extractGlobalConstruct(file, lineReader, line,
608 } else if (constructName.equals(SpecNaming.Interface)) {
609 extractInterfaceConstruct(file, lineReader, line,
612 WrongAnnotationException.err(file, beginLineNum,
614 + " is not a supported annotation.");
619 } catch (FileNotFoundException e) {
621 } catch (IOException e) {
626 } catch (IOException e) {
634 * Given a list of files, it scans each file and add found SpecConstrcut to
635 * the _constructs list.
639 * The list of files that needs to be processed. In general, this
640 * list only need to contain those that have specification
642 * @throws WrongAnnotationException
643 * @throws ParseException
645 public void extract(File[] files) throws WrongAnnotationException,
647 for (int i = 0; i < files.length; i++)
650 // Check basic specification semantics
654 public void extract(ArrayList<File> files) throws WrongAnnotationException,
656 for (int i = 0; i < files.size(); i++)
657 extract(files.get(i));
659 // Check basic specification semantics
665 * Extract the specification annotations and header files in the current
666 * file. This function should generally be called by extractFiles.
670 * The list of files that needs to be processed. In general, this
671 * list only need to contain those that have specification
673 * @throws WrongAnnotationException
674 * @throws ParseException
676 public void extract(File file) throws WrongAnnotationException,
678 extractConstruct(file);