Fidel
Fidel

Reputation: 7427

Syntax check on a java file

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

Answers (3)

Jiri Tousek
Jiri Tousek

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

helvete
helvete

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:

  • Run:
# 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
  • Prepare a Manifest file that will look similarly to:
Class-Path: antlr4-4.5.3.jar
Main-Class: checker.<name-of-your-checker-class>
  • Run:
# 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

Fidel
Fidel

Reputation: 7427

As Jiri suggested, use the ANTLR library to put together a syntax checker.

  1. Download and extract the ANTLR grammars from here

  2. Download the ANTLR4 jar from here

  3. 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

  4. 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

Related Questions