Wiki
Clone wikiMindStream / Статьи на русском / Устройство скриптовой машины / PROCEDURE, FUNCTION. Параметры справа и слева. Часть 1
Предыдущая серия была тут - О кешировании. Поговорим про добавление вложенных элементов.
Ну и тут поступил вопрос про параметры слов (функций).
Раз есть вопрос, то постараюсь описать - как определяются слова и их параметры.
Историю коммитов можно посмотреть тут.
Ещё раз оговорюсь:
Идеологически наша скриптовая машина построена на стековой FORTH-машине.
Так что - было бы неплохо ознакомится с языком FORTH.
Для "общего понимания".
Итак.
Простейшее слово определяется так:
#!delphi : // - признак начала слова A // - имя слова 1 >>std::out // - код слова - печатает число 1 или 1 . // - также печатает число 1 ; // - признак конца слова A // - вызов слова A
Пример можно скопировать в файл Example.script и запустить:
call.ms.script.exe Example.script или call.ms.script.exe Example.script > a.out
Утилита call.ms.script.exe находится тут.
Замечение: Утилита может молча не запускаться, это значит, что её блокирует антивирус. Так как она "получена не из благонадёжного источника".
В этом случае её стоит проверить антивирусом и включить в список разрешённых программ. Продолжим.
Естественно, как и в любом другом языке программирования, у нас слова могут иметь параметры.
Простейший пример:
#!delphi : A IN aParam // - определяем параметр aParam, слева от нашего слова A aParam // - получаем значение параметра . // - печатаем значение параметра : // A 1 A // - вызов нашего слова A с передачей ему ЗНАЧЕНИЯ числа 1 как значения параметра
Можно расширить пример и определить ТИП параметра. Вот так:
#!delphi : A INTEGER IN aParam // - определяем ЦЕЛОЧИСЛЕННЫЙ параметр aParam, слева от нашего слова A aParam // - получаем значение параметра . // - печатаем значение параметра : // A 1 A // - вызов нашего слова A с передачей ему ЗНАЧЕНИЯ числа 1 как значения параметра
Но о типах параметров и переменных мы поговорим чуть позже. В отдельной статье.
В качестве "лирического отступления" рекомендую ознакомиться с описанием "базовой аксиоматики".
До сих пор мы рассмотрели ОДИН параметр слева.
Рассмотрим теперь НЕСКОЛЬКО параметров слева.
Пример:
#!delphi : A INTEGER IN aParam1 // - определяем первый параметр INTEGER IN aParam2 // - определяем второй параметр aParam1 // - получаем значение параметра aParam1 aParam2 // - получаем значение параметра aParam2 + // - получаем сумму двух значений . // - печатаем получившийя результат ; // A 1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2
Хорошо. Мы поговорили про передачу параметров в слово.
А как получить значение из слова?
Разберём этот вопрос.
Простейший пример:
#!delphi : A INTEGER IN aParam1 // - определяем первый параметр INTEGER IN aParam2 // - определяем второй параметр aParam1 // - получаем значение параметра aParam1 aParam2 // - получаем значение параметра aParam2 + // - получаем сумму двух значений // - тут ничего не печатаем, а просто оставляем полученное значение на стеке ; // A 1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2 . // - печатаем значение со стека, фактически то, что нам вернула функция A
Тут есть один недостаток (он же на самом деле - преимущество) - вызываемая функция может не положить на стек НИЧЕГО, может положить ОДНО значение, а может положить НЕСКОЛЬКО значений.
А вызывающая сторона этот факт не сможет проконтролировать.
Как быть?
Для этого нам надо определить ТИП ВОЗВРАЩАЕМОГО значения.
Пример:
#!delphi INTEGER // - определяем тип возвращаемого значения и "неявную переменную" Result : A INTEGER IN aParam1 // - определяем первый параметр INTEGER IN aParam2 // - определяем второй параметр aParam1 // - получаем значение параметра aParam1 aParam2 // - получаем значение параметра aParam2 + // - получаем сумму двух значений >>> Result // - снимаем значение со стека и кладём его в переменную Result. ; // A 1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2 . // - печатаем значение со стека, фактически то, что нам вернула функция A
Но "есть одно но".
Возврат - скриптовая машина - контроллирует, а вот забор со стека - нет.
И можно написать так.
#!delphi INTEGER // - определяем тип возвращаемого значения и "неявную переменную" Result : A INTEGER IN aParam1 // - определяем первый параметр INTEGER IN aParam2 // - определяем второй параметр aParam1 // - получаем значение параметра aParam1 aParam2 // - получаем значение параметра aParam2 + // - получаем сумму двух значений aParam1 и aParam2 + // - получаем сумму предыдущего значения и того, что лежало на стеке >>> Result // - снимаем значение со стека и кладём его в переменную Result. ; // A 1 2 3 A // - вызываем наше слово A для ТРЁХ значений - 1, 2 и 3
Для этого есть "аналоги" слова : - FUNCTION и PROCEDURE.
Пример:
#!delphi INTEGER // - определяем тип возвращаемого значения и "неявную переменную" Result FUNCTION // - используем FUNCTION вместо : A INTEGER IN aParam1 // - определяем первый параметр INTEGER IN aParam2 // - определяем второй параметр aParam1 // - получаем значение параметра aParam1 aParam2 // - получаем значение параметра aParam2 + // - получаем сумму двух значений >>> Result // - снимаем значение со стека и кладём его в переменную Result. ; // A 1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2 . // - печатаем значение со стека, фактически то, что нам вернула функция A
Ну и пример использования слова PROCEDURE:
#!delphi PROCEDURE // - используем PROCEDURE вместо : A INTEGER IN aParam1 // - определяем первый параметр INTEGER IN aParam2 // - определяем второй параметр aParam1 // - получаем значение параметра aParam1 aParam2 // - получаем значение параметра aParam2 + // - получаем сумму двух значений . // - печатаем полученное значение ; // A 1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2
Подведём итоги.
В данной статье мы рассмотрели ключевые слова :
, ;
, IN
, FUNCTION
, PROCEDURE
.
А также передачу параметров словам и возврат значений из них.
Также мы слегка коснулись типизации значений.
Кроме типа INTEGER бывают ещё - STRING, OBJECT, CLASS, INTERFACE, CHAR, ARRAY, FILE, BOOLEAN.
А также есть ещё ANY - которое означает "значение любого типа". И PRINTABLE - которое означает "любое значение пригодное для вывода на печать". А также - VOID, который означеает "гарантированное отсутствие значения".
Также есть ещё типы ITERATABLE, ORDINAL и ATOMIC. О них мы также поговорим позже.
Пока лишь пример из аксиоматики:
#!delphi INTEGER BOOLEAN TYPE ORDINAL STRING ORDINAL TYPE ATOMIC ATOMIC TYPE COMPARABLE FILE ARRAY TYPE ITERATABLE ITERATABLE ATOMIC CASTABLE INTERFACE TYPE ANY ANY TYPE VARIANT ARRAY TYPE ITERATOR FUNCTOR TYPE VAR_REF
Надеюсь, что данная статья была вам полезна.
Updated