Skip to content

Commit 787b21c

Browse files
author
Troy Melhase
committed
Getting there...
1 parent 0f42d7f commit 787b21c

File tree

13 files changed

+308
-98
lines changed

13 files changed

+308
-98
lines changed

bin/j2py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def main(options):
137137
try:
138138
compile(source, '<string>', 'exec')
139139
except (SyntaxError, ), ex:
140-
warning('Generated source has invalid syntax.')
140+
warning('Generated source has invalid syntax. %s', ex)
141141
else:
142142
info('Generated source has valid syntax.')
143143

java2python/compiler/block.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,30 @@
1212
# very reusable. The module split does allow for grouping of related
1313
# methods and does hide some of the cluttered code.
1414

15+
from sys import modules
1516
from java2python.compiler import template, visitor
1617

1718

18-
def makeType(name, ftn=None):
19-
bases = (getattr(template, name), getattr(visitor, name))
20-
namespace = dict(factoryTypeName=ftn if ftn else name.lower())
21-
return type(name, bases, namespace)
19+
def newType(className, factoryTypeName):
20+
""" Creates a class derived from template.className and visitor.className """
21+
bases = (getattr(template, className), getattr(visitor, className))
22+
return type(className, bases, dict(factoryTypeName=factoryTypeName))
2223

2324

24-
Annotation = makeType('Annotation', 'at')
25-
Class = makeType('Class', 'klass')
26-
Comment = makeType('Comment')
27-
Enum = makeType('Enum')
28-
Expression = makeType('Expression', 'expr')
29-
Interface = makeType('Interface')
30-
Method = makeType('Method')
31-
MethodContent = makeType('MethodContent', 'methodContent')
32-
Module = makeType('Module')
33-
Statement = makeType('Statement')
25+
def addTypeToModule((cn, ftn)):
26+
""" Adds a new type to this module. """
27+
setattr(modules[__name__], cn, newType(cn, ftn))
28+
29+
30+
map(addTypeToModule, (
31+
('Annotation', 'at'),
32+
('Class', 'klass'),
33+
('Comment', 'comment'),
34+
('Enum', 'enum'),
35+
('Expression', 'expr'),
36+
('Interface', 'interface'),
37+
('Method', 'method'),
38+
('MethodContent', 'methodContent'),
39+
('Module', 'module'),
40+
('Statement', 'statement'),
41+
))

java2python/compiler/template.py

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,46 @@
22
# -*- coding: utf-8 -*-
33
""" java2python.compiler.template -> Base classes for writing Python source. """
44
##
5-
# This module defines templates, blocks of Python source code, that
6-
# can be easily manipulated and written. Each base provides string
7-
# methods (__str__, dump, dumps) for serializing instances as source
8-
# code. The base types also provide many utility methods.
5+
# This module defines templates -- blocks of Python source code --
6+
# that can be easily manipulated and written. Each base provides
7+
# string methods (__str__, dump, dumps) for serializing instances as a
8+
# source code string. The base types also provide many utility
9+
# methods.
910
#
1011
# The Factory class is used to to provide runtime lookup of concrete
1112
# classes; this was necessary to accommodate splitting the behavior of
1213
# the compiler subpackage into multiple modules. So-called patterns
1314
# are usually a sign of a bad design and/or language limitations, and
1415
# this case is no exception.
1516

17+
from cStringIO import StringIO
1618
from functools import partial
1719
from itertools import chain, ifilter, imap
18-
from StringIO import StringIO
1920

2021
from java2python.lang import tokens
2122
from java2python.lib import FS
2223
from java2python.lib.colortools import *
2324

2425

2526
class Factory(object):
26-
""" Factory -> creates pre-configured callables for new block instances. """
27+
""" Factory -> creates pre-configured callables for new block instances.
28+
29+
The templates use an instance of this class as a quick and simple
30+
interface to create new templates like this:
31+
32+
stat = self.factory.statement()
33+
34+
The `__getattr__` method does the work of looking up and returning
35+
the appropriate template class. The lookup depends on the types
36+
registry, which is populated by the FactoryTypeDetector metaclass
37+
below.
38+
39+
The important thing to realize regarding this factory is this:
40+
when an attribute is requested (`self.factory.expr` for example),
41+
the factory locates the type and returns a constructor for it with
42+
the config object pre-applied.
43+
44+
"""
2745
types = {}
2846

2947
def __init__(self, config):
@@ -39,6 +57,16 @@ def __getattr__(self, name):
3957
class FactoryTypeDetector(type):
4058
""" FactoryTypeDetector -> detects factory-creatable types as they are defined.
4159
60+
As subclasses are created they are checked for an attribute called
61+
`factoryTypeName`. If present, that key is used to populate the
62+
factory type registry above.
63+
64+
Note that the actual subclasses are not created here (none of
65+
these specify a `factoryTypeName`). Actual factory types are
66+
created in `java2python.compiler.block`. This is because we're
67+
after not templates, but visitors combined with templates, aka
68+
blocks. Refer to the `blocks` module for the specific factory
69+
type names.
4270
"""
4371
def __init__(cls, name, bases, namespace):
4472
try:
@@ -50,6 +78,38 @@ def __init__(cls, name, bases, namespace):
5078
class Base(object):
5179
""" Base -> base class for formatting Python output.
5280
81+
This class defines a large set of attributes and methods for the
82+
other concrete templates defined below. The items defined here
83+
can be grouped as follows:
84+
85+
* References
86+
87+
This class defines `bases`, `children`, `decorators`, etc. for
88+
tracking the relationship between this instance and other blocks.
89+
90+
* Type Information
91+
92+
This class defines many is-A properties, such as isClass,
93+
isModule, isVoid, etc. Subclasses typically override one or more
94+
of these with an attribute or property.
95+
96+
* Configuration
97+
98+
This class provides utility methods for retrieving values from the
99+
run-time configuration. See the definition of `configHandler` and
100+
`configHandlers` for details.
101+
102+
* Serialization
103+
104+
This class provides a default implementation for subclasses to
105+
serialize their instances as Python source code strings. Notably,
106+
the `__str__` method is provided, which in turn defers most of its
107+
work to the `dumps` method. Subclasses provide different
108+
implementations of these methods where needed.
109+
110+
Also, the `__repr__` method is defined by this class for printing
111+
a the template as tree for debugging.
112+
53113
"""
54114
__metaclass__ = FactoryTypeDetector
55115
isAnnotation = isClass = isComment = isEnum = isExpression = \
@@ -92,9 +152,7 @@ def adopt(self, child, index=-1):
92152

93153
def altIdent(self, name):
94154
""" Returns an alternate identifier for the one given. """
95-
#print '## looking for name:', name, 'parent count:', [(type(x), type(x.parent)) for x in self.parents()]
96155
for klass in self.parents(lambda v:v.isClass):
97-
#print '#### looking inside', klass.name, klass.variables
98156
if name in klass.variables:
99157
try:
100158
method = self.parents(lambda v:v.isMethod).next()
@@ -105,7 +163,6 @@ def altIdent(self, name):
105163
if name in method.variables:
106164
return name
107165
return ('cls' if method.isStatic else 'self') + '.' + name
108-
#print
109166
return name
110167

111168
def configHandler(self, part, suffix='Handler', default=None):
@@ -282,12 +339,6 @@ class Comment(Expression):
282339
"""
283340
isComment = True
284341

285-
def __init__(self, config, left='', right='', fs=FS.lr, parent=None, tail=''):
286-
super(Comment, self).__init__(config, left, right, fs, parent, tail)
287-
if False:# not fs.strip().startswith('#'): # wha?
288-
prefix = self.config.last('commentPrefix', '# ')
289-
self.fs = prefix + self.fs
290-
291342
def __repr__(self):
292343
""" Returns the debug string representation of this comment. """
293344
parts = [white(self.typeName+':'),

java2python/compiler/tool.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
#
77
# $ python -m java2python.compiler.tool ./SomeClass.java
88

9-
from java2python.lang import (
10-
Lexer, Parser, StringStream, TokenStream, TreeAdaptor, walkTreeSelector,
11-
)
9+
from java2python.lang import Lexer, Parser, StringStream, TokenStream, TreeAdaptor
1210

1311

1412
def buildAST(source, config=None):
@@ -22,7 +20,7 @@ def buildAST(source, config=None):
2220

2321
def transformAST(tree, config):
2422
for selector, call in config.last('astTransforms', ()):
25-
for node in walkTreeSelector(tree, selector):
23+
for node in selector.walk(tree):
2624
call(node, config)
2725

2826

java2python/config/default.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
33

4+
##
45
# This is the default configuration file for java2python. Unless
5-
# explicity disabled with the '-n' or '--nodefaults' options, the j2py
6+
# explicity disabled with the '-n' or '--nodefaults' option, the j2py
67
# script will import this module for runtime configuration.
78

89
from java2python.mod import basic, transform
@@ -11,42 +12,46 @@
1112

1213
# Leading indent character or characters. Four spaces are used
1314
# because that is the recommendation of PEP 8.
14-
indentPrefix = ' '*4
15+
indentPrefix = ' ' * 4
1516

1617

1718
# Prefix character or characters for comments. The hash+space is
1819
# recommended by PEP 8.
1920
commentPrefix = '# '
2021

2122

22-
# These values are strings or generator-functions that yield strings
23+
# These values are strings or generators that yield strings
2324
# for a module prologue.
2425
modulePrologueHandlers = [
25-
basic.simpleShebang,
26+
basic.shebangLine,
2627
basic.simpleDocString,
2728
basic.maybeBsr,
2829
]
2930

3031

31-
# These generator-functions yield lines for a module epilogue.
32+
# These generators yield lines for a module epilogue.
3233
moduleEpilogueHandlers = [
3334
basic.scriptMainStanza,
3435
]
3536

3637

37-
# These generator-functions yield (possibly modified) source strings
38-
# for a module. The 'outputSubs' handler references the list of
38+
# These generators yield (possibly modified) source strings for a
39+
# module. The `basic.outputSubs` handler references the list of
3940
# regular expression substitutions near the end of this module.
4041
moduleOutputHandlers = [
4142
basic.outputSubs,
4243
]
4344

4445

46+
# These generators yield doc strings for a class.
4547
classHeadHandlers = [
4648
basic.simpleDocString,
4749
]
4850

4951

52+
# These generators are called after a class has been completely
53+
# generated; this specific one sorts method bodies by name.
54+
# NB: the code generator doesn't actually use this.
5055
classPostWalkMutators = [
5156
basic.classContentSort,
5257
]
@@ -81,7 +86,7 @@
8186

8287

8388
# This handler creates enum values on enum classes after they've been
84-
# defined. The handler tries to matches Java semantics by using
89+
# defined. The handler tries to match Java semantics by using
8590
# strings. Refer to the documentation for details.
8691
enumValueHandler = basic.enumConstStrings
8792

@@ -106,9 +111,13 @@
106111
# modulePackageDeclarationHandler = basic.namespacePackages
107112

108113

109-
moduleImportDeclarationHandler = basic.commentedImports
114+
# This handler is turns java imports into python imports. No mapping
115+
# of packages is performed:
110116
moduleImportDeclarationHandler = basic.simpleImports
111117

118+
# This import decl. handler can be used instead to produce comments
119+
# instead of import statements:
120+
# moduleImportDeclarationHandler = basic.commentedImports
112121

113122
# The AST transformation function uses these declarations to modify an
114123
# AST before compiling it to python source. Having these declarations
@@ -172,6 +181,3 @@
172181
'double' : 'float',
173182
'java.lang.String' : 'str',
174183
}
175-
176-
177-

java2python/lang/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
from java2python.lang.JavaLexer import JavaLexer as Lexer
55
from java2python.lang.JavaParser import JavaParser as Parser
66
from java2python.lang.base import StringStream, TokenStream, TreeAdaptor, tokens
7-
from java2python.lang.selector import walkTreeSelector
7+

java2python/lang/base.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
3+
""" java2python.lang.base -> lexer and parser support classes. """
4+
##
5+
# This module provides the following:
6+
#
7+
# * `Tokens`
8+
#
9+
# This class is used to create the single `token` instance in this
10+
# module. It is used to map between parser tokens and their ids and
11+
# vice-versa.
12+
#
13+
# * `TreeAdaptor`
14+
#
15+
# This class is used by `java2python.compiler.tool`, where the
16+
# `buildAST` function associates an instance of it to a parser. The
17+
# `TreeAdaptor` class creates `LocalTree` instances.
18+
#
19+
# * `LocalTree`
20+
#
21+
# This class provides a slew of extra utility methods that are useful
22+
# when inspecting and printing tree nodes.
23+
#
324

425
##
526
# ANTLR notes:
@@ -25,9 +46,12 @@
2546
# Tree objects. Our adaptor, TreeAdaptor, creates the LocalTree
2647
# instances.
2748
#
28-
##
49+
50+
from cStringIO import StringIO
51+
2952
from antlr3 import ANTLRStringStream as StringStream, CommonTokenStream as TokenStream
3053
from antlr3.tree import CommonTreeAdaptor, CommonTree
54+
3155
from java2python.lib import colortools
3256

3357

@@ -143,10 +167,15 @@ def innerDump(root, offset):
143167
token, indent = root.token, ' ' * offset
144168
start, stop = root.tokenStartIndex, root.tokenStopIndex
145169
idxes, ttyp = '', tokens.map.get(token.type, '?')
170+
line = token.line
146171
if start and stop and start == stop:
147-
idxes = ' [{0}]'.format(start)
172+
idxes = 'start={}'.format(start)
148173
elif start and stop:
149-
idxes = ' [{0}:{1}]'.format(start, stop)
174+
idxes = 'start={}, stop={}'.format(start, stop)
175+
if line:
176+
idxes = 'line={}{}{}'.format(line, ', ' if idxes else '', idxes)
177+
idxes = ' [{}]'.format(idxes) if idxes else ''
178+
idxes = colortools.black(idxes)
150179
args = [indent, self.colorType(ttyp), '', idxes, '']
151180
if extras(token.text, ttyp):
152181
args[2] = ' ' + self.colorText(ttyp, token.text)
@@ -161,6 +190,12 @@ def innerDump(root, offset):
161190
print >> fd, '{0}{1}'.format(indent, line)
162191
innerDump(self, level)
163192

193+
def dumps(self, level=0):
194+
""" Dump this token to a string. """
195+
fd = StringIO()
196+
self.dump(fd, level)
197+
return fd.getvalue()
198+
164199
def dupNode(self):
165200
""" Called by the parser to create a duplicate of this tree. """
166201
get = lambda v:getattr(self, v, None)

0 commit comments

Comments
 (0)