Reputation: 7427
For a given java file, I'd like to check if it's syntactically correct. ie. If it has semi-colons in the right places, matching parenthesis etc. Importantly, I need to check the file in isolation from all of its dependencies.
This other answer is promising, however it's really doing a semantic check rather than a syntactic check. It does what a compiler would do - check all the imports statements as well as verify external classes that are referenced in the code.
Is there a way to do a true syntax check? (A check that only inspects the raw text against Java's formal grammar)
Upvotes: 4
Views: 4258
Reputation: 12450
Create or use a Java source code parser. For some parser generators there are public Java grammars available - you could use it to generate the parser.
E.g. Java 8 grammar for ANTLR (no idea about quality of that grammar though, you'd have to do your evaluation - but the grammar is written by the author of ANTLR, so should be OK I guess).
Upvotes: 4
Reputation: 2673
I was researching how to do a quick java syntax-checker for usage in vim. I inspired by @Fidel's answer (thanks for the example checker!) but it didn't suffice me as I needed it to work in a standalone way.
Here is a step by step process of what had to be done in order to have runnable command:
# download and generate .java classes
wget https://repo1.maven.org/maven2/org/antlr/antlr4/4.5.3/antlr4-4.5.3.jar -O antlr4-4.5.3.jar
wget https://raw.githubusercontent.com/antlr/grammars-v4/master/java8/Java8.g4 -O Java8.g4
java -jar antlr4-4.5.3.jar ./Java8.g4
Then write a syntax checker, perhaps similar to @Fidel's one and place it under package checker;
place it directly under ./checker
directory
Also place all of the generated .java
classes under that package (ie. put package checker;
to the first line of all the files)
Run:
# prepare directory structure and compile
mkdir -p checker && mv *.java ./checker/
javac -cp antlr4-4.5.3.jar ./checker/*.java
Class-Path: antlr4-4.5.3.jar
Main-Class: checker.<name-of-your-checker-class>
# package into a jar file and run to test everything works
jar cfm checker.jar Manifest.txt checker/*.class
java -jar ./checker.jar <filename-you-want-to-run-syntax-check-against>
For my usage in vim I then created a simple shell script to wrap execution of the process of running the java jar that looks like this:
#!/bin/bash
DIR=$(readlink -f $0)
DIR=${DIR:0:(-3)} # put the length of the script name here instead of '3'
java -jar ${DIR}checker.jar $@
Then make it executable and symlink it under $PATH
:
chmod a+x <file-name>
sudo ln -s /path/to/the/script /usr/bin/java-syntax
And finally added a keybinding like this into a .vimrc
file (ie. to map running the syntax-check when F10 key is pressed):
" java syntax validation
map <F10> :!java-syntax % <CR>
I also stored the process into a github repository, where all the commands are prepared in a makefile and it suffices to run make
in order to build and package the checker. Feel free to use it as an inspiration.
Upvotes: 1
Reputation: 7427
As Jiri suggested, use the ANTLR library to put together a syntax checker.
Download and extract the ANTLR grammars from here
Download the ANTLR4 jar from here
Run the following command to generate classes from the Java 8 grammar:
java -jar antlr-4.5.3-complete.jar ~/Downloads/grammars-v4-master/java8/Java8.g4
Copy the .java files that were created into your project
Then you can write your syntax checker:
public static void main(String[] args) throws SAXException, IOException {
ANTLRInputStream input = new ANTLRFileStream(args[0]);
Java8Lexer lexer = new Java8Lexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java8Parser parser = new Java8Parser(tokens);
final StringBuilder errorMessages = new StringBuilder();
parser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
String err = String.format("Failed to parse at line %d:%d due to %s", line, charPositionInLine + 1, msg);
errorMessages.append(err);
errorMessages.append(System.lineSeparator());
}
});
parser.compilationUnit();
int syntaxErrors = parser.getNumberOfSyntaxErrors();
if (syntaxErrors == 0) {
System.out.println(args[0] + ": PASS");
} else {
System.out.println(args[0] + ": FAILED (" + syntaxErrors + " syntax errors");
}
}
Upvotes: 3