# coding: utf8
"""
Модуль предоставляет класс для разбора типа.
Простой тип может содержать определения других подтипов и членов.
"""

if True:
	from пакОберон.пакКомпилятор.пакСущность.пакСлово import тСлово
	from пакОберон.пакКомпилятор.пакСущность.пакРод import тРод

	from .модАнализТипБазовый import тАнализТипБазовый
	from . модАнализТипВстроен import тАнализТипВстроен
	from . модАнализТипПерем import тАнализТипПерем
	from . модАнализТипМассив import тАнализТипМассив
	from . модАнализТипЗапись import тАнализТипЗапись
	from . модАнализТипПроцедура import тАнализТипПроцедура

	from пакОберон.пакКомпилятор.пакСущность.пакОшибка import тОшибка

class тАнализТип(тАнализТипБазовый):
	def __init__(сам, пОберон, пДанные):

		сам.__оберон = пОберон
		сам.__конс = пОберон.конс
		сам.__конс.Отладить("тАнализТип.__init__()")

		сам.ош = тОшибка(пОберон, "тАнализТип")

		тАнализТипБазовый.__init__(сам, пОберон, пДанные)
		if сам.ошб.бВнутр:
			сам.__конс.ОшВнутр("__init__()", "При создании предка тАнализБазовый")
			return
		if сам.ошб.бИсх:
			сам.ош.Исх("__init__()", "При создании предка тАнализБазовый")
			return
		сам.__род = тРод.сБезРода # Род записи -- один из встроенных, ARRAY, RECORD, PROCEDURE
		сам.__предок = тРод.сБезТипа # Тип предка -- один из наследуемых
		сам.__имя = "" # Временная переменная
		сам.__бУказатель = False # временный признак POINTER TO
		сам.__бЭкспорт = False # Временное хранение экспорта
		сам.массив_тип = тРод.сБезТипа # устанавливает тип массива (если тип -- массив)
		сам.поля = {} # словарь полей типа
		сам.тип = None # ссылка на полученный тип
		if len(сам.слова_секции) < 2:
			сам.ош.Внутр("__init__()", "Получена пустая секция слов")
			return
		сам.массив_размерность = {} # заполняется при определении массива
		сам.запись_поля = {} # словарь полей типа RECORD

		сам.__индекс = 0 # Смещение в словаре слов
		сам.__Имя_Проверить()     # проверяет имя всех типов
		сам.__Экспорт_Проверить() # проверяет экспортируемый ли тип
		сам.__Определитель_Проверить() # Проверяет литеру "="
		сам.__Указатель_Проверить()    # Проверяет на наличие POINTER TO
		# Проверяет род указателя (массив, процедура, встроенный тип и т. д.)
		сам.__Род_Проверить()

	def __Имя_Проверить(сам):
		"""
		Проверяет имя типа. Должно быть именем и
		не должно быть END.
		Имя НЕ МОЖЕТ быть составным
		"""
		слово_имя = сам.слова_секции[сам.__индекс]
		if слово_имя.ЕслиИмя_Строго():
			сам.__имя = слово_имя.строка
			сам.__индекс += 1
		else:
			сам.__бОшИсх = True
			стрОш = "тАнализТип: ошибка исходника. Имя типа должно быть допустимым именем" + слово_имя.стрИсх
			сам.__конс.Ошибка(стрОш)
			return

	def __Экспорт_Проверить(сам):
		"""
		Проверяет является ли тип экспортируемым.
		"""
		слово_экспорт = сам.слова_секции[сам.__индекс]
		if слово_экспорт.строка == "*": # есть экспорт
			#сам.СловаСекции_Обрезать()
			сам.__бЭкспорт = True
			сам.__индекс += 1
		elif слово_экспорт.строка == "=":
			pass # это определение типа
		else:
			сам.__бОшИсх = True
			стрОш =  "тАнализТип: ошибка исходника. Символ экспорта допустим '*' или '='" + слово_экспорт.стрИсх
			сам.__конс.Ошибка(стрОш)
			return

	def __Определитель_Проверить(сам):
		"""
		Проверяет является ли слово в начале слов секции типа -- "=".
		После обрезания, должно быть первым.
		"""
		слово_опр = сам.слова_секции[сам.__индекс]
		if слово_опр.род == тСлово.кРавно: # правильное выражение определения типа
			#сам.СловаСекции_Обрезать()
			сам.__индекс += 1
		else: # если определение типа
			сам.__бОшИсх = True
			стрОш = "тАнализТип: ошибка исходника. Отсутствует определитель \"=\" в объявлении типа" + слово_опр.стрИсх
			сам.__конс.Ошибка(стрОш)
			return

	def __Указатель_Проверить(сам):
		"""
		Проверяет наличие POINTER TO
		Если тип является указателем, то устанавливается соответствующий признак.
		Иначе, просто пропускается.
		"""
		слово_указ = сам.слова_секции[сам.__индекс]
		if слово_указ.строка == "POINTER": # модификатор типа, а не сам тип
			сам.__индекс += 1
			"""
			======== Проверяет, что после POINTER __ОБЯЗАТЕЛЬНО__ TO ==========
			"""
			слово_из = сам.слова_секции[сам.__индекс]
			if слово_из.строка != "TO":
				сам._бОшИсх = True
				стрОш = "тАнализТип: ошибка исходника. За POINTER не следует TO" + слово_из.стрИсх
				сам.__конс.Ошибка(стрОш)
				return
			#сам.СловаСекции_Обрезать()
			сам.__бУказатель = True
			сам.__индекс += 1
		else:
			сам.__бУказатель = False # Обязательно принудительно сбросить. Временный признак

	def __ЧастныйПредок_Проверить(сам, пСлово):
		"""
		Проверяет предка записи. Должно быть разрешённой строкой и
		НЕ ДОЛЖНО быть END.
		Кроме того, имя может быть составным
		"""
		# пСлово уже обрезано при рассмотрении типа
		if type(пСлово) != тСлово:
			сам.__бОшВнутр = True
			стрОш = "тАнализТип: ошибка компилятора. пСлово должно быть тСлово, type="+str(type(пСлово))
			сам.__конс.ОшВнутр(стрОш)
			return
		if not пСлово.ЕслиСтр_Допустимо():
			сам.__бОшИсх = True
			стрОш = "тАнализТип: ошибка исходника. Имя типа должно быть допустимым именем "+ стрОш + пСлово.строка + пСлово.стрИсх
			сам.__конс.Ошибка(стрОш)
			return
		if сам.__предок != тРод.сБезТипа:
			сам.__бОшВнутр = True
			стрОш = "тАнализТип: ошибка компилятора. Предок уже назначен, предок=" + сам.__предок+ слово_имя.стрИсх
			сам.__конс.ОшВнутр(стрОш)
			return
		сам.__предок = ""
		имя = пСлово.Проверить()
		# так как это слово уже обрезано -- пропускаем обрезание словаря
		if (имя == ".") or (пСлово.ЕслиСтр_Допустимо()):
			сам.__предок += имя
			слово_имя = сам.слова_секции[0]
			имя = слово_имя.Проверить()
			бРезульт = True
			сам.СловаСекции_Обрезать()

		слово_имя = сам.слова_секции[0]
		имя = слово_имя.Проверить()
		# проверить до конца имя типа
		while (имя == ".") or (слово_имя.ЕслиСтр_Допустимо()):
			сам.СловаСекции_Обрезать()
			сам.__предок += имя
			слово_имя = сам.слова_секции[0]
			имя = слово_имя.Проверить()
			бРезульт = True
		# теперь надо обрезать окончание типа
		сам.Разделитель_Обрезать()

	def __Род_Проверить(сам):
		"""
		Проверяет род встреченного типа. Возможны варианты:
		1. Алиас встроенного типа.

		2.Массив чего-либо

		3. Пустая запись, должна заканчиваться "END;" (просто запись без членов -- бесполезна,
			но для расширения в шине сообщений -- бывает может пригодиться), скорей всего с
			множеством полей.

		4. Запись с полями, множественные уровни вложенности. Рекурсивный анализ.
			Заканчивается на "END;", но после слова "RECORD" идёт не "END"

		5. ....
		"""

		парам = {}
		парам['секция']  = "анализ"
		парам['слова']   = сам.слова_секции

		слово_тип = сам.слова_секции[сам.__индекс]
		#строка_род = слово_тип.Проверить()
		# тип не может начинаться на "." или цифру, или спецсимвол
		if not слово_тип.ЕслиСтр_Допустимо():
			стрОш = "Род типа должен быть допустимым именем" + слово_тип.стрИсх
			сам.ош.Внутр("__Род_Проверить()", стрОш)
			return
		if слово_тип.строка in тРод.тип_встроен: # текущий тип основан на встроенном типе
			сам.тип = тАнализТипВстроен(сам.__оберон, парам)
			if сам.тип.ош.бВнутр:
				стрОш = "При создании тАнализТипВстроен " + слово_тип.стрИсх
				сам.ош.Внутр("__Род_Проверить()", стрОш)
				return
			if сам.тип.ош.бИсх:
				стрОш = "При создании тАнализТипВстроен " + слово_тип.стрИсх
				сам.ош.Исх("__Род_Проверить()", стрОш)
				return
		elif слово_тип.строка == тРод.сМассив: # текущий тип основан на массиве
			сам.тип = тАнализТипМассив(сам.__оберон, парам)
			if сам.тип.ош.бВнутр:
				стрОш = "При создании тАнализТипМассив " + слово_тип.стрИсх
				сам.ош.Внутр("__Род_Проверить()", стрОш)
				return
			if сам.тип.ош.бИсх:
				стрОш = "При создании тАнализТипМассив " + слово_тип.стрИсх
				сам.ош.Исх("__Род_Проверить()", стрОш)
				return
		elif слово_тип.строка == тРод.сЗапись: # текущий тип основан на записе
			сам.тип = тАнализТипЗапись(сам.__оберон, парам)
			if сам.тип.ош.бВнутр:
				стрОш = "При создании тАнализТипЗапись " + слово_тип.стрИсх
				сам.ош.Внутр("__Род_Проверить()", стрОш)
				return
			if сам.тип.ош.бИсх:
				стрОш = "При создании тАнализТипЗапись " + слово_тип.стрИсх
				сам.ош.Исх("__Род_Проверить()", стрОш)
				return
		elif слово_тип.строка == "PROCEDURE": # текущий тип основан на процедуре
			сам.тип = тАнализТипПроцедура(сам.__оберон, парам)
			if сам.тип.ош.бВнутр:
				стрОш = "При вызове анализа PROCEDURE " + слово_тип.стрИсх
				сам.ош.Внутр("__Род_Проверить()", стрОш)
				return
			if сам.тип.ош.бИсх:
				стрОш = "При вызове анализа PROCEDURE " + слово_тип.стрИсх
				сам.ош.Исх("__Род_Проверить()", стрОш)
				return
		elif слово_тип.ЕслиСтр_Допустимо(): # Пользовательский тип
			сам.тип = тАнализТипПерем(сам.__оберон, парам)
			if сам.тип.ош.бВнутр:
				стрОш = "При создании тАнализТипПерем " + слово_тип.стрИсх
				сам.ош.Внутр("__Род_Проверить()", стрОш)
				return
			if сам.тип.ош.бИсх:
				стрОш = "При создании тАнализТипПерем " + слово_тип.стрИсх
				сам.ош.Исх("__Род_Проверить()", стрОш)
				return
		else:
			стрОш = "Неизвестный тип записи" + слово_тип.стрИсх
			сам.ош.Внутр("__Род_Проверить()", стрОш)
			return
		if сам.тип == None:
			сам.слова_секции = {}
			сам.слова_секции = сам.__тип.слова_секции
			стрОш = "Прерывание в этом месте исходником не предусмотрено, неизвестная ошибка"+слово_тип.стрИсх
			сам.ош.Внутр("__Род_Проверить()", стрОш)
		сам.слова_cекции = {}
		сам.слова_секции = сам.тип.слова_секции
		#сам.Предок_Уст(сам.__тип.предок)

	@property
	def имя(сам):
		return сам.__имя

	def Паспорт_Печать(сам):
		сам.__конс.Печать("+ тАнализТип: имя_типа=", сам.имя)
		сам.__конс.Печать("|      экспорт =", сам.тип.бЭкспорт)
		сам.__конс.Печать("|    указатель =", сам.тип.бУказатель)
		сам.__конс.Печать("|       предок =", сам.тип.предок)
		if сам.тип.предок == тРод.сМассив:
			for ключ in range(len(сам.тип.массив_размерность)):
				сам.__конс.Печать("|      ключ ", ключ, ":",сам.тип.массив_размерность[ключ])
			сам.__конс.Печать("|      элем    =", сам.тип.элем)
		elif сам.тип.предок == тРод.сЗапись:
			for ключ in range(len(сам.тип.запись_поля)):
				сам.тип.запись.поля[ключ].Паспорт_Печать()
		сам.__конс.Печать("+" + "-"*35+"\n")
