Использование Prolog совместно с другими ЯП
Использование
Prolog совместно с другими ЯП.
Понятие Dll.
Вспомним процесс
программирования в DOS. Преобразование исходного текста в
машинный код включал в себя 2 процесса: компиляцию и линковку. Во время
линковки в код программы помещались не только объявления функций и процедур, но
и их полный код.
В многозадачной
среде подобный подход был бы весьма расточителен, так как огромное количество
функций, отвечающих за прорисовку элементов пользовательского интерфейса, за
обращение к системным ресурсам и т.п. дублировались в каждой программе. В
качестве решения возникшей проблемы была предложена концепция динамической
компоновки (см. рис. 1).
рис 1.
DLL (библиотека динамической связи) – файл, выступающий в качестве
коллективной библиотеки предикатов, которые могут быть использованы
одновременно в нескольких приложениях. Prolog способен
генерировать DLL, включать DLL
статически и загружать динамически.
Вызов в программе на VP процедур и функций на других языках.
Прежде чем вызвать процедуры и функции на
других языках их нужно объявить как внешний предикат, упомянув, что он
осуществляется на другом языке. При этом необходимо знать количество и порядок
входных параметров:
GLOBAL PREDICATES
procedure add(integer A, integer B, integer C) – (i,i,o)
language pascal
Замечание: обратите внимание, что
в VP явно указывается язык процедуры
Передача
входных/выходных параметров и возвращение значений.
Размер входных параметров определен
однозначно и зависит только от объявленного типа. Выходной параметр – 32 битный
указатель на область памяти, где хранится выходное значение.
Следует отметить, что функции на Pascal не могут возвращать значения в формате чисел с плавающей точкой, а
функции C - структуры (но могут, конечно, возвращать
указатели на них).
Многочисленные
декларации.
Предикат VP может
иметь различные комбинации входных/выходных параметров, и для каждой из них
необходима отдельная процедура. Идентификаторы, используемые в Prolog должны совпадать с идентификаторами в библиотеке + суффикс _X, где X – целое число (порядковый номер процедуры,
нумерация начинается с 0). Если существует только один вариант, то суффикс
отсутствует. Рассмотрим пример:
GLOBAL PREDICATES
subtraction(integer, integer, integer) – (i,i,o), (i,o,i),
(o,i,i), (i,i,i) language C
change(integer, integer) – (i,o) language C
GOAL
subtraction(2,2,X), write(“2-2=”,X), nl,
subtraction(2,Y,5), write(“2-5=”,Y), nl,
subtraction(Z,5,4), write(“5-4=”,X), nl,
subtraction(2,2,5), write(“2-2 равно
5”), nl,
change(5, Ch), write(Ch).
Модуль,
связываемый с этой программой должен содержать процедуры:
subtraction_0 (int x, int y, int *z)
{*z=x-y;}
subtraction_1 (int x, int *y, int z)
{*y=x-z;}
subtraction_2 (int *x, int y, int z)
{*x=y-z;}
subtraction_3 (int x, int y, int z)
{if ((x-y)!=z)RUN_Fail();}
change(int a, int *b)
{*b=a;}
Примечание: если процедура
написана на языке C, то параметры заносятся в стек в
обратном порядке (после возврата значений указатель автоматически
корректируется VP), в противном случае, параметры
заносятся в стек в нормальном порядке (см. таблицу 1).
Форматы
объектных файлов в Win32.
Под Win32
используется 2 формата объектных файлов: OMF
(объектно-модульный формат – используется, например, Borland C++ ) и COFF (Общий объектно-файловый формат,
используется, например, Visual C++ ).
1.
При использовании файла в формате OMF имя предиката должно совпадать с именем функции.
2.
При использовании файла в формате COFF, к имени предиката добавляется знак подчеркивания, и после символа @
указывается количество байт, добавленных в стек (например, если предикат name
имеет 2 целых аргумента, то он должен быть объявлен как _name@8 (см. таблицу 1)).
Установка
указателя на стек.
Существует два способа установки
указателя на стек: при объявлении функции и при ее вызове. Так сложилось, что Pascal устанавливает указатель при объявлении функции, а С – при вызове (см.
таблицу 1).
|
Конвертирует
имена в верхний регистр.
|
Порядок
аргументов прямой.
|
Устанавливает
указатель на стек при объявлении.
|
Необходимость
конвертировать имена в формат COFF.
|
C
|
-
|
-
|
-
|
|
pascal
|
+
|
+
|
+
|
|
stdcall
|
|
+
|
-
|
+
|
syscall
|
|
+
|
+
|
-
|
Таблица
1: вызов модулей из VP.
Неавтоматическое обозначение внешних
предикатов.
Идентификатор
процедуры или функции в VP не обязательно должен совпадать
с идентификатором во внешнем модуле. В этом случае объявление такого предиката
имеет вид:
GLOBAL PREDICATES
add(integer,
integer, integer) – (i,o) language c as “_myadd@12”
Эквивалентность типов.
Большинство
простых типов переменных в VP имеют эквиваленты в других
языках программирования, однако размер резервируемой для них памяти может не
совпадать (см. таблицу 2).
Тип переменной
|
Размер (Win32).
|
char, byte
|
1 байт
|
short, word
|
2 байт
|
long, dword
|
4 байт
|
unsigned, integer
|
4 байт
|
Real
|
8 байт
|
Ref
|
4 байт
|
Таблица
2: размер переменных в VP.
Обработка списков.
Ниже приведен
пример программы, преобразующей список в массив, и затем вновь возвращающей
данные в список.
Программа ListToArray на языке С преобразует список целых чисел в массив, записывает в стек
элементы массива и возвращает количество элементов (массив и количество
элементов передаются в программу как параметры).
Преобразование
списка проходит в 2 этапа:
1.
Просматривается список и находится количество
элементов в нем.
2.
Целые числа из списка заносятся в массив, состоящий
из известного количества элементов.
/* Program lstar_p.pro */
project "lstar"
global
domains
ilist
= integer*
global
predicates
inclist(ilist,ilist)
- (i,o) language c
goal
inclist([1,2,3,4,5,6,7],L),
write(L).
/* Program lstar_c.c */
#define listfno
1
#define nilfno
2
typedef
unsigned char BYTE;
void
*MEM_AllocGStack(unsigned);
typedef
struct ilist {
BYTE
Functor;
int
Value;
struct
ilist *Next;
}
INTLIST;
int
ListToArray(INTLIST *List,int **ResultArray)
{
INTLIST
*SaveList = List;
int
*Array, len;
register
int *ArrP;
register
int i;
/* количество элементов в списке */
i =
0;
while
( List->Functor == listfno ) {
i++;
List = List->Next;
}
len
= i;
Array
= MEM_AllocGStack(i*sizeof(int));
/* перемещение элементов списка в массив */
List =
SaveList;
while
( i != 0 ) {
*ArrP++
= List->Value;
List
= List->Next;
i--;
}
*ResultArray
= Array;
return(len);
}
void ArrayToList(register int *ArrP,register int n,
register INTLIST **ListPP)
{
while
( n != 0 ) {
*ListPP = MEM_AllocGStack(sizeof(INTLIST));
(*ListPP)->Functor = listfno;
(*ListPP)->Value = *ArrP++;
ListPP = &(*ListPP)->Next;
n--;
}
*ListPP
= MEM_AllocGStack(sizeof((*ListPP)->Functor));
/* конец списка */
(*ListPP)->Functor
= nilfno;
}
void
inclist(INTLIST *InList,INTLIST **OutList)
{
register
int *ArrP, i, len;
int
*Array;
len
= ListToArray(InList,&Array);
ArrP
= Array;
for
( i = 0; i < len; i++)
++*ArrP++;
ArrayToList(Array,len,OutList);
}
Вызов предикатов VP.
VP способен не только вызывать предикаты, но и предоставлять их другим
программам. Ниже приведен пример вызова предиката prowin_msg из программы
на С:
/* Program hello_p.pro */
global
predicates
char
prowin_msg(string) - (i) language c
hello_c
- language c
clauses
prowin_msg(S,C)
:-
write(S,"
(press any key)"), readchar(C).
goal
prowin_msg("Hello
from PDC Prolog"),
hello_c.
/* Program hello_c.c */
char
prowin_msg(char *);
void
hello_c()
{
while ( prowin_msg("Hello from C (press
'C')") != 'C' )
;
}
2003 Pechenkin
pechenkin@pochtamt.ru
www.cs.vsu.ru/~pechenkin