package facade; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Stack; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.BailErrorStrategy; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.LexerNoViableAltException; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.misc.ParseCancellationException; import sugarVisitors.ToAst; import tools.Assertions; import antlrGenerated.L42Lexer; import antlrGenerated.L42Parser; import antlrGenerated.L42Parser.NudeEContext; import ast.ErrorMessage; import ast.ErrorMessage.ParsingError; import ast.Expression; import facade.Parser.ParData.State; /* **NOT WHAT I'm doing** Implement the interface ANTLRErrorListener. You may extend BaseErrorListener for that. Collect the errors and append them to a list. Call parser.removeErrorListeners() to remove the default listeners Call parser.addErrorListener(yourListenerInstance) to add your own listener Parse your input Now, for the lexer, you may either do the same thing removeErrorListeners/addErrorListener, or add the following rule at the end: UNKNOWN_CHAR : . ; With this rule, the lexer will never fail (it will generate UNKNOWN_CHAR tokens when it can't do anything else) and all errors will be generated by the parser (because it won't know what to do with these UNKNOWN_CHAR tokens). I recommend this approach. http://stackoverflow.com/questions/25832429/how-get-error-messages-of-antlr-parsing/25838580#25838580 You should get a ParseCancellationException, which wraps a RecognitionException with the error message. Also, this answer of mine could be related. * */ public class Parser { private static NudeEContext parseCtx(String s){ LoggedPrintStream lpsErr = LoggedPrintStream.create(System.err); System.setErr(lpsErr); try{ NudeEContext ctxResult = getParser(s).nudeE(); if(lpsErr.buf.length()!=0){ throw new ParseCancellationException("\n\n------------------------------------\nCurrent file: "+Parser.fileName+"\nParsing error:\n"+lpsErr.buf.toString()); } return ctxResult; } finally{System.setErr(lpsErr.underlying);} } private static String fileName=null; public static String getFileName(){ return fileName; } public synchronized static Expression parse(String fileName,String s){ String old=Parser.fileName; Parser.fileName=fileName; try{ checkForTab(s); checkForBalancedParenthesis(s); s = replaceORoundWithTab(s); NudeEContext ctxResult=parseCtx(s); Expression result=ctxResult.accept(new ToAst()); return result; } finally{Parser.fileName=old;} } private static void checkForTab(String s) { if(s.contains("\t")){throw new ErrorMessage.InvalidCharacter(getFileName(), "Tab ('\\t') is an invalid character in 42");} } public static antlrGenerated.L42Parser getParser(String s){ ANTLRInputStream in = new ANTLRInputStream(s); antlrGenerated.L42Lexer l=new antlrGenerated.L42Lexer(in);/*{ @Override public void recover(LexerNoViableAltException e) { throw new IllegalArgumentException(e); // Bail out } @Override public void recover(RecognitionException re) { throw new IllegalArgumentException(re); // Bail out }};*/ CommonTokenStream t = new CommonTokenStream(l); antlrGenerated.L42Parser p=new antlrGenerated.L42Parser(t); // p.setErrorHandler(new BailErrorStrategy()); return p; } static final class Pos { Pos(int line, int pos, facade.Parser.ParData.State state) { this.line = line; this.pos = pos; this.state = state; } final int line; final int pos; final ParData.State state; } static class ParData{ int currentLine=1; int currentPos=0; public static enum State{ None,CommSL,CommML,StrSL,StrMLText,StrMLPadding,Round,Square,Curly} Stack<Pos>s=new Stack<>(); {push(State.None);} //Stack<ErrorMessage.ParsingError>lastPar=new Stack<ErrorMessage.ParsingError>(); void nextPos(){currentPos+=1;} boolean fails(State... fs){ Pos cs=s.peek(); for (State si:fs){if (si==cs.state){return true;} } return false; } void failPadding(String token){ if(fails(State.StrMLPadding)){ throw new ErrorMessage.InvalidCharacterInMultilineStringIndentation( Parser.fileName,this.currentLine,this.currentPos,token);}} boolean popIf(State ...ps){ Pos cs=s.peek(); for (State si:ps){ if (si==cs.state){s.pop();return true;} } return false; } void swapIf(State ns,State ...ss){ Pos cs=s.peek(); for (State si:ss){ if (si==cs.state){s.pop();push(ns);return;} } } boolean pushIf(State pushed,State ... ps){ Pos cs=s.peek(); for (State si:ps){ if (si==cs.state){push(pushed);return true;} } return false; } void push(State state){ s.push(new Pos(this.currentLine,this.currentPos,state)); } private void parMismatch(State s2) { String token2=s2==State.Round?")":s.peek().state==State.Square?"]":"}"; if(s.peek().state==State.None){ throw new ErrorMessage.UnopenedParenthesis(Parser.fileName,currentLine,currentPos,token2);} String token1=s.peek().state==State.Round?"(":s.peek().state==State.Square?"[":"{"; throw new ErrorMessage.ParenthesisMismatchRange(fileName, s.peek().line,s.peek().pos, token1, this.currentLine,this.currentPos, token2); } void newLine(){//\n currentLine+=1; currentPos=0; //fail in StrSL if(fails(State.StrSL)){throw new ErrorMessage.UnclosedStringLiteral( fileName, currentLine,currentPos,"\n");} //swap StrMLPadding if State.StrMLText swapIf(State.StrMLPadding,State.StrMLText); //pop inCommSL popIf(State.CommSL); } void singleQuote(){// ' //fail in Round,Square,Curly if(fails(State.Round,State.Square,State.Curly)){ throw new ErrorMessage.InvalidCharacterOutOfString( Parser.fileName,this.currentLine,this.currentPos,"\'"); } //swap in StrMLPadding, push StrMLText swapIf(State.StrMLText,State.StrMLPadding); } void doubleQuote(){// " //pop in StrSL,StrMLPadding if(popIf(State.StrSL,State.StrMLPadding)){return;} //push State.StrSL if State.Round,State.Square,State.Curly if(pushIf(State.StrSL,State.None,State.Round,State.Square,State.Curly)){return;} } void doubleQuoteNL(){// " //pop in StrSL,StrMLPadding if(popIf(State.StrSL,State.StrMLPadding)){return;} //push State.StrSL if State.Round,State.Square,State.Curly if(pushIf(State.StrMLPadding,State.None,State.Round,State.Square,State.Curly)){return;} } void barBar(){// // //fail in StrMLPadding failPadding("//"); //push CommSL in Round,Square,Curly if(pushIf(State.CommSL,State.None,State.Round,State.Square,State.Curly)){return;} } void barStarO(){// /* //fail in StrMLPadding failPadding("/*"); //push CommML in Round,Square,Curly if(pushIf(State.CommML,State.None,State.Round,State.Square,State.Curly)){return;} } void barStarC(){// */ //fail in StrMLPadding, Round,Square,Curly failPadding("*/"); //pop in CommML if(popIf(State.CommML)){return;} } void oRound(){// ( //fail in StrMLPadding failPadding("("); //push Round in Round,Square,Curly if(pushIf(State.Round,State.None,State.Round,State.Square,State.Curly)){return;} } void cRound(){// ) //fail in StrMLPadding failPadding(")"); //pop in Round if(popIf(State.Round)){return;} if(fails(State.None,State.Square,State.Curly)){ parMismatch(State.Round);} } void oSquare(){// [ //fail in StrMLPadding failPadding("("); //push Square in Round,Square,Curly if(pushIf(State.Square,State.None,State.Round,State.Square,State.Curly)){return;} } void cSquare(){// ] //fail in StrMLPadding failPadding("["); //pop in Square if(popIf(State.Square)){return;} if(fails(State.None,State.Round,State.Curly)){ parMismatch(State.Square);} } void oCurly(){// { //fail in StrMLPadding failPadding("{"); //push Curly in Round,Square,Curly if(pushIf(State.Curly,State.None,State.Round,State.Square,State.Curly)){return;} } void cCurly(){// } //fail in StrMLPadding failPadding("("); //pop in Curly if(popIf(State.Curly)){return;} if(fails(State.None,State.Round,State.Square)){ parMismatch(State.Curly);} } void space(){//all ok } void comma(){//all ok } void nonSpacing(String c){ failPadding(c); } } private static String replaceORoundWithTab(String s) { char[] toReplace=" ,\n+-*/({[<>=!~:".toCharArray(); char[] source=s.toCharArray(); char[] result=s.toCharArray(); for(char c:toReplace){ for(int i=1;i<source.length;i++){ if (source[i]!='('){continue;} if (source[i-1]==c){result[i]='\t';} } } s=String.copyValueOf(result); return s; } static class LoggedPrintStream extends PrintStream { final StringBuilder buf; final PrintStream underlying; LoggedPrintStream(StringBuilder sb, OutputStream os, PrintStream ul) { super(os); this.buf = sb; this.underlying = ul; } private static LoggedPrintStream create(PrintStream toLog) {//from http://stackoverflow.com/questions/4334808/how-could-i-read-java-console-output-into-a-string-buffer try { final StringBuilder sb = new StringBuilder(); Field f = FilterOutputStream.class.getDeclaredField("out"); f.setAccessible(true); OutputStream psout = (OutputStream) f.get(toLog); return new LoggedPrintStream(sb, new FilterOutputStream(psout) { boolean lastIsSlash=false; public void write(int b) throws IOException { //TODO: hack to put back the "(" that was replaced with tab if(lastIsSlash && b=='\\'){ lastIsSlash=false; super.write(b); sb.append((char) b); return; } if(lastIsSlash && b=='t'){ lastIsSlash=false; super.write('('); sb.append((char) '('); return;} if(lastIsSlash){ super.write('\\'); sb.append((char) '\\'); } lastIsSlash=b=='\\'; if(!lastIsSlash){ super.write(b); sb.append((char) b); } } }, toLog); } catch (NoSuchFieldException|IllegalArgumentException |IllegalAccessException e) { throw Assertions.codeNotReachable(); }}} public static void checkForBalancedParenthesis(String s) { ParData d=new ParData(); char[] cs=s.toCharArray(); for(int i=0;i<cs.length;i++){ char c=cs[i]; char cp1=' '; d.nextPos(); if(i+1<cs.length){cp1=cs[i+1];} switch (c){ case '\n': d.newLine(); break;case '/': if(cp1=='/'){i++;d.barBar();} else if(cp1=='*'){i++;d.barStarO();} else d.nonSpacing("/"); break;case '{':d.oCurly(); break;case '[':d.oSquare(); break;case '(':d.oRound(); break;case '}':d.cCurly(); break;case ']':d.cSquare(); break;case ')':d.cRound(); break;case '\"': if(cp1=='\n' && ( d.s.peek().state==State.None ||d.s.peek().state==State.Round || d.s.peek().state==State.Square || d.s.peek().state==State.Curly)){i++;d.doubleQuoteNL();} else d.doubleQuote(); break;case '\'':d.singleQuote(); break;case '*': if(cp1=='/'){i++;d.barStarC();} else d.nonSpacing("*"); break;case ' ':d.space(); break;case ',':d.comma(); break;default: d.nonSpacing(c+""); } } if(d.s.peek().state!=ParData.State.None){ ParData.State f=d.s.peek().state; String token=f==ParData.State.Round?"(":f==ParData.State.Square?"[":f==ParData.State.Curly?"{":f==ParData.State.CommML?"/*":"string literal"; throw new ErrorMessage.UnclosedParenthesis(fileName,d.s.peek().line,d.s.peek().pos,token); } } }