package edu.ucsd.arcum.interpreter.parser; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.*; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.jface.text.IDocument; import edu.ucsd.arcum.interpreter.query.ArcumDeclarationTable; import edu.ucsd.arcum.ui.editor.ConceptMapEntry; import edu.ucsd.arcum.ui.editor.TopLevelSourceEntry; @SuppressWarnings("restriction") public class ArcumSourceFileParser { private String source; private IResource resource; private String sourcefileName; private IProject project; public static List<TopLevelSourceEntry> quickParse(IDocument document) { System.out.printf("quickParse%n"); String source = document.get(); BacktrackingScanner scanner = new BacktrackingScanner(source); String importsString = extractImportParts(scanner); List<TopLevelSourceEntry> result = new ArrayList<TopLevelSourceEntry>(); DietParsing: for (;;) { while (scanner.lookaheadIsNoneOf(TokenNameIdentifier, TokenNameEOF)) { scanner.match(); } if (scanner.lookahead() == TokenNameEOF) { break DietParsing; } else if (scanner.lookahead() == TokenNameIdentifier) { ArcumKeyword keyword = ArcumKeyword.lookup(scanner.getCurrentTokenString()); if (keyword == ArcumKeyword.REQUIRE_KEYWORD) { int returned = scanner.consumeUntil(TokenNameLBRACE); if (returned == TokenNameEOF) break DietParsing; scanner.nextToken(); while (scanner.lookahead() == TokenNameIdentifier) { String optionUsed; int start; int end; int length; String args; optionUsed = scanner.getCurrentTokenString(); start = scanner.getCurrentTokenStartPosition(); scanner.consumeUntil(TokenNameSEMICOLON); end = scanner.getCurrentTokenStartPosition(); length = end - start; scanner.match(); args = scanner.getText(start, length); result.add(new ConceptMapEntry(document, optionUsed, args)); } consumeNextMatchedCurlies(scanner); } else if (keyword == ArcumKeyword.OPTION_KEYWORD) { // NOTE: We don't show this structure yet, instead we just // skip it to go to the next map. A better strategy would // allow traits to be seen too, but that would require a // hierarchial structure and a better strategy for determining // when to reparse (all doable, but just not a priority) // int t = scanner.nextToken(); // if (t == TokenNameinterface) { // t = scanner.nextToken(); // } // if (t == TokenNameIdentifier) { // String id = scanner.getCurrentTokenString(); // result.add(new OptionOrConceptDeclaration(id)); // } consumeNextMatchedCurlies(scanner); } } } return result; } public ArcumSourceFileParser(String source, IResource resource, String name, IProject project) { this.source = source; this.resource = resource; this.sourcefileName = name; this.project = project; } // Parses each segment of the .arcum file, the first segment is the list of // imports, followed by trait, option or requirement map definitions. Returns // the number of parse errors encountered. public int parseArcumSource(ArcumDeclarationTable table) { int numErrors = 0; try { BacktrackingScanner scanner = new BacktrackingScanner(source, resource); String importsString = extractImportParts(scanner); ArcumStructureParser structParser; structParser = new ArcumStructureParser(resource, project, importsString); while (scanner.lookaheadIsNoneOf(TokenNameEOF)) { structParser.parse(scanner, table); } numErrors += structParser.getNumErrors(); } catch (Exception e) { ++numErrors; e.printStackTrace(); } return numErrors; } // Read all tokens until the first "{" or EOF is found. Then, the position // of the imports is from the start of the buffer up until and including // the last ";" read. private static String extractImportParts(BacktrackingScanner scanner) { int lastSemi = -1; for (;;) { int token = scanner.lookahead(); if (token == TokenNameEOF || token == TokenNameLBRACE) break; if (token == TokenNameSEMICOLON) lastSemi = scanner.getCurrentTokenStartPosition(); scanner.match(); } scanner.backtrack(); String result = String.format("import java.lang.*;%n"); if (lastSemi != -1) { while (scanner.getCurrentTokenStartPosition() < lastSemi) scanner.match(); scanner.match(); return result + scanner.getText(0, scanner.getCurrentTokenStartPosition()); } return result; } // Reads in all tokens until EOF or a matched pair of {}'s is read. // Returns the last token read, which will either be EOF or RBRACE. private static int consumeNextMatchedCurlies(BacktrackingScanner scanner) { int token = scanner.consumeUntil(TokenNameLBRACE); // eat more tokens, until the "}" is closed int nesting = 0; for (;;) { if (token == TokenNameLBRACE) { ++nesting; } else if (token == TokenNameRBRACE) { --nesting; if (nesting == 0) return TokenNameRBRACE; } else if (token == TokenNameEOF) { return TokenNameEOF; } token = scanner.nextToken(); } } }