From 87e96d3ed180006a8bd3f0737a20a2e5dff2d77c Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Thu, 15 Dec 2011 23:51:11 -0500 Subject: [PATCH] javap is work-in-progress --HG-- branch : pmacs2 --- application.py | 2 +- mode/javap.py | 127 +++++++++++++++++++++ mode/scala.py | 304 +++++++++++++++++++++++++------------------------ 3 files changed, 285 insertions(+), 148 deletions(-) create mode 100644 mode/javap.py diff --git a/application.py b/application.py index acb3893..62467ad 100755 --- a/application.py +++ b/application.py @@ -170,7 +170,7 @@ class Application(object): mode.install(self) names = ( 'blame', 'c', 'console', 'consolemini', 'css', 'diff', 'dir', - 'elisp', 'hex', 'html', 'java', 'javascript', 'lisp', 'make', + 'elisp', 'hex', 'html', 'java', 'javap', 'javascript', 'lisp', 'make', 'mini', 'mutt', 'nasm', 'ocaml', 'perl', 'python', 'replace', 'rst', 'scheme', 'search', 'sh', 'sql', 'tt', 'text', 'text2', 'which', 'xml', 'cheetah', 'colortext', 'latex', 'insertmini', 'conf', diff --git a/mode/javap.py b/mode/javap.py new file mode 100644 index 0000000..c82bd52 --- /dev/null +++ b/mode/javap.py @@ -0,0 +1,127 @@ +from mode import Fundamental +from lex import Grammar, PatternRule, RegionRule, PatternMatchRule + +name = '[a-zA-Z0-9_$.]+' + +class JavapGrammar(Grammar): + rules = [ + #PatternMatchRule('x', r'^( )(\d+:)([ \t]+)([a-z0-9_]+)', + # 'spaces', 'javap.linenum', 'spaces', 'javap.ins'), + #PatternMatchRule('x', r'^( )(\d+:)([ \t]+)([a-z0-9_]+)', + # 'spaces', 'javap.linenum', 'spaces', 'javap.ins'), + #PatternMatchRule('x', r'^( )(\d+:)([ \t]+)([a-z0-9_]+)', + # 'spaces', 'javap.linenum', 'spaces', 'javap.ins'), + + PatternMatchRule('x', '^(package)( )(.+)$', + 'javap.keyword', 'spaces', 'javap.package'), + + PatternRule('javap.keyword', '(?:static|public|protected|private|final|abstract)'), + PatternMatchRule('x', '(--class)( +)([^ ]+)', + 'javap.pseudo', 'spaces', 'javap.type'), + PatternMatchRule('x', '(--field)( +)([^ ]+)( +)([^ ]+)', + 'javap.pseudo', 'spaces', 'javap.type', 'spaces', 'javap.type'), + PatternRule('javap.pseudo', '--(?:method|iface|string|field)'), + + PatternMatchRule('x', '([^ ]+)( +)(\()', + 'javap.type', 'spaces', 'javap.delim'), + + PatternMatchRule('x', '(class|extends|implements)( )([^ ]+)', + 'javap.keyword', 'spaces', 'javap.type'), + + PatternMatchRule('x', '([^ ]+)( )([^ ]+)( )(\()', + 'javap.type', 'spaces', 'javap.method', 'spaces', 'javap.delim'), + PatternMatchRule('x', '([^ ]+)( )([^ ]+)( )({)(})', + 'javap.type', 'spaces', 'javap.method', 'spaces', 'javap.delim', 'javap.delim'), + PatternMatchRule('x', '([^ ]+)(,)( )', + 'javap.type', 'javap.delim', 'spaces'), + PatternMatchRule('x', '([^ ]+)(\))', + 'javap.type', 'javap.delim'), + + #PatternMatchRule('x', '(//class )(.+)', 'javap.zcomment', 'javap.class'), + #PatternMatchRule('x', '(//InterfaceMethod )([^:]+)(:)(.+)', + # 'javap.zcomment', 'javap.method', 'javap.zcomment', 'javap.sig'), + #PatternMatchRule('x', '(//Method )([^:]+)(:)(.+)', + # 'javap.zcomment', 'javap.method', 'javap.zcomment', 'javap.sig'), + + PatternMatchRule('x', '^( +)(\d+)( +)([^ ]+)', + 'spaces', 'javap.byte', 'spaces', 'javap.ins'), + PatternRule('javap.const', r'#\d+'), + PatternRule('javap.int', r'\d+'), + + #PatternRule('javap.zcomment', r'^Compiled from .*$'), + #PatternRule('javap.zcomment', r'Code:'), + + #PatternMatchRule('x', r'(class)( )(' + name + ')', 'javap.zkeyword', 'spaces', 'javap.class'), + #PatternMatchRule('x', r'(extends)( )(' + name + ')', 'javap.zkeyword', 'spaces', 'javap.class'), + #PatternMatchRule('x', r'(public)( )(static)( )(' + name + ')( )(' + name + ')', + # 'javap.zkeyword', 'spaces', 'javap.zkeyword', 'spaces', + # 'javap.class', 'spaces', 'javap.method'), + + #PatternRule(r'javap.zkeyword', r'(?:static|public|protected|private|extends|class|abstract)'), + #PatternRule(r'javap.cls', r'L[^;]+'), + #PatternRule(r'javap.cls2', r'B|C|D|F|I|J|S|Z|\['), + #PatternRule(r'javap.ins', r'(?<= )\d+:'), + #PatternRule(r'javap.class', name), + + PatternRule('spaces', r' +'), + PatternRule('eol', r'\n'), + ] + + +# white is for delimiters, operators, numbers +c_default = ('default', 'default') + +# magenta is for keywords/builtins, translation, globs +lo_magenta = ('magenta202', 'default') +hi_magenta = ('magenta505', 'default') + +# red is for comments, pods, endblocks +lo_red = ('red300', 'default') +hi_red = ('red511', 'default') + +# orange are for arrays and hashes +hi_orange = ('yellow531', 'default') +lo_orange = ('yellow520', 'default') + +# yellow is for scalars and prototypes +hi_yellow = ('yellow551', 'default') +lo_yellow = ('yellow330', 'default') + +# green is for strings and hash keys +lo_green = ('green030', 'default') +hi_green = ('green050', 'default') + +# cyan is for quotes, evals, regexes, subs +lo_cyan = ('cyan033', 'default') +hi_cyan = ('cyan155', 'default') + +# blue is unused +lo_blue = ('blue113', 'default') +hi_blue = ('blue225', 'default') + +class Javap(Fundamental): + name = 'Javap' + grammar = JavapGrammar() + colors = { + ###'javap.string': ('green', 'default'), + ##'javap.label': ('blue', 'default', 'bold'), + #'javap.class': ('cyan', 'default', 'bold'), + 'javap.package': hi_yellow, + 'javap.type': hi_cyan, + 'javap.method': hi_blue, + ##'javap.abbrev': ('green', 'default', 'bold'), + #'javap.name': ('yellow', 'default', 'bold'), + #'javap.linenum': ('green', 'default', 'bold'), + 'javap.byte': lo_orange, + 'javap.ins': hi_orange, + #'javap.cls': ('cyan', 'default', 'bold'), + #'javap.int': ('green', 'default', 'bold'), + 'javap.const': hi_yellow, + 'javap.keyword': hi_magenta, + 'javap.pseudo': hi_magenta, + } + _bindings = { + 'ipython-start': ('M-e',), + } + +install = Javap.install diff --git a/mode/scala.py b/mode/scala.py index e9efc67..a98e075 100644 --- a/mode/scala.py +++ b/mode/scala.py @@ -10,6 +10,7 @@ import default import urllib2 import os import re +from subprocess import Popen, PIPE, STDOUT chr1 = '[a-zA-Z_]' chr2 = '[a-zA-Z_0-9]' @@ -131,164 +132,172 @@ class ScalaStart(Interact): def _execute(self, w, **vargs): Interact._execute(self, w, bname='*Scala*', cmd='scala') -class ScalaDocBrowse(Method): - def _execute(self, w, **vargs): - a = w.application - url = a.config['scala.api'] - Interact().execute(w, bname='*Scala-Doc*', cmd='links "%s"' % url) +class ScalaDecompile(Method): + args = [arg('classname', t='string', p='Class: ', h='The class to decompile')] -class ScalaDocLookup(Method): - args = [arg('name', t='string', p='Name: ', dv=default.current_word, - ld=True, h='The Scala name to get help on')] - def _get_path(self, w, name): - return w.application.getpath('cache', 'scala', name) - def _get_url(self, w, name, url): - path = self._get_path(w, name) - w.application.mkdirs('cache', 'scala') + ins_re = re.compile(r'^(\d+):') - if not os.path.exists(path): - open(path, 'w').write(urllib2.urlopen(url).read()) + def _abbrev(self, s): + s = s.replace('/', '.') + s = s.replace('java.lang.', '') + s = s.replace('scala.', '') + if self.pkg is not None: + s = s.replace(self.pkg, '') + return s - html = open(path, 'r').read() - return html + pkg = None + obj_re = re.compile(r'^.+ (?:class|interface) ([^ ]+) ') + def _parse_obj(self, line): + s = self._abbrev(line[:-1]) + m = self.obj_re.match(line) + obj = m.group(1) + toks = obj.split('.') - def _execute(self, w, **vargs): - try: - from BeautifulSoup import BeautifulSoup - except ImportError: - w.set_error('BeautifulSoup is not installed...') - return - - a = w.application - name = vargs.get('name') - html = self._get_url(w, 'api.html', a.config['scala.api']) - soup = BeautifulSoup(html) - - tags = soup.findAll('li') - for li in tags: - if li['title'].endswith(name): - frag = li.contents[0]['href'] - url2 = a.config['scala.api-base'] + '/' + frag - a.run_external('links', url2) - return - - w.set_error('error looking up %s...' % name) - -class ScalaXRayBase(Method): - _is_method = False - pkg_re = re.compile('^package (.+)$') - def find_pkg(self, w): - for line in w.buffer.lines: - m = self.pkg_re.match(line) - if m: - return m.group(1) - raise Exception("no package found") - def get_sxr_dir(self, w): - # TODO: fixme fixme - path = 'target/scala_2.8.0/classes.sxr' - if os.path.exists(path): - return path + if len(toks) > 1: + self.pkg = '.'.join(toks[:-1]) + '.' else: - raise Exception("classes.sxr not found") + self.pkg = None - def get_pkg_tags(self, w, pkg, base): - return os.path.join(base, pkg.replace('.', '/') + '.scala.txt') - def get_public_tags(self, w, base): - return os.path.join(base, 'public-tags') + return self._abbrev(line[:-1]) - def term_def_lookup(self, w): - pkg = self.find_pkg(w) - base = self.get_sxr_dir(w) - tags = self.get_public_tags + def _parse_method(self, line): + return self._abbrev(line[:-1]).replace("(", " (") - def type_def_lookup(self, t, w): - base = self.get_sxr_dir(w) - tags = self.get_public_tags(w, base) - f = open(tags, 'r') - for line in f: - toks = line.split() - if toks[0] != 'type': continue - if toks[1] != t: continue - path = toks[2] - n = int(toks[4]) - return (path, n) - return (None, None) + prims = { + 'B': 'byte', + 'C': 'char', + 'D': 'double', + 'F': 'float', + 'I': 'int', + 'J': 'long', + 'S': 'short', + 'V': 'void', + 'Z': 'bool', + } + def _parse_args(self, line): + args = [] + i = 0 + arr = "" + while i < len(line): + if line[i] in self.prims: + args.append(self.prims[line[i]] + arr) + arr = "" + elif line[i] == "[": + arr += "[]" + elif line[i] == "L": + j = i + 1 + while line[j] != ';': j += 1 + args.append(self._abbrev(line[i + 1:j]) + arr) + arr = "" + i = j + else: + raise Exception("huh? saw %r (%r)" % (line[i:], line)) + i += 1 + return args - def type_lookup(self, n, w): - pkg = self.find_pkg(w) - base = self.get_sxr_dir(w) - path = w.buffer.path + typs = { + 'InterfaceMethod': 'iface ', + 'Method': 'method', + } - r = re.compile('^.*src/main/scala/(.+)$') - m = r.match(path) - tags = os.path.join(base, m.group(1) + '.txt') + field_re = re.compile('^Field\s+([^:]+):(.+)$') + sig_re = re.compile('^(\w+)\s+([^:]+):\(([^)]*)\)(.+)$') + def _parse_sig(self, line): + if line.startswith('class'): + _, name = line.split() + return "class " + self._abbrev(name) - f = open(tags, 'r') - for line in f: - toks = line.split('\t') - i = int(toks[0]) - j = int(toks[1]) - if i <= n and n <= j: - return toks[2] - return None + elif line.startswith('String'): + return 'string' -class ScalaGetType(ScalaXRayBase): - type_re = re.compile('(?:[^ \[\]{}()\.,;:]+\.)*([^ \[\]{}()\.,;:]+)') - ret_re = re.compile('\)(?=[a-zA-Z0-9_])') - def _execute(self, w, **vargs): - word = w.get_token().string - if word is None or word.strip() == "": - w.set_error('no word selected') - return + elif line.startswith('Field'): + m = self.field_re.match(line) + if not m: raise Exception("failed to match %r" % line) + name = self._abbrev(m.group(1)) + result = self._parse_args(m.group(2))[0] + #return "field %s -> %s" % (name, result) + return "field %s %s" % (result, name) - n = w.cursor_byte_offset() - t = self.type_lookup(n, w) - if not t: - w.set_error('%s has unknown type' % word) - return - - if w.application.config['scala.type-abbrev']: - t = self.type_re.sub(lambda m: m.group(1), t) - #t = t.replace(')', ') => ') - t = self.ret_re.sub(') => ', t) - t = t.replace(': ', ':') - t = t.replace(',', ', ') - w.set_error(t) - -class ScalaGotoDefinition(ScalaXRayBase): - generic_re = re.compile('\[.+\]') - def _execute(self, w, **vargs): - word = w.get_token().string - if word is None or word.strip() == "": - w.set_error('no word selected') - return - - n = w.cursor_byte_offset() - t = self.type_lookup(n, w) - if not t: - w.set_error('%s has unknown type' % word) - return - - # remove generics - t = self.generic_re.sub('', t) - - path, n = self.type_def_lookup(t, w) - if not path: - w.set_error('%s not found' % t) - return - - b = w.buffer - a = w.application - b2 = a.open_path(path) - a.switch_buffer(b2) - - if b == b2: - a.methods['goto-char'].execute(w, charno=n - 1) - a.methods['center-view'].execute(w) else: - a.methods['goto-char'].execute(b2.windows[0], charno=n - 1) - a.methods['center-view'].execute(b2.windows[0]) - w.set_error('opening %s...' % path) + m = self.sig_re.match(line) + if not m: raise Exception("failed to match %r" % line) + typ = self.typs[m.group(1)] + name = self._abbrev(m.group(2)) + + args = self._parse_args(m.group(3)) + result = self._parse_args(m.group(4))[0] + + #return "%s %s (%s) -> %s" % (typ, name, ', '.join(args), result) + return "%s %s %s (%s)" % (typ, result, name, ', '.join(args)) + + def _parse_instruction(self, line): + if '//' in line: + real, x = line.split('//') + sig = self._parse_sig(x) + else: + real, sig = line, None + + toks = real.split() + n = toks[0][:-1] + ins = toks[1] + rest = [t[:-1] for t in toks[2:]] + args = " ".join(rest) + + if sig: + #return "%4s %-22s -- %s" % (n, ins + " " + args, sig) + return "%4s %-22s --%s" % (n, ins + " " + args, sig) + else: + return "%4s %-22s" % (n, ins + " " + args) + + def _execute(self, w, **vargs): + cp = 'target/scala-2.9.1.final/classes' #FIXME + argv = ['javap', '-classpath', cp, '-c', vargs['classname']] + + p = Popen(argv, stdout=PIPE, stderr=STDOUT) + lines = p.stdout.readlines() + + obj = None + methods = [] + curr = None + instructions = [] + + for i, line in enumerate(lines): + line = line.strip() + if i == 0: + continue + elif i == 1: + obj = self._parse_obj(line) + elif line == "Code:": + continue + elif line == "": + if curr: + methods.append((curr, instructions)) + curr = None + instructions = [] + elif self.ins_re.match(line): + instructions.append(self._parse_instruction(line)) + else: + curr = self._parse_method(line) + + outlines = [] + if self.pkg: + outlines.append('package ' + self.pkg[:-1]) + outlines.append('') + outlines.append(obj + " {") + for method, instructions in methods: + if instructions: + outlines.append(" " + method + " {") + for ins in instructions: + outlines.append(" " + ins) + outlines.append(" }") + else: + outlines.append(" " + method + " {}") + outlines.append("}") + + output = "\n".join(outlines) + + #w.application.data_buffer("*Javap*", output, switch_to=True) + w.application.data_buffer("*Javap*", output, modename='javap', switch_to=True) # white is for delimiters, operators, numbers default = ('default', 'default') @@ -328,7 +337,8 @@ class Scala(Fundamental): tabbercls = ScalaTabber grammar = ScalaGrammar commentc = '//' - actions = [ScalaStart, ScalaDocBrowse, ScalaDocLookup, ScalaGetType, ScalaGotoDefinition] + #actions = [ScalaStart, ScalaDocBrowse, ScalaDocLookup, ScalaGetType, ScalaGotoDefinition] + actions = [ScalaStart, ScalaDecompile] opentokens = ('delimiter', 'sub.start', 'sub.sub.start', 'sub.sub.sub.start') opentags = {'(': ')', '[': ']', '{': '}'} closetokens = ('delimiter', 'sub.end', 'sub.sub.end', 'sub.sub.sub.end') @@ -365,8 +375,8 @@ class Scala(Fundamental): } _bindings = { - 'scala-get-type': ('M-,',), - 'scala-goto-definition': ('C-c ,',), + #'scala-get-type': ('M-,',), + #'scala-goto-definition': ('C-c ,',), 'close-paren': (')',), 'close-brace': ('}',), 'close-bracket': (']',),