Особенности реализации функций ввода-вывода
В приведенном языке для ввода и вывода данных определены две
«встроенные» функции print и input, реализация которых не может быть
переопределена пользователем. Тем не менее на самом деле обе они не
имеют под собой реализации на этом языке, и в местах их вызова
происходит обращение к стандартной библиотеке языка Си.
Более того, в силу специфики использования функции print и input
можно считать в большей степени языковыми конструкциями, нежели
функциями. Функция print может принимать одно или несколько выражений
любого из поддерживаемых типов в качестве аргументов (в языке C++,
например, такое поведение функций называется перегрузкой), что для
пользовательских функций запрещено. Функция input ведет себя еще более
нестандартно: она может находиться только в операциях присваивания и
только в качестве правого операнда, при этом не принимает аргументов, но
тип возвращаемого значения при ее вызове определяется типом переменной в
роли левого операнда того же выражения (фигурально такое поведение можно
назвать «перегрузкой по типу возвращаемого значения», по аналогии с
обычными перегрузками из C++).
Рассмотрим теперь, каким образом вызовы этих функций компилируются во время генерации кода LLVM IR.
В месте вызова функции print происходит вставка инструкции LLVM IR,
декларирующей вызов функции printf. Как известно, эта функция
принимает форматирующую строку и список выражений, тип которых должен
совпадать с типами, соответствующими шаблонным последовательностям в
форматирующей строке. Таким образом, при генерации кода инструкция
вызова printf содержит два передаваемых аргумента:
форматирующую строку, состоящую из одной шаблонной последовательности (типу int соответствует
%d, типу float соответствует%lf, типу str соответствует%s, остальные типы содержат фиксированное множество значений и свою строку для каждого из значений:True,False,None);значение выражения определенного типа, которое должно быть выведено в стандартный поток вывода.
В месте вызова функции input происходит аналогичная вставка вызова
функции scanf. Принцип использования этой функции отличается тем, что
вместо значения выражения должен передаваться адрес переменной, в
которую будет записано значение, полученное из стандартного потока
ввода.