Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added raise statement #135

Merged
merged 23 commits into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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": ""}