Skip to content

Commit 8861f5d

Browse files
author
whitequark
committedApr 29, 2015
25% grammar coverage.
1 parent 45914f5 commit 8861f5d

File tree

4 files changed

+363
-22
lines changed

4 files changed

+363
-22
lines changed
 

Diff for: ‎pyparser/ast.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,15 @@ class arguments(commonloc):
8686
:ivar default_equals_locs: locations of ``=``
8787
"""
8888

89-
class boolop(commonloc):
90-
"""Base class for binary boolean operators."""
89+
class boolop:
90+
"""
91+
Base class for binary boolean operators.
92+
93+
This class is unlike others in that it does not have the ``loc`` field.
94+
It serves only as an indicator of operation and corresponds to no source
95+
itself; locations are recorded in :class:`BoolOp`.
96+
"""
97+
_locs = ()
9198
class And(boolop, ast.And):
9299
"""The ``and`` operator."""
93100
class Or(boolop, ast.Or):
@@ -168,7 +175,9 @@ class BoolOp(expr, ast.BoolOp):
168175
:ivar left: (node) left-hand side
169176
:ivar op: (:class:`boolop`) operator
170177
:ivar right: (node) right-hand side
178+
:ivar op_locs: locations of operators
171179
"""
180+
_locs = expr._locs + ('op_locs',)
172181
class Call(beginendloc, expr, ast.Call):
173182
"""
174183
A function call, e.g. ``f(x, y=1, *z, **t)``.
@@ -414,8 +423,9 @@ class Assign(stmt, ast.Assign):
414423
415424
:ivar targets: (list of assignable node) left-hand sides
416425
:ivar value: (node) right-hand side
417-
:ivar ops_loc: location of equality signs corresponding to ``targets``
426+
:ivar op_locs: location of equality signs corresponding to ``targets``
418427
"""
428+
_locs = stmt._locs + ('op_locs',)
419429
class AugAssign(stmt, ast.AugAssign):
420430
"""
421431
The operator-assignment statement, e.g. ``+=``.

Diff for: ‎pyparser/coverage/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def report(parser, name='parser'):
7979
total_pts += pts
8080
total_covered += covered
8181

82-
print("GRAMMAR COVERAGE: %.2f%%" % (total_covered / total_pts))
82+
print("GRAMMAR COVERAGE: %.2f%%" % (total_covered / total_pts * 100))
8383

8484
content = rewriter.rewrite().source
8585
content = '\n'.join(map(
@@ -117,7 +117,7 @@ def report(parser, name='parser'):
117117
<pre>{content}</pre>
118118
</body>
119119
</html>
120-
""".format(percentage=total_covered / total_pts,
120+
""".format(percentage=total_covered / total_pts * 100,
121121
pts=total_pts, covered=total_covered,
122122
file=os.path.basename(_buf.name),
123123
content=content))

Diff for: ‎pyparser/parser.py

+28-12
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ def expr_stmt_1(self, augassign, rhs_expr):
414414
def expr_stmt_2(self, seq):
415415
if len(seq) > 0:
416416
return ast.Assign(targets=map(lambda x: x[1], seq[:-1]), value=seq[-1][1],
417-
ops_loc=map(lambda x: x[0], seq))
417+
op_locs=map(lambda x: x[0], seq))
418418
else:
419419
return None
420420

@@ -423,18 +423,18 @@ def expr_stmt(self, lhs, rhs):
423423
"""expr_stmt: testlist (augassign (yield_expr|testlist) |
424424
('=' (yield_expr|testlist))*)"""
425425
if isinstance(rhs, ast.AugAssign):
426-
if len(lhs.elts) == 1:
427-
rhs.target = self._assignable(lhs.elts[0])
428-
rhs.loc = rhs.target.loc.join(rhs.value.loc)
429-
return rhs
430-
else:
426+
if isinstance(lhs, ast.Tuple):
431427
error = diagnostic.Diagnostic(
432428
"error", "illegal expression for augmented assignment", {}, rhs.loc)
433429
raise diagnostic.DiagnosticException(error)
430+
else:
431+
rhs.target = self._assignable(lhs)
432+
rhs.loc = rhs.target.loc.join(rhs.value.loc)
433+
return rhs
434434
elif rhs is not None:
435435
rhs.targets = map(self._assignable, [lhs] + rhs.targets)
436436
rhs.loc = lhs.loc.join(rhs.value.loc)
437-
return ast.Expr(value=rhs, loc=rhs.loc)
437+
return rhs
438438
else:
439439
return ast.Expr(value=lhs, loc=lhs.loc)
440440

@@ -754,11 +754,27 @@ def test_1(self, lhs, rhs_opt):
754754
test = Alt(test_1, Rule('lambdef'))
755755
"""test: or_test ['if' or_test 'else' test] | lambdef"""
756756

757-
or_test = BinOper('and_test', Oper(ast.Or, 'or'), node=ast.BoolOp)
758-
"""or_test: and_test ('or' and_test)*"""
757+
@action(Seq(Rule('and_test'), Star(Seq(Loc('or'), Rule('and_test')))))
758+
def or_test(self, lhs, rhs):
759+
"""or_test: and_test ('or' and_test)*"""
760+
if len(rhs) > 0:
761+
return ast.BoolOp(op=ast.Or(),
762+
values=[lhs] + map(lambda x: x[1], rhs),
763+
loc=lhs.loc.join(rhs[-1][1].loc),
764+
op_locs=map(lambda x: x[0], rhs))
765+
else:
766+
return lhs
759767

760-
and_test = BinOper('not_test', Oper(ast.And, 'and'), node=ast.BoolOp)
761-
"""and_test: not_test ('and' not_test)*"""
768+
@action(Seq(Rule('not_test'), Star(Seq(Loc('and'), Rule('not_test')))))
769+
def and_test(self, lhs, rhs):
770+
"""and_test: not_test ('and' not_test)*"""
771+
if len(rhs) > 0:
772+
return ast.BoolOp(op=ast.And(),
773+
values=[lhs] + map(lambda x: x[1], rhs),
774+
loc=lhs.loc.join(rhs[-1][1].loc),
775+
op_locs=map(lambda x: x[0], rhs))
776+
else:
777+
return lhs
762778

763779
@action(Seq(Oper(ast.Not, 'not'), Rule('not_test')))
764780
def not_test_1(self, op, operand):
@@ -779,7 +795,7 @@ def comparison(self, lhs, rhs):
779795
return lhs
780796

781797
comp_op = Alt(Oper(ast.Lt, '<'), Oper(ast.Gt, '>'), Oper(ast.Eq, '=='),
782-
Oper(ast.GtE, '>='), Oper(ast.GtE, '<='), Oper(ast.NotEq, '<>'),
798+
Oper(ast.GtE, '>='), Oper(ast.LtE, '<='), Oper(ast.NotEq, '<>'),
783799
Oper(ast.NotEq, '!='),
784800
Oper(ast.In, 'in'), Oper(ast.NotIn, 'not', 'in'),
785801
Oper(ast.Is, 'is'), Oper(ast.IsNot, 'is', 'not'))

Diff for: ‎pyparser/test/test_parser.py

+320-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def flatten_ast(self, node):
3030
# Validate locs
3131
for attr in node.__dict__:
3232
if attr.endswith('_loc') or attr.endswith('_locs'):
33-
self.assertTrue(attr in node._locs)
33+
self.assertTrue(attr in node._locs,
34+
"%s not in %s._locs" % (attr, repr(node)))
3435
for loc in node._locs:
3536
self.assertTrue(loc in node.__dict__)
3637

@@ -45,7 +46,7 @@ def flatten_ast(self, node):
4546
return flat_node
4647

4748
_loc_re = re.compile(r"\s*([~^]+)\s+([a-z_0-9.]+)")
48-
_path_re = re.compile(r"(([a-z_]+)|([0-9]+))(.)?")
49+
_path_re = re.compile(r"(([a-z_]+)|([0-9]+))(\.)?")
4950

5051
def match_loc(self, ast, matcher, root=lambda x: x):
5152
ast = root(ast)
@@ -68,9 +69,9 @@ def match_loc(self, ast, matcher, root=lambda x: x):
6869
if path_match is None:
6970
raise Exception("invalid location matcher path %s" % path)
7071

71-
path_field = path_match.group(1)
72-
path_index = path_match.group(2)
73-
path_last = not path_match.group(3)
72+
path_field = path_match.group(2)
73+
path_index = path_match.group(3)
74+
path_last = not path_match.group(4)
7475

7576
if path_field is not None:
7677
obj = getattr(obj, path_field)
@@ -148,3 +149,317 @@ def test_string(self):
148149
"^ begin_loc"
149150
" ^ end_loc")
150151

152+
def test_ident(self):
153+
self.assertParsesExpr(
154+
{'ty': 'Name', 'id': 'foo', 'ctx': None},
155+
"foo",
156+
"~~~ loc")
157+
158+
#
159+
# OPERATORS
160+
#
161+
162+
ast_1 = {'ty': 'Num', 'n': 1}
163+
164+
def test_unary(self):
165+
self.assertParsesExpr(
166+
{'ty': 'UnaryOp', 'op': {'ty': 'UAdd'}, 'operand': self.ast_1},
167+
"+1",
168+
"~~ loc"
169+
"~ op.loc")
170+
171+
self.assertParsesExpr(
172+
{'ty': 'UnaryOp', 'op': {'ty': 'USub'}, 'operand': self.ast_1},
173+
"-1",
174+
"~~ loc"
175+
"~ op.loc")
176+
177+
self.assertParsesExpr(
178+
{'ty': 'UnaryOp', 'op': {'ty': 'Invert'}, 'operand': self.ast_1},
179+
"~1",
180+
"~~ loc"
181+
"~ op.loc")
182+
183+
def test_binary(self):
184+
self.assertParsesExpr(
185+
{'ty': 'BinOp', 'op': {'ty': 'Pow'}, 'left': self.ast_1, 'right': self.ast_1},
186+
"1 ** 1",
187+
"~~~~~~ loc"
188+
" ~~ op.loc")
189+
190+
self.assertParsesExpr(
191+
{'ty': 'BinOp', 'op': {'ty': 'Mult'}, 'left': self.ast_1, 'right': self.ast_1},
192+
"1 * 1",
193+
"~~~~~ loc"
194+
" ^ op.loc")
195+
196+
self.assertParsesExpr(
197+
{'ty': 'BinOp', 'op': {'ty': 'Div'}, 'left': self.ast_1, 'right': self.ast_1},
198+
"1 / 1",
199+
"~~~~~ loc"
200+
" ^ op.loc")
201+
202+
self.assertParsesExpr(
203+
{'ty': 'BinOp', 'op': {'ty': 'Mod'}, 'left': self.ast_1, 'right': self.ast_1},
204+
"1 % 1",
205+
"~~~~~ loc"
206+
" ^ op.loc")
207+
208+
self.assertParsesExpr(
209+
{'ty': 'BinOp', 'op': {'ty': 'FloorDiv'}, 'left': self.ast_1, 'right': self.ast_1},
210+
"1 // 1",
211+
"~~~~~~ loc"
212+
" ~~ op.loc")
213+
214+
self.assertParsesExpr(
215+
{'ty': 'BinOp', 'op': {'ty': 'Add'}, 'left': self.ast_1, 'right': self.ast_1},
216+
"1 + 1",
217+
"~~~~~ loc"
218+
" ^ op.loc")
219+
220+
self.assertParsesExpr(
221+
{'ty': 'BinOp', 'op': {'ty': 'Sub'}, 'left': self.ast_1, 'right': self.ast_1},
222+
"1 - 1",
223+
"~~~~~ loc"
224+
" ^ op.loc")
225+
226+
def test_bitwise(self):
227+
self.assertParsesExpr(
228+
{'ty': 'BinOp', 'op': {'ty': 'LShift'}, 'left': self.ast_1, 'right': self.ast_1},
229+
"1 << 1",
230+
"~~~~~~ loc"
231+
" ~~ op.loc")
232+
233+
self.assertParsesExpr(
234+
{'ty': 'BinOp', 'op': {'ty': 'RShift'}, 'left': self.ast_1, 'right': self.ast_1},
235+
"1 >> 1",
236+
"~~~~~~ loc"
237+
" ~~ op.loc")
238+
239+
self.assertParsesExpr(
240+
{'ty': 'BinOp', 'op': {'ty': 'BitAnd'}, 'left': self.ast_1, 'right': self.ast_1},
241+
"1 & 1",
242+
"~~~~~ loc"
243+
" ^ op.loc")
244+
245+
self.assertParsesExpr(
246+
{'ty': 'BinOp', 'op': {'ty': 'BitOr'}, 'left': self.ast_1, 'right': self.ast_1},
247+
"1 | 1",
248+
"~~~~~ loc"
249+
" ^ op.loc")
250+
251+
self.assertParsesExpr(
252+
{'ty': 'BinOp', 'op': {'ty': 'BitXor'}, 'left': self.ast_1, 'right': self.ast_1},
253+
"1 ^ 1",
254+
"~~~~~ loc"
255+
" ^ op.loc")
256+
257+
def test_compare(self):
258+
self.assertParsesExpr(
259+
{'ty': 'Compare', 'ops': [{'ty': 'Lt'}],
260+
'left': self.ast_1, 'comparators': [self.ast_1]},
261+
"1 < 1",
262+
"~~~~~ loc"
263+
" ^ ops.0.loc")
264+
265+
self.assertParsesExpr(
266+
{'ty': 'Compare', 'ops': [{'ty': 'LtE'}],
267+
'left': self.ast_1, 'comparators': [self.ast_1]},
268+
"1 <= 1",
269+
"~~~~~~ loc"
270+
" ~~ ops.0.loc")
271+
272+
self.assertParsesExpr(
273+
{'ty': 'Compare', 'ops': [{'ty': 'Gt'}],
274+
'left': self.ast_1, 'comparators': [self.ast_1]},
275+
"1 > 1",
276+
"~~~~~ loc"
277+
" ^ ops.0.loc")
278+
279+
self.assertParsesExpr(
280+
{'ty': 'Compare', 'ops': [{'ty': 'GtE'}],
281+
'left': self.ast_1, 'comparators': [self.ast_1]},
282+
"1 >= 1",
283+
"~~~~~~ loc"
284+
" ~~ ops.0.loc")
285+
286+
self.assertParsesExpr(
287+
{'ty': 'Compare', 'ops': [{'ty': 'Eq'}],
288+
'left': self.ast_1, 'comparators': [self.ast_1]},
289+
"1 == 1",
290+
"~~~~~~ loc"
291+
" ~~ ops.0.loc")
292+
293+
self.assertParsesExpr(
294+
{'ty': 'Compare', 'ops': [{'ty': 'NotEq'}],
295+
'left': self.ast_1, 'comparators': [self.ast_1]},
296+
"1 != 1",
297+
"~~~~~~ loc"
298+
" ~~ ops.0.loc")
299+
300+
self.assertParsesExpr(
301+
{'ty': 'Compare', 'ops': [{'ty': 'NotEq'}],
302+
'left': self.ast_1, 'comparators': [self.ast_1]},
303+
"1 <> 1",
304+
"~~~~~~ loc"
305+
" ~~ ops.0.loc")
306+
307+
self.assertParsesExpr(
308+
{'ty': 'Compare', 'ops': [{'ty': 'In'}],
309+
'left': self.ast_1, 'comparators': [self.ast_1]},
310+
"1 in 1",
311+
"~~~~~~ loc"
312+
" ~~ ops.0.loc")
313+
314+
self.assertParsesExpr(
315+
{'ty': 'Compare', 'ops': [{'ty': 'NotIn'}],
316+
'left': self.ast_1, 'comparators': [self.ast_1]},
317+
"1 not in 1",
318+
"~~~~~~~~~~ loc"
319+
" ~~~~~~ ops.0.loc")
320+
321+
self.assertParsesExpr(
322+
{'ty': 'Compare', 'ops': [{'ty': 'Is'}],
323+
'left': self.ast_1, 'comparators': [self.ast_1]},
324+
"1 is 1",
325+
"~~~~~~ loc"
326+
" ~~ ops.0.loc")
327+
328+
# self.assertParsesExpr(
329+
# {'ty': 'Compare', 'ops': [{'ty': 'NotIs'}],
330+
# 'left': self.ast_1, 'comparators': [self.ast_1]},
331+
# "1 is not 1",
332+
# "~~~~~~~~~~ loc"
333+
# " ~~~~~~ ops.0.loc")
334+
335+
def test_compare_multi(self):
336+
self.assertParsesExpr(
337+
{'ty': 'Compare', 'ops': [{'ty': 'Lt'}, {'ty': 'LtE'}],
338+
'left': self.ast_1,
339+
'comparators': [{'ty': 'Num', 'n': 2}, {'ty': 'Num', 'n': 3}]},
340+
"1 < 2 <= 3",
341+
"~~~~~~~~~~ loc"
342+
" ^ ops.0.loc"
343+
" ~~ ops.1.loc")
344+
345+
def test_boolop(self):
346+
self.assertParsesExpr(
347+
{'ty': 'BoolOp', 'op': {'ty': 'And'}, 'values': [self.ast_1, self.ast_1]},
348+
"1 and 1",
349+
"~~~~~~~ loc"
350+
" ~~~ op_locs.0")
351+
352+
self.assertParsesExpr(
353+
{'ty': 'BoolOp', 'op': {'ty': 'Or'}, 'values': [self.ast_1, self.ast_1]},
354+
"1 or 1",
355+
"~~~~~~ loc"
356+
" ~~ op_locs.0")
357+
358+
self.assertParsesExpr(
359+
{'ty': 'UnaryOp', 'op': {'ty': 'Not'}, 'operand': self.ast_1},
360+
"not 1",
361+
"~~~~~ loc"
362+
"~~~ op.loc")
363+
364+
def test_boolop_multi(self):
365+
self.assertParsesExpr(
366+
{'ty': 'BoolOp', 'op': {'ty': 'Or'}, 'values': [self.ast_1, self.ast_1, self.ast_1]},
367+
"1 or 1 or 1",
368+
"~~~~~~~~~~~ loc"
369+
" ~~ op_locs.0"
370+
" ~~ op_locs.1")
371+
372+
#
373+
# STATEMENTS
374+
#
375+
376+
ast_x = {'ty': 'Name', 'id': 'x', 'ctx': None}
377+
ast_y = {'ty': 'Name', 'id': 'y', 'ctx': None}
378+
379+
def test_assign(self):
380+
self.assertParsesSuite(
381+
[{'ty': 'Assign', 'targets': [self.ast_x], 'value': self.ast_1}],
382+
"x = 1",
383+
"~~~~~ 0.loc"
384+
" ^ 0.op_locs.0")
385+
386+
self.assertParsesSuite(
387+
[{'ty': 'Assign', 'targets': [self.ast_x, self.ast_y], 'value': self.ast_1}],
388+
"x = y = 1",
389+
"~~~~~~~~~ 0.loc"
390+
" ^ 0.op_locs.0"
391+
" ^ 0.op_locs.1")
392+
393+
def test_augassign(self):
394+
self.assertParsesSuite(
395+
[{'ty': 'AugAssign', 'op': {'ty': 'Add'}, 'target': self.ast_x, 'value': self.ast_1}],
396+
"x += 1",
397+
"~~~~~~ 0.loc"
398+
" ~~ 0.op.loc")
399+
400+
self.assertParsesSuite(
401+
[{'ty': 'AugAssign', 'op': {'ty': 'Sub'}, 'target': self.ast_x, 'value': self.ast_1}],
402+
"x -= 1",
403+
"~~~~~~ 0.loc"
404+
" ~~ 0.op.loc")
405+
406+
self.assertParsesSuite(
407+
[{'ty': 'AugAssign', 'op': {'ty': 'Mult'}, 'target': self.ast_x, 'value': self.ast_1}],
408+
"x *= 1",
409+
"~~~~~~ 0.loc"
410+
" ~~ 0.op.loc")
411+
412+
self.assertParsesSuite(
413+
[{'ty': 'AugAssign', 'op': {'ty': 'Div'}, 'target': self.ast_x, 'value': self.ast_1}],
414+
"x /= 1",
415+
"~~~~~~ 0.loc"
416+
" ~~ 0.op.loc")
417+
418+
self.assertParsesSuite(
419+
[{'ty': 'AugAssign', 'op': {'ty': 'Mod'}, 'target': self.ast_x, 'value': self.ast_1}],
420+
"x %= 1",
421+
"~~~~~~ 0.loc"
422+
" ~~ 0.op.loc")
423+
424+
self.assertParsesSuite(
425+
[{'ty': 'AugAssign', 'op': {'ty': 'Pow'}, 'target': self.ast_x, 'value': self.ast_1}],
426+
"x **= 1",
427+
"~~~~~~~ 0.loc"
428+
" ~~~ 0.op.loc")
429+
430+
self.assertParsesSuite(
431+
[{'ty': 'AugAssign', 'op': {'ty': 'FloorDiv'}, 'target': self.ast_x, 'value': self.ast_1}],
432+
"x //= 1",
433+
"~~~~~~~ 0.loc"
434+
" ~~~ 0.op.loc")
435+
436+
self.assertParsesSuite(
437+
[{'ty': 'AugAssign', 'op': {'ty': 'RShift'}, 'target': self.ast_x, 'value': self.ast_1}],
438+
"x >>= 1",
439+
"~~~~~~~ 0.loc"
440+
" ~~~ 0.op.loc")
441+
442+
self.assertParsesSuite(
443+
[{'ty': 'AugAssign', 'op': {'ty': 'LShift'}, 'target': self.ast_x, 'value': self.ast_1}],
444+
"x <<= 1",
445+
"~~~~~~~ 0.loc"
446+
" ~~~ 0.op.loc")
447+
448+
self.assertParsesSuite(
449+
[{'ty': 'AugAssign', 'op': {'ty': 'BitAnd'}, 'target': self.ast_x, 'value': self.ast_1}],
450+
"x &= 1",
451+
"~~~~~~ 0.loc"
452+
" ~~ 0.op.loc")
453+
454+
self.assertParsesSuite(
455+
[{'ty': 'AugAssign', 'op': {'ty': 'BitOr'}, 'target': self.ast_x, 'value': self.ast_1}],
456+
"x |= 1",
457+
"~~~~~~ 0.loc"
458+
" ~~ 0.op.loc")
459+
460+
self.assertParsesSuite(
461+
[{'ty': 'AugAssign', 'op': {'ty': 'BitXor'}, 'target': self.ast_x, 'value': self.ast_1}],
462+
"x ^= 1",
463+
"~~~~~~ 0.loc"
464+
" ~~ 0.op.loc")
465+

0 commit comments

Comments
 (0)
Please sign in to comment.