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<DefineConstruct>> defineListMap;
34 public final HashMap<File, ArrayList<InterfaceConstruct>> interfaceListMap;
35 public final HashMap<File, ArrayList<OPConstruct>> OPListMap;
36 public final HashSet<String> OPLabelSet;
37 // Note that we only allow one entry per file at most
38 public final HashMap<File, EntryConstruct> entryMap;
40 public final HashSet<String> headerFiles;
42 // In the generated header file, we need to forward the user-defined
43 public final HashSet<String> forwardClass;
45 private GlobalConstruct globalConstruct;
47 public SpecExtractor() {
48 defineListMap = new HashMap<File, ArrayList<DefineConstruct>>();
49 interfaceListMap = new HashMap<File, ArrayList<InterfaceConstruct>>();
50 OPListMap = new HashMap<File, ArrayList<OPConstruct>>();
51 OPLabelSet = new HashSet<String>();
52 entryMap = new HashMap<File, EntryConstruct>();
53 headerFiles = new HashSet<String>();
54 forwardClass = new HashSet<String>();
55 globalConstruct = null;
58 private void addDefineConstruct(DefineConstruct construct) {
59 ArrayList<DefineConstruct> list = defineListMap.get(construct.file);
61 list = new ArrayList<DefineConstruct>();
62 defineListMap.put(construct.file, list);
67 private void addInterfaceConstruct(InterfaceConstruct construct) {
68 ArrayList<InterfaceConstruct> list = interfaceListMap
71 list = new ArrayList<InterfaceConstruct>();
72 interfaceListMap.put(construct.file, list);
77 private void addOPConstruct(OPConstruct construct) {
78 ArrayList<OPConstruct> list = OPListMap.get(construct.file);
80 list = new ArrayList<OPConstruct>();
81 OPListMap.put(construct.file, list);
86 private void addEntryConstruct(File file, EntryConstruct construct)
87 throws WrongAnnotationException {
88 EntryConstruct old = entryMap.get(file);
90 entryMap.put(file, construct);
91 else { // Error processing
92 String errMsg = "Multiple @Entry annotations in the same file.\n\t Other @Entry at Line "
93 + old.beginLineNum + ".";
94 WrongAnnotationException.err(file, construct.beginLineNum, errMsg);
98 public GlobalConstruct getGlobalConstruct() {
99 return this.globalConstruct;
104 * A print out function for the purpose of debugging. Note that we better
105 * call this function after having called the checkSemantics() function to
106 * check annotation consistency.
109 public void printAnnotations() {
111 .println("/********** Print out of specification extraction **********/");
112 System.out.println("// Extracted header files");
113 for (String header : headerFiles)
114 System.out.println(header);
116 System.out.println("// Global State Construct");
117 if (globalConstruct != null)
118 System.out.println(globalConstruct);
120 for (File file : interfaceListMap.keySet()) {
121 ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
122 System.out.println("// Interface in file: " + file.getName());
123 for (InterfaceConstruct construct : list) {
124 System.out.println(construct);
125 System.out.println("EndLineNumFunc: "
126 + construct.getEndLineNumFunction());
130 for (File file : OPListMap.keySet()) {
131 System.out.println("// Ordering points in file: " + file.getName());
132 ArrayList<OPConstruct> list = OPListMap.get(file);
133 for (OPConstruct construct : list)
134 System.out.println(construct);
137 for (File file : entryMap.keySet()) {
138 System.out.println("// Entry in file: " + file.getName());
139 System.out.println(entryMap.get(file));
145 * Perform basic semantics checking of the extracted specification.
149 * @throws WrongAnnotationException
151 public void checkSemantics() throws WrongAnnotationException {
152 String errMsg = null;
154 // Assert that we have defined and only defined one global state
156 if (globalConstruct == null) {
157 errMsg = "Spec error: There should be one global state annotation.\n";
158 throw new WrongAnnotationException(errMsg);
161 // Assert that the interface constructs have unique label name
162 HashMap<String, InterfaceConstruct> interfaceMap = new HashMap<String, InterfaceConstruct>();
163 for (File f : interfaceListMap.keySet()) {
164 ArrayList<InterfaceConstruct> list = interfaceListMap.get(f);
166 for (InterfaceConstruct construct : list) {
167 InterfaceConstruct existingConstruct = interfaceMap
168 .get(construct.getName());
169 if (existingConstruct != null) { // Error
170 errMsg = "Interface labels duplication with: \""
171 + construct.getName() + "\" in File \""
172 + existingConstruct.file.getName()
173 + "\", Line " + existingConstruct.beginLineNum
175 WrongAnnotationException.err(construct.file,
176 construct.beginLineNum, errMsg);
178 interfaceMap.put(construct.getName(), construct);
184 // Process ordering point labels
185 for (File file : OPListMap.keySet()) {
186 ArrayList<OPConstruct> list = OPListMap.get(file);
187 for (OPConstruct construct : list) {
188 if (construct.type == OPType.OPCheck
189 || construct.type == OPType.PotentialOP) {
190 String label = construct.label;
191 OPLabelSet.add(label);
200 * This function applies on a String (a plain line of text) to check whether
201 * the current line is a C/C++ header include statement. If it is, it
202 * extracts the header file name and store it, and returns true; otherwise,
207 * The line of text to be processed
208 * @return Returns true if the current line is a C/C++ header include
211 public boolean extractHeaders(String line) {
212 // "^( |\t)*#include( |\t)+("|<)([a-zA-Z_0-9\-\.])+("|>)"
213 Pattern regexp = Pattern
214 .compile("^( |\\t)*(#include)( |\\t)+(\"|<)([a-zA-Z_0-9\\-\\.]+)(\"|>)");
215 Matcher matcher = regexp.matcher(line);
218 if (matcher.find()) {
219 String header = null;
220 String braceSymbol = matcher.group(4);
221 if (braceSymbol.equals("<"))
222 header = "<" + matcher.group(5) + ">";
224 header = "\"" + matcher.group(5) + "\"";
225 if (!SpecNaming.isPreIncludedHeader(header)) {
226 headerFiles.add(header);
235 * A sub-routine to extract the construct from beginning till end. When
236 * called, we have already match the beginning of the construct. We will
237 * call this sub-routine when we extract the interface construct and the
238 * global state construct.
242 * The side effect of this function is that the lineReader has just read the
243 * end of the construct, meaning that the caller can get the end line number
244 * by calling lineReader.getLineNumber().
248 * The file that we are processing
250 * The LineNumberReader that we are using when processing the
253 * The file that we are processing
255 * The current line that we are processing. It should be the
256 * beginning line of the annotation construct.
257 * @param beginLineNum
258 * The beginning line number of the interface construct
260 * @return Returns the annotation string list of the current construct
261 * @throws WrongAnnotationException
263 private ArrayList<String> extractTillConstructEnd(File file,
264 LineNumberReader lineReader, String curLine, int beginLineNum)
265 throws WrongAnnotationException {
266 ArrayList<String> annotations = new ArrayList<String>();
267 annotations.add(curLine);
268 // System.out.println(curLine);
269 // Initial settings for matching lines
271 Pattern regexpEnd = Pattern.compile("\\*/\\s*$");
272 Matcher matcher = regexpEnd.matcher(curLine);
273 if (matcher.find()) {
274 // The beginning line is also the end line
275 // In this case, we have already add the curLine
280 while ((line = lineReader.readLine()) != null) {
282 // System.out.println(line);
284 matcher.reset(line); // reset the input
285 annotations.add(line);
289 WrongAnnotationException
292 "The interface annotation should have the matching closing symbol closing \"*/\"");
293 } catch (IOException e) {
302 * A sub-routine to extract the global construct. When called, we have
303 * already match the beginning of the construct.
307 * The file that we are processing
309 * The LineNumberReader that we are using when processing the
312 * The current line that we are processing. It should be the
313 * beginning line of the annotation construct.
314 * @param beginLineNum
315 * The beginning line number of the interface construct
317 * @throws WrongAnnotationException
319 private void extractGlobalConstruct(File file, LineNumberReader lineReader,
320 String curLine, int beginLineNum) throws WrongAnnotationException {
321 ArrayList<String> annotations = extractTillConstructEnd(file,
322 lineReader, curLine, beginLineNum);
323 GlobalConstruct construct = new GlobalConstruct(file, beginLineNum,
325 if (globalConstruct != null) { // Check if we have seen a global state
327 File otherDefinitionFile = globalConstruct.file;
328 int otherDefinitionLine = globalConstruct.beginLineNum;
329 String errMsg = "Multiple definition of global state.\n"
330 + "\tAnother definition is in File \""
331 + otherDefinitionFile.getName() + "\" (Line "
332 + otherDefinitionLine + ").";
333 WrongAnnotationException.err(file, beginLineNum, errMsg);
335 globalConstruct = construct;
340 * The current file we are processing
342 * Call this function when the lineReader will read the beginning
343 * of the definition right away
344 * @param startingLine
345 * The line that we should start processing
346 * @return The line number of the ending line of the interfae definition. If
347 * returning -1, it means the curl symbols in the interface do not
349 * @throws WrongAnnotationException
351 private int findEndLineNumFunction(File file, LineNumberReader lineReader,
352 String startingLine) throws WrongAnnotationException {
353 String line = startingLine;
354 // FIXME: We assume that in the string of the code, there does not exist
355 // the symbol '{' & '{'
357 boolean foundFirstCurl = false;
358 int unmatchedCnt = 0;
361 // System.out.println(line);
363 // Extract the one-liner construct first
364 extractOneLineConstruct(file, lineReader.getLineNumber(), line);
366 for (int i = 0; i < line.length(); i++) {
367 char ch = line.charAt(i);
369 foundFirstCurl = true;
371 } else if (ch == '}') {
374 // The current line is the end of the function
375 if (foundFirstCurl && unmatchedCnt == 0) {
376 int endLineNumFunction = lineReader.getLineNumber();
377 return endLineNumFunction;
380 } while ((line = lineReader.readLine()) != null);
381 } catch (IOException e) {
384 // -1 means the curl symbols in the interface do not match
390 * A sub-routine to extract the define construct. When called, we have
391 * already match the beginning of the construct, and we also need to find
392 * the ending line number of the anntotation.
396 * The file that we are processing
398 * The LineNumberReader that we are using when processing the
401 * The current line that we are processing. It should be the
402 * beginning line of the annotation construct.
403 * @param beginLineNum
404 * The beginning line number of the interface construct
406 * @throws WrongAnnotationException
407 * @throws IOException
408 * @throws ParseException
410 private void extractDefineConstruct(File file, LineNumberReader lineReader,
411 String curLine, int beginLineNum) throws WrongAnnotationException,
412 IOException, ParseException {
413 ArrayList<String> annotations = extractTillConstructEnd(file,
414 lineReader, curLine, beginLineNum);
415 int endLineNum = lineReader.getLineNumber();
416 DefineConstruct construct = new DefineConstruct(file, beginLineNum,
417 endLineNum, annotations);
418 addDefineConstruct(construct);
423 * A sub-routine to extract the interface construct. When called, we have
424 * already match the beginning of the construct, and we also need to find
425 * the ending line number of the closing brace of the corresponding
430 * The file that we are processing
432 * The LineNumberReader that we are using when processing the
435 * The current line that we are processing. It should be the
436 * beginning line of the annotation construct.
437 * @param beginLineNum
438 * The beginning line number of the interface construct
440 * @throws WrongAnnotationException
441 * @throws IOException
442 * @throws ParseException
444 private void extractInterfaceConstruct(File file,
445 LineNumberReader lineReader, String curLine, int beginLineNum)
446 throws WrongAnnotationException, IOException, ParseException {
447 ArrayList<String> annotations = extractTillConstructEnd(file,
448 lineReader, curLine, beginLineNum);
449 int endLineNum = lineReader.getLineNumber();
450 InterfaceConstruct construct = new InterfaceConstruct(file,
451 beginLineNum, endLineNum, annotations);
452 addInterfaceConstruct(construct);
454 // Process the corresponding interface function declaration header
459 line = lineReader.readLine();
460 lineNum = lineReader.getLineNumber();
461 construct.processFunctionDeclaration(line);
463 // Record those user-defined struct
465 String returnType = construct.getFunctionHeader().returnType;
466 if (SpecUtils.isUserDefinedStruct(returnType))
467 forwardClass.add(SpecUtils.getPlainType(returnType));
469 for (VariableDeclaration decl : construct.getFunctionHeader().args) {
470 if (SpecUtils.isUserDefinedStruct(decl.type))
471 forwardClass.add(SpecUtils.getPlainType(decl.type));
474 } catch (IOException e) {
475 errMsg = "Spec error in file \""
479 + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
480 System.out.println(errMsg);
482 } catch (ParseException e) {
483 errMsg = "Spec error in file \""
487 + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
488 System.out.println(errMsg);
492 // Now we find the end of the interface definition
493 int endLineNumFunction = findEndLineNumFunction(file, lineReader, line);
494 construct.setEndLineNumFunction(endLineNumFunction);
495 if (endLineNumFunction == -1) {
496 WrongAnnotationException
497 .err(file, beginLineNum,
498 "The interface definition does NOT have matching curls '}'");
504 * A sub-routine to extract the ordering point construct. When called, we
505 * have already match the beginning of the construct.
509 * The file that we are processing
510 * @param beginLineNum
511 * The beginning line number of the interface construct
514 * The current line that we are processing. It should be the
515 * beginning line of the annotation construct.
517 * The type of ordering point construct we are processing
518 * @throws WrongAnnotationException
520 private void extractOPConstruct(File file, int beginLineNum,
521 String curLine, OPType type) throws WrongAnnotationException {
522 String condition = null;
525 // "(\(\s?(\w+)\s?\))?\s:\s?(.+)\*/\s?$"
526 Pattern regexp = Pattern
527 .compile("(\\(\\s*(\\w+)\\s*\\))?\\s*:\\s*(.+)\\*/\\s*$");
528 Matcher matcher = regexp.matcher(curLine);
529 if (matcher.find()) {
530 label = matcher.group(2);
531 condition = matcher.group(3);
533 WrongAnnotationException
536 "Wrong syntax for the ordering point construct. You might need a colon before the condition.");
538 OPConstruct op = new OPConstruct(file, beginLineNum, type, label,
545 * A sub-routine to extract the entry construct. When called, we have
546 * already match the beginning of the construct.
550 * The file that we are processing
551 * @param beginLineNum
552 * The beginning line number of the interface construct
555 * Current line being processed
556 * @throws WrongAnnotationException
558 public void extractEntryConstruct(File file, int beginLineNum,
559 String curLine) throws WrongAnnotationException {
560 addEntryConstruct(file, new EntryConstruct(file, beginLineNum, curLine));
565 * A sub-routine to extract those annotation constructs that take only one
566 * line --- Entry, OPDefine, PotentialOP, OPCheck, OPClear and OPClearDefin.
570 * The file that we are processing
571 * @param beginLineNum
572 * The beginning line number of the interface construct
575 * The current line that we are processing. It should be the
576 * beginning line of the annotation construct.
577 * @throws WrongAnnotationException
579 private void extractOneLineConstruct(File file, int beginLineNum,
580 String curLine) throws WrongAnnotationException {
581 // "/\*\*\s*@(Entry|OPDefine|PotentialOP|OPCheck|OPClear|OPClearDefine)"
582 Pattern regexpBegin = Pattern.compile("/\\*\\*\\s*@(\\w+)");
583 Matcher matcher = regexpBegin.matcher(curLine);
584 matcher.reset(curLine);
585 if (matcher.find()) {
586 String name = matcher.group(1);
587 if (name.equals("Entry"))
588 extractEntryConstruct(file, beginLineNum, curLine);
589 else if (name.equals("OPDefine") || name.equals("PotentialOP")
590 || name.equals("OPCheck") || name.equals("OPClear")
591 || name.equals("OPClearDefine")
592 || name.equals("OPDefineUnattached")
593 || name.equals("OPClearDefineUnattached"))
594 extractOPConstruct(file, beginLineNum, curLine,
595 OPType.valueOf(name));
601 * This function will process a given C/C++ file ( .h, .c or .cc). It will
602 * extract all the headers included in that file, and all the annotation
603 * constructs specified in that file. We then will store the information in
604 * the corresponding containers.
608 * The basic idea is to read the file line by line, and then use regular
609 * expression to match the specific annotations or the header files.
613 * The file object of the corresponding file to be processed
614 * @throws WrongAnnotationException
615 * @throws ParseException
617 public void extractConstruct(File file) throws WrongAnnotationException,
619 BufferedReader br = null;
620 LineNumberReader lineReader = null;
622 // Initial settings for processing the lines
623 br = new BufferedReader(new FileReader(file));
624 lineReader = new LineNumberReader(br);
625 // "/\*\*\s*@(DeclareState|Interface)"
626 Pattern regexpBegin = Pattern
627 .compile("/\\*\\*\\s*@(DeclareState|Interface|PreCondition|JustifyingCondition|Transition|PostCondition|Define)");
628 Matcher matcher = regexpBegin.matcher("");
631 while ((line = lineReader.readLine()) != null) {
632 // Start to process the line
634 // First try to process the line to see if it's a header file
636 boolean succ = extractHeaders(line);
637 if (succ) // It's a header line and we successfully extract it
640 int beginLineNum = lineReader.getLineNumber();
641 // Extract the one-liner construct first
642 extractOneLineConstruct(file, beginLineNum, line);
644 // Now we process the line to see if it's an annotation (State
646 matcher.reset(line); // reset the input
647 if (matcher.find()) { // Found the beginning line
648 // The matching annotation name
649 String constructName = matcher.group(1);
651 // Process each annotation accordingly
652 if (constructName.equals(SpecNaming.DeclareState)) {
653 extractGlobalConstruct(file, lineReader, line,
655 } else if (constructName.equals(SpecNaming.Interface)
656 || constructName.equals(SpecNaming.PreCondition)
657 || constructName.equals(SpecNaming.JustifyingCondition)
658 || constructName.equals(SpecNaming.Transition)
659 || constructName.equals(SpecNaming.PostCondition)) {
660 extractInterfaceConstruct(file, lineReader, line,
662 } else if (constructName.equals(SpecNaming.Define)) {
663 extractDefineConstruct(file, lineReader, line,
666 WrongAnnotationException.err(file, beginLineNum,
668 + " is not a supported annotation.");
673 } catch (FileNotFoundException e) {
675 } catch (IOException e) {
680 } catch (IOException e) {
688 * Given a list of files, it scans each file and add found SpecConstrcut to
689 * the _constructs list.
693 * The list of files that needs to be processed. In general, this
694 * list only need to contain those that have specification
696 * @throws WrongAnnotationException
697 * @throws ParseException
699 public void extract(File[] files) throws WrongAnnotationException,
701 for (int i = 0; i < files.length; i++)
704 // Check basic specification semantics
708 public void extract(ArrayList<File> files) throws WrongAnnotationException,
710 for (int i = 0; i < files.size(); i++)
711 extract(files.get(i));
713 // Check basic specification semantics
719 * Extract the specification annotations and header files in the current
720 * file. This function should generally be called by extractFiles.
724 * The list of files that needs to be processed. In general, this
725 * list only need to contain those that have specification
727 * @throws WrongAnnotationException
728 * @throws ParseException
730 public void extract(File file) throws WrongAnnotationException,
732 extractConstruct(file);