[devel] I: alterator internals - 4

Stanislav Ievlev =?iso-8859-1?q?inger_=CE=C1_altlinux=2Eorg?=
Чт Июн 16 17:54:19 MSD 2005


Четвёртая часть рассказа.
Часть информации в ней будет касаться alterator которого пока ещё нет в
Сизифе, но он скоро до него доберётся.

2.10 Открываем капот.

Может быть из академических соображений так поступать не стоит, однако
иногда бывает проще рассказать про причину, чем про её следствия.
Давайте разберёмся с тем как работает интерпретатор схемы, благо знания
нам уже это позволяют сделать. После этого всё что было до сего момента
неясно уже станет очевидным.

Вот основной цикл работы интерпретатора:
 * прочитать очередное выражение
 * провести интерпретацию этого выражения

 Если мы находимся в командном интерпретаторе, то после последнего этапа
 ещё происходит печать получившегося выражения.

Вот собственно и всё! Теперь разберёмся с каждым шагом. Как и многое
другое процесс интерпретации разделён на несколько отдельных функций, что
позволяет контролировать его с точностью до миллиметра, если это конечно
надо.

Рассмотрим выражения:
3
"string"
(+ 1 3)
(string-append "str1" "str2")
(some-func a #t (+ 1 3))
a

2.10.1 Чтение

Прочитать выражение позволяет функция read. По-умолчанию, чтение
происходит со стандартного ввода. Глядя на примеры выражений и любовь
языка к списковым структурам в круглых скобках не сложно догадаться что мы получаем на
выходе из read ... правильно именно списки и получаем ну или просто
константы, если скобок не было.
Первое - константа - число 3
Второе - константа - строка "string"
Третье - список из констант:
  -- символ '+' (вот они символы-то! не спроста они в языке существуют ;))
  -- число 1
  -- число 3
Четвёртое - сами наверное уже догадались
Пятое - список из:
  -- константа - символ some-func
  -- константа - символ a
  -- константа - логическая ложь.
  -- ещё один список из: символа + и чисел 1 и 3.
Шестое - символ a.

Вот вам и весь парсер ;)

2.10.2 Исполнение

Исполнить полученный список, можно с помощью функции eval, eval передаются
два параметра: список и среда исполнения. Что-такое второй параметр не
будем пока заморачиваться, а примем как данность.

Исполнитель рекурсивно пробегается по всем вложенным спискам, большинство
констант интерпретируются в них самих (строки в строки, числа в числа), а
вот наткнувшись на символ, производится поиск соотв. переменной и в
результате подставляется то на что она ссылается (Note: здесь описана так
называемая подстановочная модель, она не точная зато, простая и понятная,
то что происходит в реальном интерпретаторе гораздо сложнее, но суть
остаётся примерно та же)

После того как все переменные разобраны, то если в результате остаётся
список, то происходит запуск функции. Первый элемент списка - собственно 
указатель на саму функцию, например в одном из наших примеров 
- это стандартная функция string-append, а все осташиеся - 
это аргументы функции.
Вычисляем указанную функцию от данных аргументов (в том же примере - 
это две константные строки "str1" и "str2") ... и получаем результат исполнения.

Пройдёмся по нашим примерам:
3 --> собственно 3 и получим, никаких функций запускать не надо
"string" --> получим строку
(+ 1 3) --> выполним функцию сложения от аргументов 1 и 3, результат - число 4.
(string-append "str1" "str2") - результат - строка "str1str2"
(some-func a #t (+ 1 3)) - получим результат выполнения функцию от аргументов:
 -- значение переменной a
 -- лочическая ложь
 -- результат сложения 1 и 3, то есть аргумент равен 4.
a --> Значение переменной a

2.10.3 Вывод результата на экран

Для вывода результата на экран есть множество функций, самая интересная из
них это, пожалуй, write.
Если в результате вы получили простую структуру, соcтоящую из списков,
возможно вложенных и каких-либо констант(строк, чисел, символов), то write
напечатает их в таком виде, что потом read может их обратно съесть.

Те кто знаком с языками типа Java узнает в этом знакомые вещи называемые
словами: сериализация и маршалинг ... только сделанные лет за дцать до этого ;)

Константы будут выведенны естественным образом
7 напечатается как 7
"str" напечатается как "str"
Списки опять напечатаются как знакомые выражения, окруженные скобками,
например список из 1 2 и 3 будет напечатан как (1 2 3)

2.10.4

Ну а теперь повторим пройдённое. Помните про функцию quote, которая
позволяла заполучить символы?
quote просто напросто говорит интерпретатору, что не надо исполнять eval
после read.
Поэтому 'a - это просто символ a
'(1 2 3) - это список из 1 2 3

2.11 Особые формы

Аргументы функции обрабатываются в некотором недокументированном порядке - 
зависит от реализации scheme.

В отдельных случаях хочется заранее знать как и когда будут аргументы
вычисленны. Поскольку эти "функции" такие особые, то и называются они
"особые формы", вот основные, которые нам потребуются при работе с
alterator.

2.11.1 Условные выражения

(if условие команда-если-истина команда-если-ложь)

присутствие команда-если-ложь традиционно необязательно.

Сначала вычисляется условие, если оно истинно (то есть не #f), то
вычисляется "команда-если-истина", иначе вычисляется "команда-если-ложь".

Например (if #f 3 4) - вернёт 4
         (if (+ 1 3) 5 6) - вернёт 5, поскольку результат (+ 1 3), то есть
	 4 "не ложь".
	 (if (string=? "aaa" "bbb") 3 5) - вернёт 5 ибо строки не равны
	 (if (number? 5) "number" "not number") - вернёт строку "number"
	 ибо 5 действительно число, а не что другое ;)
	 (if #f 3) - поскльку "команда-если-ложь" отсутствует, то if
	 вернёт некоторое волшебную сущность, которую иногда называют
	 unspecific, иногда unspecified  в общем "то не знаю что".

2.11.2 Множественное ветвление

Если в данной точке программы надо исследовать множество различных
вариантов, то используйте cond
Формат: (cond вариант1 вариант2 ... )

вариант - оформляется в виде (тест выражение1 выражение2 ... )

есть ещё специальный вариант (else выражение1 выражение2 ... ) который
применяется если никакой другой вариант не прошёл. else может
остуствовать, и должен быть всегда последним вариантом в случае 
присутствия.

Например давайте попробуем понять, а что же пришло в функцию: строка или
число, символ или что-то ещё?
(define (func x)
  (cond
    ((string? x) "строка")
    ((number? x) "число")
    ((symbol? x) "символ")
    (else "не знаю что такое")))

2.11.3 последовательное исполнение

Иногда в if допустима только одна команда на условие истины и одна на
условие лжи. А что делать если хочется исполнить сразу много команд в
случае некоторого условия? Иногда можно конечно обойтись имеющимися средствами,
но гораздо проще воспользоваться особой формой begin, которая вычисляет
свои аргументы строго последовательно слева направо. В качестве результата
возвращается результат работы последнего выражения.

Пример: (begin (+ 1 2)
	       (+ 3 4))
Сначала сложит 1 и 2, потом сложит 3 и 4 и вернёт в качестве результата 7.


2.12 Вернёмся к alterator

Попробуем применить полученные знания к alterator.

Попробуем сделать форму, состоящую из:
 - поля ввода (edit)
 - метки (label)
 - checkbox (не знаю как это сказать по-русски)
При вводе чего-либо в edit метка получит текст "something changed"
При нажатии Enter метка получит текст "enter pressed"
checkbox будет переключать режим отображения edit - нормальный и
звездочками (как при вводе пароля).

Раздадим имена виджетам: edit - editor
                         label - lbl
		         checkbox - setter.

На этом краткое введение в Scheme завершается, но не заканчивается. В ходе
дальнейшего рассказа про alterator мы попутно будем совершенствоваться в
этом языке и изучать новые подробности.

Если хотим узнать выставленна ли галочка у checkbox по именни setter,
переформулируем эту фразу на языке alterator следующим образом: (setter checked)

В результате получаем или #t или #f.

Итак, вот какое описание формы получается, если собрать всё воедино:
--
(id 'editor (edit ""
                  (on-change (lbl text "something changed"))
                  (on-return (lbl text "enter pressed"))))

(id 'lbl (label "some label"))
(id 'setter (checkbox "Password mode"
                      (on-click (if (setter checked)
                                  (editor echo stars)
                                  (editor echo yes)))))

--



Подробная информация о списке рассылки Devel