Программист Вася изучает новый язык программирования &K*. Язык &K* по своему синтаксису напоминает языки семейства C, однако он гораздо более мощный, и поэтому правила реальных C-подобных языков к нему не применимы. Для полного понимания условия внимательно прочитайте описание языка ниже, и следуйте ему, а не аналогичным правилам в реальных языках программирования.
В &K* есть очень мощная технология указателей — к имеющемуся типу X можно приписать звездочку после него и получить тип X * . Это называется операцией взятия указателя. А есть противоположная операция — к любому типу X, являющемуся указателем, можно приписать слева амперсанд и получить тип &X, на который ссылается X. Это называется операцией разыменовывания.
В языке &K* есть только два базовых типа данных — void и errtype. Также в этом языке есть операторы typedef и typeof.
- Оператор "typedef A B" определяет новый тип данных B, эквивалентный типу A. В A могут быть звездочки и амперсанды, а в B — не могут. Например, оператор typedef void** ptptvoid создаст новый тип ptptvoid, который может использоваться в качестве void**.
- Оператор "typeof A" возвращает тип A, приведённый к void, т. е. возвращает эквивалентный ему тип void**...* с необходимым количеством звёздочек (возможно и нулевым). То есть после определения типа ptptvoid, как показано выше, оператор typeof ptptvoid вернёт void**.
Попытка разыменовать тип void приводит к ошибке: особому типу данных errtype. Для errtype верно следующее соотношение: errtype* = &errtype = errtype. Попытка использовать не объявленный ранее тип данных так же приведет к типу errtype.
С помощью typedef можно определить один тип несколько раз. Из всех определений силу имеет только последнее. Однако все типы, которые были определены с его использованием ранее, не меняются.
Заметим также, что операция разыменовывания имеет меньший приоритет, чем операция взятия указателя, иными словами &T * всегда равно T.
Учтите, что операторы выполняются последовательно, один за другим. Пусть у нас есть два оператора "typedef &void a" и "typedef a* b". Сначала a принимает значение errtype, а затем b принимает значение errtype* = errtype, но не &void* = void (см. пример 2).
Вася еще не до конца понимает эту мощную технологию, поэтому он попросил вас помочь в ней разобраться. Напишите программу, которая обрабатывает эти операторы и операции.
Примечание
Разберем второй пример.
После первых двух запросов typedef тип b эквивалентен void*, а с — void**.
Следующий запрос typedef переопределяет b — теперь он равен &b = &void* = void. Тип с при этом не меняется.
После чего тип с определяется как &&b* = &&void* = &void = errtype. На тип b это никак не влияет, поэтому следующий typedef определяет c, как &void* = void.
Далее тип b снова переопределяется как &void = errtype.
Отметим, что тип c в следующем запросе определяется именно как errtype******* = errtype, а не &void******* = void******. Тоже самое происходит и в последнем typedef.