/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.antlr.works.editor.grammar.parser;
import java.util.ArrayList;
import java.util.List;
import org.antlr.grammar.v3.ANTLRParser;
import org.antlr.netbeans.editor.parsing.SyntaxError;
import org.antlr.netbeans.editor.text.DocumentSnapshot;
import org.antlr.runtime.IntStream;
import org.antlr.runtime.MismatchedTokenException;
import org.antlr.runtime.MissingTokenException;
import org.antlr.runtime.NoViableAltException;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.UnwantedTokenException;
import org.antlr.runtime.tree.CommonTreeAdaptor;
import org.antlr.runtime.tree.Tree;
import org.antlr.runtime.tree.TreeNodeStream;
import org.antlr.tool.ANTLRErrorListener;
import org.antlr.tool.GrammarAST;
import org.antlr.tool.GrammarSyntaxMessage;
import org.antlr.tool.Message;
import org.antlr.tool.ToolMessage;
import org.antlr.works.editor.antlr3.parsing.AntlrSyntaxErrorV3;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.spi.editor.hints.Severity;
/**
*
* @author Sam Harwell
*/
public class ANTLRErrorProvidingParser extends ANTLRParser {
private final List<SyntaxError> syntaxErrors = new ArrayList<>();
private final DocumentSnapshot snapshot;
public ANTLRErrorProvidingParser(TokenStream input, DocumentSnapshot snapshot) {
super(input);
this.snapshot = snapshot;
}
@NonNull
public List<SyntaxError> getSyntaxErrors() {
return syntaxErrors;
}
public String getCurrentRuleName() {
return currentRuleName;
}
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
//String header = getErrorHeader(e);
String message = getErrorMessage(e, tokenNames);
syntaxErrors.add(new AntlrSyntaxErrorV3(snapshot, e != null ? e.token : null, e, message, Severity.ERROR));
super.displayRecognitionError(tokenNames, e);
}
public static final class ErrorListener implements ANTLRErrorListener {
private final DocumentSnapshot snapshot;
public ErrorListener(DocumentSnapshot snapshot) {
this.snapshot = snapshot;
}
@Override
public void info(String string) {
}
@Override
public void error(Message msg) {
if (msg instanceof GrammarSyntaxMessage) {
GrammarSyntaxMessage syntaxMessage = (GrammarSyntaxMessage)msg;
Token token = syntaxMessage.offendingToken;
if (token == null)
return;
if (!(syntaxMessage.exception.input instanceof ANTLRParserTokenStream))
return;
ANTLRParserTokenStream stream = (ANTLRParserTokenStream)syntaxMessage.exception.input;
ANTLRErrorProvidingParser parser = stream.getParser();
if (parser == null)
return;
parser.syntaxErrors.add(new AntlrSyntaxErrorV3(snapshot, syntaxMessage.offendingToken, syntaxMessage.exception, msg.toString(), Severity.ERROR));
}
}
@Override
public void warning(Message msg) {
}
@Override
public void error(ToolMessage tm) {
}
}
private static class GrammarASTErrorNode extends GrammarAST {
public IntStream input;
public Token start;
public Token stop;
public RecognitionException trappedException;
public GrammarASTErrorNode(TokenStream input, Token start, Token stop, RecognitionException e) {
super(stop);
//Console.Out.WriteLine( "start: " + start + ", stop: " + stop );
if ( stop == null ||
( stop.getTokenIndex() < start.getTokenIndex() &&
stop.getType() != Token.EOF) ) {
// sometimes resync does not consume a token (when LT(1) is
// in follow set. So, stop will be 1 to left to start. adjust.
// Also handle case where start is the first token and no token
// is consumed during recovery; LT(-1) will return null.
stop = start;
}
this.input = input;
this.start = start;
this.stop = stop;
this.trappedException = e;
}
@Override
public boolean isNil() { return false; }
@Override
public String getText()
{
String badText;
if (start instanceof Token) {
int i = start.getTokenIndex();
int j = stop.getTokenIndex();
if (stop.getType() == Token.EOF) {
j = ((TokenStream)input).size();
}
badText = ((TokenStream)input).toString(i, j);
} else if (start instanceof Tree) {
badText = ((TreeNodeStream)input).toString(start, stop);
} else {
// people should subclass if they alter the tree type so this
// next one is for sure correct.
badText = "<unknown>";
}
return badText;
}
@Override
public void setText(String value) { }
@Override
public int getType() { return Token.INVALID_TOKEN_TYPE; }
@Override
public void setType(int value) { }
@Override
public String toString()
{
if (trappedException instanceof MissingTokenException)
{
return "<missing type: " +
( (MissingTokenException)trappedException ).getMissingType() +
">";
} else if (trappedException instanceof UnwantedTokenException) {
return "<extraneous: " +
( (UnwantedTokenException)trappedException ).getUnexpectedToken() +
", resync=" + getText() + ">";
} else if (trappedException instanceof MismatchedTokenException) {
return "<mismatched token: " + trappedException.token + ", resync=" + getText() + ">";
} else if (trappedException instanceof NoViableAltException) {
return "<unexpected: " + trappedException.token +
", resync=" + getText() + ">";
}
return "<error: " + getText() + ">";
}
}
public static class grammar_Adaptor extends CommonTreeAdaptor {
private final ANTLRErrorProvidingParser parser;
public grammar_Adaptor(ANTLRErrorProvidingParser parser) {
this.parser = parser;
}
@Override
public Object create(Token payload) {
GrammarAST t = new GrammarAST(payload);
if (parser != null)
t.enclosingRuleName = parser.getCurrentRuleName();
return t;
}
@Override
public Object errorNode(TokenStream input, Token start, Token stop, RecognitionException e) {
GrammarAST t = new GrammarASTErrorNode(input, start, stop, e);
if (parser != null)
t.enclosingRuleName = parser.getCurrentRuleName();
return t;
}
}
}