Работа с библиотекой HomeLispLib.Exe

Работа в интегрированной среде HomeLispIde не позволяет использовать возможности языка Лисп при программировании на других языках. Представлется вполне разумным, дать возможность использовать язык Лисп и тем, кому по каким-либо причинам не требуются (или не нравятся!) возможности интегрированной среды, но желательно использовать преимущества Лиспа для решения своих задач. Вот почему разработчик решил предоставить пользователям библиотеку HomeLispLib.Exe. Разработчик посчитал своим долгом вложить в эту библитеку возможно большую функциональность. Насколько это удалось - судить читателю.

Кроме подробного описания билиотеки HomeLispLib.Exe, эта глава содержит описание двух скриптовых реализаций HomeLisp. Их использование позволяет писать скрипты на языке HomeLisp в любом текстовом редакторе.

О г л а в л е н и е
Ручная инсталляция библиотеки HomeLispLib.Exe.
Класс clsLisp и его методы.
Использование класса clsLisp в программах.
Использование класса clsLisp в VBA/VB.
Использование класса clsLisp в VBScript.
Скриптовые реализации HomeLisp.
Скриптовый движок ConLisp.Exe.
Скриптовый движок WLisp.Exe.
Ручная инсталляция библиотеки HomeLispLib.Exe  

Прежде, чем описывать работу с библиотекой HomeLispLib.Exe, разработчик должен отметить, что эта библиотека явлется объектной (по терминологии Майкрософт - ActiveX-Exe). Последнее означает, что прежде, чем пользователь сможет использовать библиотеку, она должна быть зарегистрирована на компьютере пользователя. Если пакет HomeLisp успешно проинсталлирован на компьютере в полном объеме, то библиотека HomeLispLib.Exe будет зарегистрована автоматически. Если же возникает необходимость установить библиотеку вручную, то разработчик советует поступить следующим образом:

Создать на своем компьютере директорию (например, D:\HLisp);

Скопировать в нее следующие файлы: HomeLispLib.Exe, HomeLispD.ini, Lib-a.lsp, Reglib.cmd, Conlisp.exe, Wlisp.exe и Starter.exe. Первые четыре файла являются обязательными, а три последних нужны только тем, кто собирается писать скрипты на языке HomeLisp.

Зарегистрировать библиотеку HomeLispLib.Exe, для чего войти в директорию D:\HLisp и выполнить командный файл Reglib.cmd, содержащий следующую команду:

HomeLispLib.Exe   /Regserver

Успешная регистрация завершается "молча". При возникновении ошибок при регистрации будет выдано системное сообщение. Наиболее возможные причины ошибок:

  Отсутствие у пользователя, выполняющего регистрацию, необходимых прав. С этими проблемами - к системному администратору;

  Отсутствие на компьютере библиотеки MSVBVM60.DLL (виртуальная машина Visual Basic 6.0). Чрезвычайно маловерояно, но возможно. Необходимо установить и зарегистрировать на компьютере эту библиотеку.

Если пользователь хочет писать скрипты на языке HomeLisp, следует связать расширение .lsp или какое-либо другое (.li, .l) с программой Wlisp.exe.

Как уже говорилось, все описанные действия автоматически выполняются на этапе инсталляци HomeLisp. Приведенные выше сведения предназначены тем, кто захочет использовать только библиотеку HomeLispLib.Exe и желает перенести ее на другой компьютер.

Класс clsLisp и его методы.  

Библиотека HomeLispLib.Exe содержит единственный класс clsLisp. Этот класс имеет шестнадцать методов и не имеет свойств. Методы класса clsLisp приведены в нижеследующей таблице:

Имя Параметры Результат
1 About - Отображает модальную форму "О программе...". Результата не возвращает.
2 Config - Отображает модальную форму "Конфигурация", позволяющую настроить ряд параметров, хранящихся в конфигурационном файле HomeLispD.ini. Если конфигурация успешно изменена, функция возвращает логическое значение True, в противном случае - False.
3 Get_ErrorMessage [cP As integer] Возвращает последнее сообщение об ошибке. Если ошибка не возникала, возвращает пустую строку. Если значение необязательного параметра не равно нулю, строка возвращается в OEM-кодировке, в противном случае - в ANSI-кодировке.
4 Get_flgSucc - Возвращает флаг успешности завершения метода SEval. Если значение равно False, значит последний вызов SEval завершился с грубой ошибкой. Продолжение работы обычно не имеет смысла, и программа должна быть завершена.
5 Get_flgTerm - Возвращает флаг терминирования. Если значение равно True, значит была вызвана функция EXIT.
6 Get_LastExpr [cP As integer] Возвращает последнее вычисленное S-выражение в виде строки символов. Если значение необязательного параметра не равно нулю, строка возвращается в OEM-кодировке, в противном случае - в ANSI-кодироке.
7 Get_Output [cP As integer] Возвращает буфер вывода (вывод функций PRINT). Если функции PRINT/PRINTLINE при вычислении последнего S-выражения не вызывались, то возвращается пустая строка. Если значение необязательного параметра не равно нулю, строка возвращается в OEM-кодировке, в противном случае - в ANSI-кодироке.
8 Get_Resp [cP As integer] Возвращает результат вычисления очередного S-выражения в виде строки. Если значение необязательного параметра не равно нулю, строка возвращается в OEM-кодировке, в противном случае - в ANSI-кодироке.
9 Get_Stat [cP As integer] Возвращает строку статистики после вычисления последнего S-выражения, переданного методу SEval. Если значение необязательного параметра не равно нулю, строка возвращается в OEM-кодировке, в противном случае - в ANSI-кодироке. Строка статистики включает в себя время вычисления (в сек.); количество списочных ячеек, количество атомов (и через слэш - количество переменных) в области данных HomeLisp.
10 Get_SysMessage [cP As integer] Возвращает последнее системное сообщение. Если при вычислении системное сообщение не генерировалось, возвращается пустая строка. Если значение необязательного параметра не равно нулю, строка возвращается в OEM-кодировке, в противном случае - в ANSI-кодироке.
11 Get_Ver [cP As integer] Возвращает версию ядра HomeLisp в виде строки символов. Если значение необязательного параметра не равно нулю, строка возвращается в OEM-кодировке, в противном случае - в ANSI-кодироке.
12 Set_Argc n As Integer Устанавливает значение количества аргументов командной строки, доступной функции sysArgc. Результата не возвращает.
13 Set_Argv i As Integer
V As String
Устанавливает значение i-го параметра командной строки, доступной функции sysArgv равным значению V. Результата не возвращает.
14 Set_HomeDir D As String Устанавливает путь к директории, которая является текущей для вызывающего приложения (клиента), равным значению параметра D. Результата не возвращает.
15 Set_Time ti As Long Устанавливает интервал времени работы ядра Лиспа при вычислении S-выражения до принудительного завершения (в миллисекундах) разным значению параметра ti. Результата не возвращает. Если значение параметра равно нулю - принудительного прерывания не происходит.
16 SEval inpExpr As String Вычисляет значение S-выражения, заданного обязательным параметром inpExpr, после чего можно получить значение результата (в виде символьной строки) методом Get_Resp; значения системного сообщения методом Get_SystemMessage; содержимое буфера вывода методом Get_Output; сообщение об ошибке методом Get_ErrorMessage, а время вычислений - методом Get_Time. Сам метод SEval результата не возвращает.
Использование класса clsLisp в программах.  

Общая схема использования класса clsLisp такова:

1) Создать объект из класса clsLisp и сохранить ссылку на него;

2) При необходимости вызвать методы Set_Argc и Set_ArgV для задания значений, доступных функциям sysArgC и sysArgV. Если этого не сделать, то функция sysArgC будет возвращать нуль, а функция sysArgV - Nil;

3) При необходимости вызвать метод Set_HomeDir для задания значения имени домашней директории, которое будет возвращать функция sysHome. Если этого не сделать, то функция sysHome будет возвращать имя директории, в которой зарегистрирована библиотека HomeLispLib.Exe;

4) При необходимости вызвать метод Set_Time для задания максимального времени вычисления в миллисекундах. Если этого не сделать, то временной интервал до принудительного прерывания будет определяться параметром конфигурационного файла HomeLispD.ini;

5) Подготовить S-выражение в виде текстовой строки и вызвать метод SEval;

6) Принять и, при необходимости, отобразить результат вычисления, вызвав методы Get_Resp, Get_Output, Get_SysMessage;

7) Если результат, возвращенный методом Get_Resp, равен ERRSTATE, получить методом Get_ErrorMessage сообщение об ошибке и, при необходимости, отобразить его. Можно поступить и по-другому: всегда получать сообщение об ошибке и, если оно пусто, то вычисление завершено без ошибок;

8) При необходимости принять и отобразить строку статистики, вызвав метод Get_Stat;

9) Для продолжения работы перейти к пункту 4);

7) Для завершения работы уничтожить объект, созданный в пункте 1).

Конкретная реализация этой схемы зависит от языка программирования, из которого предполагается вызывать HomeLisp. Далее будут приведены примеры для языков VB/VBA и VBScript.

Использование класса clsLisp в VBA/VB.  

Здесь будет подробно рассмотрено использование класса clsLisp в программах VB/VBA. Эта методика одинаково хорошо работает в любом офисном компоненте (Word, Excel, PowerPoint и др.), поддерживающем язык VBA. Отличия заключаются только в методах отображения результатов. В качестве примера здесь рассматривается использование класса clsLisp в среде Microsoft-Excel. Предполагается, что библитека HomeLispLib.Exe зарегистрирована и сконфигурирована, как было описано выше.

Как известно, существует два основных способа использования объектных библиотек: раннее связывание (с установкой объектной ссылки) и позднее связывание. Ниже описывается методика раннего связывания. Позднее связывание будет описано в разделе, посвященному использованию библиотеки в сценариях на языке VBScript.

Сейчас читателю предлагается последовательно проделать следующее:

1) Открыть пустую книгу Excel и перейти в режим редактирования программного кода (нажав Alt-F11);

2) Выбрать последовательно пункты главного меню Tools -> References;

3) В окне References найти библиотеку HomeLisp и поставить галочку слева, как показано на приводимом ниже рисунке, после чего нажать кнопку "OK":

Если в окне References отсутствует библиотека HomeLisp, то это означает, что регистрация библиотеки завершилась неудачно. Необходимо зарегистрировать библиотеку. Если после нажатия кнопки "OK" не появилось сообщений об ошибках, значит ссылка успешно установлена. Это означает, что теперь методы класса clsLisp будут появляться в списке ассистента VBA, а весь интерфейс класса можно увидеть в браузере объектов. Чтобы убедиться в этом, следует нажать кнопку с изображением:

Если ссылка на библиотеку HomeLispLib.Exe успешно установлена, то при нажатии на эту кнопку и выборе в выпадающем списке библиотек библиотеки HomeLispLib.Exe пользователь увидит:

4) Если ссылка успешно установлена, следует дважды щелкнуть мышью по иконке первого листа рабочей книги. При этом откроется окно ввода программного кода. В этом окне следует ввести текст:


Sub TestLisp()

Dim myLisp As HomeLisp.clsLisp

    '::: Создание объекта

    Set myLisp = New HomeLisp.clsLisp

    With myLisp
    
         '::: Считаем факториалы целых

         For i% = 1 To 30
    
             '::: Формируем S-выражение (fact i)

             SExpr$ = "(fact " + CStr(i%) + ")"
    
             '::: Вычисляем...

             .SEval SExpr$
    
             '::: Получаем сообщение об ошибке

             ErrTxt$ = .Get_errormessage()
    
             '::: Ошибка была? 

             If ErrTxt$ <> "" Then
    
                '::: Да - сообщим об этом

                MsgBox "Ошибка: " + ErrTxt$
                
                Exit For
    
             Else

                '::: Нет - занесем результат    
                '::: в очередную ячейку

                Cells(i%, 1).Value = CStr(i%) + "!=" + .Get_Resp()
                
             End If
    
         Next i%

    End With
    
    '::: Уничтожим объект (хотя он и сам уничтожится!)

    Set myLisp = Nothing

End Sub

5) Если запустить эту программу на выполнение (нажатием клавиши F5), то на первом листе пользователь увидит:

Желающий может проверить правильность вычислений на калькуляторе (шутка!)... Совершенно аналогично можно установить ссылку на библиотеку в проекте на языке VB и пользоваться всеми возможностями языка VB.

Использование класса clsLisp в VBScript.  

В языке VBScript пользователю не удастся установить ссылку на библиотеку (ее просто некуда устанавливать!); здесь придется использовать позднее связывание. Снова предполагается, что библиотека HomeLispLib.Exe зарегистрирована на компьютере пользователя.

Методика использования библиотеки HimeLispLib.Exe при позднем связывании аналогична описанной выше. Единственное существенное отличие заключается в том, что вместо оператора New придется использовать функцию CreateObject. В качестве примера рассмотрим скрипт, который считает факториалы целых числел от 1 до 50 записывает результат в текстовый файл factorials.txt, располагающийся в текущей директории. Этот скрипт может иметь, например, такой вид:


    '::: Получим имя текущей директории

    Tmp=Cstr(WScript.ScriptFullName)
       
    l=Len(Tmp)

    For i=l to 1 Step -1

        If mid(Tmp,i,1)="\" then

           HomeDir=Left(Tmp,(i-1))

           Exit For

        End if

    Next

    '::: Создаем файл и открываем его для вывода

    Set myFSO=CreateObject("Scripting.FileSystemObject")

    Set OutFile=myFSO.OpenTextFile(HomeDir & "\factorials.txt",2,True)

    '::: Создание объекта "Лисп"

    Set myLisp = CreateObject("HomeLisp.clsLisp")

    With myLisp
    
         '::: Считаем факториалы целых

         For i = 1 To 50
    
             '::: Формируем S-выражение (fact i)

             SExpr = "(fact " + CStr(i) + ")"
    
             '::: Вычисляем...

             .SEval Cstr(SExpr)
    
             '::: Получаем сообщение об ошибке

             ErrTxt = .Get_errormessage()
    
             '::: Ошибка была? 

             If ErrTxt <> "" Then
    
                '::: Да - сообщим об этом

                MsgBox "Ошибка: " + ErrTxt
                
                Exit For
    
             Else

                '::: Нет - занесем результат    
                '::: в файл

                OutFile.WriteLine CStr(i) + "!=" + .Get_Resp()
                
             End If
    
         Next 

    End With
    
    '::: Закрываем файл

    OutFile.Close

    '::: Уничтожим объекты (хотя они и сами уничтожатся!)

    Set OutFile=Nothing

    Set myFSO=Nothing

    Set myLisp = Nothing

    MsgBox "Готово!"

После завершения этого скрипта содержимое файла factorials.txt будет таким:


1!=1
2!=2
3!=6
4!=24
5!=120
6!=720
7!=5040
8!=40320
9!=362880
10!=3628800
11!=39916800
12!=479001600
13!=6227020800
14!=87178291200
15!=1307674368000
16!=20922789888000
17!=355687428096000
18!=6402373705728000
19!=121645100408832000
20!=2432902008176640000
21!=51090942171709440000
22!=1124000727777607680000
23!=25852016738884976640000
24!=620448401733239439360000
25!=15511210043330985984000000
26!=403291461126605635584000000
27!=10888869450418352160768000000
28!=304888344611713860501504000000
29!=8841761993739701954543616000000
30!=265252859812191058636308480000000
31!=8222838654177922817725562880000000
32!=263130836933693530167218012160000000
33!=8683317618811886495518194401280000000
34!=295232799039604140847618609643520000000
35!=10333147966386144929666651337523200000000
36!=371993326789901217467999448150835200000000
37!=13763753091226345046315979581580902400000000
38!=523022617466601111760007224100074291200000000
39!=20397882081197443358640281739902897356800000000
40!=815915283247897734345611269596115894272000000000
41!=33452526613163807108170062053440751665152000000000
42!=1405006117752879898543142606244511569936384000000000
43!=60415263063373835637355132068513997507264512000000000
44!=2658271574788448768043625811014615890319638528000000000
45!=119622220865480194561963161495657715064383733760000000000
46!=5502622159812088949850305428800254892961651752960000000000
47!=258623241511168180642964355153611979969197632389120000000000
48!=12413915592536072670862289047373375038521486354677760000000000
49!=608281864034267560872252163321295376887552831379210240000000000
50!=30414093201713378043612608166064768844377641568960512000000000000
Скриптовые реализации HomeLisp  

Но к чему посредники (даже такие хорошие, как VB/VBA/VBscript!) если мы любим Лисп?! Почему бы не писать на Лиспе скрипты без каких бы то ни было посредников? Такая возможность имеется. В поставку HomeLisp входят целых два скриптовых "движка": Conlisp.exe и Wlisp.exe. Первый из них (Conlisp.exe) является 32-х битным консольным приложением (команды можно вводить в консольном окне, а можно - из переопределенного стандартного ввода). Второй (Wlisp.exe) исполняет программу, которую берет из файла с подходящим расширением (*.ls *.li *.l или что-либо в этом роде) без отображения консольного окна.

Оба движка используют библиотеку HomeLispLib.Exe, поэтому и описываются в этом разделе документации. Входные языки скриптовых движков Conlisp.exe и Wlisp.exe не различаются между собой и практически не отличаются от языка HomeLisp, используемого интегрированной средой HomeLispIDE.

Единственным серьезным отличием скриптовой реализации является то, что в конкретный момент времени на экране может быть отображено только одно графическое окно. При этом нет ограничений на создание невидимых графических окон; их можно создавать в любых разумных количествах и выполнять с ними любые операции. Функция GRWSHOWALL в скриптовой реализации отсутствует, а вызов GRWSHOW для отображения окна w2 при уже отображенном окне w1 вызывает сокрытие окна w1 с последующим отображением окна w2.

Скриптовый движок Conlisp.exe  

Консольный движок может использоваться нонконформистами, которые предпочитают интерфейс командной строки нормальному графическому интерфейсу. Исполняемый модуль движка имеет имя Conlisp.exe. Этот модуль при запуске может принимать следующие параметры командной строки (ключи):

-s - этот ключ вызывает выдачу после вычисления введенного S-выражения строки статистики (включающей время выполнения, количество занятых списочных ячеек, количество атомов и количество переменных);

-o Имя_файла - этот ключ обеспечивает сохранения протокола работы в файле, имя которого задано после ключа -o. Если же строка -o является последней в командной строке, то выдается предупреждение и ключ -o игнорируется (протокол не ведется). Следует отметить, что вызов движка с ключом -o отличается от переопределения стандартного вывода: в протокол попадает вся служебная информация, сопутствующая выполнению, а в переопределенный выходной файл - только выходной поток функций print/prits/printline/printsline;

-t nnnnn - этот ключ обеспечивает принудительное прерывание вычислений после истечения nnnnn миллисекунд. Если же строка -t является последней в командной строке, то выдается предупреждение и ключ -t игнорируется, а интервал времени до принудительного прерывания берется из конфигурационного файла HomeLispD.ini;

-iANSI - наличие этого ключа означает, что входные команды будут поступать в ANSI-кодировке. Ключ имеет смысл использовать, если переопределен стандартный ввод и входные команды поступают из файла в кодировке ANSI. Если ключ опущен, то входные команды рассматриваются как команды в OEM-кодировке. Естественно, ключ влияет только на изображение русских букв;

-oANSI - наличие этого ключа означает, что весь вывод будет представляться в кодировке ANSI. Ключ имеет смысл использовать, если переопределен стандартный вывод и результат выполнения направляется в файл. Ключ влияет только на изображение русских букв. Если ключ не задан, вывод формируется в OEM-кодировке.

При запуске движка без переопределения входного и выходного потоков на экране возникает консольное окно, в котором пользователь может вводить команды. В это же окно осуществляется и вывод ответов. Для завершения работы следует с клавиатуры ввести CTRL-Z и нажать Enter (стандартное сочетание, означающее конец файла). Можно также ввести команду (exit).

Если стандартный ввод переопределен, то выполнение скрипта закончится автоматически, когда будет исчерпан входной файл, поданный на вход.

Ниже приводятся примеры вызова Conlisp.exe.

В первых строках консольного окна выводится стандартный заголовок. Он включает сведения о версии ядра и командную строку запуска (если она была задана). После строки из символов "-" начинается область диалога. При этом:

Команды, введенные пользователем, отображаются ярко-белым;

Результат вычисления S-выражения - ярко-зеленым;

Системные сообщения - ярко-желтым;

Сообщения об ошибках - ярко-красным;

Выходной поток функций print/prits/printline/printsline - светло-голубым;

Статистика выполнения команд (при заданном ключе -s) - серым;

Ниже показан еще один пример работы с Conlisp.exe:

Командная строка здесь не задана. При вызове несуществующей функции FCAT результат вычисления есть атом ERRSTATE. Красным цветом выведено сообщение об ошибке. Далее включен режим статистики. Вычисление факториала сопровождается выводом данных о количестве вызовов функций системы. Поскольку ключ -s не задан, то не выводятся и сведения о времени выполнения и памяти ядра HomeLisp. Последней строкой желтым цветом выведен результат сборки мусора (функция GC была вызвана автоматически).

Следующий пример показывает работу с функцией printline:

Пусть читатель обратит внимание на число 10, выведенное зеленым цветом. Это - результат вычисления функции FOR. Далее идут факториалы чисел от 1 до 10; ниже располагается сообщение сборщика мусора, и последняя строка содержит данные о времени выполнения и памяти (поскольку задан ключ -s).

Пример, приведенный ниже, показывает, как можно предотвратить зацикливание скрипта путем задания времени вычисления.

Здесь при запуске движка задан ключ -t 200, который задает максимально допустимый временной интервал вычислений 200 миллисекунд (0.2 сек). Вычисление 100! на компьютере разработчика требует 0.06 сек. Расчет 100!, таким образом, укладывается в заданный интервал. А вот вычисление 200! уже не укладывается - происходит принудительное прерывание, результат равен атому BRKSTATE. Ниже располагается системное предупреждение (о том, что допустимый интервал времени истек).

Если создать файл pdir.lsp, содержащий следующую программу:


(prog (lst item l)

      (setq lst (sysDir "*.li" &HFF))  // Получаем список файлов

      (printsline "+-------------------+")

@loop (setq item (car lst))            // Очередной элемент списка

      (setq lst (cdr lst))             // Остаток списка

      (setq l (strlen item))           // Длина имени

      (cond ((< l 17)                  // Если короче - добавим пробелов

             (setq item (strcat item (strspace (- 17 l)))))
      )

      (prints "| ")                    // Печатаем элемент
      (prints item)
      (printsline " |")

      (cond ((null lst) (go @exit)))   // Список исчерпан - на выход

      (Go @loop)                       // Следующий элемент

@exit
      
      (printsline "+-------------------+")

)

а затем выполнить в командной строке команду: conlisp < pdir.lsp > ls.txt, то в файле ls.txt окажется, например следующее:


+-------------------+
| 1.li              |
| b.li              |
| grafik-7p.li      |
| h.li              |
| main_test.li      |
| netEnum.li        |
| pdir.li           |
| regEnum.li        |
| u.li              |
+-------------------+

Этот же скрипт можно выполнить и по-другому: запустив conlisp и введя команду (rds "pdir.li"). Результат показан ниже:

В заключение хотелось бы отметить, что консольный характер движка conlisp нисколько не мешает вызывать скрипты с графическим интерфейсом (диалоги, графические окна).

Скриптовый движок Wlisp.exe  

Движок Wlisp.exe не использует стандартные потоки ввода/вывода. Он берет имя исполняемого скрипта из командной строки, а для общения с пользователем используется графический интерфейс.

Движок использует три ключа командной строки (два из которых полностью аналогичных соответствующим ключам движка conlisp):

-o Имя_файла - этот ключ обеспечивает сохранения протокола работы в файле, имя которого задано после ключа -o. Если же строка -o является последней в командной строке, то выдается предупреждение и ключ -o игнорируется (протокол не ведется).

-t nnnnn - этот ключ обеспечивает принудительное прерывание вычислений после истечения nnnnn миллисекунд. Если же строка -t является последней в командной строке, то выдается предупреждение и ключ -t игнорируется, а интервал времени до принудительного прерывания берется из конфигурационного файла HomeLispD.ini;

-s - этот ключ обеспечивает подавление выдачи сообщения о завершении скрипта. По умолчанию, если результат выполнения скрипта не является одним из атомов ERRSTATE, BRKSTATE или STOPSTATE, то после его завершения выдается сообщение "Скрипт nnnnnnn.nnn завершен".

Разработчик рекомендует связать какое-либо расширение (например, *.li) с движком wlisp.exe. В этом случае выполнять скрипты можно будет выполнять "двойным щелчком".