diff --git a/core/builtin_funcs.py b/core/builtin_funcs.py index 2fda55c..2215014 100755 --- a/core/builtin_funcs.py +++ b/core/builtin_funcs.py @@ -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: @@ -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)) diff --git a/core/datatypes.py b/core/datatypes.py index e2376df..c145ab1 100755 --- a/core/datatypes.py +++ b/core/datatypes.py @@ -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,) @@ -149,6 +155,13 @@ def __next__(self) -> RTResult[Value]: def __str__(self) -> str: return "" + 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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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}}}" @@ -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__() @@ -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: @@ -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]() @@ -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], @@ -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 @@ -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) diff --git a/core/interpreter.py b/core/interpreter.py index 12f5339..48e4b84 100755 --- a/core/interpreter.py +++ b/core/interpreter.py @@ -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] @@ -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) ) diff --git a/core/nodes.py b/core/nodes.py index 9d5af21..eae9d8b 100755 --- a/core/nodes.py +++ b/core/nodes.py @@ -236,6 +236,7 @@ class FuncDefNode: body_node: Node should_auto_return: bool static: bool + desc: str pos_start: Position pos_end: Position @@ -248,6 +249,7 @@ 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 @@ -255,6 +257,7 @@ def __init__( 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 diff --git a/core/parser.py b/core/parser.py index 7d7cf32..4f1aae1 100755 --- a/core/parser.py +++ b/core/parser.py @@ -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: @@ -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: @@ -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]() diff --git a/radon.py b/radon.py index 379dd5d..964686c 100755 --- a/radon.py +++ b/radon.py @@ -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") diff --git a/tests/help.rn b/tests/help.rn new file mode 100644 index 0000000..708a895 --- /dev/null +++ b/tests/help.rn @@ -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) \ No newline at end of file diff --git a/tests/help.rn.json b/tests/help.rn.json new file mode 100644 index 0000000..8f37aa4 --- /dev/null +++ b/tests/help.rn.json @@ -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": ""} \ No newline at end of file