From 6a8ded2e52268bd7cf8b1413f9be4f225188add3 Mon Sep 17 00:00:00 2001 From: Iulius Curt Date: Sat, 5 May 2012 00:28:26 +0300 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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]