AST Classes
AST Classes
JMLtk’s abstract syntax tree is the JavaParser AST extended with JML and KeY
nodes. Every construct the parser accepts — a statement, an expression, a type,
a JML clause — is represented by exactly one class that inherits from Node.
This page describes the design philosophy behind those classes: how they are
named, how they are structured, and the conventions every AST class follows.
The syntax they represent is documented, element by element with examples, on
the Syntax Elements page.
One class per syntax element
The AST mirrors the grammar javaparser-core/src/main/javacc/java.jj almost
one-to-one: a grammar rule produces a node of the same name. If two rules can’t
be told apart syntactically, they still get distinct classes; if a rule is a
simple choice of others, a single class with a kind discriminator is used.
The classes live in three worlds, clearly separated by package and prefix:
| Package | Prefix | Example | Represents |
|---|---|---|---|
com.github.javaparser.ast.* | (none, JavaParser names) | MethodCallExpr, ForStmt, ClassOrInterfaceType | plain Java |
com.github.javaparser.ast.jml.* | Jml | JmlContract, JmlQuantifiedExpr, JmlGhostStmt | JML (below, split into body, clauses, doc, expr, proof, stmt, type) |
com.github.javaparser.ast.key.* | Key | KeyCatchAllStmt, KeyExecStmt, KeyTransactionStmt | KeY’s ProofJava / SchemaJava extensions |
The jml sub-packages follow the node kind: clauses holds the contract
parts (JmlClause, JmlContract), expr the JML-only expressions
(JmlQuantifiedExpr, JmlLetExpr, JmlMultiCompareExpr, JmlLabelExpr),
stmt the JML statements (JmlGhostStmt, JmlRefiningStmt,
JmlBeginStmt/JmlEndStmt), type the specification-only types
(JmlLogicType), and doc the JML documentation constructs.
Naming
- Suffixes encode the grammatical category: expressions end in
Expr(BinaryExpr,MethodCallExpr), statements inStmt(IfStmt,JmlGhostStmt), types inType(PrimitiveType,ClassOrInterfaceType), and JML specification parts inClause/Contract(JmlClause,JmlContract). One class per clause shape:JmlConditionalClause(predicate clauses:requires/ensures/invariant),JmlSignalsClause/JmlSignalsOnlyClause(signals (E e) P/signals_only T1, T2),JmlSimpleExprClause/JmlMultiExprClause(single- and multi-expression clauses such asassignable),JmlCallableClause(callable),JmlForallClause,JmlOldClause,JmlLabeledClause,JmlInfFlowClause. The specific keyword is then an enum discriminator,JmlClauseKind(REQUIRES,ENSURES,ASSIGNABLE, …). Jml/Keyprefixes are mandatory: they are the only way to distinguish JML/KeY constructs from plain Java in a mixed AST, and they keep thejml/keysub-packages self-identifying.- Names are the grammar names: renaming a production means renaming its class, its visit methods, its pretty-printer rule and the metamodel entry together (see generated code).
Structure of an AST class
- Everything is a
Node.Nodeprovides parent/child navigation (getParentNode,findAncestor), ranges (TokenRange,Range),DataKey-based annotations and the visitor machinery. - Fields are the syntactic parts. Children are
Nodeproperties, other parts (names, modifiers, keywords) are regular fields. Both get plain getter/setter pairs. There are noprotectedback doors; accessors are the API. - Interfaces express shared shape. About 30
NodeWith*mixins (NodeWithName,NodeWithModifiers,NodeWithJavadoc,NodeWithBody, …) capture behaviour shared across classes; JML addsNodeWithContractsandNodeWithJmlTags. A class implements the mixins that fit its shape instead of inheriting them. - One constructor. Every node has an
@AllFieldsConstructorwhose parameter list mirrors the fields in declaration order — the constructor is a declaration of the node’s structure. - Generation annotations.
@OptionalPropertymarks fields that may be absent (used by the code generators, the observers and the visitors);@DerivedPropertymarks fields that are redundant with others and should be treated specially. isXxx()/asXxx()helpers. Type-test predicates and unchecked casts follow theExpression-style JavaParser idiom.
Generated code — never hand-edit
The metamodel, the visitors, the pretty printer and the token kinds are
generated: Node*MetaModel.java in metamodel, the clone/equals/hashcode/
modifier/observer visitors, ObservableProperty, JavaToken kinds and the
pretty-printing strategies. You do not edit them — you change the AST class and
regenerate:
./scripts/run-generators.sh # metamodel + core generators + spotless
./gradlew :javaparser-core:compileJavacc # regenerate the parser from java.jj./scripts/run-generators.sh finishes with git diff --exit-code, so a
missing regeneration fails CI (the “Metamodel/Model are fresh” check).
Manual edits to generated files are rejected in review.
Grammar and tokens. New tokens need a dedicated lexer rule in java.jj
(existing rules must not be reordered), and every token kind must be
classified in TokenTypes#getCategory(int) — TokenTypesTest enforces it.
Javadoc shows the syntax
Every AST class documents — in its Javadoc — an example of the underlying syntax construct. A statement node shows a piece of code; a clause node shows a JML phrase; an expression node shows the textual operator. This is the single source of truth that the Syntax Elements page is built from, and it keeps the tree answerable to “what does this class parse?”:
/**
* A {@code for} statement.
* <p>Example:
* <pre>
* for (int i = 0; i < 10; i++) { … }
* </pre>
*/
public final class ForStmt extends Statement { … }Coding style
- four-space indentation, no tabs;
- wrap at 120 columns; wrapped arguments align to the opening parenthesis;
- type and method annotations go on a separate line;
- every new file carries the project license header (LGPL-3.0/Apache-2.0),
separated from the
packagedeclaration by a blank line; spotlessApplyenforces the formatting,spotlessCheckin CI.
Extending the AST
Adding or removing classes and fields is a guided process — the full checklist
is the Guide to Adding New Nodes and Fields in the repository’s
CONTRIBUTING.md. The condensed workflow:
- Add the node outline: class in the
Nodehierarchy, fields with@OptionalPropertywhere needed,NodeWith*mixins, an@AllFieldsConstructor, and stubbed visitor methods — enough to compile. - Register the class in
ALL_NODE_CLASSESof the metamodel generator (after any parent classes). - Run
./scripts/run-generators.sh— then fix the pretty-printervisitmethods (bothDefaultPrettyPrinterVisitorandPrettyPrintVisitor) and theConcreteSyntaxModelentry (concreteSyntaxModelByClass.put(…, sequence())). - Add grammar rules (
java.jj), language-level validators, and AST tests for the new node (see examples in the repo). - Remove a class or attribute: remove it from
ALL_NODE_CLASSES, regenerate, and follow the compiler errors (visitors lose thevisitmethod; attributes lose their getter/setter stubs).
JML nodes follow the same process inside com.github.javaparser.ast.jml;
KeY nodes inside com.github.javaparser.ast.key.
See the Development section for building the sources and the generator tooling.