408 lines
14 KiB
Python
408 lines
14 KiB
Python
from tab import StackTabber2
|
|
from mode import Fundamental
|
|
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule
|
|
from mode.sh import ShGrammar
|
|
from mode.xml import XMLGrammar
|
|
from mode.pipe import Pipe
|
|
from method.shell import Interact
|
|
from method import Method, arg
|
|
import default
|
|
import urllib2
|
|
import os
|
|
import re
|
|
from subprocess import Popen, PIPE, STDOUT
|
|
|
|
chr1 = '[a-zA-Z_]'
|
|
chr2 = '[a-zA-Z_0-9]'
|
|
word = chr1 + chr2 + '*'
|
|
|
|
class NestedCommentGrammar(Grammar): pass
|
|
NestedCommentGrammar.rules = [
|
|
RegionRule('comment', r'/\*', NestedCommentGrammar, r'\*/'),
|
|
PatternRule('data', r'(?:[^\*]|\*(?!/))+'),
|
|
]
|
|
|
|
class StringGrammar(Grammar):
|
|
rules = [
|
|
PatternRule('escaped', r"\\u[0-9A-Fa-f]{4}|\\[0-7]{1,3}|\\[btnfr\"'\\]"),
|
|
PatternRule('data', r'[^\\"]+'),
|
|
]
|
|
|
|
class SubTypeGrammar(Grammar): pass
|
|
SubTypeGrammar.rules = [
|
|
RegionRule('sub', r'\[', SubTypeGrammar, r'\]'),
|
|
#PatternRule('scala.type', '(?:[a-zA-Z0-9_.]+| *=> *)+'),
|
|
PatternRule('scala.type', '[A-Z][a-zA-Z0-9_.]*'),
|
|
PatternRule('spaces', ' +'),
|
|
PatternRule('scala.annotation', '@[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
]
|
|
|
|
class ScalaGrammar(Grammar):
|
|
rules = [
|
|
PatternRule('scala.comment', '//.*$'),
|
|
RegionRule('scala.comment', r'/\*', NestedCommentGrammar, r'\*/'),
|
|
RegionRule('scala.script', r'#!.+$', ShGrammar, r'!#'),
|
|
|
|
#PatternMatchRule('x', r'(?<!:)(:)((?:[a-zA-Z0-9_.]+| *=> *)+)',
|
|
# 'delimiter', 'scala.type'),
|
|
#PatternMatchRule('x', r'(?<!:)(:)( +)((?:[a-zA-Z0-9_.]+| *=> *)+)',
|
|
# 'delimiter', 'spaces', 'scala.type'),
|
|
PatternMatchRule('x', r'(?<=[a-zA-Z0-9_ ])(:)([a-zA-Z0-9_]+)',
|
|
'delimiter', 'scala.type'),
|
|
PatternMatchRule('x', r'(?<=[a-zA-Z0-9_ ])(:)( +)([a-zA-Z0-9_]+)',
|
|
'delimiter', 'spaces', 'scala.type'),
|
|
|
|
#PatternMatchRule('x', r'(extends|with|new)( +)([a-zA-Z0-9_.]+)',
|
|
# 'scala.reserved', 'spaces', 'scala.type'),
|
|
#PatternMatchRule('x', r'(with)( +)([a-zA-Z0-9_.]+)',
|
|
# 'scala.reserved', 'spaces', 'scala.type'),
|
|
#PatternMatchRule('x', r'(new)( +)([a-zA-Z0-9_.]+)',
|
|
# 'scala.reserved', 'spaces', 'scala.type'),
|
|
|
|
PatternRule('scala.def', '(?<=(?<![a-zA-Z0-9_])def )[^0-9:\[\( ][^:\[\( ]*'),
|
|
|
|
#PatternRule('delimiter', r'(?:;|{|}|\[|\]|\(|\)|,|\.|<(?![a-zA-Z_])|>|:|/|\+|-|\*|=)'),
|
|
RegionRule('sub', r'(?<=:)\(', SubTypeGrammar, r'\)'),
|
|
PatternRule('delimiter', r'(?:;|{|}|\(|\)|,|\.|<(?![a-zA-Z_])|>|:|/|\+|-|\*|=)'),
|
|
RegionRule('sub', r'\[', SubTypeGrammar, r'\]'),
|
|
|
|
RegionRule('scala.inline', r'(?:^| )(?=<[a-zA-Z_])', XMLGrammar, '^[ \t]*$'),
|
|
PatternRule('spaces', r'(?:\t| )+'),
|
|
PatternRule('eol', r'\n'),
|
|
|
|
#PatternRule('scala.def', '(?<=(?<![a-zA-Z0-9_])def )[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
#PatternRule('scala.def', '(?<=(?<![a-zA-Z0-9_])def )[^0-9\[\( ][^\[\( ]*'),
|
|
|
|
PatternRule('scala.class', '(?<=(?<![a-zA-Z0-9_])class )[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
PatternRule('scala.object', '(?<=(?<![a-zA-Z0-9_])object )[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
PatternRule('scala.trait', '(?<=(?<![a-zA-Z0-9_])trait )[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
|
|
PatternRule('scala.pseudo', '(?:true|null|false)(?!%s)' % word),
|
|
|
|
PatternRule('scala.reserved', '(?:yield|with|while|var|val|until|type|true|try|trait|throw|to|this|super|sealed|return|protected|private|package|override|object|null|new|match|lazy|import|implicit|if|forSome|for|finally|final|false|extends|else|do|def|class|catch|case|abstract)(?!%s)' % word),
|
|
|
|
PatternRule('scala.float', r'-?[0-9]+\.[0-9]*[Ff]?'), # FIXME
|
|
|
|
PatternRule('scala.integer', '-?(?:0|[1-9])[0-9]*[Ll]?'),
|
|
PatternRule('scala.integer', '-?0x[0-9A-Fa-f]+[Ll]?'),
|
|
PatternRule('scala.integer', '-?0[0-7]+[Ll]?'),
|
|
|
|
PatternRule('scala.char', r"'(?:[^'\\]|\\u[0-9A-Fa-f]{4}|\\[0-7]{1,3}|\\[btnfr\"'\\])'"),
|
|
RegionRule('scala.string', '"""', Grammar, '"""'),
|
|
RegionRule('scala.string', '"', StringGrammar, '"'),
|
|
PatternRule('scala.symbol', "'[a-zA-Z_][a-zA-Z0-9_]*"),
|
|
|
|
PatternRule('scala.annotation', '@[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
PatternRule('scala.type', '[A-Z][a-zA-Z0-9_]*'),
|
|
PatternRule('scala.bareword', '[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
]
|
|
|
|
class ScalaTabber(StackTabber2):
|
|
open_tokens = {'delimiter': {'{': '}', '(': ')'},
|
|
'sub.start': {'[': ']'}}
|
|
close_tokens = {'delimiter': {'}': '{', ')': '('},
|
|
'sub.end': {']': '['}}
|
|
control_tokens = {'scala.reserved': set(('if', 'else', 'while', 'do', 'for'))}
|
|
end_at_eof = True
|
|
start_free_tokens = {'string.start': 'string.end'}
|
|
end_free_tokens = {'string.end': 'string.start'}
|
|
is_ignored_tokens = set(('spaces', 'eol', 'comment', 'comment.start',
|
|
'comment.data', 'comment.null', 'comment.end'))
|
|
is_indent_tokens = set(('spaces',))
|
|
def _is_base(self, y):
|
|
# the first line is always safe
|
|
if y == 0: return True
|
|
|
|
# if there are no tokens we don't really have any info
|
|
tokens = self._get_tokens(y)
|
|
if not tokens: return False
|
|
|
|
# if it looks like a top-level class, object or function, then say ok
|
|
t = tokens[0]
|
|
if t.fqmatchs('scala.reserved', ('class', 'object', 'def')):
|
|
return True
|
|
|
|
# the default is to assume no
|
|
return False
|
|
|
|
class ScalaStart(Interact):
|
|
args = []
|
|
modename = 'scalapipe'
|
|
reuse = True
|
|
def _execute(self, w, **vargs):
|
|
Interact._execute(self, w, bname='*Scala*', cmd='scala')
|
|
|
|
class ScalaDecompile(Method):
|
|
args = [arg('classname', t='string', p='Class: ', h='The class to decompile')]
|
|
|
|
ins_re = re.compile(r'^(\d+):')
|
|
|
|
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
|
|
|
|
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('.')
|
|
|
|
if len(toks) > 1:
|
|
self.pkg = '.'.join(toks[:-1]) + '.'
|
|
else:
|
|
self.pkg = None
|
|
|
|
return self._abbrev(line[:-1])
|
|
|
|
def _parse_method(self, line):
|
|
return self._abbrev(line[:-1]).replace("(", " (")
|
|
|
|
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
|
|
|
|
typs = {
|
|
'InterfaceMethod': 'iface ',
|
|
'Method': 'method',
|
|
}
|
|
|
|
field_re = re.compile('^Field\s+([^:]+):(.+)$')
|
|
long_re = re.compile('^long\s+(\d+)[lL]$')
|
|
double_re = re.compile('^double\s+([\d.]+)[dD]$')
|
|
sig_re = re.compile('^(\w+)\s+([^:]+):\(([^)]*)\)(.+)$')
|
|
def _parse_sig(self, line):
|
|
if line.startswith('class'):
|
|
_, name = line.split()
|
|
return "class " + self._abbrev(name)
|
|
|
|
elif line.startswith('String'):
|
|
return 'string "%s"' % line[7:]
|
|
|
|
elif line.startswith('Field'):
|
|
m = self.field_re.match(line)
|
|
if not m: raise Exception("failed to match (1) %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)
|
|
|
|
elif line.startswith('int'):
|
|
return 'int %s' % line[4:]
|
|
|
|
elif line.startswith('long'):
|
|
return 'long %s' % line[5:]
|
|
|
|
elif line.startswith('float'):
|
|
return 'double %s' % line[6:]
|
|
|
|
elif line.startswith('double'):
|
|
return 'double %s' % line[7:]
|
|
|
|
else:
|
|
m = self.sig_re.match(line)
|
|
if not m: raise Exception("failed to match (3) %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.strip())
|
|
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):
|
|
clsname = vargs['classname']
|
|
cp = '.:target/scala-2.9.1.final/classes' #FIXME
|
|
argv = ['javap', '-classpath', cp, '-c', '-private', clsname]
|
|
|
|
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 == "" or line == "}":
|
|
if curr:
|
|
methods.append((curr, instructions))
|
|
curr = None
|
|
instructions = []
|
|
elif self.ins_re.match(line):
|
|
instructions.append(self._parse_instruction(line))
|
|
else:
|
|
import sys
|
|
curr = self._parse_method(line)
|
|
|
|
outlines = []
|
|
if self.pkg:
|
|
outlines.append('package ' + self.pkg[:-1])
|
|
outlines.append('')
|
|
outlines.append(str(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)
|
|
|
|
name = "*Javap:%s*" % clsname
|
|
w.application.data_buffer(name, output, modename='javap', switch_to=True)
|
|
|
|
# white is for delimiters, operators, numbers
|
|
default = ('default', 'default')
|
|
|
|
# magenta is for keywords/builtins
|
|
lo_magenta = ('magenta202', 'default')
|
|
hi_magenta = ('magenta414', 'default')
|
|
|
|
# red is for comments
|
|
lo_red = ('red300', 'default')
|
|
hi_red = ('red511', 'default')
|
|
|
|
# orange is for macro definitions, headers and constants
|
|
hi_orange = ('yellow531', 'default')
|
|
lo_orange = ('yellow520', 'default')
|
|
|
|
# yellow is for parts of macros
|
|
hi_yellow = ('yellow551', 'default')
|
|
lo_yellow = ('yellow330', 'default')
|
|
|
|
# green is for strings and characters
|
|
lo_green = ('green030', 'default')
|
|
hi_green = ('green050', 'default')
|
|
|
|
# cyan is for types
|
|
lo_cyan = ('cyan033', 'default')
|
|
hi_cyan = ('cyan155', 'default')
|
|
|
|
# blue is definitions, functions and some macros
|
|
lo_blue = ('blue113', 'default')
|
|
hi_blue = ('blue225', 'default')
|
|
|
|
class Scala(Fundamental):
|
|
name = 'Scala'
|
|
extensions = ['.scala']
|
|
tabwidth = 2
|
|
tabbercls = ScalaTabber
|
|
grammar = ScalaGrammar
|
|
commentc = '//'
|
|
#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')
|
|
closetags = {')': '(', ']': '[', '}': '{'}
|
|
config = {
|
|
'scala.api': 'http://www.scala-lang.org/api/current/allclasses.html',
|
|
'scala.api-base': 'http://www.scala-lang.org/api/current',
|
|
'scala.type-abbrev': True,
|
|
}
|
|
|
|
colors = {
|
|
'scala.script.start': hi_red,
|
|
'scala.script.end': hi_red,
|
|
|
|
'scala.annotation': lo_orange,
|
|
'scala.pseudo': hi_magenta,
|
|
|
|
'scala.reserved': hi_cyan,
|
|
|
|
'scala.integer': default,
|
|
'scala.float': default,
|
|
'scala.bareword': default,
|
|
|
|
'scala.symbol': hi_orange,
|
|
|
|
'scala.class': hi_yellow,
|
|
'scala.object': hi_yellow,
|
|
'scala.trait': hi_yellow,
|
|
|
|
'scala.type': hi_magenta,
|
|
|
|
'scala.def': hi_blue,
|
|
'scala.def': hi_blue,
|
|
}
|
|
|
|
_bindings = {
|
|
#'scala-get-type': ('M-,',),
|
|
#'scala-goto-definition': ('C-c ,',),
|
|
'close-paren': (')',),
|
|
'close-brace': ('}',),
|
|
'close-bracket': (']',),
|
|
}
|
|
|
|
class ScalaPipe(Pipe):
|
|
name = 'scalapipe'
|
|
grammar = ScalaGrammar
|
|
|
|
def install(*args):
|
|
Scala.install(*args)
|
|
ScalaPipe.install(*args)
|