templates.java_pretty.py Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython-standalone Show documentation
Show all versions of jython-standalone Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
# copyright 2004-2005 Samuele Pedroni
import sys
from java_lexer import Token
import java_parser
import java_nodes
from simpleprettyspec import simplepretty,p_space,p_nl,p_repeat,p_dependent
from simpleprettyspec import p_indent,p_dedent,p_morep,P_LAST,P_EFFECT
from simpleprettyspec import p_expect_skip
class IndentPrinter:
clashes = ()
def __init__(self,output):
self.out = output
self.col = 0
self.last = '\n'
self.soft_nl = 0
self.indent_level = [0]
self.indent_step = 4
def write(self,text):
if self.last == '\n':
self.out.write(' '*self.col)
self.out.write(text)
self.col += len(text)
self.last = text
def linehome(self):
return self.last == '\n'
def nl(self,soft=0):
if self.last != '\n' or not self.soft_nl:
self.out.write('\n')
self.soft_nl = soft
self.col = self.indent_level[-1]
self.last = '\n'
def emit(self,text):
if not text: return
if text.isspace():
if self.last in " \n":
return
self.write(" ")
else:
tail = self.last[-1]
head = text[0]
if (tail+head in self.clashes or
(tail.isalnum() or tail in "$_") and
(head.isalnum() or head in "$_`")): # !!! factor out `
self.write(" ")
self.write(text)
def here(self,delta=0):
self.indent_level.append(self.col+delta)
def indent(self):
assert self.last == '\n'
self.col = self.indent_level[-1] + self.indent_step
self.indent_level.append(self.col)
def dedent(self):
self.indent_level.pop()
self.col = self.indent_level[-1]
class substs:
def __init__(self, dic):
self.dic = dic
def __getitem__(self, key):
try:
return self.dic[key]
except KeyError:
return "<%s>" % key
class JavaPrinter(IndentPrinter):
clashes = ("//","/*","++","--")
def __init__(self,output):
IndentPrinter.__init__(self,output)
def space(self):
self.emit(" ")
def vertical_space(self,delta,soft=0):
if delta > 1:
self.nl()
self.nl(soft)
def emit_comment(self,comment, subst): # doesn't write final \n!
col = comment.col
comment = comment.value
if subst is not None:
comment = comment % substs(subst)
lines = comment.split('\n')
first = lines[0]
self.write(first)
lines = lines[1:]
tostrip = col
prefix = ' '*col
for line in lines:
if not line.startswith(prefix):
tostrip = 0
for line in lines:
self.nl()
self.write(line[tostrip:])
def emit_horizontal_comments(self,comments, subst):
# space + space separated comments...
for comment in comments:
self.space()
self.emit_comment(comment, subst)
def emit_vertical_comments(self,comments, subst):
end = comments[0].end
self.emit_comment(comments[0], subst)
for comment in comments[1:]:
self.vertical_space(comment.start - end)
end = comment.end
self.emit_comment(comment, subst)
def emit_infront_comments(self,tok, subst):
if not self.linehome():
self.nl()
comments = tok.infront_comments
self.emit_vertical_comments(comments, subst)
self.vertical_space(tok.lineno - comments[-1].end)
def emit_attached_comments(self,tok, subst):
comments = tok.attached_comments
start = comments[0].start
horizontal = 1 # all on one line
for comment in comments:
if comment.end != start:
horizontal = 0
break
if horizontal:
self.emit_horizontal_comments(comments, subst)
else:
self.space()
self.here()
self.emit_vertical_comments(comments, subst)
self.dedent()
if comments[-1].value.startswith("//"): # // needs newline
delta = 1
else:
delta = 0
delta = max(delta,tok.attached_line_delta)
if delta == 0:
self.space()
else:
self.vertical_space(delta,soft=1)
def emit_tok(self,tok,ctl='ika', subst=None):
# ctl: i=>infront k=>token a=>attached
if 'i' in ctl and tok.infront_comments:
self.emit_infront_comments(tok, subst)
if 'k' in ctl:
self.emit(tok.value)
if 'a' in ctl and tok.attached_comments:
self.emit_attached_comments(tok, subst)
# ~~~
def hierarchy(cl):
hier = [cl.__name__]
while cl.__bases__:
assert len(cl.__bases__) == 1
cl = cl.__bases__[0]
hier.append(cl.__name__)
return hier
class NodeVisitTracker: # !!! polish
def __init__(self,visitor,node,left=None,right=None):
self.node = node
self.visitor = visitor
self.children = node.children
self.index = 0
self.stop = len(self.children)
self.left,self.right = left,right
def parent(self):
return self.node
def prev(self):
index = self.index
if index == 0:
return self.left
else:
return self.children[index-1]
def cur(self):
return self.children[self.index]
def next(self):
index = self.index
if index == self.stop-1:
return self.right
else:
return self.children[index+1]
def flag(self,name):
return self.flags.get(name,0)
def expect_skip(self):
self.index = -1
def skip(self):
# !!! but comments?
self.index += 1
def visit_cur_forward(self,**flags):
self.flags = flags
r = self.visitor.visit(self.children[self.index],self)
self.index += 1
return r
def exhausted(self):
return self.stop == self.index
def go(self):
r = 0
stop = self.stop
visit = self.visitor.visit
children = self.children
index = self.index
while index != stop:
self.flags = {}
r |= visit(children[index], self)
index = self.index = self.index + 1
return r
class JavaPretty:
def __init__(self,output=sys.stdout):
self.printer = JavaPrinter(output)
# shortcuts
self.nl = self.printer.nl
self.indent = self.printer.indent
self.dedent = self.printer.dedent
self.space = self.printer.space
self._separators = []
self._disp_cache = {}
def visit(self,node, ctxt=None):
cl = node.__class__
try:
before, do_visit, after = self._disp_cache[cl.__name__]
except KeyError:
hier = hierarchy(cl)
for name in hier:
do_visit = getattr(self,'visit_%s' % name,None)
if do_visit is not None:
break
else:
do_visit = self.default_visit
after = []
for name in hier:
after_meth = getattr(self,'after_%s' % name,None)
if after_meth is not None:
after.append(after_meth)
hier.reverse()
before = []
for name in hier:
before_meth = getattr(self,'before_%s' % name,None)
if before_meth is not None:
before.append(before_meth)
self._disp_cache[cl.__name__] = before, do_visit, after
for meth in before:
meth(node,ctxt)
r = do_visit(node,ctxt)
for meth in after:
meth(node,ctxt)
if r is None:
return 1
return r
def push_separator(self,tok):
self._separators.append(tok)
def pop_separator(self):
tok = self._separators.pop()
if tok is not None:
self.printer.emit_tok(tok,'ia')
def emit_tok(self,tok,ctl='ika', subst=None):
if 'k' in ctl:
seps = self._separators
for i in range(len(seps)-1,-1,-1):
if seps[i] is not None:
self.printer.emit_tok(seps[i])
seps[i] = None
else:
break
self.printer.emit_tok(tok,ctl, subst)
def default_visit(self,node,ctxt):
if node is None: return
if isinstance(node,Token):
self.emit_tok(node)
return
return NodeVisitTracker(self,node).go()
# specialized cases
# !!! Assigment: space = space
def visit_Expressions(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
if tracker.exhausted(): return 0
prev = 0
last = tracker.visit_cur_forward()
while not tracker.exhausted():
tok = tracker.cur()
tracker.skip()
prev |= last
if prev:
self.push_separator(tok)
else:
self.emit_tok(tok,'ia')
last = tracker.visit_cur_forward()
if prev:
self.pop_separator()
return last|prev
visit_FormalParameterList = visit_Expressions
def visit_ClassCreator(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
tracker.visit_cur_forward()
tracker.visit_cur_forward()
if node.ClassBodyOpt:
self.printer.here(+1)
tracker.visit_cur_forward()
self.dedent()
def visit_ArrayCreator(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
tracker.visit_cur_forward()
tracker.visit_cur_forward()
if node.has('ArrayInitializer'):
self.space()
tracker.visit_cur_forward()
def before_Block(self,node,ctxt):
if ctxt and isinstance(ctxt.parent(),java_parser.TakesBlock):
self.space()
def after_Block(self,node,ctxt):
if (ctxt and isinstance(ctxt.parent(),java_parser.TakesBlock)
and ctxt.flag('chain')):
self.space()
visit_Block = simplepretty[
'{',p_nl,p_indent,
'BlockStatements',
p_dependent[p_nl],
p_dedent,'}']
visit_BlockStatements = simplepretty[P_LAST,
p_repeat[p_dependent[p_nl],
'BlockStatement',]]
def before_Statement(self,node,ctxt):
if isinstance(ctxt.parent(),java_parser.TakesBlock):
self.nl()
self.indent()
def after_Statement(self,node,ctxt):
if isinstance(ctxt.parent(),java_parser.TakesBlock):
self.dedent()
if ctxt.flag('chain'):
self.nl()
def visit_IfStatement(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
tracker.visit_cur_forward()
def visit_IfElseStatement(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
tracker.visit_cur_forward(chain=1)
tracker.visit_cur_forward()
tracker.visit_cur_forward()
def visit_SwitchStatement(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
self.nl()
tracker.visit_cur_forward()
tracker.visit_cur_forward()
visit_SwitchBlockStatementGroup = simplepretty[
'SwitchLabel',
p_nl,p_indent,
'BlockStatements',
p_dependent[p_nl],
p_dedent]
def visit_SwitchLabel(self,node,ctxt):
if node.has('CASE'):
tracker = NodeVisitTracker(self,node)
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
tracker.visit_cur_forward()
else:
self.default_visit(node,ctxt)
def visit_TryStatement(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
tracker.visit_cur_forward()
self.space()
while not tracker.exhausted():
tracker.visit_cur_forward()
def visit_CatchClause(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
self.space()
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
tracker.visit_cur_forward()
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
visit_FinallyClause = simplepretty[p_space,"finally",p_space,"Block"]
visit_ClassBody = simplepretty[p_space,'{',
p_nl,p_indent,p_nl,
'ClassBodyDeclarations',
p_dependent[p_nl,p_nl],
p_dedent,'}']
visit_InterfaceBody = simplepretty[p_space,'{',
p_nl,p_indent,p_nl,
'InterfaceBodyDeclarations',
p_dependent[p_nl,p_nl],
p_dedent,'}']
visit_ClassBodyDeclarations = simplepretty[P_LAST,
p_repeat[p_dependent[p_nl,p_nl],'ClassBodyDeclaration',
]]
visit_InterfaceBodyDeclarations = simplepretty[P_LAST,
p_repeat[p_dependent[p_nl,p_nl],'InterfaceBodyDeclaration',
]]
def visit_InitBody(self,node,ctxt):
tracker = NodeVisitTracker(self,node)
if node.has('STATIC'):
tracker.visit_cur_forward()
self.space()
tracker.visit_cur_forward()
visit_CompilationUnit = simplepretty[
'PackageClauseOpt',
p_dependent[p_nl,p_nl],
'ImportDeclarations',
p_dependent[p_nl,p_nl],
'TypeDeclarations',
]
visit_ImportDeclarations = simplepretty[P_LAST,
p_repeat[p_dependent[p_nl],'ImportDeclaration',
]]
visit_TypeDeclarations = simplepretty[P_LAST,
p_repeat[p_dependent[p_nl,p_nl],'TypeDeclaration',
]]
def pretty(source,start='Statement',output=None):
if isinstance(source,java_parser.Node):
ast = source
else:
ast = java_parser.parse(source,start)
if output is None:
JavaPretty().visit(ast)
else:
JavaPretty(output).visit(ast)
# Statement
TEST_STUFF = """
{
x = xeon(1,2,3);
`a `([`x],[`y,`z]);
}
"""
TEST_CREATOR = """
{
x = xeon(new Beh()) +3 ;
x = xeon(new Beh() {
public void evoke() {} }) + 2;
x = new Object[][] { {a,b},
{c,d} };
}
"""
TEST_IF = """
{ if (cond) A(); if(cond) {} if (cond) { A(); }
if (cond) { A(); { B(); } }
if (cond) A(); else B(); if(cond) {} else {} if (cond) { A(); } else B();
if (cond) { A(); { B(); } } else B(); } """
TEST_SWITCH = """
{ if(cond) switch(x) {}
switch(x) { case 0: A(); }
switch(x) { default: A(); }
switch(x) { case +1: default: { A(); } }
switch(x) { case 0: A(); case 1: default: { A(); } }
switch(x) { case 0: A(); case 1: case 2: { } }
switch(x) { default: { A(); } }
} """
TEST_TRY = """
{
try { a(); } finally {}
try { a(); } catch(E e) {}
try { a(); } catch(E e) {} finally {}
try { a(); } catch(E e) {} catch(F e) {} finally {}
}
"""
# other
TEST_COMPUNIT0 = 'CompilationUnit',"""
package a;
import b.B;
import c.*;
"""
TEST_COMPUNIT = 'CompilationUnit',"""
package a;
import b.B;
import c.*;
public class Y {
}
class X {
}
"""
TEST_COMPUNIT1 = 'CompilationUnit',"""
package a;
public class Y {
}
"""
TEST_METH = 'MethodDecl',"""
public void method(int x,int y) {
} """
TEST_CLASS = 'ClassDeclaration',"""
public class B extends C {
public int a,b;
public abstract method x();
public method y(int[] a) {
}
public class Z { }
{
while(false) {}
x();
}
static {
X();
}
}"""
def test():
for name,test in globals().items():
if name.startswith('TEST_'):
print name
if isinstance(test,tuple):
start, source = test
pretty(source,start)
else:
pretty(test)