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 help() shell function #150

Merged
merged 14 commits into from
May 30, 2024
Merged
9 changes: 9 additions & 0 deletions core/builtin_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ def execute_input(self, exec_ctx: Context) -> RTResult[Value]:
text = input(str(exec_ctx.symbol_table.get("value")))
return RTResult[Value]().success(String(text))

@args(["obj"])
def execute_help(self, exec_ctx: Context) -> RTResult[Value]:
obj = exec_ctx.symbol_table.get("obj")
if obj is None:
return RTResult[Value]().failure(Error(self.pos_start, self.pos_end, "TypeError", "Argument is null"))
print(obj.__help_repr__())
return RTResult[Value]().success(Null.null())

@args([])
def execute_input_int(self, exec_ctx: Context) -> RTResult[Value]:
while True:
Expand Down Expand Up @@ -608,6 +616,7 @@ def create_global_symbol_table() -> SymbolTable:
# Shell functions
ret.set("license", BuiltInFunction("license"))
ret.set("credits", BuiltInFunction("credits"))
ret.set("help", BuiltInFunction("help"))
# Built-in classes
ret.set("File", bic.BuiltInClass("File", bic.FileObject))
ret.set("String", bic.BuiltInClass("String", bic.StringObject))
Expand Down
134 changes: 133 additions & 1 deletion core/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ def copy(self: Self) -> Self:
def is_true(self) -> bool:
return False

# Help text for help() in radon
def __help_repr__(self) -> str:
return """
This data type help is not implemented yet
"""

def illegal_operation(self, *others: Value) -> RTError:
if len(others) == 0:
others = (self,)
Expand Down Expand Up @@ -149,6 +155,13 @@ def __next__(self) -> RTResult[Value]:
def __str__(self) -> str:
return "<iterator>"

def __help_repr__(self) -> str:
return """
Iterator

An Iterator is an object that enables traversal over a collection, one element at a time.
"""

def __repr__(self) -> str:
return str(self)

Expand Down Expand Up @@ -280,6 +293,22 @@ def is_true(self) -> bool:
def __str__(self) -> str:
return str(self.value)

def __help_repr__(self) -> str:
return """
Number

A Number represents a numeric value. It can be an integer, float, or other numeric type.

Operations:
+, -, *, / -> Basic arithmetic operations.
//, % -> Integer division and modulus.
^ -> Exponentiation.
math.factorial() -> Gets the factorial of a number (standard math library)
str() -> Converts the number to its string representation.

Example: 25
"""

def __repr__(self) -> str:
return str(self.value)

Expand Down Expand Up @@ -346,6 +375,19 @@ def __str__(self) -> str:
def __repr__(self) -> str:
return "true" if self.value else "false"

def __help_repr__(self) -> str:
return """
Boolean

A Boolean represents a truth value: True or False.

Operations:
and, or, not -> Logical operations.
==, != -> Equality and inequality checks.

Example: true
"""

@classmethod
def true(cls) -> Boolean:
return cls(True)
Expand Down Expand Up @@ -481,6 +523,22 @@ def __str__(self) -> str:
def __repr__(self) -> str:
return f'"{self.value}"'

def __help_repr__(self) -> str:
return """
String

A String is a sequence of characters.

Methods:
len(str) -> Returns the length of the string.

String standard library methods:
find(str) -> Find a character in a string and return its index (-1 if not found)
to_int() -> Magic method to convert string to int if possible

Example: "Hello World!"
"""

def __iter__(self) -> PyIterator[str]:
return iter(self.value)

Expand Down Expand Up @@ -657,6 +715,29 @@ def __str__(self) -> str:
def __repr__(self) -> str:
return f'[{", ".join(repr(x) for x in self.elements)}]'

def __help_repr__(self) -> str:
return """
Array

An Array is an ordered collection of elements.

Methods:
len(arr) -> Returns the number of elements in the array.

Array standard library methods:
map(func) -> Map an array with a function
append(item) -> Append an element from the right
pop(index) -> Removes and returns the last element of the array.
extend(arr) -> Extend by another array
find(element) -> Get the index of an element in the array (-1 if not found)

is_empty() -> Returns boolean indicating if the array is empty or not
to_string() -> Convert to string
is_array() -> returns true

Example: [1,2,3,true,"Hello World!"]
"""

def __iter__(self):
return iter(self.elements)

Expand Down Expand Up @@ -773,6 +854,15 @@ def copy(self) -> HashMap:
def __str__(self) -> str:
return self.__repr__()

def __help_repr__(self) -> str:
return """
HashMap

A HashMap is a collection of key-value pairs.

Example: {"key":"value"}
"""

def __repr__(self) -> str:
__val = ", ".join([f"{repr(k)}: {repr(v)}" for k, v in self.values.items()])
return f"{{{__val}}}"
Expand Down Expand Up @@ -947,6 +1037,8 @@ def copy(self) -> PyAPI:
class BaseFunction(Value):
name: str
symbol_table: Optional[SymbolTable]
desc: str
arg_names: list[str]

def __init__(self, name: Optional[str], symbol_table: Optional[SymbolTable]) -> None:
super().__init__()
Expand Down Expand Up @@ -1114,6 +1206,16 @@ def __exec_len__(self):
except AttributeError:
return Null.null()

def __help_repr__(self) -> str:
result: str = f"Help on object {self.parent_class.name}:\n\nclass {self.parent_class.name}\n|\n"
for k in self.symbol_table.symbols:
f = self.symbol_table.symbols[k]
if isinstance(f, Function):
result += f.__help_repr_method__()
elif isinstance(f, Value) and k != "this":
result += f"| {k} = {f!r}\n|\n"
return result

def bind_method(self, method: BaseFunction) -> RTResult[BaseFunction]:
method = method.copy()
if method.symbol_table is None:
Expand Down Expand Up @@ -1194,6 +1296,16 @@ def get(self, name: str) -> Optional[Value]:
return None
return method

def __help_repr__(self) -> str:
result: str = f"Help on object {self.name}:\n\nclass {self.name}\n|\n"
for k in self.symbol_table.symbols:
f = self.symbol_table.symbols[k]
if isinstance(f, Function):
result += f.__help_repr_method__()
elif isinstance(f, Value) and k != "this":
result += f"| {k} = {f!r}\n|\n"
return result

def create(self, args: list[Value]) -> RTResult[BaseInstance]:
res = RTResult[BaseInstance]()

Expand Down Expand Up @@ -1240,6 +1352,18 @@ class Function(BaseFunction):
defaults: list[Optional[Value]]
should_auto_return: bool

def __help_repr__(self) -> str:
return f"Help on function {self.name}:\n\n{self.__help_repr_method__()}"

def __help_repr_method__(self) -> str:
arg_strs: list[str] = []
for i in range(len(self.arg_names)):
if self.defaults[i] is not None:
arg_strs.append(f"{self.arg_names[i]} = {self.defaults[i].__repr__()}")
else:
arg_strs.append(self.arg_names[i])
return f"| fun {self.name}({', '.join(arg_strs)})\n|\t{self.desc}\n|\n"

def __init__(
self,
name: Optional[str],
Expand All @@ -1248,12 +1372,14 @@ def __init__(
arg_names: list[str],
defaults: list[Optional[Value]],
should_auto_return: bool,
desc: str,
) -> None:
super().__init__(name, symbol_table)
self.body_node = body_node
self.arg_names = arg_names
self.defaults = defaults
self.should_auto_return = should_auto_return
self.desc = desc

def execute(self, args: list[Value], kwargs: dict[str, Value]) -> RTResult[Value]:
from core.interpreter import Interpreter # Lazy import
Expand All @@ -1280,7 +1406,13 @@ def execute(self, args: list[Value], kwargs: dict[str, Value]) -> RTResult[Value

def copy(self) -> Function:
copy = Function(
self.name, self.symbol_table, self.body_node, self.arg_names, self.defaults, self.should_auto_return
self.name,
self.symbol_table,
self.body_node,
self.arg_names,
self.defaults,
self.should_auto_return,
self.desc,
)
copy.set_context(self.context)
copy.set_pos(self.pos_start, self.pos_end)
Expand Down
5 changes: 4 additions & 1 deletion core/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ def visit_FuncDefNode(self, node: FuncDefNode, context: Context) -> RTResult[Val
res = RTResult[Value]()

func_name = node.var_name_tok.value if node.var_name_tok else None
func_desc = node.desc
assert func_name is None or isinstance(func_name, str)
body_node = node.body_node
arg_names = [str(arg_name.value) for arg_name in node.arg_name_toks]
Expand All @@ -483,7 +484,9 @@ def visit_FuncDefNode(self, node: FuncDefNode, context: Context) -> RTResult[Val
defaults.append(default_value)

func_value = (
Function(func_name, context.symbol_table, body_node, arg_names, defaults, node.should_auto_return)
Function(
func_name, context.symbol_table, body_node, arg_names, defaults, node.should_auto_return, func_desc
)
.set_context(context)
.set_pos(node.pos_start, node.pos_end)
)
Expand Down
3 changes: 3 additions & 0 deletions core/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ class FuncDefNode:
body_node: Node
should_auto_return: bool
static: bool
desc: str

pos_start: Position
pos_end: Position
Expand All @@ -248,13 +249,15 @@ def __init__(
body_node: Node,
should_auto_return: bool,
static: bool = False,
desc: str = "",
) -> None:
self.var_name_tok = var_name_tok
self.arg_name_toks = arg_name_toks
self.defaults = defaults
self.body_node = body_node
self.should_auto_return = should_auto_return
self.static = static
self.desc = desc

if self.var_name_tok:
self.pos_start = self.var_name_tok.pos_start
Expand Down
13 changes: 11 additions & 2 deletions core/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,9 @@ def func_def(self) -> ParseResult[Node]:
return res
assert body is not None

return res.success(FuncDefNode(var_name_tok, arg_name_toks, defaults, body, True, static=static))
return res.success(
FuncDefNode(var_name_tok, arg_name_toks, defaults, body, True, static=static, desc="[No Description]")
)

self.skip_newlines()
if self.current_tok.type != TT_LBRACE:
Expand All @@ -1309,6 +1311,13 @@ def func_def(self) -> ParseResult[Node]:
)

self.advance(res)
self.skip_newlines()

desc: str = "[No Description]"
if self.current_tok.type == TT_STRING:
# Set description
desc = str(self.current_tok.value)
self.advance(res)

body = res.register(self.statements())
if res.error:
Expand All @@ -1320,7 +1329,7 @@ def func_def(self) -> ParseResult[Node]:

self.advance(res)

return res.success(FuncDefNode(var_name_tok, arg_name_toks, defaults, body, False, static=static))
return res.success(FuncDefNode(var_name_tok, arg_name_toks, defaults, body, False, static=static, desc=desc))

def switch_statement(self) -> ParseResult[Node]:
res = ParseResult[Node]()
Expand Down
2 changes: 1 addition & 1 deletion radon.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def start_text() -> None:
f"\033[1;34mRadon {base_core.__version__} on {platform.machine()} {platform.system()} ({sys.platform})\033[0m"
)
print(f"\033[1;33mDocumentation:\033[0m {documentation_link}")
print("\033[1;32mType \033[1;31mlicense()\033[1;32m for more info\033[0m")
print("\033[1;32mType \033[1;31mhelp(obj), license(), credits()\033[1;32m for more info\033[0m")
print("\033[1;32mType \033[1;31mexit()\033[1;32m to quit the shell.\033[0m")


Expand Down
33 changes: 33 additions & 0 deletions tests/help.rn
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class Test
{
property1 = "Hello, World!"

fun __constructor__()
{

}

fun func1(arg1,arg2)
Almas-Ali marked this conversation as resolved.
Show resolved Hide resolved
{
"Description for func1"
}

fun func2(arg1)
{
"Description for func2"
}

fun no_description() {
return null
}
}

fun standalone(arg1,arg2)
{
"This is a standalone function"
}

help("Hello")
help(Test)
help(Test())
help(standalone)
1 change: 1 addition & 0 deletions tests/help.rn.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"code": 0, "stdout": "\nString\n\nA String is a sequence of characters.\n\nMethods:\n len(str) -> Returns the length of the string.\n\nString standard library methods:\n find(str) -> Find a character in a string and return its index (-1 if not found)\n to_int() -> Magic method to convert string to int if possible\n\nExample: \"Hello World!\"\n\nHelp on object Test:\n\nclass Test\n|\n| property1 = \"Hello, World!\"\n|\n| fun __constructor__()\n|\t[No Description]\n|\n| fun func1(arg1, arg2)\n|\tDescription for func1\n|\n| fun func2(arg1)\n|\tDescription for func2\n|\n| fun no_description()\n|\t[No Description]\n|\n\nHelp on object Test:\n\nclass Test\n|\n| property1 = \"Hello, World!\"\n|\n| fun __constructor__()\n|\t[No Description]\n|\n| fun func1(arg1, arg2)\n|\tDescription for func1\n|\n| fun func2(arg1)\n|\tDescription for func2\n|\n| fun no_description()\n|\t[No Description]\n|\n\nHelp on function standalone:\n\n| fun standalone(arg1, arg2)\n|\tThis is a standalone function\n|\n\n", "stderr": ""}