diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index df804b1..04def0e 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -9,10 +9,18 @@ Major release 2
.. raw:: html
- 2.1.0
-
- - The strict_types decorator is here! Refer to reference/builtin_decorators/decorators.strict_types of the documentation!
-
+ 2.1.0 - 2.1.1
+
+ 2.1.1
+
+
+ 2.1.0
+
+ - The strict_types decorator is here! Refer to reference/builtin_decorators/decorators.strict_types of the documentation!
+
+
.. raw:: html
diff --git a/pyproject.toml b/pyproject.toml
index a070f18..5730919 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -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"}
diff --git a/restructuredpython.egg-info/PKG-INFO b/restructuredpython.egg-info/PKG-INFO
index b056c6e..72bf5e7 100644
--- a/restructuredpython.egg-info/PKG-INFO
+++ b/restructuredpython.egg-info/PKG-INFO
@@ -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
License: Apache-2.0
diff --git a/restructuredpython.egg-info/SOURCES.txt b/restructuredpython.egg-info/SOURCES.txt
index e381623..69bd662 100644
--- a/restructuredpython.egg-info/SOURCES.txt
+++ b/restructuredpython.egg-info/SOURCES.txt
@@ -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
diff --git a/restructuredpython/check_syntax.py b/restructuredpython/check_syntax.py
new file mode 100644
index 0000000..ff85a1d
--- /dev/null
+++ b/restructuredpython/check_syntax.py
@@ -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
\ No newline at end of file
diff --git a/restructuredpython/cload.py b/restructuredpython/cload.py
new file mode 100644
index 0000000..4b62651
--- /dev/null
+++ b/restructuredpython/cload.py
@@ -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
\ No newline at end of file
diff --git a/restructuredpython/parser.py b/restructuredpython/parser.py
new file mode 100644
index 0000000..1066bb7
--- /dev/null
+++ b/restructuredpython/parser.py
@@ -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)
\ No newline at end of file
diff --git a/restructuredpython/predefined/decorators.py b/restructuredpython/predefined/decorators.py
index 3e2d635..e77ecd0 100644
--- a/restructuredpython/predefined/decorators.py
+++ b/restructuredpython/predefined/decorators.py
@@ -1,4 +1,7 @@
import time
+import inspect
+from functools import wraps
+from typing import get_type_hints
class decorators:
@@ -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
+
diff --git a/restructuredpython/restructuredpython.py b/restructuredpython/restructuredpython.py
index 75b2828..0f991e1 100644
--- a/restructuredpython/restructuredpython.py
+++ b/restructuredpython/restructuredpython.py
@@ -1,238 +1,14 @@
import argparse
import re
-import sys
import os
-import warnings
import tempfile
from pathlib import Path
-import ctypes
import sys
import tomllib as toml
import fnmatch
-import pkgutil
-import importlib
-import struct
-
-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
-
-
-token_specification = [
- ('COMMENT', r'/\*.*?\*/'),
- ('IF', r'if'),
- ('FOR', r'for'),
- ('WHILE', r'while'),
- ('DEF', r'def'),
- ('ELIF', r'elif'),
- ('ELSE', r'else'),
- ('TRY', r'try'),
- ('EXCEPT', r'except'),
- ('CLASS', r'class'),
- ('WITH', r'with'),
- ('MATCH', r'match'),
- ('CASE', r'case'),
- ('PIPE', r'\|>'),
- ('IDENT', r'[A-Za-z_][A-Za-z0-9_]*'),
- ('NUMBER', r'\d+'),
- ('LBRACE', r'\{'),
- ('RBRACE', r'\}'),
- ('LPAREN', r'\('),
- ('RPAREN', r'\)'),
- ('EQUALS', r'='),
- ('SKIP', r'[ \t\n\r]+'),
- ('MISMATCH', r'.'),
-]
-
-token_regex = '|'.join(
- f'(?P<{pair[0]}>{pair[1]})' for pair in token_specification)
-
-
-def tokenize(code):
- """Tokenizes the rePython source code and handles multiline comments."""
- inside_multiline_comment = False
- for mo in re.finditer(token_regex, code):
- kind = mo.lastgroup
- value = mo.group()
-
- if kind == 'SKIP':
- continue
-
- elif kind == 'COMMENT':
- if value.startswith('/*') and value.endswith('*/'):
- comment_lines = value[2:-2].splitlines()
- for line in comment_lines:
- yield 'COMMENT', f"# {line.strip()}"
- continue
- elif kind == 'MISMATCH':
- warnings.warn(
- f"Unexpected character {
- value!r}. Continuing with compilation") # fmt: skip
- yield kind, value
-
- else:
- yield kind, value
-
-
-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
-
-
-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)
-
+from restructuredpython.parser import parse_repython
+from restructuredpython import cload
+from restructuredpython.cload import *
def compile_header_file(header_filename):
"""Compiles a .cdata file and returns the corresponding Python code."""
diff --git a/restructuredpython/test.py b/restructuredpython/test.py
deleted file mode 100644
index 5bf8cbb..0000000
--- a/restructuredpython/test.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import ctypes
-
-lib = ctypes.CDLL("restructuredpython/io.dll") # Windows: file_ops.dll
-
-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
-
-# Usage in Python
-filename = b"restructuredpython/source.c.repy" # Byte string for C compatibility
-print(lib.check_file_exists(filename))
-if lib.check_file_exists(filename) == 1:
- print('h')
- content = lib.read_file(filename)
- print(content.decode()) # Convert back to Python string
-
- # Write compiled output
- lib.write_file(b"output.py", content)
-
-print('done')