Skip to content

Commit

Permalink
Added raise statement (#135)
Browse files Browse the repository at this point in the history
* Added 'raise' statement

Syntax:
raise ErrorType "Error Message"

* Expressions as error messages

Things like:

raise RTError 1+1

or

msg = "Error Message"
raise TypeError msg

would work now

* New Syntax

Syntax: raise FooError("message")

* Added `raise.rn` test

* Some Bugfixes + `raise` support for only functions

Now it is only possible to raise errors with function calls
```
fun ArgError(arg)
{
    return "Argument `"+arg+"` not found"
}

raise ArgError("arg1")
```

The name of the function will also be the error type
```
ArgError: Argument `arg1` not found
```

* Updated tests

Updated `raise.rn.json` to match the new output
Removed debug line (parser.py:167)

* Some fixes

* Normalize line endings in stdout and stderr

* Some Type fixes

* Revert "Some Type fixes"

This reverts commit 6a6763d.

* feat: handled message less error formatting.

* fix: visit_RaiseNode None message type.

* fix: optional types.

* Update parser.py

* fix: type issues.

* fix: type issues.

* fix: type error

* fix: ruff formattings.

* Update parser.py

* fix: type errors

* fix: ruff formattings.

---------

Co-authored-by: Md. Almas Ali <[email protected]>
  • Loading branch information
Vardan2009 and Almas-Ali authored May 25, 2024
1 parent a9d13be commit d178d09
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 9 deletions.
5 changes: 3 additions & 2 deletions core/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down
10 changes: 8 additions & 2 deletions core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("<function"):
result += f"{Log.deep_error(self.error_name, bold=True)}"
else:
result += f"{Log.deep_error(self.error_name, bold=True)}: {Log.light_error(self.details)}"
result += "\n" + string_with_arrows(self.pos_start.ftxt, self.pos_start, self.pos_end)
return result

Expand All @@ -69,7 +72,10 @@ def set_pos(self, pos_start=None, pos_end=None):
return self

def __repr__(self) -> str:
return f"{self.error_name}: {self.details}"
# return f"{self.error_name}: {self.details}"
if not self.details.startswith("<function"):
return f"{self.error_name}: {self.details}"
return self.error_name

def copy(self):
return __class__(self.pos_start, self.pos_end, self.error_name, self.details)
Expand Down
13 changes: 13 additions & 0 deletions core/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,19 @@ def visit_VarAssignNode(self, node: VarAssignNode, context: Context) -> 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
Expand Down
18 changes: 18 additions & 0 deletions core/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 18 additions & 4 deletions core/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions core/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def copy(self) -> Position:
"case",
"default",
"fallthrough",
"raise",
]

TokenValue: TypeAlias = Optional[str | int | float]
Expand Down
6 changes: 5 additions & 1 deletion test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions tests/raise.rn
Original file line number Diff line number Diff line change
@@ -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")
1 change: 1 addition & 0 deletions tests/raise.rn.json
Original file line number Diff line number Diff line change
@@ -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": ""}

0 comments on commit d178d09

Please sign in to comment.