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 ...@@ -3,22 +3,29 @@ import ast
import builtins import builtins
import inspect import inspect
import os
os.environ["TERM"] = "xterm-256"
import colorama
colorama.init()
from transpiler.consts import MAPPINGS from transpiler.consts import MAPPINGS
from transpiler.exceptions import CompileError from transpiler.exceptions import CompileError
from transpiler.phases.desugar_with import DesugarWith 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.emit_cpp.file import FileVisitor
from transpiler.phases.if_main import IfMainVisitor from transpiler.phases.if_main import IfMainVisitor
from transpiler.phases.typing.block import ScoperBlockVisitor from transpiler.phases.typing.block import ScoperBlockVisitor
from transpiler.phases.typing.scope import Scope from transpiler.phases.typing.scope import Scope
import sys import sys
from colorama import Fore
import colorama import colorful as cf
from transpiler.utils import highlight from transpiler.utils import highlight
colorama.init()
def exception_hook(exc_type, exc_value, tb): def exception_hook(exc_type, exc_value, tb):
print = lambda *args, **kwargs: builtins.print(*args, **kwargs, file=sys.stderr) print = lambda *args, **kwargs: builtins.print(*args, **kwargs, file=sys.stderr)
...@@ -41,34 +48,37 @@ def exception_hook(exc_type, exc_value, tb): ...@@ -41,34 +48,37 @@ def exception_hook(exc_type, exc_value, tb):
filename = tb.tb_frame.f_code.co_filename filename = tb.tb_frame.f_code.co_filename
line_no = tb.tb_lineno 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): if info := local_vars.get("TB", None):
print(f", while {Fore.MAGENTA}{info}") print(f", while {cf.magenta(info)}")
else: else:
print() print()
tb = tb.tb_next tb = tb.tb_next
if last_node is not None: 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("\t" + highlight(ast.unparse(last_node)))
print() print()
print(f"{Fore.RED}Error:{Fore.RESET} {exc_value}") print(cf.red("Error:"), exc_value)
if isinstance(exc_value, CompileError): if isinstance(exc_value, CompileError):
print()
print(inspect.cleandoc(exc_value.detail(last_node))) print(inspect.cleandoc(exc_value.detail(last_node)))
print() print()
sys.excepthook = exception_hook sys.excepthook = exception_hook
def transpile(source, name="<module>", path=None): 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 = ast.parse(source, type_comments=True)
#res = initial_pytype.run(source, res) # res = initial_pytype.run(source, res)
res = DesugarWith().visit(res) res = DesugarWith().visit(res)
IfMainVisitor().visit(res) IfMainVisitor().visit(res)
ScoperBlockVisitor().visit(res) ScoperBlockVisitor().visit(res)
#print(res.scope)
# print(res.scope)
# display each scope # display each scope
def disp_scope(scope, indent=0): def disp_scope(scope, indent=0):
...@@ -78,9 +88,7 @@ def transpile(source, name="<module>", path=None): ...@@ -78,9 +88,7 @@ def transpile(source, name="<module>", path=None):
for var in scope.vars.items(): for var in scope.vars.items():
print(" " * (indent + 1), var) print(" " * (indent + 1), var)
# disp_scope(res.scope)
#disp_scope(res.scope)
code = "\n".join(filter(None, map(str, FileVisitor(Scope()).visit(res)))) code = "\n".join(filter(None, map(str, FileVisitor(Scope()).visit(res))))
return code return code
...@@ -136,8 +136,16 @@ class ScoperBlockVisitor(ScoperVisitor): ...@@ -136,8 +136,16 @@ class ScoperBlockVisitor(ScoperVisitor):
else: else:
raise NotImplementedError(ast.unparse(target)) 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): 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) rtype = Promise(self.visit_annotation(node.returns), PromiseKind.TASK)
ftype = FunctionType(argtypes, rtype) ftype = FunctionType(argtypes, rtype)
self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ftype) self.scope.vars[node.name] = VarDecl(VarKind.LOCAL, ftype)
......
...@@ -3,7 +3,7 @@ from dataclasses import dataclass ...@@ -3,7 +3,7 @@ from dataclasses import dataclass
from transpiler.utils import highlight from transpiler.utils import highlight
from transpiler.exceptions import CompileError from transpiler.exceptions import CompileError
from transpiler.phases.typing import TypeVariable from transpiler.phases.typing.types import TypeVariable, BaseType
@dataclass @dataclass
...@@ -25,8 +25,30 @@ class UnresolvedTypeVariableError(CompileError): ...@@ -25,8 +25,30 @@ class UnresolvedTypeVariableError(CompileError):
For example: For example:
↓↓↓ this tells the compiler that {highlight('math.factorial')} returns an {highlight('int')} ↓↓↓ this tells the compiler that {highlight('math.factorial')} returns an {highlight('int')}
{highlight('res: int = math.factorial(5)')}""" {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. 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. 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 ...@@ -6,6 +6,8 @@ from enum import Enum
from itertools import zip_longest from itertools import zip_longest
from typing import Dict, Optional, List, ClassVar, Callable from typing import Dict, Optional, List, ClassVar, Callable
from transpiler.utils import highlight
class IncompatibleTypesError(Exception): class IncompatibleTypesError(Exception):
pass pass
...@@ -31,6 +33,7 @@ class BaseType(ABC): ...@@ -31,6 +33,7 @@ class BaseType(ABC):
def unify(self, other: "BaseType"): def unify(self, other: "BaseType"):
a, b = self.resolve(), other.resolve() a, b = self.resolve(), other.resolve()
TB = f"unifying {highlight(a)} and {highlight(b)}"
if isinstance(b, TypeVariable): if isinstance(b, TypeVariable):
a, b = b, a a, b = b, a
a.unify_internal(b) a.unify_internal(b)
...@@ -88,7 +91,8 @@ class TypeVariable(BaseType): ...@@ -88,7 +91,8 @@ class TypeVariable(BaseType):
def __str__(self): def __str__(self):
if self.resolved is None: if self.resolved is None:
return self.name #return f"TypeVar[\"{self.name}\"]"
return "_" + self.name
return str(self.resolved) return str(self.resolved)
def resolve(self) -> BaseType: def resolve(self) -> BaseType:
...@@ -99,7 +103,8 @@ class TypeVariable(BaseType): ...@@ -99,7 +103,8 @@ class TypeVariable(BaseType):
def unify_internal(self, other: BaseType): def unify_internal(self, other: BaseType):
if self is not other: if self is not other:
if other.contains(self): 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 self.resolved = other
def contains_internal(self, other: BaseType) -> bool: def contains_internal(self, other: BaseType) -> bool:
......
...@@ -3,9 +3,25 @@ import ast ...@@ -3,9 +3,25 @@ import ast
from dataclasses import dataclass from dataclasses import dataclass
from itertools import zip_longest from itertools import zip_longest
from typing import Union from typing import Union
import colorful as cf
from colorama import Fore #
# 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): def highlight(code, full=False):
""" """
...@@ -13,20 +29,21 @@ def highlight(code, full=False): ...@@ -13,20 +29,21 @@ def highlight(code, full=False):
""" """
from transpiler.phases.typing import BaseType from transpiler.phases.typing import BaseType
if isinstance(code, ast.AST): 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): 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 import highlight as pyg_highlight
from pygments.lexers import PythonLexer from pygments.lexers import PythonLexer
from pygments.formatters import TerminalFormatter 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: if full:
return Fore.RESET + "\n".join(items) return "\n".join(items)
res = items[0] res = items[0]
if len(items) > 1: if len(items) > 1:
res += Fore.WHITE + " [...]" res += cf.white(" [...]")
return Fore.RESET + res #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: 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