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

Auto help generations #172

Merged
merged 19 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ help:
@echo "Targets:"
@echo " format - Format code"
@echo " lint - Check code quality and formattings"
@echo " typecheck - Static type checking in strict mode"
@echo " test - Run tests"
@echo " test-record - Record tests"
@echo " test-diff [FILE] - Diff tests"
@echo " py2c - Convert Python to C"
@echo " config2bin - Convert config to binary"
@echo " dev - Run development environment"
@echo " build - Build the project"
@echo " install - Install the project"
@echo " help - Show this help message"
Expand All @@ -27,6 +29,10 @@ lint:
@ruff format --check .
@ruff check .

.PHONY: typecheck
typecheck:
@mypy . --strict

.PHONY: test
test:
@$(PYTHON) test.py full
Expand All @@ -49,6 +55,13 @@ py2c:
config2bin:
@echo "Command in development"

.PHONY: dev
dev:
@echo "Running development requirements installation"
@$(PYTHON) -m pip install -r requirements-dev.txt
@echo "Running development tests"
@$(PYTHON) test.py full

.PHONY: build
build:
@echo "Command in development"
Expand Down
3 changes: 2 additions & 1 deletion core/builtin_classes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from core.builtin_classes.base_classes import BuiltInClass
from core.builtin_classes.builtins_object import BuiltinsObject
from core.builtin_classes.file_object import FileObject
from core.builtin_classes.json_object import JSONObject
from core.builtin_classes.requests_object import RequestsObject
from core.builtin_classes.string_object import StringObject

__all__ = ["BuiltInClass", "FileObject", "StringObject", "JSONObject", "RequestsObject"]
__all__ = ["BuiltInClass", "FileObject", "StringObject", "JSONObject", "RequestsObject", "BuiltinsObject"]
5 changes: 3 additions & 2 deletions core/builtin_classes/base_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@


class BuiltInClass(BaseClass):
desc: Optional[str]
instance_class: BuiltInObjectMeta

def __init__(self, name: str, instance_class: BuiltInObjectMeta) -> None:
super().__init__(name, instance_class.__symbol_table__)
def __init__(self, name: str, desc: Optional[str], instance_class: BuiltInObjectMeta) -> None:
super().__init__(name, desc, instance_class.__symbol_table__)
self.instance_class = instance_class

def create(self, args: list[Value]) -> RTResult[BaseInstance]:
Expand Down
63 changes: 63 additions & 0 deletions core/builtin_classes/builtins_object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from core.builtin_classes.base_classes import BuiltInObject, check, method, operator
from core.builtin_funcs import args
from core.datatypes import Array, Boolean, Null, String, Value
from core.errors import RTError
from core.parser import Context, RTResult


class BuiltinsObject(BuiltInObject):
"""Buili-in builtins manipulation object."""

@operator("__constructor__")
@check([], [])
def constructor(self) -> RTResult[Value]:
return RTResult[Value]().success(Null.null())

@args([])
@method
def show(self, ctx: Context) -> RTResult[Value]:
from core.builtin_funcs import global_symbol_table # Lazy import

return RTResult[Value]().success(Array(list(map(String, global_symbol_table.symbols.keys()))))

@args(["name", "obj"])
@method
def set(self, ctx: Context) -> RTResult[Value]:
from core.builtin_funcs import global_symbol_table # Lazy import

res = RTResult[Value]()
name = ctx.symbol_table.get("name")
obj = ctx.symbol_table.get("obj")

assert name is not None
assert obj is not None

if not isinstance(name, String):
return res.failure(RTError(name.pos_start, name.pos_end, "Can't set a non-string", ctx))

# if not isinstance(obj, Value):
# return res.failure(RTError(obj.pos_start, obj.pos_end, "Can't set a non-value", ctx))

try:
global_symbol_table.set(str(name), obj)
return res.success(Boolean(True))
except Exception as e:
return res.failure(RTError(name.pos_start, obj.pos_end, f"Error settting builtins: {str(e)}", ctx))

@args(["name"])
@method
def remove(self, ctx: Context) -> RTResult[Value]:
from core.builtin_funcs import global_symbol_table # Lazy import

res = RTResult[Value]()
name = ctx.symbol_table.get("name")
assert name is not None

if not isinstance(name, String):
return res.failure(RTError(name.pos_start, name.pos_end, "Can't set a non-string", ctx))

global_symbol_table.remove(str(name))
try:
return res.success(Boolean(True))
except Exception as e:
return res.failure(RTError(name.pos_start, name.pos_end, f"Error removing builtins: {str(e)}", ctx))
2 changes: 2 additions & 0 deletions core/builtin_classes/file_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@


class FileObject(BuiltInObject):
"""Buili-in file operation object."""

file: IO[str]

@operator("__constructor__")
Expand Down
2 changes: 2 additions & 0 deletions core/builtin_classes/json_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@


class JSONObject(BuiltInObject):
"""Buili-in json manipulation object."""

@operator("__constructor__")
@check([], [])
def constructor(self) -> RTResult[Value]:
Expand Down
2 changes: 2 additions & 0 deletions core/builtin_classes/requests_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@


class RequestsObject(BuiltInObject):
"""Buili-in API requests object."""

@operator("__constructor__")
@check([], [])
def constructor(self) -> RTResult[Value]:
Expand Down
2 changes: 1 addition & 1 deletion core/builtin_classes/string_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


class StringObject(BuiltInObject):
value: str
"""Buili-in String manipulation object."""

@operator("__constructor__")
@check([String], [String("")])
Expand Down
10 changes: 6 additions & 4 deletions core/builtin_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ def execute_str_get(self, exec_ctx: Context) -> RTResult[Value]:

@args(["value"])
def execute_int(self, exec_ctx: Context) -> RTResult[Value]:
"""Convert to Integer value."""
value = exec_ctx.symbol_table.get("value")
try:
return RTResult[Value]().success(Number(int(value.value))) # type: ignore
Expand Down Expand Up @@ -727,10 +728,11 @@ def create_global_symbol_table() -> SymbolTable:
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))
ret.set("Json", bic.BuiltInClass("Json", bic.JSONObject))
ret.set("Requests", bic.BuiltInClass("Requests", bic.RequestsObject))
ret.set("File", bic.BuiltInClass("File", bic.FileObject.__doc__, bic.FileObject))
ret.set("String", bic.BuiltInClass("String", bic.StringObject.__doc__, bic.StringObject))
ret.set("Json", bic.BuiltInClass("Json", bic.JSONObject.__doc__, bic.JSONObject))
ret.set("Requests", bic.BuiltInClass("Requests", bic.RequestsObject.__doc__, bic.RequestsObject))
ret.set("builtins", bic.BuiltInClass("builtins", bic.BuiltinsObject.__doc__, bic.BuiltinsObject))
return ret


Expand Down
14 changes: 14 additions & 0 deletions core/colortools.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,20 @@ def light_purple(text: str, bold: bool = False) -> str:
return f"{Style.BOLD.value}{value}"
return value

@staticmethod
def light_white(text: str, bold: bool = False) -> str:
value = f"{ForegroundColor.GRAY.value}{text}{ForegroundColor.TRANSPARENT.value}"
if bold:
return f"{Style.BOLD.value}{value}"
return value

@staticmethod
def deep_white(text: str, bold: bool = False) -> str:
value = f"{ForegroundColor.WHITE.value}{text}{ForegroundColor.TRANSPARENT.value}"
if bold:
return f"{Style.BOLD.value}{value}"
return value

@staticmethod
def underline(text: str, bold: bool = False) -> str:
return f"{Style.UNDERLINE.value}{text}{Style.CLEAR.value}"
Expand Down
100 changes: 72 additions & 28 deletions core/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Iterator as PyIterator
from typing import Optional, TypeAlias, TypeVar

from core.colortools import Log
from core.errors import Error, RTError
from core.parser import Context, RTResult, SymbolTable
from core.tokens import STDLIBS, Position
Expand All @@ -19,6 +20,34 @@
ResultTuple: TypeAlias = tuple[Optional["Value"], Optional[Error]]


ClassInstance: TypeAlias = Any


def generate_help_docs(obj: ClassInstance) -> str:
"""Generate help() docs for any class instance."""
from core.builtin_funcs import BuiltInFunction # Lazy import

output: str = ""

if isinstance(obj, Module):
output += f"Help on module {Log.deep_white(obj.name, bold=True)}:\n\n"
doc: str
if obj.__doc__:
doc = obj.__doc__
else:
doc = obj.docs
output += f"{Log.deep_white(obj.name, bold=True)}: {doc}\n"

elif isinstance(obj, BuiltInFunction):
output += f"Help on buili-in function {Log.deep_white(obj.name, bold=True)}:\n\n"
output += f"{Log.deep_white(obj.name, bold=True)}: {obj.__doc__}\n"

else:
output += f"{obj.__class__.__name__}: {obj.__doc__}\n"

return output


class Value:
pos_start: Position
pos_end: Position
Expand Down Expand Up @@ -114,9 +143,11 @@ def is_true(self) -> bool:

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

# return """
# This data type help is not implemented yet
# """

def illegal_operation(self, *others: Value) -> RTError:
if len(others) == 0:
Expand All @@ -135,7 +166,7 @@ def illegal_operation(self, *others: Value) -> RTError:


class Iterator(Value):
it: Generator[RTResult[Value], None, None]
"""An Iterator is an object that enables traversal over a collection, one element at a time."""

def __init__(self, generator: Generator[RTResult[Value], None, None]) -> None:
super().__init__()
Expand Down Expand Up @@ -1241,13 +1272,21 @@ def __exec_len__(self) -> Value | Null:
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"
result: str = f"Help on object {Log.deep_white(self.parent_class.name, bold=True)}:\n\nclass {Log.deep_white(self.parent_class.name, bold=True)}\n |\n"

desc: list[str] = self.parent_class.desc.split("\n") # type: ignore
desc = list(map(lambda s: s.strip(), desc))

for j in desc:
result += f" |\t{j}\n"
result += " |\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"
result += f" | {k} = {f!r}\n|\n"
return result

def bind_method(self, method: BaseFunction) -> RTResult[BaseFunction]:
Expand Down Expand Up @@ -1280,11 +1319,13 @@ def __repr__(self) -> str:

class BaseClass(Value, ABC):
name: str
desc: Optional[str]
symbol_table: SymbolTable

def __init__(self, name: str, symbol_table: SymbolTable) -> None:
def __init__(self, name: str, desc: Optional[str], symbol_table: SymbolTable) -> None:
super().__init__()
self.name = name
self.desc = desc
self.symbol_table = symbol_table

@abstractmethod
Expand Down Expand Up @@ -1331,13 +1372,22 @@ def get(self, name: str) -> Optional[Value]:
return method

def __help_repr__(self) -> str:
result: str = f"Help on object {self.name}:\n\nclass {self.name}\n|\n"
result: str = f"Help on object {Log.deep_white(self.name, bold=True)}:\n\nclass {Log.deep_white(self.name, bold=True)}\n |\n"
desc: list[str] = self.desc.split("\n") # type: ignore
desc = list(map(lambda s: s.strip(), desc))

for j in desc:
result += f" | {j}\n"
result += " |\n"
result += " | Methods defined here:\n"
result += " |\n"
# result += f"| {self.desc}\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"
result += f" | {Log.deep_white(k, bold=True)} = {f!r}\n |\n"
return result

def create(self, args: list[Value]) -> RTResult[BaseInstance]:
Expand Down Expand Up @@ -1388,16 +1438,24 @@ class Function(BaseFunction):
max_pos_args: int

def __help_repr__(self) -> str:
return f"Help on function {self.name}:\n\n{self.__help_repr_method__()}"
return f"Help on function {Log.deep_white(self.name, bold=True)}:\n\n{self.__help_repr_method__()}"

def __help_repr_method__(self) -> str:
arg_strs: list[str] = []
result: 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"
result += f" | {Log.deep_white(self.name, bold=True)}({', '.join(arg_strs)})\n"
desc: list[str] = self.desc.split("\n")
desc = list(map(lambda s: s.strip(), desc))

for j in desc:
result += f" |\t{j}\n"
result += " |\n"
return result

def __init__(
self,
Expand Down Expand Up @@ -1468,28 +1526,14 @@ def __repr__(self) -> str:
class Module(Value):
name: str
file_path: str
docs: str
symbol_table: SymbolTable

def __help_repr__(self) -> str:
result: str = f"Help on object {self.name}:\n\nmodule {self.name}\n|\n"
for k in self.symbol_table.symbols:
f = self.symbol_table.symbols[k]

if (type(f).__name__ == "BuiltInClass") or (type(f).__name__ == "BuiltInFunction"):
continue
if k == "null" or k == "false" or k == "true":
continue

if isinstance(f, Function):
result += f.__help_repr_method__()
elif isinstance(f, Value):
result += f"| {k} = {f!r}\n|\n"
return result

def __init__(self, name: str, file_path: str, symbol_table: SymbolTable) -> None:
def __init__(self, name: str, file_path: str, docs: str, symbol_table: SymbolTable) -> None:
super().__init__()
self.name = name
self.file_path = file_path
self.docs = docs
self.symbol_table = symbol_table

def copy(self) -> Module:
Expand Down
Loading