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 private GlobalConstruct globalConstruct;
43 public SpecExtractor() {
44 interfaceListMap = new HashMap<File, ArrayList<InterfaceConstruct>>();
45 OPListMap = new HashMap<File, ArrayList<OPConstruct>>();
46 OPLabelSet = new HashSet<String>();
47 entryMap = new HashMap<File, EntryConstruct>();
48 headerFiles = new HashSet<String>();
49 globalConstruct = null;
52 private void addInterfaceConstruct(InterfaceConstruct construct) {
53 ArrayList<InterfaceConstruct> list = interfaceListMap
56 list = new ArrayList<InterfaceConstruct>();
57 interfaceListMap.put(construct.file, list);
62 private void addOPConstruct(OPConstruct construct) {
63 ArrayList<OPConstruct> list = OPListMap.get(construct.file);
65 list = new ArrayList<OPConstruct>();
66 OPListMap.put(construct.file, list);
71 private void addEntryConstruct(File file, EntryConstruct construct)
72 throws WrongAnnotationException {
73 EntryConstruct old = entryMap.get(file);
75 entryMap.put(file, construct);
76 else { // Error processing
77 String errMsg = "Multiple @Entry annotations in the same file.\n\t Other @Entry at Line "
78 + old.beginLineNum + ".";
79 WrongAnnotationException.err(file, construct.beginLineNum, errMsg);
83 public GlobalConstruct getGlobalConstruct() {
84 return this.globalConstruct;
89 * A print out function for the purpose of debugging. Note that we better
90 * call this function after having called the checkSemantics() function to
91 * check annotation consistency.
94 public void printAnnotations() {
96 .println("/********** Print out of specification extraction **********/");
97 System.out.println("// Extracted header files");
98 for (String header : headerFiles)
99 System.out.println(header);
101 System.out.println("// Global State Construct");
102 if (globalConstruct != null)
103 System.out.println(globalConstruct);
105 for (File file : interfaceListMap.keySet()) {
106 ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
107 System.out.println("// Interface in file: " + file.getName());
108 for (InterfaceConstruct construct : list) {
109 System.out.println(construct);
110 System.out.println("EndLineNumFunc: "
111 + construct.getEndLineNumFunction());
115 for (File file : OPListMap.keySet()) {
116 System.out.println("// Ordering points in file: " + file.getName());
117 ArrayList<OPConstruct> list = OPListMap.get(file);
118 for (OPConstruct construct : list)
119 System.out.println(construct);
122 for (File file : entryMap.keySet()) {
123 System.out.println("// Entry in file: " + file.getName());
124 System.out.println(entryMap.get(file));
130 * Perform basic semantics checking of the extracted specification.
134 * @throws WrongAnnotationException
136 public void checkSemantics() throws WrongAnnotationException {
137 String errMsg = null;
139 // Assert that we have defined and only defined one global state
141 if (globalConstruct == null) {
142 errMsg = "Spec error: There should be one global state annotation.\n";
143 throw new WrongAnnotationException(errMsg);
146 // Assert that the interface constructs have unique label name
147 HashMap<String, InterfaceConstruct> interfaceMap = new HashMap<String, InterfaceConstruct>();
148 for (File f : interfaceListMap.keySet()) {
149 ArrayList<InterfaceConstruct> list = interfaceListMap.get(f);
151 for (InterfaceConstruct construct : list) {
152 InterfaceConstruct existingConstruct = interfaceMap
153 .get(construct.getName());
154 if (existingConstruct != null) { // Error
155 errMsg = "Interface labels duplication with: \""
156 + construct.getName() + "\" in File \""
157 + existingConstruct.file.getName()
158 + "\", Line " + existingConstruct.beginLineNum
160 WrongAnnotationException.err(construct.file,
161 construct.beginLineNum, errMsg);
163 interfaceMap.put(construct.getName(), construct);
169 // Process ordering point labels
170 for (File file : OPListMap.keySet()) {
171 ArrayList<OPConstruct> list = OPListMap.get(file);
172 for (OPConstruct construct : list) {
173 if (construct.type == OPType.OPCheck
174 || construct.type == OPType.PotentialOP) {
175 String label = construct.label;
176 OPLabelSet.add(label);
185 * This function applies on a String (a plain line of text) to check whether
186 * the current line is a C/C++ header include statement. If it is, it
187 * extracts the header file name and store it, and returns true; otherwise,
192 * The line of text to be processed
193 * @return Returns true if the current line is a C/C++ header include
196 public boolean extractHeaders(String line) {
197 // "^( |\t)*#include( |\t)+("|<)([a-zA-Z_0-9\-\.])+("|>)"
198 Pattern regexp = Pattern
199 .compile("^( |\\t)*(#include)( |\\t)+(\"|<)([a-zA-Z_0-9\\-\\.]+)(\"|>)");
200 Matcher matcher = regexp.matcher(line);
203 if (matcher.find()) {
204 String header = null;
205 String braceSymbol = matcher.group(4);
206 if (braceSymbol.equals("<"))
207 header = "<" + matcher.group(5) + ">";
209 header = "\"" + matcher.group(5) + "\"";
210 if (!SpecNaming.isPreIncludedHeader(header)) {
211 headerFiles.add(header);
220 * A sub-routine to extract the construct from beginning till end. When
221 * called, we have already match the beginning of the construct. We will
222 * call this sub-routine when we extract the interface construct and the
223 * global state construct.
227 * The side effect of this function is that the lineReader has just read the
228 * end of the construct, meaning that the caller can get the end line number
229 * by calling lineReader.getLineNumber().
233 * The file that we are processing
235 * The LineNumberReader that we are using when processing the
238 * The file that we are processing
240 * The current line that we are processing. It should be the
241 * beginning line of the annotation construct.
242 * @param beginLineNum
243 * The beginning line number of the interface construct
245 * @return Returns the annotation string list of the current construct
246 * @throws WrongAnnotationException
248 private ArrayList<String> extractTillConstructEnd(File file,
249 LineNumberReader lineReader, String curLine, int beginLineNum)
250 throws WrongAnnotationException {
251 ArrayList<String> annotations = new ArrayList<String>();
252 annotations.add(curLine);
253 // System.out.println(curLine);
254 // Initial settings for matching lines
256 Pattern regexpEnd = Pattern.compile("\\*/( |\\t)*$");
257 Matcher matcher = regexpEnd.matcher(curLine);
258 if (matcher.find()) { // The beginning line is also the end line
259 annotations.add(curLine);
264 while ((line = lineReader.readLine()) != null) {
266 // System.out.println(line);
268 matcher.reset(line); // reset the input
269 annotations.add(line);
273 WrongAnnotationException
276 "The interface annotation should have the matching closing symbol closing \"*/\"");
277 } catch (IOException e) {
286 * A sub-routine to extract the global construct. When called, we have
287 * already match the beginning of the construct.
291 * The file that we are processing
293 * The LineNumberReader that we are using when processing the
296 * The current line that we are processing. It should be the
297 * beginning line of the annotation construct.
298 * @param beginLineNum
299 * The beginning line number of the interface construct
301 * @throws WrongAnnotationException
303 private void extractGlobalConstruct(File file, LineNumberReader lineReader,
304 String curLine, int beginLineNum) throws WrongAnnotationException {
305 ArrayList<String> annotations = extractTillConstructEnd(file,
306 lineReader, curLine, beginLineNum);
307 GlobalConstruct construct = new GlobalConstruct(file, beginLineNum,
309 if (globalConstruct != null) { // Check if we have seen a global state
311 File otherDefinitionFile = globalConstruct.file;
312 int otherDefinitionLine = globalConstruct.beginLineNum;
313 String errMsg = "Multiple definition of global state.\n"
314 + "\tAnother definition is in File \""
315 + otherDefinitionFile.getName() + "\" (Line "
316 + otherDefinitionLine + ").";
317 WrongAnnotationException.err(file, beginLineNum, errMsg);
319 globalConstruct = construct;
324 * The current file we are processing
326 * Call this function when the lineReader will read the beginning
327 * of the definition right away
328 * @param startingLine
329 * The line that we should start processing
330 * @return The line number of the ending line of the interfae definition. If
331 * returning -1, it means the curl symbols in the interface do not
333 * @throws WrongAnnotationException
335 private int findEndLineNumFunction(File file, LineNumberReader lineReader,
336 String startingLine) throws WrongAnnotationException {
337 String line = startingLine;
338 // FIXME: We assume that in the string of the code, there does not exist
339 // the symbol '{' & '{'
341 boolean foundFirstCurl = false;
342 int unmatchedCnt = 0;
345 // System.out.println(line);
347 // Extract the one-liner construct first
348 extractOneLineConstruct(file, lineReader.getLineNumber(), line);
350 for (int i = 0; i < line.length(); i++) {
351 char ch = line.charAt(i);
353 foundFirstCurl = true;
355 } else if (ch == '}') {
358 // The current line is the end of the function
359 if (foundFirstCurl && unmatchedCnt == 0) {
360 int endLineNumFunction = lineReader.getLineNumber();
361 return endLineNumFunction;
364 } while ((line = lineReader.readLine()) != null);
365 } catch (IOException e) {
368 // -1 means the curl symbols in the interface do not match
374 * A sub-routine to extract the interface construct. When called, we have
375 * already match the beginning of the construct, and we also need to find
376 * the ending line number of the closing brace of the corresponding
381 * The file that we are processing
383 * The LineNumberReader that we are using when processing the
386 * The current line that we are processing. It should be the
387 * beginning line of the annotation construct.
388 * @param beginLineNum
389 * The beginning line number of the interface construct
391 * @throws WrongAnnotationException
392 * @throws IOException
393 * @throws ParseException
395 private void extractInterfaceConstruct(File file,
396 LineNumberReader lineReader, String curLine, int beginLineNum)
397 throws WrongAnnotationException, IOException, ParseException {
398 ArrayList<String> annotations = extractTillConstructEnd(file,
399 lineReader, curLine, beginLineNum);
400 int endLineNum = lineReader.getLineNumber();
401 InterfaceConstruct construct = new InterfaceConstruct(file,
402 beginLineNum, endLineNum, annotations);
403 addInterfaceConstruct(construct);
405 // Process the corresponding interface function declaration header
410 line = lineReader.readLine();
411 lineNum = lineReader.getLineNumber();
412 construct.processFunctionDeclaration(line);
413 } catch (IOException e) {
414 errMsg = "Spec error in file \""
418 + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
419 System.out.println(errMsg);
421 } catch (ParseException e) {
422 errMsg = "Spec error in file \""
426 + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
427 System.out.println(errMsg);
431 // Now we find the end of the interface definition
432 int endLineNumFunction = findEndLineNumFunction(file, lineReader, line);
433 construct.setEndLineNumFunction(endLineNumFunction);
434 if (endLineNumFunction == -1) {
435 WrongAnnotationException
436 .err(file, beginLineNum,
437 "The interface definition does NOT have matching curls '}'");
443 * A sub-routine to extract the ordering point construct. When called, we
444 * have already match the beginning of the construct.
448 * The file that we are processing
449 * @param beginLineNum
450 * The beginning line number of the interface construct
453 * The current line that we are processing. It should be the
454 * beginning line of the annotation construct.
456 * The type of ordering point construct we are processing
457 * @throws WrongAnnotationException
459 private void extractOPConstruct(File file, int beginLineNum,
460 String curLine, OPType type) throws WrongAnnotationException {
461 String condition = null;
464 // "(\(\s?(\w+)\s?\))?\s:\s?(.+)\*/\s?$"
465 Pattern regexp = Pattern
466 .compile("(\\(\\s*(\\w+)\\s*\\))?\\s*:\\s*(.+)\\*/\\s*$");
467 Matcher matcher = regexp.matcher(curLine);
468 if (matcher.find()) {
469 label = matcher.group(2);
470 condition = matcher.group(3);
472 WrongAnnotationException
475 "Wrong syntax for the ordering point construct. You might need a colon before the condition.");
477 OPConstruct op = new OPConstruct(file, beginLineNum, type, label,
484 * A sub-routine to extract the entry construct. When called, we have
485 * already match the beginning of the construct.
489 * The file that we are processing
490 * @param beginLineNum
491 * The beginning line number of the interface construct
494 * Current line being processed
495 * @throws WrongAnnotationException
497 public void extractEntryConstruct(File file, int beginLineNum,
498 String curLine) throws WrongAnnotationException {
499 addEntryConstruct(file, new EntryConstruct(file, beginLineNum, curLine));
504 * A sub-routine to extract those annotation constructs that take only one
505 * line --- Entry, OPDefine, PotentialOP, OPCheck, OPClear and OPClearDefin.
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.
516 * @throws WrongAnnotationException
518 private void extractOneLineConstruct(File file, int beginLineNum,
519 String curLine) throws WrongAnnotationException {
520 // "/\*\*\s*@(Entry|OPDefine|PotentialOP|OPCheck|OPClear|OPClearDefine)"
521 Pattern regexpBegin = Pattern.compile("/\\*\\*\\s*@(\\w+)");
522 Matcher matcher = regexpBegin.matcher(curLine);
523 matcher.reset(curLine);
524 if (matcher.find()) {
525 String name = matcher.group(1);
526 if (name.equals("Entry"))
527 extractEntryConstruct(file, beginLineNum, curLine);
528 else if (name.equals("OPDefine") || name.equals("PotentialOP")
529 || name.equals("OPCheck") || name.equals("OPClear")
530 || name.equals("OPClearDefine"))
531 extractOPConstruct(file, beginLineNum, curLine,
532 OPType.valueOf(name));
538 * This function will process a given C/C++ file ( .h, .c or .cc). It will
539 * extract all the headers included in that file, and all the annotation
540 * constructs specified in that file. We then will store the information in
541 * the corresponding containers.
545 * The basic idea is to read the file line by line, and then use regular
546 * expression to match the specific annotations or the header files.
550 * The file object of the corresponding file to be processed
551 * @throws WrongAnnotationException
552 * @throws ParseException
554 public void extractConstruct(File file) throws WrongAnnotationException,
556 BufferedReader br = null;
557 LineNumberReader lineReader = null;
559 // Initial settings for processing the lines
560 br = new BufferedReader(new FileReader(file));
561 lineReader = new LineNumberReader(br);
562 // "/\*\*\s*@(DeclareState|Interface)"
563 Pattern regexpBegin = Pattern
564 .compile("/\\*\\*\\s*@(DeclareState|Interface)");
565 Matcher matcher = regexpBegin.matcher("");
568 while ((line = lineReader.readLine()) != null) {
569 // Start to process the line
571 // First try to process the line to see if it's a header file
573 boolean succ = extractHeaders(line);
574 if (succ) // It's a header line and we successfully extract it
577 int beginLineNum = lineReader.getLineNumber();
578 // Extract the one-liner construct first
579 extractOneLineConstruct(file, beginLineNum, line);
581 // Now we process the line to see if it's an annotation (State
583 matcher.reset(line); // reset the input
584 if (matcher.find()) { // Found the beginning line
585 // The matching annotation name
586 String constructName = matcher.group(1);
588 // Process each annotation accordingly
589 if (constructName.equals(SpecNaming.DeclareState)) {
590 extractGlobalConstruct(file, lineReader, line,
592 } else if (constructName.equals(SpecNaming.Interface)) {
593 extractInterfaceConstruct(file, lineReader, line,
596 WrongAnnotationException.err(file, beginLineNum,
598 + " is not a supported annotation.");
603 } catch (FileNotFoundException e) {
605 } catch (IOException e) {
610 } catch (IOException e) {
618 * Given a list of files, it scans each file and add found SpecConstrcut to
619 * the _constructs list.
623 * The list of files that needs to be processed. In general, this
624 * list only need to contain those that have specification
626 * @throws WrongAnnotationException
627 * @throws ParseException
629 public void extract(File[] files) throws WrongAnnotationException,
631 for (int i = 0; i < files.length; i++)
634 // Check basic specification semantics
638 public void extract(ArrayList<File> files) throws WrongAnnotationException,
640 for (int i = 0; i < files.size(); i++)
641 extract(files.get(i));
643 // Check basic specification semantics
649 * Extract the specification annotations and header files in the current
650 * file. This function should generally be called by extractFiles.
654 * The list of files that needs to be processed. In general, this
655 * list only need to contain those that have specification
657 * @throws WrongAnnotationException
658 * @throws ParseException
660 public void extract(File file) throws WrongAnnotationException,
662 extractConstruct(file);