From 764be761d5c8266a7323db927430174b5791b111 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Mon, 23 Apr 2012 12:38:25 -0400 Subject: [PATCH 01/53] Extends AST type replacements with remaining primitive types. --- java2python/config/default.py | 16 ++++++++-------- test/BasicTypes1.java | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 test/BasicTypes1.java diff --git a/java2python/config/default.py b/java2python/config/default.py index a6a49c1..45bb503 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -148,15 +148,13 @@ (Type('TRUE'), transform.true2True), (Type('IDENT'), transform.keywordSafeIdent), + (Type('FLOATING_POINT_LITERAL'), transform.syntaxSafeFloatLiteral), - (Type('FLOATING_POINT_LITERAL'), - transform.syntaxSafeFloatLiteral), - - (Type('TYPE') > Type('BOOLEAN'), - transform.typeSub), - - (Type('TYPE') > Type('DOUBLE'), - transform.typeSub), + (Type('TYPE') > Type('BOOLEAN'), transform.typeSub), + (Type('TYPE') > Type('BYTE'), transform.typeSub), + (Type('TYPE') > Type('CHAR'), transform.typeSub), + (Type('TYPE') > Type('DOUBLE'), transform.typeSub), + (Type('TYPE') > Type('SHORT'), transform.typeSub), (Type('TYPE') > Type('QUALIFIED_TYPE_IDENT') > Type('IDENT'), transform.typeSub), @@ -202,6 +200,8 @@ 'boolean' : 'bool', 'IndexOutOfBoundsException' : 'IndexError', 'Integer' : 'int', + 'short' : 'int', + 'byte' : 'int', 'Object' : 'object', 'String' : 'str', 'char' : 'str', diff --git a/test/BasicTypes1.java b/test/BasicTypes1.java new file mode 100644 index 0000000..60c43b8 --- /dev/null +++ b/test/BasicTypes1.java @@ -0,0 +1,17 @@ +class BasicTypes1 { + char chr; + byte byt; + short shr; + int in_t; + long lng; + float flt; + double dbl; + + public static void main(String[] args) { + BasicTypes1 bt1 = new BasicTypes1(); + System.out.println(bt1.byt); + System.out.println(bt1.shr); + System.out.println(bt1.in_t); + } + +} From 2922fbd5a83c855f7c080a908d96147f64758ad8 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Mon, 23 Apr 2012 23:07:14 +0300 Subject: [PATCH 02/53] Cast traslated only for primitives --- java2python/compiler/visitor.py | 20 +++++++++++++++++++- java2python/lang/base.py | 5 +++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 40b6ea7..8f2e0da 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -678,7 +678,6 @@ def acceptPreformatted(self, node, memo): return acceptPreformatted acceptArrayElementAccess = makeNodePreformattedExpr(FS.l + '[' + FS.r + ']') - acceptCastExpr = makeNodePreformattedExpr(FS.l + '(' + FS.r + ')' ) # problem? acceptDiv = makeNodePreformattedExpr(FS.l + ' / ' + FS.r) acceptLogicalAnd = makeNodePreformattedExpr(FS.l + ' and ' + FS.r) acceptLogicalNot = makeNodePreformattedExpr('not ' + FS.l) @@ -690,6 +689,25 @@ def acceptPreformatted(self, node, memo): acceptUnaryMinus = makeNodePreformattedExpr('-' + FS.l) acceptUnaryPlus = makeNodePreformattedExpr('+' + FS.l) + def acceptCastExpr(self, node, memo): # problem? + """ Accept and process a cast expression. """ + # If the type of casting is a primitive type, + # then do the cast, else drop it. + factory = self.factory.expr + typeTok = node.firstChildOfType(tokens.TYPE) + typeIdent = typeTok.firstChild() + typeName = typeIdent.text + if typeIdent.type == tokens.QUALIFIED_TYPE_IDENT: + typeName = typeIdent.firstChild().text + + if typeName in tokens.primitiveTypeNames: + # Cast using the primitive type constructor + self.fs = typeName + '(' + FS.r + ')' + else: + self.fs = FS.r + self.left, self.right = visitors = factory(parent=self), factory(parent=self) + self.zipWalk(node.children, visitors, memo) + def makeAcceptPrePost(suffix, pre): """ Make an accept method for pre- and post- assignment expressions. """ def acceptPrePost(self, node, memo): diff --git a/java2python/lang/base.py b/java2python/lang/base.py index 4c10110..0633b8e 100644 --- a/java2python/lang/base.py +++ b/java2python/lang/base.py @@ -88,6 +88,11 @@ def methodTypes(self): mod = self.module return (mod.VOID_METHOD_DECL, mod.FUNCTION_METHOD_DECL, ) + @property + def primitiveTypeNames(self): + """ Type name of well-known primitive types """ + return ('bool', 'str', 'int', 'long', 'float', ) + @property def module(self): """ Provides lazy import to the parser module. """ From db60b700980fa89d86bcf0ca6c13bfef4710ae06 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Tue, 24 Apr 2012 00:03:54 +0300 Subject: [PATCH 03/53] Cast fully handled. For objects and primitives --- java2python/compiler/visitor.py | 15 ++++++++++++--- java2python/config/default.py | 9 +++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 8f2e0da..8e8a54d 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -689,7 +689,7 @@ def acceptPreformatted(self, node, memo): acceptUnaryMinus = makeNodePreformattedExpr('-' + FS.l) acceptUnaryPlus = makeNodePreformattedExpr('+' + FS.l) - def acceptCastExpr(self, node, memo): # problem? + def acceptCastExpr(self, node, memo): """ Accept and process a cast expression. """ # If the type of casting is a primitive type, # then do the cast, else drop it. @@ -699,12 +699,21 @@ def acceptCastExpr(self, node, memo): # problem? typeName = typeIdent.text if typeIdent.type == tokens.QUALIFIED_TYPE_IDENT: typeName = typeIdent.firstChild().text - + if typeName in tokens.primitiveTypeNames: # Cast using the primitive type constructor self.fs = typeName + '(' + FS.r + ')' else: - self.fs = FS.r + mode = self.config.last('objCastMode') + if mode == 'drop': + # Use drop policy + self.fs = FS.r + elif mode == 'ctor': + # Make constructor + self.fs = FS.l + '(' + FS.r + ')' + else: + warn('Couldn\'t perform cast operation. ' + typeName + \ + ' is not a primitive and objCastMode in the config file has wrong value.') self.left, self.right = visitors = factory(parent=self), factory(parent=self) self.zipWalk(node.children, visitors, memo) diff --git a/java2python/config/default.py b/java2python/config/default.py index 45bb503..bbe6d31 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -173,6 +173,15 @@ # convenience. +# Specifies how cast operations of non-primitive types are handled +# (primitive types are automatically handled) +# Valid values: +# - 'drop': completely drops type and cast info +# - 'ctor': converts the cast in a constructor call +# E.g.: (Cast) x -> Cast(x) +objCastMode = 'drop' + + # module output subs. moduleOutputSubs = [ (r'System\.out\.println\((.*)\)', r'print \1'), From 21f877987be66552d3701b86165291d58c184549 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Tue, 24 Apr 2012 16:17:18 -0400 Subject: [PATCH 04/53] Makes expression cast config point a callable. --- java2python/compiler/visitor.py | 15 +++++-------- java2python/config/default.py | 15 +++++-------- java2python/mod/basic.py | 8 +++++++ test/Cast0.java | 40 +++++++++++++++++++++++++++++++++ test/configs/Cast0.py | 3 +++ 5 files changed, 62 insertions(+), 19 deletions(-) create mode 100644 test/Cast0.java create mode 100644 test/configs/Cast0.py diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 8e8a54d..cb97f2c 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -699,21 +699,16 @@ def acceptCastExpr(self, node, memo): typeName = typeIdent.text if typeIdent.type == tokens.QUALIFIED_TYPE_IDENT: typeName = typeIdent.firstChild().text - + if typeName in tokens.primitiveTypeNames: # Cast using the primitive type constructor self.fs = typeName + '(' + FS.r + ')' else: - mode = self.config.last('objCastMode') - if mode == 'drop': - # Use drop policy - self.fs = FS.r - elif mode == 'ctor': - # Make constructor - self.fs = FS.l + '(' + FS.r + ')' + handler = self.configHandler('Cast') + if handler: + handler(self, node) else: - warn('Couldn\'t perform cast operation. ' + typeName + \ - ' is not a primitive and objCastMode in the config file has wrong value.') + warn('No handler to perform cast of non-primitive type %s.', typeName) self.left, self.right = visitors = factory(parent=self), factory(parent=self) self.zipWalk(node.children, visitors, memo) diff --git a/java2python/config/default.py b/java2python/config/default.py index bbe6d31..e69c220 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -168,20 +168,17 @@ # in method declarations. set to 0 to disable. #minIndentParams = 5 +# Specifies handler for cast operations of non-primitive types are handled +# (primitive types are automatically handled). Use basic.castDrop to leave +# cast expressions out of generated source. Use basic.castCtor to transform +# casts into constructor calls. Or you can specify a function of your own. +expressionCastHandler = basic.castDrop + # Values below are used by the handlers. They're here for # convenience. -# Specifies how cast operations of non-primitive types are handled -# (primitive types are automatically handled) -# Valid values: -# - 'drop': completely drops type and cast info -# - 'ctor': converts the cast in a constructor call -# E.g.: (Cast) x -> Cast(x) -objCastMode = 'drop' - - # module output subs. moduleOutputSubs = [ (r'System\.out\.println\((.*)\)', r'print \1'), diff --git a/java2python/mod/basic.py b/java2python/mod/basic.py index e226545..63c1806 100644 --- a/java2python/mod/basic.py +++ b/java2python/mod/basic.py @@ -7,6 +7,8 @@ from os import path from re import sub as rxsub +from java2python.lib import FS + def shebangLine(module): """ yields the canonical python shebang line. """ @@ -237,3 +239,9 @@ def moveStaticExpressions(cls): module.adopt(newExpr, index=len(module.children)) +def castCtor(expr, node): + expr.fs = FS.l + '(' + FS.r + ')' + + +def castDrop(expr, node): + expr.fs = FS.r diff --git a/test/Cast0.java b/test/Cast0.java new file mode 100644 index 0000000..c680377 --- /dev/null +++ b/test/Cast0.java @@ -0,0 +1,40 @@ +interface Something { + public int foo(); +} + + +interface SomethingElse { + public int foo(); +} + + +class Both implements Something { + public int foo() { + return 100; + } +} + +class What extends Both { + public int foo() { + return 200; + } +} + + +class Cast0 { + public static void main(String[] args) { + int x = 33; + Integer ix = (Integer) x; + System.out.println(x); + System.out.println(ix); + + What w = new What(); + + System.out.println( w.foo() ); + + Both b = (Both) w; + + System.out.println( b.foo() ); + + } +} \ No newline at end of file diff --git a/test/configs/Cast0.py b/test/configs/Cast0.py new file mode 100644 index 0000000..5580ba0 --- /dev/null +++ b/test/configs/Cast0.py @@ -0,0 +1,3 @@ +from java2python.mod import basic + +expressionCastHandler = basic.castDrop From 0306c8ed1617e339b319be191f03ee26bb3d22ad Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Tue, 24 Apr 2012 16:40:51 -0400 Subject: [PATCH 05/53] Adds test suite docs. --- doc/tests.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 4 +-- 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 doc/tests.md diff --git a/doc/tests.md b/doc/tests.md new file mode 100644 index 0000000..563e3c7 --- /dev/null +++ b/doc/tests.md @@ -0,0 +1,85 @@ +## Tests + +The java2python package includes a [test suite][] for exercising the compiler and +its various translation features. This doc explains how the tests work, how to +run these suite, and how to add new tests to it. + +### How the Test Suite Works + +The test suite is a [makefile][] that finds `.java` files in the same directory, +converts each to Python, runs both programs, and then compares their output. If +the output matches, the test is considered successful. If not, it's considered +a failure. + +### How to Run the Test Suite + +The simplest way to run the suite is to run all of it: + +```bash +$ cd some_path_to/java2python/test +$ make +``` + +This will print lines like this: + +``` +... +[PASS] Class00 +[PASS] Class01 +[PASS] Class02 +... +``` + +You can also run an individual test like this: + +```bash +$ make Class02 +[PASS] Class02 +``` + +Notice that there isn't a suffix to the file name; you don't run `make +Class02.java`, just `make Class02`. If you supply an extension, nothing will +happen and the test won't run. + +The `test` directory contains two helper scripts used by the makefile that you +can also use during development. The first is `[runjava][]`, which runs the +Java compiler and the Java VM with the indicated file. Use it like this: + +```bash +$ ./runjava Class01.java +Hello, world. +``` + +The second script is `[runj2py][]`, which is a handy shortcut for running the +`j2py` script with preset command line arguments for the test configuration. +You run it like this: + +```bash +$ ./runj2py Class01.java +#!/usr/bin/env python +""" generated source for module Class01 """ +class Class01(object): +... +``` + +### Adding New Tests + +When a new compiler feature is added, or when the translation semantics change, +it's a good idea to add one or more tests to the test suite. Follow this +general outline: + +1. Create a Java source file that exhibits the language feature in question. + +2. Name the Java source file `FeatureNN` where `NN` is the next number in +sequence for `Feature`, e.g., `Class14.java`. + +3. In your Java source, write one or more values to stdout with +`System.out.println`. + +4. Check the comparison via `make FeatureNN`. If the test passes, it might +indicate the new feature is working correctly. + +[test suite]: https://github.com/natural/java2python/tree/master/test/ +[makefile]: https://github.com/natural/java2python/blob/master/test/Makefile +[runjava]: https://github.com/natural/java2python/blob/master/test/runjava +[runj2py]: https://github.com/natural/java2python/blob/master/test/runj2py diff --git a/readme.md b/readme.md index 39af789..f38d91a 100644 --- a/readme.md +++ b/readme.md @@ -21,7 +21,7 @@ Here's a very simple example: ```bash $ cat HelloWorld.java ``` -```java +```java // This is the HelloWorld class with a single method. class HelloWorld { public static void main(String[] args) { @@ -60,4 +60,4 @@ if __name__ == '__main__': [introduction]: https://github.com/natural/java2python/tree/master/doc/intro.md [lots of docs]: https://github.com/natural/java2python/tree/master/doc/ [many options]: https://github.com/natural/java2python/tree/master/doc/customization.md -[plenty of tests]: https://github.com/natural/java2python/tree/master/test/ +[plenty of tests]: https://github.com/natural/java2python/tree/master/doc/tests.md From c818f80e9e7a39b966799fd2083f53c5f4a3a722 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Tue, 24 Apr 2012 16:42:37 -0400 Subject: [PATCH 06/53] Inside or outside? --- doc/tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tests.md b/doc/tests.md index 563e3c7..988d2d7 100644 --- a/doc/tests.md +++ b/doc/tests.md @@ -42,7 +42,7 @@ Class02.java`, just `make Class02`. If you supply an extension, nothing will happen and the test won't run. The `test` directory contains two helper scripts used by the makefile that you -can also use during development. The first is `[runjava][]`, which runs the +can also use during development. The first is [`runjava`][], which runs the Java compiler and the Java VM with the indicated file. Use it like this: ```bash @@ -50,7 +50,7 @@ $ ./runjava Class01.java Hello, world. ``` -The second script is `[runj2py][]`, which is a handy shortcut for running the +The second script is [`runj2py`][], which is a handy shortcut for running the `j2py` script with preset command line arguments for the test configuration. You run it like this: From 6578ed18a6a5592170931383e600584ec460648f Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Tue, 24 Apr 2012 16:43:10 -0400 Subject: [PATCH 07/53] Neither. --- doc/tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tests.md b/doc/tests.md index 988d2d7..a6592d5 100644 --- a/doc/tests.md +++ b/doc/tests.md @@ -42,7 +42,7 @@ Class02.java`, just `make Class02`. If you supply an extension, nothing will happen and the test won't run. The `test` directory contains two helper scripts used by the makefile that you -can also use during development. The first is [`runjava`][], which runs the +can also use during development. The first is [runjava][], which runs the Java compiler and the Java VM with the indicated file. Use it like this: ```bash @@ -50,7 +50,7 @@ $ ./runjava Class01.java Hello, world. ``` -The second script is [`runj2py`][], which is a handy shortcut for running the +The second script is [runj2py][], which is a handy shortcut for running the `j2py` script with preset command line arguments for the test configuration. You run it like this: From 940a4ffa10455ded43b55bd897589c8579e21255 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Tue, 24 Apr 2012 19:00:09 -0400 Subject: [PATCH 08/53] Fix for uppercase 'f' and 'd' in float/decimal literals. --- java2python/mod/transform.py | 2 +- test/BasicTypes2.java | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/BasicTypes2.java diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index 48d18fd..c01ec6a 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -48,7 +48,7 @@ def syntaxSafeFloatLiteral(node, config): value = node.token.text if value.startswith('.'): value = '0' + value - if value.endswith(('f', 'd')): + if value.lower().endswith(('f', 'd')): value = value[:-1] elif value.endswith(('l', 'L')): value = value[:-1] + 'L' diff --git a/test/BasicTypes2.java b/test/BasicTypes2.java new file mode 100644 index 0000000..5737eef --- /dev/null +++ b/test/BasicTypes2.java @@ -0,0 +1,33 @@ +class BasicTypes2 { + public static void main(String[] args) { + + Integer I = 32; + System.out.println(I); + + Boolean B = true; + System.out.println( B ? "ok" : "nope" ); + + Byte Y = 0; + System.out.println(Y); + + Character C = 'c'; + System.out.println(C); + + Short S = 128; + System.out.println(S); + + Long L = 128L; + System.out.println(L); + + Float F = 1.5F; + System.out.println(F); + + Double D = 1.5; + System.out.println(D); + + String T = "done"; + System.out.println(T); + + + } +} From 90fbe8301d1c390f81612217b72fe5e2c6fc3ebc Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Wed, 25 Apr 2012 09:20:14 -0400 Subject: [PATCH 09/53] Better manual PYTHONPATH for tests. --- doc/tests.md | 6 +++--- test/Makefile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/tests.md b/doc/tests.md index a6592d5..4cb31e8 100644 --- a/doc/tests.md +++ b/doc/tests.md @@ -41,9 +41,9 @@ Notice that there isn't a suffix to the file name; you don't run `make Class02.java`, just `make Class02`. If you supply an extension, nothing will happen and the test won't run. -The `test` directory contains two helper scripts used by the makefile that you -can also use during development. The first is [runjava][], which runs the -Java compiler and the Java VM with the indicated file. Use it like this: +The `test` directory contains two helper scripts that you can use during +development. The first is [runjava][], which runs the Java compiler and the +Java VM with the indicated file. Use it like this: ```bash $ ./runjava Class01.java diff --git a/test/Makefile b/test/Makefile index f99772e..64ac2d0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,7 @@ j2py = ../bin/j2py python_files := $(addsuffix .py, $(notdir $(basename $(wildcard *.java)))) test_targets := $(sort $(notdir $(basename $(wildcard *.java)))) -export PYTHONPATH := $(PYTHONPATH):. +export PYTHONPATH := $(PYTHONPATH):.:.. .PHONY: all clean From c878f1cd4f19cdc6a951cbfdc841c8a13702c350 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Wed, 25 Apr 2012 10:37:44 -0400 Subject: [PATCH 10/53] Typo. --- doc/translation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/translation.md b/doc/translation.md index 56e7036..41c7a6e 100644 --- a/doc/translation.md +++ b/doc/translation.md @@ -52,7 +52,7 @@ necessary, those expressions are moved outside of statements. All of the following assignment operators are translated into their Python equivalents: - + = += -= *= /= &= |= ^= %= <<= >>= The bit shift right (`>>>`)and bit shift assign right (`>>>=`) operators are @@ -131,7 +131,7 @@ statements. Java `try` and `catch` statements are translated to equivalent Python `try` and `except` statements. - + #### switch and case Java `switch` and `case` statements are translated to equivalent Python `if` @@ -160,7 +160,7 @@ statements. #### throw -Java `throw` statements are translated to equivalent Python `throw` statements. +Java `throw` statements are translated to equivalent Python `raise` statements. #### break From 38ddc21a59ce6340db3262b42d2ee8a194845155 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Wed, 25 Apr 2012 10:40:43 -0400 Subject: [PATCH 11/53] Adds link to the tests doc. --- doc/readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/readme.md b/doc/readme.md index 71f67c6..6629996 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -24,8 +24,14 @@ The [Usage][] page explains how to run the `j2py` script. The [Customization][] doc describes how to customize translation behavior. +#### Tests + +The [Tests][] page documents the test suite. + + [Customization]: https://github.com/natural/java2python/tree/master/doc/customization.md [Installation]: https://github.com/natural/java2python/tree/master/doc/install.md [Introduction]: https://github.com/natural/java2python/tree/master/doc/intro.md [Translation Details]: https://github.com/natural/java2python/tree/master/doc/translation.md [Usage]: https://github.com/natural/java2python/tree/master/doc/usage.md +[Tests]: https://github.com/natural/java2python/tree/master/doc/tests.md From a82653addf99bb842bd8e5be3fa43c4c7f6af6db Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Wed, 25 Apr 2012 13:29:31 -0400 Subject: [PATCH 12/53] Adds explicit PYTHONPATH. --- test/runj2py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runj2py b/test/runj2py index d35a109..b338a39 100755 --- a/test/runj2py +++ b/test/runj2py @@ -1,2 +1,2 @@ #!/bin/bash -../bin/j2py -d configs "$@" +PYTHONPATH=$PYTHONPATH:.. ../bin/j2py -d configs "$@" From 0d1e10388981d026c39775530dbe917593466d1c Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Wed, 25 Apr 2012 13:31:44 -0400 Subject: [PATCH 13/53] Extends typeSubs map with types listed in #9. --- java2python/config/default.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/java2python/config/default.py b/java2python/config/default.py index e69c220..7d09b1c 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -154,7 +154,10 @@ (Type('TYPE') > Type('BYTE'), transform.typeSub), (Type('TYPE') > Type('CHAR'), transform.typeSub), (Type('TYPE') > Type('DOUBLE'), transform.typeSub), + (Type('TYPE') > Type('INT'), transform.typeSub), (Type('TYPE') > Type('SHORT'), transform.typeSub), + (Type('TYPE') > Type('LONG'), transform.typeSub), + (Type('TYPE') > Type('DOUBLE'), transform.typeSub), (Type('TYPE') > Type('QUALIFIED_TYPE_IDENT') > Type('IDENT'), transform.typeSub), @@ -204,13 +207,28 @@ typeSubs = { 'Boolean' : 'bool', 'boolean' : 'bool', - 'IndexOutOfBoundsException' : 'IndexError', - 'Integer' : 'int', - 'short' : 'int', + + 'Byte' : 'int', 'byte' : 'int', - 'Object' : 'object', - 'String' : 'str', + + 'Char' : 'str', 'char' : 'str', + + 'Integer' : 'int', + 'int' : 'int', + + 'Short' : 'int', + 'short' : 'int', + + 'Long' : 'long', + 'long' : 'long', + + 'Double' : 'float', 'double' : 'float', + + 'String' : 'str', 'java.lang.String' : 'str', -} + + 'Object' : 'object', + 'IndexOutOfBoundsException' : 'IndexError', + } From 390f4339850d3a56bcb5090f0775597f29f10501 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Wed, 25 Apr 2012 21:29:03 +0300 Subject: [PATCH 14/53] Fix the skipped Float in typeSubs --- java2python/config/default.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java2python/config/default.py b/java2python/config/default.py index 7d09b1c..68f28d3 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -153,7 +153,7 @@ (Type('TYPE') > Type('BOOLEAN'), transform.typeSub), (Type('TYPE') > Type('BYTE'), transform.typeSub), (Type('TYPE') > Type('CHAR'), transform.typeSub), - (Type('TYPE') > Type('DOUBLE'), transform.typeSub), + (Type('TYPE') > Type('FLOAT'), transform.typeSub), (Type('TYPE') > Type('INT'), transform.typeSub), (Type('TYPE') > Type('SHORT'), transform.typeSub), (Type('TYPE') > Type('LONG'), transform.typeSub), @@ -223,6 +223,9 @@ 'Long' : 'long', 'long' : 'long', + 'Float' : 'float', + 'float' : 'float', + 'Double' : 'float', 'double' : 'float', From aea822ec6f60123ca583601f621e39ab024d361b Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Thu, 26 Apr 2012 20:11:07 +0300 Subject: [PATCH 15/53] Fix bug of not prepending instance vars with self --- java2python/compiler/visitor.py | 2 +- test/Self0.java | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/Self0.java diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index cb97f2c..3b55e31 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -641,7 +641,7 @@ def nodeOpExpr(self, node, memo): """ Accept and processes an operator expression. """ factory = self.factory.expr self.fs = FS.l + ' ' + node.text + ' ' + FS.r - self.left, self.right = visitors = factory(parent=self), factory() + self.left, self.right = visitors = factory(parent=self), factory(parent=self) self.zipWalk(node.children, visitors, memo) acceptAnd = nodeOpExpr diff --git a/test/Self0.java b/test/Self0.java new file mode 100644 index 0000000..269a96e --- /dev/null +++ b/test/Self0.java @@ -0,0 +1,22 @@ +class Self0 { + private int v1 = 100; + public int v2 = 2; + + public int test0(){ + return v2 + v1; + } + + public boolean test1(){ + return (v1 == v2 || v2 < v1 ); + } + + public static void main(String[] args) { + Self0 s = new Self0(); + System.out.println(s.test0()); + if(s.test1()) + System.out.println("True"); + else + System.out.println("False"); + } + +} From 6548316b203e4c03a05d9c6c4eab8d7bb2b9785a Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Thu, 3 May 2012 09:38:50 -0400 Subject: [PATCH 16/53] AST transforms for .length() method call. Fixes #17. --- java2python/config/default.py | 4 +++- java2python/mod/transform.py | 34 ++++++++++++++++++++++++++++++++++ test/Length0.java | 11 +++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/Length0.java diff --git a/java2python/config/default.py b/java2python/config/default.py index 68f28d3..6ad0651 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -159,6 +159,9 @@ (Type('TYPE') > Type('LONG'), transform.typeSub), (Type('TYPE') > Type('DOUBLE'), transform.typeSub), + (Type('METHOD_CALL') > Type('DOT') > Type('IDENT', 'length'), + transform.lengthToLen), + (Type('TYPE') > Type('QUALIFIED_TYPE_IDENT') > Type('IDENT'), transform.typeSub), @@ -193,7 +196,6 @@ (r'(\s)(\S*?)(\.toString\(\))', r'\1\2.__str__()'), (r'(\s)def toString', r'\1def __str__'), (r'(\s)(\S*?)(\.toLowerCase\(\))', r'\1\2.lower()'), - (r'(\s)(\S*?)(\.length\(\))', r'\1len(\2)'), (r'(.*?)IndexOutOfBoundsException\((.*?)\)', r'\1IndexError(\2)'), (r'\.__class__\.getName\(\)', '.__class__.__name__'), (r'\.getClass\(\)', '.__class__'), diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index c01ec6a..04fc3f5 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -14,6 +14,8 @@ import keyword import types +from java2python.lang import tokens + def invalidPythonNames(): """ Creates a list of valid Java identifiers that are invalid in Python. """ @@ -55,6 +57,38 @@ def syntaxSafeFloatLiteral(node, config): node.token.text = value +def lengthToLen(node, config): + """ Transforms expressions like 'value.length()' to 'len(value)'. + + This method changes AST branches like this: + + METHOD_CALL [start=45, stop=49] + DOT . [line=4, start=45, stop=47] + IDENT foo [line=4, start=45] + IDENT length [line=4, start=47] + ARGUMENT_LIST [line=4, start=48, stop=49] + + Into branches like this: + + IDENT len(foo) [line=4, start=45] + + Notice that the resulting IDENT node text is invalid. We can't use a + METHOD_CALL token because those are always bound to a class or instance. + It would be best to add a new token type, and that option will be explored + if we run into this problem again. + + """ + dot = node.parent + method = dot.parent + + ident = dot.firstChildOfType(tokens.IDENT) + ident.token.text = 'len({})'.format(ident.text) + + expr = method.parent + expr.children.remove(method) + expr.addChild(ident) + + def typeSub(node, config): """ Maps specific, well-known Java types to their Python counterparts. diff --git a/test/Length0.java b/test/Length0.java new file mode 100644 index 0000000..e13f429 --- /dev/null +++ b/test/Length0.java @@ -0,0 +1,11 @@ +class Length0 { + public static int dummy(int v) { + return v + 1; + } + + public static void main(String[] args) { + String foo = "asdf"; + System.out.println( dummy(foo.length() ) ); + } + +} \ No newline at end of file From 1b428df3358e78ce145041ad93b91ad5b3377926 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Thu, 3 May 2012 14:11:13 -0400 Subject: [PATCH 17/53] Updates walk() calls to pass memo in all cases. Fixes #13. --- java2python/compiler/visitor.py | 19 +++++++++++-------- test/Comments4.java | 12 ++++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 test/Comments4.java diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 3b55e31..4ae44d7 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -46,7 +46,7 @@ def insertComments(self, tmpl, tree, index, memo): """ Add comments to the template from tokens in the tree. """ prefix = self.config.last('commentPrefix', '# ') cache, parser, comTypes = memo.comments, tree.parser, tokens.commentTypes - comNew = lambda t:t.type in comTypes and t.index not in cache + comNew = lambda t:t.type in comTypes and (t.index not in cache) for tok in ifilter(comNew, parser.input.tokens[memo.last:index]): cache.add(tok.index) @@ -173,7 +173,7 @@ def nodesToAnnos(self, branch, memo): if defKey: deco = self.factory.expr(left=name, fs='@{left}({right})') deco.right = right = self.factory.expr(parent=deco) - right.walk(defKey.firstChild()) + right.walk(defKey.firstChild(), memo) else: deco = self.factory.expr(left=name, fs='@{left}({right})') arg = deco.right = self.factory.expr(parent=deco) @@ -432,17 +432,20 @@ def acceptExpr(self, node, memo): def acceptFor(self, node, memo): """ Accept and process a 'for' statement. """ - self.walk(node.firstChildOfType(tokens.FOR_INIT)) + self.walk(node.firstChildOfType(tokens.FOR_INIT), memo) whileStat = self.factory.statement('while', fs=FS.lsrc, parent=self) cond = node.firstChildOfType(tokens.FOR_CONDITION) if not cond.children: whileStat.expr.right = 'True' else: - whileStat.expr.walk(cond) + whileStat.expr.walk(cond, memo) whileBlock = self.factory.methodContent(parent=self) - whileBlock.walk(node.firstChildOfType(tokens.BLOCK_SCOPE)) + if not node.firstChildOfType(tokens.BLOCK_SCOPE).children: + self.factory.expr(left='pass', parent=whileBlock) + else: + whileBlock.walk(node.firstChildOfType(tokens.BLOCK_SCOPE), memo) updateStat = self.factory.expr(parent=whileBlock) - updateStat.walk(node.firstChildOfType(tokens.FOR_UPDATE)) + updateStat.walk(node.firstChildOfType(tokens.FOR_UPDATE), memo) def acceptForEach(self, node, memo): """ Accept and process a 'for each' style statement. """ @@ -505,7 +508,7 @@ def acceptSwitch(self, node, memo): return # we have at least one node... parExpr = self.factory.expr(parent=self) - parExpr.walk(parNode) + parExpr.walk(parNode, memo) eqFs = FS.l + '==' + FS.r for caseIdx, caseNode in enumerate(caseNodes): isDefault, isFirst = caseNode.type==tokens.DEFAULT, caseIdx==0 @@ -519,7 +522,7 @@ def acceptSwitch(self, node, memo): if not isDefault: right = self.factory.expr(parent=parExpr) - right.walk(caseNode.firstChildOfType(tokens.EXPR)) + right.walk(caseNode.firstChildOfType(tokens.EXPR), memo) caseExpr.expr.right = self.factory.expr(left=parExpr, right=right, fs=eqFs) caseContent = self.factory.methodContent(parent=self) for child in caseNode.children[1:]: diff --git a/test/Comments4.java b/test/Comments4.java new file mode 100644 index 0000000..04a19ec --- /dev/null +++ b/test/Comments4.java @@ -0,0 +1,12 @@ +// Header Comment + +class Comments4 { + void method() { + for (;;) { } + } + + public static void main(String[] args) { + System.out.println("Comments4."); + } + +} From 86fdb2a8f57cd74d6cb72e8f02919c4656469eb8 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Thu, 3 May 2012 15:22:48 -0400 Subject: [PATCH 18/53] Logs each filename before it's processed (at level 10 or INFO). Fixes #14. --- bin/j2py | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/j2py b/bin/j2py index 16e6ac3..a08aadf 100755 --- a/bin/j2py +++ b/bin/j2py @@ -68,6 +68,7 @@ def runOneOrMany(options): for name in [name for name in files if name.endswith('.java')]: fullname = path.join(dirname, name) options.inputfile = fullname + info('opening %s', fullname) if outfile and outfile != '-' and not isinstance(outfile, file): full = path.abspath(path.join(outfile, fullname)) head, tail = path.split(full) From 9590d4870483a8f3f2838f3a1f2f917b69e2a612 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Thu, 3 May 2012 16:07:57 -0400 Subject: [PATCH 19/53] Adds support for 'else if' without 'else'. Fixes #15. --- java2python/compiler/visitor.py | 11 ++++++++--- test/If6.java | 10 ++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 test/If6.java diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 4ae44d7..8cd3177 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -482,14 +482,19 @@ def acceptIf(self, node, memo): else: nextBlock = self.factory.methodContent(parent=self) nextBlock.walk(nextNode.children[1], memo) - nextNode = nextNode.children[2] - nextType = nextNode.type + + try: + nextNode = nextNode.children[2] + except (IndexError, ): + nextType = None + else: + nextType = nextNode.type if nextType == tokens.EXPR: elseStat = self.factory.statement('else', fs=FS.lc, parent=self) elseBlock = self.factory.expr(parent=elseStat) elseBlock.walk(nextNode, memo) - else: # nextType != tokens.BLOCK_SCOPE: + elif nextType: # nextType != tokens.BLOCK_SCOPE: self.factory.statement('else', fs=FS.lc, parent=self) self.factory.methodContent(parent=self).walk(nextNode, memo) diff --git a/test/If6.java b/test/If6.java new file mode 100644 index 0000000..ff16faf --- /dev/null +++ b/test/If6.java @@ -0,0 +1,10 @@ +class If6 { + public static void main(String[] args) { + if (false) { + System.out.println("fail"); + } else if (true) { + System.out.println("okay"); + } + } + +} From fa8a4103996a1e2a354f5633665ba5d148c901f0 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Thu, 3 May 2012 16:15:27 -0400 Subject: [PATCH 20/53] Support for if/else if/else with empty final else. Fixes #16. --- java2python/compiler/visitor.py | 7 +++++-- test/If7.java | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/If7.java diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 8cd3177..4109883 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -495,8 +495,11 @@ def acceptIf(self, node, memo): elseBlock = self.factory.expr(parent=elseStat) elseBlock.walk(nextNode, memo) elif nextType: # nextType != tokens.BLOCK_SCOPE: - self.factory.statement('else', fs=FS.lc, parent=self) - self.factory.methodContent(parent=self).walk(nextNode, memo) + elseStat = self.factory.statement('else', fs=FS.lc, parent=self) + if nextNode.children: + self.factory.methodContent(parent=self).walk(nextNode, memo) + else: + self.factory.expr(left='pass', parent=elseStat) def acceptSwitch(self, node, memo): diff --git a/test/If7.java b/test/If7.java new file mode 100644 index 0000000..4cf1c56 --- /dev/null +++ b/test/If7.java @@ -0,0 +1,10 @@ +class If7 { + public static void main(String[] args) { + if (false) { + System.out.println("fail 1"); + } else if (false) { + System.out.println("fail 2"); + } else { + } + } +} \ No newline at end of file From ca30900ed6bb148ddc0fb1761dbba64eb8606671 Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Thu, 3 May 2012 16:19:33 -0400 Subject: [PATCH 21/53] Bump to version 0.5.1. --- bin/j2py | 2 +- doc/install.md | 5 ++--- setup.py | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bin/j2py b/bin/j2py index a08aadf..6eb1a40 100755 --- a/bin/j2py +++ b/bin/j2py @@ -18,7 +18,7 @@ from java2python.config import Config from java2python.lib import escapes -version = '0.5.0' +version = '0.5.1' def logLevel(value): diff --git a/doc/install.md b/doc/install.md index aa66c79..0a0b8e3 100644 --- a/doc/install.md +++ b/doc/install.md @@ -21,8 +21,8 @@ We need the ANTLR Python runtime before we can install java2python: Now the goodness: - # wget https://github.com/downloads/natural/java2python/java2python-0.5.0.tar.gz - # tar xfz java2python-0.5.0.tar.gz + # wget https://github.com/downloads/natural/java2python/java2python-0.5.1.tar.gz + # tar xfz java2python-0.5.1.tar.gz # cd java2python # python setup.py install @@ -48,4 +48,3 @@ The development dependencies (what you need if you're coding java2python) are [version 3.1.3 of the Python runtime]: http://www.antlr.org/download/antlr-3.1.3.tar.gz [Python runtime]: http://www.antlr.org/wiki/display/ANTLR3/Python+runtime [ANTLR]: http://www.antlr.org - diff --git a/setup.py b/setup.py index 604faa5..fd8cf18 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ def doc_files(): setup( name='java2python', - version='0.5.0', + version='0.5.1', description=description, long_description=long_description, @@ -47,7 +47,7 @@ def doc_files(): author_email='troy@troy.io', url='https://github.com/natural/java2python/', - download_url='https://github.com/downloads/natural/java2python/java2python-0.5.0.tar.gz', + download_url='https://github.com/downloads/natural/java2python/java2python-0.5.1.tar.gz', keywords=['java', 'java2python', 'compiler'], classifiers=filter(None, classifiers.split('\n')), From d5103ae13e286f62de43b06e79e88f2f8624ad6c Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Fri, 4 May 2012 00:35:52 +0300 Subject: [PATCH 22/53] Added output sub rule for String.format --- java2python/config/default.py | 1 + test/Format.java | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/Format.java diff --git a/java2python/config/default.py b/java2python/config/default.py index 6ad0651..ea34f46 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -189,6 +189,7 @@ moduleOutputSubs = [ (r'System\.out\.println\((.*)\)', r'print \1'), (r'System\.out\.print_\((.*?)\)', r'print \1,'), + (r'String\.format\(\"(.*)\" *, *(.*)\)', r'"\1" % (\2)'), (r'(.*?)\.equals\((.*?)\)', r'\1 == \2'), (r'(.*?)\.equalsIgnoreCase\((.*?)\)', r'\1.lower() == \2.lower()'), (r'([\w.]+)\.size\(\)', r'len(\1)'), diff --git a/test/Format.java b/test/Format.java new file mode 100644 index 0000000..5ac5321 --- /dev/null +++ b/test/Format.java @@ -0,0 +1,9 @@ +public class Format { + public static void main(String[] args) { + int i = 22; + String s = "text"; + String r = String.format("> (%d) %s", i, s); + + System.out.println(r); + } +} From a839a715d81d10485d81ef5c470d12616134aaca Mon Sep 17 00:00:00 2001 From: Troy Melhase Date: Thu, 3 May 2012 18:34:12 -0400 Subject: [PATCH 23/53] Renames. --- test/{Format.java => Format0.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{Format.java => Format0.java} (100%) diff --git a/test/Format.java b/test/Format0.java similarity index 100% rename from test/Format.java rename to test/Format0.java From 6a8ded2e52268bd7cf8b1413f9be4f225188add3 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Sat, 5 May 2012 00:28:26 +0300 Subject: [PATCH 24/53] String formatting as AST transform (1st iteration) --- java2python/config/default.py | 7 ++++- java2python/mod/transform.py | 49 +++++++++++++++++++++++++++++++++++ test/Format0.java | 4 +-- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/java2python/config/default.py b/java2python/config/default.py index ea34f46..a2cdb67 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -162,6 +162,12 @@ (Type('METHOD_CALL') > Type('DOT') > Type('IDENT', 'length'), transform.lengthToLen), + (Type('METHOD_CALL') > Type('DOT') > ( + Type('IDENT', 'String') + + Type('IDENT', 'format') + ), + transform.formatString), + (Type('TYPE') > Type('QUALIFIED_TYPE_IDENT') > Type('IDENT'), transform.typeSub), @@ -189,7 +195,6 @@ moduleOutputSubs = [ (r'System\.out\.println\((.*)\)', r'print \1'), (r'System\.out\.print_\((.*?)\)', r'print \1,'), - (r'String\.format\(\"(.*)\" *, *(.*)\)', r'"\1" % (\2)'), (r'(.*?)\.equals\((.*?)\)', r'\1 == \2'), (r'(.*?)\.equalsIgnoreCase\((.*?)\)', r'\1.lower() == \2.lower()'), (r'([\w.]+)\.size\(\)', r'len(\1)'), diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index 04fc3f5..fd9bc64 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -11,6 +11,8 @@ # See the java2python.config.default and java2python.lang.selector modules to # understand how and when selectors are associated with these callables. +import re + import keyword import types @@ -89,6 +91,53 @@ def lengthToLen(node, config): expr.addChild(ident) +def formatSyntaxTransf(match): + """ Helper function for formatString AST transform. + + Translates the Java Formatter syntax into Python .format syntax. + + This function gets called by re.sub which matches all the %...$... groups + inside a format specifier string. + """ + groups = match.groupdict() + result = '{' + # TODO: add flags, width and precision + if(groups['idx']): + idx = int(groups['idx'][:-1]) + result += str(idx - 1) # Py starts count from 0 + result += ':' + groups['convers'] + '}' + + return result + +def formatString(node, config): + """ Transforms string formatting like 'String.format("%d %2$s", i, s)' + into '"{:d} {2:s}".format(i, s)'. + """ + dot = node.parent + method = dot.parent + arg_list = method.firstChildOfType(tokens.ARGUMENT_LIST) + call_args = [arg for arg in arg_list.childrenOfType(tokens.EXPR)] + + format = call_args[0].firstChildOfType(tokens.STRING_LITERAL) + args = [arg.firstChildOfType(tokens.IDENT) for arg in call_args[1:]] + + # Translate format syntax + format.token.text = re.sub(r'%(?P\d+\$)?(?P[scdoxefg])', + formatSyntaxTransf, + format.token.text, + flags=re.IGNORECASE) + + left_ident = dot.children[0] + right_ident = dot.children[1] + + # Change AST + arg_list.children.remove(format.parent) + dot.children.remove(left_ident) + dot.children.remove(right_ident) + dot.addChild(format) + dot.addChild(right_ident) + + def typeSub(node, config): """ Maps specific, well-known Java types to their Python counterparts. diff --git a/test/Format0.java b/test/Format0.java index 5ac5321..995aad2 100644 --- a/test/Format0.java +++ b/test/Format0.java @@ -1,8 +1,8 @@ -public class Format { +public class Format0 { public static void main(String[] args) { int i = 22; String s = "text"; - String r = String.format("> (%d) %s", i, s); + String r = String.format("> (%1$d) %2$s", i, s); System.out.println(r); } From 3be801021a1c24efe4fad14fa052de679d465e39 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Sun, 6 May 2012 10:33:00 +0300 Subject: [PATCH 25/53] More gracefully degrading string formatting syntax translation --- java2python/mod/transform.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index fd9bc64..da4c7d9 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -117,15 +117,25 @@ def formatString(node, config): method = dot.parent arg_list = method.firstChildOfType(tokens.ARGUMENT_LIST) call_args = [arg for arg in arg_list.childrenOfType(tokens.EXPR)] - - format = call_args[0].firstChildOfType(tokens.STRING_LITERAL) args = [arg.firstChildOfType(tokens.IDENT) for arg in call_args[1:]] - # Translate format syntax - format.token.text = re.sub(r'%(?P\d+\$)?(?P[scdoxefg])', + # Translate format syntax (if format == string_literal) + format = call_args[0].firstChildOfType(tokens.STRING_LITERAL) + if format: + format.token.text = \ + re.sub(r'%(?P\d+\$)?(?P[scdoxefg])', formatSyntaxTransf, format.token.text, flags=re.IGNORECASE) + else: + # Translation should happen at runtime + format = call_args[0].firstChild() + if format.type == tokens.IDENT: + # String variable + pass + else: + # Function that returns String + pass left_ident = dot.children[0] right_ident = dot.children[1] From 42c3b47bee6defeba4d63a090a1520df22340d91 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Mon, 7 May 2012 01:35:24 +0300 Subject: [PATCH 26/53] Added warnings for unhandled string formatting translations --- java2python/mod/transform.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index da4c7d9..55b49cd 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -12,6 +12,7 @@ # understand how and when selectors are associated with these callables. import re +from logging import warn import keyword import types @@ -132,10 +133,12 @@ def formatString(node, config): format = call_args[0].firstChild() if format.type == tokens.IDENT: # String variable - pass + warn('Formatting string %s is not automatically translated.' + % str(format.token.text)) else: # Function that returns String - pass + warn('Formatting string returned by %s() is not automatically translated.' + % str(format.firstChildOfType(tokens.IDENT).token.text)) left_ident = dot.children[0] right_ident = dot.children[1] From d178ce8a6b06a2392d31e7a8e58786ce6a204b22 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Sun, 13 May 2012 17:07:33 +0300 Subject: [PATCH 27/53] String formatting (2nd iteration) handle %n from Java --- java2python/mod/transform.py | 6 +++++- test/Format0.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index 55b49cd..3565465 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -101,6 +101,10 @@ def formatSyntaxTransf(match): inside a format specifier string. """ groups = match.groupdict() + if groups['convers'] == 'n': + # Means platform-specific line separator + return '\\n' # Py converts \n to os.linesep + result = '{' # TODO: add flags, width and precision if(groups['idx']): @@ -124,7 +128,7 @@ def formatString(node, config): format = call_args[0].firstChildOfType(tokens.STRING_LITERAL) if format: format.token.text = \ - re.sub(r'%(?P\d+\$)?(?P[scdoxefg])', + re.sub(r'%(?P\d+\$)?(?P[scdoxefgn])', formatSyntaxTransf, format.token.text, flags=re.IGNORECASE) diff --git a/test/Format0.java b/test/Format0.java index 995aad2..68a22a4 100644 --- a/test/Format0.java +++ b/test/Format0.java @@ -2,7 +2,7 @@ public class Format0 { public static void main(String[] args) { int i = 22; String s = "text"; - String r = String.format("> (%1$d) %2$s", i, s); + String r = String.format("> (%1$d) %n %2$s", i, s); System.out.println(r); } From 2c82742327aff3d71de64412e07f041e2ee84acd Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Sun, 13 May 2012 17:48:08 +0300 Subject: [PATCH 28/53] Handle flags, width and precision implemented (string formatting) --- java2python/mod/transform.py | 28 +++++++++++++++++++++++++--- test/Format1.java | 19 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 test/Format1.java diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index 3565465..d8e2652 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -106,11 +106,33 @@ def formatSyntaxTransf(match): return '\\n' # Py converts \n to os.linesep result = '{' - # TODO: add flags, width and precision + thous_sep = '' + if(groups['idx']): idx = int(groups['idx'][:-1]) result += str(idx - 1) # Py starts count from 0 - result += ':' + groups['convers'] + '}' + result += ':' + + if(groups['flags']): + if ',' in groups['flags']: + thous_sep = ',' + if '+' in groups['flags']: + result += '+' + elif ' ' in groups['flags']: + result += ' ' + if '#' in groups['flags']: + result += '#' + if '0' in groups['flags']: + result += '0' + + if(groups['width']): + result += groups['width'] + result += thous_sep + + if(groups['precision']): + result += groups['precision'] + + result += groups['convers'] + '}' return result @@ -128,7 +150,7 @@ def formatString(node, config): format = call_args[0].firstChildOfType(tokens.STRING_LITERAL) if format: format.token.text = \ - re.sub(r'%(?P\d+\$)?(?P[scdoxefgn])', + re.sub(r'%(?P\d+\$)?(?P[-+# 0,]+)?(?P[0-9]+)?(?P\.[0-9]+)?(?P[scdoxefgn])', formatSyntaxTransf, format.token.text, flags=re.IGNORECASE) diff --git a/test/Format1.java b/test/Format1.java new file mode 100644 index 0000000..29e94ee --- /dev/null +++ b/test/Format1.java @@ -0,0 +1,19 @@ +public class Format1 { + public static void main(String[] args) { + long n = 461012; + String f1 = String.format("%d%n", n); // --> "461012" + String f2 = String.format("%08d%n", n); // --> "00461012" + String f3 = String.format("%+8d%n", n); // --> " +461012" + String f4 = String.format("%,8d%n", n); // --> " 461,012" + String f5 = String.format("%+,8d%n", n); // --> "+461,012" + + System.out.println(f1 + f2 + f3 + f4 + f5); + + double pi = 3.14159265; + String pf1 = String.format("%f%n", pi); // --> "3.141593" + String pf2 = String.format("%.3f%n", pi); // --> "3.142" + String pf3 = String.format("%10.3f%n", pi); // --> " 3.142" + + System.out.println(pf1 + pf2 + pf3); + } +} From 2a4e83d642ac8b9de00c049ed6d4848564b1f62a Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Wed, 18 Sep 2013 20:48:34 +0300 Subject: [PATCH 29/53] Update ANTLR remote location As suggested by ekta1007 in #28 --- doc/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install.md b/doc/install.md index 0a0b8e3..fdb7f86 100644 --- a/doc/install.md +++ b/doc/install.md @@ -12,7 +12,7 @@ Kids these days have it easy: We need the ANTLR Python runtime before we can install java2python: - # wget http://www.antlr.org/download/antlr-3.1.3.tar.gz + # wget http://www.antlr3.org/download/antlr-3.1.3.tar.gz # tar xfz antlr-3.1.3.tar.gz # cd antlr-3.1.3/runtime/Python/ # python setup.py install From 2e92c6e0820e9e256c9e211e1db2165d2d37f7d5 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Fri, 10 Oct 2014 06:32:36 -0400 Subject: [PATCH 30/53] only call load_source if path is a file --- java2python/config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java2python/config/__init__.py b/java2python/config/__init__.py index 72d6c5e..2aa8387 100644 --- a/java2python/config/__init__.py +++ b/java2python/config/__init__.py @@ -27,7 +27,7 @@ def last(self, key, default=None): @staticmethod def load(name): """ Imports and returns a module from dotted form or filename. """ - if path.exists(name): + if path.exists(name) and path.isfile(name): mod = load_source(str(hash(name)), name) else: mod = reduce(getattr, name.split('.')[1:], __import__(name)) From 39a032316b96a4e54fe54d1bbdb8186e3c18c2e8 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Fri, 10 Oct 2014 14:59:57 -0400 Subject: [PATCH 31/53] output switch equals cases with spaces around operator --- java2python/compiler/visitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 4109883..127aae4 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -517,7 +517,7 @@ def acceptSwitch(self, node, memo): # we have at least one node... parExpr = self.factory.expr(parent=self) parExpr.walk(parNode, memo) - eqFs = FS.l + '==' + FS.r + eqFs = FS.l + ' == ' + FS.r for caseIdx, caseNode in enumerate(caseNodes): isDefault, isFirst = caseNode.type==tokens.DEFAULT, caseIdx==0 From 8a758716f3cba47b1bc55f7a26211c43ab0200ae Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Fri, 10 Oct 2014 17:37:37 -0400 Subject: [PATCH 32/53] test and fix vars defined inside blocks --- java2python/compiler/visitor.py | 4 ++-- test/If8.java | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 test/If8.java diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 127aae4..5f55f0b 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -358,7 +358,7 @@ class Interface(Class): """ Interface -> accepts AST branches for Java interfaces. """ -class MethodContent(Base): +class MethodContent(VarAcceptor, Base): """ MethodContent -> accepts trees for blocks within methods. """ def acceptAssert(self, node, memo): @@ -613,7 +613,7 @@ def acceptWhile(self, node, memo): whileStat.walk(blkNode, memo) -class Method(VarAcceptor, ModifiersAcceptor, MethodContent): +class Method(ModifiersAcceptor, MethodContent): """ Method -> accepts AST branches for method-level objects. """ def acceptFormalParamStdDecl(self, node, memo): diff --git a/test/If8.java b/test/If8.java new file mode 100644 index 0000000..37e1c46 --- /dev/null +++ b/test/If8.java @@ -0,0 +1,8 @@ +class If8 { + public static void main(String[] args) { + if (true) { + int y = 1; + System.out.println(0 + y); + } + } +} From e1fac1545cf2ad496deba791606954ca76f62548 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Fri, 10 Oct 2014 17:43:26 -0400 Subject: [PATCH 33/53] add gitignore file --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2130e0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.class +*.egg-info +*.pyc From 1d3c3f9a42dbec7fd7ddb4c8154f24df7964de99 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Fri, 10 Oct 2014 19:53:14 -0400 Subject: [PATCH 34/53] add mod.include to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index fd8cf18..210d623 100644 --- a/setup.py +++ b/setup.py @@ -59,6 +59,7 @@ def doc_files(): 'java2python.lang', 'java2python.lib', 'java2python.mod', + 'java2python.mod.include', ], package_data={ From 28f039a15706c341d051b8fe69b6e5923afe1845 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Mon, 13 Oct 2014 19:29:22 -0400 Subject: [PATCH 35/53] add failing test for class variables with no initial value --- test/BasicTypes3.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/BasicTypes3.java diff --git a/test/BasicTypes3.java b/test/BasicTypes3.java new file mode 100644 index 0000000..76821a2 --- /dev/null +++ b/test/BasicTypes3.java @@ -0,0 +1,20 @@ +class Vector {} + +class BasicTypes3 { + Boolean B; + Integer I; + Double D; + + String S; + Vector V; + + public static void main(String[] args) { + BasicTypes3 bt3 = new BasicTypes3(); + System.out.println(bt3.B == null ? 1 : 0); + System.out.println(bt3.I == null ? 1 : 0); + System.out.println(bt3.D == null ? 1 : 0); + System.out.println(bt3.S == null ? 1 : 0); + System.out.println(bt3.V == null ? 1 : 0); + } + +} From 3f2407d16a98e52351fcf301a3acc57169ae2f9e Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Mon, 13 Oct 2014 20:07:34 -0400 Subject: [PATCH 36/53] fix test, class variables with no init value are set to None --- java2python/compiler/visitor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 5f55f0b..c6a66e1 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -228,7 +228,10 @@ def acceptVarDeclaration(self, node, memo): if node.firstChildOfType(tokens.TYPE).firstChildOfType(tokens.ARRAY_DECLARATOR_LIST): val = assgnExp.pushRight('[]') else: - val = assgnExp.pushRight('{0}()'.format(identExp.type)) + if node.firstChildOfType(tokens.TYPE).firstChild().type != tokens.QUALIFIED_TYPE_IDENT: + val = assgnExp.pushRight('{0}()'.format(identExp.type)) + else: + val = assgnExp.pushRight('None') return self From af2b2e44ded258841035c32ed35f5269663679f9 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Mon, 13 Oct 2014 22:34:19 -0400 Subject: [PATCH 37/53] fix synchronized methods to lock per-instance not per-method --- java2python/mod/include/sync.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/java2python/mod/include/sync.py b/java2python/mod/include/sync.py index 8201698..742fbfd 100644 --- a/java2python/mod/include/sync.py +++ b/java2python/mod/include/sync.py @@ -1,12 +1,11 @@ from threading import RLock -_locks = {} -def lock_for_object(obj, locks=_locks): +def lock_for_object(obj, locks={}): return locks.setdefault(id(obj), RLock()) - def synchronized(call): + assert call.__code__.co_varnames[0] in {'self', 'cls'} def inner(*args, **kwds): - with lock_for_object(call): + with lock_for_object(args[0]): return call(*args, **kwds) return inner From 39d019aa2d3468aa45d3ce050ad81caa8d01b3a5 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Mon, 13 Oct 2014 22:56:12 -0400 Subject: [PATCH 38/53] use functools.wraps for synchronized decorator --- java2python/mod/include/sync.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java2python/mod/include/sync.py b/java2python/mod/include/sync.py index 742fbfd..b3e5130 100644 --- a/java2python/mod/include/sync.py +++ b/java2python/mod/include/sync.py @@ -1,3 +1,4 @@ +from functools import wraps from threading import RLock def lock_for_object(obj, locks={}): @@ -5,6 +6,7 @@ def lock_for_object(obj, locks={}): def synchronized(call): assert call.__code__.co_varnames[0] in {'self', 'cls'} + @wraps(call) def inner(*args, **kwds): with lock_for_object(args[0]): return call(*args, **kwds) From 7120676a728db01c2adfc794b79a673b71954679 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Tue, 14 Oct 2014 00:07:17 -0400 Subject: [PATCH 39/53] test/fix overloaded synchronized methods --- java2python/config/default.py | 2 +- java2python/mod/include/classmethod.py | 6 +++++ test/Synchronized2.java | 36 ++++++++++++++++++++++++++ test/configs/defaults.py | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 java2python/mod/include/classmethod.py create mode 100644 test/Synchronized2.java diff --git a/java2python/config/default.py b/java2python/config/default.py index a2cdb67..df2455a 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -97,9 +97,9 @@ methodPrologueHandlers = [ basic.maybeAbstractMethod, basic.maybeClassMethod, + basic.overloadedClassMethods, # NB: synchronized should come after classmethod basic.maybeSynchronizedMethod, - basic.overloadedClassMethods, ] diff --git a/java2python/mod/include/classmethod.py b/java2python/mod/include/classmethod.py new file mode 100644 index 0000000..cb97954 --- /dev/null +++ b/java2python/mod/include/classmethod.py @@ -0,0 +1,6 @@ +class classmethod_(classmethod): + """ Classmethod that provides attribute delegation. + + """ + def __getattr__(self, name): + return getattr(self.__func__, name) diff --git a/test/Synchronized2.java b/test/Synchronized2.java new file mode 100644 index 0000000..3ca9fa5 --- /dev/null +++ b/test/Synchronized2.java @@ -0,0 +1,36 @@ +class Synchronized2 { + + public synchronized void test1() { + System.out.println(1); + } + + public synchronized void test1(String s) { + System.out.println(s); + } + + public static synchronized void test1(int i) { + System.out.println(i); + } + + public static synchronized void test2() { + System.out.println(2); + } + + public static synchronized void test2(String s) { + System.out.println(s); + } + + public synchronized void test2(int i) { + System.out.println(i); + } + + public static void main(String[] args) { + Synchronized2 obj = new Synchronized2(); + obj.test1(); + obj.test1("test1"); + obj.test1(1); + obj.test2(); + obj.test2("test2"); + obj.test2(2); + } +} diff --git a/test/configs/defaults.py b/test/configs/defaults.py index c822d48..0cea861 100644 --- a/test/configs/defaults.py +++ b/test/configs/defaults.py @@ -4,6 +4,7 @@ modulePrologueHandlers = default.modulePrologueHandlers + [ + 'from java2python.mod.include.classmethod import classmethod_ as classmethod', 'from java2python.mod.include.overloading import overloaded', 'from abc import ABCMeta, abstractmethod', 'import zope.interface', From 4353f409e79f73d92119ffe060c4a3487600f806 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Tue, 14 Oct 2014 00:41:39 -0400 Subject: [PATCH 40/53] ensure overloaded methods are registered as synchronized --- java2python/compiler/template.py | 1 + java2python/mod/basic.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/java2python/compiler/template.py b/java2python/compiler/template.py index e117df3..2a189ef 100644 --- a/java2python/compiler/template.py +++ b/java2python/compiler/template.py @@ -119,6 +119,7 @@ def __init__(self, config, name=None, type=None, parent=None): self.children = [] self.config = config self.decorators = [] + self.overloaded = None self.factory = Factory(config) self.modifiers = [] self.name = name diff --git a/java2python/mod/basic.py b/java2python/mod/basic.py index 63c1806..7c2cb27 100644 --- a/java2python/mod/basic.py +++ b/java2python/mod/basic.py @@ -109,11 +109,13 @@ def overloadedClassMethods(method): cls = method.parent methods = [o for o in cls.children if o.isMethod and o.name==method.name] if len(methods) == 1: + if methods[0].overloaded: + yield methods[0].overloaded return for i, m in enumerate(methods[1:]): args = [p['type'] for p in m.parameters] args = ', '.join(args) - m.decorators.append('@{0}.register({1})'.format(method.name, args)) + m.overloaded = '@{0}.register({1})'.format(method.name, args) m.name = '{0}_{1}'.format(method.name, i) # for this one only: yield '@overloaded' From 2d5e91125d3f33bbc6ce7b398c6c2217e83978dc Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Tue, 14 Oct 2014 03:45:29 -0400 Subject: [PATCH 41/53] print differences when testing --- test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Makefile b/test/Makefile index 64ac2d0..e432679 100644 --- a/test/Makefile +++ b/test/Makefile @@ -43,4 +43,4 @@ parsers: @$(j2py) $(addsuffix .java, $(basename $@)) $@ -c configs.defaults -d configs %: %.py - @bash -c "diff -q <($(python) $(addsuffix .py, $@)) <(java -ea $@)" && echo "[PASS] $@" + @bash -c "diff -u <($(python) $(addsuffix .py, $@)) <(java -ea $@)" && echo "[PASS] $@" From c994f83abdfb041f5c747b1422e9053a9c4fef9e Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Tue, 14 Oct 2014 03:49:25 -0400 Subject: [PATCH 42/53] some changes for py3 compat --- java2python/config/default.py | 5 +++-- java2python/mod/include/overloading.py | 12 +++++++----- test/Self0.java | 3 +++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/java2python/config/default.py b/java2python/config/default.py index df2455a..9b10ccc 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -24,6 +24,7 @@ modulePrologueHandlers = [ basic.shebangLine, basic.simpleDocString, + 'from __future__ import print_function', basic.maybeBsr, basic.maybeSyncHelpers, ] @@ -193,8 +194,8 @@ # module output subs. moduleOutputSubs = [ - (r'System\.out\.println\((.*)\)', r'print \1'), - (r'System\.out\.print_\((.*?)\)', r'print \1,'), + (r'System\.out\.println\((.*)\)', r'print(\1)'), + (r'System\.out\.print_\((.*?)\)', r'print(\1, end="")'), (r'(.*?)\.equals\((.*?)\)', r'\1 == \2'), (r'(.*?)\.equalsIgnoreCase\((.*?)\)', r'\1.lower() == \2.lower()'), (r'([\w.]+)\.size\(\)', r'len(\1)'), diff --git a/java2python/mod/include/overloading.py b/java2python/mod/include/overloading.py index d7baafd..2f8257c 100644 --- a/java2python/mod/include/overloading.py +++ b/java2python/mod/include/overloading.py @@ -36,11 +36,13 @@ """ -import new +from types import MethodType as instancemethod -# Make the environment more like Python 3.0 -__metaclass__ = type -from itertools import izip as zip +import sys +if sys.version_info[0] < 3: + # Make the environment more like Python 3.0 + __metaclass__ = type + from itertools import izip as zip class overloaded: @@ -55,7 +57,7 @@ def __init__(self, default_func): def __get__(self, obj, type=None): if obj is None: return self - return new.instancemethod(self, obj) + return instancemethod(self, obj) def register(self, *types): """Decorator to register an implementation for a specific set of types. diff --git a/test/Self0.java b/test/Self0.java index 269a96e..c7556ba 100644 --- a/test/Self0.java +++ b/test/Self0.java @@ -17,6 +17,9 @@ public static void main(String[] args) { System.out.println("True"); else System.out.println("False"); + System.out.print("test"); + System.out.print("ing"); + System.out.println(); } } From 150e5cee1f35e053846007b279c82aa1100dab10 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 00:30:31 -0400 Subject: [PATCH 43/53] use config.every rather than config.last for configHandlers --- java2python/compiler/template.py | 2 +- test/configs/Class10.py | 4 +--- test/configs/Interface1.py | 4 +--- test/configs/defaults.py | 5 +---- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/java2python/compiler/template.py b/java2python/compiler/template.py index 2a189ef..4f4dfe1 100644 --- a/java2python/compiler/template.py +++ b/java2python/compiler/template.py @@ -173,7 +173,7 @@ def configHandler(self, part, suffix='Handler', default=None): def configHandlers(self, part, suffix='Handlers'): """ Returns config handlers for this type of template """ name = '{0}{1}{2}'.format(self.typeName, part, suffix) - return imap(self.toIter, self.config.last(name, ())) + return imap(self.toIter, chain(*self.config.every(name, []))) def dump(self, fd, level=0): """ Writes the Python source code for this template to the given file. """ diff --git a/test/configs/Class10.py b/test/configs/Class10.py index 71de720..22ac1a5 100644 --- a/test/configs/Class10.py +++ b/test/configs/Class10.py @@ -1,6 +1,4 @@ -from java2python.config.default import modulePrologueHandlers - -modulePrologueHandlers += [ +modulePrologueHandlers = [ 'from java2python.mod.include.overloading import overloaded', 'from abc import ABCMeta, abstractmethod', ] diff --git a/test/configs/Interface1.py b/test/configs/Interface1.py index 79bd196..bd28c79 100644 --- a/test/configs/Interface1.py +++ b/test/configs/Interface1.py @@ -1,5 +1,3 @@ -from java2python.config.default import modulePrologueHandlers - -modulePrologueHandlers += [ +modulePrologueHandlers = [ "from abc import ABCMeta, abstractmethod", ] diff --git a/test/configs/defaults.py b/test/configs/defaults.py index 0cea861..3ab207e 100644 --- a/test/configs/defaults.py +++ b/test/configs/defaults.py @@ -1,9 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from java2python.config import default - - -modulePrologueHandlers = default.modulePrologueHandlers + [ +modulePrologueHandlers = [ 'from java2python.mod.include.classmethod import classmethod_ as classmethod', 'from java2python.mod.include.overloading import overloaded', 'from abc import ABCMeta, abstractmethod', From 0b669980de5f406d3f010287ef1ec39d8e581821 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 00:34:23 -0400 Subject: [PATCH 44/53] use config.every rather than config.last for typeSubs --- java2python/mod/transform.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index d8e2652..c420012 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -184,6 +184,7 @@ def typeSub(node, config): mapping and further discussion. """ ident = node.token.text - subs = config.last('typeSubs') - if ident in subs: - node.token.text = subs[ident] + for subs in reversed(config.every('typeSubs', {})): + if ident in subs: + node.token.text = subs[ident] + return From daced34e702971ba542695d5747f5750559e7375 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 01:10:04 -0400 Subject: [PATCH 45/53] add abc imports when defining interfaces --- java2python/compiler/visitor.py | 7 ++++++- java2python/config/default.py | 1 + java2python/mod/basic.py | 7 +++++-- test/configs/Class10.py | 1 - test/configs/Interface1.py | 3 --- test/configs/defaults.py | 1 - 6 files changed, 12 insertions(+), 8 deletions(-) delete mode 100644 test/configs/Interface1.py diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index c6a66e1..8b05b32 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -129,7 +129,12 @@ def acceptType(self, node, memo): acceptAt = makeAcceptType('at') acceptClass = makeAcceptType('klass') acceptEnum = makeAcceptType('enum') - acceptInterface = makeAcceptType('interface') + _acceptInterface = makeAcceptType('interface') + + def acceptInterface(self, node, memo): + module = self.parents(lambda x:x.isModule).next() + module.needsAbstractHelpers = True + return self._acceptInterface(node, memo) class Module(TypeAcceptor, Base): diff --git a/java2python/config/default.py b/java2python/config/default.py index 9b10ccc..2c2a0ef 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -26,6 +26,7 @@ basic.simpleDocString, 'from __future__ import print_function', basic.maybeBsr, + basic.maybeAbstractHelpers, basic.maybeSyncHelpers, ] diff --git a/java2python/mod/basic.py b/java2python/mod/basic.py index 7c2cb27..ae7b58c 100644 --- a/java2python/mod/basic.py +++ b/java2python/mod/basic.py @@ -133,8 +133,6 @@ def maybeAbstractMethod(method): def maybeSynchronizedMethod(method): if 'synchronized' in method.modifiers: - module = method.parents(lambda x:x.isModule).next() - module.needsSyncHelpers = True yield '@synchronized' @@ -160,6 +158,11 @@ def maybeBsr(module): yield line +def maybeAbstractHelpers(module): + if getattr(module, 'needsAbstractHelpers', False): + yield 'from abc import ABCMeta, abstractmethod' + + def maybeSyncHelpers(module): if getattr(module, 'needsSyncHelpers', False): for line in getSyncHelpersSrc().split('\n'): diff --git a/test/configs/Class10.py b/test/configs/Class10.py index 22ac1a5..0b340ee 100644 --- a/test/configs/Class10.py +++ b/test/configs/Class10.py @@ -1,4 +1,3 @@ modulePrologueHandlers = [ 'from java2python.mod.include.overloading import overloaded', - 'from abc import ABCMeta, abstractmethod', ] diff --git a/test/configs/Interface1.py b/test/configs/Interface1.py deleted file mode 100644 index bd28c79..0000000 --- a/test/configs/Interface1.py +++ /dev/null @@ -1,3 +0,0 @@ -modulePrologueHandlers = [ - "from abc import ABCMeta, abstractmethod", - ] diff --git a/test/configs/defaults.py b/test/configs/defaults.py index 3ab207e..7a5d639 100644 --- a/test/configs/defaults.py +++ b/test/configs/defaults.py @@ -3,7 +3,6 @@ modulePrologueHandlers = [ 'from java2python.mod.include.classmethod import classmethod_ as classmethod', 'from java2python.mod.include.overloading import overloaded', - 'from abc import ABCMeta, abstractmethod', 'import zope.interface', ] From 04dd59cede80c667f22c5bdb4988acd24da5cf22 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 01:17:12 -0400 Subject: [PATCH 46/53] handle String.valueOf() --- java2python/config/default.py | 2 +- test/String0.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 test/String0.java diff --git a/java2python/config/default.py b/java2python/config/default.py index 2c2a0ef..c6661ac 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -209,7 +209,7 @@ (r'\.getClass\(\)', '.__class__'), (r'\.getName\(\)', '.__name__'), (r'\.getInterfaces\(\)', '.__bases__'), - #(r'String\.valueOf\((.*?)\)', r'str(\1)'), + (r'String\.valueOf\((.*?)\)', r'str(\1)'), #(r'(\s)(\S*?)(\.toString\(\))', r'\1str(\2)'), ] diff --git a/test/String0.java b/test/String0.java new file mode 100644 index 0000000..e4ed565 --- /dev/null +++ b/test/String0.java @@ -0,0 +1,9 @@ +class String0 { + static void test(String s) { + System.out.println(s); + } + + public static void main(String[] args) { + test(String.valueOf(42)); + } +} From 96897b99fff9d303e903404ccb34cba0576b4b98 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 01:26:04 -0400 Subject: [PATCH 47/53] convert IOException to IOError, test --- java2python/config/default.py | 4 +++- java2python/mod/basic.py | 2 +- test/Exception0.java | 17 +++++++++++++++++ test/configs/UsePackage0.py | 3 +++ 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 test/Exception0.java create mode 100644 test/configs/UsePackage0.py diff --git a/java2python/config/default.py b/java2python/config/default.py index c6661ac..159ebc9 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -133,7 +133,7 @@ # This handler is turns java imports into python imports. No mapping # of packages is performed: -moduleImportDeclarationHandler = basic.simpleImports +# moduleImportDeclarationHandler = basic.simpleImports # This import decl. handler can be used instead to produce comments # instead of import statements: @@ -243,5 +243,7 @@ 'java.lang.String' : 'str', 'Object' : 'object', + 'IndexOutOfBoundsException' : 'IndexError', + 'IOException': 'IOError', } diff --git a/java2python/mod/basic.py b/java2python/mod/basic.py index ae7b58c..02e2f57 100644 --- a/java2python/mod/basic.py +++ b/java2python/mod/basic.py @@ -36,7 +36,7 @@ def simpleDocString(obj): def commentedImports(module, expr): - module.factory.comment(parent=module, left=expr, fs='import: {left}') + module.factory.comment(parent=module, left=expr, fs='#import {left}') def simpleImports(module, expr): diff --git a/test/Exception0.java b/test/Exception0.java new file mode 100644 index 0000000..f784749 --- /dev/null +++ b/test/Exception0.java @@ -0,0 +1,17 @@ +import java.io.IOException; + +class Exception0 { + static void test() throws IOException { + throw new IOException("test"); + } + + public static void main(String[] args) { + try { + test(); + } catch (IOException e) { + System.out.println("catch"); + } finally { + System.out.println("done"); + } + } +} diff --git a/test/configs/UsePackage0.py b/test/configs/UsePackage0.py new file mode 100644 index 0000000..243c245 --- /dev/null +++ b/test/configs/UsePackage0.py @@ -0,0 +1,3 @@ +from java2python.mod import basic + +moduleImportDeclarationHandler = basic.simpleImports From 34630360cd395ce43a6241bbcfca5498516c202c Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 01:40:25 -0400 Subject: [PATCH 48/53] remove Math. prefix, test --- java2python/config/default.py | 1 + test/Math0.java | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 test/Math0.java diff --git a/java2python/config/default.py b/java2python/config/default.py index 159ebc9..316083a 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -211,6 +211,7 @@ (r'\.getInterfaces\(\)', '.__bases__'), (r'String\.valueOf\((.*?)\)', r'str(\1)'), #(r'(\s)(\S*?)(\.toString\(\))', r'\1str(\2)'), + (r'Math\.', ''), ] diff --git a/test/Math0.java b/test/Math0.java new file mode 100644 index 0000000..67ae731 --- /dev/null +++ b/test/Math0.java @@ -0,0 +1,6 @@ +class Math0 { + public static void main(String[] args) { + System.out.println(Math.abs(-42)); + System.out.println(Math.abs(-0.5)); + } +} From 0c6ee8e1a9dafd6ed39d4119b8be0543e6d37dca Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 04:16:02 -0400 Subject: [PATCH 49/53] fix continue in for loops --- java2python/compiler/visitor.py | 4 ++++ test/Continue0.java | 10 +++++----- test/Continue1.java | 13 +++++++++++++ test/Continue2.java | 14 ++++++++++++++ test/ForLoop2.java | 9 +++++++++ 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 test/Continue1.java create mode 100644 test/Continue2.java create mode 100644 test/ForLoop2.java diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 8b05b32..89f1dc7 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -407,6 +407,10 @@ def acceptCatch(self, node, memo): def acceptContinue(self, node, memo): """ Accept and process a continue statement. """ + parent = node.parents(lambda x: x.type in {tokens.FOR, tokens.FOR_EACH, tokens.DO, tokens.WHILE}).next() + if parent.type == tokens.FOR: + updateStat = self.factory.expr(parent=self) + updateStat.walk(parent.firstChildOfType(tokens.FOR_UPDATE), memo) contStat = self.factory.statement('continue', fs=FS.lsr, parent=self) if len(node.children): warn('Detected unhandled continue statement with label; generated code incorrect.') diff --git a/test/Continue0.java b/test/Continue0.java index 727eaef..d7bc322 100644 --- a/test/Continue0.java +++ b/test/Continue0.java @@ -3,12 +3,12 @@ public static void main(String[] args) { int x = 0; while (x < 10) { System.out.println(x); - if (x==6) { - break ; + if (x == 6) { + break; } else { - x+=2; - continue ; + x += 2; + continue; } - } + } } } diff --git a/test/Continue1.java b/test/Continue1.java new file mode 100644 index 0000000..a3e1443 --- /dev/null +++ b/test/Continue1.java @@ -0,0 +1,13 @@ +class Continue1 { + public static void main(String[] args) { + int[] ints = {1, 2, 3, 4, 5, 6, 7}; + for (int x : ints) { + if (x == 6) { + break; + } else if (x == 3) { + continue; + } + System.out.println(x); + } + } +} diff --git a/test/Continue2.java b/test/Continue2.java new file mode 100644 index 0000000..506e10f --- /dev/null +++ b/test/Continue2.java @@ -0,0 +1,14 @@ +class Continue2 { + public static void main(String[] args) { + int x = 0; + do { + System.out.println(x); + if (x == 6) { + break; + } else { + x += 2; + continue; + } + } while (x < 10); + } +} diff --git a/test/ForLoop2.java b/test/ForLoop2.java new file mode 100644 index 0000000..6c72ee2 --- /dev/null +++ b/test/ForLoop2.java @@ -0,0 +1,9 @@ +class ForLoop2 { + public static void main(String[] args) { + for (int i = 0; i < 3; i++) { + if (i == 1) + continue; + System.out.println(i); + } + } +} From 153da8b82e072ecc870e17667ff1a7c5454ab4a3 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Wed, 15 Oct 2014 07:15:39 -0400 Subject: [PATCH 50/53] put space in static array creator --- java2python/compiler/visitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java2python/compiler/visitor.py b/java2python/compiler/visitor.py index 89f1dc7..f62e53e 100644 --- a/java2python/compiler/visitor.py +++ b/java2python/compiler/visitor.py @@ -847,7 +847,7 @@ def acceptThisConstructorCall(self, node, memo): def acceptStaticArrayCreator(self, node, memo): """ Accept and process a static array expression. """ - self.right = self.factory.expr(fs='[None]*{left}') + self.right = self.factory.expr(fs='[None] * {left}') self.right.left = self.factory.expr() self.right.left.walk(node.firstChildOfType(tokens.EXPR), memo) From a7a3d5436e4a59db8dbb77402e6017e803f9b422 Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Thu, 16 Oct 2014 03:18:41 -0400 Subject: [PATCH 51/53] don't put L suffix on literals --- java2python/config/default.py | 1 + java2python/mod/transform.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/java2python/config/default.py b/java2python/config/default.py index 316083a..92c4a27 100644 --- a/java2python/config/default.py +++ b/java2python/config/default.py @@ -150,6 +150,7 @@ (Type('TRUE'), transform.true2True), (Type('IDENT'), transform.keywordSafeIdent), + (Type('DECIMAL_LITERAL'), transform.syntaxSafeDecimalLiteral), (Type('FLOATING_POINT_LITERAL'), transform.syntaxSafeFloatLiteral), (Type('TYPE') > Type('BOOLEAN'), transform.typeSub), diff --git a/java2python/mod/transform.py b/java2python/mod/transform.py index c420012..9b2e567 100644 --- a/java2python/mod/transform.py +++ b/java2python/mod/transform.py @@ -48,6 +48,14 @@ def xform(node, config): true2True = makeConst('True') +def syntaxSafeDecimalLiteral(node, config): + """ Ensures a Java decimal literal is a valid Python decimal literal. """ + value = node.token.text + if value.endswith(('l', 'L')): + value = value[:-1] + node.token.text = value + + def syntaxSafeFloatLiteral(node, config): """ Ensures a Java float literal is a valid Python float literal. """ value = node.token.text @@ -55,8 +63,6 @@ def syntaxSafeFloatLiteral(node, config): value = '0' + value if value.lower().endswith(('f', 'd')): value = value[:-1] - elif value.endswith(('l', 'L')): - value = value[:-1] + 'L' node.token.text = value From 408deeca0db7b9b6bd44a5e3edf3674ca04014eb Mon Sep 17 00:00:00 2001 From: Brian Kearns Date: Thu, 23 Oct 2014 03:18:52 -0400 Subject: [PATCH 52/53] change sync helper to be py26 friendly --- java2python/mod/include/sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java2python/mod/include/sync.py b/java2python/mod/include/sync.py index b3e5130..ddac055 100644 --- a/java2python/mod/include/sync.py +++ b/java2python/mod/include/sync.py @@ -5,7 +5,7 @@ def lock_for_object(obj, locks={}): return locks.setdefault(id(obj), RLock()) def synchronized(call): - assert call.__code__.co_varnames[0] in {'self', 'cls'} + assert call.__code__.co_varnames[0] in ['self', 'cls'] @wraps(call) def inner(*args, **kwds): with lock_for_object(args[0]): From 6f563f74ad8aeb5dca4348aba4b0abe156538fb4 Mon Sep 17 00:00:00 2001 From: Shyam Saladi Date: Tue, 10 May 2016 00:47:31 -0700 Subject: [PATCH 53/53] Erroneous link --- doc/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install.md b/doc/install.md index fdb7f86..bd2cf6e 100644 --- a/doc/install.md +++ b/doc/install.md @@ -45,6 +45,6 @@ The development dependencies (what you need if you're coding java2python) are [ANTLR][], also version 3.1.3, GNU make, and a JVM. -[version 3.1.3 of the Python runtime]: http://www.antlr.org/download/antlr-3.1.3.tar.gz +[version 3.1.3 of the Python runtime]: http://www.antlr3.org/download/antlr-3.1.3.tar.gz [Python runtime]: http://www.antlr.org/wiki/display/ANTLR3/Python+runtime [ANTLR]: http://www.antlr.org