diff --git a/core/datatypes.py b/core/datatypes.py index 6f5ebb7..96b056e 100755 --- a/core/datatypes.py +++ b/core/datatypes.py @@ -2,7 +2,7 @@ from core.parser import RTResult, Context, SymbolTable from core.tokens import Position -from core.errors import RTError +from core.errors import RTError, Error import inspect from abc import ABC, abstractmethod @@ -14,7 +14,8 @@ Self = TypeVar("Self", bound="Value") -ResultTuple: TypeAlias = "tuple[None, RTError] | tuple[Value, None]" +# ResultTuple: TypeAlias = "tuple[None, Error] | tuple[Value, None]" +ResultTuple: TypeAlias = tuple[Optional["Value"], Optional[Error]] class Value: diff --git a/core/errors.py b/core/errors.py index 078f67a..87620a7 100755 --- a/core/errors.py +++ b/core/errors.py @@ -60,7 +60,10 @@ def as_string(self) -> str: """Return error as string""" result = Log.light_purple("Radiation (most recent call last):\n") result += f" File {Log.light_info(self.pos_start.fn)}, line {Log.light_info(str(self.pos_start.ln + 1))}\n" - result += f"{Log.deep_error(self.error_name, bold=True)}: {Log.light_error(self.details)}" + if self.details.startswith(" str: - return f"{self.error_name}: {self.details}" + # return f"{self.error_name}: {self.details}" + if not self.details.startswith(" RTResult pos_end=node.pos_end, ) + def visit_RaiseNode(self, node: RaiseNode, context: Context) -> RTResult[Value]: + res = RTResult[Value]() + + if node.message is not None: + val = res.register(self.visit(node.message, context)) + else: + val = None + + if res.should_return(): + return res + + return res.failure(Error(node.pos_start, node.pos_end, str(node.errtype.value), str(val))) + def visit_ImportNode(self, node: ImportNode, context: Context) -> RTResult[Value]: res = RTResult[Value]() exec_ctx = context diff --git a/core/nodes.py b/core/nodes.py index 4b05339..1210fbc 100755 --- a/core/nodes.py +++ b/core/nodes.py @@ -114,6 +114,24 @@ def __init__(self, module: Token) -> None: self.pos_end = self.module.pos_end +class RaiseNode: + errtype: Token + message: Optional[Node] + pos_start: Position + pos_end: Position + + def __init__(self, errtype: Token, message: Optional[Node]) -> None: + self.message = message + self.errtype = errtype + + if self.message: + self.pos_start = self.errtype.pos_start + self.pos_end = self.message.pos_end + else: + self.pos_start = self.errtype.pos_start + self.pos_end = self.errtype.pos_end + + class BinOpNode: left_node: Node op_tok: Token diff --git a/core/parser.py b/core/parser.py index 6d23ff6..6a37b8e 100755 --- a/core/parser.py +++ b/core/parser.py @@ -38,7 +38,7 @@ def register_advancement(self) -> None: U = TypeVar("U") - def register(self, res: ParseResult[U]) -> Optional[U]: + def register(self, res): self.last_registered_advance_count = res.advance_count self.advance_count += res.advance_count if res.error: @@ -153,10 +153,24 @@ def statements(self) -> ParseResult[Node]: return res.success(ArrayNode(list_statements, pos_start, self.current_tok.pos_end.copy())) - def statement(self) -> ParseResult[Node]: + def statement(self) -> ParseResult[Optional[Node]] | ParseResult[Node]: res = ParseResult[Node]() pos_start = self.current_tok.pos_start.copy() + if self.current_tok.matches(TT_KEYWORD, "raise"): + # syntax + # raise FunctionCall() + + funcname = self.advance(res) + if funcname.type != TT_IDENTIFIER: + return res.failure( + InvalidSyntaxError( + funcname.pos_start, funcname.pos_end, f"Expected Error Function Call, got {funcname.type}" + ) + ) + err = res.register(self.statement()) + return res.success(RaiseNode(funcname, err)) + if self.current_tok.matches(TT_KEYWORD, "return"): if not self.in_func: return res.failure( @@ -1467,7 +1481,7 @@ class RTResult(Generic[T]): """Runtime result""" value: Optional[T] - error: Optional[RTError] + error: Optional[RTError | Error] func_return_value: Optional[Value] loop_should_continue: bool loop_should_break: bool @@ -1531,7 +1545,7 @@ def fallthrough(self) -> RTResult[T]: self.should_fallthrough = True return self - def failure(self, error: RTError) -> RTResult[T]: + def failure(self, error: Error) -> RTResult[T]: self.reset() self.error = error return self diff --git a/core/tokens.py b/core/tokens.py index f54f95e..245e96f 100755 --- a/core/tokens.py +++ b/core/tokens.py @@ -131,6 +131,7 @@ def copy(self) -> Position: "case", "default", "fallthrough", + "raise", ] TokenValue: TypeAlias = Optional[str | int | float] diff --git a/test.py b/test.py index bf1b059..ca75bf6 100755 --- a/test.py +++ b/test.py @@ -27,7 +27,11 @@ def dump(self, path: str) -> None: def run_test(test: str) -> Output: proc = subprocess.run([sys.executable, "radon.py", "-s", test], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return Output(proc.returncode, proc.stdout.decode("utf-8"), proc.stderr.decode("utf-8")) + return Output( + proc.returncode, + proc.stdout.decode("utf-8").replace("\r\n", "\n"), + proc.stderr.decode("utf-8").replace("\r\n", "\n"), + ) def run_tests(directory: str = "tests") -> int: diff --git a/tests/raise.rn b/tests/raise.rn new file mode 100644 index 0000000..836f9f2 --- /dev/null +++ b/tests/raise.rn @@ -0,0 +1,12 @@ +fun ArgError(arg) +{ + return "Argument `"+arg+"` not found" +} +print("test1") +print("test2") + +raise ArgError("arg1") +raise ArgError("arg2") + +print("test3") +print("test4") \ No newline at end of file diff --git a/tests/raise.rn.json b/tests/raise.rn.json new file mode 100644 index 0000000..7d13e78 --- /dev/null +++ b/tests/raise.rn.json @@ -0,0 +1 @@ +{"code": 1, "stdout": "test1\ntest2\n\u001b[38;5;208mRadiation (most recent call last):\n\u001b[0m File \u001b[38;5;117mtests/raise.rn\u001b[0m, line \u001b[38;5;117m8\u001b[0m\n\u001b[1m\u001b[31mArgError\u001b[0m: \u001b[38;5;203mArgument `arg1` not found\u001b[0m\n\nraise \u001b[1m\u001b[31mArgError(\"arg1\"\u001b[0m)\n \u001b[1m\u001b[31m^^^^^^^^^^^^^^^\u001b[0m\n", "stderr": ""} \ No newline at end of file