diff --git a/hy/compiler.py b/hy/compiler.py index 575ff35..53d55b3 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -39,26 +39,36 @@ from collections import defaultdict import codecs import ast import sys +import traceback class HyCompileError(HyError): - def __init__(self, exception, - start_line=0, start_column=0): + def __init__(self, exception, traceback=None): self.exception = exception - self.start_line = start_line - self.start_column = start_column + self.traceback = traceback def __str__(self): - if self.start_line == 0: - return("Internal Compiler Bug\n⤷ %s: %s" - % (self.exception.__class__.__name__, - self.exception)) - return ("Compilation error at line %d, column %d\n%s: %s" - % (self.start_line, self.start_column, - self.exception.__class__.__name__, - self.exception)) + if isinstance(self.exception, HyTypeError): + return str(self.exception) + if self.traceback: + tb = "".join(traceback.format_tb(self.traceback)).strip() + else: + tb = "No traceback available. 😟" + return("Internal Compiler Bug 😱\n⤷ %s: %s\nCompilation traceback:\n%s" + % (self.exception.__class__.__name__, + self.exception, tb)) +class HyTypeError(TypeError): + def __init__(self, expression, message): + super(HyTypeError, self).__init__(message) + self.expression = expression + + def __str__(self): + return (self.message + " (line %s, column %d)" + % (self.expression.start_line, + self.expression.start_column)) + _compile_table = {} @@ -87,10 +97,9 @@ def builds(_type): def _raise_wrong_args_number(expression, error): - err = TypeError(error % (expression.pop(0), len(expression))) - err.start_line = expression.start_line - err.start_column = expression.start_column - raise err + raise HyTypeError(expression, + error % (expression.pop(0), + len(expression))) def checkargs(exact=None, min=None, max=None): @@ -134,15 +143,10 @@ class HyASTCompiler(object): # another HyCompileError! raise except Exception as e: - if isinstance(e, HyError): - raise HyCompileError( - exception=e, - start_line=getattr(e, "start_line", 0), - start_column=getattr(e, "start_column", 0)) - raise HyCompileError(exception=e) + raise HyCompileError(e, sys.exc_info()[2]) raise HyCompileError( - "Unknown type - `%s' - %s" % (str(type(tree)), tree)) + Exception("Unknown type: `%s'" % (str(type(tree))))) def _mangle_branch(self, tree, start_line, start_column): # If tree is empty, just return a pass statement @@ -251,13 +255,14 @@ class HyASTCompiler(object): for e in expr: if not len(e): - raise TypeError("Empty list not allowed in `try'") + raise HyTypeError(e, "Empty list not allowed in `try'") if e[0] in (HySymbol("except"), HySymbol("catch")): handlers.append(self.compile(e)) elif e[0] == HySymbol("else"): if orelse: - raise TypeError( + raise HyTypeError( + e, "`try' cannot have more than one `else'") else: orelse = self._code_branch(self.compile(e[1:]), @@ -265,18 +270,20 @@ class HyASTCompiler(object): e.start_column) elif e[0] == HySymbol("finally"): if finalbody: - raise TypeError( + raise HyTypeError( + e, "`try' cannot have more than one `finally'") else: finalbody = self._code_branch(self.compile(e[1:]), e.start_line, e.start_column) else: - raise TypeError("Unknown expression in `try'") + raise HyTypeError(e, "Unknown expression in `try'") # Using (else) without (except) is verboten! if orelse and not handlers: - raise TypeError( + raise HyTypeError( + e, "`try' cannot have `else' without `except'") # (try) or (try BODY) @@ -346,9 +353,11 @@ class HyASTCompiler(object): # or # [] if not isinstance(exceptions, HyList): - raise TypeError("`%s' exceptions list is not a list" % catch) + raise HyTypeError(exceptions, + "`%s' exceptions list is not a list" % catch) if len(exceptions) > 2: - raise TypeError("`%s' exceptions list is too long" % catch) + raise HyTypeError(exceptions, + "`%s' exceptions list is too long" % catch) # [variable [list of exceptions]] # let's pop variable and use it as name @@ -386,7 +395,8 @@ class HyASTCompiler(object): elif isinstance(exceptions_list, HySymbol): _type = self.compile(exceptions_list) else: - raise TypeError("`%s' needs a valid exception list" % catch) + raise HyTypeError(exceptions, + "`%s' needs a valid exception list" % catch) body = self._code_branch([self.compile(x) for x in expr], expr.start_line, @@ -592,7 +602,7 @@ class HyASTCompiler(object): expr.pop(0) # decorate-with fn = self.compile(expr.pop(-1)) if type(fn) != ast.FunctionDef: - raise TypeError("Decorated a non-function") + raise HyTypeError(expr, "Decorated a non-function") fn.decorator_list = [self.compile(x) for x in expr] return fn @@ -603,7 +613,7 @@ class HyASTCompiler(object): args = expr.pop(0) if len(args) > 2 or len(args) < 1: - raise TypeError("with needs [arg (expr)] or [(expr)]") + raise HyTypeError(expr, "with needs [arg (expr)] or [(expr)]") args.reverse() ctx = self.compile(args.pop(0)) @@ -677,7 +687,7 @@ class HyASTCompiler(object): kwargs = expr.pop(0) if type(call) != ast.Call: - raise TypeError("kwapplying a non-call") + raise HyTypeError(expr, "kwapplying a non-call") if type(kwargs) != HyDict: raise TypeError("kwapplying with a non-dict")