package project.phase2; import project.phase2.exc.ParseException; import project.phase2.ll1parsergenerator.AST; import project.phase2.ll1parsergenerator.ASTNode; import project.scangen.ScannerGenerator; import project.scangen.tokenizer.Token; import java.util.*; public class MiniREParser { private static final Set<String> KEYWORDS = new HashSet<String>() {{ add("begin"); add("end"); add("find"); add("replace"); add("recursivereplace"); add("maxfreqstring"); add("diff"); add("union"); add("inters"); add("print"); add("with"); add("in"); }}; private final ScannerGenerator scangen; public MiniREParser(final ScannerGenerator scangen) { this.scangen = scangen; } /** * RDP utility methods */ private Token token = null; private Iterator<Token> tokenIterator; private Stack<ASTNode<String>> nodeStack; public AST<String> parse() throws ParseException { AST<String> ast = new AST<String>(); ast.setRoot(new ASTNode<String>("root", false)); nodeStack = new Stack<ASTNode<String>>(); nodeStack.push(ast.getRoot()); tokenIterator = scangen.generateTokenizer().iterator(); nextsym(); minire_program(); return ast; } /** * Set token to the next one, or special token 'EOF' if none remaining. */ private void nextsym() { if (tokenIterator.hasNext()) { token = MiniREParser.parseKeywords(tokenIterator.next()); } else { token = new Token("EOF", "EOF", -1, -1); } } /** * If tokenType matches the current token's type, consume it and return * true; otherwise return false. */ private boolean accept(String tokenType) { if (token.type.equals(tokenType)) { ASTNode<String> tokenTypeNode = new ASTNode<String>(tokenType, false); tokenTypeNode.insert(new ASTNode<String>(token.value, true)); nodeStack.peek().insert(tokenTypeNode); nextsym(); return true; } else { return false; } } /** * Invoke accept, raising an exception if token unexpected. */ private void expect(String tokenType) throws ParseException { if (!accept(tokenType)) throw new ParseException(String.format("Expected `%s' got `%s'", tokenType, token.type), token); } /** * Add a new nonterminal node and push it on the stack. */ private void push(String value) { ASTNode<String> node = new ASTNode<String>(value, false); nodeStack.peek().insert(node); nodeStack.push(node); } /** * Pop the current nonterminal node off the stack. */ private void pop() { nodeStack.pop(); } /** * Begin RDP methods */ private void minire_program() throws ParseException { push("minire_program"); expect("BEGIN"); statement_list(); minire_program_tail(); pop(); } private void minire_program_tail() throws ParseException { push("minire_program_tail"); expect("END"); pop(); } private void statement_list() throws ParseException { push("statement_list"); statement(); statement_list_tail(); pop(); } private void statement_list_tail() throws ParseException { push("statement_list_tail"); // hax if (token.type.equals("END")) { pop(); return; } statement(); statement_list_tail(); pop(); } private void statement() throws ParseException { push("statement"); if (accept("ID")) { expect("EQUALS"); if (accept("OCTOTHORPE")) { exp(); } else if (accept("MAXFREQSTRING")) { expect("OPEN-PAREN"); expect("ID"); expect("CLOSE-PAREN"); } else { exp(); } } else if (accept("REPLACE")) { expect("REGEX"); expect("WITH"); expect("ASCII-STRING"); expect("IN"); file_names(); } else if (accept("RECURSIVEREPLACE")) { expect("REGEX"); expect("WITH"); expect("ASCII-STRING"); expect("IN"); file_names(); } else if (accept("PRINT")) { expect("OPEN-PAREN"); exp_list(); expect("CLOSE-PAREN"); } else { pop(); throw new ParseException(String.format("Expected one of `ID', `REPLACE', `RECURSIVEREPLACE', " + "`PRINT' got `%s'", token.type), token); } expect("SEMICOLON"); pop(); } private void file_names() throws ParseException { push("file_names"); source_file(); expect("GREATER-BANG"); destination_file(); pop(); } private void source_file() throws ParseException { push("source_file"); expect("ASCII-STRING"); pop(); } private void destination_file() throws ParseException { push("destination_file"); expect("ASCII-STRING"); pop(); } private void exp_list() throws ParseException { push("exp_list"); exp(); exp_list_tail(); pop(); } private void exp_list_tail() throws ParseException { if (accept("COMMA")) { push("exp_list_tail"); exp(); exp_list_tail(); pop(); } } private void exp() throws ParseException { push("exp"); if (accept("ID")) { } else { try { expect("OPEN-PAREN"); exp(); expect("CLOSE-PAREN"); } catch (ParseException e) { term(); exp_tail(); } } pop(); } private void exp_tail() throws ParseException { push("exp_tail"); try { bin_op(); term(); exp_tail(); } catch (ParseException e) { } pop(); } private void term() throws ParseException { push("term"); expect("FIND"); expect("REGEX"); expect("IN"); filename(); pop(); } private void filename() throws ParseException { push("filename"); expect("ASCII-STRING"); pop(); } private void bin_op() throws ParseException { push("bin_op"); if (accept("DIFF")) { } else if (accept("UNION")) { } else if (accept("INTERS")) { } else { pop(); throw new ParseException(String.format("Expected one of `DIFF', `UNION', `INTERS' " + "got `%s'", token.type), token); } pop(); } /** * End RDP methods */ /** * If token is of type ID-OR-KEYWORD, identify which and return a new * such token, otherwise return the original. */ private static Token parseKeywords(final Token token) { if (token.type.equals("ID-OR-KEYWORD")) { if (KEYWORDS.contains(token.value)) { return new Token(token.value.toUpperCase(), token.value, token.line, token.pos); } else { return new Token("ID", token.value, token.line, token.pos); } } else { return token; } } }