This is a Java preprocessor, written in Python, which allows you to write Java using Python indentation and without semicolons. I wrote it in my spare time, so there may be lots of bugs. I tried to support every syntactical element in the Java language that I could, including new constructs from Java 12 and 13 (switch expressions and the yield statement).
Call the program with python javapy.py <filename>
and it will output a file
called the same thing except with a .java
extension.
The program tries to format the file to be human-readable but may not be quite right in places. Use your own formatter as necessary.
The parser does not check for semantically invalid syntax, such as duplicate variable names, duplicate methods, improper package names, illegal modifiers, etc.
Blocks are usually not allowed anymore. Instead of blocks, use a Python Suite, which is a colon followed by a series of elements all indented the same amount.
Examples:
Normal Java:
public class Example {
public static final int x, y;
}
JavaPy:
public class Example:
public static final int x, y
_______________________________________________________________________
Normal Java:
public int foo(int x) {
if(x < 10) {
return 2*x - 1;
} else {
return x % 3 * x - 6;
}
}
JavaPy:
public int foo(int x):
if x < 10:
return 2*x - 1
else:
return x % 3 * x - 6
If a statement would normally require a parenthesised condition after its keyword, the parenthesis are now optional.
Examples:
Normal Java:
if(condition) {
doSomething();
} else if(anotherCondition) {
doSomethingElse();
} else {
doSomething2();
}
JavaPy:
if condition:
doSomething()
else if anotherCondition:
doSomethingElse()
else:
doSomething2()
_______________________________________________________________________
Normal Java:
synchronized(this) {
doSomethingWithThis();
}
JavaPy:
synchronized: // If you leave the lock expression out, it defaults to 'this'.
doSomethingWithThis()
_______________________________________________________________________
Normal Java:
try(Scanner keys = new Scanner(System.in)) {
System.out.print("Enter a number: ");
int x = Integer.parseInt(keys.nextLine());
System.out.println("Your number was: " + x);
} catch(NumberFormatException e) {
e.printStackTrace();
} finally {
System.out.println("Goodbye");
}
JavaPy:
try var keys = new Scanner(System.in):
System.out.print("Enter a number: ")
int x = Integer.parseInt(keys.nextLine())
System.out.println("Your number was: " + x)
catch NumberFormatException e:
e.printStackTrace()
finally:
System.out.println("Goodbye")
The one statement I have brought over from python is the from ... import ... statement.
Syntax: from <package-or-type-name> import [static] <qualified-name-or-wildcard> {, <qualified-name-or-wildcard>}
Examples:
Normal Java:
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
JavaPy:
from java.util import List, ArrayList, Map
_______________________________________________________________________
Normal Java:
import static com.test.Example.foo;
import static com.test.Example.bar;
import static com.test.Example.kaz;
JavaPy:
from com.test.Example import static foo, bar, kaz
_______________________________________________________________________
Normal Java:
import java.util.*;
import java.util.function.*;
JavaPy:
from java.util import *, function.*
Additionally, a single normal import statement can have multiple comma-separated imports in it.
Example:
import java.util.List, java.util.ArrayList
Sometimes, you may want to put certain things on multiple lines. You could end a line with a backslash (\) to join it with the following line, like in Python, or you could wrap it in parenthesis.
Examples:
Normal Java:
for(int x = aVeryLongExpression(),
y = anotherVeryLongExpression(),
z = yetAnotherVeryLongExpression();
x + y < z;
x++, y--, z--) {
System.out.println(x+y+z);
}
JavaPy:
for (int x = aVeryLongExpression(),
y = anotherVeryLongExpression(),
z = yetAnotherVeryLongExpression()); \
x + y < z; \
x++, y++, z++:
System.out.println(x+y+z)
_______________________________________________________________________
Normal Java:
public abstract class Example extends Superclass implements Interface1,
Interface2,
Interface3,
Interface4 {
...
}
JavaPy:
public abstract class Example extends Superclass implements (Interface1,
Interface2,
Interface3,
Interface4):
;
_______________________________________________________________________
Normal Java:
module com.test {
exports com.test.types;
requires com.example.services;
provides com.example.services.ExampleService with com.test.services.MyService,
com.test.services.TheirService;
}
JavaPy:
module com.test:
exports com.test.types
requires com.example.services
provides com.example.services.ExampleService with (com.test.services.MyService,
com.test.services.TheirService)
_______________________________________________________________________
Normal Java:
HashMap<String,
HashMap<Integer,
List<Pair<String, ?>>>> map = new HashMap<>();
JavaPy:
HashMap<(String,
HashMap<Integer,
List<Pair<String, ?>>>)> map = new HashMap<>()
_______________________________________________________________________
Normal Java:
try(var resource1 = getResource1();
var resource2 = getResource2()) {
doStuffWithResources();
} catch(NoSuchElementException
| NullPointerException
| ClassNotFoundException
| IllegalArgumentException e) {
e.printStackTrace();
} catch(IOException
| IllegalStateException e) {
e.printStackTrace();
}
JavaPy:
try (var resource1 = getResource1();
var resource2 = getResource2()):
doStuffWithResources()
catch (NoSuchElementException
| NullPointerException
| ClassNotFoundException
| IllegalArgumentException) e:
e.printStackTrace()
catch (IOException
| IllegalStateException e):
e.printStackTrace()
_______________________________________________________________________
Normal Java:
switch(day) {
case MONDAY,
TUESDAY,
WEDNESDAY -> {
if(day == Day.MONDAY) {
message = "It's a monday";
} else {
message = "meh";
}
}
case THURSDAY, FRIDAY -> {
message = "Almost";
}
default -> throw new WeekendException();
}
JavaPy:
switch day:
case (MONDAY,
TUESDAY,
WEDNESDAY) ->
if day == Day.MONDAY:
message = "It's a monday"
else:
message = "meh"
case THURSDAY, FRIDAY -> message = "Almost"
default -> throw new WeekendException()
_______________________________________________________________________
Normal Java:
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
JavaPy:
from java.util import (
List,
Set,
Map,
ArrayList,
HashSet,
HashMap
)
In Java, you can add a comma at the end of a list initializer:
int[] ints = {1,2,3,4,5,};
Python has that, plus allows you to do it in function arguments. Thus, JavaPy allows you to do it in function arguments, too.
foo(1,2,3,4,5,)
In some places, I just couldn't do the Suite syntax for a code block, like in lambda expressions or anonymous classes. So, for those, you'll just need to wrap the block in braces (Note that the block still needs to be indented).
Examples:
Normal Java:
new Object() {
public void foo() {
System.out.println("Foo");
}
}
JavaPy:
new Object() {
public void foo():
System.out.println("Foo")
}
_______________________________________________________________________
Normal Java:
(String str, int x) -> {
if(str == null) {
System.out.println("Str is null");
} else {
assert Integer.parseInt(str) == x;
}
}
JavaPy:
(String str, int x) -> {
if str == null:
System.out.println("Str is null")
else:
assert Integer.parseInt(str) == x
}
A happy consequence of using a modified version of the standard Python tokenize
module is that most* of Python's string literals are supported.
r"This is a raw string, useful for writing regexes or Windows file locations:"
r"C:\Users\user\Documents\GitHub\JavaPy\README.md"
R"This is also a raw string. VS Code highlighters generally highlight the lower-case"
R"'r' string literal as a regex, but leave this one alone."
""" This is a multi-line string.
It can span several lines with ease.
Unlike Java 14's text blocks, the first line can appear right after the opening quotes.
I may or may not change this in the future when Java 14 is released.
"""
R""" Raw multi-line strings are also
supported. \ has no power here!"""
______________________________________________________________________________
*Single-quoted literals aren't supported, as single quotes are reserved for
character literals. Also, for obvious reasons, the unicode literal is not supported. Currently, the bytes literal isn't supported either, although I may make it a literal for a byte[]
array in the future. Additionally, the format-string literal isn't supported, but I also may implement that in the future. I'm not sure which syntax to use for that - Python's is good, but Java doesn't support Python's format()
syntax, it uses the old C syntax. Current options are: Python syntax minus the format flags, Groovy Syntax using $
, or some custom syntax.
See example.javapy
for a lot more code examples, and javapy/test.java
+ javapy/test.javapy
for two exactly equivalent files, one in Java syntax and one in JavaPy syntax.