Files
mercury/compiler/prog_foreign.m
Julien Fischer bdb50b33ce Break the cycle in the package dependency graph between the
Estimated hours taken: 1
Branches: main

Break the cycle in the package dependency graph between the
hlds and backend_libs packages by moving yet more of the
contents of the foreign module into the new prog_foreign
module.  There are no changes to any code other than
moving it around.

compiler/foreign.m:
compiler/prog_foreign.m:
	Shift most of the types related to the foreign language
	interface into the latter module.

	Fix a typo in a comment at the beginning of prog_foreign.m.

	Clean up the formatting of comments in foreign.m.

compiler/hlds.m:
	Update the comment about why the transform_hlds package
	is imported here.

compiler/*.m:
	Minor changes caused by the above.
2005-03-24 13:33:34 +00:00

448 lines
14 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 2000-2005 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
% This module defines predicate for interfacing with foreign languages.
% that are necessary for the frontend of the compiler to construct
% the list of items. The predicates in this module should not depend
% on the HLDS in any way. The predicates for interfacing with foreign
% languages that do depend on the HLDS are defined in foreign.m.
% This module also contains the parts of the name mangler that are used
% by the frontend of the compiler.
% Warning: any changes to the name mangling algorithms implemented in this
% module may also require changes to extras/dynamic_linking/name_mangle.m,
% profiler/demangle.m, util/mdemangle.c and compiler/name_mangle.m.
% Main authors: trd, dgj.
% This code was originally part of the foreign module and was moved here.
%-----------------------------------------------------------------------------%
:- module parse_tree.prog_foreign.
:- interface.
:- import_module libs.globals.
:- import_module parse_tree.prog_data.
:- import_module mdbcomp.prim_data.
:- import_module bool.
:- import_module list.
:- import_module string.
:- import_module term.
%-----------------------------------------------------------------------------%
:- type foreign_decl_info == list(foreign_decl_code).
% in reverse order
:- type foreign_body_info == list(foreign_body_code).
% in reverse order
:- type foreign_decl_code
---> foreign_decl_code(
foreign_language,
foreign_decl_is_local,
string,
prog_context
).
:- type foreign_body_code
---> foreign_body_code(
foreign_language,
string,
prog_context
).
:- type foreign_export_defns == list(foreign_export).
:- type foreign_export_decls
---> foreign_export_decls(
foreign_decl_info,
list(foreign_export_decl)
).
:- type foreign_export_decl
---> foreign_export_decl(
foreign_language, % language of the export
string, % return type
string, % function name
string % argument declarations
).
% Some code from a `pragma foreign_code' declaration that is not
% associated with a given procedure.
%
:- type user_foreign_code
---> user_foreign_code(
foreign_language, % language of this code
string, % code
term__context % source code location
).
% the code for `pragma export' is generated directly as strings
% by export.m.
%
:- type foreign_export == string.
%-----------------------------------------------------------------------------%
% foreign_import_module_name(ForeignImport)
%
% returns the module name which represents the ForeignImport.
%
% For instance for the foreign_import_module representing
% :- foreign_import_module("MC++", module)
% would return the module_name
% unqualified("module__cpp_code")
%
:- func foreign_import_module_name(foreign_import_module) = module_name.
% foreign_import_module_name(ForeignImport, CurrentModule)
%
% returns the module name needed to refer to ForeignImport from the
% CurrentModule.
%
:- func foreign_import_module_name(foreign_import_module, module_name) =
module_name.
% Sub-type of foreign_language for languages for which
% we generate external files for foreign code.
%
:- inst lang_gen_ext_file
---> c
; managed_cplusplus
; csharp.
% The module name used for this foreign language.
% Not all foreign languages generate external modules
% so this function only succeeds for those that do.
%
:- func foreign_language_module_name(module_name, foreign_language) =
module_name.
:- mode foreign_language_module_name(in, in) = out is semidet.
:- mode foreign_language_module_name(in, in(lang_gen_ext_file)) = out is det.
% The file extension used for this foreign language (including the dot).
% Not all foreign languages generate external files,
% so this function only succeeds for those that do.
%
:- func foreign_language_file_extension(foreign_language) = string.
:- mode foreign_language_file_extension(in) = out is semidet.
:- mode foreign_language_file_extension(in(lang_gen_ext_file)) = out is det.
% It is possible that more than one foreign language could be used to
% implement a particular piece of code.
% Therefore, foreign languages have an order of preference, from most
% preferred to least perferred.
% prefer_foreign_language(Globals, Target, Lang1, Lang2) returns the
% yes if Lang2 is preferred over Lang1.
%
% Otherwise it will return no.
%
:- func prefer_foreign_language(globals, compilation_target,
foreign_language, foreign_language) = bool.
% The `multi' mode returns all supported foreign languages.
%
:- pred foreign_language(foreign_language).
:- mode foreign_language(in) is det.
:- mode foreign_language(out) is multi.
:- func foreign_type_language(foreign_language_type) = foreign_language.
%
% The following are the parts of the name mangler that are needed by
% the compiler frontend so that it can write out makefile fragments.
%
% Returns the name of the initialization function
% for a given module.
%
:- func make_init_name(module_name) = string.
% Returns the name of the Aditi-RL code constant
% for a given module.
%
:- func make_rl_data_name(module_name) = string.
% Mangle a possibly module-qualified Mercury symbol name
% into a C identifier.
%
:- func sym_name_mangle(sym_name) = string.
% Mangle an arbitrary name into a C etc identifier
%
:- func name_mangle(string) = string.
% Produces a string of the form Module__Name.
%
:- func qualify_name(string, string) = string.
:- func convert_to_valid_c_identifier(string) = string.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module parse_tree.modules.
:- import_module char.
:- import_module int.
%-----------------------------------------------------------------------------%
foreign_import_module_name(
foreign_import_module(Lang, ForeignImportModule, _)) =
ModuleName :-
(
Lang = c,
ModuleName = ForeignImportModule
;
Lang = il,
ModuleName = ForeignImportModule
;
Lang = java,
ModuleName = ForeignImportModule
;
Lang = managed_cplusplus,
ModuleName = foreign_language_module_name(ForeignImportModule,
Lang)
;
Lang = csharp,
ModuleName = foreign_language_module_name(ForeignImportModule,
Lang)
).
foreign_import_module_name(ModuleForeignImported, CurrentModule) =
ImportedForeignCodeModuleName :-
ModuleForeignImported = foreign_import_module(Lang, _, _),
ImportedForeignCodeModuleName1 = ModuleForeignImported ^
foreign_import_module_name,
(
Lang = c,
ImportedForeignCodeModuleName = ImportedForeignCodeModuleName1
;
Lang = il,
ImportedForeignCodeModuleName = handle_std_library(
CurrentModule, ImportedForeignCodeModuleName1)
;
Lang = managed_cplusplus,
ImportedForeignCodeModuleName = handle_std_library(
CurrentModule, ImportedForeignCodeModuleName1)
;
Lang = csharp,
ImportedForeignCodeModuleName = handle_std_library(
CurrentModule, ImportedForeignCodeModuleName1)
;
Lang = java,
ImportedForeignCodeModuleName = handle_std_library(
CurrentModule, ImportedForeignCodeModuleName1)
).
% On the il backend, we need to refer to the module "mercury" when
% referencing a std library module when we are not actually building
% the std library.
%
:- func handle_std_library(module_name, module_name) = module_name.
handle_std_library(CurrentModule, ModuleName0) = ModuleName :-
(
mercury_std_library_module_name(ModuleName0),
\+ mercury_std_library_module_name(CurrentModule)
->
ModuleName = unqualified("mercury")
;
ModuleName = ModuleName0
).
%-----------------------------------------------------------------------------%
foreign_language_module_name(M, L) = FM :-
% Only succeed if this language generates external files.
_ = foreign_language_file_extension(L),
Ending = "__" ++ simple_foreign_language_string(L) ++ "_code",
( M = unqualified(Name),
FM = unqualified(Name ++ Ending)
; M = qualified(Module, Name),
FM = qualified(Module, Name ++ Ending)
).
%-----------------------------------------------------------------------------%
foreign_language_file_extension(c) = ".c".
foreign_language_file_extension(managed_cplusplus) = ".cpp".
foreign_language_file_extension(csharp) = ".cs".
foreign_language_file_extension(java) = ".java".
foreign_language_file_extension(il) = _ :- fail.
%-----------------------------------------------------------------------------%
% Currently we don't use the globals to compare foreign language
% interfaces, but if we added appropriate options we might want
% to do this later.
% When compiling to C, C is always preferred over any other language.
prefer_foreign_language(_Globals, c, Lang1, Lang2) =
( Lang2 = c, not Lang1 = c ->
yes
;
no
).
% When compiling to asm, C is always preferred over any other language.
prefer_foreign_language(_Globals, asm, Lang1, Lang2) =
( Lang2 = c, not Lang1 = c ->
yes
;
no
).
% Whe compiling to il, first we prefer il, then csharp, then
% managed_cplusplus, after that we don't care.
prefer_foreign_language(_Globals, il, Lang1, Lang2) = Comp :-
PreferredList = [il, csharp, managed_cplusplus],
FindLangPriority = (func(L) = X :-
( list__nth_member_search(PreferredList, L, X0) ->
X = X0
;
X = list__length(PreferredList) + 1
)),
N1 = FindLangPriority(Lang1),
N2 = FindLangPriority(Lang2),
( N2 < N1 ->
Comp = yes
;
Comp = no
).
% Nothing useful to do here, but when we add Java as a
% foreign language, we should add it here.
prefer_foreign_language(_Globals, java, _Lang1, _Lang2) = no.
%-----------------------------------------------------------------------------%
foreign_language(c).
foreign_language(java).
foreign_language(csharp).
foreign_language(managed_cplusplus).
foreign_language(il).
%-----------------------------------------------------------------------------%
foreign_type_language(il(_)) = il.
foreign_type_language(c(_)) = c.
foreign_type_language(java(_)) = java.
%-----------------------------------------------------------------------------%
make_init_name(ModuleName) = InitName :-
MangledModuleName = sym_name_mangle(ModuleName),
string__append_list(["mercury__", MangledModuleName, "__"], InitName).
make_rl_data_name(ModuleName) = RLDataConstName :-
MangledModuleName = sym_name_mangle(ModuleName),
string__append("mercury__aditi_rl_data__", MangledModuleName,
RLDataConstName).
sym_name_mangle(unqualified(Name)) =
name_mangle(Name).
sym_name_mangle(qualified(ModuleName, PlainName)) = MangledName :-
MangledModuleName = sym_name_mangle(ModuleName),
MangledPlainName = name_mangle(PlainName),
MangledName = qualify_name(MangledModuleName, MangledPlainName).
%
% Warning: any changes to the name mangling algorithm here may also
% require changes to extras/dynamic_linking/name_mangle.m,
% profiler/demangle.m, util/mdemangle.c and compiler/name_mangle.m.
%
name_mangle(Name) = MangledName :-
( string__is_alnum_or_underscore(Name) ->
% any names that start with `f_' are changed so that
% they start with `f__', so that we can use names starting
% with `f_' (followed by anything except an underscore)
% without fear of name collisions
( string__append("f_", Suffix, Name) ->
string__append("f__", Suffix, MangledName)
;
MangledName = Name
)
;
MangledName = convert_to_valid_c_identifier(Name)
).
qualify_name(Module0, Name0) = Name :-
string__append_list([Module0, "__", Name0], Name).
convert_to_valid_c_identifier(String) = Name :-
( name_conversion_table(String, Name0) ->
Name = Name0
;
Name0 = convert_to_valid_c_identifier_2(String),
string__append("f", Name0, Name)
).
% A table used to convert Mercury functors into
% C identifiers. Feel free to add any new translations you want.
% The C identifiers should start with "f_",
% to avoid introducing name clashes.
% If the functor name is not found in the table, then
% we use a fall-back method which produces ugly names.
:- pred name_conversion_table(string::in, string::out) is semidet.
name_conversion_table("\\=", "f_not_equal").
name_conversion_table(">=", "f_greater_or_equal").
name_conversion_table("=<", "f_less_or_equal").
name_conversion_table("=", "f_equal").
name_conversion_table("<", "f_less_than").
name_conversion_table(">", "f_greater_than").
name_conversion_table("-", "f_minus").
name_conversion_table("+", "f_plus").
name_conversion_table("*", "f_times").
name_conversion_table("/", "f_slash").
name_conversion_table(",", "f_comma").
name_conversion_table(";", "f_semicolon").
name_conversion_table("!", "f_cut").
name_conversion_table("{}", "f_tuple").
name_conversion_table("[|]", "f_cons").
name_conversion_table("[]", "f_nil").
% This is the fall-back method.
% Given a string, produce a C identifier
% for that string by concatenating the decimal
% expansions of the character codes in the string,
% separated by underlines.
% The C identifier will start with "f_"; this predicate
% constructs everything except the initial "f".
%
% For example, given the input "\n\t" we return "_10_8".
:- func convert_to_valid_c_identifier_2(string) = string.
convert_to_valid_c_identifier_2(String) = Name :-
( string__first_char(String, Char, Rest) ->
% XXX This will cause ABI incompatibilities between
% compilers which are built in grades that have
% different character representations.
char__to_int(Char, Code),
string__int_to_string(Code, CodeString),
string__append("_", CodeString, ThisCharString),
Name0 = convert_to_valid_c_identifier_2(Rest),
string__append(ThisCharString, Name0, Name)
;
% String is the empty string
Name = String
).
%-----------------------------------------------------------------------------%
:- end_module prog_foreign.
%-----------------------------------------------------------------------------%