Commit 84a03ffc authored by Tom Niget's avatar Tom Niget

Continue work on error display

parent e65a0e3f
def f(x):
return x
if __name__ == "__main__":
y = f(f)
\ No newline at end of file
......@@ -3,22 +3,29 @@ import ast
import builtins
import inspect
import os
os.environ["TERM"] = "xterm-256"
import colorama
colorama.init()
from transpiler.consts import MAPPINGS
from transpiler.exceptions import CompileError
from transpiler.phases.desugar_with import DesugarWith
#from transpiler.phases import initial_pytype
# from transpiler.phases import initial_pytype
from transpiler.phases.emit_cpp.file import FileVisitor
from transpiler.phases.if_main import IfMainVisitor
from transpiler.phases.typing.block import ScoperBlockVisitor
from transpiler.phases.typing.scope import Scope
import sys
from colorama import Fore
import colorama
import colorful as cf
from transpiler.utils import highlight
colorama.init()
def exception_hook(exc_type, exc_value, tb):
print = lambda *args, **kwargs: builtins.print(*args, **kwargs, file=sys.stderr)
......@@ -41,34 +48,37 @@ def exception_hook(exc_type, exc_value, tb):
filename = tb.tb_frame.f_code.co_filename
line_no = tb.tb_lineno
print(f"{Fore.RED}File \"{filename}\", line {line_no}, in {name}", end="")
print(cf.red(f"File \"{filename}\", line {line_no}, in {name}"), end="")
if info := local_vars.get("TB", None):
print(f", while {Fore.MAGENTA}{info}")
print(f", while {cf.magenta(info)}")
else:
print()
tb = tb.tb_next
if last_node is not None:
print(f"In file \"{Fore.RESET}{last_file}{Fore.RED}\", line {last_node.lineno}")
print(f"In file \"{cf.white(last_file)}\", line {last_node.lineno}")
print("\t" + highlight(ast.unparse(last_node)))
print()
print(f"{Fore.RED}Error:{Fore.RESET} {exc_value}")
print(cf.red("Error:"), exc_value)
if isinstance(exc_value, CompileError):
print()
print(inspect.cleandoc(exc_value.detail(last_node)))
print()
sys.excepthook = exception_hook
def transpile(source, name="<module>", path=None):
TB = f"transpiling module {Fore.RESET}{name}"
TB = f"transpiling module {cf.white(name)}"
res = ast.parse(source, type_comments=True)
#res = initial_pytype.run(source, res)
# res = initial_pytype.run(source, res)
res = DesugarWith().visit(res)
IfMainVisitor().visit(res)
ScoperBlockVisitor().visit(res)
#print(res.scope)
# print(res.scope)
# display each scope
def disp_scope(scope, indent=0):
......@@ -78,9 +88,7 @@ def transpile(source, name="<module>", path=None):
for var in scope.vars.items():
print(" " * (indent + 1), var)
#disp_scope(res.scope)
# disp_scope(res.scope)
code = "\n".join(filter(None, map(str, FileVisitor(Scope()).visit(res))))
return code
......@@ -136,8 +136,16 @@ class ScoperBlockVisitor(ScoperVisitor):
else:
raise NotImplementedError(ast.unparse(target))
def annotate_arg(self, arg: ast.arg) -> BaseType:
if arg.annotation is None:
res = TypeVariable()
arg.annotation = ast.Name(id=str(res), ctx=ast.Load())
return res
else:
return self.visit_annotation(arg.annotation)
def visit_FunctionDef(self, node: ast.FunctionDef):
argtypes = [self.visit_annotation(arg.annotation) for arg in node.args.args]
argtypes = [self.annotate_arg(arg) for arg in node.args.args]
rtype = Promise(self.visit_annotation(node.returns), PromiseKind.TASK)
ftype = FunctionType(argtypes, rtype)
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ftype)
......
......@@ -3,7 +3,7 @@ from dataclasses import dataclass
from transpiler.utils import highlight
from transpiler.exceptions import CompileError
from transpiler.phases.typing import TypeVariable
from transpiler.phases.typing.types import TypeVariable, BaseType
@dataclass
......@@ -25,8 +25,30 @@ class UnresolvedTypeVariableError(CompileError):
For example:
↓↓↓ this tells the compiler that {highlight('math.factorial')} returns an {highlight('int')}
{highlight('res: int = math.factorial(5)')}"""
return """
return f"""
This generally indicates the compiler was unable to infer the type of a variable or expression.
A common fix is to add a type annotation to the variable or function.
For example:
↓↓↓ this tells the compiler that {highlight('x')} is an {highlight('int')}
{highlight('def f(x: int):')}
"""
@dataclass
class RecursiveTypeUnificationError(CompileError):
needle: BaseType
haystack: BaseType
def __str__(self) -> str:
return f"Recursive type unification: {highlight(self.needle)} and {highlight(self.haystack)}"
def detail(self, last_node: ast.AST = None) -> str:
return f"""
This generally indicates a recursive type definition. Such types are not currently supported.
For example:
{highlight('T = tuple[T]')}
In the current case, {highlight(self.haystack)} contains type {highlight(self.needle)}, but an attempt was made to
unify them.
"""
\ No newline at end of file
......@@ -6,6 +6,8 @@ from enum import Enum
from itertools import zip_longest
from typing import Dict, Optional, List, ClassVar, Callable
from transpiler.utils import highlight
class IncompatibleTypesError(Exception):
pass
......@@ -31,6 +33,7 @@ class BaseType(ABC):
def unify(self, other: "BaseType"):
a, b = self.resolve(), other.resolve()
TB = f"unifying {highlight(a)} and {highlight(b)}"
if isinstance(b, TypeVariable):
a, b = b, a
a.unify_internal(b)
......@@ -88,7 +91,8 @@ class TypeVariable(BaseType):
def __str__(self):
if self.resolved is None:
return self.name
#return f"TypeVar[\"{self.name}\"]"
return "_" + self.name
return str(self.resolved)
def resolve(self) -> BaseType:
......@@ -99,7 +103,8 @@ class TypeVariable(BaseType):
def unify_internal(self, other: BaseType):
if self is not other:
if other.contains(self):
raise ValueError(f"Recursive type: {self} and {other}")
from transpiler.phases.typing.exceptions import RecursiveTypeUnificationError
raise RecursiveTypeUnificationError(self, other)
self.resolved = other
def contains_internal(self, other: BaseType) -> bool:
......
......@@ -3,9 +3,25 @@ import ast
from dataclasses import dataclass
from itertools import zip_longest
from typing import Union
from colorama import Fore
import colorful as cf
#
# from colorama import Fore, Back
# from colorama.ansi import AnsiCodes
#
#
# class AnsiStyle(AnsiCodes):
# BOLD = 1
# DIM = 2
# ITALIC = 3
# UNDERLINE = 4
# BLINK = 5
# REVERSE = 7
# HIDDEN = 8
# STRIKETHROUGH = 9
#
# RESET = "21;22;23;24;25;27;28;29"
#
# Style = AnsiStyle()
def highlight(code, full=False):
"""
......@@ -13,20 +29,21 @@ def highlight(code, full=False):
"""
from transpiler.phases.typing import BaseType
if isinstance(code, ast.AST):
return f"{Fore.WHITE}[{type(code).__name__}] " + highlight(ast.unparse(code))
return cf.italic_darkGrey(f"[{type(code).__name__}] ") + highlight(ast.unparse(code))
elif isinstance(code, BaseType):
return f"{Fore.WHITE}[{type(code).__name__}] " + highlight(str(code))
return cf.italic_grey50(f"[{type(code).__name__}] ") + highlight(str(code))
from pygments import highlight as pyg_highlight
from pygments.lexers import PythonLexer
from pygments.formatters import TerminalFormatter
items = pyg_highlight(code, PythonLexer(), TerminalFormatter()).splitlines()
items = pyg_highlight(code, PythonLexer(), TerminalFormatter()).replace("\x1b[39;49;00m", "\x1b[39m").splitlines()
if full:
return Fore.RESET + "\n".join(items)
return "\n".join(items)
res = items[0]
if len(items) > 1:
res += Fore.WHITE + " [...]"
return Fore.RESET + res
res += cf.white(" [...]")
#return Back.LIGHTBLACK_EX + Fore.RESET + res + Back.RESET
return cf.on_gray30(res)
def compare_ast(node1: Union[ast.expr, list[ast.expr]], node2: Union[ast.expr, list[ast.expr]]) -> bool:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment