<reponame>StoryScriptorg/StoryScript-CTranspiler<filename>langParser.py
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

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

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

def try_parse_int(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

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"

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

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

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

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

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

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

	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)

	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

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

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

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

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

def try_parse_int(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

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"

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

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

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

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

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

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

	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)

	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

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
# for i in command: #	res += i + " " # res = res[:-1] 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. 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. Returns If the variable naming valid or not Parse If the condition is True or False # [:OperatorIndex] = Accessing a Message before the operator # [OperatorIndex + 1:] = Accessing a Message after the operator # Analyse the message on the left # Analyse the message on the right # Type conversion # If the operator was == # If the operator was > # If the operator was < # If the operator was != # If the operator was >= # If the operator was <= Separate expressions into a list of conditions # List of conditions # List of condition # Condition # Current condition type