From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Date: Thu, 16 Jun 2005 17:54:19 +0400 From: Stanislav Ievlev To: devel@altlinux.org Message-ID: <20050616135419.GC23402@basalt.office.altlinux.org> Mime-Version: 1.0 Content-Type: text/plain; charset=koi8-r Content-Disposition: inline Content-Transfer-Encoding: 8bit Cc: Subject: [devel] I: alterator internals - 4 X-BeenThere: devel@altlinux.ru X-Mailman-Version: 2.1.5 Precedence: list Reply-To: ALT Devel discussion list List-Id: ALT Devel discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 16 Jun 2005 14:07:27 -0000 Archived-At: List-Archive: List-Post: Четвёртая часть рассказа. Часть информации в ней будет касаться 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))))) --