Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 12 additions & 4 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ Major release 2
.. raw:: html

<details>
<summary>2.1.0</summary>
<ul>
<li>The strict_types decorator is here! Refer to reference/builtin_decorators/decorators.strict_types of the documentation!</li>
</ul>
<summary>2.1.0 - 2.1.1</summary>
<details>
<summary>2.1.1</summary>
<ul>
<li>Fix #25</li>
</details>
<details>
<summary>2.1.0</summary>
<ul>
<li>The strict_types decorator is here! Refer to reference/builtin_decorators/decorators.strict_types of the documentation!</li>
</ul>
</details>
</details>

.. raw:: html
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "restructuredpython"
version = "2.1.0"
version = "2.1.1"
description = "A superset of Python with many new features, including full JS integration, multiline comments, header files, and optional curly brackets around control statements"
authors = [{name = "Rihaan Meher", email = "meherrihaan@gmail.com"}]
license = {text = "Apache-2.0"}
Expand Down
2 changes: 1 addition & 1 deletion restructuredpython.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: restructuredpython
Version: 2.1.0
Version: 2.1.1
Summary: A superset of Python with many new features, including full JS integration, multiline comments, header files, and optional curly brackets around control statements
Author-email: Rihaan Meher <meherrihaan@gmail.com>
License: Apache-2.0
Expand Down
4 changes: 3 additions & 1 deletion restructuredpython.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ MANIFEST.in
README.md
pyproject.toml
restructuredpython/__init__.py
restructuredpython/check_syntax.py
restructuredpython/cload.py
restructuredpython/parser.py
restructuredpython/restructuredpython.py
restructuredpython/test.py
restructuredpython.egg-info/PKG-INFO
restructuredpython.egg-info/SOURCES.txt
restructuredpython.egg-info/dependency_links.txt
Expand Down
24 changes: 24 additions & 0 deletions restructuredpython/check_syntax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def check_syntax(input_lines):
for i in range(len(input_lines)):
line = input_lines[i].strip()

if line.startswith(('} else', '} elif')):
raise SyntaxError(
f"Misplaced '{line}' statement at line {
i + 1}. (REPY-0001)") # fmt: skip
if line.startswith('} except'):
raise SyntaxError(
f"Misplaced 'except' statement at line {
i + 1}. (REPY-0002)") # fmt: skip
if line.startswith('} def'):
raise SyntaxError(
f"Misplaced 'def' statement at line {
i + 1}. (REPY-0003)") # fmt: skip
if line.startswith('} class'):
raise SyntaxError(
f"Misplaced 'class' statement at line {
i + 1}. (REPY-0004)") # fmt: skip
if line.startswith('} case'):
raise SyntaxError(
f"Misplaced 'case' statement at line {
i + 1}. (REPY-0005)") # fmt: skip
72 changes: 72 additions & 0 deletions restructuredpython/cload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import importlib
import struct
import sys
import os
import ctypes
import tomllib as toml

spec = importlib.util.find_spec("restructuredpython")
if spec and spec.origin:
package_dir = os.path.dirname(spec.origin)
io_dll = os.path.join(package_dir, "lib", "windows-libs", "io64.dll")

io32_dll = os.path.join(package_dir, "lib", "windows-lib", "io32.dll")

io_so = os.path.join(package_dir, "lib", "linux-libs", "io.so")

io_dylib = os.path.join(package_dir, "lib", "macos-libs", "io.dylib")

if sys.platform == "win32":
if (struct.calcsize("P") * 8) == 32:
lib = ctypes.WinDLL(io32_dll)
else:
lib = ctypes.WinDLL(io_dll)
elif sys.platform == "darwin":
lib = ctypes.CDLL(io_dylib)
else:
lib = ctypes.CDLL(io_so)


lib.check_file_exists.argtypes = [ctypes.c_char_p]
lib.check_file_exists.restype = ctypes.c_int

lib.read_file.argtypes = [ctypes.c_char_p]
lib.read_file.restype = ctypes.c_char_p

lib.write_file.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
lib.write_file.restype = ctypes.c_int

lib.read_binary_file.argtypes = [
ctypes.c_char_p, ctypes.POINTER(
ctypes.c_size_t)]
lib.read_binary_file.restype = ctypes.POINTER(ctypes.c_char)


def load_toml_binary(filename):
filename = str(filename)
size = ctypes.c_size_t()
raw_data_ptr = lib.read_binary_file(filename.encode(), ctypes.byref(size))

if not raw_data_ptr:
raise FileNotFoundError(f"Could not read {filename}")

raw_data = ctypes.string_at(raw_data_ptr, size.value)
return toml.loads(raw_data.decode())


def read_file_utf8(filename: str) -> str:
size = ctypes.c_size_t()
filename_bytes = filename.encode('utf-8')

ptr = lib.read_binary_file(filename_bytes, ctypes.byref(size))
if not ptr:
raise FileNotFoundError(f"File not found: {filename}")

raw_bytes = ctypes.string_at(ptr, size.value)

try:
text = raw_bytes.decode('utf-8')
except UnicodeDecodeError as e:
raise ValueError(f"File is not valid UTF-8: {e}")

return text
73 changes: 73 additions & 0 deletions restructuredpython/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from restructuredpython.check_syntax import check_syntax
import re

def parse_repython(code):
"""Parses the rePython code and converts it to valid Python code."""
def chain_pipeline(code):
parts = [part.strip() for part in code.split('|>')]
if len(parts) > 1:
def nest(parts):
if len(parts) == 1:
return parts[0]
else:
return f'{parts[-1]}({nest(parts[:-1])})'

variable, pipeline = parts[0].split(
'=') if '=' in parts[0] else ('', parts[0])
variable = variable.strip()
pipeline = pipeline.strip()

if variable:
nested_call = nest([pipeline] + parts[1:])
return f'{variable} = {nested_call}'
else:
return nest(parts)
return code

modified_code = []
inside_block = False
brace_stack = []
lines = code.splitlines()

check_syntax(lines)

inside_comment_block = False

for line in lines:
processed_line = chain_pipeline(line)

if inside_comment_block:
if processed_line.endswith("*/"):
modified_code.append(f"# {processed_line[:-2].strip()}")
inside_comment_block = False
else:
modified_code.append(f"# {processed_line.strip()}")
elif processed_line.startswith("/*") and processed_line.endswith("*/"):
modified_code.append(f"# {processed_line[2:-2].strip()}")
elif processed_line.startswith("/*"):
modified_code.append(f"# {processed_line[2:].strip()}")
inside_comment_block = True
elif processed_line.endswith("*/"):
modified_code.append(f"# {processed_line[:-2].strip()}")
else:
if re.match(
r'^\s*(if|for|while|def|try|elif|else|except|class|match|with|case)\s.*\{',
processed_line):
modified_code.append(processed_line.split('{')[0] + ':')
brace_stack.append('{')
inside_block = True
elif re.match(r'^\s*\}', processed_line) and inside_block:
brace_stack.pop()
inside_block = len(brace_stack) > 0
elif re.match(r'^\s*match\(', processed_line):
modified_code.append(processed_line.split('{')[0] + ':')
brace_stack.append('{')
inside_block = True
elif re.match(r'^\s*case', processed_line):
modified_code.append(processed_line.split('{')[0] + ':')
brace_stack.append('{')
inside_block = True
else:
modified_code.append(processed_line)

return '\n'.join(modified_code)
35 changes: 35 additions & 0 deletions restructuredpython/predefined/decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import time
import inspect
from functools import wraps
from typing import get_type_hints


class decorators:
Expand Down Expand Up @@ -58,3 +61,35 @@ def wrapper(*args, **kwargs):
print(f"{func.__name__} took {end_time - start_time:.2f} seconds.")
return result
return wrapper

@staticmethod
def strict_types(func):
sig = inspect.signature(func)
type_hints = get_type_hints(func)

@wraps(func)
def wrapper(*args, **kwargs):
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()

# Check argument types
for name, value in bound_args.arguments.items():
expected_type = type_hints.get(name)
if expected_type and not isinstance(value, expected_type):
raise TypeError(
f"Argument '{name}' expected {
expected_type.__name__}, got {
type(value).__name__}")

# Call the function
result = func(*args, **kwargs)

# Check return type
expected_return = type_hints.get('return')
if expected_return and not isinstance(result, expected_return):
raise TypeError(f"Return value expected {expected_return.__name__}, got {type(result).__name__}")

return result

return wrapper

Loading