# coding: utf8
"""
Модуль предоставляет тип слова для построения AST.
Настройка пакета слов.
Слово -- кусочек текста в исходнике.
Обладает несколькими свойствами:
1. Литеры (само слово)
2. Координаты.
3. Строка, в которой он находится.
"""
if True:
	from пакОберон.пакКомпилятор.пакСущность.пакКоорд import тКоордФикс
	from numba import jit #type:ignore
	from пакОберон.пакКомпилятор.пакСущность.пакОшибка import тОшибка

class тСлово:
	"""
	Тип тСлово описывает слово, как отдельную сущность в составе исходного
	текста, обладает различными атрибутами.
	"""
	if True:
		всего_слов = 0 # глобальный счётчик
		кПусто = 0
		кМодульИмя   = кПусто + 1
		кМодульАлиас = кМодульИмя   + 1
		кЗапятая     = кМодульАлиас + 1
		кТочкаЗапятая= кЗапятая + 1
		кИмя         = кТочкаЗапятая + 1
		кКомментНачать=кИмя   + 1
		кКомментЗакончить=кКомментНачать + 1
		кОпределить  = кКомментЗакончить + 1
		кПрисвоить   = кОпределить  + 1
		кСкобкаОткрКругл=кПрисвоить + 1
		кСкобкаЗакрКругл=кСкобкаОткрКругл + 1
		кДеление     = кСкобкаЗакрКругл   + 1
		кУмножить    = кДеление + 1
		кМинус       = кУмножить+ 1
		кПлюс        = кМинус   + 1
		кЧисло       = кПлюс    + 1
		кСтрока      = кЧисло   + 1
		кРавно       = кСтрока  + 1
		кТочка       = кРавно   + 1
	запр_имя     = ["MODULE", "IMPORT", "CONST", "TYPE", "BOOLEAN", "BYTE", \
							"INTEGER", "CHAR", "SET", "REAL", "VAR", "POINTER", "TO", \
							"ARRAY", "OF", "BEGIN", "END", "PROCEDURE", "FOR", \
							"WHILE", "DO", "RECORD", "IS" ]
	__slots__ = ("__оберон", "__конс", "__исх", "__род", "ош", "__строка", \
						"__номер", "коорд", "__стрИсх")
	def __init__(сам, пОберон, пИсх, пСтрока):
		def пСтрока_Проверить():
			if type(пСтрока) != str:
				стрСообщ = "пСтрока должно быть строкой, type(пСтрока)="+str(type(пСтрока))
				сам.ош.Внутр("__init__()", стрОш)
				return

			if len(пСтрока) == 0:
				стрСообщ = "пСтрока не может быть пустой"
				сам.ош.Внутр("__init__()", стрОш)
				return
		def Род_Проверить():
			"""
			Устанавливает род слова.
			"""
			if пСтрока == ";":
				сам.__род = тСлово.кТочкаЗапятая
			elif пСтрока == ",":
				сам.__род = тСлово.кЗапятая
			elif пСтрока == "+":
				сам.__род = тСлово.кПлюс
			elif пСтрока == "-":
				сам.__род = тСлово.кМинус
			elif пСтрока == "/":
				сам.__род = тСлово.кДеление
			elif пСтрока == "(":
				сам.__род = тСлово.кСкобкаОткрКругл
			elif пСтрока == "(*":
				сам.__род = тСлово.кКомментНачать
			elif пСтрока == ")":
				сам.__род = тСлово.кСкобкаЗакрКругл
			elif пСтрока == "*)":
				сам.__род = тСлово.кКомментЗакончить
			elif пСтрока == "*":
				сам.__род = тСлово.кУмножить
			elif пСтрока == ":=":
				сам.__род = тСлово.кПрисвоить
			elif пСтрока == ":":
				сам.__род =  тСлово.кОпределить
			elif пСтрока[0]=="_" or пСтрока[0].isalpha():
				сам.__род =  тСлово.кИмя
			elif пСтрока[0].isdigit() or пСтрока[0] == ".":
				сам.__род =  тСлово.кЧисло
			elif пСтрока == "=":
				сам.__род =  тСлово.кРавно
			elif пСтрока == ".":
				сам.__род =  тСлово.кТочка
			elif пСтрока[0] == '"' and пСтрока[-1] == '"':
				сам.__род =  тСлово.кСтрока
			else:
				стрОш = "Не могу классифицировать строку, строка="+пСтрока
				сам.ош.Исх("Род_Проверить()", стрОш)

		сам.__оберон = пОберон
		сам.__конс = пОберон.конс
		сам.__исх = пИсх
		сам.__род = "" # род слова
		сам.ош = тОшибка(пОберон, "тСлово")

		пСтрока_Проверить()
		if сам.ош.бВнутр:
			return

		сам.__строка = пСтрока
		Род_Проверить()
		if сам.ош.бИсх:
			return

		сам.__номер = тСлово.всего_слов
		тСлово.всего_слов += 1

		сам.коорд = тКоордФикс(пОберон, пИсх.коорд.цСтр, пИсх.коорд.цПоз)
		if сам.коорд.ош.бВнутр:
			сам.ош.Внутр("__init__()", "При создании тКоордФикс")

		сам.__стрИсх = пИсх.строки(пИсх.коорд.цСтр)

	@property
	def стрИсх(сам)->str:
		"""
		Возвращает строку исходника, где это слово
		"""
		if type(сам.__стрИсх) != str:
			стрСообщ = "Строка исходника должна быть str, type="+str(type(сам.__стрИсх))
			сам.ош.Внутр("стрИсх", стрСообщ)
			return ""

		if сам.__стрИсх == "":
			сам.ош.Внутр("стрИсх", "Строка исходника не может быть пустой")
			return ""

		стр_ном=str(сам.__номер)
		while len(стр_ном) < 4:
			стр_ном = " " + стр_ном
		стр_указ = (len(стр_ном)+2) * "-" + "-" * сам.коорд.цПоз + "^" +"   Коорд: " + str(сам.коорд.цСтр) + "." + str(сам.коорд.цПоз)
		return "\n" + стр_ном + ": " + сам.__стрИсх + "\n" + стр_указ

	@property
	def строка(сам)->str:
		"""
		Возвращает строковое представление тСлово
		"""
		return сам.__Проверить()

	@property
	def номер(сам)->int:
		"""
		Возвращает порядковый номер слова в исходнике.
		"""
		if type(сам.__номер) != int:
			стрСообщ = "Номер тСлово должна быть int, type="+str(type(сам.__номер))
			сам.ош.Внутр("номер", стрСообщ)
			return ""
		if сам.__номер < 0:
			сам.ош.Внутр("номер", "Номер тСлово не может быть меньше нуля")
			return -3
		return сам.__номер

	def ЕслиСтр_Допустимо(сам)->bool:
		"""
		Проверяет на допустимость строки для имени сущности, первая литера должна быть "_"
		или буква.
		1) без специальных символов.
		2) без точки
		3) цифры разрешены
		4) ключевые слова разрешены
		"""
		бВыход = False
		if сам.__строка[0]=="_" or сам.__строка[0].isalpha():
			for лит in сам.__строка:
					# Точка в имени -- допустимо, но здесь её не будет.
					if not (лит in ".~`!@$%^&*()-_=+{}[]|\\<,>?/\"№;:/"):
						бВыход = True
						break
		return бВыход

	def ЕслиИмя_Строго(сам)->bool:
		'''
		Проверяет на допустимость литер в слове для обнаружения имени сущности.
		Не допускает зарезервированные слова.
		1) без специаяльных символов
		2) точка разрешена
		3) цифры разрешены
		4) ключевые слова запрещены
		'''
		# имя сущности должно начинаться либо с "_", либо с буквы
		бВыход = False
		if сам.__строка[0]=="_" or сам.__строка[0].isalpha():
			if not (сам.__строка) in тСлово.запр_имя:
				for лит in сам.__строка:
					# Точка в имени -- допустимо, но здесь её не будет.
					if not (лит in "~`!@$%^&*()-_=+{}[]|\\<,>?/\"№;:/"):
						бВыход = True
						break
		return бВыход

	def __Проверить(сам)->str:
		"""
		Возвращает строковое представление тСлово.Проверяет слово на допустимость:
		1. Совпадние типа.
		2. Строка тСлово не может быть пустой
		"""
		if type(сам.__строка) != str:
			стрСообщ = "Строка тСлово должна быть 'str', type=" + str(type(сам.__строка))
			сам.ош.Внутр("Проверить()", стрСообщ)
			return ""

		if сам.__строка == "":
			сам.ош.Внутр("Проверить()", "тСтрока тСлово не может быть пустой")
			return ""

		return сам.__строка

	def __str__(сам):
		стрСлово = сам.строка
		while len(стрСлово) < 7:
			стрСлово = " " + стрСлово
		return str(сам.__номер)+"\tслово="+ стрСлово + "\t" + str(сам.коорд)

	@property
	def род(сам)->str:
		#TODO: переделать род в int
		return сам.__род
