JavaPy
Program Java with Python indentation!
Install / Use
/learn @raptor494/JavaPyREADME
JavaPy
Description
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).
Usage
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.
Differences from Normal Java
Code Blocks
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
Statements containing other statements
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")
Import Declarations
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
<!-- No matter what I try, I cannot get all three keywords to be
highlighted (from, import, static). Even though JavaScript has
all three keywords, and the highlighting works as I want it to
in the VS Code preview, GitHub for whatever reason only highlights
'import'. So, I'll just stick with Java for now. -->
_______________________________________________________________________
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
Optional parenthesis
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
)
Unnecessary Commas
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,)
Places where blocks ARE needed
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
}
Additions
String Literals
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 wh
