Move exceptions to its own package

This commit is contained in:
2023-04-17 18:20:34 -04:00
parent 137f366839
commit 098623ab53
2 changed files with 127 additions and 88 deletions

128
aurora.py
View File

@@ -11,7 +11,7 @@ Purpose: Main implementation of Aurora
# Imports # Imports
import string import string
from internal.term_utils import point_at from exceptions import core as exceptions
#################### ####################
# CONSTANTS # CONSTANTS
@@ -24,64 +24,16 @@ LETTERS_DIGITS = LETTERS + DIGITS
#################### ####################
# ERRORS # ERRORS
#################### ####################
class Error:
def __init__(self, pos_start, pos_end, error_name, details):
self.pos_start = pos_start
self.pos_end = pos_end
self.error_name = error_name
self.details = details
def as_string(self):
result = f'{self.error_name}: {self.details}\n'
result += f'File {self.pos_start.fn}, line {self.pos_start.ln + 1}'
result += f'\n{point_at(self.pos_start.ftxt, self.pos_start, self.pos_end)}'
return result
class IllegalCharErr(Error):
def __init__(self, pos_start, pos_end, details):
super().__init__(pos_start, pos_end, 'IllegalCharacter', details)
class ExpectedCharErr(Error):
def __init__(self, pos_start, pos_end, details):
super().__init__(pos_start, pos_end, 'ExpectedCharacter', details)
class InvalidSyntaxErr(Error):
def __init__(self, pos_start, pos_end, details=''):
super().__init__(pos_start, pos_end, 'InvalidSyntax', details)
class RuntimeErr(Error):
def __init__(self, pos_start, pos_end, details, context):
super().__init__(pos_start, pos_end, 'RuntimeErr', details)
self.context = context
def as_string(self):
result = self.generate_traceback()
result += f'{self.error_name}: {self.details}\n'
result += f'\n{point_at(self.pos_start.ftxt, self.pos_start, self.pos_end)}'
return result
def generate_traceback(self):
result = ''
pos = self.pos_start
context = self.context
while context:
result = f' File {pos.fn}, line {str(pos.ln + 1)}, in {context.display_name}\n' + result
pos = context.parent_entry_pos
context = context.parent
return f"Traceback (most recent call last):\n" \
f"{result}"
#################### ####################
# POSITION # POSITION
#################### ####################
class Position: class Position:
"""
An advanceable, indexed position within the program.
"""
def __init__(self, idx, ln, col, fn, ftxt): def __init__(self, idx, ln, col, fn, ftxt):
self.idx = idx self.idx = idx
self.ln = ln self.ln = ln
@@ -239,7 +191,7 @@ class Lexer:
pos_start = self.pos.copy() pos_start = self.pos.copy()
char = self.current_char char = self.current_char
self.advance() self.advance()
return [], IllegalCharErr(pos_start, self.pos, "'" + char + "'") return [], exceptions.IllegalCharErr(pos_start, self.pos, "'" + char + "'")
tokens.append(Token(TT_EOF, pos_start=self.pos)) tokens.append(Token(TT_EOF, pos_start=self.pos))
return tokens, None return tokens, None
@@ -323,7 +275,7 @@ class Lexer:
return Token(TT_NE, pos_start=pos_start, pos_end=self.pos), None return Token(TT_NE, pos_start=pos_start, pos_end=self.pos), None
self.advance() self.advance()
return None, ExpectedCharErr(pos_start, self.pos, "'=' after '!'") return None, exceptions.ExpectedCharErr(pos_start, self.pos, "'=' after '!'")
def make_equals(self): def make_equals(self):
token_type = TT_EQ token_type = TT_EQ
@@ -541,7 +493,7 @@ class Parser:
def parse(self): def parse(self):
result = self.expr() result = self.expr()
if not result.error and self.current_token.type != TT_EOF: if not result.error and self.current_token.type != TT_EOF:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected '+', '-', '*', '/', or '^'" "Expected '+', '-', '*', '/', or '^'"
@@ -569,7 +521,7 @@ class Parser:
else: else:
arg_nodes.append(result.register(self.expr())) arg_nodes.append(result.register(self.expr()))
if result.error: if result.error:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected ')', 'set', 'if', 'for', 'while', 'fun', int, float, identifier, '+', '-', '(' or " "Expected ')', 'set', 'if', 'for', 'while', 'fun', int, float, identifier, '+', '-', '(' or "
@@ -585,7 +537,7 @@ class Parser:
return result return result
if self.current_token.type != TT_RPAREN: if self.current_token.type != TT_RPAREN:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected ',' or')'" "Expected ',' or')'"
@@ -623,7 +575,7 @@ class Parser:
self.advance() self.advance()
return result.success(expr) return result.success(expr)
else: else:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_end, self.current_token.pos_start, self.current_token.pos_end,
"Expected ')'" "Expected ')'"
)) ))
@@ -651,10 +603,10 @@ class Parser:
return result return result
return result.success(fun_def) return result.success(fun_def)
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
token.pos_start, token.pos_start,
token.pos_end, token.pos_end,
"Exprected int, float, identifier, '+', '-', '(', 'if', 'for', 'while', or 'fun'" "Expected int, float, identifier, '+', '-', '(', 'if', 'for', 'while', or 'fun'"
)) ))
def factor(self): def factor(self):
@@ -692,7 +644,7 @@ class Parser:
node = result.register(self.binary_operation(self.ar_expr, (TT_EE, TT_NE, TT_LT, TT_GT, TT_LTE, TT_GTE))) node = result.register(self.binary_operation(self.ar_expr, (TT_EE, TT_NE, TT_LT, TT_GT, TT_LTE, TT_GTE)))
if result.error: if result.error:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected int, float, identifier, '+', '-', '(', or 'not'" "Expected int, float, identifier, '+', '-', '(', or 'not'"
@@ -708,7 +660,7 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_IDENTIFIER: if self.current_token.type != TT_IDENTIFIER:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected identifier" "Expected identifier"
@@ -719,7 +671,7 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_EQ: if self.current_token.type != TT_EQ:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected '='" "Expected '='"
@@ -734,7 +686,7 @@ class Parser:
node = result.register(self.binary_operation(self.com_expr, ((TT_KEYWORD, "and"), (TT_KEYWORD, "or")))) node = result.register(self.binary_operation(self.com_expr, ((TT_KEYWORD, "and"), (TT_KEYWORD, "or"))))
if result.error: if result.error:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'set', int, float, identifier, '+', '-', '(', 'not', 'if', 'for', 'while', or 'fun'" "Expected 'set', int, float, identifier, '+', '-', '(', 'not', 'if', 'for', 'while', or 'fun'"
@@ -747,7 +699,7 @@ class Parser:
else_case = None else_case = None
if not self.current_token.matches(TT_KEYWORD, 'if'): if not self.current_token.matches(TT_KEYWORD, 'if'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'if'" "Expected 'if'"
@@ -761,7 +713,7 @@ class Parser:
return result return result
if not self.current_token.matches(TT_KEYWORD, 'then'): if not self.current_token.matches(TT_KEYWORD, 'then'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'then'" "Expected 'then'"
@@ -784,7 +736,7 @@ class Parser:
return result return result
if not self.current_token.matches(TT_KEYWORD, 'then'): if not self.current_token.matches(TT_KEYWORD, 'then'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'then'" "Expected 'then'"
@@ -811,7 +763,7 @@ class Parser:
result = ParsedResult() result = ParsedResult()
if not self.current_token.matches(TT_KEYWORD, 'for'): if not self.current_token.matches(TT_KEYWORD, 'for'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'for'" "Expected 'for'"
@@ -821,7 +773,7 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_IDENTIFIER: if self.current_token.type != TT_IDENTIFIER:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected identifier" "Expected identifier"
@@ -832,7 +784,7 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_EQ: if self.current_token.type != TT_EQ:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected '='" "Expected '='"
@@ -846,7 +798,7 @@ class Parser:
return result return result
if not self.current_token.matches(TT_KEYWORD, 'to'): if not self.current_token.matches(TT_KEYWORD, 'to'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'to'" "Expected 'to'"
@@ -869,7 +821,7 @@ class Parser:
return result return result
if not self.current_token.matches(TT_KEYWORD, 'then'): if not self.current_token.matches(TT_KEYWORD, 'then'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'then'" "Expected 'then'"
@@ -888,7 +840,7 @@ class Parser:
result = ParsedResult() result = ParsedResult()
if not self.current_token.matches(TT_KEYWORD, 'while'): if not self.current_token.matches(TT_KEYWORD, 'while'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'while'" "Expected 'while'"
@@ -902,7 +854,7 @@ class Parser:
return result return result
if not self.current_token.matches(TT_KEYWORD, 'then'): if not self.current_token.matches(TT_KEYWORD, 'then'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'then'" "Expected 'then'"
@@ -921,7 +873,7 @@ class Parser:
result = ParsedResult() result = ParsedResult()
if not self.current_token.matches(TT_KEYWORD, 'fun'): if not self.current_token.matches(TT_KEYWORD, 'fun'):
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected 'fun'" "Expected 'fun'"
@@ -937,14 +889,14 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_LPAREN: if self.current_token.type != TT_LPAREN:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected '('" "Expected '('"
)) ))
else: else:
if self.current_token.type != TT_LPAREN: if self.current_token.type != TT_LPAREN:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected identifier or '('" "Expected identifier or '('"
@@ -965,7 +917,7 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_IDENTIFIER: if self.current_token.type != TT_IDENTIFIER:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected identifier" "Expected identifier"
@@ -976,14 +928,14 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_RPAREN: if self.current_token.type != TT_RPAREN:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected ')'" "Expected ')'"
)) ))
else: else:
if self.current_token.type != TT_RPAREN: if self.current_token.type != TT_RPAREN:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected identifier or ')'" "Expected identifier or ')'"
@@ -993,7 +945,7 @@ class Parser:
self.advance() self.advance()
if self.current_token.type != TT_ARROW: if self.current_token.type != TT_ARROW:
return result.failure(InvalidSyntaxErr( return result.failure(exceptions.InvalidSyntaxErr(
self.current_token.pos_start, self.current_token.pos_start,
self.current_token.pos_end, self.current_token.pos_end,
"Expected '->'" "Expected '->'"
@@ -1102,7 +1054,7 @@ class Type:
def illegal_operation(self, other=None): def illegal_operation(self, other=None):
if not other: if not other:
other = self other = self
return RuntimeErr( return exceptions.RuntimeErr(
self.pos_start, self.pos_start,
other.pos_end, other.pos_end,
"Illegal operation", "Illegal operation",
@@ -1148,7 +1100,7 @@ class Number(Type):
def divided_by(self, other): def divided_by(self, other):
if isinstance(other, Number): if isinstance(other, Number):
if other.value == 0: if other.value == 0:
return None, RuntimeErr( return None, exceptions.RuntimeErr(
other.pos_start, other.pos_start,
other.pos_end, other.pos_end,
"Division by zero", "Division by zero",
@@ -1272,14 +1224,14 @@ class Function(Type):
fun_context.symbol_table = SymbolTable(fun_context.parent.symbol_table) fun_context.symbol_table = SymbolTable(fun_context.parent.symbol_table)
if len(args) > len(self.arg_names): if len(args) > len(self.arg_names):
return result.failure(RuntimeErr( return result.failure(exceptions.RuntimeErr(
self.pos_start, self.pos_start,
self.pos_end, self.pos_end,
f"Too many args ({len(args)}/{len(self.arg_names)}) passed into '{self.name}'", f"Too many args ({len(args)}/{len(self.arg_names)}) passed into '{self.name}'",
self.context self.context
)) ))
elif len(args) < len(self.arg_names): elif len(args) < len(self.arg_names):
return result.failure(RuntimeErr( return result.failure(exceptions.RuntimeErr(
self.pos_start, self.pos_start,
self.pos_end, self.pos_end,
f"Too few args ({len(args)}/{len(self.arg_names)} needed) passed into '{self.name}'", f"Too few args ({len(args)}/{len(self.arg_names)} needed) passed into '{self.name}'",
@@ -1370,7 +1322,7 @@ class Interpreter:
value = context.symbol_table.get(var_name) value = context.symbol_table.get(var_name)
if not value: if not value:
return result.failure(RuntimeErr( return result.failure(exceptions.RuntimeErr(
node.pos_start, node.pos_start,
node.pos_end, node.pos_end,
f"'{var_name}' is not defined", f"'{var_name}' is not defined",
@@ -1490,9 +1442,9 @@ class Interpreter:
i = start_value.value i = start_value.value
if step_value.value >= 0: if step_value.value >= 0:
condition = lambda: i < end_value.value def condition(): return i < end_value.value
else: else:
condition = lambda: i > end_value.value def condition(): return i > end_value.value
while condition(): while condition():
context.symbol_table.set(node.var_name_token.value, Number(i)) context.symbol_table.set(node.var_name_token.value, Number(i))

87
exceptions/core.py Normal file
View File

@@ -0,0 +1,87 @@
# d888888
# d88P aaa 888 888 888d88 .d88b. 888d88 8888b.
# d88P aaa 888 888 888P d88^^88b 888P 88b
# d88P aaa 888 888 888 888 888 888 .d888888
# d8888888888 Y88b 888 888 Y88..88P 888 888 888
# d88P aaa Y88888 888 Y88P 888 Y888888
# -- Copyright © 2023 Zandercraft. All rights reserved. --
"""
Purpose: Core Exception Set
"""
# Imports
from internal.term_utils import point_at
class Error:
"""
Base class for Aurora exception types.
"""
def __init__(self, pos_start, pos_end, error_name, details):
self.pos_start = pos_start
self.pos_end = pos_end
self.error_name = error_name
self.details = details
def as_string(self):
result = f'{self.error_name}: {self.details}\n'
result += f'File {self.pos_start.fn}, line {self.pos_start.ln + 1}'
result += f'\n{point_at(self.pos_start.ftxt, self.pos_start, self.pos_end)}'
return result
class IllegalCharErr(Error):
"""
Illegal Character Error
- Thrown when a character is detected that is not valid in a particular situation.
"""
def __init__(self, pos_start, pos_end, details):
super().__init__(pos_start, pos_end, 'IllegalCharacter', details)
class ExpectedCharErr(Error):
"""
Expected Character Error
- Thrown when a character is missing that is required in a particular situation.
"""
def __init__(self, pos_start, pos_end, details):
super().__init__(pos_start, pos_end, 'ExpectedCharacter', details)
class InvalidSyntaxErr(Error):
"""
Invalid Syntax Error
- Thrown when invalid keywords or symbols are used or when they are misused.
"""
def __init__(self, pos_start, pos_end, details=''):
super().__init__(pos_start, pos_end, 'InvalidSyntax', details)
class RuntimeErr(Error):
"""
Runtime Error
- Thrown during the evaluation of an expression when something goes wrong.
Example: dividing by zero (valid syntax, but invalid operation)
"""
def __init__(self, pos_start, pos_end, details, context):
super().__init__(pos_start, pos_end, 'RuntimeErr', details)
self.context = context
def as_string(self):
result = self.generate_traceback()
result += f'{self.error_name}: {self.details}\n'
result += f'\n{point_at(self.pos_start.ftxt, self.pos_start, self.pos_end)}'
return result
def generate_traceback(self):
result = ''
pos = self.pos_start
context = self.context
while context:
result = f' File {pos.fn}, line {str(pos.ln + 1)}, in {context.display_name}\n' + result
pos = context.parent_entry_pos
context = context.parent
return f"Traceback (most recent call last):\n" \
f"{result}"