diff options
Diffstat (limited to 'lab/parser.py')
| -rw-r--r-- | lab/parser.py | 158 |
1 files changed, 93 insertions, 65 deletions
diff --git a/lab/parser.py b/lab/parser.py index 97c81d8..4deb93e 100644 --- a/lab/parser.py +++ b/lab/parser.py @@ -5,29 +5,29 @@ from __future__ import division -import glob, os, sys import collections -from optparse import OptionParser +import glob +import optparse +import os +import re +import sys +import textwrap import disgen -from coverage.misc import CoverageException -from coverage.parser import ByteParser, PythonParser +from coverage.parser import PythonParser from coverage.python import get_python_source opcode_counts = collections.Counter() + class ParserMain(object): """A main for code parsing experiments.""" def main(self, args): """A main function for trying the code from the command line.""" - parser = OptionParser() - parser.add_option( - "-c", action="store_true", dest="chunks", - help="Show basic block chunks" - ) + parser = optparse.OptionParser() parser.add_option( "-d", action="store_true", dest="dis", help="Disassemble" @@ -69,67 +69,74 @@ class ParserMain(object): for opcode, number in opcode_counts.most_common(): print("{0:20s} {1:6d} {2:.1%}".format(opcode, number, number/total)) - def one_file(self, options, filename): """Process just one file.""" + # `filename` can have a line number suffix. In that case, extract those + # lines, dedent them, and use that. This is for trying test cases + # embedded in the test files. + match = re.search(r"^(.*):(\d+)-(\d+)$", filename) + if match: + filename, start, end = match.groups() + start, end = int(start), int(end) + else: + start = end = None try: text = get_python_source(filename) - bp = ByteParser(text, filename=filename) + if start is not None: + lines = text.splitlines(True) + text = textwrap.dedent("".join(lines[start-1:end]).replace("\\\\", "\\")) + pyparser = PythonParser(text, filename=filename, exclude=r"no\s*cover") + pyparser.parse_source() except Exception as err: print("%s" % (err,)) return if options.dis: print("Main code:") - self.disassemble(bp, histogram=options.histogram) + self.disassemble(pyparser.byte_parser, histogram=options.histogram) - arcs = bp._all_arcs() - if options.chunks:# and not options.dis: - chunks = bp._all_chunks() - if options.recursive: - print("%6d: %s" % (len(chunks), filename)) - else: - print("Chunks: %r" % chunks) - print("Arcs: %r" % sorted(arcs)) + arcs = pyparser.arcs() if options.source or options.tokens: - cp = PythonParser(filename=filename, exclude=r"no\s*cover") - cp.show_tokens = options.tokens - cp._raw_parse() + pyparser.show_tokens = options.tokens + pyparser.parse_source() if options.source: - if options.chunks: - arc_width, arc_chars = self.arc_ascii_art(arcs) - else: - arc_width, arc_chars = 0, {} - - exit_counts = cp.exit_counts() - - for lineno, ltext in enumerate(cp.lines, start=1): - m0 = m1 = m2 = m3 = a = ' ' - if lineno in cp.statement_starts: - m0 = '-' + arc_chars = self.arc_ascii_art(arcs) + if arc_chars: + arc_width = max(len(a) for a in arc_chars.values()) + + exit_counts = pyparser.exit_counts() + + for lineno, ltext in enumerate(pyparser.lines, start=1): + marks = [' ', ' ', ' ', ' ', ' '] + a = ' ' + if lineno in pyparser.raw_statements: + marks[0] = '-' + if lineno in pyparser.statements: + marks[1] = '=' exits = exit_counts.get(lineno, 0) if exits > 1: - m1 = str(exits) - if lineno in cp.docstrings: - m2 = '"' - if lineno in cp.classdefs: - m2 = 'C' - if lineno in cp.excluded: - m3 = 'x' - a = arc_chars[lineno].ljust(arc_width) - print("%4d %s%s%s%s%s %s" % - (lineno, m0, m1, m2, m3, a, ltext) - ) + marks[2] = str(exits) + if lineno in pyparser.raw_docstrings: + marks[3] = '"' + if lineno in pyparser.raw_classdefs: + marks[3] = 'C' + if lineno in pyparser.raw_excluded: + marks[4] = 'x' + + if arc_chars: + a = arc_chars[lineno].ljust(arc_width) + else: + a = "" + + print("%4d %s%s %s" % (lineno, "".join(marks), a, ltext)) def disassemble(self, byte_parser, histogram=False): """Disassemble code, for ad-hoc experimenting.""" for bp in byte_parser.child_parsers(): - chunks = bp._split_into_chunks() - chunkd = dict((chunk.byte, chunk) for chunk in chunks) if bp.text: srclines = bp.text.splitlines() else: @@ -149,22 +156,19 @@ class ParserMain(object): elif disline.offset > 0: print("") line = disgen.format_dis_line(disline) - chunk = chunkd.get(disline.offset) - if chunk: - chunkstr = ":: %r" % chunk - else: - chunkstr = "" - print("%-70s%s" % (line, chunkstr)) + print("%-70s" % (line,)) print("") def arc_ascii_art(self, arcs): """Draw arcs as ascii art. - Returns a width of characters needed to draw all the arcs, and a - dictionary mapping line numbers to ascii strings to draw for that line. + Returns a dictionary mapping line numbers to ascii strings to draw for + that line. """ + + plus_ones = set() arc_chars = collections.defaultdict(str) for lfrom, lto in sorted(arcs): if lfrom < 0: @@ -173,13 +177,13 @@ class ParserMain(object): arc_chars[lfrom] += '^' else: if lfrom == lto - 1: - # Don't show obvious arcs. + plus_ones.add(lfrom) continue if lfrom < lto: l1, l2 = lfrom, lto else: l1, l2 = lto, lfrom - w = max(len(arc_chars[l]) for l in range(l1, l2+1)) + w = first_all_blanks(arc_chars[l] for l in range(l1, l2+1)) for l in range(l1, l2+1): if l == lfrom: ch = '<' @@ -187,16 +191,40 @@ class ParserMain(object): ch = '>' else: ch = '|' - arc_chars[l] = arc_chars[l].ljust(w) + ch - arc_width = 0 + arc_chars[l] = set_char(arc_chars[l], w, ch) - if arc_chars: - arc_width = max(len(a) for a in arc_chars.values()) - else: - arc_width = 0 + # Add the plusses as the first character + for lineno, arcs in arc_chars.items(): + arc_chars[lineno] = ( + ("+" if lineno in plus_ones else " ") + + arcs + ) + + return arc_chars + + +def set_char(s, n, c): + """Set the nth char of s to be c, extending s if needed.""" + s = s.ljust(n) + return s[:n] + c + s[n+1:] + + +def blanks(s): + """Return the set of positions where s is blank.""" + return set(i for i, c in enumerate(s) if c == " ") + + +def first_all_blanks(ss): + """Find the first position that is all blank in the strings ss.""" + ss = list(ss) + blankss = blanks(ss[0]) + for s in ss[1:]: + blankss &= blanks(s) + if blankss: + return min(blankss) + else: + return max(len(s) for s in ss) - return arc_width, arc_chars if __name__ == '__main__': ParserMain().main(sys.argv[1:]) - |
