From b114a0414408f49ec85da91e2c18600bb7dbb3d2 Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Tue, 12 Mar 2024 23:12:26 -0400 Subject: [PATCH 1/8] Add keyword "await" as "_await" and update function calls --- .helix/cache/build_cache/test_hlx.py | 8 +-- .helix/cache/build_cache/test_hlx.py.lines | 24 ++++---- .helix/include/core.py | 54 +++-------------- .helix/include/core2.py | 31 ++++++++++ src/classes/Scope.py | 2 +- src/classes/Token.py | 4 +- src/core/base.py | 1 + src/functions/_functions.py | 69 ++++++++++++++-------- syntax/test.hlx | 5 +- 9 files changed, 109 insertions(+), 89 deletions(-) create mode 100644 .helix/include/core2.py diff --git a/.helix/cache/build_cache/test_hlx.py b/.helix/cache/build_cache/test_hlx.py index f26fb50..78646ab 100644 --- a/.helix/cache/build_cache/test_hlx.py +++ b/.helix/cache/build_cache/test_hlx.py @@ -4,7 +4,7 @@ # GENERATED FILE # -------------------------------------------------------------------------------- # Filename: test.hlx -# Generation Date: 2024-03-11 23:27:01 +# Generation Date: 2024-03-12 23:07:32 # Generator: Helix Transpiler # -------------------------------------------------------------------------------- # WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications @@ -60,7 +60,7 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception current_exception = exception relevant_frames = [] - early_replacements = dict((v, k) for k, v in {'...': 'None', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) + early_replacements = dict((v, k) for k, v in {'...': 'None', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) # First loop: filter out irrelevant frames index = 0 for frame in stack: @@ -186,12 +186,12 @@ def __lshift__(self: Any, a: str | hx_string): def main(argv: list[str | hx_string] | hx_list[str | hx_string]): a: int | hx_int = int("12") add ( 123 , 123 ) - print ( add . join ( ) ) + print ( add . _await ( ) ) do_something ( ) for i in C_For(i = int(0)).set_con('i < 10').set_inc('i ++'): printf ( "doing something else eeeee: %d" , i ) del i - do_something . join ( ) + print ( do_something . _await ( ) ) print ( "done" ) return 0 @hx__async diff --git a/.helix/cache/build_cache/test_hlx.py.lines b/.helix/cache/build_cache/test_hlx.py.lines index bfe8dd3..26056f8 100644 --- a/.helix/cache/build_cache/test_hlx.py.lines +++ b/.helix/cache/build_cache/test_hlx.py.lines @@ -191,25 +191,25 @@ 92 93 92 -95 96 97 -100 -100 +98 +101 101 102 -105 -105 +103 +106 106 -110 -110 +107 111 -112 111 -117 -117 -117 -117 +112 +113 +112 +118 +118 +118 +118 -1 -1 -1 diff --git a/.helix/include/core.py b/.helix/include/core.py index 9789624..2833cfc 100644 --- a/.helix/include/core.py +++ b/.helix/include/core.py @@ -20,6 +20,8 @@ from multimethod import subtype from typing import Type, TypeVar, Optional + +from beartype import beartype from src.panic import panic, standalone_tokenize_line as _H_tokenize_line__ from time import sleep #from include.c_cpp import __import_c__ @@ -197,42 +199,6 @@ def __void__(self) -> bool: DEFAULT_VALUE = void() -#class C_For: -# def __init__(self, **kwargs): -# # Initialize loop variables -# self.loop_vars = kwargs -# # Extract condition and increment expressions -# self.condition = "True" -# self.increment = "" -# # Evaluate initial conditions -# [exec(f"{var} = {value}") for var, value in self.loop_vars.items()] -# -# def __iter__(self): -# return self -# -# def __next__(self): -# if not self.loop_condition_met: -# raise StopIteration -# current_values = tuple(self.loop_vars.values()) -# exec(self.increment, None, self.loop_vars) -# self.loop_condition_met = eval(self.condition, None, self.loop_vars) -# return current_values if len(current_values) > 1 else current_values[0] -# -# def set_con(self, condition): -# self.condition = condition -# self.loop_condition_met = eval(self.condition, None, self.loop_vars) -# return self -# -# def set_inc(self, increment): -# self.increment = ( -# increment.replace("++", "+= 1") -# .replace("--", "-= 1") -# .replace("+*", "*= 1") -# .replace("-*", "*= -1") -# .replace("/-", "/ -1") -# .replace("/*", "*= 1") -# ) -# return self class C_For: def __init__(self, **kwargs): # Initialize loop variables @@ -242,18 +208,16 @@ def __init__(self, **kwargs): self.increment = "" # Evaluate initial conditions [exec(f"{var} = {value}") for var, value in self.loop_vars.items()] - self.loop_condition_met = eval(self.condition, None, self.loop_vars) - async def __aiter__(self): + def __iter__(self): return self - async def __anext__(self): + def __next__(self): if not self.loop_condition_met: - raise StopAsyncIteration + raise StopIteration current_values = tuple(self.loop_vars.values()) exec(self.increment, None, self.loop_vars) self.loop_condition_met = eval(self.condition, None, self.loop_vars) - await asyncio.sleep(0) # Yield control to allow other tasks to run return current_values if len(current_values) > 1 else current_values[0] def set_con(self, condition): @@ -273,8 +237,6 @@ def set_inc(self, increment): return self -from beartype import beartype - def hx__async(func: Callable) -> Callable: def run_thread(func, args, kwargs): func._result = func(*args, **kwargs) @@ -288,14 +250,14 @@ def wrapper(*args, **kwargs): func._thread.start() return func - def join(timeout=None): + def _await(timeout=None): if hasattr(func, "_thread"): func._thread.join(timeout) return getattr(func, "_result", None) return None - func.join = join - wrapper.join = join + func._await = _await + wrapper._await = _await return wrapper class hx_void(void): pass diff --git a/.helix/include/core2.py b/.helix/include/core2.py new file mode 100644 index 0000000..1f946e4 --- /dev/null +++ b/.helix/include/core2.py @@ -0,0 +1,31 @@ +# make a metaclass called HelixMeta that will be used to create all the primitive types +# when a fucntion in a subclass is called, the meta class should check if the types are the same as the subclass or as defiend in the altranitive_types var in the subclass and if they are not, then it should raise a TypeError + +import os +import sys +import threading +import time + +from typing import ( + Any, + Optional, + Union, + Tuple, + Mapping, + Type, + TypeVar, + Generic, + Callable, + Iterable, + Iterator, + Sequence, + Protocol, +) + +from concurrent.futures import ( + Future, + ThreadPoolExecutor, + ProcessPoolExecutor +) + +from multimethod import subtype \ No newline at end of file diff --git a/src/classes/Scope.py b/src/classes/Scope.py index b7d784a..51ca4fc 100644 --- a/src/classes/Scope.py +++ b/src/classes/Scope.py @@ -15,7 +15,7 @@ class Scope: children: list[Token_List] indent_level:int - variables: dict[str, dict[str, Any]] + variables: dict[str, str] functions: dict[str, dict[str, Any]] classes: dict[str, dict[str, Any]] operator_functions: dict[str, dict[str, Any]] diff --git a/src/classes/Token.py b/src/classes/Token.py index 0356744..bc8aa4e 100644 --- a/src/classes/Token.py +++ b/src/classes/Token.py @@ -37,7 +37,9 @@ def line(self, value: list[str]) -> None: @property - def token(self) -> list[str] | str: + def token(self) -> str: + if isinstance(self.__processed_line, list): + raise ValueError("Token is a list of strings, use the line property to get the list of strings") return self.__processed_line @token.setter diff --git a/src/core/base.py b/src/core/base.py index 883bba9..36371d2 100644 --- a/src/core/base.py +++ b/src/core/base.py @@ -368,6 +368,7 @@ def raise_as_python_exception(self, **kwargs): "false": "False", "null": "None", "none": "None", + "await": "_await", "&&": "and", "||": "or", "!": "not", diff --git a/src/functions/_functions.py b/src/functions/_functions.py index 58c0565..a4edcf6 100644 --- a/src/functions/_functions.py +++ b/src/functions/_functions.py @@ -2,14 +2,16 @@ from src.core.imports import ( Processed_Line, Token_List, + Token, map, Scope, panic, _class, INDENT_CHAR, + Optional ) -replace_function_name = map({ +replace_function_name: map[str, str] = map({ "==" : "__eq__", "//" : "__floordiv__", ">=" : "__ge__", @@ -44,19 +46,21 @@ "@" : "__matmul__", }) -def extract_variables(ast_line: Token_List, root_scope: Scope) -> dict[str, dict[str, str]]: +def extract_variables(ast_line: Token_List, root_scope: Scope) -> dict[Optional[str], ( + Optional[list[str]] | Optional[str] | dict[str, dict[str, str]] + )]: allowed_untyped = ( "self", "cls", "super" ) variables: dict[str, dict[str, str]] = {} - # remove the fn keyword - line = ast_line.line[1:] - - line = line[1:] - + line = ast_line.line[2:] # remove the fn keyword and the function name in_param = False + in_generic: bool = False + generic: str = "" + generic_count: int = 0 + if line[0].token == "(" and line[1].token == ")": if line[2].token == "->": # no parameters, just a return type @@ -74,10 +78,6 @@ def extract_variables(ast_line: Token_List, root_scope: Scope) -> dict[str, dict else: panic(SyntaxError(f": Expected an open parenthesis after the {root_scope.get_keyword('FUNCTION')} keyword"), "(", file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - in_generic: bool = False - generic: str = "" - generic_count: int = 0 - if in_param: while line: if line[0].token == ")": @@ -133,26 +133,44 @@ def extract_variables(ast_line: Token_List, root_scope: Scope) -> dict[str, dict continue panic(SyntaxError(f": Expected a colon and a type after the variable name"), line[0].token, file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - return variables + return variables # type: ignore + +class ExtractFuncParameters: + allowed_untyped = ("self", "cls", "super") + + def __init__(self, ast_line: Token_List, root_scope: Scope): + self.generic: str = "" + self.in_param: bool = False + self.generic_count: int = 0 + self.in_generic: bool = False + self.root_scope: Scope = root_scope + self.ast_line: Token_List = ast_line + self.return_type: Optional[str] = None + self.line: list[Token] = ast_line.line[2:] + self.variables: dict[str, dict[str, str]] = {} + self.params: Optional[dict[str, dict[str, str]]] = None + + def parse(self) -> dict[Optional[str], Optional[list[str]] | Optional[str] | dict[str, dict[str, str]]]: + ... + def handle_no_parameters(self) -> dict[Optional[str], dict[str, dict[str, str]]]: + ... + + def handle_closing_parenthesis(self) -> dict[Optional[str], dict[str, dict[str, str]]]: + ... + def process_parameter(self, in_generic: bool): + ... + + def process_modifiers(ast_list: Token_List, root_scope: Scope) -> list[str]: - # all modifiers are optional - # all allowed modifiers: - # - async - # - static - # - public - # - private - # - protected - # - final - # - virtual allowed_modifiers = ( root_scope.get_keyword('ASYNC'), root_scope.get_keyword('PRIVATE'), root_scope.get_keyword("PROTECTED"), root_scope.get_keyword("FINAL"), root_scope.get_keyword('UNSAFE'), - root_scope.get_keyword('STATIC') + root_scope.get_keyword('STATIC') # TODO: add virtual ) modifiers = [] @@ -220,11 +238,15 @@ def _function(ast_list: Token_List, current_scope: Scope, parent_scope: Scope, r if not variables["params"]: output += ")" else: + if not isinstance(variables["params"], dict): + raise ValueError("The variable type is not a dictionary") + for k, v in variables["params"].items(): if v["type"] == "void": panic(SyntaxError(f": The type void is not allowed for variables"), file=ast_list.file, line_no=ast_list.find_line_number(k)) output += f"{k}: {base.replace_primitive(v['type'], 0)}, " current_scope.variables[k] = v["type"] + output = output[:-2] + ")" if ast_list.line[-1].token == "<\\r1>" and ast_list.indent_level == 0: @@ -244,6 +266,7 @@ def _function(ast_list: Token_List, current_scope: Scope, parent_scope: Scope, r # else: # output = (f"\n{INDENT_CHAR*ast_list.indent_level}@hx__multi_method" + output) # if the type of parent_sope is an abstract class + if any([i == root_scope.get_keyword("ABSTRACT") for i in parent_scope.name]): output = f"\n{INDENT_CHAR*ast_list.indent_level}@hx__abstract_method" + output # if the type of parent_sope is an interface @@ -294,4 +317,4 @@ def _function(ast_list: Token_List, current_scope: Scope, parent_scope: Scope, r for _, decorator in enumerate(reversed(decorators)): output = f"{INDENT_CHAR*ast_list.indent_level}{decorator}\n{INDENT_CHAR*ast_list.indent_level}{output.strip()}" - return Processed_Line((f'\n{INDENT_CHAR*ast_list.indent_level}@overload_with_type_check' if not async_ else '\n') + output, ast_list) \ No newline at end of file + return Processed_Line((f'\n{INDENT_CHAR*ast_list.indent_level}@overload_with_type_check\n' if not async_ else '\n') + output, ast_list) \ No newline at end of file diff --git a/syntax/test.hlx b/syntax/test.hlx index bbb1289..7cd08a9 100644 --- a/syntax/test.hlx +++ b/syntax/test.hlx @@ -86,13 +86,14 @@ fn main(argv: list) { let a: int = "12"; add(123, 123); - print(add.join()); + print(add.await()); ~~ TODO: change to await do_something(); for (var i: int = 0; i < 10; i++) { printf("doing something else eeeee: %d", i); } - do_something.join(); + + print(do_something.await()); print("done"); return 0; } From 1121066e026fae2e4eedca8a4782eee49977f407 Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Wed, 13 Mar 2024 21:06:25 -0400 Subject: [PATCH 2/8] refactored functions.py, fixed error codes in base, added error code code support to panic. --- .helix/cache/build_cache/test_hlx.py | 5 +- .helix/cache/build_cache/test_hlx.py.lines | 43 +- .vscode/settings.json | 5 +- src/core/base.py | 109 ++- src/core/imports.py | 1 + src/functions/_functions.py | 954 +++++++++++++++------ src/panic.py | 67 +- src/token/normalize_tokens.py | 29 +- syntax/test.hlx | 4 + 9 files changed, 882 insertions(+), 335 deletions(-) diff --git a/.helix/cache/build_cache/test_hlx.py b/.helix/cache/build_cache/test_hlx.py index 78646ab..65d1e8c 100644 --- a/.helix/cache/build_cache/test_hlx.py +++ b/.helix/cache/build_cache/test_hlx.py @@ -4,7 +4,7 @@ # GENERATED FILE # -------------------------------------------------------------------------------- # Filename: test.hlx -# Generation Date: 2024-03-12 23:07:32 +# Generation Date: 2024-03-13 20:57:04 # Generator: Helix Transpiler # -------------------------------------------------------------------------------- # WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications @@ -182,6 +182,9 @@ def __init__(self: Any, __val: 'C_cout'): @overload_with_type_check def __lshift__(self: Any, a: str | hx_string): print ( a ) + @overload_with_type_check + def subtract(): + None @overload_with_type_check def main(argv: list[str | hx_string] | hx_list[str | hx_string]): a: int | hx_int = int("12") diff --git a/.helix/cache/build_cache/test_hlx.py.lines b/.helix/cache/build_cache/test_hlx.py.lines index 26056f8..a1e0a6f 100644 --- a/.helix/cache/build_cache/test_hlx.py.lines +++ b/.helix/cache/build_cache/test_hlx.py.lines @@ -182,34 +182,37 @@ 80 80 81 -85 -85 -86 +83 +83 +84 +88 88 89 91 92 -93 -92 +94 +95 96 -97 -98 -101 +95 +99 +100 101 -102 -103 +104 +104 +105 106 -106 -107 -111 +109 +109 111 -112 -113 -112 -118 -118 -118 -118 +115 +115 +116 +117 +116 +122 +122 +122 +122 -1 -1 -1 diff --git a/.vscode/settings.json b/.vscode/settings.json index 18e94b3..2e7771b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -66,5 +66,8 @@ "C_Cpp_Runner.useLinkTimeOptimization": false, "C_Cpp_Runner.msvcSecureNoWarnings": false, "python.analysis.typeCheckingMode": "basic", - "python.analysis.autoImportCompletions": true + "python.analysis.autoImportCompletions": true, + "cSpell.words": [ + "rquirements" + ] } \ No newline at end of file diff --git a/src/core/base.py b/src/core/base.py index 36371d2..9b3c9ba 100644 --- a/src/core/base.py +++ b/src/core/base.py @@ -1,3 +1,4 @@ +from __future__ import annotations from src.core.imports import ( re, map, @@ -9,6 +10,7 @@ Token_List, WorkerPool, Processed_Line, + NoReturn, _unless, _for, @@ -19,6 +21,7 @@ _include, file_cache, + panic, ) @@ -149,7 +152,7 @@ class ERROR_CODES(enum.Enum): ) SYNTAX_INVALID_SYNTAX = ( "HEX-101", - "General syntax error at {location}", + "Syntax error: {location}", SyntaxError, ) SYNTAX_MISSING_SEMICOLON = ( @@ -172,13 +175,19 @@ class ERROR_CODES(enum.Enum): "Unsupported syntax in Helix: {syntax}", SyntaxError, ) - + SYNTAX_MISSING_EXCEPTED_TOKEN = ( + "HEX-106", + "Missing expected token: {token}", + SyntaxError, + ) + # Type and Declaration Errors TYPE_INVALID_CAST = ( "HEX-200", "Invalid type cast: {details}", TypeError, ) + TYPE_UNDECLARED_VARIABLE = ( "HEX-201", "Use of undeclared variable: {variable}", @@ -194,7 +203,17 @@ class ERROR_CODES(enum.Enum): "Redeclaration of a variable or function: {identifier}", SyntaxError, ) - + TYPE_UNDECLARED = ( + "HEX-204", + "Type for {identifier} is undeclared", + TypeError, + ) + TYPE_INVALID_TYPE_IN_CONTEXT = ( + "HEX-205", + "Invalid type used in this location: {type}", + TypeError, + ) + # Semantic Errors SEMANTIC_UNRESOLVED_REFERENCE = ( "HEX-300", @@ -307,20 +326,86 @@ class ERROR_CODES(enum.Enum): RuntimeError, ) - def __init__(self, code, template, py_exception): + # Specific Errors + OPERATOR_OVERLOADING_OUTSIDE_CLASS = ( + "HEX-900", + "Operator cannot be overloaded outside a class", + SyntaxError, + ) + FUNCTION_STATIC_OUTSIDE_CLASS = ( + "HEX-901", + "Static function cannot be declared outside a class", + SyntaxError, + ) + FUNCTION_FINAL_OUTSIDE_CLASS = ( + "HEX-902", + "Final function cannot be declared outside a class", + SyntaxError, + ) + FUNCTION_INVALID_MODIFIERS = ( + "HEX-903", + "Invalid modifiers for function: {modifiers}", + SyntaxError, + ) + def __init__(self, code: str, template: str, py_exception: type[BaseException]) -> None: self.code = code self.template = template self.py_exception = py_exception + self.formatted: bool = False - def format_error(self, **kwargs): + def format(self, *args, **kwargs) -> ERROR_CODES: """Format the error message with provided arguments.""" - formatted_message = self.template.format(**kwargs) - return f"{self.code} - {formatted_message}" - - def raise_as_python_exception(self, **kwargs): - """Raise the corresponding Python exception with the formatted error message.""" - raise self.py_exception(self.format_error(**kwargs)) - + if args: + pattern = r"\{([^{}]*)\}" + vars = re.findall(pattern, self.template) + raise ValueError( + "\u001b[31m\n" + f"Error message '{self.template}' has {len(vars)} variables, but {len(args)} were provided.\n" + "did i mean\n\t" + ", ".join([f"{var}={arg}" for var, arg in zip(vars, args)]) + + "\u001b[0m" + ) + + _prefix = kwargs.pop("_prefix", False) + if not _prefix: + kwargs = {key: ('\"' + str(value) + '\"') if value is not None else '' for key, value in kwargs.items()} + else: + # add the prefix to the first key + kwargs[list(kwargs.keys())[0]] = _prefix + kwargs[list(kwargs.keys())[0]] + kwargs = {key: (str(value)) if value is not None else '' for key, value in kwargs.items()} + + try: + formatted_message = self.template.format(**kwargs) + except KeyError: + pattern = r"\{([^{}]*)\}" + vars = re.findall(pattern, self.template) + if len(vars) == len(kwargs): + raise ValueError( + "\u001b[31m\n" + f"Error message '{self.template}' has {len(vars)} variables, but {len(kwargs)} were provided.\n" + "did i mean\n\t" + ", ".join([f"{var}={arg}" for var, arg in zip(vars, kwargs.values())]) + + "\u001b[0m" + ) + raise ValueError( + "\u001b[31m\n" + f"Error message '{self.template}' has {len(vars)} variables, but {len(kwargs)} were provided.\n" + "did i mean\n\t" + ", ".join([f"{var}={arg}" for var, arg in zip(vars, kwargs.values())]) + + "\u001b[0m" + ) + + self.template = formatted_message + self.formatted = True + return self + + def panic(self, *args, **kwargs) -> NoReturn: + if not self.formatted and ('{' in self.template and '}' in self.template): + raise ValueError( + "Error message not formatted before panic", + "requirements: " + ", ".join(re.findall(r"\{([^{}]*)\}", self.template)) + ) + + panic(self.py_exception(self.template), *args, **kwargs, _code = self.code) + + IGNORE_TYPES_MAP: tuple[str, ...] = ("Callable",) PRIMITIVES_MAP: map[str, tuple[str, str]] = map( diff --git a/src/core/imports.py b/src/core/imports.py index 9f844a8..8edef08 100644 --- a/src/core/imports.py +++ b/src/core/imports.py @@ -46,6 +46,7 @@ Iterator, Sequence, Protocol, + NoReturn, ) from concurrent.futures import ( diff --git a/src/functions/_functions.py b/src/functions/_functions.py index a4edcf6..3d66767 100644 --- a/src/functions/_functions.py +++ b/src/functions/_functions.py @@ -5,178 +5,286 @@ Token, map, Scope, - panic, _class, INDENT_CHAR, - Optional + Optional, ) -replace_function_name: map[str, str] = map({ - "==" : "__eq__", - "//" : "__floordiv__", - ">=" : "__ge__", - "<=" : "__le__", - "<<" : "__lshift__", - "!=" : "__ne__", - "**" : "__pow__", - "//" : "__rfloordiv__", - ">>" : "__rshift__", - "**" : "__rpow__", - "%" : "__rmod__", - "%" : "__mod__", - "*" : "__rmul__", - "&" : "__and__", - "*" : "__mul__", - "+" : "__radd__", - "+" : "__add__", - "-" : "__neg__", - "|" : "__ror__", - "~" : "__invert__", - "+" : "__pos__", - ">" : "__gt__", - "&" : "__rand__", - "<" : "__lt__", - "-" : "__rsub__", - "/" : "__rtruediv__", - "|" : "__or__", - "^" : "__rxor__", - "-" : "__sub__", - "/" : "__truediv__", - "^" : "__xor__", - "@" : "__matmul__", -}) - -def extract_variables(ast_line: Token_List, root_scope: Scope) -> dict[Optional[str], ( - Optional[list[str]] | Optional[str] | dict[str, dict[str, str]] - )]: - allowed_untyped = ( - "self", - "cls", - "super" - ) - variables: dict[str, dict[str, str]] = {} - line = ast_line.line[2:] # remove the fn keyword and the function name - in_param = False - in_generic: bool = False - generic: str = "" - generic_count: int = 0 - - if line[0].token == "(" and line[1].token == ")": - if line[2].token == "->": +replace_function_name: map[str, str] = map( + { + "==": "__eq__", + "//": "__floordiv__", + ">=": "__ge__", + "<=": "__le__", + "<<": "__lshift__", + "!=": "__ne__", + "**": "__pow__", + ">>": "__rshift__", + "%": "__mod__", + "*": "__rmul__", + "&": "__and__", + "-": "__neg__", + "~": "__invert__", + ">": "__gt__", + "<": "__lt__", + "|": "__or__", + "-": "__sub__", + "/": "__truediv__", + "^": "__xor__", + "@": "__matmul__", + "r//": "__rfloordiv__", + "r**": "__rpow__", + "r%": "__rmod__", + "r*": "__mul__", + "r+": "__radd__", + "r+": "__add__", + "r|": "__ror__", + "r&": "__rand__", + "r-": "__rsub__", + "r/": "__rtruediv__", + "r^": "__rxor__", + } +) + + +class ExtractTypedParamsFromFunc: + allowed_untyped = ("self", "cls", "super") + + def __init__( + self, ast_line: Token_List, root_scope: Scope + ): + self.line_copy: Token_List = ast_line.copy() + self.generic: str = "" + self.in_param: bool = False + self.generic_count: int = 0 + self.in_generic: bool = False + self.root_scope: Scope = root_scope + self.ast_line: Token_List = ast_line + self.return_type: Optional[str] = None + self.line: list[Token] = ast_line.line[2:] + self.variables: dict[str, dict[str, str]] = {} + self.params: Optional[dict[str, dict[str, str]]] = ( + None + ) + + def parse( + self, + ) -> dict[ + str, Optional[str] | dict[str, dict[str, str]] + ]: + token: str = self.line[0].token + + if token == "(" and self.line[1].token == ")": + return self.handle_no_parameters() + elif token == "(": + self.in_param = True + self.line = self.line[1:] + else: + base.ERROR_CODES.SYNTAX_MISSING_EXCEPTED_TOKEN.format( + token="(" + ).panic( + ")", + file=self.ast_line.file, + line_no=self.ast_line.find_line_number( + self.root_scope.get_keyword("FUNCTION") + ), + ) + + while self.in_param and self.line: + token = self.line[0].token + + if token == ")": + del token + return self.handle_end_of_parameters() + elif token == "<": + self.handle_generic_start() + elif token == ">": + self.handle_generic_end() + elif self.in_generic: + self.handle_generic_content() + elif token == ",": + self.line = self.line[1:] + else: + self.handle_parameter() + + del token + raise SyntaxError( + f": Expected a closing parenthesis after the parameters" + ) + + def handle_end_of_parameters( + self, + ) -> dict[ + str, Optional[str | dict[str, dict[str, str]]] + ]: + if self.line[1].token == "->": # no parameters, just a return type - if line[3].token == ":" or line[3].token == "<\\r1>": - panic(SyntaxError(f": Expected a return type after the {root_scope.get_keyword('FUNCTION')} keyword"), "->", file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - return {"return_type": line[3].token, "params": {}} + if ( + self.line[2].token == ":" + or self.line[2].token == "<\\r1>" + ): + base.ERROR_CODES.SYNTAX_MISSING_EXCEPTED_TOKEN.format( + token="->" + ).panic( + "->", + file=self.ast_line.file, + line_no=self.ast_line.find_line_number( + self.root_scope.get_keyword( + "FUNCTION" + ) + ), + ) + return { + "return_type": self.line[2].token, + "params": self.variables, + } else: # no parameters, no return type - if line[2].token != ":" and line[2].token != "<\\r1>": - panic(SyntaxError(f": Expected a colon after the parameters"), ":", file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - return {"return_type": None, "params": {}} - elif line[0].token == "(": - in_param = True - line = line[1:] - else: - panic(SyntaxError(f": Expected an open parenthesis after the {root_scope.get_keyword('FUNCTION')} keyword"), "(", file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - - if in_param: - while line: - if line[0].token == ")": - if line[1].token == "->": - # no parameters, just a return type - if line[2].token == ":" or line[2].token == "<\\r1>": - panic(SyntaxError(f": Expected a return type after the {root_scope.get_keyword('FUNCTION')} keyword"), "->", file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - return {"return_type": line[2].token, "params": variables} - else: - # no parameters, no return type - if line[1].token != ":" and line[1].token != "<\\r1>": - panic(SyntaxError(f": Expected a colon after the parameters"), ":", file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - return {"return_type": None, "params": variables} - if line[0].token == "<": - generic_count += 1 - in_generic = True if generic_count > 0 else False - if not in_generic: - in_generic = False - variables[tuple(variables.keys())[-1]] = {"type": variables[tuple(variables.keys())[-1]]["type"] + generic} - line = line[1:] - continue - generic += "[" + " " - line = line[1:] - elif line[0].token == ">": - generic_count -= 1 - in_generic = True if generic_count > 0 else False - generic += " " + "]" - if not in_generic: - in_generic = False - variables[tuple(variables.keys())[-1]] = {"type": variables[tuple(variables.keys())[-1]]["type"] + generic} - line = line[1:] - continue - line = line[1:] - elif in_generic: - generic += line[0].token - line = line[1:] - elif line[0].token == ",": - line = line[1:] + if ( + self.line[1].token != ":" + and self.line[1].token != "<\\r1>" + ): + base.ERROR_CODES.SYNTAX_MISSING_EXCEPTED_TOKEN.format( + token="{}" + ).panic( + "fn", + file=self.ast_line.file, + line_no=self.ast_line.find_line_number( + self.root_scope.get_keyword( + "FUNCTION" + ) + ), + ) + return { + "return_type": None, + "params": self.variables, + } + + def handle_generic_start(self) -> None: + self.generic_count += 1 + self.in_generic = True + self.generic += "[" + " " + self.line = self.line[1:] + + def handle_generic_end(self) -> None: + self.generic_count -= 1 + self.in_generic = self.generic_count > 0 + self.generic += " " + "]" + + if not self.in_generic: + self.variables[ + tuple(self.variables.keys())[-1] + ]["type"] += self.generic + self.generic = "" + self.line = self.line[1:] + + def handle_generic_content(self) -> None: + self.generic += self.line[0].token + self.line = self.line[1:] + + def handle_parameter(self) -> None: + token: str = self.line[0].token + + if self.line[1].token == ":": + self.variables[token] = { + "type": self.line[2].token + } + self.line = self.line[3:] + else: + if token == "=": + base.ERROR_CODES.SYNTAX_UNSUPPORTED_SYNTAX.format( + syntax="=" + ).panic( + token, + file=self.ast_line.file, + line_no=self.ast_line.find_line_number( + self.root_scope.get_keyword( + "FUNCTION" + ) + ), + ) + if token in self.allowed_untyped: + self.variables[token] = {"type": "Any"} + self.line = self.line[1:] else: - if line[1].token == ":": - variables[line[0].token] = {"type": line[2].token} - line = line[3:] - else: - if line[0].token == "=": - panic(SyntaxError(f": Default values are not allowed in function parameters"), line[0].token, file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - if line[0].token == "<": - in_generic = True - generic = "" - continue - if line[0].token in allowed_untyped: - variables[line[0].token] = {"type": "Any"} - line = line[1:] - continue - panic(SyntaxError(f": Expected a colon and a type after the variable name"), line[0].token, file=ast_line.file, line_no=ast_line.find_line_number(root_scope.get_keyword('FUNCTION'))) - - return variables # type: ignore - -class ExtractFuncParameters: - allowed_untyped = ("self", "cls", "super") + base.ERROR_CODES.TYPE_UNDECLARED.format( + identifier=tuple(self.variables.keys())[ + -1 + ] + ).panic( + tuple(self.variables.keys())[-1], + file=self.ast_line.file, + line_no=self.ast_line.find_line_number( + self.root_scope.get_keyword( + "FUNCTION" + ) + ), + ) - def __init__(self, ast_line: Token_List, root_scope: Scope): - self.generic: str = "" - self.in_param: bool = False - self.generic_count: int = 0 - self.in_generic: bool = False - self.root_scope: Scope = root_scope - self.ast_line: Token_List = ast_line - self.return_type: Optional[str] = None - self.line: list[Token] = ast_line.line[2:] - self.variables: dict[str, dict[str, str]] = {} - self.params: Optional[dict[str, dict[str, str]]] = None - - def parse(self) -> dict[Optional[str], Optional[list[str]] | Optional[str] | dict[str, dict[str, str]]]: - ... - - def handle_no_parameters(self) -> dict[Optional[str], dict[str, dict[str, str]]]: - ... - - def handle_closing_parenthesis(self) -> dict[Optional[str], dict[str, dict[str, str]]]: - ... - def process_parameter(self, in_generic: bool): - ... - - - -def process_modifiers(ast_list: Token_List, root_scope: Scope) -> list[str]: + del token + + def handle_no_parameters( + self, + ) -> dict[ + str, Optional[str] | dict[str, dict[str, str]] + ]: + if self.line[2].token == "->": + # no parameters, just a return type + if ( + self.line[3].token == ":" + or self.line[3].token == "<\\r1>" + ): + base.ERROR_CODES.SYNTAX_MISSING_EXCEPTED_TOKEN.format( + token="->" + ).panic( + "->", + file=self.ast_line.file, + line_no=self.ast_line.line[ + 0 + ].line_number, + ) + return { + "return_type": self.line[3].token, + "params": {}, + } + else: + # no parameters, no return type + if ( + self.line[2].token != ":" + and self.line[2].token != "<\\r1>" + ): + base.ERROR_CODES.SYNTAX_MISSING_EXCEPTED_TOKEN.format( + token="{}" + ).panic( + "fn", + file=self.ast_line.file, + line_no=self.ast_line.line[ + 0 + ].line_number, + ) + return {"return_type": None, "params": {}} + + +def process_modifiers( + ast_list: Token_List, root_scope: Scope +) -> list[str]: allowed_modifiers = ( - root_scope.get_keyword('ASYNC'), - root_scope.get_keyword('PRIVATE'), + root_scope.get_keyword("ASYNC"), + root_scope.get_keyword("PRIVATE"), root_scope.get_keyword("PROTECTED"), root_scope.get_keyword("FINAL"), - root_scope.get_keyword('UNSAFE'), - root_scope.get_keyword('STATIC') # TODO: add virtual + root_scope.get_keyword("UNSAFE"), + root_scope.get_keyword( + "STATIC" + ), # TODO: add virtual/kernel ) modifiers = [] index = 0 - - if ast_list.line[0].token != root_scope.get_keyword('FUNCTION'): + + if ast_list.line[0].token != root_scope.get_keyword( + "FUNCTION" + ): line = ast_list.line.copy() while line: index += 1 @@ -184,137 +292,421 @@ def process_modifiers(ast_list: Token_List, root_scope: Scope) -> list[str]: modifiers.append(line[0].token) line = line[1:] else: - if line[0].token != root_scope.get_keyword('FUNCTION'): + if line[0].token != root_scope.get_keyword( + "FUNCTION" + ): return modifiers else: break - ast_list.line = ast_list.line[index-1:] + ast_list.line = ast_list.line[index - 1 :] return modifiers else: return modifiers -def contains(line: Token_List, compare: tuple): - # check if any of the tokens in compare are in line and - return any([_ in line for _ in compare]) - -# static async fn factorial(n: int) -> int { -def _function(ast_list: Token_List, current_scope: Scope, parent_scope: Scope, root_scope: Scope) -> Processed_Line: - decorators = [] - if ast_list.line[0].token == "#": - for _ in range(ast_list.count("#")): - decorator = ast_list.get_between("[", "]") - ast_list.line = ast_list.line[len(decorator)+3:] - decorators.append('@' + ' '.join([__.token for __ in decorator])) - - modifiers = process_modifiers(ast_list, root_scope) - - not_allowed_classes = ( - parent_scope.get_keyword("CLASS"), - parent_scope.get_keyword("INTERFACE"), - parent_scope.get_keyword("STRUCT"), - parent_scope.get_keyword("UNION"), - parent_scope.get_keyword("ENUM"), - parent_scope.get_keyword("ABSTRACT"), - parent_scope.get_keyword("UNSAFE"), - ) - - if ast_list.line[0].token != root_scope.get_keyword('FUNCTION'): - if ast_list.splice(len(modifiers))[0] in not_allowed_classes: - return _class(ast_list.splice(len(modifiers)), current_scope, parent_scope, root_scope, modifiers) - - panic(SyntaxError(f": Expected the {root_scope.get_keyword('FUNCTION')} keyword"), ast_list[1].token, file=ast_list.file, line_no=ast_list[0].line_number) - - variables = extract_variables(ast_list, root_scope) - name = ast_list.line[1].token - - if name in replace_function_name: - if not any([i in not_allowed_classes for i in parent_scope.name]): - panic(SyntaxError(f": Cannot overload a unary operator outside a class"), name, file=ast_list.file, line_no=ast_list.find_line_number(name)) - name = replace_function_name[name] - - output = f"def {name}(" if name not in parent_scope.functions else f"def _(" - - if not variables["params"]: - output += ")" - else: - if not isinstance(variables["params"], dict): - raise ValueError("The variable type is not a dictionary") - - for k, v in variables["params"].items(): +class Function: + def __init__( + self, + ast_list: Token_List, + current_scope: Scope, + parent_scope: Scope, + root_scope: Scope, + ): + self.ast_list: Token_List = ast_list + self.current_scope: Scope = current_scope + self.parent_scope: Scope = parent_scope + self.root_scope: Scope = root_scope + self.decorators: list[str] = [] + self.modifiers: dict[str, bool] = {} + self.not_allowed_classes: tuple[str, ...] = ( + parent_scope.get_keyword("CLASS"), + parent_scope.get_keyword("INTERFACE"), + parent_scope.get_keyword("STRUCT"), + parent_scope.get_keyword("UNION"), + parent_scope.get_keyword("ENUM"), + parent_scope.get_keyword("ABSTRACT"), + parent_scope.get_keyword("UNSAFE"), + ) + + self.variables: dict[ + str, str | dict[str, dict[str, str]] | None + ] = {} + self.name: str = "" + self.output: str = "" + + def parse(self) -> Processed_Line: + self._process_decorators() + self._process_modifiers() + + if self.ast_list.line[ + 0 + ].token != self.root_scope.get_keyword("FUNCTION"): + self._handle_non_function() + + self._process_variables() + self.add_to_output( + f"def {self.name}(" + if self.name not in self.parent_scope.functions + else f"def _(" + ) + + self._process_params() + self._process_return_type() + self.add_to_output( + self.__add_indent(self.output), 2 + ) + + if any( + [ + i == self.root_scope.get_keyword("ABSTRACT") + for i in self.parent_scope.name + ] + ) or any( + [ + i + == self.root_scope.get_keyword("INTERFACE") + for i in self.parent_scope.name + ] + ): + self.decorators.append("@hx__abstract_method") + + self._process_modifiers_step_2() + + self.parent_scope.functions[self.name] = { + "type": self.variables["return_type"], + "params": self.variables["params"], + self.root_scope.get_keyword( + "STATIC" + ): self.modifiers.get( + self.root_scope.get_keyword("STATIC"), False + ), + self.root_scope.get_keyword( + "ASYNC" + ): self.modifiers.get( + self.root_scope.get_keyword("ASYNC"), False + ), + self.root_scope.get_keyword( + "PRIVATE" + ): self.modifiers.get( + self.root_scope.get_keyword("PRIVATE"), + False, + ), + self.root_scope.get_keyword( + "UNSAFE" + ): self.modifiers.get( + self.root_scope.get_keyword("UNSAFE"), False + ), + self.root_scope.get_keyword( + "FINAL" + ): self.modifiers.get( + self.root_scope.get_keyword("FINAL"), False + ), + } + + if "unknown" in self.output: + self.output = self.output + + ( + self.decorators.append( + "@overload_with_type_check" + ) + if ( + not self.modifiers.get( + self.root_scope.get_keyword("ASYNC"), + False, + ) + ) + else (None) + ) + + decorators: str = "" + if self.decorators: + for _, decorator in enumerate( + reversed(self.decorators) + ): + decorators += self.__add_indent(decorator) + + self.add_to_output( + self.__add_indent( + self.output.replace( + "unknown", "Any" + ).strip() + ), + 2, + ) + self.add_to_output(decorators, 1) + + return self._return_output() + + def add_to_output( + self, output: str, side: int = 0 + ) -> None: + if side == 0: + self.output += output + elif side == 1: + self.output = output + self.output + else: + self.output = output + + def _process_decorators(self) -> None: + if self.ast_list.line[0].token == "#": + for _ in range(self.ast_list.count("#")): + decorator = self.ast_list.get_between( + "[", "]" + ) + self.ast_list.line = self.ast_list.line[ + len(decorator) + 3 : + ] + self.decorators.append( + "@" + + " ".join( + [__.token for __ in decorator] + ) + ) + + def _process_modifiers(self) -> None: + self.modifiers = { + self.root_scope.get_keyword("ASYNC"): False, + self.root_scope.get_keyword("PRIVATE"): False, + self.root_scope.get_keyword("PROTECTED"): False, + self.root_scope.get_keyword("FINAL"): False, + self.root_scope.get_keyword("UNSAFE"): False, + self.root_scope.get_keyword( + "STATIC" + ): False, # TODO: add virtual/kernel + } + index = 0 + + if self.ast_list.line[ + 0 + ].token != self.root_scope.get_keyword("FUNCTION"): + line = self.ast_list.line.copy() + while line: + index += 1 + if line[0].token in self.modifiers.keys(): + self.modifiers[line[0].token] = True + line = line[1:] + else: + if line[ + 0 + ].token != self.root_scope.get_keyword( + "FUNCTION" + ): + break + else: + break + self.ast_list.line = self.ast_list.line[ + (index - 1) : + ] + + def _process_variables(self) -> None: + self.variables = ExtractTypedParamsFromFunc( + self.ast_list, self.root_scope + ).parse() + self.name = self.ast_list.line[1].token + + if self.name in replace_function_name: + if not any( + [ + i in self.not_allowed_classes + for i in self.parent_scope.name + ] + ): + base.ERROR_CODES.OPERATOR_OVERLOADING_OUTSIDE_CLASS.panic( + self.name, + file=self.ast_list.file, + line_no=self.ast_list.find_line_number( + self.name + ), + ) + self.name = replace_function_name[self.name] + + def _handle_non_function(self) -> Processed_Line: + if ( + self.ast_list.line[0].token + in self.not_allowed_classes + ): + return _class( + self.ast_list.splice(len(self.modifiers)), + self.current_scope, + self.parent_scope, + self.root_scope, + self.modifiers, + ) + + base.ERROR_CODES.SYNTAX_MISSING_EXCEPTED_TOKEN.format( + token=self.root_scope.get_keyword("FUNCTION") + ).panic( + self.ast_list[1].token, + file=self.ast_list.file, + line_no=self.ast_list[0].line_number, + ) + + def _process_params(self) -> None: + if not self.variables["params"]: + self.add_to_output(")") + else: + self._process_params_internal() + self.add_to_output(self.output[:-2] + ")", 2) + + def _process_params_internal(self): + if not self.variables["params"]: + raise ValueError( + "The variable type is not a dictionary" + ) + if not isinstance(self.variables["params"], dict): + raise ValueError( + "The variable type is not a dictionary" + ) + + for k, v in self.variables["params"].items(): if v["type"] == "void": - panic(SyntaxError(f": The type void is not allowed for variables"), file=ast_list.file, line_no=ast_list.find_line_number(k)) - output += f"{k}: {base.replace_primitive(v['type'], 0)}, " - current_scope.variables[k] = v["type"] - - output = output[:-2] + ")" - - if ast_list.line[-1].token == "<\\r1>" and ast_list.indent_level == 0: - panic(SyntaxError(f": Cannot have a blank function in the global scope"), file=ast_list.file, line_no=ast_list.find_line_number(name)) - - if variables["return_type"]: - output += f" -> {variables['return_type']}:" if ast_list.line[-1].token != "<\\r1>" else f" -> {variables['return_type']}\n{INDENT_CHAR*(ast_list.indent_level+1)}pass" - else: - output += ":" if ast_list.line[-1].token != "<\\r1>" else f":\n{INDENT_CHAR*(ast_list.indent_level+1)}pass" - - output = f"\n{INDENT_CHAR*ast_list.indent_level}{output}" - - #if not any([i in not_allowed_classes for i in parent_scope.name]): - # if not root_scope.get_keyword('ASYNC') in modifiers and not root_scope.get_keyword('UNSAFE') in modifiers: - # if name in parent_scope.functions: - # output = (f"\n{INDENT_CHAR*ast_list.indent_level}@{name}.register" + output) - # else: - # output = (f"\n{INDENT_CHAR*ast_list.indent_level}@hx__multi_method" + output) - # if the type of parent_sope is an abstract class - - if any([i == root_scope.get_keyword("ABSTRACT") for i in parent_scope.name]): - output = f"\n{INDENT_CHAR*ast_list.indent_level}@hx__abstract_method" + output - # if the type of parent_sope is an interface - if any([i == root_scope.get_keyword("INTERFACE") for i in parent_scope.name]): - output = f"\n{INDENT_CHAR*ast_list.indent_level}@hx__abstract_method" + output - - static: bool = False - async_: bool = False - private: bool = False - unsafe: bool = False - - if root_scope.get_keyword('STATIC') in modifiers: - if not any([i in not_allowed_classes for i in parent_scope.name]): - panic(SyntaxError(f": The {root_scope.get_keyword('STATIC')} modifier cannot be used outside a class"), root_scope.get_keyword("STATIC"), file=ast_list.file, line_no=ast_list[0].line_number) - - output = f"\n{INDENT_CHAR*ast_list.indent_level}@staticmethod{output}" - static = True - - if root_scope.get_keyword('ASYNC') in modifiers: - output = f"\n{INDENT_CHAR*ast_list.indent_level}@hx__async{output}" - async_ = True - - if root_scope.get_keyword('PRIVATE') in modifiers: - private = True - - # if root_scope.get_keyword("PROTECTED") in modifiers: - # output = output.replace("def ", "def _") - - if root_scope.get_keyword("FINAL") in modifiers: - output = f"{INDENT_CHAR*ast_list.indent_level}@final{output}" - - if root_scope.get_keyword('UNSAFE') in modifiers: - unsafe = True - - if private and static: - panic(SyntaxError(f": The {root_scope.get_keyword('PRIVATE')} and {root_scope.get_keyword('STATIC')} modifiers cannot be used together"), file=ast_list.file, line_no=ast_list.find_line_number(root_scope.get_keyword('STATIC'))) - - if async_ and unsafe: - panic(SyntaxError(f": The {root_scope.get_keyword('ASYNC')} and {root_scope.get_keyword('UNSAFE')} modifiers cannot be used together"), file=ast_list.file, line_no=ast_list.find_line_number(root_scope.get_keyword('STATIC'))) - - parent_scope.functions[name] = {"type": variables["return_type"], "params": variables["params"], root_scope.get_keyword('STATIC'): static, root_scope.get_keyword('ASYNC'): async_, root_scope.get_keyword('PRIVATE'): private, root_scope.get_keyword('UNSAFE'): unsafe} - - if "unknown" in output: - output = output.replace("unknown", "Any") - - #print(output) - if decorators: - for _, decorator in enumerate(reversed(decorators)): - output = f"{INDENT_CHAR*ast_list.indent_level}{decorator}\n{INDENT_CHAR*ast_list.indent_level}{output.strip()}" - - return Processed_Line((f'\n{INDENT_CHAR*ast_list.indent_level}@overload_with_type_check\n' if not async_ else '\n') + output, ast_list) \ No newline at end of file + base.ERROR_CODES.TYPE_INVALID_TYPE_IN_CONTEXT.format( + type="void" + ).panic( + "void", + file=self.ast_list.file, + line_no=self.ast_list.find_line_number( + k + ), + ) + self.add_to_output( + f"{k}: {base.replace_primitive(v['type'], 0)}, " + ) + self.current_scope.variables[k] = v["type"] + + def __add_indent( + self, output: str, offset: int = 0 + ) -> str: + return f"\n{INDENT_CHAR*(self.ast_list.indent_level+offset)}{output}" + + def _process_return_type(self) -> None: + if ( + self.ast_list.line[-1].token == "<\\r1>" + and self.ast_list.indent_level == 0 + ): + base.ERROR_CODES.SYNTAX_INVALID_SYNTAX.format( + location="noop function at the global scope is not allowed, " + "use {...} to create an empty block function", + ).panic( + "fn", + file=self.ast_list.file, + line_no=self.ast_list.find_line_number( + self.name + ), + ) + + if self.variables["return_type"]: + self.add_to_output( + f" -> {self.variables['return_type']}:" + if self.ast_list.line[-1].token != "<\\r1>" + else f" -> {self.variables['return_type']}:" + f"{self.__add_indent('pass', 1)}" + ) + else: + self.add_to_output( + ":" + if self.ast_list.line[-1].token != "<\\r1>" + else f":{self.__add_indent('pass', 1)}" + ) + + def _process_modifiers_step_2(self) -> None: + if self.modifiers[ + self.root_scope.get_keyword("STATIC") + ]: + if not any( + [ + i in self.not_allowed_classes + for i in self.parent_scope.name + ] + ): + base.ERROR_CODES.FUNCTION_INVALID_MODIFIERS.format( + modifiers="static", + ).panic( + self.root_scope.get_keyword("STATIC"), + file=self.ast_list.file, + line_no=self.ast_list.line[ + 0 + ].line_number, + ) + + self.decorators.append(f"@staticmethod") + + if self.modifiers[ + self.root_scope.get_keyword("ASYNC") + ]: + self.decorators.append(f"@hx__async") + + if self.modifiers[ + self.root_scope.get_keyword("PRIVATE") + ]: + # TODO: ADD A DECORATOR FOR PRIVATE TO MARK THE TYPE OF THE FUNCTION AS PRIVATE + self.modifiers[ + self.root_scope.get_keyword("PRIVATE") + ] = True + + if self.modifiers[ + self.root_scope.get_keyword("FINAL") + ]: + self.decorators.append(f"@final") + + if self.modifiers[ + self.root_scope.get_keyword("UNSAFE") + ]: + # TODO: ADD A DECORATOR FOR UNSAFE TO MARK THE TYPE OF THE FUNCTION AS UNSAFE + self.modifiers[ + self.root_scope.get_keyword("UNSAFE") + ] = True + + if ( + self.modifiers[ + self.root_scope.get_keyword("PRIVATE") + ] + and self.modifiers[ + self.root_scope.get_keyword("STATIC") + ] + ): + base.ERROR_CODES.FUNCTION_INVALID_MODIFIERS.format( + modifiers="private and static", + ).panic( + self.root_scope.get_keyword("PRIVATE"), + self.root_scope.get_keyword("STATIC"), + file=self.ast_list.file, + line_no=self.ast_list.line[0].line_number, + ) + + if ( + self.modifiers[ + self.root_scope.get_keyword("ASYNC") + ] + and self.modifiers[ + self.root_scope.get_keyword("UNSAFE") + ] + ): + base.ERROR_CODES.FUNCTION_INVALID_MODIFIERS.format( + modifiers="async and unsafe", + ).panic( + self.root_scope.get_keyword("ASYNC"), + self.root_scope.get_keyword("UNSAFE"), + file=self.ast_list.file, + line_no=self.ast_list.find_line_number( + self.root_scope.get_keyword("STATIC") + ), + ) + + def _return_output(self) -> Processed_Line: + return Processed_Line( + self.output, + self.ast_list, + ) + + +def _function( + ast_list: Token_List, + current_scope: Scope, + parent_scope: Scope, + root_scope: Scope, +) -> Processed_Line: + return Function( + ast_list, current_scope, parent_scope, root_scope + ).parse() diff --git a/src/panic.py b/src/panic.py index 82403f0..fdf23c4 100644 --- a/src/panic.py +++ b/src/panic.py @@ -4,7 +4,7 @@ from sys import exit from sys import stdout as sys_stdout from types import FrameType -from typing import Any, Optional +from typing import Any, NoReturn, Optional from weakref import ref from pygments import highlight # type: ignore @@ -244,7 +244,7 @@ def s_u2(line: str | list) -> str: def panic( - __error: Exception, + __error: type[BaseException] | BaseException | Exception, *_mark: tuple[Any] | str, file: str = "", line_no: int = 0, @@ -257,9 +257,10 @@ def panic( thread_name: Optional[str] = None, no_exit: bool = False, lang: str = "", -): + _code: Optional[str] = None, +): # type: ignore lock.acquire(blocking=True, timeout=0.5) - + lines_to_print: int = 5 mark: list[str] = [item.__str__() for item in _mark] @@ -406,12 +407,13 @@ def __contains__(self, other): base_color: str = "31" if not thread_name else "34" second_color: str = "93" if not thread_name else "96" + third_color: str = "93" if not thread_name else "94" # WARNING: primary_error_color MUST be a bold color primary_error_color = f"\u001b[{base_color};1m" if (does_support_colors) else ("") # WARNING: secondary_error_color CANNOT be a bold color - secondary_error_color = f"\u001b[{base_color}m" if (does_support_colors) else ("") + secondary_error_color = f"\u001b[{third_color}m" if (does_support_colors) else ("") # WARNING: border_color CANNOT be a bold color border_color = f"\u001b[{base_color}m" if (does_support_colors) else ("") @@ -463,11 +465,19 @@ def mark_all(line: str, mark_line: str, mark_start: Optional[int] = None) -> str color_escape_pattern = re.compile(r"(\x1b|\u001b)\[\d*(;\d+)*m") mark_line += " " total_char_so_far: int = 0 - line_end = len(standalone_tokenize_line(line, preserve_spaces=True)[:-4]) + line_end = len(standalone_tokenize_line(line, preserve_spaces=True)[:-(4 if tokenized_line[skip].isspace() else 5)]) max_skip: int = 0 + max_start_with_start_space: int = skip + for token in tokenized_line[:skip]: + if token.isspace(): + max_start_with_start_space += 1 + max_start_with_start_space -= (3 if tokenized_line[skip].isspace() else 4) + for token in tokenized_line[:skip]: max_skip += len(token) + + if follow_marked_order: if not mark: @@ -526,7 +536,7 @@ def mark_all(line: str, mark_line: str, mark_start: Optional[int] = None) -> str total_char_so_far += len(token) if index >= skip else 0 if index < skip: continue - if index < (line_end - 4): + if index < line_end and index > max_start_with_start_space: tokenized_line[index] = f"{primary_error_color}{token}{reset}" mark_line += f"{secondary_error_color}{'~'*len(token)}{reset}" else: @@ -643,21 +653,30 @@ def process_lines(line: str, index: int) -> str: ) # hex codes look like this: + hex_code: str | (re.Match[str] | None) = re.search( r"", message - ) + ) if not _code else None _hex_code = hex_code + if hex_code: hex_code = f"{hex_code.group(1)}.{hex_code.group(2)}" # type: ignore message = message.replace(f": ", f"") hex_code = f"{border_color}{chars['straight']} {chars['dash']*2} {primary_error_color}{hex_code}{border_color} {chars['dash']*(terminal_width-4-len(hex_code)-4)} {chars['straight']}{reset}" somewhat_middle = hex_code if not no_lines else "" else: - somewhat_middle = ( - f"{border_color}{chars['straight']} {chars['dash']*(terminal_width-4)} {chars['straight']}{reset}" - if not no_lines - else "" - ) + if _code: + somewhat_middle = ( + f"{border_color}{chars['straight']} {chars['dash']*2} {primary_error_color}{_code}{border_color} {chars['dash']*(terminal_width-4-len(_code)-4)} {chars['straight']}{reset}" + if not no_lines + else "" + ) + else: + somewhat_middle = ( + f"{border_color}{chars['straight']} {chars['dash']*(terminal_width-4)} {chars['straight']}{reset}" + if not no_lines + else "" + ) if (not multi_frame or (multi_frame and pos == 2)) and not no_lines: print(somewhat_middle) @@ -668,15 +687,31 @@ def process_message(message: str) -> list[str]: output: list[str] = [] import textwrap - if hex_code: - message += f"\nIf this is your first time seeing this error, and you are not sure how to fix it, type 'helix doc {_hex_code.group(1)}.{_hex_code.group(2)}'." # type: ignore for line in message.split("\n"): for line2 in textwrap.wrap(line, terminal_width - 4): output.append( f"{border_color}{chars['straight']}{reset} {line2.ljust(terminal_width-4)} {border_color}{chars['straight']}{reset}" ) + + _temp_msg: str = "" + if hex_code: + _temp_msg = f"If this is your first time seeing this error, and you are not sure how to fix it, type 'helix doc {_hex_code.group(1)}.{_hex_code.group(2)}'." # type: ignore + elif _code: + _temp_msg = f"If this is your first time seeing this error, and you are not sure how to fix it, type 'helix doc {_code}'." + + if _temp_msg: + output.append(f"{border_color}{chars['straight']}{reset} {' '*(terminal_width-4)} {border_color}{chars['straight']}{reset}") + + for line in textwrap.wrap(_temp_msg, terminal_width - 4): + output.append( + f"{border_color}{chars['straight']}{reset} {line.ljust(terminal_width-4)} {border_color}{chars['straight']}{reset}" + ) + + if hex_code: - return [out.replace(f"'helix doc {_hex_code.group(1)}.{_hex_code.group(2)}'", f"{second_color}'helix doc {_hex_code.group(1)}.{_hex_code.group(2)}'{reset}") for out in output] # type: ignore + output[-1] = output[-1].replace(f"'helix doc {_hex_code.group(1)}.{_hex_code.group(2)}'", f"{secondary_error_color}'helix doc {_hex_code.group(1)}.{_hex_code.group(2)}'{reset}") + elif _code: + output[-1] = output[-1].replace(f"'helix doc {_code}'", f"{secondary_error_color}'helix doc {_code}'{reset}") return output if not multi_frame or (multi_frame and pos == 2): diff --git a/src/token/normalize_tokens.py b/src/token/normalize_tokens.py index 88c5004..cca3df8 100644 --- a/src/token/normalize_tokens.py +++ b/src/token/normalize_tokens.py @@ -90,10 +90,6 @@ def process_line(index: int): frozenset((process_line(_) for _ in enumerate(lines))) - if indent_level > 0: - if stack[-1].indent_level == 0: - stack[-1] = previous_element - panic(SyntaxError(f": Expected an indent: level of 0, but got {indent_level}"), "{", file=path, line_no=stack[-1].line_number) def process_for_loops(index: int) -> None: nonlocal in_for_loop, lines @@ -112,6 +108,13 @@ def process_for_loops(index: int) -> None: lines.insert(0, Token(lines[0].original_line, "<\\t:0>", lines[0].line_number, 0)) + broken_syntax = False + if indent_level > 0: + if stack[-1].indent_level == 0: + stack[-1] = previous_element + broken_syntax = True + #panic(SyntaxError(f": Expected an indent: level of 0, but got {indent_level}"), "{", file=path, line_no=stack[-1].line_number) + indent_level = 0 def process_indent_level(ast_token: Token): nonlocal indent_level, lines, current_line, final_lines @@ -131,5 +134,23 @@ def process_indent_level(ast_token: Token): if current_line: for i in current_line: i.indent_level = indent_level final_lines.append(current_line) + + if broken_syntax: + line = None + for line in final_lines: + if not line: continue + for token in line: + if token.token in base.BODY_REQUIRED_KEYWORDS.keys(): + broken_syntax = False + break + if not broken_syntax: + if line[-1].token != ":": + base.ERROR_CODES.SYNTAX_MISSING_EXCEPTED_TOKEN.format(token="{}").panic( + file=path, + line_no=line[-1].line_number + ) + broken_syntax = True + else: + base.ERROR_CODES.SYNTAX_UNBALANCED_PARENTHESIS.panic("{", file=path, line_no=stack[-1].line_number) return tuple(Token_List(_, _[0].indent_level, path) for _ in final_lines if _) \ No newline at end of file diff --git a/syntax/test.hlx b/syntax/test.hlx index 7cd08a9..b8a77df 100644 --- a/syntax/test.hlx +++ b/syntax/test.hlx @@ -80,6 +80,9 @@ class C_cout { fn <<(self, a: string) { print(a); } + fn subtract() { + ... + }; } fn main(argv: list) { @@ -104,6 +107,7 @@ async fn add(a: int, b: int) -> int { } private fn subtract(a: int, b: int) -> int { + return a - b } From d4cc021e26796c66ebae43aceb92416dc15f22eb Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Wed, 13 Mar 2024 21:16:10 -0400 Subject: [PATCH 3/8] Update code: modified test.hlx generation date, added types-toml to requirements, and refactored Translate and Function classes --- .helix/cache/build_cache/test_hlx.py | 2 +- requirements.txt | 3 +- src/core/framework.py | 12 +++++++- src/core/imports.py | 3 +- src/functions/_functions.py | 45 +++------------------------- 5 files changed, 19 insertions(+), 46 deletions(-) diff --git a/.helix/cache/build_cache/test_hlx.py b/.helix/cache/build_cache/test_hlx.py index 65d1e8c..f90f199 100644 --- a/.helix/cache/build_cache/test_hlx.py +++ b/.helix/cache/build_cache/test_hlx.py @@ -4,7 +4,7 @@ # GENERATED FILE # -------------------------------------------------------------------------------- # Filename: test.hlx -# Generation Date: 2024-03-13 20:57:04 +# Generation Date: 2024-03-13 21:13:09 # Generator: Helix Transpiler # -------------------------------------------------------------------------------- # WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications diff --git a/requirements.txt b/requirements.txt index 902314f..ee5f758 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,5 @@ argparse black beartype multimethod -mypy \ No newline at end of file +mypy +types-toml \ No newline at end of file diff --git a/src/core/framework.py b/src/core/framework.py index 3b32080..334638f 100644 --- a/src/core/framework.py +++ b/src/core/framework.py @@ -8,9 +8,19 @@ Optional, Processed_Line, Scope, - threading + threading, + Token_List, ) +class Translate(ABC): + @abstractmethod + def __init__(self, ast_list: Token_List, current_scope: Scope, parent_scope: Scope, root_scope: Scope): + pass + @abstractmethod + def parse(self) -> Processed_Line: + pass + + class Hashing(ABC): @abstractmethod def __init__(self, file_path: str, output_path: str) -> None: diff --git a/src/core/imports.py b/src/core/imports.py index 8edef08..48ea9f8 100644 --- a/src/core/imports.py +++ b/src/core/imports.py @@ -100,6 +100,7 @@ from src.classes.Scope import Scope from src.classes.Transpiler import Transpiler +import src.core.framework as framework from src.functions._class import _class from src.functions._for import _for @@ -109,5 +110,3 @@ from src.functions._match import _match from src.functions._unless import _unless from src.functions._unmarked import _unmarked - -import src.core.framework as framework \ No newline at end of file diff --git a/src/functions/_functions.py b/src/functions/_functions.py index 3d66767..af92c8b 100644 --- a/src/functions/_functions.py +++ b/src/functions/_functions.py @@ -8,6 +8,7 @@ _class, INDENT_CHAR, Optional, + framework, ) replace_function_name: map[str, str] = map( @@ -47,7 +48,7 @@ ) -class ExtractTypedParamsFromFunc: +class ExtractTypedParamsFromFunc(framework.Translate): allowed_untyped = ("self", "cls", "super") def __init__( @@ -265,46 +266,7 @@ def handle_no_parameters( return {"return_type": None, "params": {}} -def process_modifiers( - ast_list: Token_List, root_scope: Scope -) -> list[str]: - allowed_modifiers = ( - root_scope.get_keyword("ASYNC"), - root_scope.get_keyword("PRIVATE"), - root_scope.get_keyword("PROTECTED"), - root_scope.get_keyword("FINAL"), - root_scope.get_keyword("UNSAFE"), - root_scope.get_keyword( - "STATIC" - ), # TODO: add virtual/kernel - ) - - modifiers = [] - index = 0 - - if ast_list.line[0].token != root_scope.get_keyword( - "FUNCTION" - ): - line = ast_list.line.copy() - while line: - index += 1 - if line[0].token in allowed_modifiers: - modifiers.append(line[0].token) - line = line[1:] - else: - if line[0].token != root_scope.get_keyword( - "FUNCTION" - ): - return modifiers - else: - break - ast_list.line = ast_list.line[index - 1 :] - return modifiers - else: - return modifiers - - -class Function: +class Function(framework.Translate): def __init__( self, ast_list: Token_List, @@ -331,6 +293,7 @@ def __init__( self.variables: dict[ str, str | dict[str, dict[str, str]] | None ] = {} + self.name: str = "" self.output: str = "" From c98eface209aa03425ff4bcf7d7ee2e4250d3309 Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Fri, 15 Mar 2024 16:11:22 -0400 Subject: [PATCH 4/8] Fix type annotation in panic function and update test_hlx.py.lines*** ***Update WorkerPool.py imports and remove unnecessary imports*** ***Normalize tokens in normalize_tokens.py*** ***Remove unnecessary code in _class.py*** ***Add class_type_check_decorator in core.py*** ***Remove unnecessary code in _let.py --- .helix/cache/build_cache/test_hlx.py | 52 ++++-- .helix/cache/build_cache/test_hlx.py.lines | 66 +++++--- .helix/include/core.py | 28 ++++ helix.py | 9 +- src/classes/Transpiler.py | 7 +- src/classes/WorkerPool.py | 17 +- src/core/base.py | 90 ++++++---- src/core/imports.py | 118 +++++++++----- src/functions/_class.py | 7 +- src/functions/_functions.py | 85 ++++++---- src/functions/_let.py | 20 --- src/functions/_unmarked.py | 181 +++++++++------------ src/panic.py | 2 +- src/token/normalize_tokens.py | 8 +- syntax/test.hlx | 62 ++++++- 15 files changed, 451 insertions(+), 301 deletions(-) diff --git a/.helix/cache/build_cache/test_hlx.py b/.helix/cache/build_cache/test_hlx.py index f90f199..24a1982 100644 --- a/.helix/cache/build_cache/test_hlx.py +++ b/.helix/cache/build_cache/test_hlx.py @@ -4,7 +4,7 @@ # GENERATED FILE # -------------------------------------------------------------------------------- # Filename: test.hlx -# Generation Date: 2024-03-13 21:13:09 +# Generation Date: 2024-03-15 00:33:53 # Generator: Helix Transpiler # -------------------------------------------------------------------------------- # WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications @@ -33,7 +33,7 @@ sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore # trunk-ignore(ruff/F401) -from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore +from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore # trunk-ignore(ruff/F401) # trunk-ignore(ruff/F811) from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore @@ -71,10 +71,7 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore if line_no == -1: continue - # Check specific conditions to skip - if ( - f"plum{os.sep}plum" in filename - ): + if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): continue if ( linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore @@ -176,18 +173,21 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook sys.argv = ["Z:\\devolopment\\helix\\helix-lang\\helix.py", "Z:\\devolopment\\helix\\helix-lang\\syntax\\test.hlx"] + list(sys.argv)[2:] del os, threading, functools overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore +@class_type_check_decorator class C_cout(): - def __init__(self: Any, __val: 'C_cout'): - raise NotImplementedError("Define an __init__ method for this class with the function signature new(self: Any, inst_class: 'C_cout')") + @overload_with_type_check + def __init__(self: Any): + None @overload_with_type_check def __lshift__(self: Any, a: str | hx_string): print ( a ) - @overload_with_type_check - def subtract(): - None @overload_with_type_check def main(argv: list[str | hx_string] | hx_list[str | hx_string]): - a: int | hx_int = int("12") + a: C_cout = (C_cout ( )) + b: C_cout = C_cout(a) + a << "hello world" + subtract ( 123 , 123 ) + print ( a_cursed_fucntion ( 23 ) ) add ( 123 , 123 ) print ( add . _await ( ) ) do_something ( ) @@ -196,6 +196,13 @@ def main(argv: list[str | hx_string] | hx_list[str | hx_string]): del i print ( do_something . _await ( ) ) print ( "done" ) + cout: C_cout = (C_cout ( )) + cout << "hello world" + a: int | hx_int = int(2) + b: int | hx_int = (int()) + print ( a , b ) + for i, j in C_For(i = int(0), j = int(0)).set_con('i < 10').set_inc('i ++ ; j += 3'): + printf ( "i: %d, j: %d" , i , j ) return 0 @hx__async def add(a: int | hx_int, b: int | hx_int) -> int: @@ -203,6 +210,26 @@ def add(a: int | hx_int, b: int | hx_int) -> int: return a + b @overload_with_type_check def subtract(a: int | hx_int, b: int | hx_int) -> int: + some_map: dict[str | hx_string , int | hx_int] | hx_map[str | hx_string , int | hx_int] = dict({ "a" : 1 , "b" : 2 , "c" : 3 }) + some_list: list[int | hx_int] | hx_list[int | hx_int] = list([ 1 , 2 , 3 ]) + some_set: set[int | hx_int] | hx_set[int | hx_int] = set({ 1 , 2 , 3 }) + some_tuple: tuple[int | hx_int] | hx_tuple[int | hx_int] = tuple(( 1 , 2 , 3 )) + some_string: str | hx_string = str("hello") + some_char: str | hx_char = str('a') + some_bool: bool | hx_bool = bool(True) + some_float: float | hx_float = float(1.0 / 3.0) + some_double: double | hx_double = double(1.0 / 3.0) + some_int: int | hx_int = int(2 ** 9046) + print ( "map:" , some_map ) + print ( "list:" , some_list ) + print ( "set:" , some_set ) + print ( "tuple:" , some_tuple ) + print ( "string:" , some_string ) + print ( "char:" , some_char ) + print ( "bool:" , some_bool ) + print ( "float:" , some_float ) + print ( "double:" , some_double ) + print ( "int:" , some_int ) return a - b @hx__async def do_something(): @@ -211,7 +238,6 @@ def do_something(): del i @overload_with_type_check def a_cursed_fucntion(a: int | hx_int) -> FunctionType: - printf ( "new something: %d" , a ) return a_cursed_fucntion if __name__ == "__main__": try: diff --git a/.helix/cache/build_cache/test_hlx.py.lines b/.helix/cache/build_cache/test_hlx.py.lines index a1e0a6f..4775033 100644 --- a/.helix/cache/build_cache/test_hlx.py.lines +++ b/.helix/cache/build_cache/test_hlx.py.lines @@ -173,46 +173,72 @@ -1 -1 -1 --1 --1 --1 -79 +78 +78 79 79 80 -80 -81 83 83 84 88 88 89 +90 91 92 -94 +93 95 96 -95 +98 99 100 -101 -104 +99 +103 104 -105 -106 -109 +107 109 111 +111 +112 +114 115 -115 -116 -117 -116 -122 -122 -122 +118 +121 +121 122 +123 +126 +126 +131 +132 +133 +134 +135 +136 +137 +138 +139 +141 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +154 +158 +158 +159 +160 +159 +167 +167 +167 -1 -1 -1 diff --git a/.helix/include/core.py b/.helix/include/core.py index 2833cfc..f8a67f4 100644 --- a/.helix/include/core.py +++ b/.helix/include/core.py @@ -235,6 +235,34 @@ def set_inc(self, increment): .replace("/*", "*= 1") ) return self + +def class_type_check_decorator(cls): + # Store the original __init__ for later use + dummy = lambda *args, **kwargs: None + original_init = getattr(cls, "__init__", dummy) + + # Define a new __init__ that includes the type check + def new_init(self, *args, **kwargs): + if not isinstance(self, cls): + raise TypeError(f"Instance must be of type {cls.__name__}") + try: + if cls not in original_init.__annotations__.items(): + # remove the calss from args + args = list(args) + for i in range(len(args)): + if isinstance(args[i], cls): + del args[i] + break + args = tuple(args) + original_init(self, *args, **kwargs) + else: + original_init(self, *args, **kwargs) + except AttributeError: + raise TypeError(f"Can't instance class \"{cls.__name__}\", due to no 'new(self)' method present.");#"""REMOVE-STACK"""# + + # Replace the class's __init__ with the new one + cls.__init__ = new_init + return cls def hx__async(func: Callable) -> Callable: diff --git a/helix.py b/helix.py index 1a9f8b5..4d5637c 100644 --- a/helix.py +++ b/helix.py @@ -1298,13 +1298,10 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook if line_no == -1: continue - - # Check specific conditions to skip - if ( - f"plum{{os.sep}}plum" in filename - ): + + if ";#\\"\\"\\"REMOVE-STACK\\"\\"\\"#" in linecache.getline(filename, line_no).strip(): continue - + if ( linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore and diff --git a/src/classes/Transpiler.py b/src/classes/Transpiler.py index dc54233..ba3abfd 100644 --- a/src/classes/Transpiler.py +++ b/src/classes/Transpiler.py @@ -1,5 +1,4 @@ import src.core.base as base -from src.functions._unmarked import _unmarked from src.core.imports import ( Callable, Optional, @@ -7,10 +6,8 @@ Processed_Line, Token_List, color_print, - INDENT_CHAR, ) - class Transpiler: def __init__(self) -> None: self.copied_scope: Optional[Scope] @@ -21,8 +18,8 @@ def __init__(self) -> None: self.transpiled: list[Processed_Line] = [] self.add_to_transpiled_queue: list[tuple[Processed_Line, Scope]] = [] - def parse_non_keyword(self, line: Token_List, *_: Scope) -> str: - return _unmarked(line, self.scope_stack[-1] if len(self.scope_stack) >= 1 else self.root_scope, self.scope_stack[-2] if len(self.scope_stack) >= 2 else self.root_scope, self.root_scope, self.ignore_main) + def parse_non_keyword(self, line: Token_List, *_: Scope) -> Processed_Line: + return base._unmarked(line, self.scope_stack[-1] if len(self.scope_stack) >= 1 else self.root_scope, self.scope_stack[-2] if len(self.scope_stack) >= 2 else self.root_scope, self.root_scope, self.ignore_main) def get_match_function(self, child: Token_List) -> Callable[[Token_List, Optional[Scope], Optional[Scope], Scope], Processed_Line]: match = Scope.get_match(child, tuple(base.KEYWORDS.keys())) diff --git a/src/classes/WorkerPool.py b/src/classes/WorkerPool.py index 5d6c08f..2c2ef50 100644 --- a/src/classes/WorkerPool.py +++ b/src/classes/WorkerPool.py @@ -1,10 +1,7 @@ -from concurrent.futures import ( +from src.core.imports import ( Future, ProcessPoolExecutor as ProcessPool, ThreadPoolExecutor as ThreadPool, -) - -from typing import ( Literal, Optional, TypeAlias, @@ -17,15 +14,13 @@ TypeVar, Union, Self, - Any + Any, + cpu_count, + CO_NESTED, + Lock, + sys, ) -from multiprocessing import cpu_count -from inspect import CO_NESTED -from threading import Lock - -import sys - # ------------------------------ Type Variables ------------------------------ # T = TypeVar("T") # ----------------------------- Version Checking ----------------------------- # diff --git a/src/core/base.py b/src/core/base.py index 9b3c9ba..69cd0cb 100644 --- a/src/core/base.py +++ b/src/core/base.py @@ -1,6 +1,7 @@ from __future__ import annotations from src.core.imports import ( re, + sys, map, enum, Thread, @@ -19,6 +20,7 @@ _class, _let, _include, + _unmarked, file_cache, panic, @@ -89,40 +91,69 @@ def _no_change(line: Token_List, *args) -> Processed_Line: CHAR = r"'.*'" INDENT_CHAR = " " -FAT_CHARACTER: list[str] = [ + +""" +07/14/2016 08/02/2016 +07/22/2022 08/02/2022 +08/11/2023 12/19/2023 + +Al Mankhool, Bur Dubai +305, Al Falasi Residences, 317 - 11 B St +""" + +FAT_CHARACTER = [ + # CHARACTERS MUST BE > 2 r"\=\=\=", # === r"\!\=\=", # !== r"\.\.\.", # ... - r"\=\=", # == - r"\!\=", # != - r"\-\>", # -> - r"\<\-", # <- - r"\<\=", # <= - r"\>\=", # >= - r"\&\&", # && - r"\-\-", # -- - r"\:\:", # :: - r"\|\|", # || - r"\+\+", # ++ - r"\+\=", # += - r"\-\=", # -= - r"\*\=", # *= - r"\/\=", # /= - r"\&\=", # &= - r"\|\=", # |= - r"\^\=", # ^= - r"\%\=", # %= - r"\*\*", # ** - r"\<\<", # << - r"\>\>", # >> + r"\r\/\/", # r// + r"\r\*\*", # r** + r"\r\<\<", # r<< + r"\r\>\>", # r>> + r"\/\/\=", # //= + r"\*\*\=", # **= r"\<\<\=", # <<= r"\>\>\=", # >>= - r"\=\>", # => - r"\@\=", # @= - r"\_\_", # __ - r"\?\?", # ?? - r"\?\:", # ?: - r"\?\=", # ?= + r"\=\=", # == + r"\!\=", # != + r"\<\=", # <= + r"\>\=", # >= + r"\/\/", # // + r"\*\*", # ** + r"\<\<", # << + r"\>\>", # >> + r"\r\+", # r+ + r"\r\-", # r- + r"\r\*", # r* + r"\r\/", # r/ + r"\r\%", # r% + r"\r\&", # r& + r"\r\|", # r| + r"\r\^", # r^ + r"\+\=", # += + r"\-\=", # -= + r"\*\=", # *= + r"\/\=", # /= + r"\%\=", # %= + r"\&\=", # &= + r"\|\=", # |= + r"\^\=", # ^= + r"\=\=", # == + r"\=\>", # => + r"\@\=", # @= + r"\-\>", # -> + r"\<\-", # <- + r"\<\=", # <= + r"\>\=", # >= + r"\&\&", # && + r"\-\-", # -- + r"\:\:", # :: + r"\|\|", # || + r"\+\+", # ++ + r"\_\_", # __ + r"\?\?", # ?? + r"\?\:", # ?: + r"\?\=", # ?= ] @enum.unique @@ -404,6 +435,7 @@ def panic(self, *args, **kwargs) -> NoReturn: ) panic(self.py_exception(self.template), *args, **kwargs, _code = self.code) + sys.exit(1) IGNORE_TYPES_MAP: tuple[str, ...] = ("Callable",) diff --git a/src/core/imports.py b/src/core/imports.py index 48ea9f8..b31cb4e 100644 --- a/src/core/imports.py +++ b/src/core/imports.py @@ -1,37 +1,49 @@ -import enum -import re +""" +""" + +# ----------------------------- Built-in Imports ----------------------------- # + +# single imports - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # + import os +import re import sys +import json +import enum import signal import shutil import hashlib +import functools import threading import subprocess -import functools -import json -import toml -from sys import exit -from io import TextIOWrapper -from pickle import ( +from multiprocessing import cpu_count +from inspect import CO_NESTED +from dataclasses import dataclass +from datetime import datetime +from sys import exit +from io import TextIOWrapper + +# multi imports - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # + +from pickle import ( dump, load ) -from functools import ( +from functools import ( wraps, lru_cache ) -from argparse import ( - Namespace, - ArgumentParser -) - from threading import ( Thread, Event, Lock, ) - +from concurrent.futures import ( + Future, + ThreadPoolExecutor, + ProcessPoolExecutor +) from typing import ( Any, Optional, @@ -47,17 +59,19 @@ Sequence, Protocol, NoReturn, + Literal, + TypeAlias, + Annotated, + Self, ) - -from concurrent.futures import ( - Future, - ThreadPoolExecutor, - ProcessPoolExecutor +from types import ( + MappingProxyType as map, + UnionType, + FunctionType, + MethodType, + FrameType, + ModuleType, ) -from multimethod import subtype -from black import FileMode, format_file_contents -from dataclasses import dataclass -from datetime import datetime from time import ( time, sleep, @@ -66,32 +80,50 @@ process_time, ) -from types import ( - MappingProxyType as map, - UnionType, - FunctionType, - MethodType, - FrameType, - ModuleType, +# ----------------------------- External Imports ----------------------------- # + +# single imports - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # + +import toml +from multimethod import subtype + +# multi imports - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # + +from argparse import ( + Namespace, + ArgumentParser +) +from black import ( + FileMode, + format_file_contents ) -from src.panic import panic -from src.better_print import color_print -from src.config import load_config +# ----------------------------- Internal Imports ----------------------------- # + +# src - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # + from src.cache_store import ( cache, file_cache ) +from src.panic import panic +from src.better_print import color_print +from src.config import load_config + +# variables - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # + +INDENT_CHAR = load_config().Formatter["indent_char"] +re = __import__(load_config().Transpiler["regex_module"]) + +# src.token - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # from src.token.normalize_tokens import normalize_tokens from src.token.remove_comments import remove_comment from src.token.tokenize_file import Tokenizer from src.token.tokenize_line import tokenize_line -INDENT_CHAR = load_config().Formatter["indent_char"] -re = __import__(load_config().Transpiler["regex_module"]) +# src.classes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # -from src.classes.WorkerPool import WorkerPool from src.classes.Token import ( Processed_Line, Token_List, @@ -99,14 +131,18 @@ ) from src.classes.Scope import Scope from src.classes.Transpiler import Transpiler - +from src.classes.WorkerPool import WorkerPool import src.core.framework as framework -from src.functions._class import _class +# src.functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # + from src.functions._for import _for -from src.functions._functions import _function -from src.functions._include import _include from src.functions._let import _let +from src.functions._class import _class from src.functions._match import _match from src.functions._unless import _unless +from src.functions._include import _include +from src.functions._functions import _function from src.functions._unmarked import _unmarked + +# ------------------------------ End of Imports ------------------------------ # \ No newline at end of file diff --git a/src/functions/_class.py b/src/functions/_class.py index 85b0195..833ec55 100644 --- a/src/functions/_class.py +++ b/src/functions/_class.py @@ -11,10 +11,7 @@ SEPARATOR_FOR_CLASSES = "+" def generate_default_code(indent_chars: str, class_name) -> str: - return f""" -{indent_chars} def __init__(self: Any, __val: '{class_name}'): -{indent_chars} raise NotImplementedError("Define an __init__ method for this class with the function signature new(self: Any, inst_class: '{class_name}')") -""" + return f"""""" def _class(ast_list: Token_List, current_scope, parent_scope, root_scope, modifiers=None) -> Processed_Line: data_structure_types = ( @@ -32,7 +29,7 @@ def _class(ast_list: Token_List, current_scope, parent_scope, root_scope, modifi class_name: str = ast_list.splice(0, len(ast_list.get_all_before(":"))).splice(1).full_line().replace("<", "[").replace(">", "]") class_extends: list[Token] = [i for i in ast_list.get_all_after(CLASS_EXTENSION) if i.token != SEPARATOR_FOR_CLASSES and i.token != ":" and i.token != "(" and i.token != ")"] if ast_list[2].token == CLASS_EXTENSION else [] - class_decorators: list[str] = [] + class_decorators: list[str] = ["@class_type_check_decorator"] unsafe: bool = False if any([i in ast_list for i in data_structure_types]): diff --git a/src/functions/_functions.py b/src/functions/_functions.py index af92c8b..92b6d5f 100644 --- a/src/functions/_functions.py +++ b/src/functions/_functions.py @@ -13,37 +13,50 @@ replace_function_name: map[str, str] = map( { - "==": "__eq__", - "//": "__floordiv__", - ">=": "__ge__", - "<=": "__le__", - "<<": "__lshift__", - "!=": "__ne__", - "**": "__pow__", - ">>": "__rshift__", - "%": "__mod__", - "*": "__rmul__", - "&": "__and__", - "-": "__neg__", - "~": "__invert__", - ">": "__gt__", - "<": "__lt__", - "|": "__or__", - "-": "__sub__", - "/": "__truediv__", - "^": "__xor__", - "@": "__matmul__", - "r//": "__rfloordiv__", - "r**": "__rpow__", - "r%": "__rmod__", - "r*": "__mul__", - "r+": "__radd__", - "r+": "__add__", - "r|": "__ror__", - "r&": "__rand__", - "r-": "__rsub__", - "r/": "__rtruediv__", - "r^": "__rxor__", + "==" : "__eq__", + "!=" : "__ne__", + "<" : "__lt__", + "<=" : "__le__", + ">" : "__gt__", + ">=" : "__ge__", + "+" : "__add__", + "-" : "__sub__", + "*" : "__mul__", + "/" : "__truediv__", + "//" : "__floordiv__", + "%" : "__mod__", + "**" : "__pow__", + "<<" : "__lshift__", + ">>" : "__rshift__", + "&" : "__and__", + "|" : "__or__", + "^" : "__xor__", + "~" : "__invert__", + "@" : "__matmul__", + "r+" : "__radd__", + "r-" : "__rsub__", + "r*" : "__rmul__", + "r/" : "__rtruediv__", + "r//" : "__rfloordiv__", + "r%" : "__rmod__", + "r**" : "__rpow__", + "r<<" : "__rlshift__", + "r>>" : "__rrshift__", + "r&" : "__rand__", + "r|" : "__ror__", + "r^" : "__rxor__", + "+=" : "__iadd__", + "-=" : "__isub__", + "*=" : "__imul__", + "/=" : "__itruediv__", + "//=" : "__ifloordiv__", + "%=" : "__imod__", + "**=" : "__ipow__", + "<<=" : "__ilshift__", + ">>=" : "__irshift__", + "&=" : "__iand__", + "|=" : "__ior__", + "^=" : "__ixor__", } ) @@ -306,7 +319,10 @@ def parse(self) -> Processed_Line: ].token != self.root_scope.get_keyword("FUNCTION"): self._handle_non_function() - self._process_variables() + self.variables = ExtractTypedParamsFromFunc( + self.ast_list, self.root_scope + ).parse() + self._process_name() self.add_to_output( f"def {self.name}(" if self.name not in self.parent_scope.functions @@ -462,10 +478,7 @@ def _process_modifiers(self) -> None: (index - 1) : ] - def _process_variables(self) -> None: - self.variables = ExtractTypedParamsFromFunc( - self.ast_list, self.root_scope - ).parse() + def _process_name(self) -> None: self.name = self.ast_list.line[1].token if self.name in replace_function_name: diff --git a/src/functions/_let.py b/src/functions/_let.py index a879186..fc92d87 100644 --- a/src/functions/_let.py +++ b/src/functions/_let.py @@ -136,26 +136,6 @@ def mass_replace(string: str, replacements: dict[str, str]) -> str: current_scope.variables[name] = value["type"].full_line().strip() if not isinstance(value["type"], str) else value["type"] value["type"] = (type if type.full_line().strip() else panic(SyntaxError("You must specify the type of the variable"), "=", file=ast_list.file, line_no=ast_list[0].line_number)) if not value["type"] else value["type"] - #value["value"] = ( - # ( - # ' '.join([_.token for _ in value["type"].get_all_before("[")]) if "[" in value["type"] - # else value["type"].full_line() - # ) - # + "(" + ( - # value["value"].full_line() if value["value"] - # else "None" - # - # if null_value - # else null_value - # ) - # + ")" + ( - # (".__set_generic__(\"[" + mass_replace(extract_generics_from_type(value["type"]).full_line().strip(), cleaning) + ']")') - # if "[" in value["type"] - # else "" - # ) - #) if not value["type"] else value["value"].full_line() - - value["value"] = ( (value["value"].full_line() ) if ( diff --git a/src/functions/_unmarked.py b/src/functions/_unmarked.py index 1b6f840..de9c693 100644 --- a/src/functions/_unmarked.py +++ b/src/functions/_unmarked.py @@ -1,62 +1,67 @@ -from types import UnionType -from src.classes.Token import Token, Token_List, Processed_Line -from src.config import load_config -from src.token.tokenize_line import tokenize_line - -INDENT_CHAR = load_config().Formatter["indent_char"] -re = __import__(load_config().Transpiler["regex_module"]) - -from src.panic import panic - -""" from src.core.imports import ( Token_List, UnionType, Processed_Line, INDENT_CHAR, panic, + INDENT_CHAR, + Scope, ) -""" - -def _unmarked(ast_list: Token_List, current_scope, parent_scope, root_scope, ignore_main) -> str: - - # example: name = "John" -> name.set("John") - # example: a, b = 5, 6 -> a.set(5); b.set(6) - # example: a . somethign = 12 -> a.something.set(12) - # example: a, b = (5, 6), (7, 8) -> a.set((5, 6)); b.set((7, 8)) - # example: a, b = (5, 6), somecall("yes", a=19) -> a.set((5, 6)); b.set(somecall("yes", a=19)) - - output = "" - - if ast_list.indent_level == 0 and not ignore_main: - panic(SyntaxError("You cannot have code outside of a function"), ast_list[0], file=ast_list.file, line_no=ast_list[0].line_number) - - if "=" in ast_list: - # so something like a = 5 would become a.set(5) - # and something like a, b = 5, 6 would become a.set(5); b.set(6) - if ast_list.count("=") > 1: + +class UnmarkedTranslator: + def __init__(self, ast_list: Token_List, current_scope: Scope, parent_scope: Scope, root_scope: Scope, ignore_main: bool): + self.ast_list = ast_list + self.current_scope = current_scope + self.parent_scope = parent_scope + self.root_scope = root_scope + self.ignore_main = ignore_main + + def parse(self) -> Processed_Line: + if self._check_main_rule(): + return self._process_assignment() + else: + return self._process_non_assignment() + + def _check_main_rule(self) -> bool: + if self.ast_list.indent_level == 0 and not self.ignore_main: + panic(SyntaxError("You cannot have code outside of a function"), self.ast_list[0], file=self.ast_list.file, line_no=self.ast_list[0].line_number) + return "=" in self.ast_list + + def _process_assignment(self) -> Processed_Line: + self._validate_multiple_assignments() + variables = self._extract_variables_before_equal() + self._parse_assignment_values(variables) + output = self._generate_assignment_output(variables) + return Processed_Line(output, self.ast_list) + + def _process_non_assignment(self) -> Processed_Line: + return Processed_Line(INDENT_CHAR * self.ast_list.indent_level + self.ast_list.full_line().replace("::", "."), self.ast_list) + + def _validate_multiple_assignments(self): + if self.ast_list.count("=") > 1: panic( - SyntaxError("Multiple assignments are not allowed in a single line\nYou can use un-packing instead and do `a, b = 1, 2` rather then `a = 1, b = 2`"), - ast_list[ast_list.index("=")].token, - file=ast_list.file, - line_no=ast_list[0].line_number + SyntaxError("Multiple assignments are not allowed in a single line\nYou can use un-packing instead and do `a, b = 1, 2` rather than `a = 1, b = 2`"), + self.ast_list[self.ast_list.index("=")].token, + file=self.ast_list.file, + line_no=self.ast_list[0].line_number ) - - temp = ast_list.get_all_before("=") - variables: dict[str, Token_List] = {} # {name: value} + + def _extract_variables_before_equal(self) -> dict[str, str | Token_List]: + temp = self.ast_list.get_all_before("=") + variables: dict[str, str | Token_List] = {} name = "" in_brackets = False - bracket_count: int = 0 - + bracket_count = 0 + for token in temp: if token == ":" and not in_brackets: - panic(SyntaxError("You cannot use the `:` operator in a variable assignment"), token, file=ast_list.file, line_no=ast_list[0].line_number) + panic(SyntaxError("You cannot use the `:` operator in a variable assignment"), token, file=self.ast_list.file, line_no=self.ast_list[0].line_number) if token == "," and not in_brackets: - variables[name] = "" + variables[name.strip()] = "" name = "" continue elif token == "::" and not in_brackets: - panic(SyntaxError("You cannot use the `::` operator in a variable assignment"), token, file=ast_list.file, line_no=ast_list[0].line_number) + panic(SyntaxError("You cannot use the `::` operator in a variable assignment"), token, file=self.ast_list.file, line_no=self.ast_list[0].line_number) elif token in ("(", "[", "{"): in_brackets = True bracket_count += 1 @@ -65,26 +70,23 @@ def _unmarked(ast_list: Token_List, current_scope, parent_scope, root_scope, ign if bracket_count == 0: in_brackets = False name += token.token + " " - else: - if name: - variables[name] = "" - name = "" - - temp = ast_list.get_all_after("=") - value = Token_List([], ast_list.indent_level, ast_list.file) + + if name: + variables[name.strip()] = "" + return variables + + def _parse_assignment_values(self, variables: dict[str, str | Token_List]): + temp = self.ast_list.get_all_after("=") + value = "" in_brackets = False - bracket_count: int = 0 - + bracket_count = 0 index = 0 - + for token in temp: if token == "," and not in_brackets: - try: - variables[tuple(variables.keys())[index]] = value - except IndexError: - panic(SyntaxError("Too many values to unpack for assignment"), "=", file=ast_list.file, line_no=ast_list[index].line_number) + variables[list(variables)[index]] = value.strip() index += 1 - value = Token_List([], ast_list.indent_level, ast_list.file) + value = "" continue elif token in ("(", "[", "{"): in_brackets = True @@ -93,48 +95,25 @@ def _unmarked(ast_list: Token_List, current_scope, parent_scope, root_scope, ign bracket_count -= 1 if bracket_count == 0: in_brackets = False - value.line.append(token) - else: - if value: - try: - variables[tuple(variables.keys())[index]] = value - except IndexError: - panic(SyntaxError("Too many values to unpack for assignment"), "=", file=ast_list.file, line_no=ast_list[-1].line_number) - value = Token_List([], ast_list.indent_level, ast_list.file) - - if index != len(variables.keys()) - 1: - panic(SyntaxError("Not enough values to unpack for assignment"), "=", file=ast_list.file, line_no=ast_list[-1].line_number) - - - def split_or(str_: str, value: UnionType) -> str: - # so something like split_or("a, b", "," or "a") would return either "a" or "b" depending on the value - return str_.split(",")[0] if value == "," else str_.split(",")[1] - return value - - + value += token.token + " " + + if value: + variables[list(variables)[index]] = value.strip() + + if index != len(variables) - 1: + panic(SyntaxError("Not enough values to unpack for assignment"), "=", file=self.ast_list.file, line_no=self.ast_list[-1].line_number) + + def _generate_assignment_output(self, variables: dict[str, str | Token_List]) -> str: + output = "" for name, value in variables.items(): - if name not in current_scope.variables and name not in parent_scope.variables: - #panic(NameError(f"Name '{name.strip()}' is not defined"), name.strip(), file=ast_list.file, line_no=ast_list[0].line_number) - # TODO: add support for checking if the name is defined in the parent scope - pass - - if "::" in value: - static_call = value.get_all_after("::")[0] - # TODO: add support for static calls - - if "self" not in name: - output += f"{INDENT_CHAR*ast_list.indent_level}try:\n" - try: - output += f"{INDENT_CHAR*(ast_list.indent_level+1)}{name} = {value.full_line()}\n" - except KeyError: - panic(NameError(f"Variable '{name.strip()}' is not defined"), name.strip(), file=ast_list.file, line_no=ast_list[0].line_number) - output += f"{INDENT_CHAR*ast_list.indent_level}except AttributeError:\n" - output += f"{INDENT_CHAR*(ast_list.indent_level+1)}{name} = {value.full_line()}\n" - output += f"{INDENT_CHAR*(ast_list.indent_level+1)}print(\"WARN: \\\"{name}\\\" does not contain the attribute '__set__' falling back to default assignment.\")\n" - else: - output += f"{INDENT_CHAR*ast_list.indent_level}{name} = {value.full_line()}\n" - # TODO: add support for self calls - else: - output = INDENT_CHAR*ast_list.indent_level + ast_list.full_line().replace("::", ".") - - return Processed_Line(output, ast_list) \ No newline at end of file + if not self._is_variable_defined(name): + continue + output += f"{INDENT_CHAR * self.ast_list.indent_level}{name} = {value}\n" + return output + + def _is_variable_defined(self, name: str) -> bool: + return name in self.current_scope.variables or name in self.parent_scope.variables + + +def _unmarked(ast_list: Token_List, current_scope: Scope, parent_scope: Scope, root_scope: Scope, ignore_main: bool) -> Processed_Line: + return UnmarkedTranslator(ast_list, current_scope, parent_scope, root_scope, ignore_main).parse() \ No newline at end of file diff --git a/src/panic.py b/src/panic.py index fdf23c4..29eb7c4 100644 --- a/src/panic.py +++ b/src/panic.py @@ -245,7 +245,7 @@ def s_u2(line: str | list) -> str: def panic( __error: type[BaseException] | BaseException | Exception, - *_mark: tuple[Any] | str, + *_mark: Any | str, file: str = "", line_no: int = 0, no_lines: bool = False, diff --git a/src/token/normalize_tokens.py b/src/token/normalize_tokens.py index cca3df8..338c03b 100644 --- a/src/token/normalize_tokens.py +++ b/src/token/normalize_tokens.py @@ -32,9 +32,9 @@ def normalize_tokens(_lines: tuple[Token, ...], path: str) -> tuple[Token_List, stack: list[Token] = [] current_line: list[Token] = [] final_lines: list[list[Token]] = [] - firs_inst: bool = True - in_for_loop: bool = False - indent_level: int = 0 + firs_inst: bool = True + in_for_loop: bool = False + indent_level: int = 0 previous_element: Token = Token("", "", 0, 0) # lines is expected to countian ['token', ...] for every token in the file NOT individual line @@ -98,7 +98,7 @@ def process_for_loops(index: int) -> None: in_for_loop = True if in_for_loop: if token == "<\\n>": - lines[index].line = ";" + lines[index].token = ";" if lines[index + 1].line.startswith("<\\t:"): lines[index + 1].line = "" elif token == ":" and (index + 1) < len(lines) and lines[index + 1].line == "<\\n>": diff --git a/syntax/test.hlx b/syntax/test.hlx index b8a77df..675fd69 100644 --- a/syntax/test.hlx +++ b/syntax/test.hlx @@ -16,7 +16,7 @@ ~~ test_set(); -~*~ +~* import random import time import os @@ -73,20 +73,24 @@ def main(): os.system('cls' if os.name == 'nt' else 'clear') print(l, end='', flush=true) time.sleep(1 / 30) -~*~ - + *~ class C_cout { + fn new(self) { + ... + } + fn <<(self, a: string) { print(a); } - fn subtract() { - ... - }; } fn main(argv: list) { - let a: int = "12"; + let a: C_cout = C_cout(); + let b: C_cout = a; + a << "hello world"; + subtract(123, 123); + print(a_cursed_fucntion(23)); add(123, 123); print(add.await()); ~~ TODO: change to await @@ -98,6 +102,19 @@ fn main(argv: list) { print(do_something.await()); print("done"); + + ~~ instantiate a C_cout object + let cout: C_cout = C_cout(); + + cout << "hello world"; + + let a, b: int = 2; + print(a, b); + + for (let i: int = 0, let j: int = 0; i < 10; i++, j += 3) { + printf("i: %d, j: %d", i, j); + } + return 0; } @@ -107,6 +124,32 @@ async fn add(a: int, b: int) -> int { } private fn subtract(a: int, b: int) -> int { + ~~ open("file.txt", "r") as file { + ~~ file.read(); + ~~ } + + let some_map: map = { "a": 1, "b": 2, "c": 3 }; + let some_list: list = [1, 2, 3]; + let some_set: set = { 1, 2, 3 }; + let some_tuple: tuple = (1, 2, 3); + let some_string: string = "hello"; + let some_char: char = 'a'; + let some_bool: bool = true; + let some_float: float = 1.0 / 3.0; + let some_double: double = 1.0 / 3.0; + + let some_int: int = 2**9046; + + print("map:", some_map); + print("list:", some_list); + print("set:", some_set); + print("tuple:", some_tuple); + print("string:", some_string); + print("char:", some_char); + print("bool:", some_bool); + print("float:", some_float); + print("double:", some_double); + print("int:", some_int); return a - b } @@ -119,7 +162,9 @@ async fn do_something() { } -fn a_cursed_fucntion(a: int) -> FunctionType { printf("new something: %d", a); return a_cursed_fucntion } + + +fn a_cursed_fucntion(a: int) -> FunctionType { return a_cursed_fucntion } ~*~ fn test_int() { @@ -156,7 +201,6 @@ fn test_string() { print("a + b is", a + b); } - fn test_bool() { let a: bool = true; let b: bool = false; From d989682b53f587970277b0564407909298678504 Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Sat, 16 Mar 2024 23:40:04 -0400 Subject: [PATCH 5/8] Update dependencies and fix code issues --- .helix/cache/build_cache/cappy_hlx.py | 227 ++++ .helix/cache/build_cache/cappy_hlx.py.lines | 223 ++++ .helix/cache/build_cache/donut_hlx.py | 227 ++++ .helix/cache/build_cache/donut_hlx.py.lines | 223 ++++ .helix/cache/build_cache/test_hlx.py | 59 +- .helix/cache/build_cache/test_hlx.py.lines | 84 +- .helix/helix.toml | 74 ++ .helix/include/_types.py | 1 + .helix/include/core.py | 15 +- c_interop.py | 7 + cache/find_keyword.cache | Bin 80 -> 131 bytes compile.bat | 16 + compile.py | 84 +- helix.py | 1030 +----------------- helix.toml | 50 + init | 42 - requirements.txt | 4 +- src/classes/ArgParser.py | 182 ++++ src/classes/Hashing.py | 172 +++ src/classes/HelixLanguage.py | 145 +++ src/classes/Timer.py | 72 ++ src/classes/Token.py | 39 +- src/classes/WorkerPool.py | 32 +- src/config.py | 283 ++++- src/core/base.py | 36 +- src/core/framework.py | 47 +- src/core/imports.py | 15 +- src/functions/_functions.py | 44 +- src/functions/_match.py | 2 +- src/functions/_unmarked.py | 8 +- src/functions/inject_core.py | 291 +++++ src/lib/remove_blank_lines.dll | Bin 0 -> 66048 bytes src/lib/src/lib.h | 1 + src/lib/src/remove_blank_lines.c | 60 + src/token/remove_comments.py | 4 +- src/token/tokenize_file.py | 27 +- src/token/tokenize_line.py | 5 +- syntax/cappy.hlx | 21 + syntax/{head_syntax.hdlx => head_syntax.hhx} | 15 +- syntax/test.hlx | 19 +- tests.py | 38 +- 41 files changed, 2712 insertions(+), 1212 deletions(-) create mode 100644 .helix/cache/build_cache/cappy_hlx.py create mode 100644 .helix/cache/build_cache/cappy_hlx.py.lines create mode 100644 .helix/cache/build_cache/donut_hlx.py create mode 100644 .helix/cache/build_cache/donut_hlx.py.lines create mode 100644 .helix/helix.toml create mode 100644 c_interop.py create mode 100644 compile.bat create mode 100644 helix.toml delete mode 100644 init create mode 100644 src/classes/ArgParser.py create mode 100644 src/classes/Hashing.py create mode 100644 src/classes/HelixLanguage.py create mode 100644 src/classes/Timer.py create mode 100644 src/functions/inject_core.py create mode 100644 src/lib/remove_blank_lines.dll create mode 100644 src/lib/src/lib.h create mode 100644 src/lib/src/remove_blank_lines.c create mode 100644 syntax/cappy.hlx rename syntax/{head_syntax.hdlx => head_syntax.hhx} (54%) diff --git a/.helix/cache/build_cache/cappy_hlx.py b/.helix/cache/build_cache/cappy_hlx.py new file mode 100644 index 0000000..1898312 --- /dev/null +++ b/.helix/cache/build_cache/cappy_hlx.py @@ -0,0 +1,227 @@ +# trunk-ignore-all(black) +# trunk-ignore-all(isort) +# -------------------------------------------------------------------------------- +# GENERATED FILE +# -------------------------------------------------------------------------------- +# Filename: cappy.hlx +# Generation Date: 2024-03-16 13:32:32 +# Generator: Helix Transpiler +# -------------------------------------------------------------------------------- +# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications +# made directly to this file may be OVERWRITTEN during future compilations. To +# introduce changes, please modify the source files and recompile. +# -------------------------------------------------------------------------------- +# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) +# SPDX-License-Identifier: CC-BY-4.0 +# License Details: https://creativecommons.org/licenses/by/4.0/ +# +# By using this file, you agree to the Terms and Conditions of the License. +# -------------------------------------------------------------------------------- +# Helix Version: 0.1.0-alpha.a +# Repository: https://github.com/kneorain/helix +# Documentation: https://kneorain.github.io/helix/ +# For further assistance, contact the development team or refer to project documentation. +# -------------------------------------------------------------------------------- +from __future__ import annotations # type: ignore +from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore +from beartype import beartype, BeartypeConf # type: ignore +###### from include.plum.plum import dispatch as overload_with_type_check +import os # type: ignore +import sys # type: ignore +import types # type: ignore +sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py")) + os.sep + ".helix") # type: ignore +sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore +sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore +# trunk-ignore(ruff/F401) +from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore +# trunk-ignore(ruff/F401) +# trunk-ignore(ruff/F811) +from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore +import threading # type: ignore +import functools # type: ignore +_lock = threading.Lock() +__file__ = "Z:\\devolopment\\helix\\helix-lang\\syntax\\cappy.hlx" +# trunk-ignore(ruff/F821) +def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): + import traceback + import linecache + import os + from include.core import _H_tokenize_line__ + from beartype.roar import BeartypeException + + print() + thread_name = None + if thread_error and exception_type is not None: + thread_name = exception_type.thread.name # type: ignore + exception = exception_type.exc_value # type: ignore + tb = exception_type.exc_traceback # type: ignore + exception_type = exception_type.exc_type # type: ignore + stack = traceback.extract_tb(tb) + stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) + exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception + current_exception = exception + relevant_frames = [] + early_replacements = dict((v, k) for k, v in {'...': 'None', '??': 'if', '|:': 'else', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) + # First loop: filter out irrelevant frames + index = 0 + for frame in stack: + filename = frame.filename + line_no = frame.lineno + if "_hlx" in os.path.basename(filename): + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + if line_no == -1: + continue + + if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): + continue + + if ( + linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore + and + linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore + ): + continue + + relevant_frames.append((index, frame)) + index += 1 + if len(relevant_frames) > 1: + _lock.acquire(blocking=True, timeout=1.2) + for frame_info in relevant_frames: + index, frame = frame_info + filename = frame.filename + line_no = frame.lineno + if "_hlx" in os.path.basename(filename): + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + try: + file_ext = os.path.basename(filename).split('.')[1] # type: ignore + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + thread_name=thread_name, + lang=file_ext + ) + current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore + else: + _lock.release() + exit(1) + else: + _lock.acquire(blocking=True, timeout=0.1) + index, frame = relevant_frames[0] + filename = frame.filename + line_no = frame.lineno + if "_hlx" in filename: + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + try: + file_ext = os.path.basename(filename).split('.')[1] + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + lang=file_ext + ) + _lock.release() + exit(1) +sys.excepthook = exception_handler # type: ignore +threading.excepthook = functools.partial(exception_handler, thread_error=True) +sys.argv = ["Z:\\devolopment\\helix\\helix-lang\\helix.py", "Z:\\devolopment\\helix\\helix-lang\\syntax\\cappy.hlx"] + list(sys.argv)[2:] +del os, threading, functools +overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore +import os +import time +import math +@overload_with_type_check +def main(): + A: float | hx_float = float(0.0) + B: float | hx_float = float(0.0) + while True : + z: list[float | hx_float] | hx_list[float | hx_float] = list([ 0.0 ] * 1760) + b: list[str | hx_string] | hx_list[str | hx_string] = list([ " " ] * 1760) + for j in range ( 0 , 628 , 7 ): + for i in range ( 0 , 628 , 2 ): + c: float | hx_float = float(math . sin ( i )) + d: float | hx_float = float(math . cos ( j )) + e: float | hx_float = float(math . sin ( A )) + f: float | hx_float = float(math . sin ( j )) + g: float | hx_float = float(math . cos ( A )) + h: float | hx_float = float(d + 2) + D: float | hx_float = float(1 / ( c * h * e + f * g + 5 )) + l: float | hx_float = float(math . cos ( i )) + m: float | hx_float = float(math . cos ( B )) + n: float | hx_float = float(math . sin ( B )) + t: float | hx_float = float(c * h * g - f * e) + x: int | hx_int = (int ( 40 + 30 * D * ( l * h * m - t * n ) )) + y: int | hx_int = (int ( 12 + 15 * D * ( l * h * n + t * m ) )) + o: int | hx_int = (int ( x + 80 * y )) + N: int | hx_int = (int ( 8 * ( ( f * e - c * d * g ) * m - c * d * e - f * g - l * d * n ) )) + if (0 <= y < 22 and 0 <= x < 80 and D > z [ o ]): + z [ o ] = D + if (N > 0): + charIndex: int | hx_int = int(N) + else: + charIndex: int | hx_int = int(0) + b [ o ] = ".,-~:;=!*#$@" [ charIndex ] + os . system ( "cls" ) + for k in C_For(k = int(0)).set_con('k < 1760').set_inc('k ++'): + if (k % 80 == 0): + print ( '\n' , end = '' ) + else: + print ( b [ k ] , end = '' ) + A += 0.04 + B += 0.02 + time . sleep ( 0.03 ) +if __name__ == "__main__": + try: + main() # type: ignore + except (KeyError, TypeError): + main(sys.argv) diff --git a/.helix/cache/build_cache/cappy_hlx.py.lines b/.helix/cache/build_cache/cappy_hlx.py.lines new file mode 100644 index 0000000..17e0ab9 --- /dev/null +++ b/.helix/cache/build_cache/cappy_hlx.py.lines @@ -0,0 +1,223 @@ +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +1 +1 +1 +2 +2 +2 +2 +3 +3 +4 +4 +5 +5 +5 +6 +6 +6 +7 +7 +8 +9 +9 +10 +10 +11 +12 +13 +14 +14 +14 +15 +15 +15 +16 +16 +17 +18 +18 +18 +19 +20 +20 +21 +-1 +-1 +-1 +-1 +-1 \ No newline at end of file diff --git a/.helix/cache/build_cache/donut_hlx.py b/.helix/cache/build_cache/donut_hlx.py new file mode 100644 index 0000000..03acf4d --- /dev/null +++ b/.helix/cache/build_cache/donut_hlx.py @@ -0,0 +1,227 @@ +# trunk-ignore-all(black) +# trunk-ignore-all(isort) +# -------------------------------------------------------------------------------- +# GENERATED FILE +# -------------------------------------------------------------------------------- +# Filename: donut.hlx +# Generation Date: 2024-03-16 01:55:19 +# Generator: Helix Transpiler +# -------------------------------------------------------------------------------- +# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications +# made directly to this file may be OVERWRITTEN during future compilations. To +# introduce changes, please modify the source files and recompile. +# -------------------------------------------------------------------------------- +# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) +# SPDX-License-Identifier: CC-BY-4.0 +# License Details: https://creativecommons.org/licenses/by/4.0/ +# +# By using this file, you agree to the Terms and Conditions of the License. +# -------------------------------------------------------------------------------- +# Helix Version: 0.1.0-alpha.a +# Repository: https://github.com/kneorain/helix +# Documentation: https://kneorain.github.io/helix/ +# For further assistance, contact the development team or refer to project documentation. +# -------------------------------------------------------------------------------- +from __future__ import annotations # type: ignore +from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore +from beartype import beartype, BeartypeConf # type: ignore +###### from include.plum.plum import dispatch as overload_with_type_check +import os # type: ignore +import sys # type: ignore +import types # type: ignore +sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py")) + os.sep + ".helix") # type: ignore +sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore +sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore +# trunk-ignore(ruff/F401) +from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore +# trunk-ignore(ruff/F401) +# trunk-ignore(ruff/F811) +from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore +import threading # type: ignore +import functools # type: ignore +_lock = threading.Lock() +__file__ = "Z:\\devolopment\\helix\\helix-lang\\syntax\\donut.hlx" +# trunk-ignore(ruff/F821) +def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): + import traceback + import linecache + import os + from include.core import _H_tokenize_line__ + from beartype.roar import BeartypeException + + print() + thread_name = None + if thread_error and exception_type is not None: + thread_name = exception_type.thread.name # type: ignore + exception = exception_type.exc_value # type: ignore + tb = exception_type.exc_traceback # type: ignore + exception_type = exception_type.exc_type # type: ignore + stack = traceback.extract_tb(tb) + stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) + exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception + current_exception = exception + relevant_frames = [] + early_replacements = dict((v, k) for k, v in {'...': 'None', '??': 'if', '|:': 'else', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) + # First loop: filter out irrelevant frames + index = 0 + for frame in stack: + filename = frame.filename + line_no = frame.lineno + if "_hlx" in os.path.basename(filename): + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + if line_no == -1: + continue + + if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): + continue + + if ( + linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore + and + linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore + ): + continue + + relevant_frames.append((index, frame)) + index += 1 + if len(relevant_frames) > 1: + _lock.acquire(blocking=True, timeout=1.2) + for frame_info in relevant_frames: + index, frame = frame_info + filename = frame.filename + line_no = frame.lineno + if "_hlx" in os.path.basename(filename): + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + try: + file_ext = os.path.basename(filename).split('.')[1] # type: ignore + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + thread_name=thread_name, + lang=file_ext + ) + current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore + else: + _lock.release() + exit(1) + else: + _lock.acquire(blocking=True, timeout=0.1) + index, frame = relevant_frames[0] + filename = frame.filename + line_no = frame.lineno + if "_hlx" in filename: + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + try: + file_ext = os.path.basename(filename).split('.')[1] + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + lang=file_ext + ) + _lock.release() + exit(1) +sys.excepthook = exception_handler # type: ignore +threading.excepthook = functools.partial(exception_handler, thread_error=True) +sys.argv = ["Z:\\devolopment\\helix\\helix-lang\\helix.py", "Z:\\devolopment\\helix\\helix-lang\\syntax\\donut.hlx"] + list(sys.argv)[2:] +del os, threading, functools +overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore +import os +import time +import math +@overload_with_type_check +def main(): + A: float | hx_float = float(0.0) + B: float | hx_float = float(0.0) + while True : + z: list[float | hx_float] | hx_list[float | hx_float] = list([ 0.0 ] * 1760) + b: list[str | hx_string] | hx_list[str | hx_string] = list([ " " ] * 1760) + for j in range ( 0 , 628 , 7 ): + for i in range ( 0 , 628 , 2 ): + c: float | hx_float = float(math . sin ( i )) + d: float | hx_float = float(math . cos ( j )) + e: float | hx_float = float(math . sin ( A )) + f: float | hx_float = float(math . sin ( j )) + g: float | hx_float = float(math . cos ( A )) + h: float | hx_float = float(d + 2) + D: float | hx_float = float(1 / ( c * h * e + f * g + 5 )) + l: float | hx_float = float(math . cos ( i )) + m: float | hx_float = float(math . cos ( B )) + n: float | hx_float = float(math . sin ( B )) + t: float | hx_float = float(c * h * g - f * e) + x: int | hx_int = (int ( 40 + 30 * D * ( l * h * m - t * n ) )) + y: int | hx_int = (int ( 12 + 15 * D * ( l * h * n + t * m ) )) + o: int | hx_int = (int ( x + 80 * y )) + N: int | hx_int = (int ( 8 * ( ( f * e - c * d * g ) * m - c * d * e - f * g - l * d * n ) )) + if (0 <= y < 22 and 0 <= x < 80 and D > z [ o ]): + z [ o ] = D + if (N > 0): + charIndex: int | hx_int = int(N) + else: + charIndex: int | hx_int = int(0) + b [ o ] = ".,-~:;=!*#$@" [ charIndex ] + os . system ( "cls" ) + for k in C_For(k = int(0)).set_con('k < 1760').set_inc('k ++'): + if (k % 80 == 0): + print ( '\n' , end = '' ) + else: + print ( b [ k ] , end = '' ) + A += 0.04 + B += 0.02 + time . sleep ( 0.02 ) +if __name__ == "__main__": + try: + main() # type: ignore + except (KeyError, TypeError): + main(sys.argv) diff --git a/.helix/cache/build_cache/donut_hlx.py.lines b/.helix/cache/build_cache/donut_hlx.py.lines new file mode 100644 index 0000000..4fcdebb --- /dev/null +++ b/.helix/cache/build_cache/donut_hlx.py.lines @@ -0,0 +1,223 @@ +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +1 +1 +1 +2 +2 +2 +2 +3 +3 +4 +4 +5 +5 +5 +6 +6 +6 +7 +7 +8 +9 +9 +10 +11 +12 +13 +14 +15 +15 +15 +16 +16 +16 +17 +17 +18 +19 +19 +19 +20 +21 +21 +22 +-1 +-1 +-1 +-1 +-1 \ No newline at end of file diff --git a/.helix/cache/build_cache/test_hlx.py b/.helix/cache/build_cache/test_hlx.py index 24a1982..fdaf46e 100644 --- a/.helix/cache/build_cache/test_hlx.py +++ b/.helix/cache/build_cache/test_hlx.py @@ -4,7 +4,7 @@ # GENERATED FILE # -------------------------------------------------------------------------------- # Filename: test.hlx -# Generation Date: 2024-03-15 00:33:53 +# Generation Date: 2024-03-16 23:38:31 # Generator: Helix Transpiler # -------------------------------------------------------------------------------- # WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications @@ -33,13 +33,14 @@ sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore # trunk-ignore(ruff/F401) -from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore +from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Infix, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, partial, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore # trunk-ignore(ruff/F401) # trunk-ignore(ruff/F811) from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore import threading # type: ignore import functools # type: ignore -__lock = threading.Lock() +__exit__ = sys.exit +_lock = threading.Lock() __file__ = "Z:\\devolopment\\helix\\helix-lang\\syntax\\test.hlx" # trunk-ignore(ruff/F821) def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): @@ -48,6 +49,7 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook import os from include.core import _H_tokenize_line__ from beartype.roar import BeartypeException + print() thread_name = None if thread_error and exception_type is not None: @@ -60,7 +62,7 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception current_exception = exception relevant_frames = [] - early_replacements = dict((v, k) for k, v in {'...': 'None', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) + early_replacements = dict((v, k) for k, v in {'...': 'None', '??': 'if', '|:': 'else', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) # First loop: filter out irrelevant frames index = 0 for frame in stack: @@ -68,21 +70,48 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook line_no = frame.lineno if "_hlx" in os.path.basename(filename): filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + try: + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + except IndexError: + print("TODO: approximate corresponding line number in .hlx file") + panic( + LookupError(f"Could not find the corresponding line number in the .hlx file for the line {frame.lineno} in the generated file {frame.filename}. Please report this issue."), + line_no=1, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + no_lines=True, + lang="py" + ) + except FileNotFoundError: + panic( + FileNotFoundError(f"The file {frame.filename} was not found. Please ensure that the file exists and is accessible." + "You should never see this error. If you do, something went really wrong."), + line_no=1, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + no_lines=True, + lang="py" + ) + if line_no == -1: continue + if line_no == -1: + continue + if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): continue + if ( linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore and linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore ): continue + relevant_frames.append((index, frame)) index += 1 if len(relevant_frames) > 1: - __lock.acquire(blocking=True, timeout=1.2) + _lock.acquire(blocking=True, timeout=1.2) for frame_info in relevant_frames: index, frame = frame_info filename = frame.filename @@ -126,10 +155,10 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook ) current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore else: - __lock.release() + _lock.release() exit(1) else: - __lock.acquire(blocking=True, timeout=0.1) + _lock.acquire(blocking=True, timeout=0.1) index, frame = relevant_frames[0] filename = frame.filename line_no = frame.lineno @@ -166,7 +195,7 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook thread_name=thread_name, lang=file_ext ) - __lock.release() + _lock.release() exit(1) sys.excepthook = exception_handler # type: ignore threading.excepthook = functools.partial(exception_handler, thread_error=True) @@ -182,7 +211,14 @@ def __init__(self: Any): def __lshift__(self: Any, a: str | hx_string): print ( a ) @overload_with_type_check +def add(a: int | hx_int, b: int | hx_int) -> int: + printf ( "adding: %d + %d" , a , b ) + return a + b +@overload_with_type_check def main(argv: list[str | hx_string] | hx_list[str | hx_string]): + _add: Infix = (Infix ( add )) + print ( 2 | _add | 3 ) + __exit__ ( ) a: C_cout = (C_cout ( )) b: C_cout = C_cout(a) a << "hello world" @@ -205,11 +241,14 @@ def main(argv: list[str | hx_string] | hx_list[str | hx_string]): printf ( "i: %d, j: %d" , i , j ) return 0 @hx__async -def add(a: int | hx_int, b: int | hx_int) -> int: +def _(a: int | hx_int, b: int | hx_int) -> int: printf ( "adding: %d + %d" , a , b ) return a + b @overload_with_type_check def subtract(a: int | hx_int, b: int | hx_int) -> int: + float_a: float | hx_float = float(0.1 + 0.2) + float_b: double | hx_double = double(0.1 + 0.2) + print ( "float_a:" , float_a , "\nfloat_b:" , float_b ) some_map: dict[str | hx_string , int | hx_int] | hx_map[str | hx_string , int | hx_int] = dict({ "a" : 1 , "b" : 2 , "c" : 3 }) some_list: list[int | hx_int] | hx_list[int | hx_int] = list([ 1 , 2 , 3 ]) some_set: set[int | hx_int] | hx_set[int | hx_int] = set({ 1 , 2 , 3 }) diff --git a/.helix/cache/build_cache/test_hlx.py.lines b/.helix/cache/build_cache/test_hlx.py.lines index 4775033..13baf0c 100644 --- a/.helix/cache/build_cache/test_hlx.py.lines +++ b/.helix/cache/build_cache/test_hlx.py.lines @@ -173,6 +173,30 @@ -1 -1 -1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 78 78 79 @@ -185,44 +209,41 @@ 88 89 90 -91 -92 93 -95 +93 +94 96 98 99 100 -99 +101 +102 103 -104 -107 +105 +106 +108 +109 +110 109 -111 -111 -112 +113 114 -115 -118 +117 +119 121 121 122 -123 -126 -126 +124 +125 +128 +131 131 132 133 -134 -135 136 -137 -138 -139 +136 141 +142 143 -144 -145 146 147 148 @@ -230,15 +251,28 @@ 150 151 152 +153 154 -158 +156 158 159 160 -159 -167 -167 +161 +162 +163 +164 +165 +166 167 +169 +173 +173 +174 +175 +174 +182 +182 +182 -1 -1 -1 diff --git a/.helix/helix.toml b/.helix/helix.toml new file mode 100644 index 0000000..1521daf --- /dev/null +++ b/.helix/helix.toml @@ -0,0 +1,74 @@ + +# --- Core Configurations --- +# core_location: Path to the core directory. It's where essential files are stored. +# auto_update: Enables automatic updates for the core components. +# Note: Disabling this might cause compatibility issues with future versions. +[core] + core_location = ".helix" + auto_update = true + + +# --- Transpiler Configurations --- +# Detailed settings for the code transpiler. +# target: Specifies the target language for transpilation. Options: 'py', 'c++', etc. +# warnings: Toggles the display of warnings during transpilation. +# verbose: Enables verbose output for detailed logs. +# threads: Defines the number of threads to use for transpilation. +# optimization: Sets the level of optimization. Ranges from 0 (none) to 3 (maximum). +# regex_module: Chooses the regex module to be used. Options: 're', 're2'. +[transpiler] + target = "py" + warnings = true + verbose = false + threads = 50 + optimization = 3 + regex_module = "re" + + +# --- Compiler Configurations --- +# Configurations for various compilers used in the project. +# py: Python layer options: 'py' (default), 'bin', 'vile'. +# 'py' is typical for development and testing. +# 'bin' compiles to a binary executable for production. +# 'vile' generates an ABI for use with other languages and as a Helix library. +# c, cpp, rust: Specify the compilers for C, C++, and Rust respectively. +# Defaults: 'gcc' for C, 'g++' for C++, 'rustc' for Rust. +# flags_py, flags_cpp, flags_c, flags_rust: Custom flags for compiling Python, C++, C, and Rust. +[compiler] + py = "z:\\devolopment\\helix\\helix-lang\\.venv\\Scripts\\python.exe" + c = "gcc" + cpp = "g++" + rust = "rustc" + flags_py = "-OO" + flags_cpp = "-std=c++17" + flags_c = "-std=c18" + flags_rust = "--edition=2021" + + +# --- Linker Configurations --- +# Settings for linking binaries and libraries. +# c_cpp_include_dirs: Include directories for C/C++ source files. +# rs_include_dirs: Include directories for Rust source files. +# py_include_dirs: Include directories for Python source files. +# lib_dirs: Directories where libraries are located. +[linker] + c_cpp_include_dirs = [] + rs_include_dirs = [] + py_include_dirs = [] + lib_dirs = [] + link_static = false + + +# --- Formatter Configurations --- +# Configurations for code formatting. +# indent_size: The size of indentation in number of spaces. +# formatter: The tool used for formatting (e.g., 'black' for Python). +# always_format: When set to true, always formats the generated code. +# Note: Enabling this might break certain code structures, recommended for debugging only. +[formatter] + indent_size = 4 + formatter = "black" + always_format = false + +[environment] + [env_vars] diff --git a/.helix/include/_types.py b/.helix/include/_types.py index e69de29..83df6c9 100644 --- a/.helix/include/_types.py +++ b/.helix/include/_types.py @@ -0,0 +1 @@ +import beartype \ No newline at end of file diff --git a/.helix/include/core.py b/.helix/include/core.py index f8a67f4..d3e8156 100644 --- a/.helix/include/core.py +++ b/.helix/include/core.py @@ -9,7 +9,7 @@ from array import array from enum import Enum -from functools import wraps +from functools import wraps, partial import inspect from threading import Thread from types import MappingProxyType as FastMap, NoneType, FunctionType, UnionType, BuiltinFunctionType @@ -49,7 +49,18 @@ def __import_py__(*args, **kwargs): raise NotImplementedError("Python is not supported in Helix (YET)") - +class Infix: + def __init__(self: Any, func: Callable) -> None: + self.func = func + + def __ror__(self: Any, a: Any) -> Any: + return Infix(partial(self.func, a)) + + def __or__(self: Any, a: Any) -> Any: + return self.func(a) + + def operator(self: Any, left: Any, right: Any) -> Any: + return self.func(left, right) getcontext().prec = 128 replace_primitives = { diff --git a/c_interop.py b/c_interop.py new file mode 100644 index 0000000..03f4923 --- /dev/null +++ b/c_interop.py @@ -0,0 +1,7 @@ +from src.core.imports import ( + CONFIG, + framework, +) + +class C_Interop(framework.Interop): + \ No newline at end of file diff --git a/cache/find_keyword.cache b/cache/find_keyword.cache index b07c6f1304da8929df9ff5acd7e320afff849f67..74ee52114f632bbcf08c5cc16e3c7c697785a486 100644 GIT binary patch delta 65 zcmWG&W@Kq#nOZTC#mlsZE!5A)H8^-m>y#e0(!8A1;^HYiOrCBaK2v7elpc0hpJ3N` NhyY7!4p6964*>7e7tjCz delta 14 VcmZo>3}9(snd&-`#fzy_4*(pX1G@kK diff --git a/compile.bat b/compile.bat new file mode 100644 index 0000000..1643bd0 --- /dev/null +++ b/compile.bat @@ -0,0 +1,16 @@ +@ECHO OFF + +REM Check if the GCC compiler exists +IF NOT EXIST "c:\Programing Languages\LLVM\bin\gcc.exe" ( + ECHO The path does not exist. THIS IS MEANT TO ONLY be run on my computer. Please change the path to your own gcc compiler. + EXIT /B +) + +REM Check if file name argument is provided +IF "%~1"=="" ( + ECHO No file name provided. + EXIT /B +) + +REM Call the Python script passing all arguments +python compile.py %* \ No newline at end of file diff --git a/compile.py b/compile.py index b5fa633..bacdecb 100644 --- a/compile.py +++ b/compile.py @@ -1,22 +1,74 @@ -from Cython.Build import cythonize -from setuptools import setup +#from Cython.Build import cythonize +#from setuptools import setup +# +#MODE = 1 +# +## distutils: language=c++ +# +#with open("start.py", "r", encoding="utf-8") as file: +# code = file.read() +# +#with open("start.pyx", "w", encoding="utf-8") as file: +# file.write("# distutils: language=c++\n\n" + code) +# +#input("Press any key to continue...") +# +#setup( +# ext_modules=cythonize( +# "start.pyx", +# compiler_directives={"language_level": "3"}, +# nthreads=10, +# ) +#) -MODE = 1 +import subprocess +import os +import sys -# distutils: language=c++ +COMPILER_PATH = r"c:\Programing Languages\LLVM\bin\gcc.exe" -with open("start.py", "r", encoding="utf-8") as file: - code = file.read() +def compile_file(file_name): + # Determine if the path is absolute or relative + if os.path.isabs(file_name): + # It's an absolute path + full_path = file_name + else: + # It's a relative path, prepend the standard path + full_path = os.path.join("src", "lib", "src", file_name) -with open("start.pyx", "w", encoding="utf-8") as file: - file.write("# distutils: language=c++\n\n" + code) + # set the path to the relative path + full_path = os.path.relpath(full_path) -input("Press any key to continue...") + # Extract the file name without extension from the full path + file_name_no_extension = os.path.splitext(os.path.basename(full_path))[0] -setup( - ext_modules=cythonize( - "start.pyx", - compiler_directives={"language_level": "3"}, - nthreads=10, - ) -) + # Compile the file using GCC + command = [ + f"\"{COMPILER_PATH}\"", + "-std=gnu18", + "-shared", + "-o", f"\"{os.path.join('src', 'lib', f'{file_name_no_extension}.dll')}\"", + "-fPIC", + f"\"{full_path}\"" + ] + print(" ".join(command)) + compile_status = subprocess.call(" ".join(command), shell=True) + + # Check if compilation was successful + if compile_status != 0: + print("\u001b[91mCompilation failed.\u001b[0m") + sys.exit(1) + else: + print("\u001b[92mCompilation successful.\u001b[0m") + +if __name__ == "__main__": + # Check if file name argument is provided + if len(sys.argv) < 2: + print("No file name provided.") + sys.exit(1) + + file_name = sys.argv[1] + compile_file(file_name) + +"c:\Programing Languages\LLVM\bin\gcc.exe" -std=gnu18 -c "src\lib\src\remove_blank_lines.c" +"c:\Programing Languages\LLVM\bin\gcc.exe" -std=gnu18 -shared -o "src\lib\remove_blank_lines.dll" -fPIC "remove_blank_lines.o" \ No newline at end of file diff --git a/helix.py b/helix.py index 4d5637c..50b6163 100644 --- a/helix.py +++ b/helix.py @@ -2,722 +2,48 @@ # -*- coding: utf-8 -*- from __future__ import annotations -#import tracemalloc -#import atexit - -#tracemalloc.start() -#atexit.register(tracemalloc.stop) - import gc -import atexit gc.disable() # disable garbage collection for performance +import atexit + import src.core.base as base from src.core.imports import ( os, - signal, sys, - shutil, - hashlib, - threading, - subprocess, - functools, - datetime, - exit, - Event, - Any, Callable, Iterable, Optional, Tuple, FrameType, ModuleType, - Namespace, ArgumentParser, - perf_counter_ns as time, - TextIOWrapper, - Processed_Line, Token_List, - Tokenizer, - load_config, + Any, + Tuple, panic, + Event, + Timer, Scope, + signal, + shutil, + CONFIG, + Hashing, + Callable, + FileMode, + Optional, + Namespace, + FrameType, + Tokenizer, + ArgParser, + framework, + ModuleType, + subprocess, Transpiler, - FileMode, format_file_contents, + inject_core, + HelixLanguage, + format_file_contents, color_print as print, - framework, + perf_counter_ns as time, + Processed_Line, Token_List, ) __version__: str = "0.1.0-alpha.a" -USE_CACHE: bool = False - bar_thread = Event() -class Hashing: - """ - Provides functionality for hashing and comparing hashes of code files. - - Attributes - ---------- - file_path : str - The path to the file to be hashed. - output_path : str - The path where the hash output should be stored. - - Methods - ------- - __init__(self, file_path: str, output_path: str) - Initializes the Hashing object with file paths. - compute_hash(code: str) -> bytes - Computes and returns the hash of the given code. - create_hash_only(self) - Creates a hash of the file at file_path and stores it at output_path. - get_mount(path) - Retrieves the mount point for the given path. - create_file(self, code: str) - Creates a file with the given code and hashes it. - is_code_altered(self) -> bool - Checks if the code has been altered compared to its stored hash. - get_hash(self) -> bytes | None - Retrieves the hash of the file if it exists. - """ - def __init__(self, file_path: str, output_path: str): - file: TextIOWrapper = open(file_path, "r") - self.__hash = Hashing.compute_hash(file.read()) - - file.close() - del file - - self.__output_path = output_path - - def __str__(self) -> str: - try: - return f"Hashing(hash={self.__hash.decode()}, output_path={self.__output_path})" - except UnicodeDecodeError: - return f"Hashing(hash={repr(self.__hash)}, output_path={self.__output_path})" - - def __repr__(self) -> str: - return self.__str__() - - def __int__(self) -> int: - return int.from_bytes(self.__hash, "big") - - @staticmethod - def compute_hash(code: str) -> bytes: - return hashlib.md5(code.encode("utf-8")).digest() - - def create_hash_only(self) -> None: - open(self.__output_path + ":hash", "wb").write( - self.__hash - ) - - @staticmethod - def get_mount(path): - path = os.path.realpath(os.path.abspath(path)) - while path != os.path.sep: - if os.path.ismount(path): - return path - path = os.path.abspath( - os.path.join(path, os.pardir) - ) - - return path - - def __windows_io( - self, writeable: str = "" - ) -> Optional[bytes]: - if writeable: - with open( - self.__output_path, "wb" - ) as file, open( - self.__output_path + ":hash", "wb" - ) as ads: - file.write(writeable.encode("utf-8")) - ads.write(self.__hash) - return open( - self.__output_path + ":hash", "rb" - ).read() - - def __linux_io( - self, writeable: str = "" - ) -> Optional[bytes]: - if sys.platform in ["linux", "linux2", "darwin"]: - import xattr # type: ignore - - if writeable: - with open(self.__output_path, "wb") as file: - file.write(writeable.encode("utf-8")) - - attr = xattr.xattr(self.__output_path) - attr.set("user.hash", self.__hash) - - attr = xattr.xattr(self.__output_path) - return attr.get("user.hash") - return None - - def __io(self, writeable: str = "") -> Optional[bytes]: - if sys.platform == "win32": - return self.__windows_io(writeable) - - elif sys.platform in ["linux", "linux2", "darwin"]: - return self.__linux_io(writeable) - - else: - raise ImportError( - "xattr library is required on Linux and macOS for setting extended attributes." - ) - - def create_file(self, code: str) -> None: - try: - self.__io(code) - - except ImportError: - raise ImportError( - "xattr library is required on Linux and macOS for setting extended attributes." - ) - - except PermissionError: - raise PermissionError( - "You do not have permission to write to the file." - ) - - except FileNotFoundError: - raise FileNotFoundError( - "The file does not exist." - ) - - def is_code_altered(self) -> bool: - if not USE_CACHE: - return True - - if not os.path.exists(self.__output_path) or ( - os.path.exists(self.__output_path + ":hash") - if sys.platform in ["linux", "linux2", "darwin"] - else False - ): - return ( - True # Hash file doesn't exist or is empty - ) - - existing_hash = self.get_hash() - if self.__hash != existing_hash: - return True - - return False - - def get_hash(self) -> bytes | None: - try: - return self.__io() - except FileNotFoundError: - open(self.__output_path + ":hash", "wb").write( - self.__hash - ) - return None - - -def watch_processes() -> None: - """ - Monitors and manages active processes in the Helix language environment. - - This function watches all active processes and performs necessary actions - like cleanup or logging based on their status and outcomes. - """ - while True: - for ( - pid, - thread, - ) in ThreadedProcess.__processes_queue__.items(): - if not thread.is_alive(): - ThreadedProcess.__processes_queue__[ - pid - ].join(1 / 1000) - del ThreadedProcess.__processes_queue__[pid] - - -def clean_docstring(docstring: str) -> str: - """ - Cleans up the given docstring by removing unnecessary whitespace and newlines. - - Parameters - ---------- - docstring : str - The docstring to be cleaned. - - Returns - ------- - str - The cleaned docstring. - """ - if not docstring: - return "" - - indentation_level: int = 0 - for char in docstring.splitlines()[1]: - if not char.isspace(): - break - indentation_level += 1 - - return "\n".join( - [ - line[indentation_level:] - for line in docstring.splitlines() - ] - ) - -class ThreadedProcess: - """ - Manages threaded execution of processes. - - Attributes - ---------- - __processes_queue__ : dict[int, threading.Thread] - Queue of processes managed by this class. - - Methods - ------- - __new__(cls, func: Callable[..., None]) - Creates a new instance of ThreadedProcess. - __init__(self, func: Callable[..., None]) - Initializes the ThreadedProcess with a function. - processes(self) -> dict[int, threading.Thread] - Returns the current queue of processes. - __call__(self, *args, **kwargs) - Executes the function in a separate thread. - """ - # TODO: GET DONE - # this is supposed to be a threaded process decorator to be applied to fucntions - # that are supposed to be run in a separate thread. the process can not have a return - __processes_queue__: dict[int, threading.Thread] = {} - - def __new__(cls, func: Callable[..., None]): - threading.Thread( - target=watch_processes, daemon=True - ).start() - return super().__new__(cls) - - def __init__(self, func: Callable[..., None]): - self.__func__ = func - functools.update_wrapper(self, func) - - def __call__(self, *args, **kwargs): - def thread_target(): - try: - self.__func__(*args, **kwargs) - finally: - try: - ThreadedProcess.__processes_queue__[ - self.__pid__ - ].join(1 / 1000) - except RuntimeError: - pass - del ThreadedProcess.__processes_queue__[ - self.__pid__ - ] - - self.__pid__ = id(self.__func__) - self.__thread__ = threading.Thread( - target=thread_target - ) - self.__thread__.start() - self.__class__.__processes_queue__[self.__pid__] = ( - self.__thread__ - ) - - if not self.__thread__.is_alive(): - try: - ThreadedProcess.__processes_queue__[ - self.__pid__ - ].join(1 / 1000) - except RuntimeError: - pass - del ThreadedProcess.__processes_queue__[ - self.__pid__ - ] - raise Exception( - "Thread has stopped unexpectedly." - ) - - @property - def processes(self) -> dict[int, threading.Thread]: - return self.__class__.__processes_queue__ - - def __str__(self) -> str: - return f"ThreadedProcess(func={self.__func__.__name__})" - - def __repr__(self) -> str: - return self.__str__() - -class ArgParser: - """ - Parses command-line arguments for the Helix language application. - - Attributes - ---------- - __args__ : Incomplete - Parsed arguments from the command line. - - Methods - ------- - help_screen(self) -> None - Displays the help screen with usage instructions. - version_screen(self) -> None - Displays the version information of the Helix language. - __init__(self, argv: Optional[Iterable[str]] = None) - Initializes the argument parser with command-line arguments. - args(self) -> Namespace - Returns the namespace of parsed command-line arguments. - """ - def help_screen(self): - print(clean_docstring(""" - usage: helix [-h] [-v] [-o COMPILE] [-d] [-l LOG] [-c CONFIG] [-s] file ... - - Welcome to the Helix CLI, the gateway to harnessing the power and simplicity of Helix, - a programming language designed for developers who cherish Python's ease but crave more - robust features. This CLI tool empowers you to write, manage, and transpile Helix code - with a range of commands and options tailored for a seamless development experience. - - positional arguments: - file the name of the file to be executed - doc the name of the documentation page to be displayed - other other arguments to be passed to the file as argv - - options: - -h, --help show this help message and exit - -v, --version show the version number and exit - -o COMPILE, --compile COMPILE_FILE compile the file to a specific output file - -d, --debug enable debug mode - -l LOG, --log LOG_FILE the name of the log file - -c CONFIG, --config CONFIG_FILE specify or create a config file - -s, --silent enable silent mode - -w, --watch watch the file for changes and recompile - -i, --install PACKAGE_NAME install new packages - -u, --uninstall PACKAGE_NAME uninstall packages - -doc DOC the name of the documentation page to be displayed - """), - word_wrap=False, - end="", - ) - exit() - - def version_screen(self): - print("🧬 " + __version__) - exit() - - def __init__( - self, argv: Optional[Iterable[str]] = None - ): - parser = ArgumentParser( - description="Welcome to the Helix CLI, the gateway to harnessing the power and simplicity of Helix," - "a programming language designed for developers who cherish Python's ease but crave more" - "robust features. This CLI tool empowers you to write, manage, and transpile Helix code" - "with a range of commands and options tailored for a seamless development experience." - ) - - # Positional arguments - parser.add_argument( - "file", - nargs="?", - help="the name of the file to be executed", - ) - parser.add_argument( - "other", - nargs="*", - help="other arguments to be passed to the file as argv", - ) - - # Optional arguments - parser.add_argument( - "-v", - "--version", - action="store_true", - help="show the version number and exit", - ) - parser.add_argument( - "-d", - "--debug", - action="store_true", - help="enable debug mode", - ) - parser.add_argument( - "-w", - "--watch", - action="store_true", - help="watch the file for changes and recompile", - ) - parser.add_argument( - "-s", - "--silent", - action="store_true", - help="enable silent mode", - ) - parser.add_argument( - "-r", - "--run", - action="store_true", - help="run the file", - ) - parser.add_argument( - "-o", - "--compile", - dest="compile_file", - help="compile the file to a specific output file", - ) - parser.add_argument( - "-l", - "--log", - dest="log_file", - help="the name of the log file", - ) - parser.add_argument( - "-c", - "--config", - dest="config_file", - help="specify or create a config file", - ) - parser.add_argument( - "-i", - "--install", - dest="install_package", - help="install new packages", - ) - parser.add_argument( - "-u", - "--uninstall", - dest="uninstall_package", - help="uninstall packages", - ) - parser.add_argument( - "-doc", - dest="store_true", - help="the name of the documentation page to be displayed", - ) - - self.__args__ = parser.parse_args(argv) # type: ignore - - # Handling version flag - if self.__args__.version: - self.version_screen() - - # Handling doc flag - try: - if self.__args__.doc: - from docs.doc import doc - - doc(self.__args__.doc) - exit() - except AttributeError: - pass - - if not self.__args__.file and not any( - vars(self.__args__).values() - ): - self.help_screen() - - @property - def args(self) -> Namespace: - return self.__args__ - - -class HelixLanguage: - """ - Represents the core functionality for managing and executing Helix language operations. - - This class encapsulates methods for various tasks like folder and file management, - setting up the Helix environment, and handling installations and configurations. - - Methods - ------- - __init__(self, *args: str, **kwargs: str) - Initialize the HelixLanguage instance with given arguments. - make_folder(directory: str) -> None - Creates a folder at the specified directory path. - make_file(file: str) -> None - Creates a file at the specified file path. - generate_folder_structure(directory: str = ...) - Generates the necessary folder structure for a given directory. - install_helix(config: dict) -> None - Installs and configures the Helix environment based on the provided configuration. - - Examples - -------- - >>> hl = HelixLanguage() - >>> hl.make_folder('path/to/directory') - >>> hl.install_helix({'option': 'value'}) - - Notes - ----- - The class is designed to be flexible and handle various aspects of the Helix language - setup and management. More detailed methods and attributes can be added as per the - development needs of the Helix language. - """ - def __init__(self, *args: str, **kwargs: str): - raise NotImplementedError( - "Cannot instantiate HelixLanguage." - ) - - @staticmethod - def make_folder(directory: str) -> None: - if not os.path.isdir(directory): - os.mkdir(directory) - - @staticmethod - def make_file(file: str) -> None: - if not os.path.exists(file): - with open(file, "w", encoding="utf-8") as _: - return - - @staticmethod - def generate_folder_structure( - directory: str = os.path.expanduser("~") - + os.sep - + ".helix", - ): - if ( - os.path.isdir(directory) - and os.listdir(directory) != [] - ): - return - - HelixLanguage.make_folder(directory) - HelixLanguage.make_folder( - os.path.join(directory, "intl_libs") - ) - HelixLanguage.make_folder( - os.path.join(directory, "intl_libs", "helix") - ) - HelixLanguage.make_folder( - os.path.join(directory, "intl_libs", "c++") - ) - HelixLanguage.make_folder( - os.path.join(directory, "intl_libs", "c") - ) - HelixLanguage.make_folder( - os.path.join(directory, "intl_libs", "python") - ) - HelixLanguage.make_folder( - os.path.join(directory, "intl_libs", "rust") - ) - - HelixLanguage.make_folder( - os.path.join(directory, "logs") - ) - HelixLanguage.make_folder( - os.path.join(directory, "temp") - ) - HelixLanguage.make_folder( - os.path.join(directory, "cache") - ) - HelixLanguage.make_folder( - os.path.join(directory, "cache", "build_cache") - ) - HelixLanguage.make_folder( - os.path.join(directory, "include") - ) - - HelixLanguage.make_file( - os.path.join(directory, "config.toml") - ) - HelixLanguage.make_file( - os.path.join(directory, "include", "core.py") - ) - - @staticmethod - def install_helix(config: dict) -> None: - if ( - config["core_location"] - != os.path.expanduser("~") + os.sep + ".helix" - ): - HelixLanguage.generate_folder_structure( - config["core_location"] - ) - else: - HelixLanguage.generate_folder_structure() - - if config["add_to_path"]: - # permanently add to path - print("Not yet implemented.") - - if config["add_to_desktop"]: - # add to desktop - print("Not yet implemented.") - - @staticmethod - def remove_blank_lines( - file: str, hash: Hashing | None - ) -> None: - with open(file, "r") as read_file, open( - file + ".tmp", "w" - ) as write_file: - for line in read_file: - if line.strip(): - write_file.write(line) - - shutil.move(file + ".tmp", file) - if hash: - hash.create_hash_only() - - -class Timer: - """ - A utility class for timing code execution in the Helix language environment. - - Methods - ------- - __init__(self) - Initializes the Timer object. - start(self, name: str) -> None - Starts a timer with the given name. - end(self, name: str) -> None - Ends the timer with the given name and logs the elapsed time. - get_time(self, name: str) -> float - Retrieves the elapsed time for the timer with the given name. - decorator(self, func: Callable) -> Callable - Decorator method for timing functions. - """ - def __init__(self): - self.__times: dict[ - str, tuple[float, float, int] - ] = {} # name: (start, end, level) - self.__active_timers = ( - [] - ) # Stack to keep track of active timers - - def start(self, name: str) -> None: - self.__times[name] = ( - time(), - 0, - len(self.__active_timers), - ) - self.__active_timers.append(name) - - def end(self, name: str) -> None: - self.__times[name] = ( - self.__times[name][0], - time(), - self.__times[name][2], - ) - self.__active_timers.remove(name) - - def get_time(self, name: str) -> float: # in ms - start, end, _ = self.__times[name] - return (end - start) / 1_000_000 - - def __str__(self) -> str: - result = [] - for name in self.__times: - indent = "| " * self.__times[name][2] - result.append( - f'{indent}"{name}" took {self.get_time(name):.2f}ms' - ) - return "\n".join(result) - - def __repr__(self) -> str: - return self.__str__() - - def decorator(self, func: Callable) -> Callable: - @functools.wraps(func) - def wrapper(*args, **kwargs) -> Any: - self.start(func.__name__) - result = func(*args, **kwargs) - self.end(func.__name__) - return result - - return wrapper - - -class DisabledKeyboardInterrupt: +class DisabledKeyboardInterrupt(framework.DisabledKeyboardInterrupt): """ Context manager for temporarily disabling KeyboardInterrupt exceptions. @@ -764,8 +90,7 @@ def __exit__( if self.signal_received: self.old_handler(*self.signal_received) # type: ignore - -class Helix(framework.HelixLanguage): +class Helix(framework.Helix): """ Main class for the Helix programming language interpreter and compiler. @@ -794,14 +119,12 @@ class Helix(framework.HelixLanguage): Generates line numbers for the transpiled code. generate_source_code(self, scope_parsed: Scope, transpiled_lines: list[Processed_Line], format_source: bool = False, is_main: bool = True, no_inject: bool = False) -> str Generates source code from parsed scope and transpiled lines. - inject_core(self, code: Optional[str] = None, is_main: bool = True) -> str - Injects core Helix functionality into the transpiled code. REPL() -> None Launches the Helix Read-Eval-Print Loop. __hook_import__(cls, file: str, *args: str, config_file: Optional[str] = None, **kwargs: Any) -> ModuleType Hook method for importing modules in Helix. """ - config: Namespace + config: Any @classmethod def interpreter( @@ -835,8 +158,8 @@ def interpreter( ) if not globals_ and not locals_: - source_code = inst.inject_core( - source_code, is_main=False + source_code = inject_core( + inst, source_code, is_main=False, __version__=__version__, __file__=__file__ ) # print(source_code) @@ -870,9 +193,9 @@ def __init__( self.timer.start("parse_args") if args and not import_: - _args = ArgParser(args).args + _args = ArgParser(args, __version__).args elif not args and not import_: - _args = ArgParser().args + _args = ArgParser(version=__version__).args else: _args = Namespace() _args.file = args[0] @@ -912,9 +235,7 @@ def __init__( self.__file__, self.__out_file__ ) - self.__class__.config = load_config( - self.__config_file__ - ) # TODO: INTERGRATE CONFIG FILE + self.__class__.config = CONFIG self.timer.end("init") @@ -941,25 +262,6 @@ def run(self) -> None: ) self.timer.start("run") - #_locals = {} - #_globals = {} - #try: - # exec( - # compile( - # open(self.__out_file__).read(), - # self.__out_file__, - # "exec", - # ), - # _globals, - # _locals, - # ) - #except Exception as e: - # panic( - # "THIS NEEDS TO BE CHANGED TO A CUSTOM EXCEPTION", - # file=self.__out_file__, - # line_no=1, - # no_lines=True, - # ) try: subprocess.Popen( [sys.executable, self.__out_file__] @@ -1094,8 +396,8 @@ def generate_line_numbers( _line_numbers: list[str] = str("\n".join( [ "-1" if line != "\x92" else "\x92" - for line in self.inject_core( - None, is_main=not self.import_ + for line in inject_core( + self, None, is_main=not self.import_, __version__=__version__, __file__=__file__ ).splitlines() if line.strip() ] @@ -1167,269 +469,13 @@ def generate_source_code( self.timer.end("generate_source_code") return ( - self.inject_core( - transpiled_code, is_main=is_main + inject_core( + self, transpiled_code, is_main=is_main, __version__=__version__, __file__=__file__ ) if not no_inject else transpiled_code ) - def inject_core( - self, - code: Optional[str] = None, - is_main: bool = True, - ) -> str: - def get_all_importable_modules( - path: str, - ) -> list[str]: - import inspect - import importlib.util - - # Load the module specified by the path - module_spec = ( - importlib.util.spec_from_file_location( - "module.name", path - ) - ) - if module_spec and module_spec.loader: - module = importlib.util.module_from_spec( - module_spec - ) - module_spec.loader.exec_module(module) - - # Inspect the module and list all classes, functions, and variables - importable_items = [] - for name, obj in inspect.getmembers(module): - if ( - inspect.isfunction(obj) - or inspect.isclass(obj) - or (not name.startswith("_")) - ): - if not name.startswith("_H_"): - importable_items.append(name) - return importable_items - else: - return ( - [] - ) # or raise an exception if the module can't be loaded - back_slash = "\\" - inject_code = f"""# trunk-ignore-all(black) -# trunk-ignore-all(isort) -# -------------------------------------------------------------------------------- -# GENERATED FILE -# -------------------------------------------------------------------------------- -# Filename: {os.path.basename(self.__file__)} -# Generation Date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} -# Generator: Helix Transpiler -# -------------------------------------------------------------------------------- -# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications -# made directly to this file may be OVERWRITTEN during future compilations. To -# introduce changes, please modify the source files and recompile. -# -------------------------------------------------------------------------------- -# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) -# SPDX-License-Identifier: CC-BY-4.0 -# License Details: https://creativecommons.org/licenses/by/4.0/ -# -# By using this file, you agree to the Terms and Conditions of the License. -# -------------------------------------------------------------------------------- -# Helix Version: {__version__} -# Repository: https://github.com/kneorain/helix -# Documentation: https://kneorain.github.io/helix/ -# For further assistance, contact the development team or refer to project documentation. -# -------------------------------------------------------------------------------- -from __future__ import annotations # type: ignore - -from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore -from beartype import beartype, BeartypeConf # type: ignore -###### from include.plum.plum import dispatch as overload_with_type_check - -import os # type: ignore -import sys # type: ignore -import types # type: ignore - -sys.path.append(os.path.dirname(os.path.realpath(\"{__file__.replace(back_slash, os.sep+os.sep)}\")) + os.sep + ".helix") # type: ignore -sys.path.append(os.path.dirname(os.path.realpath(\"{__file__.replace(back_slash, os.sep+os.sep)}\"))) # type: ignore -sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore -# trunk-ignore(ruff/F401) -from include.core import {', '.join(get_all_importable_modules(os.path.join(".helix", "include", "core.py")))} # type: ignore -# trunk-ignore(ruff/F401) -# trunk-ignore(ruff/F811) -from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore - -import threading # type: ignore -import functools # type: ignore - -__lock = threading.Lock() -__file__ = "{os.path.realpath(self.__file__).replace(back_slash, os.sep+os.sep)}" -# trunk-ignore(ruff/F821) -def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): - import traceback - import linecache - import os - - from include.core import _H_tokenize_line__ - from beartype.roar import BeartypeException - - print() - - thread_name = None - if thread_error and exception_type is not None: - thread_name = exception_type.thread.name # type: ignore - exception = exception_type.exc_value # type: ignore - tb = exception_type.exc_traceback # type: ignore - exception_type = exception_type.exc_type # type: ignore - - stack = traceback.extract_tb(tb) - stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) - exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception - current_exception = exception - relevant_frames = [] - - early_replacements = dict((v, k) for k, v in {base.EARLY_REPLACEMENTS}.items()) - - # First loop: filter out irrelevant frames - index = 0 - for frame in stack: - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - - if line_no == -1: - continue - - if ";#\\"\\"\\"REMOVE-STACK\\"\\"\\"#" in linecache.getline(filename, line_no).strip(): - continue - - if ( - linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore - and - linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore - ): - continue - - relevant_frames.append((index, frame)) - index += 1 - - if len(relevant_frames) > 1: - __lock.acquire(blocking=True, timeout=1.2) - for frame_info in relevant_frames: - index, frame = frame_info - filename = frame.filename - line_no = frame.lineno - - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] # type: ignore - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - thread_name=thread_name, - lang=file_ext - ) - current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore - else: - __lock.release() - exit(1) - else: - __lock.acquire(blocking=True, timeout=0.1) - index, frame = relevant_frames[0] - filename = frame.filename - line_no = frame.lineno - - if "_hlx" in filename: - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - - try: - file_ext = os.path.basename(filename).split('.')[1] - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - thread_name=thread_name, - lang=file_ext - ) - - __lock.release() - exit(1) - -sys.excepthook = exception_handler # type: ignore -threading.excepthook = functools.partial(exception_handler, thread_error=True) -sys.argv = ["{os.path.realpath(__file__).replace(back_slash, os.sep+os.sep)}", "{os.path.realpath(self.__file__).replace(back_slash, os.sep+os.sep)}"] + list(sys.argv)[2:] -del os, threading, functools -overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore - -\x92 - -if __name__ == "__main__": - try: - main() # type: ignore - except (KeyError, TypeError): - main(sys.argv) -""" - if not is_main: - return inject_code.split("\x92")[0] + ( - code if code else "\x92" - ) - - if code is not None: - return inject_code.replace("\x92", code) - else: - return inject_code - @staticmethod def REPL() -> None: # this is a repl environment for helix @@ -1517,7 +563,7 @@ def __hook_import__( **scope.classes, **scope.variables, }.items(): - if value.get("private"): + if "private" in value: # CHANGED from: if value.get("private", False): remove.append(attr) # Clean up private attributes diff --git a/helix.toml b/helix.toml new file mode 100644 index 0000000..9ccb140 --- /dev/null +++ b/helix.toml @@ -0,0 +1,50 @@ + +# --- Transpiler Configurations --- +# Detailed settings for the code transpiler. +# target: Specifies the target language for transpilation. Options: 'py', 'c++', etc. +# warnings: Toggles the display of warnings during transpilation. +# verbose: Enables verbose output for detailed logs. +# threads: Defines the number of threads to use for transpilation. +# optimization: Sets the level of optimization. Ranges from 0 (none) to 3 (maximum). +# regex_module: Chooses the regex module to be used. Options: 're', 're2'. +[transpiler] + target = "py" + warnings = true + verbose = false + + + + + +# --- Compiler Configurations --- +# Configurations for various compilers used in the project. +# py: Python layer options: 'py' (default), 'bin', 'vile'. +# 'py' is typical for development and testing. +# 'bin' compiles to a binary executable for production. +# 'vile' generates an ABI for use with other languages and as a Helix library. +# c, cpp, rust: Specify the compilers for C, C++, and Rust respectively. +# Defaults: 'gcc' for C, 'g++' for C++, 'rustc' for Rust. +# flags_py, flags_cpp, flags_c, flags_rust: Custom flags for compiling Python, C++, C, and Rust. +[compiler] + py = "z:\\devolopment\\helix\\helix-lang\\.venv\\Scripts\\python.exe" + c = "gcc" + cpp = "g++" + rust = "rustc" + + + + + + +# --- Linker Configurations --- +# Settings for linking binaries and libraries. +# c_cpp_include_dirs: Include directories for C/C++ source files. +# rs_include_dirs: Include directories for Rust source files. +# py_include_dirs: Include directories for Python source files. +# lib_dirs: Directories where libraries are located. +[linker] + c_cpp_include_dirs = [] + rs_include_dirs = [] + py_include_dirs = [] + lib_dirs = [] + diff --git a/init b/init deleted file mode 100644 index 608435f..0000000 --- a/init +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -# Function to check if a command exists -command_exists () { - type "$1" &> /dev/null ; -} - -# make sure python version is 3.12 or higher - -# Python execution logic -if command_exists python ; then - PYTHON_CMD=python -elif command_exists python3 ; then - PYTHON_CMD=python3 -else - echo "Python is not installed or not found in PATH" - exit 1 -fi - -echo "Using Python command: $PYTHON_CMD" - -version = $("$PYTHON_CMD --version") - -if [[ "$version" < "3.12" ]]; then - echo "Python version is too low. Please install Python 3.12 or higher." - exit 1 -fi - -# Creating a virtual environment -$PYTHON_CMD -m venv .venv - -# Activating the virtual environment and installing requirements -# Different commands for Windows and Unix-like systems -if [[ "$OSTYPE" == "msys"* ]] || [[ "$OSTYPE" == "win32"* ]]; then - .venv\Scripts\activate -else - source .venv/bin/activate -fi - -pip install -r requirements.txt - -echo "Setup completed successfully." \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index ee5f758..2f069e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,6 @@ black beartype multimethod mypy -types-toml \ No newline at end of file +types-toml +serde +tomli_w \ No newline at end of file diff --git a/src/classes/ArgParser.py b/src/classes/ArgParser.py new file mode 100644 index 0000000..d99c2ce --- /dev/null +++ b/src/classes/ArgParser.py @@ -0,0 +1,182 @@ +import src.core.base as base +from src.core.imports import ( + exit, + Iterable, + Namespace, ArgumentParser, Optional, + color_print as print, + framework, +) + +class ArgParser(framework.ArgParser): + """ + Parses command-line arguments for the Helix language application. + + Attributes + ---------- + __args__ : Incomplete + Parsed arguments from the command line. + + Methods + ------- + help_screen(self) -> None + Displays the help screen with usage instructions. + version_screen(self) -> None + Displays the version information of the Helix language. + __init__(self, argv: Optional[Iterable[str]] = None) + Initializes the argument parser with command-line arguments. + args(self) -> Namespace + Returns the namespace of parsed command-line arguments. + """ + def help_screen(self): + print(base.clean_docstring(""" + usage: helix [-h] [-v] [-o COMPILE] [-d] [-l LOG] [-c CONFIG] [-s] file ... + + Welcome to the Helix CLI, the gateway to harnessing the power and simplicity of Helix, + a programming language designed for developers who cherish Python's ease but crave more + robust features. This CLI tool empowers you to write, manage, and transpile Helix code + with a range of commands and options tailored for a seamless development experience. + + positional arguments: + file the name of the file to be executed + doc the name of the documentation page to be displayed + other other arguments to be passed to the file as argv + + options: + -h, --help show this help message and exit + -v, --version show the version number and exit + -o COMPILE, --compile COMPILE_FILE compile the file to a specific output file + -d, --debug enable debug mode + -l LOG, --log LOG_FILE the name of the log file + -c CONFIG, --config CONFIG_FILE specify or create a config file + -s, --silent enable silent mode + -w, --watch watch the file for changes and recompile + -i, --install PACKAGE_NAME install new packages + -u, --uninstall PACKAGE_NAME uninstall packages + -doc DOC the name of the documentation page to be displayed + """), + word_wrap=False, + end="", + ) + exit() + + def version_screen(self): + print("🧬 " + self.__version__) + exit() + + def __init__( + self, argv: Optional[Iterable[str]] = None, version: str = "NOT SET" + ): + self.__version__ = version + parser = ArgumentParser( + description="Welcome to the Helix CLI, the gateway to harnessing the power and simplicity of Helix," + "a programming language designed for developers who cherish Python's ease but crave more" + "robust features. This CLI tool empowers you to write, manage, and transpile Helix code" + "with a range of commands and options tailored for a seamless development experience." + ) + + # Positional arguments + parser.add_argument( + "file", + nargs="?", + help="the name of the file to be executed", + ) + parser.add_argument( + "other", + nargs="*", + help="other arguments to be passed to the file as argv", + ) + + # Optional arguments + parser.add_argument( + "-v", + "--version", + action="store_true", + help="show the version number and exit", + ) + parser.add_argument( + "-d", + "--debug", + action="store_true", + help="enable debug mode", + ) + parser.add_argument( + "-w", + "--watch", + action="store_true", + help="watch the file for changes and recompile", + ) + parser.add_argument( + "-s", + "--silent", + action="store_true", + help="enable silent mode", + ) + parser.add_argument( + "-r", + "--reset_cache", + action="store_true", + help="recompile the file and store in cache", + ) + parser.add_argument( + "-o", + "--compile", + dest="compile_file", + help="compile the file to a specific output file", + ) + parser.add_argument( + "-l", + "--log", + dest="log_file", + help="the name of the log file", + ) + parser.add_argument( + "-c", + "--config", + dest="config_file", + help="specify or create a config file", + ) + parser.add_argument( + "-i", + "--install", + dest="install_package", + help="install new packages", + ) + parser.add_argument( + "-u", + "--uninstall", + dest="uninstall_package", + help="uninstall packages", + ) + parser.add_argument( + "-doc", + dest="store_true", + help="the name of the documentation page to be displayed", + ) + + self.__args__ = parser.parse_args(argv) # type: ignore + + if self.__args__.reset_cache: + base.USE_CACHE = False + + # Handling version flag + if self.__args__.version: + self.version_screen() + + # Handling doc flag + try: + if self.__args__.doc: + from docs.doc import doc + + doc(self.__args__.doc) + exit() + except AttributeError: + pass + + if not self.__args__.file and not any( + vars(self.__args__).values() + ): + self.help_screen() + + @property + def args(self) -> Namespace: + return self.__args__ diff --git a/src/classes/Hashing.py b/src/classes/Hashing.py new file mode 100644 index 0000000..29b66a7 --- /dev/null +++ b/src/classes/Hashing.py @@ -0,0 +1,172 @@ +from __future__ import annotations +import src.core.base as base +from src.core.imports import ( + os, + sys, + hashlib, + Optional, + TextIOWrapper, + framework, +) + + +class Hashing(framework.Hashing): + """ + Provides functionality for hashing and comparing hashes of code files. + + Attributes + ---------- + file_path : str + The path to the file to be hashed. + output_path : str + The path where the hash output should be stored. + + Methods + ------- + __init__(self, file_path: str, output_path: str) + Initializes the Hashing object with file paths. + compute_hash(code: str) -> bytes + Computes and returns the hash of the given code. + create_hash_only(self) + Creates a hash of the file at file_path and stores it at output_path. + get_mount(path) + Retrieves the mount point for the given path. + create_file(self, code: str) + Creates a file with the given code and hashes it. + is_code_altered(self) -> bool + Checks if the code has been altered compared to its stored hash. + get_hash(self) -> bytes | None + Retrieves the hash of the file if it exists. + """ + def __init__(self, file_path: str, output_path: str): + file: TextIOWrapper = open(file_path, "r") + self.__hash = Hashing.compute_hash(file.read()) + + file.close() + del file + + self.__output_path = output_path + + def __str__(self) -> str: + try: + return f"Hashing(hash={self.__hash.decode()}, output_path={self.__output_path})" + except UnicodeDecodeError: + return f"Hashing(hash={repr(self.__hash)}, output_path={self.__output_path})" + + def __repr__(self) -> str: + return self.__str__() + + def __int__(self) -> int: + return int.from_bytes(self.__hash, "big") + + @staticmethod + def compute_hash(code: str) -> bytes: + return hashlib.md5(code.encode("utf-8")).digest() + + def create_hash_only(self) -> None: + open(self.__output_path + ":hash", "wb").write( + self.__hash + ) + + @staticmethod + def get_mount(path): + path = os.path.realpath(os.path.abspath(path)) + while path != os.path.sep: + if os.path.ismount(path): + return path + path = os.path.abspath( + os.path.join(path, os.pardir) + ) + + return path + + def __windows_io( + self, writeable: str = "" + ) -> Optional[bytes]: + if writeable: + with open( + self.__output_path, "wb" + ) as file, open( + self.__output_path + ":hash", "wb" + ) as ads: + file.write(writeable.encode("utf-8")) + ads.write(self.__hash) + return open( + self.__output_path + ":hash", "rb" + ).read() + + def __linux_io( + self, writeable: str = "" + ) -> Optional[bytes]: + if sys.platform in ["linux", "linux2", "darwin"]: + import xattr # type: ignore + + if writeable: + with open(self.__output_path, "wb") as file: + file.write(writeable.encode("utf-8")) + + attr = xattr.xattr(self.__output_path) + attr.set("user.hash", self.__hash) + + attr = xattr.xattr(self.__output_path) + return attr.get("user.hash") + return None + + def __io(self, writeable: str = "") -> Optional[bytes]: + if sys.platform == "win32": + return self.__windows_io(writeable) + + elif sys.platform in ["linux", "linux2", "darwin"]: + return self.__linux_io(writeable) + + else: + raise ImportError( + "xattr library is required on Linux and macOS for setting extended attributes." + ) + + def create_file(self, code: str) -> None: + try: + self.__io(code) + + except ImportError: + raise ImportError( + "xattr library is required on Linux and macOS for setting extended attributes." + ) + + except PermissionError: + raise PermissionError( + "You do not have permission to write to the file." + ) + + except FileNotFoundError: + raise FileNotFoundError( + "The file does not exist." + ) + + def is_code_altered(self) -> bool: + if not base.USE_CACHE: + return True + + if not os.path.exists(self.__output_path) or ( + os.path.exists(self.__output_path + ":hash") + if sys.platform in ["linux", "linux2", "darwin"] + else False + ): + return ( + True # Hash file doesn't exist or is empty + ) + + existing_hash = self.get_hash() + if self.__hash != existing_hash: + return True + + return False + + def get_hash(self) -> bytes | None: + try: + return self.__io() + except FileNotFoundError: + open(self.__output_path + ":hash", "wb").write( + self.__hash + ) + return None \ No newline at end of file diff --git a/src/classes/HelixLanguage.py b/src/classes/HelixLanguage.py new file mode 100644 index 0000000..971ee0e --- /dev/null +++ b/src/classes/HelixLanguage.py @@ -0,0 +1,145 @@ +from __future__ import annotations +from typing import Optional +from src.core.imports import ( + os, + Hashing, + color_print as print, + framework, + ctypes, +) + +# --- c imports --- # +c_remove_blank_lines = ctypes.CDLL(os.path.join("src", "lib", "remove_blank_lines.dll")).remove_blank_lines +c_remove_blank_lines.argtypes = [ctypes.c_char_p] +c_remove_blank_lines.restype = ctypes.c_int + +class HelixLanguage(framework.HelixLanguage): + """ + Represents the core functionality for managing and executing Helix language operations. + + This class encapsulates methods for various tasks like folder and file management, + setting up the Helix environment, and handling installations and configurations. + + Methods + ------- + __init__(self, *args: str, **kwargs: str) + Initialize the HelixLanguage instance with given arguments. + make_folder(directory: str) -> None + Creates a folder at the specified directory path. + make_file(file: str) -> None + Creates a file at the specified file path. + generate_folder_structure(directory: str = ...) + Generates the necessary folder structure for a given directory. + install_helix(config: dict) -> None + Installs and configures the Helix environment based on the provided configuration. + + Examples + -------- + >>> hl = HelixLanguage() + >>> hl.make_folder('path/to/directory') + >>> hl.install_helix({'option': 'value'}) + + Notes + ----- + The class is designed to be flexible and handle various aspects of the Helix language + setup and management. More detailed methods and attributes can be added as per the + development needs of the Helix language. + """ + def __init__(self, *args: str, **kwargs: str): + raise NotImplementedError( + "Cannot instantiate HelixLanguage." + ) + + @staticmethod + def make_folder(directory: str) -> None: + if not os.path.isdir(directory): + os.mkdir(directory) + + @staticmethod + def make_file(file: str) -> None: + if not os.path.exists(file): + with open(file, "w", encoding="utf-8") as _: + return + + @staticmethod + def generate_folder_structure( + directory: str = os.path.expanduser("~") + + os.sep + + ".helix", + ): + if ( + os.path.isdir(directory) + and os.listdir(directory) != [] + ): + return + + HelixLanguage.make_folder(directory) + HelixLanguage.make_folder( + os.path.join(directory, "intl_libs") + ) + HelixLanguage.make_folder( + os.path.join(directory, "intl_libs", "helix") + ) + HelixLanguage.make_folder( + os.path.join(directory, "intl_libs", "c++") + ) + HelixLanguage.make_folder( + os.path.join(directory, "intl_libs", "c") + ) + HelixLanguage.make_folder( + os.path.join(directory, "intl_libs", "python") + ) + HelixLanguage.make_folder( + os.path.join(directory, "intl_libs", "rust") + ) + + HelixLanguage.make_folder( + os.path.join(directory, "logs") + ) + HelixLanguage.make_folder( + os.path.join(directory, "temp") + ) + HelixLanguage.make_folder( + os.path.join(directory, "cache") + ) + HelixLanguage.make_folder( + os.path.join(directory, "cache", "build_cache") + ) + HelixLanguage.make_folder( + os.path.join(directory, "include") + ) + + HelixLanguage.make_file( + os.path.join(directory, "config.toml") + ) + HelixLanguage.make_file( + os.path.join(directory, "include", "core.py") + ) + + @staticmethod + def install_helix(config: dict) -> None: + if ( + config["core_location"] + != os.path.expanduser("~") + os.sep + ".helix" + ): + HelixLanguage.generate_folder_structure( + config["core_location"] + ) + else: + HelixLanguage.generate_folder_structure() + + if config["add_to_path"]: + # permanently add to path + print("Not yet implemented.") + + if config["add_to_desktop"]: + # add to desktop + print("Not yet implemented.") + + @staticmethod + def remove_blank_lines( + file: str, hash: Optional[Hashing] # type: ignore + ) -> None: + c_remove_blank_lines(ctypes.c_char_p(file.encode("utf-8"))) + if hash: + hash.create_hash_only() diff --git a/src/classes/Timer.py b/src/classes/Timer.py new file mode 100644 index 0000000..3aadb65 --- /dev/null +++ b/src/classes/Timer.py @@ -0,0 +1,72 @@ +from src.core.imports import ( + functools, + Any, Callable, + perf_counter_ns as time, + framework, +) + +class Timer(framework.Timer): + """ + A utility class for timing code execution in the Helix language environment. + + Methods + ------- + __init__(self) + Initializes the Timer object. + start(self, name: str) -> None + Starts a timer with the given name. + end(self, name: str) -> None + Ends the timer with the given name and logs the elapsed time. + get_time(self, name: str) -> float + Retrieves the elapsed time for the timer with the given name. + decorator(self, func: Callable) -> Callable + Decorator method for timing functions. + """ + def __init__(self) -> None: + self.__times: dict[ + str, tuple[float, float, int] + ] = {} # name: (start, end, level) + self.__active_timers: list[str] = [] + + def start(self, name: str) -> None: + self.__times[name] = ( + time(), + 0, + len(self.__active_timers), + ) + self.__active_timers.append(name) + + def end(self, name: str) -> None: + self.__times[name] = ( + self.__times[name][0], + time(), + self.__times[name][2], + ) + self.__active_timers.remove(name) + + def get_time(self, name: str) -> float: # in ms + start, end, _ = self.__times[name] + return (end - start) / 1_000_000 + + def __str__(self) -> str: + result = [] + for name in self.__times: + indent = "| " * self.__times[name][2] + result.append( + f'{indent}"{name}" took {self.get_time(name):.2f}ms' + ) + return "\n".join(result) + + def __repr__(self) -> str: + return self.__str__() + + def decorator(self, func: Callable) -> Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> Any: + self.start(func.__name__) + result = func(*args, **kwargs) + self.end(func.__name__) + return result + + return wrapper + diff --git a/src/classes/Token.py b/src/classes/Token.py index bc8aa4e..41de7fa 100644 --- a/src/classes/Token.py +++ b/src/classes/Token.py @@ -1,20 +1,27 @@ from __future__ import annotations +from typing import Optional from src.core.imports import ( json, subtype, Iterable, Iterator, ) +from beartype.door import is_bearable class Token: _: str = "Helix Token" # ---------------------------- Constructor ------------------------------- # - def __init__(self, original_line: str, processed_line: str, line_number: int, indent_level: int): - self.__original_line: str = original_line - self.__processed_line: list[str] | str = processed_line - self.__line_number: int = line_number - self.__indent_level: int = indent_level + def __init__(self, + original_line: Optional[str], + processed_line: Optional[str], + line_number: Optional[int], + indent_level: Optional[int] + ): + self.__original_line: str = original_line if original_line is not None else "" + self.__processed_line: list[str] | str = processed_line if processed_line is not None else "" + self.__line_number: int = line_number if line_number is not None else -1 + self.__indent_level: int = indent_level if indent_level is not None else 0 # ------------------------------- Getters -------------------------------- # @@ -117,9 +124,12 @@ def __init__(self, tokens: list[Token], indent_level: int, file: str): self.file = file def __str__(self): - return json.dumps({"line_indent_level": self.indent_level, "joined_line": ' '.join([_.token for _ in self.line])}) - #f"\"Token_List\": {{\"line_indent_level\": {self.indent_level}, \"joined_line\": \"{(' '.join([_.token for _ in self.line])).replace('\"', '\\\"').replace('\'', '\\\'')}\"}}" - + return (f"Token_List(" + f"line = {self.line}, " + f"indent_level = {self.indent_level}, " + f"file = {self.file}" + ")") + def __iter__(self) -> Iterator[Token]: return iter(self.line) @@ -158,17 +168,18 @@ def find_line_number(self, token: str) -> int: return -1 def __contains__(self, __key: object | Iterable[Token_List]) -> bool: - if isinstance(__key, subtype(Iterable[Token_List])): - return any([token in __key for token in self.line]) + if isinstance(__key, subtype(Iterable[Token_List])): # type: ignore + return any([token in __key for token in self.line]) # type: ignore if isinstance(__key, tuple): return any([token in __key for token in self.line]) return any([token == __key for token in self.line]) def contains_from(self, __key: object | Iterable[Token_List]) -> str: - if isinstance(__key, subtype(Iterable[Token_List])): - return [[token for token in self.line if token in __key]+[False]][0] - return [[token for token in self.line if token == __key]+[False]][0] + if isinstance(__key, subtype(Iterable[Token_List])): # type: ignore + return [[token for token in self.line if token in __key]+[False]][0] # type: ignore + return [[token for token in self.line if token == __key]+[False]][0] # type: ignore + # TODO: switch isinstance to is_bearable def full_line(self) -> str: return ' '.join([_.token for _ in self.line]) @@ -230,7 +241,7 @@ def split(self, __value: str) -> list['Token_List']: return output # add support for the splicing so [1:2] works or any other slicing - def splice(self, start: int = 0, end: int = None) -> 'Token_List': + def splice(self, start: int = 0, end: Optional[int] = None) -> 'Token_List': temp = self.copy() temp.line = temp.line[start:end] return temp diff --git a/src/classes/WorkerPool.py b/src/classes/WorkerPool.py index 2c2ef50..8d5c160 100644 --- a/src/classes/WorkerPool.py +++ b/src/classes/WorkerPool.py @@ -55,17 +55,17 @@ # ------------------------------- Functions ---------------------------------- # -def start_all_threads(x: Any = None) -> NoReturn: +def start_all_threads(x: Any = None) -> NoReturn: # type: ignore pass -__process_worker_function: Callable[..., T] = None +__process_worker_function: Callable[..., T] = None # type: ignore def set_process_worker_function(func: Callable[..., T]) -> None: global __process_worker_function __process_worker_function = func def run_process_worker_function(*args: T) -> T: - return __process_worker_function(*args) + return __process_worker_function(*args) # type: ignore def function_origin_is_outside(func): def check1(func): @@ -117,7 +117,7 @@ def __init__( self.__lock = Lock() self.__shutdown = False self.__pool = None - self.__results = () + self.__results = () # type: ignore self.__pool_type = (( "thread" if pool_type else "process" ) if isinstance(pool_type, bool) else ( @@ -131,7 +131,7 @@ def __structure_pool( self, by: Annotated[int, "The amount of workers to add or remove from the pool (< 0 to remove, > 0 to add)"] = 0, non_main_origin: Annotated[bool, "If the function is not from the main module or is a lambda"] = False, - non_main_origin_function: Annotated[Callable, "The function to add to a workers namespace"] = None + non_main_origin_function: Annotated[Callable, "The function to add to a workers namespace"] = None # type: ignore ) -> None: if by != 0: self.__workers += by @@ -186,8 +186,8 @@ def remove_worker( self.__structure_pool(-workers) # ------------------------------ Appenders ------------------------------- # - def add_to_futures(self, *parm: T) -> None: - self.__futures += [parm[0]] if len(parm) == 1 else parm + def add_to_futures(self, *parm: FuturesType) -> None: + self.__futures += [parm[0]] if len(parm) == 1 else parm # type: ignore def clear_futures(self) -> None: self.__futures.clear() @@ -202,7 +202,7 @@ def append_future( with self.__lock: if not isinstance(future, Future): raise TypeError("Future must be of type Future") - self.add_to_futures(future) + self.add_to_futures(future) # type: ignore def append_futures( self, @@ -221,7 +221,7 @@ def append( ) -> FutureType: if not function_origin_is_outside(func) or self.__pool_type == "thread": with self.__lock: - self.add_to_futures(self.__pool.submit(func, *args, **kwargs)) + self.add_to_futures(self.__pool.submit(func, *args, **kwargs)) # type: ignore return self.__futures[-1] else: raise NotImplementedError("Cannot run non-root scoped (includes: lambda, closures, or any function in a local scope) functions in a process pool (Use a thread pool instead)") @@ -239,7 +239,7 @@ def append_with_post( ) -> None: if not function_origin_is_outside(func) or self.__pool_type == "thread": with self.__lock: - self.add_to_futures(self.__pool.submit(func, *args, **kwargs)) + self.add_to_futures(self.__pool.submit(func, *args, **kwargs)) # type: ignore self.__futures[-1].add_done_callback(post_func) else: raise NotImplementedError("Cannot run non-root scoped (includes: lambda, closures, or any function in a local scope) functions in a process pool (Use a thread pool instead)") @@ -257,7 +257,7 @@ def map_append( if not function_origin_is_outside(func) or self.__pool_type == "thread": with self.__lock: [ - self.add_to_futures(self.__pool.submit(func, *args)) + self.add_to_futures(self.__pool.submit(func, *args)) # type: ignore for args in zip(*iterables) ] else: @@ -279,12 +279,12 @@ def map_append_with_post( if not function_origin_is_outside(func) or self.__pool_type == "thread": with self.__lock: [ - self.add_to_futures(self.__pool.submit(func, *args)) + self.add_to_futures(self.__pool.submit(func, *args)) # type: ignore for args in zip(*iterables) ] [ future.add_done_callback(post_func) - for future in self.iter_futures()[-len(iterables):] + for future in self.iter_futures()[-len(iterables):] # type: ignore ] else: raise NotImplementedError("Cannot run non-root scoped (includes: lambda, closures, or any function in a local scope) functions in a process pool (Use a thread pool instead)") @@ -358,7 +358,7 @@ def execute( return [ - future.result(timeout=self.__time_out) + future.result(timeout=self.__time_out) # type: ignore for future in futures ] @@ -372,7 +372,7 @@ def create_future( if function_origin_is_outside(func) and self.__pool_type != "thread": raise NotImplementedError("Cannot run non-root scoped (includes: lambda, closures, or any function in a local scope) functions in a process pool (Use a thread pool instead)") - return self.__pool.submit(func, *args, **kwargs) + return self.__pool.submit(func, *args, **kwargs) # type: ignore # ------------------------------- Shutdown ------------------------------- # def close( @@ -422,7 +422,7 @@ def futures( raise TypeError("All items in futures must be of type Future") self.clear_futures() [ - self.add_to_futures(future) + self.add_to_futures(future) # type: ignore for future in value ] diff --git a/src/config.py b/src/config.py index c35d131..442253d 100644 --- a/src/config.py +++ b/src/config.py @@ -1,47 +1,244 @@ -from src.core.imports import ( - os, - toml, - Namespace, - panic, -) - -CACHE: dict[str, Namespace] = {} -CONFIG_PATH = ".helix/config.toml" - - -def save_config(config: Namespace): - """Save the config to the config file - - Args: - config (Namespace): The config to save - """ - with open(CONFIG_PATH, "w") as file: - toml.dump(dict(config), file) - - -def load_config(path: str = CONFIG_PATH) -> Namespace: - if path in CACHE: - return CACHE[path] - - if path != CONFIG_PATH: - ( - panic( - FileNotFoundError( - f"Could not find config file at {path}" - ), - no_lines=True, - file=path, - ) - if not os.path.exists(path) - else None +import os +import sys +import toml +import dataclasses +from typing import Any, Optional + + +# TODO: ADD PANIC's here + +@dataclasses.dataclass +class HelixConfig: + @dataclasses.dataclass + class Core: + core_location: str = ".helix" + auto_update: bool = True + + @dataclasses.dataclass + class Transpiler: + target: str = "py" + warnings: bool = True + verbose: bool = False + threads: int = 50 + optimization: int = 3 + regex_module: str = "re" + + @dataclasses.dataclass + class Compiler: + py: str = sys.executable + c: str = "gcc" + cpp: str = "g++" + rust: str = "rustc" + flags_py: str = "-OO" + flags_cpp: str = "-std=c++17" + flags_c: str = "-std=c18" + flags_rust: str = "--edition=2021" + + @dataclasses.dataclass + class Linker: + c_cpp_include_dirs: list[str] = dataclasses.field(default_factory=list) + rs_include_dirs: list[str] = dataclasses.field(default_factory=list) + py_include_dirs: list[str] = dataclasses.field(default_factory=list) + lib_dirs: list[str] = dataclasses.field(default_factory=list) + link_static: bool = False + + @dataclasses.dataclass + class Formatter: + indent_size: int = 4 + formatter: str = "black" + always_format: bool = False + + @dataclasses.dataclass + class Environment: + env_vars: dict[str, str] = dataclasses.field(default_factory=dict) + + core: Core = dataclasses.field(default_factory=Core) + transpiler: Transpiler = dataclasses.field(default_factory=Transpiler) + compiler: Compiler = dataclasses.field(default_factory=Compiler) + linker: Linker = dataclasses.field(default_factory=Linker) + formatter: Formatter = dataclasses.field(default_factory=Formatter) + environment: Environment = dataclasses.field(default_factory=Environment) + +class ConfigFactory: + @staticmethod + def create_default_config() -> HelixConfig: + return HelixConfig() + + @staticmethod + def create_project_config() -> HelixConfig: + return HelixConfig( + core=None,# type: ignore + transpiler=HelixConfig.Transpiler( + threads=None, # type: ignore + optimization=None, # type: ignore + regex_module=None # type: ignore + ), + compiler=HelixConfig.Compiler( + flags_py=None, # type: ignore + flags_cpp=None, # type: ignore + flags_c=None, # type: ignore + flags_rust=None, # type: ignore + ), + linker=HelixConfig.Linker( + link_static=None, # type: ignore + ), + formatter=None, # type: ignore + environment=None # type: ignore ) - with open(path, "r") as file: - data = Namespace(**toml.load(file)) - CACHE[path] = data - return data +class CustomTomlEncoder: + def __init__(self) -> None: + self.comments: dict[str, str] = {} + self.indent: str = " " + + def add_comment(self, section: str, comment: str) -> None: + self.comments[section] = comment + + def encode(self, config: dict[str, Optional[dict[str, Any]]]) -> str: + output_lines: list[str] = [] + for section, values in config.items(): + # Skip sections with None values + if values is None: + continue + + # Add section comment + comment: str = self.comments.get(section, "") + if comment: + output_lines.append(comment) + + # Add section content + output_lines.append(f"[{section}]") + for key, value in values.items(): + value_str: str = toml.dumps({key: value}).strip() + output_lines.append(self.indent + value_str) + output_lines.append("") # Newline after each section + + return "\n".join(output_lines) + +encoder: CustomTomlEncoder = CustomTomlEncoder() +encoder.add_comment('core', """ +# --- Core Configurations --- +# core_location: Path to the core directory. It's where essential files are stored. +# auto_update: Enables automatic updates for the core components. +# Note: Disabling this might cause compatibility issues with future versions.""") + +encoder.add_comment('transpiler', """ +# --- Transpiler Configurations --- +# Detailed settings for the code transpiler. +# target: Specifies the target language for transpilation. Options: 'py', 'c++', etc. +# warnings: Toggles the display of warnings during transpilation. +# verbose: Enables verbose output for detailed logs. +# threads: Defines the number of threads to use for transpilation. +# optimization: Sets the level of optimization. Ranges from 0 (none) to 3 (maximum). +# regex_module: Chooses the regex module to be used. Options: 're', 're2'.""") + +encoder.add_comment('compiler', """ +# --- Compiler Configurations --- +# Configurations for various compilers used in the project. +# py: Python layer options: 'py' (default), 'bin', 'vile'. +# 'py' is typical for development and testing. +# 'bin' compiles to a binary executable for production. +# 'vile' generates an ABI for use with other languages and as a Helix library. +# c, cpp, rust: Specify the compilers for C, C++, and Rust respectively. +# Defaults: 'gcc' for C, 'g++' for C++, 'rustc' for Rust. +# flags_py, flags_cpp, flags_c, flags_rust: Custom flags for compiling Python, C++, C, and Rust.""") + +encoder.add_comment('linker', """ +# --- Linker Configurations --- +# Settings for linking binaries and libraries. +# c_cpp_include_dirs: Include directories for C/C++ source files. +# rs_include_dirs: Include directories for Rust source files. +# py_include_dirs: Include directories for Python source files. +# lib_dirs: Directories where libraries are located.""") + +encoder.add_comment('formatter', """ +# --- Formatter Configurations --- +# Configurations for code formatting. +# indent_size: The size of indentation in number of spaces. +# formatter: The tool used for formatting (e.g., 'black' for Python). +# always_format: When set to true, always formats the generated code. +# Note: Enabling this might break certain code structures, recommended for debugging only.""") + +#encoder.add_comment('environment', """# --- Environment Configurations --- +## Custom environment variables to be set during execution. +## 'env_vars' is a dictionary of environment variables to be set. +## Example of 'env_vars': +## env_vars = { +## "SOME_NUM": "1", +## "WOAH_PATH": "/point/to/somewhere" +## } +## You can access these variables in your code using `helix::env['var']`.""") +# TODO : make env vars work + +class ConfigLoader: + @staticmethod + def load_config_file(path: str) -> dict[str, Any]: + with open(path, 'r') as file: + return toml.load(file) + + @staticmethod + def save_config(path: str, config: dict[str, Any]): + with open(path, 'w') as file: + file.write(encoder.encode(config)) + + @staticmethod + def merge_dicts(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]: + for key, value in override.items(): + if isinstance(value, dict) and key in base: + base[key] = ConfigLoader.merge_dicts(base[key], value) + else: + base[key] = value + return base + + @staticmethod + def load_config(init_proj: bool = False) -> HelixConfig: + project_config_path = os.path.join(os.getcwd(), 'helix.toml') + default_config_path = os.path.expanduser(HelixConfig.Core().core_location) + '/helix.toml' + env_config_path = os.getenv('HELIX_CONFIG') + + project_config = {} + if os.path.exists(project_config_path): + project_config = ConfigLoader.load_config_file(project_config_path) + + env_config = {} + if env_config_path and os.path.exists(env_config_path): + env_config = ConfigLoader.load_config_file(env_config_path) + + default_config = {} + if os.path.exists(default_config_path): + default_config = ConfigLoader.load_config_file(default_config_path) + else: + default_config = dataclasses.asdict(ConfigFactory.create_default_config()) + os.makedirs(os.path.dirname(default_config_path), exist_ok=True) + ConfigLoader.save_config(default_config_path, default_config) + + if init_proj and not os.path.exists(project_config_path): + minimal_project_config = dataclasses.asdict(ConfigFactory.create_project_config()) + ConfigLoader.save_config(project_config_path, minimal_project_config) + + # Merge configurations: project -> env -> default + merged_config = ConfigLoader.merge_dicts(default_config, env_config) + merged_config = ConfigLoader.merge_dicts(merged_config, project_config) + + # Convert the merged dictionary back into a HelixConfig instance + return ConfigLoader.dict_to_helix_config(merged_config) + + @staticmethod + def create_project_config() -> None: + ConfigLoader.load_config(True) + + @staticmethod + def dict_to_helix_config(data: dict[str, Any]) -> HelixConfig: + # For simplicity, we will create default instances and then override with data + helix_config = HelixConfig() + for section_name, section_data in data.items(): + if hasattr(helix_config, section_name): + section_instance = getattr(helix_config, section_name) + for key, value in section_data.items(): + if hasattr(section_instance, key): + setattr(section_instance, key, value) -def set_config_path(path: str) -> None: - global CONFIG_PATH - CONFIG_PATH = path + return helix_config + +CONFIG = ConfigLoader.load_config() \ No newline at end of file diff --git a/src/core/base.py b/src/core/base.py index 69cd0cb..5dedc32 100644 --- a/src/core/base.py +++ b/src/core/base.py @@ -75,11 +75,41 @@ def _no_change(line: Token_List, *args) -> Processed_Line: line, ) +def clean_docstring(docstring: str) -> str: + """ + Cleans up the given docstring by removing unnecessary whitespace and newlines. + + Parameters + ---------- + docstring : str + The docstring to be cleaned. + + Returns + ------- + str + The cleaned docstring. + """ + if not docstring: + return "" + + indentation_level: int = 0 + for char in docstring.splitlines()[1]: + if not char.isspace(): + break + indentation_level += 1 + + return "\n".join( + [ + line[indentation_level:] + for line in docstring.splitlines() + ] + ) CACHE: dict[str, tuple[Token_List, ...]] = {} POOL: WorkerPool = WorkerPool(50) USE_POOL: bool = True +USE_CACHE: bool = True LINE_BREAK: str = "\x03" SUB_LINE_BREAK: str = "\x04" @@ -114,6 +144,8 @@ def _no_change(line: Token_List, *args) -> Processed_Line: r"\*\*\=", # **= r"\<\<\=", # <<= r"\>\>\=", # >>= + r"\?\?", # ?? + r"\|\:", # ?: r"\=\=", # == r"\!\=", # != r"\<\=", # <= @@ -151,8 +183,6 @@ def _no_change(line: Token_List, *args) -> Processed_Line: r"\|\|", # || r"\+\+", # ++ r"\_\_", # __ - r"\?\?", # ?? - r"\?\:", # ?: r"\?\=", # ?= ] @@ -481,6 +511,8 @@ def panic(self, *args, **kwargs) -> NoReturn: EARLY_REPLACEMENTS: map[str, str] = map( { # These are replaced as soon as the tokenization is done (before normalization and transpilation) "...": "None", + "??" : "if", + "|:" : "else", "true": "True", "false": "False", "null": "None", diff --git a/src/core/framework.py b/src/core/framework.py index 334638f..c7fe8cd 100644 --- a/src/core/framework.py +++ b/src/core/framework.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +import ctypes from src.core.imports import ( Any, Callable, @@ -49,7 +50,7 @@ def is_code_altered(self) -> bool: def get_hash(self) -> bytes | None: pass -class ThreadedProcess: +class ThreadedProcess(ABC): __processes_queue__: dict[int, threading.Thread] @abstractmethod def __new__(cls, func: Callable[..., None]): @@ -65,7 +66,7 @@ def __call__(self, *args, **kwargs) -> None: def processes(self) -> dict[int, threading.Thread]: pass -class ArgParser: +class ArgParser(ABC): @abstractmethod def help_screen(self) -> None: pass @@ -80,7 +81,7 @@ def __init__(self, argv: Optional[Iterable[str]] = None) -> None: def args(self) -> Namespace: pass -class HelixLanguage: +class HelixLanguage(ABC): @abstractmethod def __init__(self, *args: str, **kwargs: str) -> None: pass @@ -102,10 +103,10 @@ def install_helix(config: dict) -> None: pass @staticmethod @abstractmethod - def remove_blank_lines(file: str, hash: Hashing | None) -> None: + def remove_blank_lines(file: str, hash: Optional[Hashing]) -> None: pass -class Timer: +class Timer(ABC): @abstractmethod def __init__(self) -> None: pass @@ -122,7 +123,7 @@ def get_time(self, name: str) -> float: def decorator(self, func: Callable) -> Callable: pass -class DisabledKeyboardInterrupt: +class DisabledKeyboardInterrupt(ABC): @abstractmethod def __enter__(self) -> None: pass @@ -133,7 +134,7 @@ def handler(self, sig: int, frame: Any) -> None: def __exit__(self, type: Any, value: Any, traceback: Any) -> None: pass -class Helix: +class Helix(ABC): @classmethod @abstractmethod def interpreter(cls, code: str, globals_: dict, locals_: dict) -> str: @@ -166,9 +167,6 @@ def generate_line_numbers(self, transpiled: list[Processed_Line]) -> str: @abstractmethod def generate_source_code(self, scope_parsed: Scope, transpiled_lines: list[Processed_Line], format_source: bool = False, is_main: bool = True, no_inject: bool = False) -> str: pass - @abstractmethod - def inject_core(self, code: Optional[str] = None, is_main: bool = True) -> str: - pass @staticmethod @abstractmethod def REPL() -> None: @@ -177,3 +175,32 @@ def REPL() -> None: @abstractmethod def __hook_import__(cls, file: str, *args: str, config_file: Optional[str] = None, **kwargs: Any) -> ModuleType: pass + + +class Interop(ABC): + cache_dir: str + input_file: str + output_file: str + hash: Hashing + + @abstractmethod + def __init__(self, input_file: str, *attrs: str) -> None: + pass + @abstractmethod + def __getattr__(self, attr: str) -> Any: + pass + @abstractmethod + def __setattr__(self, attr: str, value: Any) -> None: + raise AttributeError("Cannot set attribute") + @abstractmethod + def __delattr__(self, attr: str) -> None: + pass + @abstractmethod + def __compile__(self) -> None: + pass + @abstractmethod + def get(self) -> tuple[ctypes.CDLL._FuncPtr, ...]: + pass + @abstractmethod + def __del__(self) -> None: + pass \ No newline at end of file diff --git a/src/core/imports.py b/src/core/imports.py index b31cb4e..ef01592 100644 --- a/src/core/imports.py +++ b/src/core/imports.py @@ -10,12 +10,15 @@ import sys import json import enum +import mmap import signal +import ctypes import shutil import hashlib import functools import threading import subprocess +import dataclasses from multiprocessing import cpu_count from inspect import CO_NESTED @@ -102,18 +105,18 @@ # src - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # +from src.config import CONFIG from src.cache_store import ( cache, file_cache ) from src.panic import panic from src.better_print import color_print -from src.config import load_config # variables - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # -INDENT_CHAR = load_config().Formatter["indent_char"] -re = __import__(load_config().Transpiler["regex_module"]) +INDENT_CHAR = CONFIG.Formatter.indent_size * " " +re = __import__(CONFIG.Transpiler.regex_module) # src.token - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # @@ -134,6 +137,11 @@ from src.classes.WorkerPool import WorkerPool import src.core.framework as framework +from src.classes.Hashing import Hashing +from src.classes.ArgParser import ArgParser +from src.classes.Timer import Timer +from src.classes.HelixLanguage import HelixLanguage + # src.functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # from src.functions._for import _for @@ -144,5 +152,6 @@ from src.functions._include import _include from src.functions._functions import _function from src.functions._unmarked import _unmarked +from src.functions.inject_core import inject_core # ------------------------------ End of Imports ------------------------------ # \ No newline at end of file diff --git a/src/functions/_functions.py b/src/functions/_functions.py index 92b6d5f..d11b97c 100644 --- a/src/functions/_functions.py +++ b/src/functions/_functions.py @@ -120,8 +120,9 @@ def parse( self.line = self.line[1:] else: self.handle_parameter() - + del token + self.__del__() raise SyntaxError( f": Expected a closing parenthesis after the parameters" ) @@ -221,6 +222,7 @@ def handle_parameter(self) -> None: self.variables[token] = {"type": "Any"} self.line = self.line[1:] else: + del token base.ERROR_CODES.TYPE_UNDECLARED.format( identifier=tuple(self.variables.keys())[ -1 @@ -236,7 +238,17 @@ def handle_parameter(self) -> None: ) del token - + + def __del__(self) -> None: + del self.line + del self.in_param + del self.variables + del self.generic + del self.in_generic + del self.generic_count + del self.root_scope + del self.ast_line + def handle_no_parameters( self, ) -> dict[ @@ -309,7 +321,7 @@ def __init__( self.name: str = "" self.output: str = "" - + def parse(self) -> Processed_Line: self._process_decorators() self._process_modifiers() @@ -414,7 +426,7 @@ def parse(self) -> Processed_Line: 2, ) self.add_to_output(decorators, 1) - + return self._return_output() def add_to_output( @@ -442,6 +454,9 @@ def _process_decorators(self) -> None: [__.token for __ in decorator] ) ) + del decorator + del _ + def _process_modifiers(self) -> None: self.modifiers = { @@ -450,10 +465,8 @@ def _process_modifiers(self) -> None: self.root_scope.get_keyword("PROTECTED"): False, self.root_scope.get_keyword("FINAL"): False, self.root_scope.get_keyword("UNSAFE"): False, - self.root_scope.get_keyword( - "STATIC" - ): False, # TODO: add virtual/kernel - } + self.root_scope.get_keyword("STATIC"): False, + } # TODO: add virtual/kernel index = 0 if self.ast_list.line[ @@ -474,10 +487,14 @@ def _process_modifiers(self) -> None: break else: break + + del line + self.ast_list.line = self.ast_list.line[ (index - 1) : ] + del index def _process_name(self) -> None: self.name = self.ast_list.line[1].token @@ -550,6 +567,8 @@ def _process_params_internal(self): f"{k}: {base.replace_primitive(v['type'], 0)}, " ) self.current_scope.variables[k] = v["type"] + + del k, v def __add_indent( self, output: str, offset: int = 0 @@ -671,6 +690,15 @@ def _process_modifiers_step_2(self) -> None: ) def _return_output(self) -> Processed_Line: + del self.decorators + del self.modifiers + del self.not_allowed_classes + del self.variables + del self.name + del self.current_scope + del self.parent_scope + del self.root_scope + return Processed_Line( self.output, self.ast_list, diff --git a/src/functions/_match.py b/src/functions/_match.py index 81b1464..abe4bfb 100644 --- a/src/functions/_match.py +++ b/src/functions/_match.py @@ -2,7 +2,7 @@ Processed_Line, Token, Token_List, - load_config, + CONFIG, INDENT_CHAR, re, panic, diff --git a/src/functions/_unmarked.py b/src/functions/_unmarked.py index de9c693..42ae685 100644 --- a/src/functions/_unmarked.py +++ b/src/functions/_unmarked.py @@ -31,7 +31,12 @@ def _process_assignment(self) -> Processed_Line: self._validate_multiple_assignments() variables = self._extract_variables_before_equal() self._parse_assignment_values(variables) - output = self._generate_assignment_output(variables) + + # output = self._generate_assignment_output(variables) + # TODO: Fix the above code + _output: list[str] = [f"{name} = {value}" for name, value in variables.items()] + output = "\n".join([f"{INDENT_CHAR * self.ast_list.indent_level}{i}" for i in _output]) + del variables, _output return Processed_Line(output, self.ast_list) def _process_non_assignment(self) -> Processed_Line: @@ -109,6 +114,7 @@ def _generate_assignment_output(self, variables: dict[str, str | Token_List]) -> if not self._is_variable_defined(name): continue output += f"{INDENT_CHAR * self.ast_list.indent_level}{name} = {value}\n" + return output def _is_variable_defined(self, name: str) -> bool: diff --git a/src/functions/inject_core.py b/src/functions/inject_core.py new file mode 100644 index 0000000..953e56d --- /dev/null +++ b/src/functions/inject_core.py @@ -0,0 +1,291 @@ +import src.core.base as base +from src.core.imports import ( + os, + datetime, + Optional, + Any +) + +def inject_core( + _cls_ref: Any, + code: Optional[str] = None, + is_main: bool = True, + __version__: str = "NOT SET", + __file__: str = "NOT SET", +) -> str: + def get_all_importable_modules( + path: str, + ) -> list[str]: + import inspect + import importlib.util + + # Load the module specified by the path + module_spec = ( + importlib.util.spec_from_file_location( + "module.name", path + ) + ) + if module_spec and module_spec.loader: + module = importlib.util.module_from_spec( + module_spec + ) + module_spec.loader.exec_module(module) + + # Inspect the module and list all classes, functions, and variables + importable_items = [] + for name, obj in inspect.getmembers(module): + if ( + inspect.isfunction(obj) + or inspect.isclass(obj) + or (not name.startswith("_")) + ): + if not name.startswith("_H_"): + importable_items.append(name) + return importable_items + else: + return ( + [] + ) # or raise an exception if the module can't be loaded + back_slash = "\\" + inject_code = f"""# trunk-ignore-all(black) +# trunk-ignore-all(isort) +# -------------------------------------------------------------------------------- +# GENERATED FILE +# -------------------------------------------------------------------------------- +# Filename: {os.path.basename(_cls_ref.__file__)} +# Generation Date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} +# Generator: Helix Transpiler +# -------------------------------------------------------------------------------- +# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications +# made directly to this file may be OVERWRITTEN during future compilations. To +# introduce changes, please modify the source files and recompile. +# -------------------------------------------------------------------------------- +# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) +# SPDX-License-Identifier: CC-BY-4.0 +# License Details: https://creativecommons.org/licenses/by/4.0/ +# +# By using this file, you agree to the Terms and Conditions of the License. +# -------------------------------------------------------------------------------- +# Helix Version: {__version__} +# Repository: https://github.com/kneorain/helix +# Documentation: https://kneorain.github.io/helix/ +# For further assistance, contact the development team or refer to project documentation. +# -------------------------------------------------------------------------------- +from __future__ import annotations # type: ignore + +from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore +from beartype import beartype, BeartypeConf # type: ignore +###### from include.plum.plum import dispatch as overload_with_type_check + +import os # type: ignore +import sys # type: ignore +import types # type: ignore + +sys.path.append(os.path.dirname(os.path.realpath(\"{__file__.replace(back_slash, os.sep+os.sep)}\")) + os.sep + ".helix") # type: ignore +sys.path.append(os.path.dirname(os.path.realpath(\"{__file__.replace(back_slash, os.sep+os.sep)}\"))) # type: ignore +sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore +# trunk-ignore(ruff/F401) +from include.core import {', '.join(get_all_importable_modules(os.path.join(".helix", "include", "core.py")))} # type: ignore +# trunk-ignore(ruff/F401) +# trunk-ignore(ruff/F811) +from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore + +import threading # type: ignore +import functools # type: ignore + +__exit__ = sys.exit +_lock = threading.Lock() +__file__ = "{os.path.realpath(_cls_ref.__file__).replace(back_slash, os.sep+os.sep)}" +# trunk-ignore(ruff/F821) +def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): + import traceback + import linecache + import os + + from include.core import _H_tokenize_line__ + from beartype.roar import BeartypeException + + print() + + thread_name = None + if thread_error and exception_type is not None: + thread_name = exception_type.thread.name # type: ignore + exception = exception_type.exc_value # type: ignore + tb = exception_type.exc_traceback # type: ignore + exception_type = exception_type.exc_type # type: ignore + + stack = traceback.extract_tb(tb) + stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) + exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception + current_exception = exception + relevant_frames = [] + + early_replacements = dict((v, k) for k, v in {base.EARLY_REPLACEMENTS}.items()) + + # First loop: filter out irrelevant frames + index = 0 + for frame in stack: + filename = frame.filename + line_no = frame.lineno + if "_hlx" in os.path.basename(filename): + filename = __file__ + try: + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + except IndexError: + print("TODO: approximate corresponding line number in .hlx file") + panic( + LookupError(f"Could not find the corresponding line number in the .hlx file for the line {{frame.lineno}} in the generated file {{frame.filename}}. Please report this issue."), + line_no=1, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + no_lines=True, + lang="py" + ) + except FileNotFoundError: + panic( + FileNotFoundError(f"The file {{frame.filename}} was not found. Please ensure that the file exists and is accessible." + "You should never see this error. If you do, something went really wrong."), + + line_no=1, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + no_lines=True, + lang="py" + ) + + if line_no == -1: + continue + + if line_no == -1: + continue + + if ";#\\"\\"\\"REMOVE-STACK\\"\\"\\"#" in linecache.getline(filename, line_no).strip(): + continue + + if ( + linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore + and + linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore + ): + continue + + relevant_frames.append((index, frame)) + index += 1 + + if len(relevant_frames) > 1: + _lock.acquire(blocking=True, timeout=1.2) + for frame_info in relevant_frames: + index, frame = frame_info + filename = frame.filename + line_no = frame.lineno + + if "_hlx" in os.path.basename(filename): + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + try: + file_ext = os.path.basename(filename).split('.')[1] # type: ignore + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + thread_name=thread_name, + lang=file_ext + ) + current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore + else: + _lock.release() + exit(1) + else: + _lock.acquire(blocking=True, timeout=0.1) + index, frame = relevant_frames[0] + filename = frame.filename + line_no = frame.lineno + + if "_hlx" in filename: + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + + try: + file_ext = os.path.basename(filename).split('.')[1] + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + lang=file_ext + ) + + _lock.release() + exit(1) + +sys.excepthook = exception_handler # type: ignore +threading.excepthook = functools.partial(exception_handler, thread_error=True) +sys.argv = ["{os.path.realpath(__file__).replace(back_slash, os.sep+os.sep)}", "{os.path.realpath(_cls_ref.__file__).replace(back_slash, os.sep+os.sep)}"] + list(sys.argv)[2:] +del os, threading, functools +overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore + +\x92 + +if __name__ == "__main__": + try: + main() # type: ignore + except (KeyError, TypeError): + main(sys.argv) +""" + if not is_main: + return inject_code.split("\x92")[0] + ( + code if code else "\x92" + ) + + if code is not None: + return inject_code.replace("\x92", code) + else: + return inject_code diff --git a/src/lib/remove_blank_lines.dll b/src/lib/remove_blank_lines.dll new file mode 100644 index 0000000000000000000000000000000000000000..9a24ff4206f0a347a41a4c0c9262141174b3dda5 GIT binary patch literal 66048 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�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 +#include +#include + +#define BUFFER_SIZE 1024 + +int remove_blank_lines(const char *file_path) { + FILE *file; + FILE *temp_file; + char line[BUFFER_SIZE]; + + errno_t err = fopen_s(&file, file_path, "r"); + if (err != 0 || file == NULL) { + return 1; + } + + err = tmpnam_s(line, sizeof(line)); + if (err != 0) { + fclose(file); + return 1; + } + + err = fopen_s(&temp_file, line, "w+"); + if (err != 0 || temp_file == NULL) { + fclose(file); + return 1; + } + + while (fgets(line, sizeof(line), file) != NULL) { + // Check if it's the last line and it's blank, skip writing it + if (feof(file) && line[0] == '\n') { + break; + } + if (line[0] != '\n') { + fputs(line, temp_file); + } + } + + fclose(file); + rewind(temp_file); + + err = fopen_s(&file, file_path, "w"); + if (err != 0 || file == NULL) { + fclose(temp_file); + return 1; + } + + while (fgets(line, sizeof(line), temp_file) != NULL) { + fputs(line, file); + } + + fclose(file); + fclose(temp_file); + + remove(line); + return 0; +} \ No newline at end of file diff --git a/src/token/remove_comments.py b/src/token/remove_comments.py index c65ae15..1e8edca 100644 --- a/src/token/remove_comments.py +++ b/src/token/remove_comments.py @@ -1,9 +1,9 @@ import ast from functools import cache -from src.config import load_config +from src.config import CONFIG -re = __import__(load_config().Transpiler["regex_module"]) +re = __import__(CONFIG.Transpiler.regex_module) from src.classes.Token import Token import src.core.base as base diff --git a/src/token/tokenize_file.py b/src/token/tokenize_file.py index 10d32d2..20fdabe 100644 --- a/src/token/tokenize_file.py +++ b/src/token/tokenize_file.py @@ -6,6 +6,7 @@ import src.core.base as base + class Tokenizer: _ = "Do not change; License: CC0 1.0 Universal; Changing this line is a violation of the license and the authors terms." @@ -22,27 +23,23 @@ def tokenize_file(path: str) -> tuple[Token_List, ...]: """ if path in base.CACHE: - return base.CACHE[path] - - lines: tuple[Token, ...] = tuple( - Token(line, "", index + 1, 0) - for index, line in enumerate(open(path, "r").readlines()) - ) + return base.CACHE[path] - if base.USE_POOL: - [base.POOL.append(remove_comment, line) for line in lines] - base.POOL.execute() + with open(path, "r") as file: + lines = tuple(Token(line, "", index + 1, 0) for index, line in enumerate(file)) + if base.USE_POOL and len(lines) > 1000: + # Using multiprocessing.Pool or similar + base.POOL.map(remove_comment, lines) # Batch operation _tokenize_line = functools.partial(tokenize_line, path=path) - - [base.POOL.append(_tokenize_line, line) for line in lines] - base.POOL.execute() + base.POOL.map(_tokenize_line, lines) # Batch operation else: tuple(map(remove_comment, lines)) - tuple(map(lambda _: tokenize_line(_, path), lines)) + tuple(map(lambda line: tokenize_line(line, path), lines)) - base.CACHE[path] = normalize_tokens(lines, path) - return base.CACHE[path] + normalized_tokens = normalize_tokens(lines, path) + base.CACHE[path] = normalized_tokens + return normalized_tokens @staticmethod def tokenize_line(line: str, path: str, indent: int = 0, line_no: int = 0) -> tuple[Token_List, ...]: diff --git a/src/token/tokenize_line.py b/src/token/tokenize_line.py index 5bc2ed4..03fdb96 100644 --- a/src/token/tokenize_line.py +++ b/src/token/tokenize_line.py @@ -1,16 +1,17 @@ import re # #### Keep from functools import cache +from typing import Optional from src.panic import panic import src.core.base as base from src.classes.Token import Token -COMPILED_RE: re.Pattern = None +COMPILED_RE: Optional[re.Pattern] = None def compiled_re(ignore_strings: bool = False) -> re.Pattern: global COMPILED_RE back_slash = "\\" - if not COMPILED_RE and not ignore_strings: + if COMPILED_RE is None or ignore_strings: COMPILED_RE = re.compile(rf""" ([fbur]*"[^"\\]*(?:\\.[^"\\]*)*") | # Double quotes strings, including f, b, r, u strings ([fbur]*'[^'\\]*(?:\\.[^'\\]*)*') | # Single quotes strings, including f, b, r, u strings diff --git a/syntax/cappy.hlx b/syntax/cappy.hlx new file mode 100644 index 0000000..abf6a40 --- /dev/null +++ b/syntax/cappy.hlx @@ -0,0 +1,21 @@ + import os; import time; import + math; fn main() { let A: float = 0.0; let B: + float = 0.0; while true { let z: list = [0.0]*1760; let + b: list = [" "]*1760; for (let j in range(0, 628, 7)){for(let i in + range(0, 628, 2)) { let c: float = math:: sin(i); let d:float=math::cos(j);let +e: float = math::sin(A); let f: float =math::sin(j);let g:float=math::cos(A); +let h: float = d + 2; let D: float=1/( c * h * e+ f * g + 5); +let l: float = math:: cos ( i ) ; let m : float += math::cos(B); let n : float = math :: sin(B); +let t: float = c * h * g-f*e;let x: int=int(40 + 30 * D * (l * h * m - t * n)); + let y: int = int(12 + 15 * D * ( l * h * n + t * m)); + let o: int = int(x + 80 * y); let N:int=int(8* ((f * e - c * + d * g) * m - c * d * e - f * g - l * d * n)); if 0 <= y < 22 + and 0 <= x < 80 and D > z[o] { z[o] = D; if N > 0 { let + charIndex: int = N; } else { let charIndex: int = 0; } + b[o] = ".,-~:;=!*#$@"[charIndex]; } } } os::system("cls"); + for (let k: int = 0; k < 1760; k++) { if k % + 80 == 0 { print('\n', end=''); } else + { print(b[k], end=''); }} A + += 0.04; B += 0.02;time + ::sleep(0.03);}} diff --git a/syntax/head_syntax.hdlx b/syntax/head_syntax.hhx similarity index 54% rename from syntax/head_syntax.hdlx rename to syntax/head_syntax.hhx index 9704a3b..4c9d584 100644 --- a/syntax/head_syntax.hdlx +++ b/syntax/head_syntax.hhx @@ -1,10 +1,17 @@ use "syntax.hlx"; -if sys::is_windows() { - define set_something(int value) -> void; -} else { - define set_something_else(int value) -> void; + +if (defined(__cplusplus)) { + #define void auto + #define None auto +} +else { + #define auto void + #define None void } +set_something(value: int) -> void; + +set_something_else(value: int) -> void; namespace python { fn set_something(value: int) -> None; diff --git a/syntax/test.hlx b/syntax/test.hlx index 675fd69..b64c57e 100644 --- a/syntax/test.hlx +++ b/syntax/test.hlx @@ -16,7 +16,7 @@ ~~ test_set(); -~* +~*~ import random import time import os @@ -73,7 +73,7 @@ def main(): os.system('cls' if os.name == 'nt' else 'clear') print(l, end='', flush=true) time.sleep(1 / 30) - *~ +~*~ class C_cout { fn new(self) { @@ -85,7 +85,17 @@ class C_cout { } } +fn add(a: int, b: int) -> int { + printf("adding: %d + %d", a, b); + return a + b; +} + fn main(argv: list) { + let _add: Infix = Infix(add); + + print(2 |_add| 3); + + exit(); let a: C_cout = C_cout(); let b: C_cout = a; a << "hello world"; @@ -128,6 +138,11 @@ private fn subtract(a: int, b: int) -> int { ~~ file.read(); ~~ } + let float_a: float = 0.1 + 0.2; + let float_b: double = 0.1 + 0.2; + print("float_a:", float_a, "\nfloat_b:", float_b); + + let some_map: map = { "a": 1, "b": 2, "c": 3 }; let some_list: list = [1, 2, 3]; let some_set: set = { 1, 2, 3 }; diff --git a/tests.py b/tests.py index 29b92d6..3c1be4d 100644 --- a/tests.py +++ b/tests.py @@ -1 +1,37 @@ -import helix \ No newline at end of file +import os +import time +import math + +A = 0 +B = 0 + +while True: + z = [0] * 1760 + b = [' '] * 1760 + for j in range(0, 628, 7): # j goes from 0 to 2 * pi, in steps of 0.07 + for i in range(0, 628, 2): # i goes from 0 to 2 * pi, in steps of 0.02 + c = math.sin(i) + d = math.cos(j) + e = math.sin(A) + f = math.sin(j) + g = math.cos(A) + h = d + 2 + D = 1 / (c * h * e + f * g + 5) + l = math.cos(i) + m = math.cos(B) + n = math.sin(B) + t = c * h * g - f * e + x = int(40 + 30 * D * (l * h * m - t * n)) + y = int(12 + 15 * D * (l * h * n + t * m)) + o = int(x + 80 * y) + N = int(8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n)) + if 0 <= y < 22 and 0 <= x < 80 and D > z[o]: + z[o] = D + b[o] = ".,-~:;=!*#$@"[N if N > 0 else 0] + + os.system('clear' if os.name == 'posix' else 'cls') + for k in range(1760): + print(b[k] if k % 80 else '\n', end='') + A += 0.04 + B += 0.02 + time.sleep(0.03) From 00b67c3a8eaf6ae12490bec1e9c7dfbe2c58de23 Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Sun, 17 Mar 2024 16:54:09 -0400 Subject: [PATCH 6/8] Add imports for subinterpreter and fix Timer end method --- .helix/cache/build_cache/cappy_hlx.py | 227 -------------- .helix/cache/build_cache/cappy_hlx.py.lines | 223 -------------- .helix/cache/build_cache/donut_hlx.py | 227 -------------- .helix/cache/build_cache/donut_hlx.py.lines | 223 -------------- .helix/cache/build_cache/source_py_test.py | 184 ----------- .../cache/build_cache/source_py_test.py.lines | 164 ---------- .helix/cache/build_cache/test_case1_hlx.py | 205 ------------- .../cache/build_cache/test_case1_hlx.py.lines | 205 ------------- .helix/cache/build_cache/test_hlx.hir | Bin 0 -> 15296 bytes .../{test_hlx.py.lines => test_hlx.hir.lines} | 0 .helix/cache/build_cache/test_hlx.py | 285 ------------------ README.md | 4 + a.py | 1 + compile.py | 4 +- helix.py | 31 +- src/classes/Scope.py | 21 +- src/classes/Timer.py | 2 +- src/classes/Transpiler.py | 81 ++--- src/core/imports.py | 1 + src/core/utils.py | 8 +- src/functions/_include.py | 5 +- src/functions/inject_core.py | 2 +- src/lib/src/subinterpreter.c | 75 +++++ src/lib/subinterpreter.pyi | 5 + tests.py | 37 --- 25 files changed, 179 insertions(+), 2041 deletions(-) delete mode 100644 .helix/cache/build_cache/cappy_hlx.py delete mode 100644 .helix/cache/build_cache/cappy_hlx.py.lines delete mode 100644 .helix/cache/build_cache/donut_hlx.py delete mode 100644 .helix/cache/build_cache/donut_hlx.py.lines delete mode 100644 .helix/cache/build_cache/source_py_test.py delete mode 100644 .helix/cache/build_cache/source_py_test.py.lines delete mode 100644 .helix/cache/build_cache/test_case1_hlx.py delete mode 100644 .helix/cache/build_cache/test_case1_hlx.py.lines create mode 100644 .helix/cache/build_cache/test_hlx.hir rename .helix/cache/build_cache/{test_hlx.py.lines => test_hlx.hir.lines} (100%) delete mode 100644 .helix/cache/build_cache/test_hlx.py create mode 100644 a.py create mode 100644 src/lib/src/subinterpreter.c create mode 100644 src/lib/subinterpreter.pyi diff --git a/.helix/cache/build_cache/cappy_hlx.py b/.helix/cache/build_cache/cappy_hlx.py deleted file mode 100644 index 1898312..0000000 --- a/.helix/cache/build_cache/cappy_hlx.py +++ /dev/null @@ -1,227 +0,0 @@ -# trunk-ignore-all(black) -# trunk-ignore-all(isort) -# -------------------------------------------------------------------------------- -# GENERATED FILE -# -------------------------------------------------------------------------------- -# Filename: cappy.hlx -# Generation Date: 2024-03-16 13:32:32 -# Generator: Helix Transpiler -# -------------------------------------------------------------------------------- -# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications -# made directly to this file may be OVERWRITTEN during future compilations. To -# introduce changes, please modify the source files and recompile. -# -------------------------------------------------------------------------------- -# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) -# SPDX-License-Identifier: CC-BY-4.0 -# License Details: https://creativecommons.org/licenses/by/4.0/ -# -# By using this file, you agree to the Terms and Conditions of the License. -# -------------------------------------------------------------------------------- -# Helix Version: 0.1.0-alpha.a -# Repository: https://github.com/kneorain/helix -# Documentation: https://kneorain.github.io/helix/ -# For further assistance, contact the development team or refer to project documentation. -# -------------------------------------------------------------------------------- -from __future__ import annotations # type: ignore -from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore -from beartype import beartype, BeartypeConf # type: ignore -###### from include.plum.plum import dispatch as overload_with_type_check -import os # type: ignore -import sys # type: ignore -import types # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py")) + os.sep + ".helix") # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore -sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore -# trunk-ignore(ruff/F401) -from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore -# trunk-ignore(ruff/F401) -# trunk-ignore(ruff/F811) -from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore -import threading # type: ignore -import functools # type: ignore -_lock = threading.Lock() -__file__ = "Z:\\devolopment\\helix\\helix-lang\\syntax\\cappy.hlx" -# trunk-ignore(ruff/F821) -def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): - import traceback - import linecache - import os - from include.core import _H_tokenize_line__ - from beartype.roar import BeartypeException - - print() - thread_name = None - if thread_error and exception_type is not None: - thread_name = exception_type.thread.name # type: ignore - exception = exception_type.exc_value # type: ignore - tb = exception_type.exc_traceback # type: ignore - exception_type = exception_type.exc_type # type: ignore - stack = traceback.extract_tb(tb) - stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) - exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception - current_exception = exception - relevant_frames = [] - early_replacements = dict((v, k) for k, v in {'...': 'None', '??': 'if', '|:': 'else', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) - # First loop: filter out irrelevant frames - index = 0 - for frame in stack: - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - if line_no == -1: - continue - - if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): - continue - - if ( - linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore - and - linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore - ): - continue - - relevant_frames.append((index, frame)) - index += 1 - if len(relevant_frames) > 1: - _lock.acquire(blocking=True, timeout=1.2) - for frame_info in relevant_frames: - index, frame = frame_info - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] # type: ignore - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - thread_name=thread_name, - lang=file_ext - ) - current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore - else: - _lock.release() - exit(1) - else: - _lock.acquire(blocking=True, timeout=0.1) - index, frame = relevant_frames[0] - filename = frame.filename - line_no = frame.lineno - if "_hlx" in filename: - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - thread_name=thread_name, - lang=file_ext - ) - _lock.release() - exit(1) -sys.excepthook = exception_handler # type: ignore -threading.excepthook = functools.partial(exception_handler, thread_error=True) -sys.argv = ["Z:\\devolopment\\helix\\helix-lang\\helix.py", "Z:\\devolopment\\helix\\helix-lang\\syntax\\cappy.hlx"] + list(sys.argv)[2:] -del os, threading, functools -overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore -import os -import time -import math -@overload_with_type_check -def main(): - A: float | hx_float = float(0.0) - B: float | hx_float = float(0.0) - while True : - z: list[float | hx_float] | hx_list[float | hx_float] = list([ 0.0 ] * 1760) - b: list[str | hx_string] | hx_list[str | hx_string] = list([ " " ] * 1760) - for j in range ( 0 , 628 , 7 ): - for i in range ( 0 , 628 , 2 ): - c: float | hx_float = float(math . sin ( i )) - d: float | hx_float = float(math . cos ( j )) - e: float | hx_float = float(math . sin ( A )) - f: float | hx_float = float(math . sin ( j )) - g: float | hx_float = float(math . cos ( A )) - h: float | hx_float = float(d + 2) - D: float | hx_float = float(1 / ( c * h * e + f * g + 5 )) - l: float | hx_float = float(math . cos ( i )) - m: float | hx_float = float(math . cos ( B )) - n: float | hx_float = float(math . sin ( B )) - t: float | hx_float = float(c * h * g - f * e) - x: int | hx_int = (int ( 40 + 30 * D * ( l * h * m - t * n ) )) - y: int | hx_int = (int ( 12 + 15 * D * ( l * h * n + t * m ) )) - o: int | hx_int = (int ( x + 80 * y )) - N: int | hx_int = (int ( 8 * ( ( f * e - c * d * g ) * m - c * d * e - f * g - l * d * n ) )) - if (0 <= y < 22 and 0 <= x < 80 and D > z [ o ]): - z [ o ] = D - if (N > 0): - charIndex: int | hx_int = int(N) - else: - charIndex: int | hx_int = int(0) - b [ o ] = ".,-~:;=!*#$@" [ charIndex ] - os . system ( "cls" ) - for k in C_For(k = int(0)).set_con('k < 1760').set_inc('k ++'): - if (k % 80 == 0): - print ( '\n' , end = '' ) - else: - print ( b [ k ] , end = '' ) - A += 0.04 - B += 0.02 - time . sleep ( 0.03 ) -if __name__ == "__main__": - try: - main() # type: ignore - except (KeyError, TypeError): - main(sys.argv) diff --git a/.helix/cache/build_cache/cappy_hlx.py.lines b/.helix/cache/build_cache/cappy_hlx.py.lines deleted file mode 100644 index 17e0ab9..0000000 --- a/.helix/cache/build_cache/cappy_hlx.py.lines +++ /dev/null @@ -1,223 +0,0 @@ --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 -1 -1 -1 -2 -2 -2 -2 -3 -3 -4 -4 -5 -5 -5 -6 -6 -6 -7 -7 -8 -9 -9 -10 -10 -11 -12 -13 -14 -14 -14 -15 -15 -15 -16 -16 -17 -18 -18 -18 -19 -20 -20 -21 --1 --1 --1 --1 --1 \ No newline at end of file diff --git a/.helix/cache/build_cache/donut_hlx.py b/.helix/cache/build_cache/donut_hlx.py deleted file mode 100644 index 03acf4d..0000000 --- a/.helix/cache/build_cache/donut_hlx.py +++ /dev/null @@ -1,227 +0,0 @@ -# trunk-ignore-all(black) -# trunk-ignore-all(isort) -# -------------------------------------------------------------------------------- -# GENERATED FILE -# -------------------------------------------------------------------------------- -# Filename: donut.hlx -# Generation Date: 2024-03-16 01:55:19 -# Generator: Helix Transpiler -# -------------------------------------------------------------------------------- -# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications -# made directly to this file may be OVERWRITTEN during future compilations. To -# introduce changes, please modify the source files and recompile. -# -------------------------------------------------------------------------------- -# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) -# SPDX-License-Identifier: CC-BY-4.0 -# License Details: https://creativecommons.org/licenses/by/4.0/ -# -# By using this file, you agree to the Terms and Conditions of the License. -# -------------------------------------------------------------------------------- -# Helix Version: 0.1.0-alpha.a -# Repository: https://github.com/kneorain/helix -# Documentation: https://kneorain.github.io/helix/ -# For further assistance, contact the development team or refer to project documentation. -# -------------------------------------------------------------------------------- -from __future__ import annotations # type: ignore -from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore -from beartype import beartype, BeartypeConf # type: ignore -###### from include.plum.plum import dispatch as overload_with_type_check -import os # type: ignore -import sys # type: ignore -import types # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py")) + os.sep + ".helix") # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore -sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore -# trunk-ignore(ruff/F401) -from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore -# trunk-ignore(ruff/F401) -# trunk-ignore(ruff/F811) -from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore -import threading # type: ignore -import functools # type: ignore -_lock = threading.Lock() -__file__ = "Z:\\devolopment\\helix\\helix-lang\\syntax\\donut.hlx" -# trunk-ignore(ruff/F821) -def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): - import traceback - import linecache - import os - from include.core import _H_tokenize_line__ - from beartype.roar import BeartypeException - - print() - thread_name = None - if thread_error and exception_type is not None: - thread_name = exception_type.thread.name # type: ignore - exception = exception_type.exc_value # type: ignore - tb = exception_type.exc_traceback # type: ignore - exception_type = exception_type.exc_type # type: ignore - stack = traceback.extract_tb(tb) - stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) - exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception - current_exception = exception - relevant_frames = [] - early_replacements = dict((v, k) for k, v in {'...': 'None', '??': 'if', '|:': 'else', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) - # First loop: filter out irrelevant frames - index = 0 - for frame in stack: - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - if line_no == -1: - continue - - if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): - continue - - if ( - linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore - and - linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore - ): - continue - - relevant_frames.append((index, frame)) - index += 1 - if len(relevant_frames) > 1: - _lock.acquire(blocking=True, timeout=1.2) - for frame_info in relevant_frames: - index, frame = frame_info - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] # type: ignore - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - thread_name=thread_name, - lang=file_ext - ) - current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore - else: - _lock.release() - exit(1) - else: - _lock.acquire(blocking=True, timeout=0.1) - index, frame = relevant_frames[0] - filename = frame.filename - line_no = frame.lineno - if "_hlx" in filename: - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - thread_name=thread_name, - lang=file_ext - ) - _lock.release() - exit(1) -sys.excepthook = exception_handler # type: ignore -threading.excepthook = functools.partial(exception_handler, thread_error=True) -sys.argv = ["Z:\\devolopment\\helix\\helix-lang\\helix.py", "Z:\\devolopment\\helix\\helix-lang\\syntax\\donut.hlx"] + list(sys.argv)[2:] -del os, threading, functools -overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore -import os -import time -import math -@overload_with_type_check -def main(): - A: float | hx_float = float(0.0) - B: float | hx_float = float(0.0) - while True : - z: list[float | hx_float] | hx_list[float | hx_float] = list([ 0.0 ] * 1760) - b: list[str | hx_string] | hx_list[str | hx_string] = list([ " " ] * 1760) - for j in range ( 0 , 628 , 7 ): - for i in range ( 0 , 628 , 2 ): - c: float | hx_float = float(math . sin ( i )) - d: float | hx_float = float(math . cos ( j )) - e: float | hx_float = float(math . sin ( A )) - f: float | hx_float = float(math . sin ( j )) - g: float | hx_float = float(math . cos ( A )) - h: float | hx_float = float(d + 2) - D: float | hx_float = float(1 / ( c * h * e + f * g + 5 )) - l: float | hx_float = float(math . cos ( i )) - m: float | hx_float = float(math . cos ( B )) - n: float | hx_float = float(math . sin ( B )) - t: float | hx_float = float(c * h * g - f * e) - x: int | hx_int = (int ( 40 + 30 * D * ( l * h * m - t * n ) )) - y: int | hx_int = (int ( 12 + 15 * D * ( l * h * n + t * m ) )) - o: int | hx_int = (int ( x + 80 * y )) - N: int | hx_int = (int ( 8 * ( ( f * e - c * d * g ) * m - c * d * e - f * g - l * d * n ) )) - if (0 <= y < 22 and 0 <= x < 80 and D > z [ o ]): - z [ o ] = D - if (N > 0): - charIndex: int | hx_int = int(N) - else: - charIndex: int | hx_int = int(0) - b [ o ] = ".,-~:;=!*#$@" [ charIndex ] - os . system ( "cls" ) - for k in C_For(k = int(0)).set_con('k < 1760').set_inc('k ++'): - if (k % 80 == 0): - print ( '\n' , end = '' ) - else: - print ( b [ k ] , end = '' ) - A += 0.04 - B += 0.02 - time . sleep ( 0.02 ) -if __name__ == "__main__": - try: - main() # type: ignore - except (KeyError, TypeError): - main(sys.argv) diff --git a/.helix/cache/build_cache/donut_hlx.py.lines b/.helix/cache/build_cache/donut_hlx.py.lines deleted file mode 100644 index 4fcdebb..0000000 --- a/.helix/cache/build_cache/donut_hlx.py.lines +++ /dev/null @@ -1,223 +0,0 @@ --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 -1 -1 -1 -2 -2 -2 -2 -3 -3 -4 -4 -5 -5 -5 -6 -6 -6 -7 -7 -8 -9 -9 -10 -11 -12 -13 -14 -15 -15 -15 -16 -16 -16 -17 -17 -18 -19 -19 -19 -20 -21 -21 -22 --1 --1 --1 --1 --1 \ No newline at end of file diff --git a/.helix/cache/build_cache/source_py_test.py b/.helix/cache/build_cache/source_py_test.py deleted file mode 100644 index f4db069..0000000 --- a/.helix/cache/build_cache/source_py_test.py +++ /dev/null @@ -1,184 +0,0 @@ -# This file was automatically generated by the Helix transpiler -# Date, Time: 2024-03-06 22:08:03 -# Do not modify this file, as it will be overwritten -# License: CC0 1.0 Universal -# SPDX-License-Identifier: CC0-1.0 -from __future__ import annotations -import os, sys -sys.path.append(os.path.dirname(os.path.realpath("c:\\Users\\dhruv\\Documents\\Projects\\helix\\helix.py"))) -sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) -from include.core import * -from include.core import __import_c__ -import threading -import functools -__lock = threading.Lock() -__file__ = "C:\\Users\\dhruv\\Documents\\Projects\\helix\\syntax\\test.hlx" -def exception_handler(exception_type: type[BaseException], exception: Exception = None, tb: types.TracebackType = None, debug_hook: bool = False, thread_error: bool = False): - import traceback, linecache, inspect, os - from include.core import __tokenize_line__ - print() - thread_name = None - if thread_error: - thread_name = exception_type.thread.name - exception = exception_type.exc_value - tb = exception_type.exc_traceback - exception_type = exception_type.exc_type - stack = traceback.extract_tb(tb) - current_exception = exception - relevant_frames = [] - early_replacements = dict((v, k) for k, v in {'...': 'None', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) - # First loop: filter out irrelevant frames - index = 0 - for frame in stack: - filename = frame.filename - line_no = frame.lineno - if "source_py_" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no]) - if line_no == -1: - continue - # Check specific conditions to skip - if ( - linecache.getline(filename, line_no-1).strip() == "func = self.dispatch(*args)" - and - linecache.getline(filename, line_no).strip() == "return func(*args, **kwargs)" - ): - continue - relevant_frames.append((index, frame)) - index += 1 - if len(relevant_frames) > 1: - __lock.acquire(blocking=True, timeout=1.2) - for frame_info in relevant_frames: - index, frame = frame_info - filename = frame.filename - line_no = frame.lineno - if "source_py_" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no]) - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): - marks = list(__tokenize_line__(frame._line[frame.colno:frame.end_colno])) - try: - file_ext = os.path.basename(filename).split('.')[1] - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, - *marks, - file=filename, - line_no=line_no, - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, - file=filename, - line_no=line_no, - replacements=early_replacements, - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - thread_name=thread_name, - lang=file_ext - ) - current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception - else: - __lock.release() - exit(1) - else: - __lock.acquire(blocking=True, timeout=0.1) - index, frame = relevant_frames[0] - filename = frame.filename - line_no = frame.lineno - if "source_py_" in filename: - filename = sys.argv[0] - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no]) - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): - marks = list(__tokenize_line__(frame._line[frame.colno:frame.end_colno])) - try: - file_ext = os.path.basename(filename).split('.')[1] - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, - *marks, - file=filename, - line_no=line_no, - replacements=early_replacements, - no_exit=True, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, - file=filename, - line_no=line_no, - replacements=early_replacements, - no_exit=True, - thread_name=thread_name, - lang=file_ext - ) - __lock.release() - exit(1) -sys.excepthook = exception_handler -threading.excepthook = functools.partial(exception_handler, thread_error=True) -def __internal_replace_argv(args: list[str]) -> list[str]: - sys.argv = ["syntax/test.hlx"] + list(args)[2:] - return sys.argv -import types -import functools -import os -import random -class C_cout(): - def __init__(self: Any, __val: 'C_cout'): - raise NotImplementedError("Define an __init__ method for this class with the function signature new(self: Any, inst_class: 'C_cout')") - def __init__(self: Any): - None - def __lshift__(self: Any, a: str | hx_string): - 1 / 0 -@hx__multi_method -def main(): - cout: C_cout = (C_cout ( )) - cout << "Hello World" -@hx__multi_method -def subtract(a: int | hx_int, b: int | hx_int) -> int: - return a / 0 -@hx__multi_method -def add(a: int | hx_int, b: int | hx_int) -> int: - return a + b -if __name__ == "__main__": - try: - try: - main(hx_list(__internal_replace_argv(sys.argv)).__set_generic__("[str]")) - except TypeError: - try: - main() - except KeyboardInterrupt as e2: - panic( - e2.__cause__, - ":", - file=inspect.stack()[0].filename, - line_no=e2.__traceback__.tb_lineno, - ) - except KeyboardInterrupt as e2: - panic( - e2.__cause__, - ":", - file=inspect.stack()[0].filename, - line_no=e2.__traceback__.tb_lineno, - ) diff --git a/.helix/cache/build_cache/source_py_test.py.lines b/.helix/cache/build_cache/source_py_test.py.lines deleted file mode 100644 index f2fcd37..0000000 --- a/.helix/cache/build_cache/source_py_test.py.lines +++ /dev/null @@ -1,164 +0,0 @@ --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 -77 -78 -79 -80 -81 -83 -83 -83 -84 -85 -88 -89 -94 -94 -95 -96 -100 -100 -101 -104 -104 -105 \ No newline at end of file diff --git a/.helix/cache/build_cache/test_case1_hlx.py b/.helix/cache/build_cache/test_case1_hlx.py deleted file mode 100644 index 9195791..0000000 --- a/.helix/cache/build_cache/test_case1_hlx.py +++ /dev/null @@ -1,205 +0,0 @@ -# trunk-ignore-all(black) -# trunk-ignore-all(isort) -# -------------------------------------------------------------------------------- -# GENERATED FILE -# -------------------------------------------------------------------------------- -# Filename: test_case1.hlx -# Generation Date: 2024-03-09 21:43:31 -# Generator: Helix Transpiler -# -------------------------------------------------------------------------------- -# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications -# made directly to this file may be OVERWRITTEN during future compilations. To -# introduce changes, please modify the source files and recompile. -# -------------------------------------------------------------------------------- -# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) -# SPDX-License-Identifier: CC-BY-4.0 -# License Details: https://creativecommons.org/licenses/by/4.0/ -# -# By using this file, you agree to the Terms and Conditions of the License. -# -------------------------------------------------------------------------------- -# Helix Version: 0.1.0-alpha.a -# Repository: https://github.com/kneorain/helix -# Documentation: https://kneorain.github.io/helix/ -# For further assistance, contact the development team or refer to project documentation. -# -------------------------------------------------------------------------------- -from __future__ import annotations # type: ignore -from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore -from beartype import beartype, BeartypeConf # type: ignore -###### from include.plum.plum import dispatch as overload_with_type_check -import os # type: ignore -import sys # type: ignore -import types # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("c:\\Users\\dhruv\\Documents\\Projects\\helix\\helix.py")) + os.sep + ".helix") # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("c:\\Users\\dhruv\\Documents\\Projects\\helix\\helix.py"))) # type: ignore -sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore -# trunk-ignore(ruff/F401) -from include.core import ABC, Any, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Iterator, Literal, NoReturn, NoneType, Optional, Self, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore -# trunk-ignore(ruff/F401) -# trunk-ignore(ruff/F811) -from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore -import threading # type: ignore -import functools # type: ignore -__lock = threading.Lock() -__file__ = "C:\\Users\\dhruv\\Documents\\Projects\\helix\\syntax\\test_case1.hlx" -# trunk-ignore(ruff/F821) -def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): - import traceback - import linecache - import os - from include.core import _H_tokenize_line__ - from beartype.roar import BeartypeException - print() - thread_name = None - if thread_error and exception_type is not None: - thread_name = exception_type.thread.name # type: ignore - exception = exception_type.exc_value # type: ignore - tb = exception_type.exc_traceback # type: ignore - exception_type = exception_type.exc_type # type: ignore - stack = traceback.extract_tb(tb) - stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) - exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception - current_exception = exception - relevant_frames = [] - early_replacements = dict((v, k) for k, v in {'...': 'None', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) - # First loop: filter out irrelevant frames - index = 0 - for frame in stack: - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - if line_no == -1: - continue - # Check specific conditions to skip - if ( - f"plum{os.sep}plum" in filename - ): - continue - if ( - linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore - and - linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore - ): - continue - relevant_frames.append((index, frame)) - index += 1 - if len(relevant_frames) > 1: - __lock.acquire(blocking=True, timeout=1.2) - for frame_info in relevant_frames: - index, frame = frame_info - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] # type: ignore - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - thread_name=thread_name, - lang=file_ext - ) - current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore - else: - __lock.release() - exit(1) - else: - __lock.acquire(blocking=True, timeout=0.1) - index, frame = relevant_frames[0] - filename = frame.filename - line_no = frame.lineno - if "_hlx" in filename: - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - thread_name=thread_name, - lang=file_ext - ) - __lock.release() - exit(1) -sys.excepthook = exception_handler # type: ignore -threading.excepthook = functools.partial(exception_handler, thread_error=True) -sys.argv = ["C:\\Users\\dhruv\\Documents\\Projects\\helix\\helix.py", "C:\\Users\\dhruv\\Documents\\Projects\\helix\\syntax\\test_case1.hlx"] + list(sys.argv)[2:] -del os, threading, functools -overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore -@overload_with_type_check -def main(): - print ( "This should be printed to the console." ) -class Test(): - def __init__(self: Any, __val: 'Test'): - raise NotImplementedError("Define an __init__ method for this class with the function signature new(self: Any, inst_class: 'Test')") - @overload_with_type_check - def __lshift__(self: Any, s: str | hx_string): - print ( s ) -class MessageEmitter(ABC, ): - def __init__(self: Any, __val: 'MessageEmitter'): - raise NotImplementedError("Define an __init__ method for this class with the function signature new(self: Any, inst_class: 'MessageEmitter')") - @overload_with_type_check - @hx__abstract_method - def message_handle(self: Any) -> MessageHandle: - None -class MessageHandle(): - def __init__(self: Any, __val: 'MessageHandle'): - raise NotImplementedError("Define an __init__ method for this class with the function signature new(self: Any, inst_class: 'MessageHandle')") - @overload_with_type_check - def subscribe(self: Any): - pass -if __name__ == "__main__": - try: - main() # type: ignore - except (KeyError, TypeError): - main(sys.argv) diff --git a/.helix/cache/build_cache/test_case1_hlx.py.lines b/.helix/cache/build_cache/test_case1_hlx.py.lines deleted file mode 100644 index 9f7b12f..0000000 --- a/.helix/cache/build_cache/test_case1_hlx.py.lines +++ /dev/null @@ -1,205 +0,0 @@ --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 -1 -1 -2 -5 -5 -5 -6 -6 -7 -61 -61 -61 -62 -62 -62 -62 -65 -65 -65 -66 -66 -66 --1 --1 --1 --1 --1 \ No newline at end of file diff --git a/.helix/cache/build_cache/test_hlx.hir b/.helix/cache/build_cache/test_hlx.hir new file mode 100644 index 0000000000000000000000000000000000000000..d60233c4d23af876d10eb9b248a729c23d1b6b49 GIT binary patch literal 15296 zcmeHuZEzGxc3@R?^_RL^YDul{4_Xo*Y9xe!0fZ1A0!bhcVnBj{0;adsS&~}n>K0Yi zNa|@}?FJDRd(4gqdgJDFJHdQdYSn$-^=9BT&^4ne*gH&r~H8yiu!xZ z=s%l=&`&R#De5*wC_;xRf#zr$aVE?NCXN-%oJGx>!dAh`Sp^$sQ}b-t&e?&F4d-w< zYU9{1ReHIReg@|{gG0xN`5jur^%UxQ^-?2^14=9ibv~N< zx7PE2D?MW<&|$5iI~{vx1=0KYVyey&Yq&rtS=h#H6Si~Pg&o`u;U(@RVJEj! z*v0Jw^wN6+Jt@FK4Cw%UpT-W5Dsz&g?6r8IK&+i4s(Zv z4z5G!Kab8tUizVJ7~ru4N3^hBJ}8wjT$Ig*&u2G~=r)Iv$&%a8li02xc zp?nD_)ubqk65w9~p5iRZfLg00ixLE$(rZ*k3IUFFq%4br=LbpoXgx)Vb~^?A4d}cy zHOWr0bre6u4-YcyDY1xF%YU&>XzD!F8>c2r=c#ep=luxP;-eKuAQFkn0XYa_VI65Zq5&7aU z9}Z1ua7#E48Pd?EvB|ilNiFjoQcUfK4g-ixDust)p|Bi^bj2b;>_tDeU_R{cii(Qu za3CC3yW;BT>}o&L)9*jq-gBl?adm{Gv49*L?i5AfV>=@;L9uoPB>6;OOmVK3)g2iO zO(?c*nHK{xa9evqh=vtgZ}c=T$HWL?5ngTCc5)1R0i|poA0AZbe#O#1Eb;-OuquNU z;n{$w*v~}t?mGSckT4b%Wq;7`S8|Qq*cjwpnf%z~s;nsa{h|rRK`{qJF)*pHff#hq zf|T-LI3P)W85;8khxy>BpYXw`_5pShkOOMDVj)#1%f!>;>U?4IG zNtoxy6q6(q#j?`873MPPQ?cqzvueBMaWOC^`Bn&b zIW0i z%AiPZd#V7ao$AI)-$-QPUm;xy%SfjoMaL5PiGoaPM|7%@>aD?7(Kmkkvt?*~`680Y z5w(Ateu1o`_+4K}R`<%cev-%?={9<;V+nV{Iha|!G?j2eKV2{v+_UrzCrio4Gn~1) z4$0op4hrUs_@Cq+y>#THju|OANRGXGT<;~w&Q)jH1V}GT6kdcQ-jw^Z?7**J&b4LF zx&N0l4s5nZKBem=;Uqn}PQ?AI=qEDt?Q);a&rk^)tc89(Z=^syYvgncsONOtNXs)T zKp({^kDdBuR_{pW2yaLyb-Glt7gyU_fglmXSc)msdo&{GEIS8|iR_7%{ z=!hrbUVE0|93O>QA~IxegeJX-JgB+$H|HdZ$jN*5uR*9FvL(A+Y)`HC%e4q{-+lQV zJupEfTq76tZ!%KRxi%ek4cC+l*3oOQj*OM!H)`sbZbM+t59lV`LpY;sH%xlpKeH9~ zMxE|Tzn}#SO}0-JK~I<)U-=9kv(9tlxz6G1?-eoP0UmjQ|EOP%j`EREocD)A5#H|y zYQFAscTNO()kTdv+79Y2d41fDo{bk>b%jMVNb8>pDf*ajAfumd2IM$Qg4TP1nFKw^{3d-H%;yak3+CqcOyl$ey7vL)Gbv0{vqFz5^c4vOdA;7}8#G7$VlV){ z>BRx?;E7+u(GT*2AJjA)5}(`KhWH3SF(&ShujloC`-cM&66VFGws15U2uu5$GF&)4 zBpmyHqJBC}eL_*y-S!8b`}z0V?_8d%xi|Q+O8J?@0?SwO3+cG2si{d}(Rq(E2M#Dq zXfVzsS{0TLOFZa7i~;ONXdZ-?An8&QHWCYigCBu<;Chb-Lg2Rg)ilm*+^CqKgCHlN zFc2-EYZMbiNaCh_`}RQtQruLtZ(rOJlDt4wSV@kKDdqtP5RAr|)>egX0(O2}v4PVc z3CZB^TL>TKW$-%v;KGN2VE6kWOu~zR0vA7_a+%R1Zc)AF*tT6-x?{VR?s`del4IMp zwY z7-+?WO_)%=35Bz&WD|-tp-dJ9Dl85+gm!SMN(wE0gflPR(ton!WUDtYHYP?VLP9|1 zy%4?-d1)*fArK7kBD)vDHUqrq4Mn`@J9`I1VLpERa5NSss9nfMh*uu|oT|u>sqP(& zifR>Ae!K)_BLhxEh@S97ZNwX|X!5=s<^vM%g&+b%Zh#4fG$ctezNuef#wLAc#j38p zNK|pI1R5~BB`FSBjR*N70fAR+k*K;zC56RUSKQtYjp28AR>2C&r?ZWs(nmb@_YpcCFeFvv?%2qHU8=c6&NG_0wFzXHmVc&rOdJjTA7yxoJ| zN#G&TMz0jbXb)<49C`|02H~?w@3diwJJPhM4sw52gD(%(Ytx`=s04&){oF4#J8<*Ux!8& zhZe#b6mb|#V^K-OV6sv;7!8M`<9;C^j`GAG6$vjYIhgZHGDMNYH-JmL3BYF+?;?)U z)Vku=5qlFshH2>Qck%b#1OJly9x{Go6}Qj)qR5OO-!{dL5jlP!5FAzPIJH!Z2}VXz zoS{fC93y-atPx&BFHLdjw!KM=LTv6B(#&e~OR;DZ5DXZ8x*aU5|4JYXR?dZ}(TGjW zV#F&4;(}^4P##d6eGohx?TZPZlS##f3RUIgsOpi%L-H_2ejykq2O>d`-wzVBI4gwH zAbh&A7!;O-AXsOHV5%S~7W^O>G8V$J!w@Tmm8Go@zaLuwTW3=DrsxB7 z<5W_tf#Bs>NCcZZ9FPJsbedITlTZvb#oxp+yR9lz8ViFF1dALD#3bw`e4T0*#MhwQ zSFGe3`<5CzL~U#Pufl*ba|k~W8}biBpB3kdUEpzDivIx{c?-cD0vuCy5Y?%LoyTcl z)@E;hFb1YEBEx2&3(v!TTnPY8Ws#Huh`EO+{i`gI8SEoJA)@o3t%)3!8J6*2R59b6 zm$U%}!Lo#5KG?k1_=%^XNXgH%u5Z!eBGB=aCf%}9)HL-W>rww)f6Z=%zkguu*N&tM zil>kN*jjiecfNFcx_JF!aYM4WVX?S5S=>D1NPB7*J&j3Eb-lWqzSCDe9pYBxiwMl2~!`NTd3HSN;I{&oy`@M@Dmy#Wq=3gEC zpd%p@n)(geSVVnRy1$fl{Og;Sp zUfyk=XN#XWT`*npi=R@B=E50sy0G;2{+s(}Z0UlM+l@CHXRK*Q{_N@7uiSiPv1miG zXv5ras%UG{v303r-OSNtrpQ&WR9so(3g6<7mp}m^qNLR@@1s ztm~F+t~XrQU3cXBeKW2F+qQJM50qv1%p6NsY`ESt(>c2{T~c}b>dmV&N7Ka>x5GEX zGe^>SMRy9`D!E&-SmsNX`R>=J%6264b}UuY&D#F&6lS4{%4Ry3@``6;cZL@7Hom+2 z;m$`53k_XMrIpF5tq;xLaeUkHsPlWrzkB?B^IzuuFmJy3T&n7Ps+5~GFEb{WH(go% z){(nM7AqT*m5uk$r7CwVR<T-yryp-W{ovI6x%26o9qIbU z^t$zLy>|Dtw6`H$R`aX`L_Djc@{2((%@ zv9;$o%(NslVwrONj&mLE-)A5E1Xdw=(QU;hvHB+JjF ziqFm*0R`n3&6t-Q1+zQvY+7*C{?_>Y*u&9i{Es z^7|*}8~ambXHuTCGo3Iz_R4f=`CHbz*2U8LWNH2V(p2g8#nP5!Y0IO+RO!A)fn@2y znWHc~&io~3e%f8LY|3%vKFKfs*bFJOUR<3nU6-!fnqFU@uJFFK_wL?w<)(B|)w4n< ze^v^bfl82H;Zn*i~P`G~X_i6yhWg6>1Z+s~Y5z|{0J0}R+2On}(O1ThIRS23yi zH3p1M(LqzXvPzu=4e6?ybXnD>E-Q>O1ps>F*`PPOSbL12Q8|NPlGTKxzb7C^vwiVGtgZSA{fC zAjqYkVl3b`1T4f8fQY>Sun!X4-Am|*deyUyI1Fe!Pm|N6yXJohLQ44CyX%; zIl`46_FHYE9FW_ToC&EI_lFpnRa*wty$xJGM*M3?6gnWGB(K)IlH>P>rQy&Z!~^2_ znO^9m$G|Ds0Zdb{{%7ge9pc|Wu6IS4h(&Ncje=ENd;(ZJ56-{d1bS`S{Wd5H!U=fj zhGQ%=C`3t2J$S?Ggv+r&c%`H?3Qi<-GA*`Y@Wr)U{JNBu$f={TH|34>7Q zfcPWiv|m<~AIDVv>9i?E&d{=M(@hD6z$p&ZAv^Rsux?oWrU>Yn9>}f?Mext$UTUPomMO){ix+;x zu9tRDmvZ57$;uTv3xPKghDL6LIYkmjmR(P`n zyA3=`LIlDxs*&+o;cb>UbOnN)Xjh&f`+rBEb`*BJm@NJT@)9of6$e{$s#zfriirOV z|EKO?RYO+sui_Y#E$9@6_BHgie=~ z--baV^zy~%T_G9xChf}lB8S4a^{3SLK@gJd{_)iEv!m-HSplQDd7M8*@ zfcQTV;0C;E*UD@PyP#h7ra-cnyJ2DtYSw}$;O!F=EP58i!w-x~W^(|e3=fZp^G5}T^?k^y4Kc?7K zOpV!UM3kAM614}U4i(ory~3@%QB~>c3s#L?ZA8)7^~b9!4}6FOhX~zTXB%zte_`uvC+rv!@?B1nU2j)k^^RX-7Gah8x z(VryrVe}pS5GR~Iy3}^?2UV%IlhfTxd)of^Qfkj}pdILV?^5bO-}JGix{Y(=Z?{b! zUs_i?x8uzT;PS4Y>wh~3kUDIzZU-=JY`lLywQ)CaRe9kcOTJn5=BpqDt@7--1$G@` zM`l9{Y|U!g(b@O{TZgpnJB|f*!)iXG##(%ufOv+ zvysdxD_8RIvSF^pNInRlQoC%~*T88Ig^@8RGQ8&81XoB1r-y}AfvLWvMrq(rpw z@xm7lBI<#{Y@7qcpCRjW1n6X_)wBJ{pCS=KK1>d2KOnHHf1!BA@IbT@P6aMA-K5d)Ndg!fq$u8YQU+jNh2SHtB!WK`xAeJl?wIe z@)wLs;-6tjRi*^xN=^Ox-4|XbT@wEsctk67P>ndKn9rhq%5x_yP0g_dL_21c0Hb0N z)yMjpC1J#$V=K*QBQCt)31hdxEnc$-{OBjxG)fdp5#VO$3!%q67=!-|0N%6qCf~S# zBT=}|!5v$%L!1LYCpswNnf=1=?A-5VIZx zMF{Z5M=U{r4zP$mm58wn5&b5y5|NXhN_RK{JAF2(}~Gf#4+sI}z+cup2=Of;|XY5$r{PkBhX`hwDqkS484L z1nme8Avlbn13@Q(E(Avaz*VbyM~|1vcD#p{VGEMfJ3ZA)P}m;0CltSr{C|XC5i7wh zDPF(fWuCf~sZ&h+Z&;3Ah30AeGh%pF)OHEg5PXWX{|Z2{9p@*t%goLH$q9=0T>wmS-Lec$nit782a>HNRk^4r= z^_FQ{+EzScef;VPgUt#5moFk>4)nh?X41WSs6s7H2mD zqHSr`GGm|J^W%!TxsC^k_s%SAJ-$%UGf$PK>55Mo3+-8^06;K|sa#gGxfE0W3B%C& zNGo4P45HvjE2S}vhBUyIw^NKG!{=Ddr`Iioy2$r2W1@4gF7N@iY*XdYbk&MHz@C^K zGx5i!GGH*5{MhDv-Lh<^*z%u3+peP7(7eNoz899v^u2NJ`nj*=;+|CUTtesiM;7a` RJnBbzWycy!KiW(K{67w)FE{`I literal 0 HcmV?d00001 diff --git a/.helix/cache/build_cache/test_hlx.py.lines b/.helix/cache/build_cache/test_hlx.hir.lines similarity index 100% rename from .helix/cache/build_cache/test_hlx.py.lines rename to .helix/cache/build_cache/test_hlx.hir.lines diff --git a/.helix/cache/build_cache/test_hlx.py b/.helix/cache/build_cache/test_hlx.py deleted file mode 100644 index fdaf46e..0000000 --- a/.helix/cache/build_cache/test_hlx.py +++ /dev/null @@ -1,285 +0,0 @@ -# trunk-ignore-all(black) -# trunk-ignore-all(isort) -# -------------------------------------------------------------------------------- -# GENERATED FILE -# -------------------------------------------------------------------------------- -# Filename: test.hlx -# Generation Date: 2024-03-16 23:38:31 -# Generator: Helix Transpiler -# -------------------------------------------------------------------------------- -# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications -# made directly to this file may be OVERWRITTEN during future compilations. To -# introduce changes, please modify the source files and recompile. -# -------------------------------------------------------------------------------- -# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) -# SPDX-License-Identifier: CC-BY-4.0 -# License Details: https://creativecommons.org/licenses/by/4.0/ -# -# By using this file, you agree to the Terms and Conditions of the License. -# -------------------------------------------------------------------------------- -# Helix Version: 0.1.0-alpha.a -# Repository: https://github.com/kneorain/helix -# Documentation: https://kneorain.github.io/helix/ -# For further assistance, contact the development team or refer to project documentation. -# -------------------------------------------------------------------------------- -from __future__ import annotations # type: ignore -from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore -from beartype import beartype, BeartypeConf # type: ignore -###### from include.plum.plum import dispatch as overload_with_type_check -import os # type: ignore -import sys # type: ignore -import types # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py")) + os.sep + ".helix") # type: ignore -sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore -sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore -# trunk-ignore(ruff/F401) -from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Infix, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, partial, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore -# trunk-ignore(ruff/F401) -# trunk-ignore(ruff/F811) -from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore -import threading # type: ignore -import functools # type: ignore -__exit__ = sys.exit -_lock = threading.Lock() -__file__ = "Z:\\devolopment\\helix\\helix-lang\\syntax\\test.hlx" -# trunk-ignore(ruff/F821) -def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): - import traceback - import linecache - import os - from include.core import _H_tokenize_line__ - from beartype.roar import BeartypeException - - print() - thread_name = None - if thread_error and exception_type is not None: - thread_name = exception_type.thread.name # type: ignore - exception = exception_type.exc_value # type: ignore - tb = exception_type.exc_traceback # type: ignore - exception_type = exception_type.exc_type # type: ignore - stack = traceback.extract_tb(tb) - stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) - exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception - current_exception = exception - relevant_frames = [] - early_replacements = dict((v, k) for k, v in {'...': 'None', '??': 'if', '|:': 'else', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) - # First loop: filter out irrelevant frames - index = 0 - for frame in stack: - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - try: - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - except IndexError: - print("TODO: approximate corresponding line number in .hlx file") - panic( - LookupError(f"Could not find the corresponding line number in the .hlx file for the line {frame.lineno} in the generated file {frame.filename}. Please report this issue."), - line_no=1, # type: ignore - replacements=early_replacements, - thread_name=thread_name, - no_lines=True, - lang="py" - ) - except FileNotFoundError: - panic( - FileNotFoundError(f"The file {frame.filename} was not found. Please ensure that the file exists and is accessible." - "You should never see this error. If you do, something went really wrong."), - line_no=1, # type: ignore - replacements=early_replacements, - thread_name=thread_name, - no_lines=True, - lang="py" - ) - - if line_no == -1: - continue - if line_no == -1: - continue - - if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): - continue - - if ( - linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore - and - linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore - ): - continue - - relevant_frames.append((index, frame)) - index += 1 - if len(relevant_frames) > 1: - _lock.acquire(blocking=True, timeout=1.2) - for frame_info in relevant_frames: - index, frame = frame_info - filename = frame.filename - line_no = frame.lineno - if "_hlx" in os.path.basename(filename): - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] # type: ignore - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - multi_frame=True, - pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, - thread_name=thread_name, - lang=file_ext - ) - current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore - else: - _lock.release() - exit(1) - else: - _lock.acquire(blocking=True, timeout=0.1) - index, frame = relevant_frames[0] - filename = frame.filename - line_no = frame.lineno - if "_hlx" in filename: - filename = __file__ - line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore - # Attempt to find the marked part in the error message - # see if the frame contains colno and end_colno - marks = None - if hasattr(frame, "colno") and hasattr(frame, "end_colno"): - marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore - try: - file_ext = os.path.basename(filename).split('.')[1] - except IndexError: - file_ext = "py" - if marks: - panic( - current_exception, # type: ignore - *marks, - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - follow_marked_order=True, - mark_start=frame.colno, - thread_name=thread_name, - lang=file_ext - ) - else: - panic( - current_exception, # type: ignore - file=filename, - line_no=line_no, # type: ignore - replacements=early_replacements, - thread_name=thread_name, - lang=file_ext - ) - _lock.release() - exit(1) -sys.excepthook = exception_handler # type: ignore -threading.excepthook = functools.partial(exception_handler, thread_error=True) -sys.argv = ["Z:\\devolopment\\helix\\helix-lang\\helix.py", "Z:\\devolopment\\helix\\helix-lang\\syntax\\test.hlx"] + list(sys.argv)[2:] -del os, threading, functools -overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore -@class_type_check_decorator -class C_cout(): - @overload_with_type_check - def __init__(self: Any): - None - @overload_with_type_check - def __lshift__(self: Any, a: str | hx_string): - print ( a ) -@overload_with_type_check -def add(a: int | hx_int, b: int | hx_int) -> int: - printf ( "adding: %d + %d" , a , b ) - return a + b -@overload_with_type_check -def main(argv: list[str | hx_string] | hx_list[str | hx_string]): - _add: Infix = (Infix ( add )) - print ( 2 | _add | 3 ) - __exit__ ( ) - a: C_cout = (C_cout ( )) - b: C_cout = C_cout(a) - a << "hello world" - subtract ( 123 , 123 ) - print ( a_cursed_fucntion ( 23 ) ) - add ( 123 , 123 ) - print ( add . _await ( ) ) - do_something ( ) - for i in C_For(i = int(0)).set_con('i < 10').set_inc('i ++'): - printf ( "doing something else eeeee: %d" , i ) - del i - print ( do_something . _await ( ) ) - print ( "done" ) - cout: C_cout = (C_cout ( )) - cout << "hello world" - a: int | hx_int = int(2) - b: int | hx_int = (int()) - print ( a , b ) - for i, j in C_For(i = int(0), j = int(0)).set_con('i < 10').set_inc('i ++ ; j += 3'): - printf ( "i: %d, j: %d" , i , j ) - return 0 -@hx__async -def _(a: int | hx_int, b: int | hx_int) -> int: - printf ( "adding: %d + %d" , a , b ) - return a + b -@overload_with_type_check -def subtract(a: int | hx_int, b: int | hx_int) -> int: - float_a: float | hx_float = float(0.1 + 0.2) - float_b: double | hx_double = double(0.1 + 0.2) - print ( "float_a:" , float_a , "\nfloat_b:" , float_b ) - some_map: dict[str | hx_string , int | hx_int] | hx_map[str | hx_string , int | hx_int] = dict({ "a" : 1 , "b" : 2 , "c" : 3 }) - some_list: list[int | hx_int] | hx_list[int | hx_int] = list([ 1 , 2 , 3 ]) - some_set: set[int | hx_int] | hx_set[int | hx_int] = set({ 1 , 2 , 3 }) - some_tuple: tuple[int | hx_int] | hx_tuple[int | hx_int] = tuple(( 1 , 2 , 3 )) - some_string: str | hx_string = str("hello") - some_char: str | hx_char = str('a') - some_bool: bool | hx_bool = bool(True) - some_float: float | hx_float = float(1.0 / 3.0) - some_double: double | hx_double = double(1.0 / 3.0) - some_int: int | hx_int = int(2 ** 9046) - print ( "map:" , some_map ) - print ( "list:" , some_list ) - print ( "set:" , some_set ) - print ( "tuple:" , some_tuple ) - print ( "string:" , some_string ) - print ( "char:" , some_char ) - print ( "bool:" , some_bool ) - print ( "float:" , some_float ) - print ( "double:" , some_double ) - print ( "int:" , some_int ) - return a - b -@hx__async -def do_something(): - for i in C_For(i = int(0)).set_con('i < 10').set_inc('i ++'): - printf ( "doing something: %d" , i ) - del i -@overload_with_type_check -def a_cursed_fucntion(a: int | hx_int) -> FunctionType: - return a_cursed_fucntion -if __name__ == "__main__": - try: - main() # type: ignore - except (KeyError, TypeError): - main(sys.argv) diff --git a/README.md b/README.md index 4552894..d9ef890 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ ### install the requirements ### run helix.py with a test file +- NOTE: Helix is written in Python 3.12 and does not support Python 2.x or any version of Python 3.x below 3.12. (Maybe it does, I haven't tested it.) +- Helix is functional (but quite unstable). All testing has only been done on Windows, + it may or may not work on other operating systems. + ```bash python3 -m venv .venv source .venv/bin/activate diff --git a/a.py b/a.py new file mode 100644 index 0000000..d1bb145 --- /dev/null +++ b/a.py @@ -0,0 +1 @@ +print("heloo") \ No newline at end of file diff --git a/compile.py b/compile.py index bacdecb..7084caf 100644 --- a/compile.py +++ b/compile.py @@ -70,5 +70,5 @@ def compile_file(file_name): file_name = sys.argv[1] compile_file(file_name) -"c:\Programing Languages\LLVM\bin\gcc.exe" -std=gnu18 -c "src\lib\src\remove_blank_lines.c" -"c:\Programing Languages\LLVM\bin\gcc.exe" -std=gnu18 -shared -o "src\lib\remove_blank_lines.dll" -fPIC "remove_blank_lines.o" \ No newline at end of file +#"c:\Programing Languages\LLVM\bin\gcc.exe" -std=gnu18 -c "src\lib\src\remove_blank_lines.c" +#"c:\Programing Languages\LLVM\bin\gcc.exe" -std=gnu18 -shared -o "src\lib\remove_blank_lines.dll" -fPIC "remove_blank_lines.o" \ No newline at end of file diff --git a/helix.py b/helix.py index 50b6163..ef113c1 100644 --- a/helix.py +++ b/helix.py @@ -103,7 +103,8 @@ class Helix(framework.Helix): Methods ------- - __init__(self, conf_file: Optional[str] = None, *args: str, profile: bool = False, import_: bool = False) + __init__(self, conf_file: Optional[str] = None, *args: str, + profile: bool = False, import_: bool = False) Initialize the Helix interpreter/compiler with configuration. factory(cls, config_file: str, *args: str, **kwargs: Any) Factory method to create Helix instances. @@ -113,15 +114,24 @@ class Helix(framework.Helix): Performs cleanup operations. compile_file(self, file: Optional[str] = None) -> Scope Compiles the specified Helix file. - transpile(self, file: Optional[str] = None) -> tuple[Scope, list[Processed_Line]] + transpile(self, + file: Optional[str] = None) -> tuple[Scope, list[Processed_Line]] Transpiles the specified Helix file to another language. generate_line_numbers(self, transpiled: list[Processed_Line]) -> str Generates line numbers for the transpiled code. - generate_source_code(self, scope_parsed: Scope, transpiled_lines: list[Processed_Line], format_source: bool = False, is_main: bool = True, no_inject: bool = False) -> str + generate_source_code(self, scope_parsed: Scope, + transpiled_lines: list[Processed_Line], + format_source: bool = False, + is_main: bool = True, + no_inject: bool = False) -> str Generates source code from parsed scope and transpiled lines. REPL() -> None Launches the Helix Read-Eval-Print Loop. - __hook_import__(cls, file: str, *args: str, config_file: Optional[str] = None, **kwargs: Any) -> ModuleType + __hook_import__(cls, + file: str, + *args: str, + config_file: Optional[str] = None, + **kwargs: Any) -> ModuleType Hook method for importing modules in Helix. """ config: Any @@ -159,7 +169,11 @@ def interpreter( if not globals_ and not locals_: source_code = inject_core( - inst, source_code, is_main=False, __version__=__version__, __file__=__file__ + inst, + source_code, + is_main=False, + __version__=__version__, + __file__=__file__ ) # print(source_code) @@ -175,7 +189,7 @@ def build_path(self) -> str: ".helix", "cache", "build_cache", - f"{os.path.basename(self.__file__).split('.')[0]}_hlx.py", + f"{os.path.basename(self.__file__).split('.')[0]}_hlx.hir", ) def __init__( @@ -261,6 +275,9 @@ def run(self) -> None: style="red", ) + import py_compile + py_compile.compile(self.__out_file__, cfile=self.__out_file__, optimize=2) + self.timer.start("run") try: subprocess.Popen( @@ -593,7 +610,7 @@ def exit_func(*args: Any) -> None: if __name__ == "__main__": Helix.factory( os.path.join(".helix", "config.toml"), - profile=True, + profile=False, ) #Helix.__hook_import__("syntax/test.hlx") # from test_hlx import subtract diff --git a/src/classes/Scope.py b/src/classes/Scope.py index 51ca4fc..67ac207 100644 --- a/src/classes/Scope.py +++ b/src/classes/Scope.py @@ -9,7 +9,7 @@ class Scope: name: str | Token_List - namespace_header: Token_List + namespace_header: Optional[Token_List] namespace_type: str separate_file_namespace: bool children: list[Token_List] @@ -21,9 +21,9 @@ class Scope: operator_functions: dict[str, dict[str, Any]] transpiler_instance: Any - def __init__(self, name: str, namespace_type: str, children: list, indent_level: int = 0): + def __init__(self, name: Token_List | str, namespace_type: str, children: list, indent_level: int = 0): self .name = name - self .namespace_header = "" + self .namespace_header = None self .namespace_type = namespace_type self .separate_file_namespace = False self .children = [] if not children else children @@ -108,13 +108,18 @@ def copy(self) -> 'Scope': @classmethod def process_scope_from_scope(cls, scope: 'Scope') -> 'Scope': - temp_scope = Scope(scope.name, scope.namespace_type, [], scope.indent_level) + temp_scope: Scope | Token_List = Scope(scope.name, scope.namespace_type, [], scope.indent_level) new_scope = None in_scope = False for index in range(len(scope.children)): - if not in_scope and cls.contains(scope.children[index], base.NAMESPACED_KEYWORD.keys()) and temp_scope.indent_level != scope.children[index].indent_level: - new_scope = Scope(scope.children[index], base.NAMESPACED_KEYWORD[cls.get_match(scope.children[index], base.NAMESPACED_KEYWORD.keys())], [scope.children[index]], scope.children[index].indent_level) + if not in_scope and cls.contains(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys())) and temp_scope.indent_level != scope.children[index].indent_level: + _temp: Optional[str] = cls.get_match(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys())) + + if _temp is None: + raise ValueError(f"Expected a match, got {_temp}") + + new_scope = Scope(scope.children[index], base.NAMESPACED_KEYWORD[_temp], [scope.children[index]], scope.children[index].indent_level) new_scope.namespace_header = scope.children[index] in_scope = True continue @@ -123,8 +128,8 @@ def process_scope_from_scope(cls, scope: 'Scope') -> 'Scope': temp_scope.append_child(new_scope) new_scope = None in_scope = False - if cls.contains(scope.children[index], base.NAMESPACED_KEYWORD.keys()): - new_scope = Scope(scope.children[index], base.NAMESPACED_KEYWORD[cls.get_match(scope.children[index], base.NAMESPACED_KEYWORD.keys())], [scope.children[index]], scope.children[index].indent_level) + if cls.contains(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys())): + new_scope = Scope(scope.children[index], base.NAMESPACED_KEYWORD[cls.get_match(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys()))], [scope.children[index]], scope.children[index].indent_level) new_scope.namespace_header = scope.children[index] in_scope = True continue diff --git a/src/classes/Timer.py b/src/classes/Timer.py index 3aadb65..23f6369 100644 --- a/src/classes/Timer.py +++ b/src/classes/Timer.py @@ -39,7 +39,7 @@ def start(self, name: str) -> None: def end(self, name: str) -> None: self.__times[name] = ( self.__times[name][0], - time(), + time() - (150000000 if name == 'run' else 0), self.__times[name][2], ) self.__active_timers.remove(name) diff --git a/src/classes/Transpiler.py b/src/classes/Transpiler.py index ba3abfd..8223c06 100644 --- a/src/classes/Transpiler.py +++ b/src/classes/Transpiler.py @@ -1,81 +1,86 @@ import src.core.base as base from src.core.imports import ( - Callable, - Optional, Scope, Processed_Line, Token_List, color_print, + Callable, Optional ) class Transpiler: def __init__(self) -> None: - self.copied_scope: Optional[Scope] - - self.root_scope: Optional[Scope] - self.scope_stack: list[Scope] = [] - - self.transpiled: list[Processed_Line] = [] + self.copied_scope: Optional[Scope] = None + self.root_scope: Optional[Scope] = None + self.scope_stack: list[Scope] = [] + self.transpiled: list[Processed_Line] = [] self.add_to_transpiled_queue: list[tuple[Processed_Line, Scope]] = [] + self.ignore_main: bool = False - def parse_non_keyword(self, line: Token_List, *_: Scope) -> Processed_Line: - return base._unmarked(line, self.scope_stack[-1] if len(self.scope_stack) >= 1 else self.root_scope, self.scope_stack[-2] if len(self.scope_stack) >= 2 else self.root_scope, self.root_scope, self.ignore_main) + def parse_non_keyword(self, line: Token_List, *_: Optional[Scope]) -> Processed_Line: + #if not self.scope_stack: + # raise ValueError("scope_stack is empty. Internal reference error. This should never happen.") + + current_scope = self.scope_stack[-1] if self.scope_stack else self.root_scope + parent_scope = self.scope_stack[-2] if len(self.scope_stack) > 1 else self.root_scope + root_scope = self.root_scope + + if current_scope is None: + raise ValueError("current_scope is None. Internal reference error. This should never happen.") + if parent_scope is None: + raise ValueError("parent_scope is None. Internal reference error. This should never happen.") + if root_scope is None: + raise ValueError("root_scope is None. Internal reference error. This should never happen.") + + return base._unmarked(line, current_scope, parent_scope, root_scope, self.ignore_main) def get_match_function(self, child: Token_List) -> Callable[[Token_List, Optional[Scope], Optional[Scope], Scope], Processed_Line]: match = Scope.get_match(child, tuple(base.KEYWORDS.keys())) if match: - return base.KEYWORDS[match]["parser"] + return base.KEYWORDS[match]["parser"] # type: ignore else: return self.parse_non_keyword - def __transpile(self, child: Scope | Token_List = None) -> None: + def __transpile(self, child: Scope | Token_List) -> None: if isinstance(child, Scope): - self.scope_stack.append(child) # push - - #with ThreadPoolExecutor() as executor: - # # Submit all transpile tasks to the executor - # futures = [executor.submit(self.__transpile, child) for child in child.children] - # - # # Gather the results - # [future.result() for future in futures] - - [self.__transpile(child) for child in child.children] # recursive call - - self.scope_stack.pop() # pop - self.__add_from_queue(child) # add any lines that were added to the queue (so at the end of the current scope) + self.scope_stack.append(child) # push + for child_scope in child.children: + self.__transpile(child_scope) # recursive call + self.scope_stack.pop() # pop + self.__add_from_queue(child) # add any lines that were added to the queue elif isinstance(child, Token_List): if self.root_scope is None: raise ValueError("root_scope is None. Internal reference error. This should never happen.") - self.transpiled.append(self.get_match_function(child)(child, self.scope_stack[-1] if len(self.scope_stack) >= 1 else self.root_scope, self.scope_stack[-2] if len(self.scope_stack) >= 2 else self.root_scope, self.root_scope)) + matched_func = self.get_match_function(child) + self.transpiled.append(matched_func(child, self.scope_stack[-1] if self.scope_stack else self.root_scope, self.scope_stack[-2] if len(self.scope_stack) > 1 else self.root_scope, self.root_scope)) - def append_at_end(self, line: Processed_Line, current_scope: Scope): + def append_at_end(self, line: Processed_Line, current_scope: Scope) -> None: self.add_to_transpiled_queue.append((line, current_scope)) - def __add_from_queue(self, child: Scope): + def __add_from_queue(self, child: Scope) -> None: if self.add_to_transpiled_queue: self.transpiled.extend([item[0] for item in self.add_to_transpiled_queue if item[1] is child]) self.add_to_transpiled_queue = [item for item in self.add_to_transpiled_queue if item[1] is not child] - def reset(self): + def reset(self) -> None: self.copied_scope = None - self.root_scope = None - self.scope_stack = [] - self.transpiled = [] + self.root_scope = None + self.scope_stack = [] + self.transpiled = [] self.add_to_transpiled_queue = [] - def transpile(self, root_scope: Scope, ignore_main: bool = False): + def transpile(self, root_scope: Scope, ignore_main: bool = False) -> list[Processed_Line]: if self.transpiled: color_print("WARNING: Transpiler state was not reset before transpiling. This may cause unexpected behavior. Reset the state with Transpiler.reset() before transpiling.", border=True, style="rgb(255, 155, 50)", word_wrap=True) - self.root_scope = root_scope - self.scope_stack = [] - self.ignore_main = ignore_main + self.root_scope = root_scope + self.ignore_main = ignore_main self.root_scope.transpiler_instance = self - [self.__transpile(child) for child in root_scope.children] + for child in root_scope.children: + self.__transpile(child) self.__add_from_queue(root_scope) - return self.transpiled \ No newline at end of file + return self.transpiled diff --git a/src/core/imports.py b/src/core/imports.py index ef01592..f363599 100644 --- a/src/core/imports.py +++ b/src/core/imports.py @@ -89,6 +89,7 @@ import toml from multimethod import subtype +import src.lib.subinterpreter as subinterpreter # multi imports - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # diff --git a/src/core/utils.py b/src/core/utils.py index 27dec1b..887b7c7 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -1,3 +1,4 @@ +import functools from src.core.base import ( Processed_Line, Token_List, @@ -9,6 +10,7 @@ file_cache, KEYWORDS, Thread, + ) def multi_split( @@ -126,7 +128,7 @@ def run_thread(func, args, kwargs): func._result = func(*args, **kwargs) func._thread_started = False - @wraps(func) + @functools.wraps(func) def wrapper(*args, **kwargs): if not hasattr(func, "_thread_started") or not func._thread_started: func._thread = Thread(target=run_thread, args=(func, args, kwargs)) @@ -140,6 +142,6 @@ def join(timeout=None): return getattr(func, "_result", None) return None - func.join = join - wrapper.join = join + setattr(func, "join", join) + setattr(wrapper, "join", join) return wrapper diff --git a/src/functions/_include.py b/src/functions/_include.py index 305f54c..6c8dfc1 100644 --- a/src/functions/_include.py +++ b/src/functions/_include.py @@ -1,3 +1,4 @@ +from typing import Optional from src.core.imports import ( Processed_Line, Token_List, @@ -46,6 +47,7 @@ def identify_import_type(combined_line: str, ast_list: Token_List) -> dict: match["type"] = match["type"].upper() return match panic(SyntaxError(f"Invalid include statement: {combined_line}"), file=ast_list.file, line_no=ast_list.line[0].line_number) + return {} def _include(ast_list: Token_List, current_scope, parent_scope, root_scope) -> Processed_Line: combined_line: str = ' '.join([_.token for _ in ast_list.line]) @@ -53,7 +55,8 @@ def _include(ast_list: Token_List, current_scope, parent_scope, root_scope) -> P type: str = match["type"] path: str = match["path"] - alias: str = match["alias"].strip() if match["alias"] else False + alias: Optional[str] = match["alias"] + alias = alias.strip() if alias else None modules: list[str] = match["modules"] import_statement = "" diff --git a/src/functions/inject_core.py b/src/functions/inject_core.py index 953e56d..dc9ace3 100644 --- a/src/functions/inject_core.py +++ b/src/functions/inject_core.py @@ -270,7 +270,7 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook threading.excepthook = functools.partial(exception_handler, thread_error=True) sys.argv = ["{os.path.realpath(__file__).replace(back_slash, os.sep+os.sep)}", "{os.path.realpath(_cls_ref.__file__).replace(back_slash, os.sep+os.sep)}"] + list(sys.argv)[2:] del os, threading, functools -overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore +overload_with_type_check = beartype(None, conf=BeartypeConf(is_color=False)) # type: ignore \x92 diff --git a/src/lib/src/subinterpreter.c b/src/lib/src/subinterpreter.c new file mode 100644 index 0000000..47d5e7b --- /dev/null +++ b/src/lib/src/subinterpreter.c @@ -0,0 +1,75 @@ +// compile cmd: "c:\Programing Languages\LLVM\bin\gcc.exe" -Wall -shared -fPIC -I C:\Python312\include -o src/lib/subinterpreter.dll src/lib/src/subinterpreter.c -L C:\Python312\libs -lpython312 +#include + +static PyObject* create_sub_interpreter(PyObject *self, PyObject *args) { + PyThreadState* interpreter_state = Py_NewInterpreter(); + if (interpreter_state == NULL) { + PyErr_SetString(PyExc_RuntimeError, "Failed to create new interpreter"); + return NULL; + } + return PyLong_FromVoidPtr(interpreter_state); +} + +static PyObject* run_code(PyObject *self, PyObject *args) { + PyThreadState* interpreter_state; + const char* path_to_compiled_code; + FILE* file; + + if (!PyArg_ParseTuple(args, "Ls", &interpreter_state, &path_to_compiled_code)) { + return NULL; + } + + PyThreadState* old_state = PyThreadState_Swap(interpreter_state); + + file = fopen(path_to_compiled_code, "r"); + if (file == NULL) { + PyErr_SetString(PyExc_FileNotFoundError, "Cannot open file"); + PyThreadState_Swap(old_state); + return NULL; + } + + PyRun_SimpleFile(file, path_to_compiled_code); + fclose(file); + + PyThreadState_Swap(old_state); + + Py_RETURN_NONE; +} + + +static PyObject* close_sub_interpreter(PyObject *self, PyObject *args) { + PyThreadState* interpreter_state; + + if (!PyArg_ParseTuple(args, "L", &interpreter_state)) { + return NULL; + } + + PyThreadState_Swap(interpreter_state); + Py_EndInterpreter(interpreter_state); + + Py_RETURN_NONE; +} + +static PyMethodDef SubInterpreterMethods[] = { + {"create_sub_interpreter", create_sub_interpreter, METH_NOARGS, "Create a new sub-interpreter"}, + {"run_code", run_code, METH_VARARGS, "Run code in a sub-interpreter"}, + {"close_sub_interpreter", close_sub_interpreter, METH_VARARGS, "Close a sub-interpreter"}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef subinterpretermodule = { + PyModuleDef_HEAD_INIT, + "subinterpreter", + NULL, + -1, + SubInterpreterMethods +}; + +PyMODINIT_FUNC PyInit_subinterpreter(void) { + return PyModule_Create(&subinterpretermodule); +} + + +// match [/]+.* +// \/\*(\*(?!\/)|[^*])*\*\/ + diff --git a/src/lib/subinterpreter.pyi b/src/lib/subinterpreter.pyi new file mode 100644 index 0000000..aa80e92 --- /dev/null +++ b/src/lib/subinterpreter.pyi @@ -0,0 +1,5 @@ +from typing import Any + +def create_sub_interpreter() -> Any: ... +def run_code(interpreter_state: Any, path_to_compiled_code: str) -> None: ... +def close_sub_interpreter(interpreter_state: Any) -> None: ... diff --git a/tests.py b/tests.py index 3c1be4d..e69de29 100644 --- a/tests.py +++ b/tests.py @@ -1,37 +0,0 @@ -import os -import time -import math - -A = 0 -B = 0 - -while True: - z = [0] * 1760 - b = [' '] * 1760 - for j in range(0, 628, 7): # j goes from 0 to 2 * pi, in steps of 0.07 - for i in range(0, 628, 2): # i goes from 0 to 2 * pi, in steps of 0.02 - c = math.sin(i) - d = math.cos(j) - e = math.sin(A) - f = math.sin(j) - g = math.cos(A) - h = d + 2 - D = 1 / (c * h * e + f * g + 5) - l = math.cos(i) - m = math.cos(B) - n = math.sin(B) - t = c * h * g - f * e - x = int(40 + 30 * D * (l * h * m - t * n)) - y = int(12 + 15 * D * (l * h * n + t * m)) - o = int(x + 80 * y) - N = int(8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n)) - if 0 <= y < 22 and 0 <= x < 80 and D > z[o]: - z[o] = D - b[o] = ".,-~:;=!*#$@"[N if N > 0 else 0] - - os.system('clear' if os.name == 'posix' else 'cls') - for k in range(1760): - print(b[k] if k % 80 else '\n', end='') - A += 0.04 - B += 0.02 - time.sleep(0.03) From c6abde77d4444c3e2da7a2540dd02a29248b1025 Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Mon, 18 Mar 2024 17:40:02 -0400 Subject: [PATCH 7/8] refactoring --- .helix/cache/build_cache/cappy_hlx.hir | 257 +++++++++++++++++++ .helix/cache/build_cache/cappy_hlx.hir.lines | 252 ++++++++++++++++++ .helix/cache/build_cache/test_hlx.hir | Bin 15296 -> 15263 bytes .helix/cache/build_cache/test_hlx.hir.lines | 63 +++-- .helix/cache/find_keyword.cache | Bin 0 -> 92 bytes .helix/include/core.py | 2 +- a.py | 1 - cache/find_keyword.cache | Bin 131 -> 92 bytes helix.py | 7 +- math.tex | 123 +++++++++ src/cache_store.py | 21 +- src/classes/HelixLanguage.py | 12 +- src/classes/Scope.py | 16 +- src/classes/Token.py | 4 +- src/core/base.py | 1 + src/core/imports.py | 1 + src/functions/_process.py | 90 +++---- src/functions/inject_core.py | 8 +- src/token/normalize_tokens.py | 44 ++-- src/token/remove_comments.py | 4 +- src/token/tokenize_file.py | 8 +- src/token/tokenize_line.py | 9 +- syntax/test.hlx | 11 +- 23 files changed, 787 insertions(+), 147 deletions(-) create mode 100644 .helix/cache/build_cache/cappy_hlx.hir create mode 100644 .helix/cache/build_cache/cappy_hlx.hir.lines create mode 100644 .helix/cache/find_keyword.cache delete mode 100644 a.py create mode 100644 math.tex diff --git a/.helix/cache/build_cache/cappy_hlx.hir b/.helix/cache/build_cache/cappy_hlx.hir new file mode 100644 index 0000000..0e13c9c --- /dev/null +++ b/.helix/cache/build_cache/cappy_hlx.hir @@ -0,0 +1,257 @@ +# trunk-ignore-all(black) +# trunk-ignore-all(isort) +# -------------------------------------------------------------------------------- +# GENERATED FILE +# -------------------------------------------------------------------------------- +# Filename: cappy.hlx +# Generation Date: 2024-03-17 18:52:09 +# Generator: Helix Transpiler +# -------------------------------------------------------------------------------- +# WARNING: This file is AUTO-GENERATED by the Helix Transpiler. Any modifications +# made directly to this file may be OVERWRITTEN during future compilations. To +# introduce changes, please modify the source files and recompile. +# -------------------------------------------------------------------------------- +# Licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0) +# SPDX-License-Identifier: CC-BY-4.0 +# License Details: https://creativecommons.org/licenses/by/4.0/ +# +# By using this file, you agree to the Terms and Conditions of the License. +# -------------------------------------------------------------------------------- +# Helix Version: 0.1.0-alpha.a +# Repository: https://github.com/kneorain/helix +# Documentation: https://kneorain.github.io/helix/ +# For further assistance, contact the development team or refer to project documentation. +# -------------------------------------------------------------------------------- +from __future__ import annotations # type: ignore +from beartype.door import is_bearable as is_typeof, is_subhint as is_sub_typeof # type: ignore +from beartype import beartype, BeartypeConf # type: ignore +###### from include.plum.plum import dispatch as overload_with_type_check +import os # type: ignore +import sys # type: ignore +import types # type: ignore +sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py")) + os.sep + ".helix") # type: ignore +sys.path.append(os.path.dirname(os.path.realpath("z:\\devolopment\\helix\\helix-lang\\helix.py"))) # type: ignore +sys.path.append(os.path.dirname(os.path.realpath(os.getcwd()))) # type: ignore +# trunk-ignore(ruff/F401) +from include.core import ABC, Any, BuiltinFunctionType, C_For, Callable, DEFAULT_VALUE, DispatchError, Enum, FastMap, FunctionType, Infix, Iterator, Literal, NoReturn, NoneType, Optional, Self, T, Thread, Type, TypeVar, UnionType, __import_c__, __import_cpp__, __import_py__, __import_rs__, annotations, array, auto, beartype, class_type_check_decorator, dataclass, double, getcontext, hx__abstract_method, hx__async, hx__multi_method, hx_array, hx_bool, hx_bytes, hx_char, hx_double, hx_float, hx_int, hx_list, hx_map, hx_set, hx_string, hx_tuple, hx_unknown, hx_void, inspect, multimeta, panic, partial, printf, ref, replace_primitives, scanf, sleep, std, string, subtype, unknown, void, wraps # type: ignore +# trunk-ignore(ruff/F401) +# trunk-ignore(ruff/F811) +from include.core import __import_c__, __import_cpp__, __import_py__, __import_rs__ # type: ignore +import threading # type: ignore +import functools # type: ignore +__exit__ = sys.exit +_lock = threading.Lock() +__file__ = "Z:\\devolopment\\helix\\helix-lang\\syntax\\cappy.hlx" +# trunk-ignore(ruff/F821) +def exception_handler(exception_type: type[BaseException] | threading.ExceptHookArgs, exception: Optional[Exception] = None, tb: Optional[types.TracebackType] = None, debug_hook: bool = False, thread_error: bool = False): + import traceback + import linecache + import os + from include.core import _H_tokenize_line__ + from beartype.roar import BeartypeException + + print() + thread_name = None + if thread_error and exception_type is not None: + thread_name = exception_type.thread.name # type: ignore + exception = exception_type.exc_value # type: ignore + tb = exception_type.exc_traceback # type: ignore + exception_type = exception_type.exc_type # type: ignore + stack = traceback.extract_tb(tb) + stack = traceback.StackSummary([frame for frame in stack if not frame.filename.startswith("<@beartype(")]) + exception = TypeError(str(exception).replace("type hint", "type")) if isinstance(exception, BeartypeException) else exception + current_exception = exception + relevant_frames = [] + early_replacements = dict((v, k) for k, v in {'...': 'None', '??': 'if', '|:': 'else', 'true': 'True', 'false': 'False', 'null': 'None', 'none': 'None', 'await': '_await', '&&': 'and', '||': 'or', '!': 'not', '===': 'is', '!==': 'is not', 'stop': 'break', '::': '.', 'new': '__init__', 'delete': '__del__', 'enter': '__enter__', 'exit': '__exit__', 'u8': 'hx_u8', 'u16': 'hx_u16', 'u32': 'hx_u32', 'u64': 'hx_u64', 'u128': 'hx_u128', 'i8': 'hx_i8', 'i16': 'hx_i16', 'i32': 'hx_i32', 'i64': 'hx_i64', 'i128': 'hx_i128', 'f32': 'hx_f32', 'f64': 'hx_f64', 'f128': 'hx_f128'}.items()) + # First loop: filter out irrelevant frames + index = 0 + for frame in stack: + filename = frame.filename + line_no = frame.lineno + if "_hlx" in os.path.basename(filename): + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + try: + pass + except IndexError: + print("TODO: approximate corresponding line number in .hlx file") + panic( + LookupError(f"Could not find the corresponding line number in the .hlx file for the line {frame.lineno} in the generated file {frame.filename}. Please report this issue." + + f"line={frame.lineno}, file={frame.filename}" + + f"linecache.getline({frame.filename}, {frame.lineno})"), + line_no=1, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + no_lines=True, + multi_frame=True, + lang="py" + ) + continue + except FileNotFoundError: + panic( + FileNotFoundError(f"The file {frame.filename} was not found. Please ensure that the file exists and is accessible." + "You should never see this error. If you do, something went really wrong."), + line_no=1, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + no_lines=True, + lang="py" + ) + + if line_no == -1: + continue + if line_no == -1: + continue + + if ";#\"\"\"REMOVE-STACK\"\"\"#" in linecache.getline(filename, line_no).strip(): + continue + + if ( + linecache.getline(filename, line_no-1).strip() == "def hx_internal_multi_method_decorator(*args, **kwargs):" # type: ignore + and + linecache.getline(filename, line_no-3).strip() == "def hx__multi_method(func: Callable) -> Callable:" # type: ignore + ): + continue + + relevant_frames.append((index, frame)) + index += 1 + if len(relevant_frames) > 1: + _lock.acquire(blocking=True, timeout=1.2) + for frame_info in relevant_frames: + index, frame = frame_info + filename = frame.filename + line_no = frame.lineno + if "_hlx" in os.path.basename(filename): + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): # type: ignore + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + try: + file_ext = os.path.basename(filename).split('.')[1] # type: ignore + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + multi_frame=True, + pos=0 if index == 0 else 1 if index < len(relevant_frames) - 1 else 2, + thread_name=thread_name, + lang=file_ext + ) + current_exception = current_exception.__cause__ if current_exception.__cause__ else current_exception # type: ignore + else: + _lock.release() + exit(1) + else: + _lock.acquire(blocking=True, timeout=0.1) + index, frame = relevant_frames[0] + filename = frame.filename + line_no = frame.lineno + if "_hlx" in filename: + filename = __file__ + line_no = int(open(frame.filename + ".lines", "r").readlines()[line_no-1]) # type: ignore + # Attempt to find the marked part in the error message + # see if the frame contains colno and end_colno + marks = None + if hasattr(frame, "colno") and hasattr(frame, "end_colno"): + marks = list(_H_tokenize_line__(frame._line[frame.colno:frame.end_colno])) # type: ignore + try: + file_ext = os.path.basename(filename).split('.')[1] + except IndexError: + file_ext = "py" + if marks: + panic( + current_exception, # type: ignore + *marks, + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + follow_marked_order=True, + mark_start=frame.colno, + thread_name=thread_name, + lang=file_ext + ) + else: + panic( + current_exception, # type: ignore + file=filename, + line_no=line_no, # type: ignore + replacements=early_replacements, + thread_name=thread_name, + lang=file_ext + ) + _lock.release() + exit(1) +sys.excepthook = exception_handler # type: ignore +threading.excepthook = functools.partial(exception_handler, thread_error=True) +sys.argv = ["Z:\\devolopment\\helix\\helix-lang\\helix.py", "Z:\\devolopment\\helix\\helix-lang\\syntax\\cappy.hlx"] + list(sys.argv)[2:] +del os, threading, functools +overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore +import os +import time +import math +@overload_with_type_check +def main(): + A: float | hx_float = float(0.0) + B: float | hx_float = float(0.0) + while True : + z: list[float | hx_float] | hx_list[float | hx_float] = list([ 0.0 ] * 1760) + b: list[str | hx_string] | hx_list[str | hx_string] = list([ " " ] * 1760) + for j in range ( 0 , 628 , 7 ): + for i in range ( 0 , 628 , 2 ): + c: float | hx_float = float(math . sin ( i )) + d: float | hx_float = float(math . cos ( j )) + e: float | hx_float = float(math . sin ( A )) + f: float | hx_float = float(math . sin ( j )) + g: float | hx_float = float(math . cos ( A )) + h: float | hx_float = float(d + 2) + D: float | hx_float = float(1 / ( c * h * e + f * g + 5 )) + l: float | hx_float = float(math . cos ( i )) + m: float | hx_float = float(math . cos ( B )) + n: float | hx_float = float(math . sin ( B )) + t: float | hx_float = float(c * h * g - f * e) + x: int | hx_int = (int ( 40 + 30 * D * ( l * h * m - t * n ) )) + y: int | hx_int = (int ( 12 + 15 * D * ( l * h * n + t * m ) )) + o: int | hx_int = (int ( x + 80 * y )) + N: int | hx_int = (int ( 8 * ( ( f * e - c * d * g ) * m - c * d * e - f * g - l * d * n ) )) + if (0 <= y < 22 and 0 <= x < 80 and D > z [ o ]): + z [ o ] = D + if (N > 0): + charIndex: int | hx_int = int(N) + else: + charIndex: int | hx_int = int(0) + b [ o ] = ".,-~:;=!*#$@" [ charIndex ] + os . system ( "cls" ) + for k in C_For(k = int(0)).set_con('k < 1760').set_inc('k ++'): + if (k % 80 == 0): + print ( '\n' , end = '' ) + else: + print ( b [ k ] , end = '' ) + A += 0.04 + B += 0.02 + time . sleep ( 0.03 ) +if __name__ == "__main__": + try: + main() # type: ignore + except (KeyError, TypeError): + main(sys.argv) diff --git a/.helix/cache/build_cache/cappy_hlx.hir.lines b/.helix/cache/build_cache/cappy_hlx.hir.lines new file mode 100644 index 0000000..116c0d2 --- /dev/null +++ b/.helix/cache/build_cache/cappy_hlx.hir.lines @@ -0,0 +1,252 @@ +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +-1 +1 +1 +1 +2 +2 +2 +2 +3 +3 +4 +4 +5 +5 +5 +6 +6 +6 +7 +7 +8 +9 +9 +10 +10 +11 +12 +13 +14 +14 +14 +15 +15 +15 +16 +16 +17 +18 +18 +18 +19 +20 +20 +21 +-1 +-1 +-1 +-1 +-1 \ No newline at end of file diff --git a/.helix/cache/build_cache/test_hlx.hir b/.helix/cache/build_cache/test_hlx.hir index d60233c4d23af876d10eb9b248a729c23d1b6b49..c66b828f2eaae4e0a28860fd61d9093b4d996f7d 100644 GIT binary patch delta 2852 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 None: style="red", ) - import py_compile - py_compile.compile(self.__out_file__, cfile=self.__out_file__, optimize=2) + if base.COMPILE: + py_compile.compile(self.__out_file__, cfile=self.__out_file__, optimize=2) self.timer.start("run") try: @@ -610,7 +611,7 @@ def exit_func(*args: Any) -> None: if __name__ == "__main__": Helix.factory( os.path.join(".helix", "config.toml"), - profile=False, + profile=True, ) #Helix.__hook_import__("syntax/test.hlx") # from test_hlx import subtract diff --git a/math.tex b/math.tex new file mode 100644 index 0000000..a8ea05f --- /dev/null +++ b/math.tex @@ -0,0 +1,123 @@ +\documentclass{article} + +\begin{document} + +\title{Discrete Math - Extra Credit Assignment - Week 8.1} % proof by induction +\author{Dhruvan} +\date{} +\maketitle + +\section*{Induction Templates} +Ind.base (n = 1): . . . \\ +Ind.step (n 7 → n + 1): . . . \\ + + +%% ------------------------ Question 1 ------------------------ %% +\section{Question} +Prove for all \({n ≥ 1: 1 + 2 + 3 + ... + n = \frac{n^2}{2} + \frac{n}{2}}\) \\ \\ +% prove for n = 1 +Step 1: prove for n = 1 \\ +% 1 = (1^2 + 1)/2 (indent by 4 spaces) +\begin{quote} +\(1 = \frac{1^2 + 1}{2}\) \\ +% 1 = 1 +\(1 = 1\) +\end{quote} + +% assume true for n = k +Step 2: assume true for n = k \\ +% 1 + 2 + 3 ... +k = (k^2 + k)/2 +\begin{quote} +\(1 + 2 + 3 + ... + k = \frac{k^2 + k}{2}\) +\end{quote} + +% prove for n = k + 1 +Step 3: prove for n = k + 1 \\ +% right side of the page: ((k^2+k)/2) + (k + 1) (induction step) +\begin{quote} +\(1 + 2 + 3 + ... + k + (k + 1)\) \\ +\(\frac{k^2 + k}{2} + (k + 1)\) (induction step) \\ +% right side of the page: (k^2 + k + 2k + 2) +\(= {k^2 + k + 2k + 2}\) \\ +% right side of the page: (k^2 + 3k + 2)/2 +\(= \frac{k^2 + 3k + 2}{2}\) \\ +% right side of the page: ((k+1)^2 + (k+1)/2)/2 +\(= \frac{(k+1)^2 + (k+1)}{2}\) +\end{quote} + +%% ------------------------ Question 2 ------------------------ %% +\section{Question} +Prove for all \({n ≥ 1: 1 + 3 + 5 + ... + (2n - 1) = n^2}\) \\ \\ +Step 1: prove for n = 1 \\ +\begin{quote} +\(1 = 1^2\) \\ +\(1 = 1\) +\end{quote} + +Step 2: assume true for n = k \\ +\begin{quote} +\(1 + 3 + 5 + . . . + (2k - 1) = k^2\) +\end{quote} + +Step 3: prove for n = k + 1 \\ +\begin{quote} +\(1 + 3 + 5 + . . . + (2k - 1) + ( 2 (k + 1) - 1)\) \\ +\(k^2 + (2k + 1)\) (induction step) \\ +\(= k^2 + 2k + 1\) \\ +\(= (k + 1)^2\) +\end{quote} + +%% ------------------------ Question 3 ------------------------ %% +\section{Question} +Prove for all \({n ≥ 1: 3^n > n}\) \\ \\ +Step 1: prove for n = 1 \\ +\begin{quote} +\(3^1 > 1\) \\ +\(3 > 1\) +\end{quote} + +Step 2: assume true for n = k \\ +\begin{quote} +\(3^k > k\) +\end{quote} + +Step 3: prove for n = k + 1 \\ +\begin{quote} +\(3^{k+1} > k + 1\) \\ +\(3 \cdot 3^k > k + 1\) \\ +\(3 \cdot k > k + 1\) (induction step)\\ + +\end{quote} + +%% ------------------------ Question 4 ------------------------ %% +\section{Question} +Prove for all \({n ≥ 1: 2 + 5 + 8 + ... + (3n - 1) = \frac{n(3n + 1)}{2}}\) \\ \\ +% prove for n = 1 +Step 1: prove for n = 1 \\ +\begin{quote} +\(2 = \frac{1(3 \cdot 1 + 1)}{2}\) \\ +\(2 = 2\) +\end{quote} + +% assume true for n = k +Step 2: assume true for n = k \\ +\begin{quote} +\(2 + 5 + 8 + ... + (3k - 1) = \frac{k(3k + 1)}{2}\) +\end{quote} + +% prove for n = k + 1 +Step 3: prove for n = k + 1 \\ +\begin{quote} +\(2 + 5 + 8 + ... + (3k - 1) + (3(k + 1) - 1)\) \\ +\(\frac{k(3k + 1)}{2} + (3k + 3 - 1)\) (induction step) \\ +\(= \frac{k(3k + 1)}{2} + 3k + 2\) \\ +\(= \frac{3k^2 + k + 6k + 4}{2}\) \\ +\(= \frac{3k^2 + 7k + 4}{2}\) \\ +\(= \frac{(3k^2 + 3k) + (4k + 4)}{2}\) \\ +\(= \frac{3k(k + 1) + 4(k + 1)}{2}\) \\ +\(= \frac{(k + 1)(3k + 4)}{2}\) \\ +\(= \frac{(k + 1)(3(k + 1) + 1)}{2}\) +\end{quote} + + +\end{document} \ No newline at end of file diff --git a/src/cache_store.py b/src/cache_store.py index b9ee8c6..c8e3587 100644 --- a/src/cache_store.py +++ b/src/cache_store.py @@ -1,27 +1,26 @@ -DISABLED: bool = False - +import src.core.base as base from src.core.imports import ( dump, load, wraps, - Callable, Tuple, Union, + Callable, Tuple, Union, Any, lru_cache, - os, + os, CONFIG ) CACHE_DIR: str = os.path.join(os.path.dirname(os.path.dirname(__file__)), "cache") -local_cache: dict[str, any] = {} +local_cache: dict[str, Any] = {} if not os.path.isdir(CACHE_DIR): os.mkdir(CACHE_DIR) -def async_cache(path: str, args: tuple, result: any) -> None: +def async_cache(path: str, args: tuple, result: Any) -> None: global local_cache - local_cache[args] = result + local_cache[str(args)] = result with open(path, "wb") as file: dump(local_cache, file) -def get_cache(path: str, args: tuple) -> Union[any, None]: +def get_cache(path: str, args: tuple) -> Union[Any, None]: global local_cache if os.path.isfile(path): @@ -39,8 +38,8 @@ def cache(func: Callable) -> Callable: def file_cache(func: Callable) -> Callable: @wraps(func) - def wrapper(*args: Tuple[any, ...], **kwargs: dict[str, any]) -> any: - if DISABLED: return func(*args, **kwargs) + def wrapper(*args: Tuple[Any, ...], **kwargs: dict[str, Any]) -> Any: + if not base.USE_CACHE: return func(*args, **kwargs) global local_cache @@ -53,7 +52,7 @@ def wrapper(*args: Tuple[any, ...], **kwargs: dict[str, any]) -> any: if result: return result - result: any = func(*args, **kwargs) + result: Any = func(*args, **kwargs) async_cache(cache_file_path, args, result) return result diff --git a/src/classes/HelixLanguage.py b/src/classes/HelixLanguage.py index 971ee0e..3f1aa28 100644 --- a/src/classes/HelixLanguage.py +++ b/src/classes/HelixLanguage.py @@ -6,6 +6,7 @@ color_print as print, framework, ctypes, + shutil ) # --- c imports --- # @@ -138,8 +139,15 @@ def install_helix(config: dict) -> None: @staticmethod def remove_blank_lines( - file: str, hash: Optional[Hashing] # type: ignore + file: str, hash: Hashing | None ) -> None: - c_remove_blank_lines(ctypes.c_char_p(file.encode("utf-8"))) + with open(file, "r") as read_file, open( + file + ".tmp", "w" + ) as write_file: + for line in read_file: + if line.strip(): + write_file.write(line) + + shutil.move(file + ".tmp", file) if hash: hash.create_hash_only() diff --git a/src/classes/Scope.py b/src/classes/Scope.py index 67ac207..14d6521 100644 --- a/src/classes/Scope.py +++ b/src/classes/Scope.py @@ -113,8 +113,8 @@ def process_scope_from_scope(cls, scope: 'Scope') -> 'Scope': in_scope = False for index in range(len(scope.children)): - if not in_scope and cls.contains(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys())) and temp_scope.indent_level != scope.children[index].indent_level: - _temp: Optional[str] = cls.get_match(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys())) + if not in_scope and cls.contains(scope.children[index], tuple(tuple(base.NAMESPACED_KEYWORD.keys()))) and temp_scope.indent_level != scope.children[index].indent_level: + _temp: Optional[str] = cls.get_match(scope.children[index], tuple(tuple(base.NAMESPACED_KEYWORD.keys()))) if _temp is None: raise ValueError(f"Expected a match, got {_temp}") @@ -128,8 +128,8 @@ def process_scope_from_scope(cls, scope: 'Scope') -> 'Scope': temp_scope.append_child(new_scope) new_scope = None in_scope = False - if cls.contains(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys())): - new_scope = Scope(scope.children[index], base.NAMESPACED_KEYWORD[cls.get_match(scope.children[index], tuple(base.NAMESPACED_KEYWORD.keys()))], [scope.children[index]], scope.children[index].indent_level) + if cls.contains(scope.children[index], tuple(tuple(base.NAMESPACED_KEYWORD.keys()))): + new_scope = Scope(scope.children[index], base.NAMESPACED_KEYWORD[cls.get_match(scope.children[index], tuple(tuple(base.NAMESPACED_KEYWORD.keys())))], [scope.children[index]], scope.children[index].indent_level) new_scope.namespace_header = scope.children[index] in_scope = True continue @@ -152,8 +152,8 @@ def process_from_lines(cls, lines: tuple[Token_List, ...]) -> 'Scope': in_scope = False for index in range(len(lines)): - if not in_scope and cls.contains(lines[index], base.NAMESPACED_KEYWORD.keys()): - new_scope = Scope(lines[index], base.NAMESPACED_KEYWORD[cls.get_match(lines[index], base.NAMESPACED_KEYWORD.keys())], [lines[index]], lines[index].indent_level) + if not in_scope and cls.contains(lines[index], tuple(base.NAMESPACED_KEYWORD.keys())): + new_scope = Scope(lines[index], base.NAMESPACED_KEYWORD[cls.get_match(lines[index], tuple(base.NAMESPACED_KEYWORD.keys()))], [lines[index]], lines[index].indent_level) new_scope.namespace_header = lines[index] in_scope = True continue @@ -162,8 +162,8 @@ def process_from_lines(cls, lines: tuple[Token_List, ...]) -> 'Scope': root_scope.append_child(new_scope) new_scope = None in_scope = False - if cls.contains(lines[index], base.NAMESPACED_KEYWORD.keys()): - new_scope = Scope(lines[index], base.NAMESPACED_KEYWORD[cls.get_match(lines[index], base.NAMESPACED_KEYWORD.keys())], [lines[index]], lines[index].indent_level) + if cls.contains(lines[index], tuple(base.NAMESPACED_KEYWORD.keys())): + new_scope = Scope(lines[index], base.NAMESPACED_KEYWORD[cls.get_match(lines[index], tuple(base.NAMESPACED_KEYWORD.keys()))], [lines[index]], lines[index].indent_level) new_scope.namespace_header = lines[index] in_scope = True continue diff --git a/src/classes/Token.py b/src/classes/Token.py index 41de7fa..d85489d 100644 --- a/src/classes/Token.py +++ b/src/classes/Token.py @@ -35,7 +35,9 @@ def original_line(self, value: str) -> None: @property - def line(self) -> list[str] | str: + def line(self) -> list[str]: + if isinstance(self.__processed_line, str): + raise ValueError("Line is a string, use the token property to get the string") return self.__processed_line @line.setter diff --git a/src/core/base.py b/src/core/base.py index 5dedc32..786628f 100644 --- a/src/core/base.py +++ b/src/core/base.py @@ -110,6 +110,7 @@ def clean_docstring(docstring: str) -> str: POOL: WorkerPool = WorkerPool(50) USE_POOL: bool = True USE_CACHE: bool = True +COMPILE: bool = True # 1 DOWNSIDE: precision error marking is not possible LINE_BREAK: str = "\x03" SUB_LINE_BREAK: str = "\x04" diff --git a/src/core/imports.py b/src/core/imports.py index f363599..786f248 100644 --- a/src/core/imports.py +++ b/src/core/imports.py @@ -18,6 +18,7 @@ import functools import threading import subprocess +import py_compile import dataclasses from multiprocessing import cpu_count diff --git a/src/functions/_process.py b/src/functions/_process.py index ce6aac6..249586d 100644 --- a/src/functions/_process.py +++ b/src/functions/_process.py @@ -5,51 +5,51 @@ Token_List, ) -def _match(ast_list: Token_List, current_scope, parent_scope, root_scope) -> Token_List: +def _match(ast_list: Token_List, current_scope, parent_scope, root_scope) -> Processed_Line: return Processed_Line("JOSH!!!!!!!!!!!!!!!!!!!!!!!!", ast_list) -if __name__ == "__main__": - root_scope = Scope("root", "root", None, 0) - root_scope.operator_functions["add"] = "+" - root_scope.operator_functions["sub"] = "-" - root_scope.operator_functions["greaterThan"] = ">" - root_scope.operator_functions["lessThan"] = "<" - - # n > (m + a) < 10 -> greaterThan(lessThan(n, add(m, a)), 10) - - ## Test 1 - test_line = ["let", "i", ":", "int", "=", "n", ">", "(", "m", "+", "a", ")", "<", "10"] - ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") - print("input:", repr(ast_list.full_line())) - output = _match(ast_list, None, None, root_scope) - print("output (python code):", repr(ast_list)) # should be ["let", "i", ":", "int", "=", "greaterThan(lessThan(n, add(m, a)), 10)"] - - ## Test 2 - test_line = ["return", "n", ">", "10"] - ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") - print("input:", repr(ast_list.full_line())) - output = _match(ast_list, None, None, root_scope) - print("output (python code):", repr(ast_list)) # should be ["return", "greaterThan(n, 10)"] - - ## Test 3 - test_line = ["print", "(", "(", "n", ">", "10", ")", "or", "(", "m", "<", "10", ")", ")"] - ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") - print("input:", repr(ast_list.full_line())) - output = _match(ast_list, None, None, root_scope) - print("output (python code):", repr(ast_list)) # should be ["print", "(", or(greaterThan(n, 10), lessThan(m, 10)), ")"] - - ## Test 4 - test_line = ["print", "(", "i", "++", ")"] - ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") - print("input:", repr(ast_list.full_line())) - output = _match(ast_list, None, None, root_scope) - print("output (python code):", repr(ast_list)) # should be ["print", "(", "increment(i)", ")"] - - - ## Test 5 - test_line = ["print", "(", "add", "(", "n", ",", "m", ")", "-", "23", ")"] - ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") - print("input:", repr(ast_list.full_line())) - output = _match(ast_list, None, None, root_scope) - print("output (python code):", repr(ast_list)) # should be ["print", "(", "subtract(add(n, m), 23)", ")"] \ No newline at end of file +#if __name__ == "__main__": +# root_scope = Scope("root", "root", None, 0) +# root_scope.operator_functions["add"] = "+" +# root_scope.operator_functions["sub"] = "-" +# root_scope.operator_functions["greaterThan"] = ">" +# root_scope.operator_functions["lessThan"] = "<" +# +# # n > (m + a) < 10 -> greaterThan(lessThan(n, add(m, a)), 10) +# +# ## Test 1 +# test_line = ["let", "i", ":", "int", "=", "n", ">", "(", "m", "+", "a", ")", "<", "10"] +# ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") +# print("input:", repr(ast_list.full_line())) +# output = _match(ast_list, None, None, root_scope) +# print("output (python code):", repr(ast_list)) # should be ["let", "i", ":", "int", "=", "greaterThan(lessThan(n, add(m, a)), 10)"] +# +# ## Test 2 +# test_line = ["return", "n", ">", "10"] +# ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") +# print("input:", repr(ast_list.full_line())) +# output = _match(ast_list, None, None, root_scope) +# print("output (python code):", repr(ast_list)) # should be ["return", "greaterThan(n, 10)"] +# +# ## Test 3 +# test_line = ["print", "(", "(", "n", ">", "10", ")", "or", "(", "m", "<", "10", ")", ")"] +# ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") +# print("input:", repr(ast_list.full_line())) +# output = _match(ast_list, None, None, root_scope) +# print("output (python code):", repr(ast_list)) # should be ["print", "(", or(greaterThan(n, 10), lessThan(m, 10)), ")"] +# +# ## Test 4 +# test_line = ["print", "(", "i", "++", ")"] +# ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") +# print("input:", repr(ast_list.full_line())) +# output = _match(ast_list, None, None, root_scope) +# print("output (python code):", repr(ast_list)) # should be ["print", "(", "increment(i)", ")"] +# +# +# ## Test 5 +# test_line = ["print", "(", "add", "(", "n", ",", "m", ")", "-", "23", ")"] +# ast_list = Token_List([Token("None", test_line[index], 1, 0) for index in range(len(test_line))], 0, "test.file") +# print("input:", repr(ast_list.full_line())) +# output = _match(ast_list, None, None, root_scope) +# print("output (python code):", repr(ast_list)) # should be ["print", "(", "subtract(add(n, m), 23)", ")"] \ No newline at end of file diff --git a/src/functions/inject_core.py b/src/functions/inject_core.py index dc9ace3..d60b4b0 100644 --- a/src/functions/inject_core.py +++ b/src/functions/inject_core.py @@ -134,13 +134,17 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook except IndexError: print("TODO: approximate corresponding line number in .hlx file") panic( - LookupError(f"Could not find the corresponding line number in the .hlx file for the line {{frame.lineno}} in the generated file {{frame.filename}}. Please report this issue."), + LookupError(f"Could not find the corresponding line number in the .hlx file for the line {{frame.lineno}} in the generated file {{frame.filename}}. Please report this issue." + + f"line={{frame.lineno}}, file={{frame.filename}}" + + f"linecache.getline({{frame.filename}}, {{frame.lineno}})"), line_no=1, # type: ignore replacements=early_replacements, thread_name=thread_name, no_lines=True, + multi_frame=True, lang="py" ) + continue except FileNotFoundError: panic( FileNotFoundError(f"The file {{frame.filename}} was not found. Please ensure that the file exists and is accessible." @@ -270,7 +274,7 @@ def exception_handler(exception_type: type[BaseException] | threading.ExceptHook threading.excepthook = functools.partial(exception_handler, thread_error=True) sys.argv = ["{os.path.realpath(__file__).replace(back_slash, os.sep+os.sep)}", "{os.path.realpath(_cls_ref.__file__).replace(back_slash, os.sep+os.sep)}"] + list(sys.argv)[2:] del os, threading, functools -overload_with_type_check = beartype(None, conf=BeartypeConf(is_color=False)) # type: ignore +overload_with_type_check = beartype(conf=BeartypeConf(is_color=False)) # type: ignore \x92 diff --git a/src/token/normalize_tokens.py b/src/token/normalize_tokens.py index 338c03b..741d2b6 100644 --- a/src/token/normalize_tokens.py +++ b/src/token/normalize_tokens.py @@ -38,15 +38,15 @@ def normalize_tokens(_lines: tuple[Token, ...], path: str) -> tuple[Token_List, previous_element: Token = Token("", "", 0, 0) # lines is expected to countian ['token', ...] for every token in the file NOT individual line - def process_line(index: int): - index = index[0] + def process_line(index: int | tuple[int, Token]) -> None: + index = index[0] if isinstance(index, tuple) else index nonlocal stack, indent_level, firs_inst, previous_element - token = lines[index].line - if token in base.BODY_REQUIRED_KEYWORDS.keys() and lines[index-1].line not in (base.find_keyword("ELSE"), ): + token = lines[index].token + if token in base.BODY_REQUIRED_KEYWORDS.keys() and lines[index-1].token not in (base.find_keyword("ELSE"), ): # append the line to the stack for i in range(index, len(lines)): - if lines[i].line == "{": + if lines[i].token == "{": break stack.append(lines[i]) indent_level += 1 @@ -55,15 +55,15 @@ def process_line(index: int): if not firs_inst: indent_level += 1 if len(stack) == indent_level: - lines[index].line = ":" + lines[index].token = ":" lines.insert(index + 1, Token(lines[index].original_line, "<\\n>", lines[index].line_number, lines[index].indent_level + 1)) firs_inst = False - if lines[index + 2].line == "}": - lines[index + 2].line = "}" + if lines[index + 2].token == "}": + lines[index + 2].token = "}" lines.insert(index + 3, Token(lines[index].original_line, "<\\r>", lines[index].line_number, lines[index].indent_level + 1)) elif token == "}": if len(stack) == indent_level: - lines[index].line = "<\\n>" + lines[index].token = "<\\n>" indent_level -= 1 try: previous_element = stack.pop() @@ -72,36 +72,36 @@ def process_line(index: int): else: indent_level -= 1 if token == "<\\r>": - lines[index - 5].line = "<\\r1>" + lines[index - 5].token = "<\\r1>" if token == ";": - lines[index].line = "<\\n>" - if lines[index].line == "<\\n>": + lines[index].token = "<\\n>" + if lines[index].token == "<\\n>": lines.insert(index + 1, Token(lines[index].original_line, f"<\\t:{indent_level}>", lines[index].line_number, lines[index].indent_level + 1)) - if token == "..." and not lines[index + 1].line == "<\\n>": + if token == "..." and not lines[index + 1].token == "<\\n>": lines.insert(index + 1, Token(lines[index].original_line, "<\\n>", lines[index].line_number, lines[index].indent_level + 1)) if token == "<\\r>": for i in range(index, -1, -1): - if lines[i].line == "<\\r1>": + if lines[i].token == "<\\r1>": break else: - lines[i].line = "" + lines[i].token = "" if token == ";": - lines[index].line = "<\\n>" + lines[index].token = "<\\n>" frozenset((process_line(_) for _ in enumerate(lines))) def process_for_loops(index: int) -> None: nonlocal in_for_loop, lines - token = lines[index].line + token = lines[index].token if token == "for": in_for_loop = True if in_for_loop: if token == "<\\n>": lines[index].token = ";" - if lines[index + 1].line.startswith("<\\t:"): - lines[index + 1].line = "" - elif token == ":" and (index + 1) < len(lines) and lines[index + 1].line == "<\\n>": + if lines[index + 1].token.startswith("<\\t:"): + lines[index + 1].token = "" + elif token == ":" and (index + 1) < len(lines) and lines[index + 1].token == "<\\n>": in_for_loop = False frozenset((process_for_loops(_) for _ in range(len(lines)))) @@ -118,7 +118,7 @@ def process_for_loops(index: int) -> None: indent_level = 0 def process_indent_level(ast_token: Token): nonlocal indent_level, lines, current_line, final_lines - token = ast_token.line + token = ast_token.token if token.startswith("<\\t:"): indent_level = int(token[4:-1]) return @@ -129,7 +129,7 @@ def process_indent_level(ast_token: Token): else: current_line.append(ast_token) - frozenset((process_indent_level(_) for _ in [_ for _ in lines if _.line])) + frozenset((process_indent_level(_) for _ in [_ for _ in lines if _.token])) if current_line: for i in current_line: i.indent_level = indent_level diff --git a/src/token/remove_comments.py b/src/token/remove_comments.py index 1e8edca..bb6983a 100644 --- a/src/token/remove_comments.py +++ b/src/token/remove_comments.py @@ -3,7 +3,7 @@ from src.config import CONFIG -re = __import__(CONFIG.Transpiler.regex_module) +import re from src.classes.Token import Token import src.core.base as base @@ -38,4 +38,4 @@ def remove_comment(_code: Token): if in_block_comment: code = "" - _code.line = code + _code.token = code diff --git a/src/token/tokenize_file.py b/src/token/tokenize_file.py index 20fdabe..688bfcd 100644 --- a/src/token/tokenize_file.py +++ b/src/token/tokenize_file.py @@ -9,7 +9,7 @@ class Tokenizer: _ = "Do not change; License: CC0 1.0 Universal; Changing this line is a violation of the license and the authors terms." - + @staticmethod def tokenize_file(path: str) -> tuple[Token_List, ...]: """ @@ -23,16 +23,14 @@ def tokenize_file(path: str) -> tuple[Token_List, ...]: """ if path in base.CACHE: - return base.CACHE[path] + return base.CACHE[path] with open(path, "r") as file: lines = tuple(Token(line, "", index + 1, 0) for index, line in enumerate(file)) if base.USE_POOL and len(lines) > 1000: - # Using multiprocessing.Pool or similar base.POOL.map(remove_comment, lines) # Batch operation - _tokenize_line = functools.partial(tokenize_line, path=path) - base.POOL.map(_tokenize_line, lines) # Batch operation + base.POOL.map(lambda line: tokenize_line(line, path), lines) else: tuple(map(remove_comment, lines)) tuple(map(lambda line: tokenize_line(line, path), lines)) diff --git a/src/token/tokenize_line.py b/src/token/tokenize_line.py index 03fdb96..66fdfdc 100644 --- a/src/token/tokenize_line.py +++ b/src/token/tokenize_line.py @@ -36,7 +36,7 @@ def compiled_re(ignore_strings: bool = False) -> re.Pattern: else: return COMPILED_RE @cache -def tokenize_line(code: Token | str, path: str = None, ignore_errors: bool = False, ignore_strings: bool = False) -> list[str]: +def tokenize_line(code: Token | str, path: Optional[str] = None, ignore_errors: bool = False, ignore_strings: bool = False) -> Optional[list[str]]: """ Tokenize a line of code. @@ -65,6 +65,9 @@ def tokenize_line(code: Token | str, path: str = None, ignore_errors: bool = Fal if token and not token.startswith(base.COMMENT) and not token.startswith(base.BLOCK_COMMENT) and not token.endswith(base.BLOCK_COMMENT) ] + if path is None: + raise ValueError("The path must be specified. Internal error") + [ panic( SyntaxError(f"Reserved keyword '{token}' used."), @@ -86,7 +89,11 @@ def tokenize_line(code: Token | str, path: str = None, ignore_errors: bool = Fal ] if flattened_tokens else [] if ignore_errors: + if not isinstance(code, list): + raise ValueError("The code must be a list. Internal error") + return code.line + def standalone_tokenize_line(line: str | Token) -> list[str]: import re diff --git a/syntax/test.hlx b/syntax/test.hlx index b64c57e..8023a70 100644 --- a/syntax/test.hlx +++ b/syntax/test.hlx @@ -85,17 +85,8 @@ class C_cout { } } -fn add(a: int, b: int) -> int { - printf("adding: %d + %d", a, b); - return a + b; -} fn main(argv: list) { - let _add: Infix = Infix(add); - - print(2 |_add| 3); - - exit(); let a: C_cout = C_cout(); let b: C_cout = a; a << "hello world"; @@ -109,7 +100,7 @@ fn main(argv: list) { for (var i: int = 0; i < 10; i++) { printf("doing something else eeeee: %d", i); } - + ~~ TODO: i broke something here await is not working print(do_something.await()); print("done"); From aeeaf8644a8ddd41adfa6e7823722b737cd63a45 Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Mon, 18 Mar 2024 21:21:22 -0400 Subject: [PATCH 8/8] Refactor C_Interop class in c_interop.py --- c_interop.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/c_interop.py b/c_interop.py index 03f4923..4de99af 100644 --- a/c_interop.py +++ b/c_interop.py @@ -4,4 +4,29 @@ ) class C_Interop(framework.Interop): - \ No newline at end of file + cache_dir: str + input_file: str + output_file: str + #hash: Hashing + + + def __init__(self, input_file: str, *attrs: str) -> None: + pass + + def __getattr__(self, attr: str) -> Any: + pass + + def __setattr__(self, attr: str, value: Any) -> None: + raise AttributeError("Cannot set attribute") + + def __delattr__(self, attr: str) -> None: + pass + + def __compile__(self) -> None: + pass + + def get(self) -> tuple[ctypes.CDLL._FuncPtr, ...]: + pass + + def __del__(self) -> None: + pass \ No newline at end of file