From eee6b216a732381f1befdab6112dc48125501e6d Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Sat, 4 Dec 2021 10:46:22 +0000 Subject: [PATCH] Format code with yapf and isort This commit fixes the style issues introduced in e51dc87 according to the output from yapf and isort. Details: https://deepsource.io/gh/StoryScriptorg/StoryScript-CTranspiler/transform/d0c22059-8fc7-4986-a234-ae994fff2d66/ --- filehelper.py | 52 +- langEnums.py | 43 +- langParser.py | 499 +++++++++-------- lexer.py | 1497 ++++++++++++++++++++++++++++--------------------- processor.py | 195 ++++--- 5 files changed, 1279 insertions(+), 1007 deletions(-) diff --git a/filehelper.py b/filehelper.py index f1c8c7a..cc8a611 100644 --- a/filehelper.py +++ b/filehelper.py @@ -1,31 +1,31 @@ class FileHelper: - def __init__(self, filename): - self.filename = filename - self.header = [] - self.body = [] - self.footer = [] - self.indent_level = 0 - self.minified = False + def __init__(self, filename): + self.filename = filename + self.header = [] + self.body = [] + self.footer = [] + self.indent_level = 0 + self.minified = False - def insert_header(self, content): - """ Insert the Content in the Header section of the file. """ - self.header.append(content + "\n") + def insert_header(self, content): + """Insert the Content in the Header section of the file.""" + self.header.append(content + "\n") - def insert_content(self, content): - """ Insert the Content in the Body section of the file. """ - if not self.minified: - self.body.append(("\t" * self.indent_level) + content + "\n") - else: - self.body.append(content) + def insert_content(self, content): + """Insert the Content in the Body section of the file.""" + if not self.minified: + self.body.append(("\t" * self.indent_level) + content + "\n") + else: + self.body.append(content) - def insert_footer(self, content): - """ Insert the Content in the Footer section of the file. """ - self.footer.append(content + "\n") + def insert_footer(self, content): + """Insert the Content in the Footer section of the file.""" + self.footer.append(content + "\n") - def write_data_to_file(self): - """ Write all the Data stored to File. """ - f = open(self.filename, 'w') - f.writelines(self.header) - f.writelines(self.body) - f.writelines(self.footer) - f.close() \ No newline at end of file + def write_data_to_file(self): + """Write all the Data stored to File.""" + f = open(self.filename, "w") + f.writelines(self.header) + f.writelines(self.body) + f.writelines(self.footer) + f.close() diff --git a/langEnums.py b/langEnums.py index 26b8566..a0973fe 100644 --- a/langEnums.py +++ b/langEnums.py @@ -1,27 +1,30 @@ from enum import Enum + class Exceptions(Enum): - InvalidSyntax = 100 - AlreadyDefined = 101 - NotImplementedException = 102 - NotDefinedException = 103 - GeneralException = 104 - DivideByZeroException = 105 - InvalidValue = 106 - InvalidTypeException = 107 + InvalidSyntax = 100 + AlreadyDefined = 101 + NotImplementedException = 102 + NotDefinedException = 103 + GeneralException = 104 + DivideByZeroException = 105 + InvalidValue = 106 + InvalidTypeException = 107 + class Types(Enum): - Boolean = 0 - Integer = 1 - Float = 2 - List = 3 - Dictionary = 4 - Tuple = 5 - Dynamic = 6 - String = 7 - Any = 8 + Boolean = 0 + Integer = 1 + Float = 2 + List = 3 + Dictionary = 4 + Tuple = 5 + Dynamic = 6 + String = 7 + Any = 8 + class ConditionType(Enum): - And = 0 - Or = 1 - Single = 2 \ No newline at end of file + And = 0 + Or = 1 + Single = 2 diff --git a/langParser.py b/langParser.py index a6decba..15cdef2 100644 --- a/langParser.py +++ b/langParser.py @@ -1,256 +1,307 @@ from string import ascii_letters, digits from langEnums import Types, ConditionType, Exceptions + def parse_escape_character(trimmed_string): - is_escape_char_detected = False - outstr = "" - for i in str(trimmed_string): - outchar = i - if i == "\\": - is_escape_char_detected = True - continue - if is_escape_char_detected: - is_escape_char_detected = False - if i == "n": - outchar = "\n" - elif i == "\\": - outchar = "\\" - elif i == "t": - outchar = "\t" - outstr += outchar - return outstr + is_escape_char_detected = False + outstr = "" + for i in str(trimmed_string): + outchar = i + if i == "\\": + is_escape_char_detected = True + continue + if is_escape_char_detected: + is_escape_char_detected = False + if i == "n": + outchar = "\n" + elif i == "\\": + outchar = "\\" + elif i == "t": + outchar = "\t" + outstr += outchar + return outstr + def parse_string_list(command): - if isinstance(command, str): - command = [command] - res = " ".join(command) - # for i in command: - # res += i + " " - # res = res[:-1] - return res + if isinstance(command, str): + command = [command] + res = " ".join(command) + # for i in command: + # res += i + " " + # res = res[:-1] + return res + def check_is_float(command): - is_float = False - if(not isinstance(command, str)): - command = str(command) - for i in command: - for j in i: - if j == ".": - is_float = True - break - return is_float + is_float = False + if not isinstance(command, str): + command = str(command) + for i in command: + for j in i: + if j == ".": + is_float = True + break + return is_float + def try_parse_int(val): - try: - return int(val) - except ValueError: - return val + try: + return int(val) + except ValueError: + return val + def convert_to_python_native_type(valtype, value): - """ - Returns a Converted to Python version of the value provided to a Type specified. - [PARAMETER] valtype: Target output type - [PARAMETER] value: The input value that will be converted. - [RETURNS] a Converted value. Else the input value If the type is not support yet by the Converter. - """ - if valtype == Types.Integer: - return int(value) - elif valtype == Types.Float: - return float(value) - elif value == Types.String: - return str(value[1:-1]) - elif value == Types.Boolean: - if value == "true": - return True - elif value == "false": - return False - else: - return value - else: return value + """ + Returns a Converted to Python version of the value provided to a Type specified. + [PARAMETER] valtype: Target output type + [PARAMETER] value: The input value that will be converted. + [RETURNS] a Converted value. Else the input value If the type is not support yet by the Converter. + """ + if valtype == Types.Integer: + return int(value) + elif valtype == Types.Float: + return float(value) + elif value == Types.String: + return str(value[1:-1]) + elif value == Types.Boolean: + if value == "true": + return True + elif value == "false": + return False + else: + return value + else: + return value + def convert_types_enum_to_string(enumvalue): - if enumvalue == Types.Integer: - return "int" - elif enumvalue == Types.Float: - return "float" - elif enumvalue == Types.String: - return "string" - elif enumvalue == Types.Boolean: - return "bool" - elif enumvalue == Types.Dynamic: - return "dynamic" - elif enumvalue == Types.Tuple: - return "tuple" - elif enumvalue == Types.List: - return "list" - elif enumvalue == Types.Dictionary: - return "dictionary" + if enumvalue == Types.Integer: + return "int" + elif enumvalue == Types.Float: + return "float" + elif enumvalue == Types.String: + return "string" + elif enumvalue == Types.Boolean: + return "bool" + elif enumvalue == Types.Dynamic: + return "dynamic" + elif enumvalue == Types.Tuple: + return "tuple" + elif enumvalue == Types.List: + return "list" + elif enumvalue == Types.Dictionary: + return "dictionary" + def convert_to_storyscript_native_type(valtype, value): - """ - Returns a Converted to StoryScript version of the value provided to a Type specified. - [PARAMETER] valtype: Target output type - [PARAMETER] value: The input value that will be converted. - [RETURNS] a Converted value. Else the input value If the type is not support yet by the Converter. - """ - if valtype == Types.Integer: - return str(value) - elif valtype == Types.Float: - return str(value) - elif value == Types.String: - return f"\"{value}\"" - elif value == Types.Boolean: - if value: return "true" - else: return "false" - elif value == Types.Dynamic: - return f"new Dynamic ({value})" - else: return value + """ + Returns a Converted to StoryScript version of the value provided to a Type specified. + [PARAMETER] valtype: Target output type + [PARAMETER] value: The input value that will be converted. + [RETURNS] a Converted value. Else the input value If the type is not support yet by the Converter. + """ + if valtype == Types.Integer: + return str(value) + elif valtype == Types.Float: + return str(value) + elif value == Types.String: + return f'"{value}"' + elif value == Types.Boolean: + if value: + return "true" + else: + return "false" + elif value == Types.Dynamic: + return f"new Dynamic ({value})" + else: + return value + def trim_space(string): - out_str = "" - in_string = False - for i in string: - if i == "\"": - if in_string: in_string = False - else: in_string = True - if not in_string and i == " ": - continue - out_str += i - return out_str + out_str = "" + in_string = False + for i in string: + if i == '"': + if in_string: + in_string = False + else: + in_string = True + if not in_string and i == " ": + continue + out_str += i + return out_str + def parse_type_from_value(value): - if not isinstance(value, str): - value = str(value) - is_float = check_is_float(value) - if(value.startswith('"') or value.endswith('"')): - if(not (value.startswith('"') and value.endswith('"'))): - return Exceptions.InvalidSyntax - return Types.String - elif(value == "true" or value == "false"): - return Types.Boolean - elif(value.startswith("new List")): - return Types.List - elif(value.startswith("new Dictionary")): - return Types.Dictionary - elif(value.startswith("new Tuple")): - return Types.Tuple - elif(value.startswith("new Dynamic")): - return Types.Dynamic - elif(is_float): - return Types.Float - elif(not is_float): - return Types.Integer - else: return Exceptions.InvalidSyntax + if not isinstance(value, str): + value = str(value) + is_float = check_is_float(value) + if value.startswith('"') or value.endswith('"'): + if not (value.startswith('"') and value.endswith('"')): + return Exceptions.InvalidSyntax + return Types.String + elif value == "true" or value == "false": + return Types.Boolean + elif value.startswith("new List"): + return Types.List + elif value.startswith("new Dictionary"): + return Types.Dictionary + elif value.startswith("new Tuple"): + return Types.Tuple + elif value.startswith("new Dynamic"): + return Types.Dynamic + elif is_float: + return Types.Float + elif not is_float: + return Types.Integer + else: + return Exceptions.InvalidSyntax + def parse_type_string(string): - if(string == "bool"): - return Types.Boolean - elif(string == "int"): - return Types.Integer - elif(string == "float"): - return Types.Float - elif(string == "list"): - return Types.List - elif(string == "dictionary"): - return Types.Dictionary - elif(string == "tuple"): - return Types.Tuple - elif(string == "dynamic"): - return Types.Dynamic - elif(string == "string"): - return Types.String - elif(string == "any"): - return Types.Any - else: - return Exceptions.InvalidSyntax + if string == "bool": + return Types.Boolean + elif string == "int": + return Types.Integer + elif string == "float": + return Types.Float + elif string == "list": + return Types.List + elif string == "dictionary": + return Types.Dictionary + elif string == "tuple": + return Types.Tuple + elif string == "dynamic": + return Types.Dynamic + elif string == "string": + return Types.String + elif string == "any": + return Types.Any + else: + return Exceptions.InvalidSyntax + def check_naming_violation(name): - """ Returns If the variable naming valid or not """ - if not isinstance(name, str): - name = str(name) - if name in ["if", "else", "var", "int", - "bool", "float", "list", "dictionary", - "tuple", "const", "override", "func", - "end", "print", "input", "throw", - "string", "typeof", "del", "namespace"]: - return False - elif name[0] in digits: - return False - else: return True + """Returns If the variable naming valid or not""" + if not isinstance(name, str): + name = str(name) + if name in [ + "if", + "else", + "var", + "int", + "bool", + "float", + "list", + "dictionary", + "tuple", + "const", + "override", + "func", + "end", + "print", + "input", + "throw", + "string", + "typeof", + "del", + "namespace", + ]: + return False + elif name[0] in digits: + return False + else: + return True + def parse_condition_expression(expr, analyse_command_method): - """ Parse If the condition is True or False """ - # [:OperatorIndex] = Accessing a Message before the operator - # [OperatorIndex + 1:] = Accessing a Message after the operator - operator_index = 0 - compare_operator = {">", "<", "==", "!=", ">=", "<="} - for i in expr: - if i in compare_operator: - break - operator_index += 1 - resl, error = analyse_command_method(expr[:operator_index]) # Analyse the message on the left - if error: return resl, error + """Parse If the condition is True or False""" + # [:OperatorIndex] = Accessing a Message before the operator + # [OperatorIndex + 1:] = Accessing a Message after the operator + operator_index = 0 + compare_operator = {">", "<", "==", "!=", ">=", "<="} + for i in expr: + if i in compare_operator: + break + operator_index += 1 + resl, error = analyse_command_method( + expr[:operator_index] + ) # Analyse the message on the left + if error: + return resl, error + + resr, error = analyse_command_method( + expr[operator_index + 1 :] + ) # Analyse the message on the right + if error: + return resr, error - resr, error = analyse_command_method(expr[operator_index + 1:]) # Analyse the message on the right - if error: return resr, error + # Type conversion + restype = parse_type_from_value(resl) + resl = convert_to_python_native_type(restype, resl) + restype = parse_type_from_value(resr) + resr = convert_to_python_native_type(restype, resr) - # Type conversion - restype = parse_type_from_value(resl) - resl = convert_to_python_native_type(restype, resl) - restype = parse_type_from_value(resr) - resr = convert_to_python_native_type(restype, resr) + if expr[operator_index] == "==": # If the operator was == + if resl == resr: + return True + elif expr[operator_index] == ">": # If the operator was > + if resl > resr: + return True + elif expr[operator_index] == "<": # If the operator was < + if resl < resr: + return True + elif expr[operator_index] == "!=": # If the operator was != + if resl != resr: + return True + elif expr[operator_index] == ">=": # If the operator was >= + if resl >= resr: + return True + elif expr[operator_index] == "<=": # If the operator was <= + if resl <= resr: + return True + else: + return Exceptions.InvalidSyntax - if expr[operator_index] == "==": # If the operator was == - if resl == resr: return True - elif expr[operator_index] == ">": # If the operator was > - if resl > resr: return True - elif expr[operator_index] == "<": # If the operator was < - if resl < resr: return True - elif expr[operator_index] == "!=": # If the operator was != - if resl != resr: return True - elif expr[operator_index] == ">=": # If the operator was >= - if resl >= resr: return True - elif expr[operator_index] == "<=": # If the operator was <= - if resl <= resr: return True - else: return Exceptions.InvalidSyntax + return False - return False def parse_conditions(expr): - """ Separate expressions into a list of conditions """ - conditionslist:list = [] # List of conditions - conditions:list = [] # List of condition - condition:list = [] # Condition - current_condition_type = ConditionType.Single # Current condition type - for i in expr: - if i == "and": - if current_condition_type != ConditionType.Single: - conditions.append(condition) - conditions.append(current_condition_type) - conditionslist.append(conditions) - conditions = [] - condition = [] - conditions.append(condition) - condition = [] - current_condition_type = ConditionType.And - continue - if i == "or": - if current_condition_type != ConditionType.Single: - conditions.append(condition) - conditions.append(current_condition_type) - conditionslist.append(conditions) - conditions = [] - condition = [] - conditions.append(condition) - condition = [] - current_condition_type = ConditionType.Or - continue - if i == "then": - conditions.append(condition) - conditions.append(current_condition_type) - conditionslist.append(conditions) - conditions = [] - condition = [] - break - condition.append(i) - return conditionslist + """Separate expressions into a list of conditions""" + conditionslist: list = [] # List of conditions + conditions: list = [] # List of condition + condition: list = [] # Condition + current_condition_type = ConditionType.Single # Current condition type + for i in expr: + if i == "and": + if current_condition_type != ConditionType.Single: + conditions.append(condition) + conditions.append(current_condition_type) + conditionslist.append(conditions) + conditions = [] + condition = [] + conditions.append(condition) + condition = [] + current_condition_type = ConditionType.And + continue + if i == "or": + if current_condition_type != ConditionType.Single: + conditions.append(condition) + conditions.append(current_condition_type) + conditionslist.append(conditions) + conditions = [] + condition = [] + conditions.append(condition) + condition = [] + current_condition_type = ConditionType.Or + continue + if i == "then": + conditions.append(condition) + conditions.append(current_condition_type) + conditionslist.append(conditions) + conditions = [] + condition = [] + break + condition.append(i) + return conditionslist diff --git a/lexer.py b/lexer.py index 5bcad7c..abca84d 100644 --- a/lexer.py +++ b/lexer.py @@ -4,665 +4,872 @@ from string import ascii_letters from random import randint + # This class is used to store variables and function class SymbolTable: - def __init__(self): - self.variable_table = {"true":(Types.Boolean, 1), "false":(Types.Boolean, 0)} - self.function_table = {} - self.enableFunctionFeature = False - self.ignoreInfo = False + def __init__(self): + self.variable_table = {"true": (Types.Boolean, 1), "false": (Types.Boolean, 0)} + self.function_table = {} + self.enableFunctionFeature = False + self.ignoreInfo = False + + def copyvalue(self): + return self.variable_table, self.function_table, self.enableFunctionFeature - def copyvalue(self): - return self.variable_table, self.function_table, self.enableFunctionFeature + def importdata(self, variable_table, functionTable, enable_function_feature): + self.variable_table = variable_table + self.function_table = function_table + self.enableFunctionFeature = enable_function_feature - def importdata(self, variable_table, functionTable, enable_function_feature): - self.variable_table = variable_table - self.function_table = function_table - self.enableFunctionFeature = enable_function_feature + def get_all_variable_name(self): + return self.variable_table.keys() - def get_all_variable_name(self): - return self.variable_table.keys() + def get_variable(self, key): + return self.variable_table[key] - def get_variable(self, key): - return self.variable_table[key] + def get_variable_type(self, key): + return self.variable_table[key][0] - def get_variable_type(self, key): - return self.variable_table[key][0] + def get_all_function_name(self): + return self.function_table.keys() - def get_all_function_name(self): - return self.function_table.keys() + def get_function(self, key): + return self.function_table[key] - def get_function(self, key): - return self.function_table[key] + def set_variable(self, key, value, vartype, is_heap_allocated=False, str_size=None): + self.variable_table[key] = (vartype, value, is_heap_allocated, str_size) - def set_variable(self, key, value, vartype, is_heap_allocated=False, str_size=None): - self.variable_table[key] = (vartype, value, is_heap_allocated, str_size) + def set_function(self, key, value, arguments): + self.function_table[key] = (arguments, value) - def set_function(self, key, value, arguments): - self.function_table[key] = (arguments, value) + def delete_variable(self, key): + del self.variable_table[key] - def delete_variable(self, key): - del self.variable_table[key] + def delete_function(self, key): + del self.function_table[key] - def delete_function(self, key): - del self.function_table[key] global libraryIncluded -libraryIncluded:list = ["stdio.h", "stdlib.h"] +libraryIncluded: list = ["stdio.h", "stdlib.h"] + class Lexer: - def __init__(self, symbol_table, out_file_name, file_helper=None, auto_reallocate=True): - self.symbol_table = symbol_table - self.file_helper = file_helper - self.auto_reallocate = auto_reallocate - - if file_helper is None: - self.file_helper = FileHelper(out_file_name) - self.file_helper.insert_footer("\treturn 0;") - self.file_helper.insert_footer("}") - self.file_helper.indent_level = 1 - - def throw_keyword(self, tc, multiple_commands_index, ln="Unknown"): - # from traceback import print_stack - # Throw keyword. "throw [Exception] [Description]" - def getDescription(): - msg = "" - for i in tc[2:multiple_commands_index + 1]: - if i.startswith('"'): - i = i[1:] - if i.endswith('"'): - i = i[:-1] - msg += i + " " - msg = msg[:-1] - return parser.parse_escape_character(msg) - - exceptionCode = 1 - description = "No Description provided" - - if tc[1] == "InvalidSyntax": - exceptionCode = 100 - elif tc[1] == "AlreadyDefined": - exceptionCode = 101 - elif tc[1] == "NotImplementedException": - exceptionCode = 102 - elif tc[1] == "NotDefinedException": - exceptionCode = 103 - elif tc[1] == "GeneralException": - exceptionCode = 104 - elif tc[1] == "DivideByZeroException": - exceptionCode = 105 - elif tc[1] == "InvalidValue": - exceptionCode = 106 - elif tc[1] == "InvalidTypeException": - exceptionCode = 107 - else: - self.raise_transpile_error("InvalidValue: The Exception entered is not defined", ln) - - try: - if(tc[2]): - description = getDescription() - return f"raiseException({exceptionCode}, \"{description}\");", "" - except IndexError: - return f"raiseException({exceptionCode}, \"{description}\");", "" - - def raise_transpile_error(self, text, ln="Unknown"): - print("TRANSPILATION ERROR:") - print(f"While processing line {ln}") - print(text) - raise SystemExit - - def variable_setting(self, tc, multiple_commands_index, ln): - # Error messages - invalid_value = "InvalidValue: Invalid value" - mismatch_type = "InvalidValue: Value doesn't match variable type." - all_variable_name = self.symbol_table.get_all_variable_name() - - if tc[1] == "=": # Set operator - res, error = self.analyseCommand(tc[2:multiple_commands_index + 1]) - if error: return res, error - value = "" - - for i in tc[2:multiple_commands_index + 1]: - value += i + " " - value = value[:-1] - - valtype = parser.parse_type_from_value(res) - if valtype == Exceptions.InvalidSyntax: - return invalid_value, Exceptions.InvalidValue - vartype = self.symbol_table.get_variable_type(tc[0]) - # Check if Value Type matches Variable type - if valtype != vartype: - return mismatch_type, Exceptions.InvalidValue - res = parser.parse_escape_character(res) - if res in all_variable_name: - res = (self.symbol_table.get_variable(res))[1] - oldvar = self.symbol_table.get_variable(tc[0]) - if error: self.raise_transpile_error(error[0], ln) - if oldvar[2]: - if oldvar[0] == Types.String: - error = self.symbol_table.set_variable(tc[0], res, vartype, oldvar[2], len(res) - 1) - if self.auto_reallocate: - if oldvar[3] < (len(res) - 1): - self.file_helper.insert_content(f"{tc[0]} = realloc({tc[0]}, {len(res) - 1});") - else: - if oldvar[3] > len(res) - 1 and oldvar[3] > 64: - self.file_helper.insert_content(f"{tc[0]} = realloc({tc[0]}, {len(res) - 1});") - else: - print("INFO: To set a Message to a String, Input string must be less than the Size specified or equal the Original string size If declared with initial value.") - if len(res) - 1 > oldvar[3]: - self.raise_transpile_error("InvalidValue: The input string length is more than the Original Defined size. If you want a Dynamically allocated string, Please don't use \"--no-auto-reallocate\" option.", ln) - return f"memcpy({tc[0]}, {res}, {len(res) - 1});", "" - if oldvar[0] == Types.Dynamic: - res = res.removeprefix("new Dynamic (") - res = res[:-1] - return f"{tc[0]} = (void*){res};", "" - return f"*{tc[0]} = {res};", "" - if vartype == Types.String: - length = len(res) - 1 - error = self.symbol_table.set_variable(tc[0], res, vartype, oldvar[2], length) - if length > oldvar[3]: - self.raise_transpile_error("InvalidValue: The input string length is more than the Original Defined size. If you want a Dynamically allocated string, Please use the Heap allocated string instead If you want to make the String dynamiccally allocated.", ln) - return f"memcpy({tc[0]}, {res}, {length});", "" - if oldvar[0] == Types.Dynamic: - res = res.removeprefix("new Dynamic (") - res = res[:-1] - return f"{tc[0]} = (void*){res};", "" - error = self.symbol_table.set_variable(tc[0], res, vartype, oldvar[2]) - return f"{tc[0]} = {res};", "" - elif tc[1] == "+=": # Add & Set operator - operator = "+" - elif tc[1] == "-=": # Subtract & Set operator - operator = "-" - elif tc[1] == "*=": # Multiply & Set operator - operator = "*" - elif tc[1] == "/=": # Divide & Set operator - operator = "/" - elif tc[1] == "%=": # Modulo Operaion & Set operator - operator = "%" - else: - res = "" - for i in tc: - res += i + " " - res = res[:-1] - return res, "" - res, error = self.analyseCommand(tc[2:multiple_commands_index + 1]) - if error: return res, error - value = "" - - for i in tc[2:multiple_commands_index + 1]: - value += i + " " - value = value[:-1] - - valtype = parser.parse_type_from_value(res) - if valtype == Exceptions.InvalidSyntax: - return invalid_value, Exceptions.InvalidValue - vartype = self.symbol_table.get_variable_type(tc[0]) - # Check if Value Type matches Variable type - if valtype != vartype: - return mismatch_type, Exceptions.InvalidValue - res = parser.parse_escape_character(res) - if res in all_variable_name: - res = (self.symbol_table.get_variable(res))[1] - oldvar = self.symbol_table.get_variable(tc[0]) - error = self.symbol_table.set_variable(tc[0], res, vartype, oldvar[2]) - if error: self.raise_transpile_error(error[0], ln) - if oldvar[2]: - if oldvar[0] == Types.String: - self.raise_transpile_error(f"InvalidTypeException: You cannot use {operator}= with String.", ln) - if oldvar[0] == Types.Dynamic: - self.raise_transpile_error(f"InvalidTypeException: You cannot use {operator}= with Dynamics.", ln) - return f"*{tc[0]} {operator}= {res};", "" - return f"{tc[0]} {operator}= {res};", "" - - def switch_case_statement(self, tc, ln): - cases = [] - case = [] - command = [] - isInCaseBlock = False - isInDefaultBlock = False - isAfterCaseKeyword = False - currentCaseKey = None - for i in tc[2:]: - if i == "case": - isAfterCaseKeyword = True - continue - if isAfterCaseKeyword: - outkey = i - if outkey.endswith(":"): - outkey = outkey[:-1] - currentCaseKey = outkey - isAfterCaseKeyword = False - isInCaseBlock = True - continue - if isInCaseBlock: - if i == "&&": - case.append(command) - command = [] - continue - if i == "break": - case.append(command) - cases.append((currentCaseKey, case)) - command = [] - case = [] - isInCaseBlock = False - continue - command.append(i) - if i == "end": - break - - defaultCase = False - - self.file_helper.insert_content(f"switch ({tc[1]})") - self.file_helper.insert_content("{") - self.file_helper.indent_level += 1 - for i in cases: - if i[0] == "default": - defaultCase = i - continue - self.file_helper.insert_content(f"case {i[0]}:") - self.file_helper.indent_level += 1 - for j in i[1]: - self.file_helper.insert_content(self.analyseCommand(j)[0]) - self.file_helper.insert_content("break;") - self.file_helper.indent_level -= 1 - if defaultCase: - self.file_helper.insert_content("default:") - self.file_helper.indent_level += 1 - for j in i[1]: - self.file_helper.insert_content(self.analyseCommand(j)[0]) - self.file_helper.insert_content("break;") - self.file_helper.indent_level -= 1 - self.file_helper.indent_level -= 1 - self.file_helper.insert_content("}") - - return "", "" - - def if_else_statement(self, tc: list, ln: int): - allvar = self.symbol_table.get_all_variable_name() - def check_string_comparison(condition): - operator_index = 0 - compare_operator = {">", "<", "==", "!=", ">=", "<="} - for i in condition: - if i in compare_operator: - break - operator_index += 1 - - resl = bool(parser.parse_type_from_value(" ".join(condition[:operator_index])) == Types.String) - if "".join(condition[:operator_index]) in allvar: - resl = resl or (self.symbol_table.get_variable_type("".join(condition[:operator_index])) == Types.String) - - resr = bool(parser.parse_type_from_value(" ".join(condition[operator_index + 1:])) == Types.String) - if "".join(condition[operator_index + 1:]) in allvar: - resr = resr or (self.symbol_table.get_variable_type("".join(condition[operator_index + 1:])) == Types.String) - - if resl and resr: - joinedstrleft = " ".join(condition[:operator_index]) - joinedstrright = " ".join(condition[operator_index + 1:]) - return f"strcmp({joinedstrleft}, {joinedstrright}) == 0" - - return False - - conditionslist:list = parser.parse_conditions(tc[1:]) - finalString = "if (" - for i in conditionslist: - exprs = [] - currentConditionType = ConditionType.Single - for j in i: - if j and isinstance(j, list): - strcmp = check_string_comparison(j) - if strcmp: - exprs.append(strcmp) - else: exprs.append(j) - elif isinstance(j, ConditionType): - currentConditionType = j - if currentConditionType == ConditionType.And: - for i in exprs: - finalString += parser.parse_string_list(i) + " && " - finalString = finalString[:-4] - elif currentConditionType == ConditionType.Single: - finalString += parser.parse_string_list(exprs[0]) - elif currentConditionType == ConditionType.Or: - for i in exprs: - finalString += parser.parse_string_list(i) + "||" - finalString = finalString[:-4] - finalString += ")" - - isInCodeBlock: bool = False - isInElseBlock: bool = False - have_passed_then_keyword: bool = False - ifstatement: dict = {"if":[], "else":None} - commands: list = [] - command: list = [] - endkeywordcount: int = 0 # All "end" keyword in the expression - endkeywordpassed: int = 0 # All "end" keyword passed - elsekeywordcount: int = 0 # All "else" keyword in the expression - elsekeywordpassed: int = 0 # All "else" keyword passed - for i in tc[2:]: - if i == "end": - endkeywordcount += 1 - elif i == "else": - elsekeywordcount += 1 - for i in tc: - if not have_passed_then_keyword and i == "then": - isInCodeBlock = True - have_passed_then_keyword = True - continue - if isInCodeBlock: - if i == "&&": - commands.append(command) - command = [] - continue - elif i == "end": - endkeywordpassed += 1 - if endkeywordcount == endkeywordpassed: - commands.append(command) - command = [] - if isInElseBlock: - ifstatement["else"] = commands - else: ifstatement["if"] = commands - isInElseBlock = False - isInCodeBlock = False - continue - elif i == "else": - elsekeywordpassed += 1 - if elsekeywordcount == elsekeywordpassed and endkeywordpassed + 1 == endkeywordcount: - commands.append(command) - command = [] - ifstatement["if"] = commands - commands = [] - isInElseBlock = True - continue - command.append(i) - self.file_helper.insert_content(finalString) - self.file_helper.insert_content("{") - self.file_helper.indent_level += 1 - for i in ifstatement["if"]: - self.file_helper.insert_content(self.analyseCommand(i)[0]) - self.file_helper.indent_level -= 1 - if ifstatement["else"] is not None: - self.file_helper.insert_content("} else {") - self.file_helper.indent_level += 1 - for i in ifstatement["else"]: - self.file_helper.insert_content(self.analyseCommand(i)[0]) - self.file_helper.indent_level -= 1 - self.file_helper.insert_content("}") - else: self.file_helper.insert_content("}") - - return "", "" - - def analyseCommand(self, tc, ln="Unknown", varcontext=None): - # All Keywords - basekeywords = {"if", "else", "var", "int", - "bool", "float", "list", "dictionary", - "tuple", "dynamic", "const", "override", - "func", "end", "print", "input", - "throw", "string", "del", "namespace", - "#define", "loopfor", "switch", - "input", "exit"} - - is_multiple_commands = False - multiple_commands_index = -1 - for i in tc: - multiple_commands_index += 1 - if i == "&&": - is_multiple_commands = True - break - - all_variable_name = self.symbol_table.get_all_variable_name() - all_function_name = self.symbol_table.get_all_function_name() - - # Error messages - paren_needed = "InvalidSyntax: Parenthesis is needed after a function name" - close_paren_needed = "InvalidSyntax: Parenthesis is needed after an Argument input" - - try: - if tc[0] in all_variable_name: - try: - return self.variable_setting(tc, multiple_commands_index, ln) - except IndexError: - res = "" - for i in tc: - res += i + " " - res = res[:-1] - return res, "" - elif tc[0] in basekeywords: - if tc[0] in {"var", "int", "bool", "float", "list", "dictionary", "tuple", "const", "string", "dynamic"}: - try: - if tc[2] == "=" or tc[3] == "=": pass - else: raise IndexError - definedType = parser.parse_type_string(tc[0]) - if(tc[1] in self.symbol_table.get_all_variable_name()): - self.raise_transpile_error(f"AlreadyDefined: a Variable \"{tc[1]}\" is already defined", ln) - - # Checking for variable naming violation - if not (parser.check_naming_violation(tc[1])): - self.raise_transpile_error("InvalidValue: a Variable name cannot start with digits.", ln) - - # var(0) a(1) =(2) 3(3) - # double(0) heap(1) b(2) =(3) 5(4) - isHeap = False - if tc[1] == "heap": - isHeap = True - res, error = self.analyseCommand(tc[4:multiple_commands_index + 1], tc[2]) - if isinstance(error, Exceptions): - self.raise_transpile_error(res, ln) - else: - res, error = self.analyseCommand(tc[3:multiple_commands_index + 1], tc[1]) - if isinstance(error, Exceptions): - self.raise_transpile_error(res, ln) - value = "" - - for i in tc[3:multiple_commands_index + 1]: - value += i + " " - value = value[:-1] - vartype = parser.parse_type_from_value(res) - # Check If existing variable type matches the New value type - if tc[0] != "var" and definedType != vartype: - self.raise_transpile_error("InvalidValue: Variable types doesn't match value type.", ln) - if vartype == Exceptions.InvalidSyntax: - self.raise_transpile_error("InvalidSyntax: Invalid value", ln) - res = parser.parse_escape_character(res) - - if isHeap: - outvartype = tc[0] - if tc[0] == "var": - outvartype = parser.convert_types_enum_to_string(vartype) - if vartype == Types.String: - if "string.h" not in libraryIncluded: - libraryIncluded.append("string.h") - self.symbol_table.set_variable(tc[2], res, vartype, True, len(res) - 1) - self.file_helper.insert_content(f"char *{tc[2]} = (char*)malloc({len(res) - 1});") - return f"if({tc[2]} != NULL) memcpy({tc[2]}, {res}, {len(res) - 1});", "" - if vartype == Types.Dynamic: - # dynamic[0] heap[1] a[2] =[3] new[4] Dynamic[5] (memsize)[6] - self.symbol_table.set_variable(tc[2], res, vartype, True) - args = parser.parse_string_list(tc[6:]) - args = args[1:-1] - varval = parser.parse_string_list(args) - intval = parser.try_parse_int(varval) - if isinstance(intval, int): - if intval <= 2147483647 and intval >= -2147483647: - if intval <= 9223372036854775807 and intval >= -9223372036854775807: - bytesize = 16 - else: bytesize = 8 - else: bytesize = 4 - else: - bytesize = len(varval) - 1 - try: - self.file_helper.insert_content(f"void* {tc[2]} = malloc({bytesize});") - return f"{tc[2]} = (void*){varval};", "" - except NameError: - return f"void* {tc[2]} = malloc({bytesize});" - self.symbol_table.set_variable(tc[2], res, vartype, True) - self.file_helper.insert_content(f"{outvartype} *{tc[2]} = ({outvartype}*)malloc(sizeof({outvartype}));") - return f"*{tc[2]} = {res};", "" - if tc[0] == "var": - outvartype = parser.convert_types_enum_to_string(vartype) - if vartype == Types.String: - self.symbol_table.set_variable(tc[1], res, vartype, False, len(res) - 1) - return f"char {tc[1]}[{len(res) - 1}] = {res};", "" - if vartype == Types.Dynamic: - # var[0] a[1] =[2] new[3] Dynamic[4] (memsize)[5] - self.symbol_table.set_variable(tc[1], res, vartype, False) - args = parser.parse_string_list(tc[5:]) - args = args[1:-1] - varval = parser.parse_string_list(args) - intval = parser.try_parse_int(varval) - if isinstance(intval, int): - if intval >= 2147483647 or intval <= -2147483647: - if intval >= 9223372036854775807 or intval <= -9223372036854775807: - bytesize = 16 - else: bytesize = 8 - else: bytesize = 4 - else: - bytesize = len(varval) - 1 - return f"void* {tc[1]} = (void*){varval};", "" - self.symbol_table.set_variable(tc[1], res, vartype, False) - return f"{outvartype} {tc[1]} = {res};", "" - if vartype == Types.String: - self.symbol_table.set_variable(tc[1], res, vartype, False, len(res) - 1) - return f"char {tc[1]}[{len(res) - 1}] = {res};", "" - if vartype == Types.Dynamic: - # dynamic[0] a[1] =[2] new[3] Dynamic[4] (memsize)[5] - self.symbol_table.set_variable(tc[1], res, vartype, False) - args = parser.parse_string_list(tc[5:]) - args = args[1:-1] - varval = parser.parse_string_list(args) - intval = parser.try_parse_int(varval) - if isinstance(intval, int): - if intval >= 2147483647 or intval <= -2147483647: - if intval >= 9223372036854775807 or intval <= -9223372036854775807: - bytesize = 16 - else: bytesize = 8 - else: bytesize = 4 - else: - bytesize = len(varval) - 1 - return f"void* {tc[1]} = (void*){varval};", "" - self.symbol_table.set_variable(tc[1], res, vartype, isHeap) - return f"{tc[0]} {tc[1]} = {res};", "" - except IndexError: - # var(0) a(1) (Stack allocation) - # int(0) heap(1) a(2) (Heap allocation) - # string(0) heap(1) a(2) 20(3) (String heap allocation) - if tc[0] == "var": - self.raise_transpile_error("InvalidSyntax: Initial value needed for var keyword", ln) - vartype = parser.parse_type_string(tc[0]) - if vartype == Exceptions.InvalidSyntax: - self.raise_transpile_error("InvalidSyntax: Invalid type", ln) - if tc[1] == "heap": - if vartype == Types.String: - self.symbol_table.set_variable(tc[2], None, vartype, True, int(tc[3])) - if "string.h" not in libraryIncluded: - libraryIncluded.append("string.h") - return f"char *{tc[2]} = (char*)malloc({tc[3]});", "" - self.symbol_table.set_variable(tc[2], None, vartype, True) - return f"{tc[0]} *{tc[2]} = ({tc[0]}*)malloc(sizeof({tc[0]}));", "" - self.symbol_table.set_variable(tc[1], None, vartype, False) - return f"{tc[0]} {tc[1]};", "" - elif tc[0] == "print": - value = "" - for i in tc[1:multiple_commands_index + 1]: - value += i + " " - value = value[:-1] - if not value.startswith('('): # Check If the expression has parentheses around or not - return paren_needed, Exceptions.InvalidSyntax # Return error if not exists - if not value.endswith(')'): # Check If the expression has parentheses around or not - return close_paren_needed, Exceptions.InvalidSyntax # Return error if not exists - value = value[1:-1] - svalue = value.split() - value = str(value) - return f"printf({value});", None - elif tc[0] == "input": - value = "" - for i in tc[2:multiple_commands_index + 1]: # Get all parameters provided as 1 long string - value += i + " " - value = value[:-1] - if not value.startswith('('): # Check If the expression has parentheses around or not - self.raise_transpile_error(paren_needed, ln) # Return error if not exists - if not value.endswith(')'): # Check If the expression has parentheses around or not - self.raise_transpile_error(close_paren_needed, ln) # Return error if not exists - value = value[1:-1] # Cut parentheses out of the string - return f"scanf(\"%s\", &{varcontext})", {int(tc[1])} # Return the Recieved Input - elif tc[0] == "if": - return self.if_else_statement(tc, ln) - elif tc[0] == "exit": - value = "" - for i in tc[1:multiple_commands_index + 1]: # Get all parameters provided as 1 long string - value += i + " " - value = value[:-1] - if not value.startswith('('): # Check If the expression has parentheses around or not - self.raise_transpile_error(paren_needed, ln) # Return error if not exists - if not value.endswith(')'): # Check If the expression has parentheses around or not - self.raise_transpile_error(close_paren_needed, ln) # Return error if not exists - value = value[1:-1] - valtype = parser.parse_type_from_value(value) - if value.startswith('"'): - self.raise_transpile_error("InvalidValue: Exit code can only be integer.", ln) - if value.endswith('"'): - self.raise_transpile_error("InvalidValue: Exit code can only be integer.", ln) - if not value: - self.raise_transpile_error("InvalidValue: Parameter \"status\" (int) required.", ln) - return f"exit({int(value)});", valtype - elif tc[0] == "#define": - try: - # Set Interpreter Settings - if tc[1] == "interpet" and tc[2] == "ignoreInfo": - self.symbol_table.ignoreInfo = bool(tc[3] == "true") - return "", "" - except IndexError: - self.raise_transpile_error("InvalidValue: You needed to describe what you will change.", ln) - elif tc[0] == "throw": - return self.throw_keyword(tc, multiple_commands_index) # Go to the Throw keyword function - elif tc[0] == "del": - if tc[1] not in all_variable_name: - self.raise_transpile_error(f"NotDefinedException: The variable {tc[1]} is not defined.", ln) - if not self.symbol_table.get_variable(tc[1])[2]: - self.raise_transpile_error(f"InvalidValue: The variable {tc[1]} is not heap allocated.", ln) - return f"free({tc[1]});", "" - elif tc[0] == "loopfor": - try: - commands = [] # list of commands - command = [] - endkeywordcount = 0 # All "end" keyword in the expression - endkeywordpassed = 0 # All "end" keyword passed - for i in tc[2:]: - if i == "end": - endkeywordcount += 1 - for i in tc[2:]: - if i == "&&": - commands.append(command) - command = [] - continue - if i == "end": - endkeywordpassed += 1 - if endkeywordcount == endkeywordpassed: - commands.append(command) - command = [] - break - command.append(i) - genvarname = None - genvarname = "__sts_loopcount_" - genvarname += ascii_letters[randint(0, 51)] - genvarname += ascii_letters[randint(0, 51)] - genvarname += ascii_letters[randint(0, 51)] - self.file_helper.insert_content(f"for (int {genvarname} = 0; {genvarname} < {int(tc[1])}; {genvarname}++)" + " {") - self.file_helper.indent_level += 1 - for i in commands: - self.file_helper.insert_content(self.analyseCommand(i)[0]) - self.file_helper.indent_level -= 1 - self.file_helper.insert_content("}") - return "", "" - except ValueError: - self.raise_transpile_error("InvalidValue: Count must be an Integer. (Whole number)", ln) - elif tc[0] == "switch": - return self.switch_case_statement(tc, ln) - else: - self.raise_transpile_error("NotImplementedException: This feature is not implemented", ln) - elif tc[0] in all_function_name: - return parser.parse_string_list(tc), "" - elif tc[0] == "//": - return parser.parse_string_list(tc), "" - else: - return parser.parse_string_list(tc), "" - except IndexError: - return "", "" \ No newline at end of file + def __init__( + self, symbol_table, out_file_name, file_helper=None, auto_reallocate=True + ): + self.symbol_table = symbol_table + self.file_helper = file_helper + self.auto_reallocate = auto_reallocate + + if file_helper is None: + self.file_helper = FileHelper(out_file_name) + self.file_helper.insert_footer("\treturn 0;") + self.file_helper.insert_footer("}") + self.file_helper.indent_level = 1 + + def throw_keyword(self, tc, multiple_commands_index, ln="Unknown"): + # from traceback import print_stack + # Throw keyword. "throw [Exception] [Description]" + def getDescription(): + msg = "" + for i in tc[2 : multiple_commands_index + 1]: + if i.startswith('"'): + i = i[1:] + if i.endswith('"'): + i = i[:-1] + msg += i + " " + msg = msg[:-1] + return parser.parse_escape_character(msg) + + exceptionCode = 1 + description = "No Description provided" + + if tc[1] == "InvalidSyntax": + exceptionCode = 100 + elif tc[1] == "AlreadyDefined": + exceptionCode = 101 + elif tc[1] == "NotImplementedException": + exceptionCode = 102 + elif tc[1] == "NotDefinedException": + exceptionCode = 103 + elif tc[1] == "GeneralException": + exceptionCode = 104 + elif tc[1] == "DivideByZeroException": + exceptionCode = 105 + elif tc[1] == "InvalidValue": + exceptionCode = 106 + elif tc[1] == "InvalidTypeException": + exceptionCode = 107 + else: + self.raise_transpile_error( + "InvalidValue: The Exception entered is not defined", ln + ) + + try: + if tc[2]: + description = getDescription() + return f'raiseException({exceptionCode}, "{description}");', "" + except IndexError: + return f'raiseException({exceptionCode}, "{description}");', "" + + def raise_transpile_error(self, text, ln="Unknown"): + print("TRANSPILATION ERROR:") + print(f"While processing line {ln}") + print(text) + raise SystemExit + + def variable_setting(self, tc, multiple_commands_index, ln): + # Error messages + invalid_value = "InvalidValue: Invalid value" + mismatch_type = "InvalidValue: Value doesn't match variable type." + all_variable_name = self.symbol_table.get_all_variable_name() + + if tc[1] == "=": # Set operator + res, error = self.analyseCommand(tc[2 : multiple_commands_index + 1]) + if error: + return res, error + value = "" + + for i in tc[2 : multiple_commands_index + 1]: + value += i + " " + value = value[:-1] + + valtype = parser.parse_type_from_value(res) + if valtype == Exceptions.InvalidSyntax: + return invalid_value, Exceptions.InvalidValue + vartype = self.symbol_table.get_variable_type(tc[0]) + # Check if Value Type matches Variable type + if valtype != vartype: + return mismatch_type, Exceptions.InvalidValue + res = parser.parse_escape_character(res) + if res in all_variable_name: + res = (self.symbol_table.get_variable(res))[1] + oldvar = self.symbol_table.get_variable(tc[0]) + if error: + self.raise_transpile_error(error[0], ln) + if oldvar[2]: + if oldvar[0] == Types.String: + error = self.symbol_table.set_variable( + tc[0], res, vartype, oldvar[2], len(res) - 1 + ) + if self.auto_reallocate: + if oldvar[3] < (len(res) - 1): + self.file_helper.insert_content( + f"{tc[0]} = realloc({tc[0]}, {len(res) - 1});" + ) + else: + if oldvar[3] > len(res) - 1 and oldvar[3] > 64: + self.file_helper.insert_content( + f"{tc[0]} = realloc({tc[0]}, {len(res) - 1});" + ) + else: + print( + "INFO: To set a Message to a String, Input string must be less than the Size specified or equal the Original string size If declared with initial value." + ) + if len(res) - 1 > oldvar[3]: + self.raise_transpile_error( + 'InvalidValue: The input string length is more than the Original Defined size. If you want a Dynamically allocated string, Please don\'t use "--no-auto-reallocate" option.', + ln, + ) + return f"memcpy({tc[0]}, {res}, {len(res) - 1});", "" + if oldvar[0] == Types.Dynamic: + res = res.removeprefix("new Dynamic (") + res = res[:-1] + return f"{tc[0]} = (void*){res};", "" + return f"*{tc[0]} = {res};", "" + if vartype == Types.String: + length = len(res) - 1 + error = self.symbol_table.set_variable( + tc[0], res, vartype, oldvar[2], length + ) + if length > oldvar[3]: + self.raise_transpile_error( + "InvalidValue: The input string length is more than the Original Defined size. If you want a Dynamically allocated string, Please use the Heap allocated string instead If you want to make the String dynamiccally allocated.", + ln, + ) + return f"memcpy({tc[0]}, {res}, {length});", "" + if oldvar[0] == Types.Dynamic: + res = res.removeprefix("new Dynamic (") + res = res[:-1] + return f"{tc[0]} = (void*){res};", "" + error = self.symbol_table.set_variable(tc[0], res, vartype, oldvar[2]) + return f"{tc[0]} = {res};", "" + elif tc[1] == "+=": # Add & Set operator + operator = "+" + elif tc[1] == "-=": # Subtract & Set operator + operator = "-" + elif tc[1] == "*=": # Multiply & Set operator + operator = "*" + elif tc[1] == "/=": # Divide & Set operator + operator = "/" + elif tc[1] == "%=": # Modulo Operaion & Set operator + operator = "%" + else: + res = "" + for i in tc: + res += i + " " + res = res[:-1] + return res, "" + res, error = self.analyseCommand(tc[2 : multiple_commands_index + 1]) + if error: + return res, error + value = "" + + for i in tc[2 : multiple_commands_index + 1]: + value += i + " " + value = value[:-1] + + valtype = parser.parse_type_from_value(res) + if valtype == Exceptions.InvalidSyntax: + return invalid_value, Exceptions.InvalidValue + vartype = self.symbol_table.get_variable_type(tc[0]) + # Check if Value Type matches Variable type + if valtype != vartype: + return mismatch_type, Exceptions.InvalidValue + res = parser.parse_escape_character(res) + if res in all_variable_name: + res = (self.symbol_table.get_variable(res))[1] + oldvar = self.symbol_table.get_variable(tc[0]) + error = self.symbol_table.set_variable(tc[0], res, vartype, oldvar[2]) + if error: + self.raise_transpile_error(error[0], ln) + if oldvar[2]: + if oldvar[0] == Types.String: + self.raise_transpile_error( + f"InvalidTypeException: You cannot use {operator}= with String.", ln + ) + if oldvar[0] == Types.Dynamic: + self.raise_transpile_error( + f"InvalidTypeException: You cannot use {operator}= with Dynamics.", + ln, + ) + return f"*{tc[0]} {operator}= {res};", "" + return f"{tc[0]} {operator}= {res};", "" + + def switch_case_statement(self, tc, ln): + cases = [] + case = [] + command = [] + isInCaseBlock = False + isInDefaultBlock = False + isAfterCaseKeyword = False + currentCaseKey = None + for i in tc[2:]: + if i == "case": + isAfterCaseKeyword = True + continue + if isAfterCaseKeyword: + outkey = i + if outkey.endswith(":"): + outkey = outkey[:-1] + currentCaseKey = outkey + isAfterCaseKeyword = False + isInCaseBlock = True + continue + if isInCaseBlock: + if i == "&&": + case.append(command) + command = [] + continue + if i == "break": + case.append(command) + cases.append((currentCaseKey, case)) + command = [] + case = [] + isInCaseBlock = False + continue + command.append(i) + if i == "end": + break + + defaultCase = False + + self.file_helper.insert_content(f"switch ({tc[1]})") + self.file_helper.insert_content("{") + self.file_helper.indent_level += 1 + for i in cases: + if i[0] == "default": + defaultCase = i + continue + self.file_helper.insert_content(f"case {i[0]}:") + self.file_helper.indent_level += 1 + for j in i[1]: + self.file_helper.insert_content(self.analyseCommand(j)[0]) + self.file_helper.insert_content("break;") + self.file_helper.indent_level -= 1 + if defaultCase: + self.file_helper.insert_content("default:") + self.file_helper.indent_level += 1 + for j in i[1]: + self.file_helper.insert_content(self.analyseCommand(j)[0]) + self.file_helper.insert_content("break;") + self.file_helper.indent_level -= 1 + self.file_helper.indent_level -= 1 + self.file_helper.insert_content("}") + + return "", "" + + def if_else_statement(self, tc: list, ln: int): + allvar = self.symbol_table.get_all_variable_name() + + def check_string_comparison(condition): + operator_index = 0 + compare_operator = {">", "<", "==", "!=", ">=", "<="} + for i in condition: + if i in compare_operator: + break + operator_index += 1 + + resl = bool( + parser.parse_type_from_value(" ".join(condition[:operator_index])) + == Types.String + ) + if "".join(condition[:operator_index]) in allvar: + resl = resl or ( + self.symbol_table.get_variable_type( + "".join(condition[:operator_index]) + ) + == Types.String + ) + + resr = bool( + parser.parse_type_from_value(" ".join(condition[operator_index + 1 :])) + == Types.String + ) + if "".join(condition[operator_index + 1 :]) in allvar: + resr = resr or ( + self.symbol_table.get_variable_type( + "".join(condition[operator_index + 1 :]) + ) + == Types.String + ) + + if resl and resr: + joinedstrleft = " ".join(condition[:operator_index]) + joinedstrright = " ".join(condition[operator_index + 1 :]) + return f"strcmp({joinedstrleft}, {joinedstrright}) == 0" + + return False + + conditionslist: list = parser.parse_conditions(tc[1:]) + finalString = "if (" + for i in conditionslist: + exprs = [] + currentConditionType = ConditionType.Single + for j in i: + if j and isinstance(j, list): + strcmp = check_string_comparison(j) + if strcmp: + exprs.append(strcmp) + else: + exprs.append(j) + elif isinstance(j, ConditionType): + currentConditionType = j + if currentConditionType == ConditionType.And: + for i in exprs: + finalString += parser.parse_string_list(i) + " && " + finalString = finalString[:-4] + elif currentConditionType == ConditionType.Single: + finalString += parser.parse_string_list(exprs[0]) + elif currentConditionType == ConditionType.Or: + for i in exprs: + finalString += parser.parse_string_list(i) + "||" + finalString = finalString[:-4] + finalString += ")" + + isInCodeBlock: bool = False + isInElseBlock: bool = False + have_passed_then_keyword: bool = False + ifstatement: dict = {"if": [], "else": None} + commands: list = [] + command: list = [] + endkeywordcount: int = 0 # All "end" keyword in the expression + endkeywordpassed: int = 0 # All "end" keyword passed + elsekeywordcount: int = 0 # All "else" keyword in the expression + elsekeywordpassed: int = 0 # All "else" keyword passed + for i in tc[2:]: + if i == "end": + endkeywordcount += 1 + elif i == "else": + elsekeywordcount += 1 + for i in tc: + if not have_passed_then_keyword and i == "then": + isInCodeBlock = True + have_passed_then_keyword = True + continue + if isInCodeBlock: + if i == "&&": + commands.append(command) + command = [] + continue + elif i == "end": + endkeywordpassed += 1 + if endkeywordcount == endkeywordpassed: + commands.append(command) + command = [] + if isInElseBlock: + ifstatement["else"] = commands + else: + ifstatement["if"] = commands + isInElseBlock = False + isInCodeBlock = False + continue + elif i == "else": + elsekeywordpassed += 1 + if ( + elsekeywordcount == elsekeywordpassed + and endkeywordpassed + 1 == endkeywordcount + ): + commands.append(command) + command = [] + ifstatement["if"] = commands + commands = [] + isInElseBlock = True + continue + command.append(i) + self.file_helper.insert_content(finalString) + self.file_helper.insert_content("{") + self.file_helper.indent_level += 1 + for i in ifstatement["if"]: + self.file_helper.insert_content(self.analyseCommand(i)[0]) + self.file_helper.indent_level -= 1 + if ifstatement["else"] is not None: + self.file_helper.insert_content("} else {") + self.file_helper.indent_level += 1 + for i in ifstatement["else"]: + self.file_helper.insert_content(self.analyseCommand(i)[0]) + self.file_helper.indent_level -= 1 + self.file_helper.insert_content("}") + else: + self.file_helper.insert_content("}") + + return "", "" + + def analyseCommand(self, tc, ln="Unknown", varcontext=None): + # All Keywords + basekeywords = { + "if", + "else", + "var", + "int", + "bool", + "float", + "list", + "dictionary", + "tuple", + "dynamic", + "const", + "override", + "func", + "end", + "print", + "input", + "throw", + "string", + "del", + "namespace", + "#define", + "loopfor", + "switch", + "input", + "exit", + } + + is_multiple_commands = False + multiple_commands_index = -1 + for i in tc: + multiple_commands_index += 1 + if i == "&&": + is_multiple_commands = True + break + + all_variable_name = self.symbol_table.get_all_variable_name() + all_function_name = self.symbol_table.get_all_function_name() + + # Error messages + paren_needed = "InvalidSyntax: Parenthesis is needed after a function name" + close_paren_needed = ( + "InvalidSyntax: Parenthesis is needed after an Argument input" + ) + + try: + if tc[0] in all_variable_name: + try: + return self.variable_setting(tc, multiple_commands_index, ln) + except IndexError: + res = "" + for i in tc: + res += i + " " + res = res[:-1] + return res, "" + elif tc[0] in basekeywords: + if tc[0] in { + "var", + "int", + "bool", + "float", + "list", + "dictionary", + "tuple", + "const", + "string", + "dynamic", + }: + try: + if tc[2] == "=" or tc[3] == "=": + pass + else: + raise IndexError + definedType = parser.parse_type_string(tc[0]) + if tc[1] in self.symbol_table.get_all_variable_name(): + self.raise_transpile_error( + f'AlreadyDefined: a Variable "{tc[1]}" is already defined', + ln, + ) + + # Checking for variable naming violation + if not (parser.check_naming_violation(tc[1])): + self.raise_transpile_error( + "InvalidValue: a Variable name cannot start with digits.", + ln, + ) + + # var(0) a(1) =(2) 3(3) + # double(0) heap(1) b(2) =(3) 5(4) + isHeap = False + if tc[1] == "heap": + isHeap = True + res, error = self.analyseCommand( + tc[4 : multiple_commands_index + 1], tc[2] + ) + if isinstance(error, Exceptions): + self.raise_transpile_error(res, ln) + else: + res, error = self.analyseCommand( + tc[3 : multiple_commands_index + 1], tc[1] + ) + if isinstance(error, Exceptions): + self.raise_transpile_error(res, ln) + value = "" + + for i in tc[3 : multiple_commands_index + 1]: + value += i + " " + value = value[:-1] + vartype = parser.parse_type_from_value(res) + # Check If existing variable type matches the New value type + if tc[0] != "var" and definedType != vartype: + self.raise_transpile_error( + "InvalidValue: Variable types doesn't match value type.", + ln, + ) + if vartype == Exceptions.InvalidSyntax: + self.raise_transpile_error( + "InvalidSyntax: Invalid value", ln + ) + res = parser.parse_escape_character(res) + + if isHeap: + outvartype = tc[0] + if tc[0] == "var": + outvartype = parser.convert_types_enum_to_string( + vartype + ) + if vartype == Types.String: + if "string.h" not in libraryIncluded: + libraryIncluded.append("string.h") + self.symbol_table.set_variable( + tc[2], res, vartype, True, len(res) - 1 + ) + self.file_helper.insert_content( + f"char *{tc[2]} = (char*)malloc({len(res) - 1});" + ) + return ( + f"if({tc[2]} != NULL) memcpy({tc[2]}, {res}, {len(res) - 1});", + "", + ) + if vartype == Types.Dynamic: + # dynamic[0] heap[1] a[2] =[3] new[4] Dynamic[5] (memsize)[6] + self.symbol_table.set_variable( + tc[2], res, vartype, True + ) + args = parser.parse_string_list(tc[6:]) + args = args[1:-1] + varval = parser.parse_string_list(args) + intval = parser.try_parse_int(varval) + if isinstance(intval, int): + if intval <= 2147483647 and intval >= -2147483647: + if ( + intval <= 9223372036854775807 + and intval >= -9223372036854775807 + ): + bytesize = 16 + else: + bytesize = 8 + else: + bytesize = 4 + else: + bytesize = len(varval) - 1 + try: + self.file_helper.insert_content( + f"void* {tc[2]} = malloc({bytesize});" + ) + return f"{tc[2]} = (void*){varval};", "" + except NameError: + return f"void* {tc[2]} = malloc({bytesize});" + self.symbol_table.set_variable(tc[2], res, vartype, True) + self.file_helper.insert_content( + f"{outvartype} *{tc[2]} = ({outvartype}*)malloc(sizeof({outvartype}));" + ) + return f"*{tc[2]} = {res};", "" + if tc[0] == "var": + outvartype = parser.convert_types_enum_to_string(vartype) + if vartype == Types.String: + self.symbol_table.set_variable( + tc[1], res, vartype, False, len(res) - 1 + ) + return f"char {tc[1]}[{len(res) - 1}] = {res};", "" + if vartype == Types.Dynamic: + # var[0] a[1] =[2] new[3] Dynamic[4] (memsize)[5] + self.symbol_table.set_variable( + tc[1], res, vartype, False + ) + args = parser.parse_string_list(tc[5:]) + args = args[1:-1] + varval = parser.parse_string_list(args) + intval = parser.try_parse_int(varval) + if isinstance(intval, int): + if intval >= 2147483647 or intval <= -2147483647: + if ( + intval >= 9223372036854775807 + or intval <= -9223372036854775807 + ): + bytesize = 16 + else: + bytesize = 8 + else: + bytesize = 4 + else: + bytesize = len(varval) - 1 + return f"void* {tc[1]} = (void*){varval};", "" + self.symbol_table.set_variable(tc[1], res, vartype, False) + return f"{outvartype} {tc[1]} = {res};", "" + if vartype == Types.String: + self.symbol_table.set_variable( + tc[1], res, vartype, False, len(res) - 1 + ) + return f"char {tc[1]}[{len(res) - 1}] = {res};", "" + if vartype == Types.Dynamic: + # dynamic[0] a[1] =[2] new[3] Dynamic[4] (memsize)[5] + self.symbol_table.set_variable(tc[1], res, vartype, False) + args = parser.parse_string_list(tc[5:]) + args = args[1:-1] + varval = parser.parse_string_list(args) + intval = parser.try_parse_int(varval) + if isinstance(intval, int): + if intval >= 2147483647 or intval <= -2147483647: + if ( + intval >= 9223372036854775807 + or intval <= -9223372036854775807 + ): + bytesize = 16 + else: + bytesize = 8 + else: + bytesize = 4 + else: + bytesize = len(varval) - 1 + return f"void* {tc[1]} = (void*){varval};", "" + self.symbol_table.set_variable(tc[1], res, vartype, isHeap) + return f"{tc[0]} {tc[1]} = {res};", "" + except IndexError: + # var(0) a(1) (Stack allocation) + # int(0) heap(1) a(2) (Heap allocation) + # string(0) heap(1) a(2) 20(3) (String heap allocation) + if tc[0] == "var": + self.raise_transpile_error( + "InvalidSyntax: Initial value needed for var keyword", + ln, + ) + vartype = parser.parse_type_string(tc[0]) + if vartype == Exceptions.InvalidSyntax: + self.raise_transpile_error( + "InvalidSyntax: Invalid type", ln + ) + if tc[1] == "heap": + if vartype == Types.String: + self.symbol_table.set_variable( + tc[2], None, vartype, True, int(tc[3]) + ) + if "string.h" not in libraryIncluded: + libraryIncluded.append("string.h") + return f"char *{tc[2]} = (char*)malloc({tc[3]});", "" + self.symbol_table.set_variable(tc[2], None, vartype, True) + return ( + f"{tc[0]} *{tc[2]} = ({tc[0]}*)malloc(sizeof({tc[0]}));", + "", + ) + self.symbol_table.set_variable(tc[1], None, vartype, False) + return f"{tc[0]} {tc[1]};", "" + elif tc[0] == "print": + value = "" + for i in tc[1 : multiple_commands_index + 1]: + value += i + " " + value = value[:-1] + if not value.startswith( + "(" + ): # Check If the expression has parentheses around or not + return ( + paren_needed, + Exceptions.InvalidSyntax, + ) # Return error if not exists + if not value.endswith( + ")" + ): # Check If the expression has parentheses around or not + return ( + close_paren_needed, + Exceptions.InvalidSyntax, + ) # Return error if not exists + value = value[1:-1] + svalue = value.split() + value = str(value) + return f"printf({value});", None + elif tc[0] == "input": + value = "" + for i in tc[ + 2 : multiple_commands_index + 1 + ]: # Get all parameters provided as 1 long string + value += i + " " + value = value[:-1] + if not value.startswith( + "(" + ): # Check If the expression has parentheses around or not + self.raise_transpile_error( + paren_needed, ln + ) # Return error if not exists + if not value.endswith( + ")" + ): # Check If the expression has parentheses around or not + self.raise_transpile_error( + close_paren_needed, ln + ) # Return error if not exists + value = value[1:-1] # Cut parentheses out of the string + return f'scanf("%s", &{varcontext})', { + int(tc[1]) + } # Return the Recieved Input + elif tc[0] == "if": + return self.if_else_statement(tc, ln) + elif tc[0] == "exit": + value = "" + for i in tc[ + 1 : multiple_commands_index + 1 + ]: # Get all parameters provided as 1 long string + value += i + " " + value = value[:-1] + if not value.startswith( + "(" + ): # Check If the expression has parentheses around or not + self.raise_transpile_error( + paren_needed, ln + ) # Return error if not exists + if not value.endswith( + ")" + ): # Check If the expression has parentheses around or not + self.raise_transpile_error( + close_paren_needed, ln + ) # Return error if not exists + value = value[1:-1] + valtype = parser.parse_type_from_value(value) + if value.startswith('"'): + self.raise_transpile_error( + "InvalidValue: Exit code can only be integer.", ln + ) + if value.endswith('"'): + self.raise_transpile_error( + "InvalidValue: Exit code can only be integer.", ln + ) + if not value: + self.raise_transpile_error( + 'InvalidValue: Parameter "status" (int) required.', ln + ) + return f"exit({int(value)});", valtype + elif tc[0] == "#define": + try: + # Set Interpreter Settings + if tc[1] == "interpet" and tc[2] == "ignoreInfo": + self.symbol_table.ignoreInfo = bool(tc[3] == "true") + return "", "" + except IndexError: + self.raise_transpile_error( + "InvalidValue: You needed to describe what you will change.", + ln, + ) + elif tc[0] == "throw": + return self.throw_keyword( + tc, multiple_commands_index + ) # Go to the Throw keyword function + elif tc[0] == "del": + if tc[1] not in all_variable_name: + self.raise_transpile_error( + f"NotDefinedException: The variable {tc[1]} is not defined.", + ln, + ) + if not self.symbol_table.get_variable(tc[1])[2]: + self.raise_transpile_error( + f"InvalidValue: The variable {tc[1]} is not heap allocated.", + ln, + ) + return f"free({tc[1]});", "" + elif tc[0] == "loopfor": + try: + commands = [] # list of commands + command = [] + endkeywordcount = 0 # All "end" keyword in the expression + endkeywordpassed = 0 # All "end" keyword passed + for i in tc[2:]: + if i == "end": + endkeywordcount += 1 + for i in tc[2:]: + if i == "&&": + commands.append(command) + command = [] + continue + if i == "end": + endkeywordpassed += 1 + if endkeywordcount == endkeywordpassed: + commands.append(command) + command = [] + break + command.append(i) + genvarname = None + genvarname = "__sts_loopcount_" + genvarname += ascii_letters[randint(0, 51)] + genvarname += ascii_letters[randint(0, 51)] + genvarname += ascii_letters[randint(0, 51)] + self.file_helper.insert_content( + f"for (int {genvarname} = 0; {genvarname} < {int(tc[1])}; {genvarname}++)" + + " {" + ) + self.file_helper.indent_level += 1 + for i in commands: + self.file_helper.insert_content(self.analyseCommand(i)[0]) + self.file_helper.indent_level -= 1 + self.file_helper.insert_content("}") + return "", "" + except ValueError: + self.raise_transpile_error( + "InvalidValue: Count must be an Integer. (Whole number)", ln + ) + elif tc[0] == "switch": + return self.switch_case_statement(tc, ln) + else: + self.raise_transpile_error( + "NotImplementedException: This feature is not implemented", ln + ) + elif tc[0] in all_function_name: + return parser.parse_string_list(tc), "" + elif tc[0] == "//": + return parser.parse_string_list(tc), "" + else: + return parser.parse_string_list(tc), "" + except IndexError: + return "", "" diff --git a/processor.py b/processor.py index 14dbb03..e09974f 100644 --- a/processor.py +++ b/processor.py @@ -7,61 +7,74 @@ GlobalVariableTable = SymbolTable() STORYSCRIPT_INTERPRETER_DEBUG_MODE = True + def parse_string_list(self, command): - res = "" - for i in command: - res += i + " " - res = res[:-1] - return res + res = "" + for i in command: + res += i + " " + res = res[:-1] + return res + def parse_file(out_file, file_name, auto_reallocate=True, minified=False): - """ - This method read the file and give it to the Parser, Then write the output data to file. - [PARAMETERS] - out_file = Output file name - file_name = Input file name - auto_reallocate = Turn on auto memory reallocation in the Output code or not. - minified = Tell the file writer to minify the file or not. - """ - tracemalloc.start() - start_time = perf_counter() - if STORYSCRIPT_INTERPRETER_DEBUG_MODE: # Check if the run mode was Debug mode or not. - from os import getcwd - print("[DEBUG] Current Working Directory: " + getcwd()) # Prints the current working directory - try: - f = open(file_name, "r") # Try open the file - except FileNotFoundError: - print(f"Cannot open file {fileName}. File does not exist.") # Print the error and terminate the function If the file does not exist. - return - if STORYSCRIPT_INTERPRETER_DEBUG_MODE and not auto_reallocate: - # a Debug message telling that autoreallocate is turned off. - print("[DEBUG] Auto reallocate turned off.") - # Creates a new Lexer for the Parsing operation - lexer = Lexer(GlobalVariableTable, out_file, auto_reallocate=auto_reallocate) - lexer.file_helper.minified = minified - # Read all the lines from the file - lines = f.readlines() - line_index = 0 - print("Conversion starting...") - # Looping through all lines. While using tqdm to update the progress bar as well. - for i in tqdm(lines, ncols=75): - line_index += 1 - commands = i.split() - # Insert the returned C code into the File content. - try: - if minified and len(commands) != 0 and commands[0] == "//": - continue - lexer.file_helper.insert_content(lexer.analyseCommand(commands, ln=line_index)[0]) - except Exception: - from traceback import print_exc - print_exc() - print(commands) - print("Conversion done. Writing data to file...") - # Include all libraries - for i in libraryIncluded: - lexer.file_helper.insert_header(f"#include <{i}>") - # Add Exception raising functionality to the C code - lexer.file_helper.insert_header(''' + """ + This method read the file and give it to the Parser, Then write the output data to file. + [PARAMETERS] + out_file = Output file name + file_name = Input file name + auto_reallocate = Turn on auto memory reallocation in the Output code or not. + minified = Tell the file writer to minify the file or not. + """ + tracemalloc.start() + start_time = perf_counter() + if ( + STORYSCRIPT_INTERPRETER_DEBUG_MODE + ): # Check if the run mode was Debug mode or not. + from os import getcwd + + print( + "[DEBUG] Current Working Directory: " + getcwd() + ) # Prints the current working directory + try: + f = open(file_name, "r") # Try open the file + except FileNotFoundError: + print( + f"Cannot open file {fileName}. File does not exist." + ) # Print the error and terminate the function If the file does not exist. + return + if STORYSCRIPT_INTERPRETER_DEBUG_MODE and not auto_reallocate: + # a Debug message telling that autoreallocate is turned off. + print("[DEBUG] Auto reallocate turned off.") + # Creates a new Lexer for the Parsing operation + lexer = Lexer(GlobalVariableTable, out_file, auto_reallocate=auto_reallocate) + lexer.file_helper.minified = minified + # Read all the lines from the file + lines = f.readlines() + line_index = 0 + print("Conversion starting...") + # Looping through all lines. While using tqdm to update the progress bar as well. + for i in tqdm(lines, ncols=75): + line_index += 1 + commands = i.split() + # Insert the returned C code into the File content. + try: + if minified and len(commands) != 0 and commands[0] == "//": + continue + lexer.file_helper.insert_content( + lexer.analyseCommand(commands, ln=line_index)[0] + ) + except Exception: + from traceback import print_exc + + print_exc() + print(commands) + print("Conversion done. Writing data to file...") + # Include all libraries + for i in libraryIncluded: + lexer.file_helper.insert_header(f"#include <{i}>") + # Add Exception raising functionality to the C code + lexer.file_helper.insert_header( + """ // Exception Raising void raiseException(int code, char* description) { @@ -94,45 +107,43 @@ def parse_file(out_file, file_name, auto_reallocate=True, minified=False): } exit(code); } -''') - lexer.file_helper.insert_header("int main() {") - lexer.file_helper.write_data_to_file() - print("Successfully written data to file.") - # Prints out statistics when done running. - print(" -- Statistics -- ") - current, peak = tracemalloc.get_traced_memory() - finish_time = perf_counter() - print(f'Memory usage:\t\t {current / 10**6:.6f} MB \n' - f'Peak memory usage:\t {peak / 10**6:.6f} MB ') - print(f'Time elapsed in seconds: {finish_time - start_time:.6f}') - print("-"*40) - # Stop the memory allocation tracking - tracemalloc.stop() +""" + ) + lexer.file_helper.insert_header("int main() {") + lexer.file_helper.write_data_to_file() + print("Successfully written data to file.") + # Prints out statistics when done running. + print(" -- Statistics -- ") + current, peak = tracemalloc.get_traced_memory() + finish_time = perf_counter() + print( + f"Memory usage:\t\t {current / 10**6:.6f} MB \n" + f"Peak memory usage:\t {peak / 10**6:.6f} MB " + ) + print(f"Time elapsed in seconds: {finish_time - start_time:.6f}") + print("-" * 40) + # Stop the memory allocation tracking + tracemalloc.stop() + if __name__ == "__main__": - # python processor.py -o main.c -i main.sts - print("// StoryScript C Transpiler // Version Alpha 1 //") - parser = argparse.ArgumentParser(description="Transpile StoryScript code into C code.") - parser.add_argument( - "-i", "--input", - help="The input file.", - required=True - ) - parser.add_argument( - "-o", "--output", - help="The target output file.", - required=True - ) - parser.add_argument( - "-no-realloc", - "--no-auto-reallocate", - action="store_true", - help="Tell the transpiler to not auto-reallocate memory." - ) - parser.add_argument( - "--minified", - action="store_true", - help="Tell the file writer to minify the output file or not." - ) - args = parser.parse_args() - parse_file(args.output, args.input, not args.no_auto_reallocate, args.minified) + # python processor.py -o main.c -i main.sts + print("// StoryScript C Transpiler // Version Alpha 1 //") + parser = argparse.ArgumentParser( + description="Transpile StoryScript code into C code." + ) + parser.add_argument("-i", "--input", help="The input file.", required=True) + parser.add_argument("-o", "--output", help="The target output file.", required=True) + parser.add_argument( + "-no-realloc", + "--no-auto-reallocate", + action="store_true", + help="Tell the transpiler to not auto-reallocate memory.", + ) + parser.add_argument( + "--minified", + action="store_true", + help="Tell the file writer to minify the output file or not.", + ) + args = parser.parse_args() + parse_file(args.output, args.input, not args.no_auto_reallocate, args.minified)