You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
3.3 KiB
Python

from .defs import Span, LexingContext, Token, TokenType
from math import exp, log10, ceil
from typing import Iterable
def create_span_context_str(span: Span, message: str, color: str = '\033[31m'):
lines, offset_into_file, line_no = span.context.get_lines_containing(span)
relative_offset = span.start - offset_into_file
annotation_len = span.end - span.start
digit_len = ceil(log10(line_no + len(lines)))
if digit_len == 0:
digit_len = 1
output_str = ">>> In file {}:{}\n".format(span.source_name, line_no)
for i, source_line in enumerate(lines):
source_line = source_line[:relative_offset] + color + source_line[relative_offset:relative_offset+annotation_len] + '\033[0m' + source_line[relative_offset+annotation_len:]
output_str += '{:>{}d}: {}\n'.format(line_no + i, digit_len, source_line)
if relative_offset > len(source_line):
continue
# TODO: handle multi-line underlines
output_str += "{}{}{}{}\n".format(
color,
' ' * (relative_offset + digit_len + 2),
'^' * min(annotation_len, len(source_line) - relative_offset),
'\033[0m'
)
if annotation_len > len(source_line) - relative_offset:
relative_offset = 0
annotation_len -= len(source_line) - relative_offset
if message:
output_str += color
output_str += ' ' * (relative_offset + digit_len + 2) + '|\n'
for message_line in message.split("\n"):
output_str += ' ' * (relative_offset + digit_len + 2) + message_line + '\n'
return output_str + '\033[0m'
def print_warning(span: Span, message: str, color="\033[33m"):
print(create_span_context_str(span, "Warning: " + message, color))
class CompilerError(Exception):
span: Span
message: str
def __init__(self, msg: str, span: Span=None) -> None:
super().__init__((msg, span))
self.span = span
self.message = msg
def print_context_message(self):
if not self.span:
print("\n".join(">>> {}".format(line) for line in self.message.split('\n')))
else:
print(create_span_context_str(self.span, self.message))
class EndOfInputError(CompilerError):
def __init__(self,span: Span, search_str:str = None) -> None:
if search_str:
super().__init__(f"Unexpected end-of-input in {span.source_name} while scanning for {search_str}!", span)
else:
super().__init__(f"Unexpected end-of-input in {span.source_name}!", span)
class ParseError(CompilerError):
def __init__(self, msg: str, span: Span = None) -> None:
super().__init__(msg, span)
class InvalidTokenError(CompilerError):
def __init__(self, token: Token, expected_type: Iterable[str | TokenType] = None, message: str = None) -> None:
expected = ", expected {}".format(", ".join(f"{x}" for x in expected_type)) if expected_type else ""
super().__init__("Unexpected token {}{} {}".format(
token, expected, '\n' + message if message else ""
), token.span if token is not None else None)
class UnsupportedSyntaxError(CompilerError):
def __init__(self, token: Token, feature: str) -> None:
super().__init__("Unsupported syntax: {}".format(feature), token.span)