% ---------------------------------------------------------------------- % BEGIN LICENSE BLOCK % Version: CMPL 1.1 % % The contents of this file are subject to the Cisco-style Mozilla Public % License Version 1.1 (the "License"); you may not use this file except % in compliance with the License. You may obtain a copy of the License % at www.eclipse-clp.org/license. % % Software distributed under the License is distributed on an "AS IS" % basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See % the License for the specific language governing rights and limitations % under the License. % % The Original Code is The ECLiPSe Constraint Logic Programming System. % The Initial Developer of the Original Code is Cisco Systems, Inc. % Portions created by the Initial Developer are % Copyright (C) 1989-2006 Cisco Systems, Inc. All Rights Reserved. % % Contributor(s): ECRC GmbH % Contributor(s): IC-Parc, Imperal College London % % END LICENSE BLOCK % % System: ECLiPSe Constraint Logic Programming System % Version: $Id: foreign.pl,v 1.2 2009/07/16 09:11:24 jschimpf Exp $ % ---------------------------------------------------------------------- /* * SEPIA PROLOG SOURCE MODULE * * IDENTIFICATION: foreign.pl * * sccsid("%W% %E%"). */ :- module(foreign). :- comment(categories, ["Compatibility","Interfacing"]). :- comment(summary, "Simple foreign interface like SICStus or Quintus"). :- comment(author, "Micha Meier, ECRC Munich"). :- comment(copyright, "Cisco Systems, Inc"). :- comment(date, "$Date: 2009/07/16 09:11:24 $"). :- comment(desc, ascii(" Simple foreign interface like SICStus or Quintus(TM). For every C function an interface C function is generated which converts the arguments from and to the Sepia ones. Note that this is faster (and uses more C code) than one generic interface procedure which dispatches all external calls like in the above systems. Use it as follows: 1) load this file with lib(foreign) 2) compile (Sepia) the file with the foreign/3 clauses 3) call make_simple_interface/0 or /1; this will generate the file e_interface.c 4) compile (cc) e_interface.c and the other C source files On a SVR4 system (e.g. Solaris 2.x) produce one .so file from all the .o files that should be loaded, including the file e_interface.o. On Solaris this can be done with something like cc -I$ECLIPSEDIR/include -G -o myfile.so file1.c file2.c \ ... e_interface.c 5) call load_foreign_files/2. This assumes that the foreign/3 clauses are still available. This means that a file which contains both foreign/3 facts and a goal :- load_foreign_files(...) must be split so that make_simple_interface/0 can be called before the goal. However, once the file e_interface.c is created, this file can be used in the original form. The main purpose of the interface is to port foreign files from SICStus or Quintus to Sepia, or to automate interfacing of other existing C functions. When writing new external functions, it is more efficient to use the Sepia external interface directly. User-defined types and conversion can also be specified. If some additional declarations and function calls are necessary before or after calling the C function, the type can be specified as a user type: [+-]user(Type) When such a type is encountered, this library will call the predicate foreign_user_type(+Type, +I, -CType, -Local, -TypeCheck, -InConv, -Call, -OutConv, -Unif) which is supposed to yield the data to be inserted into the source of the interface file. The arguments are as follows: Type - the name of the user type with + or -, e.g. +mystring I - the argument position; used to make distinction among several args of the same type CType - the corresponding C type Local - declaration of local variables for this type TypeCheck - type testing of the predicate arguments InConv - calls to be made before calling the foreign function Call - the actual argument passed to the foreign function OutConv - calls to be made after the foreign call and before the unification of output arguments Unif - unification of the output argument The source of the generated interface for a declaration foreign(funct, c, funct(+integer, +user(ut))) looks as follows: p_funct(val1, tag1, val2, tag2) value val1, val2; type tag1, tag2; { LOCAL Check_Integer(tag1); TYPECHECK INCONV funct(val1.nint, CALL) OUTCONV Return_UNIF } where the capitalized ids are substituted by the user definition. ")). % NOTE: Sepia strings are not (yet) supported by this interface % (keyword sepia_string), but their inclusion is trivial. :- dynamic foreign/3, % foreign/2 not supported foreign_file/2. % ignored :- export declare_externals/1, declare_externals/2. :- export tr_foreign/3. :- export macro(foreign/3, tr_foreign/3, [clause]). :- export load_foreign_files/2, make_simple_interface/0, make_simple_interface/1. :- import external_/3, external_body/2 from sepia_kernel. :- tool(load_foreign_files/2, load_foreign_files/3). :- tool(make_simple_interface/0, make_simple_interface_body/1). :- tool(make_simple_interface/1, make_simple_interface/2). :- tool(declare_externals/1, declare_externals/2). :- op(300, yfx, '.'). tr_foreign(E, E, M) :- E = foreign(_, _, P), functor(P, F, A), external_body(F/A, M). make_simple_interface_body(M) :- make_simple_interface([], M). make_simple_interface(Incl, M) :- open('e_interface.c', write, c), write(c, "/* This file has been automatically generated\n by make_simple_interface/1\n*/\n\n"), write(c, '#include "external.h"'), write_incls(Incl), printf(c, "\n\nextern char *\tmalloc();\n\n", []), call(foreign(CName, _, Proc))@M, (make_interface(CName, Proc, M) -> fail ; printf(error, "*** The function %s failed to interface!\n%b", CName), fail ). make_simple_interface(_, _) :- close(c). write_incls([]). write_incls([I|Incl]) :- printf(c, '%n#include "%s"', [I]), write_incls(Incl). load_foreign_files(Files, Libraries, M) :- (Files = :(Module, List) -> true ; Module = M, List = Files ), (get_flag(object_suffix, "o") -> list_to_string(Libraries, "", LS), list_to_string(List, "", FS), concat_string([FS, " e_interface.o ", LS], FilesString), load(FilesString) ; List = [File], load(File) ), declare_externals(Module, M). list_to_string([], S, S). list_to_string([L|Libs], S, String) :- concat_string([S, " ", L], S1), list_to_string(Libs, S1, String). declare_externals(Mod, M) :- call(foreign(CName, _, Proc))@M, concat_atoms(p_, CName, P_Name), functor(Proc, F, A), external_(F/A, P_Name, Mod), fail. declare_externals(_, _). make_interface(CName, Proc, M) :- concat_atoms(p_, CName, P_Name), Proc =.. [_|Args], proc_type(Args, CArity, PArity, ProcType, OutArg, M), printf(c, "%s(", P_Name), write_args(1, PArity), printf(c, ")\n", []), write_declaration(PArity), printf(c, "{\n", []), local_variables(1, Args, 0, Count, M), write_checks(1, Args, M), input_conversion(1, Args, M), (OutArg > 0 -> printf(c, "\tout%w = ", [OutArg]) ; write(c, '\t') ), printf(c, "(%s) %s(", [ProcType, CName]), write_call(1, CArity, Args, M), printf(c, ");\n", []), output_conversion(1, Args, M), write_unification(1, Args, Count, M), (Count > 1 -> printf(c, "\tReturn_Unify;\n", []) ; Count = 0 -> printf(c, "\tSucceed;\n", []) ; true ), printf(c, "}\n\n", []). write_args(I, Arity) :- I > Arity, !. write_args(Arity, Arity) :- !, printf(c, "val%d, tag%d", [Arity, Arity]). write_args(I, Arity) :- printf(c, "val%d, tag%d, ", [I, I]), I1 is I + 1, write_args(I1, Arity). write_declaration(Arity) :- write_values(Arity), write_types(Arity). write_values(0) :- !. write_values(Arity) :- printf(c, "value\tval1", []), write_ids(val, 2, Arity), printf(c, ";\n", []). write_types(0) :- !. write_types(Arity) :- printf(c, "type\ttag1", []), write_ids(tag, 2, Arity), printf(c, ";\n", []). write_ids(_, I, Arity) :- I > Arity, !. write_ids(Id, I, Arity) :- printf(c, ", %s%d", [Id, I]), I1 is I + 1, write_ids(Id, I1, Arity). local_variables(_, [], C, C, _) :- (C > 1 -> printf(c, "\tPrepare_Requests;\n", []) ; true), nl(c). local_variables(I, [-user(Type)|Args], C, Count, M) :- !, call(foreign_user_type(-Type, I, _, Local, _, _, _, _, _))@M, printf(c, "\t%s\n", [Local]), I1 is I + 1, local_variables(I1, Args, C, Count, M). local_variables(I, [OArg|Args], C, Count, M) :- (OArg = [-Arg] -> true ; OArg = -Arg -> true ), arg_type(Arg, Type, M), !, printf(c, "\t%s\tout%d;\n", [Type, I]), (Arg = string(_) -> printf(c, "\tchar *\tstr%d;\n", [I]); true), (Arg = user(T) -> call(foreign_user_type(-T, I, _, Local, _, _, _, _, _))@M, printf(c, "\t%s\n", [Local]) ; true ), I1 is I + 1, C1 is C + 1, local_variables(I1, Args, C1, Count, M). local_variables(I, [+string(_)|Args], C, Count, M) :- !, printf(c, "\tchar *\tstr%d;\n", I), I1 is I + 1, local_variables(I1, Args, C, Count, M). local_variables(I, [+user(Type)|Args], C, Count, M) :- !, call(foreign_user_type(+Type, I, _, Local, _, _, _, _, _))@M, printf(c, "\t%s\n", [Local]), I1 is I + 1, local_variables(I1, Args, C, Count, M). local_variables(I, [_|Args], C, Count, M) :- I1 is I + 1, local_variables(I1, Args, C, Count, M). input_conversion(_, [], _). input_conversion(I, [+string(N)|Args], M) :- !, printf(c, "\t{\n\t\tregister char *\tcp;\n\t\tstr%d = malloc(%d + 1);\n", [I, N]), printf(c, "\t\tstrncpy(str%d, DidName(val%d.did), %d);\n", [I, I, N]), printf(c, "\t\tcp = str%d + %d;\n\t\twhile(!*cp && cp > str%d)\n", [I, N, I]), printf(c, "\t\t\t*cp-- = ' ';\n\t}\n", []), I1 is I + 1, input_conversion(I1, Args, M). input_conversion(I, [-string(N)|Args], M) :- !, printf(c, "\tstr%d = malloc(%d + 1);\n", [I, N]), I1 is I + 1, input_conversion(I1, Args, M). input_conversion(I, [[-string(N)]|Args], M) :- !, printf(c, "\tstr%d = malloc(%d + 1);\n", [I, N]), I1 is I + 1, input_conversion(I1, Args, M). input_conversion(I, [+user(Type)|Args], M) :- !, call(foreign_user_type(+Type, I, _, _, _, InConv, _, _, _))@M, printf(c, "\t%s\n", [InConv]), I1 is I + 1, input_conversion(I1, Args, M). input_conversion(I, [-user(Type)|Args], M) :- !, call(foreign_user_type(-Type, I, _, _, _, InConv, _, _, _))@M, printf(c, "\t%s\n", [InConv]), I1 is I + 1, input_conversion(I1, Args, M). input_conversion(I, [_|Args], M) :- I1 is I + 1, input_conversion(I1, Args, M). proc_type(Args, CArity, PArity, Type, Output, M) :- proc_params(Args, 0, PArity, Type, Output, M), (Output = 0 -> CArity = PArity ; CArity is PArity - 1 ). proc_params([], I, I, void, 0, _). proc_params([[-Arg]|Args], I, Arity, Type, I1, M) :- arg_type(Arg, Type, M), !, I1 is I + 1, proc_params(Args, I1, Arity, _, _, M). proc_params([_|Args], I, Arity, Type, Output, M) :- I1 is I + 1, proc_params(Args, I1, Arity, Type, Output, M). write_checks(_, [], _). write_checks(I, [OArg|Args], M) :- (OArg = [Arg] -> true; Arg = OArg), arg_check(Arg, Check, M), !, (Check = 'Check_Atom_Or_Nil' -> printf(c, "\t%s(val%d, tag%d);\n", [Check, I, I]) ; Check = '' -> true ; printf(c, "\t%s(tag%d);\n", [Check, I]) ), I1 is I + 1, write_checks(I1, Args, M). write_call(_, _, [], _). write_call(I, Arity, [[_]|Args], M) :- !, I1 is I + 1, write_call(I1, Arity, Args, M). write_call(I, Arity, [Arg|Args], M) :- call_arg(I, Arg, M), (I < Arity -> write(c, ', '); true), I1 is I + 1, write_call(I1, Arity, Args, M). call_arg(I, +string(_), _) :- !, printf(c, "str%d", [I]). call_arg(I, -string(_), _) :- !, printf(c, "&str%d", [I]). call_arg(I, +user(Type), M) :- !, call(foreign_user_type(+Type, I, _, _, _, _, Call, _, _))@M, printf(c, "%s", [Call]). call_arg(I, -user(Type), M) :- !, call(foreign_user_type(-Type, I, _, _, _, _, Call, _, _))@M, printf(c, "%s", [Call]). call_arg(I, +Arg, M) :- !, concat_atom([val, I], Val), concat_atom([tag, I], Tag), arg_access(Arg, Val, Tag, Access, M), printf(c, "%.w", [Access]). call_arg(I, -_, _) :- !, printf(c, "&out%d", I). write_unification(_, [], _, _). write_unification(I, [Arg|Args], C, M) :- out_string(Arg), !, unif_type(C, Type), printf(c, "\t%s_Unify_Atom(val%d, tag%d, in_dict(out%d, 0));\n", [Type, I, I, I]), I1 is I + 1, write_unification(I1, Args, C, M). write_unification(I, [Arg|Args], C, M) :- out_buffer(Arg), !, unif_type(C, Type), printf(c, "\t%s_Unify_Atom(val%d, tag%d, in_dict(str%d, 0));\n", [Type, I, I, I]), I1 is I + 1, write_unification(I1, Args, C, M). /* write_unification(I, [Arg|Args], C, M) :- out_sepia_string(Arg), !, unif_type(C, Type), printf(c, "\t%s_Unify_String(val%d, tag%d, str%d);\n", [Type, I, I, I]), I1 is I + 1, write_unification(I1, Args, C, M). */ write_unification(I, [-user(T)|Args], C, M) :- !, unif_type(C, Type), call(foreign_user_type(-T, I, _, _, _, _, _, _, Unif))@M, printf(c, "\t%s_%s\n", [Type, Unif]), I1 is I + 1, write_unification(I1, Args, C, M). write_unification(I, [-Arg|Args], C, M) :- !, unif_type(C, Type), arg_unif(Arg, Unif, Cast, M), printf(c, "\t%s_Unify_%s(val%d, tag%d, %sout%d);\n", [Type, Unif, I, I, Cast, I]), I1 is I + 1, write_unification(I1, Args, C, M). write_unification(I, [[-Arg]|Args], C, M) :- !, unif_type(C, Type), arg_unif(Arg, Unif, Cast, M), printf(c, "\t%s_Unify_%s(val%d, tag%d, %sout%d);\n", [Type, Unif, I, I, Cast, I]), I1 is I + 1, write_unification(I1, Args, C, M). write_unification(I, [_|Args], C, M) :- I1 is I + 1, write_unification(I1, Args, C, M). unif_type(1, 'Return') :- !. unif_type(_, 'Request'). output_conversion(_, [], _). output_conversion(I, [[-string(N)]|Args], M) :- !, printf(c, "\tstrncpy(str%d, out%d, %d + 1);\n", [I, I, N]), I1 is I + 1, output_conversion(I1, Args, M). output_conversion(I, [-user(T)|Args], M) :- !, call(foreign_user_type(-T, I, _, _, _, _, _, OutConv, _))@M, printf(c, "\t%s\n", [OutConv]), I1 is I + 1, output_conversion(I1, Args, M). output_conversion(I, [_|Args], M) :- I1 is I + 1, output_conversion(I1, Args, M). arg_type(integer, 'long ', _). arg_type(float, double, _). arg_type(atom, 'unsigned long', _). arg_type(string, 'char *', _). arg_type(string(_), 'char *', _). arg_type(address, 'char *', _). arg_type(address(Type), T, _) :- concat_atoms(Type, ' *', T). arg_type(user(Type), T, M) :- call(foreign_user_type(+Type, 0, T, _, _, _, _, _, _))@M. arg_check(+integer, 'Check_Integer', _). arg_check(-integer, 'Check_Output_Integer', _). arg_check(+float, 'Check_Float', _). arg_check(-float, 'Check_Output_Float', _). arg_check(+atom, 'Check_Atom_Or_Nil', _). arg_check(-atom, 'Check_Output_Atom', _). arg_check(+string, 'Check_Atom_Or_Nil', _). arg_check(-string, 'Check_Output_Atom', _). arg_check(+string(_), 'Check_Atom', _). arg_check(-string(_), 'Check_Output_Atom', _). arg_check(+address, 'Check_Integer', _). arg_check(-address, 'Check_Output_Integer', _). arg_check(+address(_), 'Check_Integer', _). arg_check(-address(_), 'Check_Output_Integer', _). arg_check(+user(T), Check, M) :- call(foreign_user_type(+T, 0, _, _, Check, _, _, _, _))@M. arg_check(-user(T), Check, M) :- call(foreign_user_type(-T, 0, _, _, Check, _, _, _, _))@M. arg_access(integer, Val, _, Val.nint, _). arg_access(float, Val, Tag, 'FloatVal'(Val,Tag), _). arg_access(atom, Val, _, Val.did, _). arg_access(sepia_string, Val, _, 'StringStart'(Val), _). arg_access(string, Val, _, 'DidName'(Val.did), _). arg_access(string(_), _, _, _, _). arg_access(address, Val, _, Val.str, _). arg_access(address(T), Val, _, F.did, _) :- functor(F, Val, 1), arg(1, F, A), functor(A, T, 1), arg(1, A, *), op(50, xf, Val), op(50, fx, T). arg_unif(integer, 'Integer', '', _). arg_unif(float, 'Float', '', _). arg_unif(atom, 'Atom', '', _). arg_unif(string, 'Atom', '', _). arg_unif(sepia_string, 'String', '', _). arg_unif(address, 'Integer', '(unsigned long) ', _). arg_unif(address(_), 'Integer', '(unsigned long) ', _). /* out_sepia_string(-sepia_string). out_sepia_string([-sepia_string]). out_sepia_string(-sepia_string(_)). out_sepia_string([-sepia_string(_)]). */ out_string(-string). out_string([-string]). out_buffer(-string(_)). out_buffer([-string(_)]).