Skip to main content

Enhanced Java parser with Java 9-22 support, fork of javalang

Project description

Ljavalang

PyPI Python GitHub Actions

English | 中文

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

  • var local variable type inference
  • var in for-each / try-with-resources
  • var in lambda parameters

Java 14

  • Switch expression (case X -> arrow syntax)
  • Switch expression at expression level (return switch(...))
  • Multi-label case (case 1, 2, 3 ->)
  • yield statement
  • Pattern matching instanceof (obj instanceof String s)

Java 15

  • Text block ("""...""" triple-quoted strings)

Java 16

  • record class declaration
  • Local record / enum (inside method body)
  • Record as class member

Java 17

  • sealed class / interface
  • permits clause
  • non-sealed modifier

Java 21

  • Pattern matching switch (case String s ->)
  • Record pattern deconstruction (case Point(int x, int y) ->)
  • Nested record patterns
  • case null matching

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ljavalang-2.1.0.tar.gz (42.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ljavalang-2.1.0-py3-none-any.whl (45.2 kB view details)

Uploaded Python 3

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

Hashes for ljavalang-2.1.0.tar.gz
Algorithm Hash digest
SHA256 3c6fcbb210e10e0e07daed8e77262e3d74110064c4f9fcf0839b8a0ea9e158e8
MD5 43686fb7ab9fca23b9bd87c600770c0b
BLAKE2b-256 c4459552688d52524c30cb0812d114736bde99392554ee24a6d21434010d0253

See more details on using hashes here.

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

Hashes for ljavalang-2.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 05f2ed875f00d929116d54577a82f2843c4a18009a19439f942ea28ca9dd369d
MD5 041117e3c3e3bc58c0272c7d5bc53e9c
BLAKE2b-256 3046db79dda3658df7bf3fdcbea03a4a9f6d01754cdfa4f911fd60bbd5cb2957

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page