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 #from etags import TagManager from ctags import TagManager 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 String3Grammar(Grammar): rules = [ 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 IString3Grammar(Grammar): rules = [ PatternRule('interp', r'\$[a-zA-Z0-9_]+'), PatternRule('escaped', r'\$\$'), PatternRule('data', r'[^$"]+'), ] class IStringGrammar(Grammar): rules = [ PatternRule('interp', r'\$[a-zA-Z0-9_]+'), PatternRule('escaped', r'\$\$'), 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-Z_][a-zA-Z0-9_.#]*'), PatternRule('spaces', ' +'), PatternRule('delimiter', r'(?:;|=>|{|}|\(|\)|,|\.|<(?![a-zA-Z_])|>|:|/|\+|-|\*|=|#)'), PatternRule('scala.annotation', '@[a-zA-Z_][a-zA-Z0-9_.]*'), RegionRule('scala.string', '"', StringGrammar, '"'), ] class ScalaGrammar(Grammar): rules = [ PatternRule('scala.comment', '//.*$'), RegionRule('scala.comment', r'/\*', NestedCommentGrammar, r'\*/'), RegionRule('scala.script', r'#!.+$', ShGrammar, r'!#'), # determine types based on context 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'(?<=[^a-zA-Z0-9_])(new|extends|with)( +)([^{}()0-9:\[\n\t ][^:\[\]{}()\n\t ]*)', 'scala.reserved', 'spaces', 'scala.type'), # class, object, and trait PatternRule('scala.class', '(?<=(?|{|}|\(|\)|,|\.|<(?![a-zA-Z_])|>|:|/|\+|-|\*|=|#)'), # semi-hack to support XML RegionRule('scala.inline', r'(?:^| )(?=<[a-zA-Z_])', XMLGrammar, '^[ \t]*$'), PatternRule('spaces', r'(?:\t| )+'), PatternRule('eol', r'\n'), # these are some constants that show up a lot PatternRule('scala.pseudo', '(?:true|null|false)(?!%s)' % chr2), PatternRule('scala.reserved', '(?:yield|with|while|var|val|type|true|try|trait|throw|this|super|sealed|return|protected|private|package|override|object|null|new|match|macro|lazy|import|implicit|if|forSome|for|finally|final|false|extends|else|do|def|class|catch|case object|case class|case|abstract)(?!%s)' % chr2), PatternRule('scala.float', r'-?[0-9]+\.[0-9]*(?:[eE][0-9]+)?[FfDd]?'), # FIXME PatternRule('scala.float', r'-?(?:0|[1-9])[0-9]*[FfDd]'), # 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', '[a-zA-Z_][a-zA-Z0-9_]*"""', IString3Grammar, '"""+'), RegionRule('scala.string', '[a-zA-Z_][a-zA-Z0-9_]*"', IStringGrammar, '"'), RegionRule('scala.string', '"""', String3Grammar, '"""+'), 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'])} case_tokens = {'scala.reserved': set(['case'])} case_delim_tokens = {'delimiter': set(['=>'])} continue_tokens = {'delimiter': set(['='])} 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']) fixed_indent = True 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 or object, then say ok if tokens[0].fqmatchs('scala.reserved', ('class', 'object')): 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) class ScalaSetClasspath(Method): '''Set Scala's classpath''' args = [arg("lib", dt='path', p="Lib: ", dv=lambda w: '.')] def _execute(self, w, **vargs): w.application.config['scala.cp'] = vargs['lib'].split(':') class ScalaAddClasspath(ScalaSetClasspath): '''Add path(s) to Scala's classpath''' def _execute(self, w, **vargs): w.application.config['scala.cp'].extend(vargs['lib'].split(':')) class ScalaShowClasspath(ScalaSetClasspath): '''Display Scala's classpath''' def _execute(self, w, **vargs): w.set_error(w.application.config.get('scala.cp')) w.application.config['scala.cp'].extend(vargs['lib'].split(':')) class ScalaTagManager(TagManager): lang = 'scala' exts = set(['.scala', '.sbt']) # 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', '.sbt'] tabwidth = 2 tabbercls = ScalaTabber tagcls = ScalaTagManager grammar = ScalaGrammar commentc = '//' actions = [ScalaStart, ScalaDecompile, ScalaSetClasspath, ScalaAddClasspath, ScalaShowClasspath] 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.reserved': hi_cyan, 'scala.pseudo': hi_magenta, 'scala.integer': default, 'scala.float': default, 'scala.bareword': default, 'scala.symbol': hi_orange, 'scala.package': hi_orange, 'scala.class': hi_yellow, 'scala.object': hi_yellow, 'scala.trait': hi_yellow, 'scala.def': hi_blue, 'scala.type': hi_magenta, 'scala.string.interp': hi_yellow, } _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)