Skip to content

Commit

Permalink
Merge pull request #171 from radon-project/dir-builtin-function
Browse files Browse the repository at this point in the history
Added `dir()` builtin function.
  • Loading branch information
Almas-Ali authored Jul 24, 2024
2 parents 8cbc4e4 + 8e22f9d commit 34da0c0
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 10 deletions.
16 changes: 15 additions & 1 deletion core/builtin_classes/base_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def get(self, name: str) -> Optional[Value]:
return self.symbol_table.get(name)

def __repr__(self) -> str:
return f"<built-in class {self.name}>"
return f"<class {self.name!r} (built-in)>"


class BuiltInInstance(BaseInstance):
Expand Down Expand Up @@ -72,6 +72,14 @@ def operator(self, operator: str, *args: Value) -> ResultTuple:
assert value is not None
return value, None

def __repr__(self) -> str:
if "__string_display__" in dir(self.obj):
return str(getattr(self.obj, "__string_display__")())
return f"<instance '{self.obj}' (built-in)>"

def __len__(self) -> int:
return len(self.obj) # type: ignore


class BuiltInObjectMeta(type):
__symbol_table__: SymbolTable
Expand Down Expand Up @@ -104,6 +112,12 @@ class BuiltInObject(metaclass=BuiltInObjectMeta):
def __init__(self, parent_class: BuiltInClass) -> None:
self.parent_class = parent_class

def __str__(self) -> str:
return self.parent_class.name

def __repr__(self) -> str:
return self.parent_class.name


# Decorators for methods and operators
C = TypeVar("C", bound=Callable) # type: ignore
Expand Down
8 changes: 8 additions & 0 deletions core/builtin_classes/string_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ def add(self, other: String) -> RTResult[Value]:
res = RTResult[Value]()
return res.success(String(self.value + other.value))

def __string_display__(self) -> str:
"""This method helps __repr__ to display as we want."""
return self.value

def __len__(self) -> int:
"""Return the length of string."""
return len(self.value)

@args([])
@method
def upper(self, _ctx: Context) -> RTResult[Value]:
Expand Down
94 changes: 89 additions & 5 deletions core/builtin_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@
from sys import stdout
from typing import Callable, Generic, NoReturn, Optional, ParamSpec, Protocol, Sequence, Union, cast

from core.datatypes import Array, BaseFunction, Boolean, HashMap, Null, Number, PyAPI, String, Type, Value
from core.datatypes import (
Array,
BaseFunction,
Boolean,
Class,
Function,
HashMap,
Module,
Null,
Number,
PyAPI,
String,
Type,
Value,
)
from core.errors import Error, InvalidSyntaxError, RTError
from core.lexer import Lexer
from core.parser import Context, Parser, RTResult, SymbolTable
Expand Down Expand Up @@ -102,20 +116,20 @@ def execute_print_ret(self, exec_ctx: Context) -> RTResult[Value]:

@args(["value"])
def execute_len(self, exec_ctx: Context) -> RTResult[Value]:
val: Optional[Value] = exec_ctx.symbol_table.get("value")
val = exec_ctx.symbol_table.get("value")
try:
if val is not None and val.__class__ is not Value:
if hasattr(val, "__len__"):
# ret = int(val.__len__())
ret = int(getattr(val, "__len__")())
elif hasattr(val, "__exec_len__"):
# ret = int(val.__exec_len__())
ret = int(getattr(val, "__exec_len__")())
elif val.parent_class.instance_class.__len__ is not None: # type: ignore
ret = int(val.parent_class.instance_class.__len__()) # type: ignore
else:
raise TypeError()
return RTResult[Value]().success(Number(ret))
raise TypeError()
except TypeError:
except (TypeError, AttributeError):
try:
return RTResult[Value]().failure(
Error(
Expand Down Expand Up @@ -434,6 +448,75 @@ def execute_time_now(self, exec_ctx: Context) -> RTResult[Value]:

return RTResult[Value]().success(Number(time.time()))

@args(["obj"])
def execute_dir(self, exec_ctx: Context) -> RTResult[Value]:
from core.builtin_classes.base_classes import BuiltInInstance

obj: Module = exec_ctx.symbol_table.get("obj") # type: ignore

def variable_check(obj: String | Number | Boolean | HashMap | Null | Array | Value | str) -> bool:
"""
Checks if it's a datatype or not.
Returns True if it's a datatype to detect as variable.
"""
if (
isinstance(obj, String)
or isinstance(obj, Number)
or isinstance(obj, Boolean)
or isinstance(obj, HashMap)
or isinstance(obj, Null)
or isinstance(obj, Array)
):
return True
return False

# TODO: Datatypes will be supported after fixing OOP implementation.
if variable_check(obj):
return RTResult[Value]().failure(
Error(self.pos_start, self.pos_end, "TypeError", "Argument is must be Modules or Classes.")
)

variables: set[str] = set()
functions: set[str] = set()
classes: set[str] = set()
builtin_class_functions: set[str] = set()
# builtin_class_functions.add('__constructor__') # Class should always have a constructor.

f: Function | Class | Value
k: str

for k in obj.symbol_table.symbols.keys():
f = obj.symbol_table.symbols[k]
# print(k, f)
# print(type(k), type(f))
if isinstance(f, Function):
functions.add(k)
elif isinstance(f, Class):
classes.add(k)
elif variable_check(f):
if k not in {"true", "false", "null"}:
variables.add(k)
elif isinstance(f, BuiltInInstance):
bf: Function | Class | Value
bk: str

for bk in f.parent_class.symbol_table.symbols.keys():
bf = f.parent_class.symbol_table.symbols[bk]
if isinstance(bf, Function):
functions.add(bk)
elif isinstance(bf, Class):
classes.add(bk)
elif variable_check(f):
if k not in {"true", "false", "null"}:
variables.add(bk)
elif isinstance(bf, BuiltInFunction):
builtin_class_functions.add(bk)

result: list[str] = [*sorted(variables), *sorted(functions), *sorted(classes), *sorted(builtin_class_functions)]
string_list: list[String] = list(map(String, result))

return RTResult[Value]().success(Array(string_list)) # type: ignore

@args(["module"])
def execute_require(self, exec_ctx: Context) -> RTResult[Value]:
module_val = exec_ctx.symbol_table.get("module")
Expand Down Expand Up @@ -639,6 +722,7 @@ def create_global_symbol_table() -> SymbolTable:
ret.set("license", BuiltInFunction("license"))
ret.set("credits", BuiltInFunction("credits"))
ret.set("help", BuiltInFunction("help"))
ret.set("dir", BuiltInFunction("dir"))
# Built-in classes
ret.set("File", bic.BuiltInClass("File", bic.FileObject))
ret.set("String", bic.BuiltInClass("String", bic.StringObject))
Expand Down
10 changes: 6 additions & 4 deletions core/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from core.errors import Error, RTError
from core.parser import Context, RTResult, SymbolTable
from core.tokens import Position
from core.tokens import STDLIBS, Position

if TYPE_CHECKING:
from core.nodes import Node
Expand Down Expand Up @@ -1377,7 +1377,7 @@ def init(self, inst: BaseInstance, args: list[Value], kwargs: dict[str, Value])
return res.success(None)

def __repr__(self) -> str:
return f"<class {self.name}>"
return f"<class {self.name!r}>"


class Function(BaseFunction):
Expand Down Expand Up @@ -1462,7 +1462,7 @@ def copy(self) -> Function:
return copy

def __repr__(self) -> str:
return f"<function {self.name}>"
return f"<function {self.name} at {hex(id(self))}>"


class Module(Value):
Expand Down Expand Up @@ -1496,7 +1496,9 @@ def copy(self) -> Module:
return self

def __repr__(self) -> str:
return f"<module {self.name} @ {self.file_path!r}>"
if self.name in STDLIBS:
return f"<module {self.name!r} (stdlib)>"
return f"<module {self.name!r} from {self.file_path!r}>"


class Null(Value):
Expand Down
28 changes: 28 additions & 0 deletions examples/dir_example.rn
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import array

# stdlibs
print(dir(os))
print(dir(os.path))
print(dir(array))
print(dir(array.Array))

var arr = array.Array([])
print(dir(arr))

# built-in classes
var j = String("Some random string")
print(j)
print(len(j))
print(len(dir(j)))
print(dir(String))
print(dir(Requests))

# Datatypes
var my_var = "Some"

try {
print(dir(my_var))
} catch as err {
print(err) # TypeError: Argument is must be Modules or Classes.
}
7 changes: 7 additions & 0 deletions tests/dir.rn
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import os

assert len(dir(os)) == 22, "OS module has been changed"

for method in dir(os) {
assert type(method) == type(""), "dir() didn't return String data!"
}
1 change: 1 addition & 0 deletions tests/dir.rn.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"code": 0, "stdout": "", "stderr": ""}

0 comments on commit 34da0c0

Please sign in to comment.