Enhanced Java parser with Java 9-22 support, fork of javalang
Project description
Ljavalang
Enhanced fork of javalang — fixes core AST bugs, adds Java 9-22 syntax support, zero external dependencies.
Installation
pip install ljavalang
The package name is ljavalang on PyPI, but the import name remains javalang — fully compatible with upstream.
Quick Start
>>> import javalang
>>> tree = javalang.parse.parse('package com.example; class Test {}')
>>> tree.package.name
'com.example'
>>> tree.types[0].name
'Test'
New Syntax Examples
Java 14 switch expression:
>>> code = '''
... class T {
... int m(int x) {
... return switch(x) {
... case 1 -> 10;
... case 2 -> 20;
... default -> 0;
... };
... }
... }'''
>>> tree = javalang.parse.parse(code)
>>> tree.types[0].body[0].body[0].expression
SwitchExpression
Java 16 record:
>>> tree = javalang.parse.parse('record Point(int x, int y) {}')
>>> tree.types[0]
RecordDeclaration
Java 21 record pattern:
>>> code = '''
... class T {
... record Point(int x, int y) {}
... void m(Object o) {
... switch(o) {
... case Point(int x, int y) -> System.out.println(x + y);
... default -> {}
... }
... }
... }'''
>>> javalang.parse.parse(code) # parses successfully
Chained method calls (core bug fix):
>>> code = 'class T { void m(String cmd) { Runtime.getRuntime().exec(cmd); } }'
>>> tree = javalang.parse.parse(code)
>>> # Upstream incorrectly places exec in a flat selectors list
>>> # Ljavalang correctly builds nested MethodInvocation qualifier chain
Visitor Pattern
from javalang.visitor import JavaVisitor
class MethodCollector(JavaVisitor):
def __init__(self):
self.methods = []
def visit_MethodDeclaration(self, node):
self.methods.append(node.name)
self.generic_visit(node)
collector = MethodCollector()
collector.visit(tree)
print(collector.methods) # ['foo', 'bar', ...]
Token Position Range
from javalang.tokenizer import tokenize
code = 'int x = 42;'
for token in tokenize(code):
r = token.position.range
print(f'{token.value} -> code[{r.start}:{r.stop}] = {code[r]!r}')
# int -> code[0:3] = 'int'
# x -> code[4:5] = 'x'
AST Node end_position
>>> code = 'class T { void m() { try { int x = 1; } catch (Exception e) {} } }'
>>> tree = javalang.parse.parse(code)
>>> tree.types[0].end_position
Position(line=1, column=66, range=slice(65, 66, None))
Supported Java Syntax
Full list (click to expand)
Java 8 (upstream)
- Lambda expressions
- Method references
- Type annotations
- Interface default/static methods
- Generic try-with-resources
- Receiver parameter (
Inner.this)
Java 9
- try-with-resources with effectively final variables
- module-info.java (module / open module / requires / exports / opens / uses / provides)
- Interface private methods
- Anonymous class diamond operator
Java 10-11
varlocal variable type inferencevarin for-each / try-with-resourcesvarin lambda parameters
Java 14
- Switch expression (
case X ->arrow syntax) - Switch expression at expression level (
return switch(...)) - Multi-label case (
case 1, 2, 3 ->) yieldstatement- Pattern matching
instanceof(obj instanceof String s)
Java 15
- Text block (
"""..."""triple-quoted strings)
Java 16
recordclass declaration- Local record / enum (inside method body)
- Record as class member
Java 17
sealedclass / interfacepermitsclausenon-sealedmodifier
Java 21
- Pattern matching switch (
case String s ->) - Record pattern deconstruction (
case Point(int x, int y) ->) - Nested record patterns
case nullmatching
Java 22
- Unnamed variable
_ - Unnamed lambda parameters
Key Changes from Upstream
| Category | Details |
|---|---|
| Core bug fix | Chained method calls now produce nested MethodInvocation qualifier chains instead of flat selectors lists |
| Bug fixes | 6 upstream bugs fixed: #90/#117 (DecimalInteger), #145 (Character token), #81/#112 (type annotations), #141 (void return_type) |
| New features | end_position for 6 AST nodes, Visitor class, Position.range, tokenize return_index, ReceiverParameter, prefix/postfix operators |
| Dependencies | Zero external dependencies (removed six) |
| Packaging | Modern pyproject.toml (PEP 621), setuptools ≥64.0 |
| Tests | 112 pytest tests (Python 3.9-3.12 CI matrix) |
Project Structure
Ljavalang/
├── pyproject.toml # Packaging (PEP 621)
├── javalang/
│ ├── parse.py # Entry: parse() / parse_expression()
│ ├── parser.py # Recursive descent parser (~2800 lines)
│ ├── tokenizer.py # Lexer (~700 lines)
│ ├── tree.py # AST node definitions (~340 lines)
│ ├── visitor.py # Visitor pattern traversal
│ └── test/ # 112 test cases
└── docs/
├── changelog.md
└── architecture.md
Credits
Based on c2nes/javalang by Chris Thunes.
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ljavalang-2.1.0.tar.gz.
File metadata
- Download URL: ljavalang-2.1.0.tar.gz
- Upload date:
- Size: 42.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c6fcbb210e10e0e07daed8e77262e3d74110064c4f9fcf0839b8a0ea9e158e8
|
|
| MD5 |
43686fb7ab9fca23b9bd87c600770c0b
|
|
| BLAKE2b-256 |
c4459552688d52524c30cb0812d114736bde99392554ee24a6d21434010d0253
|
File details
Details for the file ljavalang-2.1.0-py3-none-any.whl.
File metadata
- Download URL: ljavalang-2.1.0-py3-none-any.whl
- Upload date:
- Size: 45.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05f2ed875f00d929116d54577a82f2843c4a18009a19439f942ea28ca9dd369d
|
|
| MD5 |
041117e3c3e3bc58c0272c7d5bc53e9c
|
|
| BLAKE2b-256 |
3046db79dda3658df7bf3fdcbea03a4a9f6d01754cdfa4f911fd60bbd5cb2957
|