Skip to content

Commit

Permalink
Merge pull request #150 from Vardan2009/help-function
Browse files Browse the repository at this point in the history
Added `help()` shell function.
  • Loading branch information
Almas-Ali authored May 30, 2024
2 parents dd36da0 + 9ad2f73 commit a0ac8b8
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 5 deletions.
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 @@ -279,6 +292,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 @@ -345,6 +374,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 @@ -480,6 +522,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 @@ -656,6 +714,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 @@ -772,6 +853,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 @@ -946,6 +1036,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 @@ -1113,6 +1205,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 @@ -1193,6 +1295,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 @@ -1239,6 +1351,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 @@ -1247,12 +1371,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 @@ -1279,7 +1405,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)
{
"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": ""}

0 comments on commit a0ac8b8

Please sign in to comment.