9/29/2025

ANTLR 4 into a Quarkus app

 

Using ANTLR 4 with Quarkus: A Practical Guide

Why combine ANTLR + Quarkus?

  • Quarkus gives you a fast, cloud/native Java framework with REST, live reload, native image, etc.

  • ANTLR is a powerful parser generator to define grammars and generate parsers/lexers.

  • Together, you can embed a DSL, query language, rule engine, or expression evaluator into a Quarkus microservice.

Challenges / caveats to be aware of:

  • You must integrate ANTLR’s code generation into your build so that generated sources are compiled by Quarkus.

  • In Quarkus dev mode, some Maven plugins (like antlr4-maven-plugin) may not run automatically; you might need to explicitly run generate-sources first. Lightrun

  • Be mindful of version mismatches between ANTLR tool/grammar version and runtime. Quarkus has historically shipped with a specific ANTLR runtime version. GitHub+1


Example: Expression Parsing via REST

Let’s build a simple Quarkus app that:

  1. Accepts a mathematical expression via REST (e.g. "2 + 3 * 4")

  2. Uses ANTLR-generated parser to parse and evaluate the expression

  3. Returns the result


Project Structure

quarkus-antlr-example/ ├── pom.xml ├── src │ ├── main │ │ ├── antlr4 │ │ │ └── Expr.g4 │ │ └── java │ │ └── com/example │ │ ├── ExpressionEvaluator.java │ │ └── ExpressionResource.java │ └── test │ └── java │ └── com/example │ └── ExpressionResourceTest.java

Grammar: Expr.g4

Place under src/main/antlr4:

grammar Expr; prog: expr EOF ; expr : expr op=('*'|'/') expr # MulDiv | expr op=('+'|'-') expr # AddSub | INT # Int | '(' expr ')' # Parens ; INT : [0-9]+ ; WS : [ \t\r\n]+ -> skip ;

This defines a simple arithmetic grammar with precedence (multiplication/division higher than addition/subtraction).


pom.xml

Here’s a minimal pom.xml configured to run ANTLR code generation and build a Quarkus app:

<project xmlns="http://maven.apache.org/POM/4.0.0" ...> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>quarkus-antlr-example</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.version>3.0.0.Final</quarkus.platform.version> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <antlr4.version>4.10.1</antlr4.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>${quarkus.platform.artifact-id}</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Quarkus dependencies --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive</artifactId> </dependency> <!-- ANTLR runtime --> <dependency> <groupId>org.antlr</groupId> <artifactId>antlr4-runtime</artifactId> <version>${antlr4.version}</version> </dependency> </dependencies> <build> <plugins> <!-- ANTLR plugin to generate parser code --> <plugin> <groupId>org.antlr</groupId> <artifactId>antlr4-maven-plugin</artifactId> <version>${antlr4.version}</version> <executions> <execution> <id>generate-antlr</id> <phase>generate-sources</phase> <goals> <goal>antlr4</goal> </goals> <configuration> <sourceDirectory>src/main/antlr4</sourceDirectory> <outputDirectory>${project.build.directory}/generated-sources/antlr4</outputDirectory> </configuration> </execution> </executions> </plugin> <!-- Quarkus Maven plugin --> <plugin> <groupId>io.quarkus</groupId> <artifactId>quarkus-maven-plugin</artifactId> <version>${quarkus.platform.version}</version> <executions> <execution> <goals> <goal>build</goal> <goal>generate-code</goal> </goals> </execution> </executions> </plugin> <!-- Compiler plugin to include generated sources --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <generatedSourcesDirectory>${project.build.directory}/generated-sources/antlr4</generatedSourcesDirectory> </configuration> </plugin> </plugins> </build> </project>

Important note: In Quarkus dev mode, sometimes the ANTLR plugin doesn't run automatically unless you explicitly run mvn generate-sources before quarkus:dev. Lightrun


ExpressionEvaluator.java

package com.example; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; public class ExpressionEvaluator { public int evaluate(String input) { CharStream cs = CharStreams.fromString(input); ExprLexer lexer = new ExprLexer(cs); CommonTokenStream tokens = new CommonTokenStream(lexer); ExprParser parser = new ExprParser(tokens); ParseTree tree = parser.prog(); EvalVisitor visitor = new EvalVisitor(); return visitor.visit(tree); } private static class EvalVisitor extends ExprBaseVisitor<Integer> { @Override public Integer visitMulDiv(ExprParser.MulDivContext ctx) { int left = visit(ctx.expr(0)); int right = visit(ctx.expr(1)); if (ctx.op.getText().equals("*")) { return left * right; } else { return left / right; } } @Override public Integer visitAddSub(ExprParser.AddSubContext ctx) { int left = visit(ctx.expr(0)); int right = visit(ctx.expr(1)); if (ctx.op.getText().equals("+")) { return left + right; } else { return left - right; } } @Override public Integer visitInt(ExprParser.IntContext ctx) { return Integer.parseInt(ctx.INT().getText()); } @Override public Integer visitParens(ExprParser.ParensContext ctx) { return visit(ctx.expr()); } } }

ExpressionResource.java

package com.example; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @Path("/expr") public class ExpressionResource { ExpressionEvaluator evaluator = new ExpressionEvaluator(); @GET @Produces(MediaType.TEXT_PLAIN) public Response eval(@QueryParam("input") String input) { try { int result = evaluator.evaluate(input); return Response.ok(String.valueOf(result)).build(); } catch (Exception e) { return Response.status(Response.Status.BAD_REQUEST) .entity("Error: " + e.getMessage()) .build(); } } }

With this endpoint, you can call:

GET /expr?input=2*(3+4)

and get back 14.


Testing & Running

  • Run in dev mode:

    mvn clean generate-sources quarkus:dev
  • In production:

    mvn package java -jar target/quarkus-antlr-example-1.0.0-SNAPSHOT-runner.jar
  • Test via HTTP call, e.g.:

    curl "http://localhost:8080/expr?input=5+6*2"

Additional Tips & Caveats

  • ANTLR version mismatches can lead to runtime errors (ATN deserialization issues) when mixing versions. Make sure the version used to generate the parser matches the runtime version. GitHub+1

  • For native image builds, dynamic reflection used by ANTLR may need explicit registration or substitutions. Quarkus extensions sometimes provide built-in support.

  • If your grammar grows complex, consider organizing it modularly (lexer grammar, parser grammar, etc.).

  • Use custom error listeners to provide better syntax error messages.

  • For large grammars, incremental parsing or partial parsing might be needed, depending on performance.

Niciun comentariu:

K6 links

 K6 links GitHub - Xray-App/tutorial-js-k6 How to integrate k6 with Xray/Jira - DEV Community Non-Functional Testing: Load and Stress Tests ...