Skip to content

Commit 646fe5f

Browse files
author
Troy Melhase
committed
Adds selector tests.
1 parent 787b21c commit 646fe5f

File tree

8 files changed

+185
-105
lines changed

8 files changed

+185
-105
lines changed

bin/j2py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,15 @@ def main(options):
9393
print 'IOError: %s.' % (msg, )
9494
return code
9595

96-
config = Config(configs)
9796
timed['comp']
9897
try:
99-
tree = buildAST(source, config)
98+
tree = buildAST(source)
10099
except (Exception, ), exc:
101100
exception('exception while parsing')
102101
return 1
103102
timed['comp_finish']
104103

104+
config = Config(configs)
105105
timed['xform']
106106
transformAST(tree, config)
107107
timed['xform_finish']
@@ -118,6 +118,11 @@ def main(options):
118118
timed['encode_finish']
119119
timed['overall_finish']
120120

121+
if options.lexertokens:
122+
for idx, tok in enumerate(tree.parser.input.tokens):
123+
print >> sys.stderr, '{0} {1}'.format(idx, tok)
124+
print >> sys.stderr
125+
121126
if options.javaast:
122127
tree.dump(sys.stderr)
123128
print >> sys.stderr
@@ -171,6 +176,9 @@ def config(argv):
171176
addopt('-l', '--loglevel', dest='loglevel',
172177
help='Set log level by name or value.',
173178
default='WARN', type='loglevel')
179+
addopt('-t', '--lexer-tokens', dest='lexertokens',
180+
help='Print lexer tokens to stderr.',
181+
default=False, action='store_true')
174182
addopt('-p', '--python-tree', dest='pytree',
175183
help='Print python object tree to stderr.',
176184
default=False, action='store_true')

java2python/compiler/__init__.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,31 @@
77
# instead of using directly referencing items within the subpackage.
88

99
from java2python.compiler.block import Module
10-
from java2python.compiler.tool import buildAST, transformAST
10+
from java2python.lang import Lexer, Parser, StringStream, TokenStream, TreeAdaptor
11+
12+
13+
def buildAST(source):
14+
""" Returns an AST for the given source. """
15+
lexer = Lexer(StringStream(source))
16+
parser = Parser(TokenStream(lexer))
17+
adapter = TreeAdaptor(lexer, parser)
18+
parser.setTreeAdaptor(adapter)
19+
scope = parser.javaSource()
20+
return scope.tree
21+
22+
23+
def buildJavaDocAST(source):
24+
""" Returns an AST for the given javadoc source. """
25+
from java2python.lang.JavaDocLexer import JavaDocLexer
26+
from java2python.lang.JavaDocParser import JavaDocParser
27+
lexer = JavaDocLexer(StringStream(source))
28+
parser = JavaDocParser(TokenStream(lexer))
29+
scope = parser.commentBody()
30+
return scope.tree
31+
32+
33+
def transformAST(tree, config):
34+
""" Walk the tree and apply the transforms in the config. """
35+
for selector, call in config.last('astTransforms', ()):
36+
for node in selector.walk(tree):
37+
call(node, config)

java2python/compiler/tool.py

Lines changed: 0 additions & 46 deletions
This file was deleted.

java2python/lang/selector.py

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55

66

77
class Selector(object):
8-
""" Selector -> base for selectors; provides operator methods.
8+
""" Selector -> base for selectors; provides operator methods. """
99

10-
"""
1110
def __add__(self, other):
1211
""" C + C => S
1312
@@ -24,6 +23,10 @@ def __and__(self, other):
2423
"""
2524
return Descendant(self, other)
2625

26+
def __call__(self, *args, **kwds):
27+
""" Subclasses must implement. """
28+
raise NotImplementedError('Selector class cannot be called.')
29+
2730
def __getitem__(self, key):
2831
""" C[n] => S
2932
@@ -41,15 +44,44 @@ def __gt__(self, other):
4144
"""
4245
return Child(self, other)
4346

44-
4547
def walk(self, tree):
48+
""" Select items from the tree and from the tree children. """
4649
for item in self(tree):
4750
yield item
4851
for child in tree.children:
4952
for item in self.walk(child):
5053
yield item
5154

5255

56+
class Token(Selector):
57+
""" Token(...) -> select tokens by matching attributes.
58+
59+
"""
60+
# channel=None, index=None, input=None, line=None, start=None, stop=None, text=None, type=None
61+
62+
def __init__(self, **attrs):
63+
self.attrs = attrs
64+
if isinstance(attrs.get('type'), (basestring, )):
65+
self.attrs['type'] = getattr(tokens, attrs.get('type'))
66+
67+
def __call__(self, tree):
68+
items = self.attrs.items()
69+
token = tree.token
70+
71+
def match_or_call(k, v):
72+
if callable(v):
73+
return v(token)
74+
return getattr(token, k)==v
75+
76+
if all(match_or_call(k, v) for k, v in items if v is not None):
77+
yield tree
78+
79+
def __str__(self):
80+
items = self.attrs.items()
81+
keys = ('{}={}'.format(k, v) for k, v in items if v is not None)
82+
return 'Token({})'.format(', '.join(keys))
83+
84+
5385
class Nth(Selector):
5486
""" E[n] -> match any slice n of E
5587
@@ -109,29 +141,6 @@ def __str__(self):
109141
return 'Type({0}{1}:{2})'.format(tokens.map[self.key], val, self.key)
110142

111143

112-
class Token(Selector):
113-
""" Token(T) select any token by matching attributes.
114-
115-
Similar to the type selector in CSS.
116-
"""
117-
# channel=None, index=None, input=None, line=None, start=None, stop=None, text=None, type=None
118-
119-
def __init__(self, **attrs):
120-
self.attrs = attrs
121-
if isinstance(attrs.get('type'), (basestring, )):
122-
self.attrs['type'] = getattr(tokens, attrs.get('type'))
123-
124-
def __call__(self, tree):
125-
items = self.attrs.items()
126-
if all(getattr(tree.token, k)==v for k, v in items if v is not None):
127-
yield tree
128-
129-
def __str__(self):
130-
items = self.attrs.items()
131-
keys = ('{}={}'.format(k, v) for k, v in items if v is not None)
132-
return 'Token({})'.format(', '.join(keys))
133-
134-
135144
class Star(Selector):
136145
""" * select any
137146

test/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ test_targets := $(sort $(notdir $(basename $(wildcard *.java))))
1414

1515
all:
1616
$(MAKE) $(test_targets)
17+
@cd selector && make
1718

1819

1920
clean:

test/selector/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.PHONY: all
2+
3+
test_targets := $(sort $(notdir $(basename $(wildcard *.py))))
4+
5+
6+
all:
7+
$(MAKE) $(test_targets)
8+
9+
%:
10+
@python -m unittest -v $@
11+
12+

test/selector/Selector1.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
class Selector1 {
2-
void bar() {
2+
void bar(int x, int y) {
33
int foo = 3;
4+
String z = "foo";
45
}
56

6-
void foo() {}
7+
void foo() {};
8+
79
}

test/selector/test_all.py

Lines changed: 95 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,104 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
3+
import os
4+
import sys
5+
import unittest
36

4-
from java2python.config import Config
57
from java2python.compiler import buildAST
68
from java2python.lang import tokens
79
from java2python.lang.selector import Token, Type
10+
from java2python.lib import colortools
811

912

10-
if __name__ == '__main__':
11-
import sys, os
13+
def setUpModule():
1214
fn = os.path.join(os.path.dirname(__file__), 'Selector1.java')
13-
source = open(fn).read()
14-
tree = buildAST(source, Config(()))
15-
tree.dump(sys.stdout)
16-
17-
selectors = [
18-
# Type('EXPR'),
19-
# Type('QUALIFIED_TYPE_IDENT') > Type('IDENT'),
20-
# Type('CLASS')[2],
21-
# Type('METHOD_CALL') & Type('IDENT'),
22-
# Type('IDENT') + Type('IDENT'),
23-
Type('CLASS') > Type('IDENT'),
24-
Type('IDENT', 'foo'),
25-
Token(text='foo'),
26-
Token(type='BLOCK_SCOPE', line=2) & Token(type='IDENT', text='foo')
27-
]
28-
29-
for index, selector in enumerate(selectors):
30-
print 'Selector Test {0}:\n ==== {1}'.format(index, selector)
31-
for node in selector.walk(tree):
32-
name = str(node)
33-
ntype = tokens.map[node.type]
34-
args = (name, '') if name == ntype else (ntype, name)
35-
print '{0}---- match: {1} {2}'.format(*(' '*8, )+args)
36-
print '{0}---- token: {1}'.format(' '*8, node.dumps() )
37-
print
15+
SelectorTest.tree = buildAST(open(fn).read())
16+
SelectorTest.tree.dump(sys.stdout)
17+
18+
19+
class SelectorTest(unittest.TestCase):
20+
def walk(self, selector):
21+
return list(selector.walk(self.tree))
22+
23+
def assertNodes(self, nodes, length):
24+
self.assertTrue(nodes)
25+
self.assertEqual(len(nodes), length)
26+
27+
def shortDescription(self):
28+
fs = 'Description: {}\nSelector: {}\n'
29+
args = (colortools.cyan(self.description), colortools.yellow(self.selector))
30+
return fs.format(*args)
31+
32+
@classmethod
33+
def make(cls, count):
34+
def t(self):
35+
nodes = self.walk(self.selector)
36+
self.assertNodes(nodes, count)
37+
return t
38+
39+
40+
class TestIdentChildOfClass(SelectorTest):
41+
description = 'select one IDENT node that is a child of a CLASS node'
42+
selector = Type('CLASS') > Type('IDENT')
43+
test = SelectorTest.make(1)
44+
45+
46+
class TestIdentWithText(SelectorTest):
47+
description = 'select two IDENT nodes with text "foo"'
48+
selector = Type('IDENT', 'foo')
49+
test = SelectorTest.make(2)
50+
51+
52+
class TestTokensWithText(SelectorTest):
53+
description = 'select two nodes with text "foo"'
54+
selector = Token(text='foo')
55+
test = SelectorTest.make(2)
56+
57+
58+
class TestTokenCallableCombo(SelectorTest):
59+
description = 'select BLOCK_SCOPE on line 7'
60+
selector = Token(type=lambda t: t.type == tokens.BLOCK_SCOPE, line=7)
61+
test = SelectorTest.make(1)
62+
63+
64+
class TestTokenMultipleCallables(SelectorTest):
65+
description = 'select BLOCK_SCOPE on line 2 or 7'
66+
selector = Token(type=lambda t: t.type == tokens.BLOCK_SCOPE, line=lambda t:t.line in (2, 7))
67+
test = SelectorTest.make(2)
68+
69+
70+
class TestTokenChildCallable(SelectorTest):
71+
description = 'select BLOCK_SCOPE with one child IDENT starting with "f"'
72+
selector = Token(type=lambda t: t.type == tokens.BLOCK_SCOPE) & Token(type='IDENT', text=lambda tok:tok.text.startswith('f'))
73+
test = SelectorTest.make(1)
74+
75+
76+
class TestNthChildWithExtraChecks(SelectorTest):
77+
description = 'select two children of FORMAL_PARAM_STD_DECL at index 2 '
78+
selector = Type('FORMAL_PARAM_STD_DECL')[2]
79+
80+
def test(self):
81+
nodes = self.walk(self.selector)
82+
self.assertNodes(nodes, 2)
83+
self.assertEquals(nodes[0].type, tokens.IDENT)
84+
self.assertEquals(nodes[1].type, tokens.IDENT)
85+
self.assertEquals(nodes[0].text, 'x')
86+
self.assertEquals(nodes[1].text, 'y')
87+
88+
89+
class TestDirectChildren(SelectorTest):
90+
description = 'select two TYPE nodes that are children of a VAR_DECLARATION node'
91+
selector = Type('VAR_DECLARATION') > Type('TYPE')
92+
test = SelectorTest.make(2)
93+
94+
95+
class TestSimpleSiblings(SelectorTest):
96+
description = 'select three IDENT nodes that are siblings of a MODIFIER_LIST'
97+
selector = Type('MODIFIER_LIST') + Type('IDENT')
98+
test = SelectorTest.make(3)
99+
100+
101+
class TestClassIdent(SelectorTest):
102+
description = 'select one IDENT node that is a child of a CLASS node'
103+
selector = Type('CLASS') > Type('IDENT')
104+
test = SelectorTest.make(1)

0 commit comments

Comments
 (0)