mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
911 lines
40 KiB
Mathematica
911 lines
40 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1996-2009, 2011 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.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: read_modules.m.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.read_modules.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.timestamp.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.file_kind.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.parse_error.
|
|
:- import_module parse_tree.prog_item.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module term.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% After we have read in Mercury code from a source file, interface file or
|
|
% optimization file, we record the parse tree we get from it, so we can
|
|
% avoid having to read it again.
|
|
% XXX ITEM_LIST We seem to sometimes re-read it anyway. Fix this.
|
|
%
|
|
% Since we use different types to represent the parse trees of source,
|
|
% interface and optimization files, we use three maps, one for each
|
|
% parse tree type. Each map maps a key, which consists of a module name
|
|
% and the kind of a file (e.g. .int0 vs .int2 for interface files)
|
|
% to the parse tree we got for that file.
|
|
%
|
|
% XXX ITEM_LIST The code that reads in optimization files does not
|
|
% record its results in hrmm_opt. I (zs) don't know whether that is
|
|
% a bug (leading to duplicate reads of optimization files) or a feature
|
|
% (keeping files that are by construction read exactly once out of a map
|
|
% where they won't be needed again).
|
|
%
|
|
|
|
:- type have_read_module_maps
|
|
---> have_read_module_maps(
|
|
hrmm_src :: have_read_module_src_map,
|
|
|
|
% XXX CLEANUP We should stop using and delete these two fields
|
|
% as soon as we can, using the last six fields instead.
|
|
hrmm_int :: have_read_module_int_map,
|
|
hrmm_opt :: have_read_module_opt_map,
|
|
|
|
% XXX CLEANUP We should store parse_tree_module_srcs,
|
|
% not raw_compilation_units.
|
|
hrmm_rcu :: have_read_module_rcu_map,
|
|
|
|
hrmm_int0 :: have_read_module_int0_map,
|
|
hrmm_int1 :: have_read_module_int1_map,
|
|
hrmm_int2 :: have_read_module_int2_map,
|
|
hrmm_int3 :: have_read_module_int3_map,
|
|
|
|
hrmm_plain_opt :: have_read_module_plain_opt_map,
|
|
hrmm_trans_opt :: have_read_module_trans_opt_map
|
|
).
|
|
|
|
:- func init_have_read_module_maps = have_read_module_maps.
|
|
|
|
:- type have_read_module_src_map ==
|
|
have_read_module_map(module_name, parse_tree_src).
|
|
:- type have_read_module_int_map ==
|
|
have_read_module_map(have_read_module_key(int_file_kind), parse_tree_int).
|
|
:- type have_read_module_opt_map ==
|
|
have_read_module_map(have_read_module_key(opt_file_kind), parse_tree_opt).
|
|
|
|
:- type have_read_module_key(Kind)
|
|
---> have_read_module_key(module_name, Kind).
|
|
|
|
:- type have_read_module_rcu_map ==
|
|
map(module_name, raw_compilation_unit).
|
|
|
|
:- type have_read_module_int0_map ==
|
|
have_read_module_map(module_name, parse_tree_int0).
|
|
:- type have_read_module_int1_map ==
|
|
have_read_module_map(module_name, parse_tree_int1).
|
|
:- type have_read_module_int2_map ==
|
|
have_read_module_map(module_name, parse_tree_int2).
|
|
:- type have_read_module_int3_map ==
|
|
have_read_module_map(module_name, parse_tree_int3).
|
|
|
|
:- type have_read_module_plain_opt_map ==
|
|
have_read_module_map(module_name, parse_tree_plain_opt).
|
|
:- type have_read_module_trans_opt_map ==
|
|
have_read_module_map(module_name, parse_tree_trans_opt).
|
|
|
|
:- type have_read_module_map(Key, PT) == map(Key, have_read_module(PT)).
|
|
|
|
:- type have_read_module(PT)
|
|
---> have_successfully_read_module(
|
|
file_name,
|
|
% Not all reads of e.g. interface files request
|
|
% that the read return the timestamp of the file.
|
|
maybe(timestamp),
|
|
PT,
|
|
list(error_spec),
|
|
read_module_errors
|
|
)
|
|
; tried_to_read_module_failed.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type maybe_ignore_errors
|
|
---> ignore_errors
|
|
; do_not_ignore_errors.
|
|
|
|
% read_module_src(Globals, Descr, IgnoreErrors, Search,
|
|
% ModuleName, FileName, ReadModuleAndTimestamps, MaybeTimestamp,
|
|
% ParseTreeSrc, Specs, Errors, !IO):
|
|
%
|
|
% Given a module name, read in and parse the source code of that file,
|
|
% printing progress messages along the way if the verbosity level
|
|
% calls for that.
|
|
%
|
|
% If Search is do_search, search all directories given by the option
|
|
% search_directories for the module;
|
|
% otherwise, search for those filenames only in the current directory.
|
|
% Return in FileName the actual source file name found
|
|
% (excluding the directory part). If the actual module name
|
|
% (as determined by the `:- module' declaration) does not match
|
|
% the specified module name, then report an error message,
|
|
% but record the *expected* module name in the parse tree,
|
|
% not the one we actually found. This is because most parts
|
|
% of the compiler (including deps_map.m and make.module_dep_file.m)
|
|
% rely on the invariant which says that if Errors does not contain
|
|
% any fatal errors, then the returned ParseTreeSrc contains the
|
|
% module with the expected name. Invocations of the compiler
|
|
% that don't specify --make or request dependency map don't really
|
|
% care which module name we return here; they will work either way,
|
|
% the only difference being whether the names of the files they generate
|
|
% are based on the expected or the actual module name.
|
|
%
|
|
% N.B. This reads a module given the MODULE name. If you want to read
|
|
% a module given the FILE name, use `read_module_src_from_file'.
|
|
%
|
|
% If ReadModuleAndTimestamps is always_read_module(dont_return_timestamp),
|
|
% return `no' in MaybeTimestamp.
|
|
%
|
|
% If ReadModuleAndTimestamps is always_read_module(do_return_timestamp),
|
|
% attempt to return the modification time of the file in MaybeTimestamp.
|
|
%
|
|
% If ReadModuleAndTimestamps is dont_read_module_if_match(OldTimeStamp),
|
|
% then
|
|
%
|
|
% - if the timestamp of that file is exactly OldTimestamp, then
|
|
% don't read the file, but return OldTimestamp as the file's timestamp,
|
|
% alongside a dummy parse tree; while
|
|
% - if the timestamp of that file differs from OldTimestamp (virtually
|
|
% always because it is newer), then read the module from the file
|
|
% as usual, parse and return its contents as usual, and also return
|
|
% its actual timestamp.
|
|
%
|
|
% If the file could not be read, MaybeTimestamp will be `no'.
|
|
%
|
|
:- pred read_module_src(globals::in, string::in,
|
|
maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, list(term.context)::in, file_name::out,
|
|
read_module_and_timestamps::in, maybe(timestamp)::out,
|
|
parse_tree_src::out, list(error_spec)::out, read_module_errors::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% read_module_src_from_file(Globals, FileName, FileNameDotM,
|
|
% Descr, Search, ReadModuleAndTimestamps, MaybeTimestamp, ParseTreeSrc,
|
|
% Specs, Errors, !IO):
|
|
%
|
|
% Does pretty much the same job as read_module_src, but its job is
|
|
% to read the module stored in a specified file (FileNameDotM),
|
|
% discovering the name of the module stored there by reading the file,
|
|
% as opposed to looking for the file containing a module with a specified
|
|
% name. It does not search for the right filename (that is FileNameDotM),
|
|
% but if Search is do_search, it does search for that filename in the
|
|
% specified directories.
|
|
%
|
|
% The rest of the argument list has the same meaning as in read_module_src.
|
|
%
|
|
:- pred read_module_src_from_file(globals::in, file_name::in, file_name::in,
|
|
string::in, maybe_search::in, read_module_and_timestamps::in,
|
|
maybe(timestamp)::out, parse_tree_src::out,
|
|
list(error_spec)::out, read_module_errors::out, io::di, io::uo) is det.
|
|
|
|
% read_module_int(Globals, Descr, IgnoreErrors, Search,
|
|
% ModuleName, IntFileKind, FileName, ReturnTimestamp, MaybeTimestamp,
|
|
% ParseTreeInt, Specs, Errors, !IO):
|
|
%
|
|
% Given a module name, and the identity of one of its interface files,
|
|
% (.int0, .int, .int2 or .int3), read in and parse the contents of that
|
|
% interface file, printing progress messages along the way if the
|
|
% verbosity level calls for that.
|
|
%
|
|
% The meanings of the arguments are pretty much the same as for
|
|
% read_module_src, but while the names of the files that contain source
|
|
% files may not be fully module qualified, the names of interface files
|
|
% are always fully module qualified, so read_module_int does not search
|
|
% for the right filename. It knows what filename it looks for; the only
|
|
% search it does, if Search is do_search, is to decide which directory
|
|
% among the search directories contains the file with that filename.
|
|
%
|
|
% XXX CLEANUP Calls to this predicate should be replaced by calls
|
|
% to its int_file_kind-specific versions below. The only such calls
|
|
% are now used in the implementation of smart recompilation.
|
|
%
|
|
:- pred read_module_int(globals::in, string::in,
|
|
maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, int_file_kind::in, file_name::out,
|
|
read_module_and_timestamps::in, maybe(timestamp)::out,
|
|
parse_tree_int::out, list(error_spec)::out, read_module_errors::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% Versions of read_module_int that read and return a non-generic
|
|
% parse tree.
|
|
%
|
|
:- pred read_module_int0(globals::in, string::in,
|
|
maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, file_name::out,
|
|
read_module_and_timestamps::in, maybe(timestamp)::out,
|
|
parse_tree_int0::out, list(error_spec)::out, read_module_errors::out,
|
|
io::di, io::uo) is det.
|
|
:- pred read_module_int1(globals::in, string::in,
|
|
maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, file_name::out,
|
|
read_module_and_timestamps::in, maybe(timestamp)::out,
|
|
parse_tree_int1::out, list(error_spec)::out, read_module_errors::out,
|
|
io::di, io::uo) is det.
|
|
:- pred read_module_int2(globals::in, string::in,
|
|
maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, file_name::out,
|
|
read_module_and_timestamps::in, maybe(timestamp)::out,
|
|
parse_tree_int2::out, list(error_spec)::out, read_module_errors::out,
|
|
io::di, io::uo) is det.
|
|
:- pred read_module_int3(globals::in, string::in,
|
|
maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, file_name::out,
|
|
read_module_and_timestamps::in, maybe(timestamp)::out,
|
|
parse_tree_int3::out, list(error_spec)::out, read_module_errors::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% maybe_read_module_intN(Globals, Descr, Search,
|
|
% ModuleName, IntFileKind, FileName, ReturnTimestamp, MaybeTimestamp,
|
|
% ParseTreeInt, Specs, Errors, !HaveReadModuleMaps, !IO):
|
|
%
|
|
% If HaveReadModuleMap contains the already-read contents of the
|
|
% relevant kind of interface file for ModuleName, then return
|
|
% the information stored in HaveReadModuleMap for that file.
|
|
% If it is not there, read that interface file using read_module_intN,
|
|
% regardless of its timestamp.
|
|
%
|
|
:- pred maybe_read_module_int0(globals::in, maybe_search::in, module_name::in,
|
|
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
|
|
parse_tree_int0::out, list(error_spec)::out, read_module_errors::out,
|
|
have_read_module_maps::in, have_read_module_maps::out,
|
|
io::di, io::uo) is det.
|
|
:- pred maybe_read_module_int1(globals::in, maybe_search::in, module_name::in,
|
|
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
|
|
parse_tree_int1::out, list(error_spec)::out, read_module_errors::out,
|
|
have_read_module_maps::in, have_read_module_maps::out,
|
|
io::di, io::uo) is det.
|
|
:- pred maybe_read_module_int2(globals::in, maybe_search::in, module_name::in,
|
|
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
|
|
parse_tree_int2::out, list(error_spec)::out, read_module_errors::out,
|
|
have_read_module_maps::in, have_read_module_maps::out,
|
|
io::di, io::uo) is det.
|
|
:- pred maybe_read_module_int3(globals::in, maybe_search::in, module_name::in,
|
|
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
|
|
parse_tree_int3::out, list(error_spec)::out, read_module_errors::out,
|
|
have_read_module_maps::in, have_read_module_maps::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% find_read_module_src(HaveReadModuleMap, ModuleName,
|
|
% ReturnTimestamp, FileName, MaybeTimestamp, ParseTree, Specs, Errors):
|
|
% find_read_module_int(HaveReadModuleMap, ModuleName, IntFileKind,
|
|
% ReturnTimestamp, FileName, MaybeTimestamp, ParseTree, Specs, Errors):
|
|
%
|
|
% Check whether HaveReadModuleMap contains the already-read contents
|
|
% of the specified source file or interface file. If it does, return
|
|
% its contents. If it does not, fail.
|
|
%
|
|
:- pred find_read_module_src(have_read_module_src_map::in, module_name::in,
|
|
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
|
|
parse_tree_src::out, list(error_spec)::out, read_module_errors::out)
|
|
is semidet.
|
|
:- pred find_read_module_int(have_read_module_int_map::in,
|
|
have_read_module_key(int_file_kind)::in,
|
|
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
|
|
parse_tree_int::out, list(error_spec)::out, read_module_errors::out)
|
|
is semidet.
|
|
|
|
% Versions of find_read_module_int that return a non-generic parse tree.
|
|
%
|
|
:- pred find_read_module_int0(have_read_module_int0_map::in, module_name::in,
|
|
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
|
|
parse_tree_int0::out, list(error_spec)::out, read_module_errors::out)
|
|
is semidet.
|
|
:- pred find_read_module_int1(have_read_module_int1_map::in, module_name::in,
|
|
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
|
|
parse_tree_int1::out, list(error_spec)::out, read_module_errors::out)
|
|
is semidet.
|
|
:- pred find_read_module_int2(have_read_module_int2_map::in, module_name::in,
|
|
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
|
|
parse_tree_int2::out, list(error_spec)::out, read_module_errors::out)
|
|
is semidet.
|
|
:- pred find_read_module_int3(have_read_module_int3_map::in, module_name::in,
|
|
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
|
|
parse_tree_int3::out, list(error_spec)::out, read_module_errors::out)
|
|
is semidet.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.find_module.
|
|
:- import_module parse_tree.parse_module.
|
|
:- import_module parse_tree.source_file_map.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module dir.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
init_have_read_module_maps =
|
|
have_read_module_maps(map.init, map.init, map.init,
|
|
map.init, map.init, map.init, map.init, map.init, map.init, map.init).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
read_module_src(Globals, Descr, IgnoreErrors, Search,
|
|
ModuleName, ExpectationContexts, FileName,
|
|
ReadModuleAndTimestamps, MaybeTimestamp,
|
|
ParseTreeSrc, Specs, Errors, !IO) :-
|
|
read_module_begin(Globals, Descr, Search, ModuleName, fk_src,
|
|
FileName0, VeryVerbose, SearchDirs, !IO),
|
|
% For `.m' files, we need to deal with the case where the module name
|
|
% does not match the file name.
|
|
search_for_module_source_and_stream(SearchDirs, ModuleName,
|
|
MaybeFileNameAndStream, !IO),
|
|
actually_read_module_src(Globals, ModuleName, ExpectationContexts,
|
|
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
ParseTreeSrc0, ModuleSpecs, Errors, !IO),
|
|
ParseTreeSrc0 = parse_tree_src(_ActualModuleName, ActualModuleNameContext,
|
|
ComponentsCord),
|
|
% If ModuleName = ActualModuleName, this obviously does the right thing.
|
|
% If ModuleName != ActualModuleName, then we must include ModuleName
|
|
% in ParseTreeSrc (see the comment above), and including recording
|
|
% ActualModuleNameContext as its context shouldn't mislead anyone
|
|
% who reads the error spec about the unexpected module name,
|
|
% which should be in Specs.
|
|
ParseTreeSrc = parse_tree_src(ModuleName, ActualModuleNameContext,
|
|
ComponentsCord),
|
|
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
|
|
MaybeFileNameAndStream, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
|
|
|
|
read_module_src_from_file(Globals, FileName, FileNameDotM, Descr, Search,
|
|
ReadModuleAndTimestamps, MaybeTimestamp,
|
|
ParseTreeSrc, Specs, Errors, !IO) :-
|
|
read_module_begin_from_file(Globals, Descr, Search,
|
|
FileName, FileNameDotM, DefaultModuleName,
|
|
VeryVerbose, SearchDirs, !IO),
|
|
search_for_file_and_stream(SearchDirs, FileNameDotM,
|
|
MaybeFileNameAndStream, !IO),
|
|
actually_read_module_src(Globals, DefaultModuleName, [],
|
|
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
ParseTreeSrc, ModuleSpecs, Errors, !IO),
|
|
read_module_end_file(Globals, VeryVerbose, FileNameDotM,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
|
|
|
|
read_module_int(Globals, Descr, IgnoreErrors, Search, ModuleName, IntFileKind,
|
|
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
|
|
ParseTreeInt, Specs, Errors, !IO) :-
|
|
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(IntFileKind),
|
|
FileName0, VeryVerbose, SearchDirs, !IO),
|
|
search_for_file_and_stream(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
actually_read_module_int(IntFileKind, Globals, ModuleName, [],
|
|
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
ParseTreeInt, ModuleSpecs, Errors, !IO),
|
|
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
|
|
MaybeFileNameAndStream, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
|
|
|
|
read_module_int0(Globals, Descr, IgnoreErrors, Search, ModuleName,
|
|
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
|
|
ParseTreeInt0, Specs, Errors, !IO) :-
|
|
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int0),
|
|
FileName0, VeryVerbose, SearchDirs, !IO),
|
|
search_for_file_and_stream(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
actually_read_module_int0(Globals, ModuleName, [],
|
|
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
ParseTreeInt0, ModuleSpecs, Errors, !IO),
|
|
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
|
|
MaybeFileNameAndStream, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
|
|
|
|
read_module_int1(Globals, Descr, IgnoreErrors, Search, ModuleName,
|
|
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
|
|
ParseTreeInt1, Specs, Errors, !IO) :-
|
|
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int1),
|
|
FileName0, VeryVerbose, SearchDirs, !IO),
|
|
search_for_file_and_stream(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
actually_read_module_int1(Globals, ModuleName, [],
|
|
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
ParseTreeInt1, ModuleSpecs, Errors, !IO),
|
|
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
|
|
MaybeFileNameAndStream, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
|
|
|
|
read_module_int2(Globals, Descr, IgnoreErrors, Search, ModuleName,
|
|
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
|
|
ParseTreeInt2, Specs, Errors, !IO) :-
|
|
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int2),
|
|
FileName0, VeryVerbose, SearchDirs, !IO),
|
|
search_for_file_and_stream(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
actually_read_module_int2(Globals, ModuleName, [],
|
|
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
ParseTreeInt2, ModuleSpecs, Errors, !IO),
|
|
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
|
|
MaybeFileNameAndStream, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
|
|
|
|
read_module_int3(Globals, Descr, IgnoreErrors, Search, ModuleName,
|
|
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
|
|
ParseTreeInt3, Specs, Errors, !IO) :-
|
|
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int3),
|
|
FileName0, VeryVerbose, SearchDirs, !IO),
|
|
search_for_file_and_stream(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
actually_read_module_int3(Globals, ModuleName, [],
|
|
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
ParseTreeInt3, ModuleSpecs, Errors, !IO),
|
|
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
|
|
MaybeFileNameAndStream, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred read_module_begin_from_file(globals::in, string::in,
|
|
maybe_search::in, file_name::in, file_name::in, module_name::out,
|
|
bool::out, list(string)::out, io::di, io::uo) is det.
|
|
|
|
read_module_begin_from_file(Globals, Descr, Search, FileName, FileNameDotM,
|
|
DefaultModuleName, VeryVerbose, SearchDirs, !IO) :-
|
|
( if dir.basename(FileName, BaseFileNamePrime) then
|
|
BaseFileName = BaseFileNamePrime
|
|
else
|
|
BaseFileName = ""
|
|
),
|
|
have_source_file_map(HaveMap, !IO),
|
|
(
|
|
HaveMap = no,
|
|
file_name_to_module_name(BaseFileName, DefaultModuleName)
|
|
;
|
|
HaveMap = yes,
|
|
lookup_source_file_module(FileNameDotM, MaybeModuleName, !IO),
|
|
(
|
|
MaybeModuleName = yes(DefaultModuleName)
|
|
;
|
|
MaybeModuleName = no,
|
|
file_name_to_module_name(BaseFileName, DefaultModuleName)
|
|
)
|
|
),
|
|
% The rest of this predicate should be kept in sync
|
|
% with read_module_begin.
|
|
(
|
|
Search = do_search,
|
|
globals.lookup_accumulating_option(Globals, search_directories,
|
|
SearchDirs)
|
|
;
|
|
Search = do_not_search,
|
|
SearchDirs = [dir.this_directory]
|
|
),
|
|
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
|
(
|
|
VeryVerbose = no
|
|
;
|
|
VeryVerbose = yes,
|
|
io.format("%% Reading %s `%s'... ", [s(Descr), s(FileNameDotM)], !IO),
|
|
io.flush_output(!IO)
|
|
).
|
|
|
|
:- pred read_module_begin(globals::in, string::in,
|
|
maybe_search::in, module_name::in, file_kind::in, file_name::out,
|
|
bool::out, list(string)::out, io::di, io::uo) is det.
|
|
|
|
read_module_begin(Globals, Descr, Search, ModuleName, FileKind,
|
|
FileName0, VeryVerbose, SearchDirs, !IO) :-
|
|
file_kind_to_extension(FileKind, _ExtStr, Ext),
|
|
(
|
|
Search = do_search,
|
|
module_name_to_search_file_name(Globals, $pred, Ext,
|
|
ModuleName, FileName0, !IO)
|
|
;
|
|
Search = do_not_search,
|
|
module_name_to_file_name(Globals, $pred, do_not_create_dirs, Ext,
|
|
ModuleName, FileName0, !IO)
|
|
),
|
|
% The rest of this predicate should be kept in sync
|
|
% with read_module_begin_from_file.
|
|
(
|
|
Search = do_search,
|
|
globals.lookup_accumulating_option(Globals, search_directories,
|
|
SearchDirs)
|
|
;
|
|
Search = do_not_search,
|
|
SearchDirs = [dir.this_directory]
|
|
),
|
|
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
|
(
|
|
VeryVerbose = no
|
|
;
|
|
VeryVerbose = yes,
|
|
io.format("%% %s `%s'... ", [s(Descr), s(FileName0)], !IO),
|
|
io.flush_output(!IO)
|
|
).
|
|
|
|
:- pred read_module_end_module(globals::in, maybe_ignore_errors::in, bool::in,
|
|
maybe_error(path_name_and_stream)::in, file_name::in, file_name::out,
|
|
maybe(io.res(timestamp))::in, maybe(timestamp)::out,
|
|
list(error_spec)::in, list(error_spec)::out, read_module_errors::in,
|
|
io::di, io::uo) is det.
|
|
|
|
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
|
|
MaybeFileNameAndStream, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO) :-
|
|
% The code of read_module_end_module and read_module_end_file
|
|
% should be kept in sync.
|
|
(
|
|
MaybeFileNameAndStream = ok(path_name_and_stream(FileName, _))
|
|
;
|
|
MaybeFileNameAndStream = error(_),
|
|
FileName = FileName0
|
|
),
|
|
check_timestamp_report_if_needed_and_missing(Globals, FileName0,
|
|
MaybeTimestampRes, MaybeTimestamp, !IO),
|
|
(
|
|
IgnoreErrors = ignore_errors,
|
|
( if set.contains(Errors, rme_could_not_open_file) then
|
|
maybe_write_string(VeryVerbose, "not found.\n", !IO),
|
|
Specs = []
|
|
else
|
|
maybe_write_string(VeryVerbose, "done.\n", !IO),
|
|
Specs = ModuleSpecs
|
|
)
|
|
;
|
|
IgnoreErrors = do_not_ignore_errors,
|
|
handle_any_read_module_errors(Globals, VeryVerbose, Errors,
|
|
ModuleSpecs, Specs, !IO)
|
|
),
|
|
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
|
|
maybe_report_stats(Statistics, !IO).
|
|
|
|
:- pred read_module_end_file(globals::in, bool::in, file_name::in,
|
|
maybe(io.res(timestamp))::in, maybe(timestamp)::out,
|
|
list(error_spec)::in, list(error_spec)::out, read_module_errors::in,
|
|
io::di, io::uo) is det.
|
|
|
|
read_module_end_file(Globals, VeryVerbose, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp,
|
|
ModuleSpecs, Specs, Errors, !IO) :-
|
|
% The code of read_module_end_module and read_module_end_file
|
|
% should be kept in sync.
|
|
%
|
|
% Unlike read_module_end_module, we assume do_not_ignore_errors.
|
|
check_timestamp_report_if_needed_and_missing(Globals, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, !IO),
|
|
handle_any_read_module_errors(Globals, VeryVerbose, Errors,
|
|
ModuleSpecs, Specs, !IO).
|
|
|
|
:- pred handle_any_read_module_errors(globals::in, bool::in,
|
|
read_module_errors::in, list(error_spec)::in, list(error_spec)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
handle_any_read_module_errors(Globals, VeryVerbose, Errors, !Specs, !IO) :-
|
|
( if set.is_empty(Errors) then
|
|
maybe_write_string(VeryVerbose, "successful parse.\n", !IO)
|
|
else
|
|
set.intersect(Errors, fatal_read_module_errors, FatalErrors),
|
|
( if set.is_empty(FatalErrors) then
|
|
maybe_write_string(VeryVerbose, "parse error(s).\n", !IO)
|
|
else
|
|
maybe_write_string(VeryVerbose, "fatal error(s).\n", !IO)
|
|
),
|
|
pre_hlds_maybe_write_out_errors(VeryVerbose, Globals, !Specs, !IO),
|
|
io.set_exit_status(1, !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
maybe_read_module_int0(Globals, Search,
|
|
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
|
|
ParseTreeInt0, Specs, Errors, !HaveReadModuleMaps, !IO) :-
|
|
OrigHaveReadModuleMapInt0 = !.HaveReadModuleMaps ^ hrmm_int0,
|
|
( if
|
|
find_read_module_int0(OrigHaveReadModuleMapInt0, ModuleName,
|
|
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
|
|
ParseTreeInt0Prime, SpecsPrime, ErrorsPrime)
|
|
then
|
|
FileName = FileNamePrime,
|
|
MaybeTimestamp = MaybeTimestampPrime,
|
|
ParseTreeInt0 = ParseTreeInt0Prime,
|
|
Specs = SpecsPrime,
|
|
Errors = ErrorsPrime
|
|
else
|
|
Descr = "Reading .int0 interface for module",
|
|
read_module_int0(Globals, Descr, do_not_ignore_errors, Search,
|
|
ModuleName, FileName,
|
|
always_read_module(ReturnTimestamp), MaybeTimestamp,
|
|
ParseTreeInt0, Specs, Errors, !IO),
|
|
( if set.member(rme_could_not_open_file, Errors) then
|
|
HaveReadModule = tried_to_read_module_failed,
|
|
map.set(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt0, HaveReadModuleMapInt0)
|
|
else
|
|
expect(unify(ModuleName, ParseTreeInt0 ^ pti0_module_name), $pred,
|
|
"ModuleName != module name in ParseTreeInt0"),
|
|
HaveReadModule = have_successfully_read_module(FileName,
|
|
MaybeTimestamp, ParseTreeInt0, Specs, Errors),
|
|
map.det_insert(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt0, HaveReadModuleMapInt0)
|
|
),
|
|
!HaveReadModuleMaps ^ hrmm_int0 := HaveReadModuleMapInt0
|
|
).
|
|
|
|
maybe_read_module_int1(Globals, Search,
|
|
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
|
|
ParseTreeInt1, Specs, Errors, !HaveReadModuleMaps, !IO) :-
|
|
OrigHaveReadModuleMapInt1 = !.HaveReadModuleMaps ^ hrmm_int1,
|
|
( if
|
|
find_read_module_int1(OrigHaveReadModuleMapInt1, ModuleName,
|
|
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
|
|
ParseTreeInt1Prime, SpecsPrime, ErrorsPrime)
|
|
then
|
|
FileName = FileNamePrime,
|
|
MaybeTimestamp = MaybeTimestampPrime,
|
|
ParseTreeInt1 = ParseTreeInt1Prime,
|
|
Specs = SpecsPrime,
|
|
Errors = ErrorsPrime
|
|
else
|
|
Descr = "Reading .int interface for module",
|
|
read_module_int1(Globals, Descr, do_not_ignore_errors, Search,
|
|
ModuleName, FileName,
|
|
always_read_module(ReturnTimestamp), MaybeTimestamp,
|
|
ParseTreeInt1, Specs, Errors, !IO),
|
|
( if set.member(rme_could_not_open_file, Errors) then
|
|
HaveReadModule = tried_to_read_module_failed,
|
|
map.set(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt1, HaveReadModuleMapInt1)
|
|
else
|
|
expect(unify(ModuleName, ParseTreeInt1 ^ pti1_module_name), $pred,
|
|
"ModuleName != module name in ParseTreeInt1"),
|
|
HaveReadModule = have_successfully_read_module(FileName,
|
|
MaybeTimestamp, ParseTreeInt1, Specs, Errors),
|
|
map.det_insert(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt1, HaveReadModuleMapInt1)
|
|
),
|
|
!HaveReadModuleMaps ^ hrmm_int1 := HaveReadModuleMapInt1
|
|
).
|
|
|
|
maybe_read_module_int2(Globals, Search,
|
|
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
|
|
ParseTreeInt2, Specs, Errors, !HaveReadModuleMaps, !IO) :-
|
|
OrigHaveReadModuleMapInt2 = !.HaveReadModuleMaps ^ hrmm_int2,
|
|
( if
|
|
find_read_module_int2(OrigHaveReadModuleMapInt2, ModuleName,
|
|
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
|
|
ParseTreeInt2Prime, SpecsPrime, ErrorsPrime)
|
|
then
|
|
FileName = FileNamePrime,
|
|
MaybeTimestamp = MaybeTimestampPrime,
|
|
ParseTreeInt2 = ParseTreeInt2Prime,
|
|
Specs = SpecsPrime,
|
|
Errors = ErrorsPrime
|
|
else
|
|
Descr = "Reading .int2 interface for module",
|
|
read_module_int2(Globals, Descr, do_not_ignore_errors, Search,
|
|
ModuleName, FileName,
|
|
always_read_module(ReturnTimestamp), MaybeTimestamp,
|
|
ParseTreeInt2, Specs, Errors, !IO),
|
|
( if set.member(rme_could_not_open_file, Errors) then
|
|
HaveReadModule = tried_to_read_module_failed,
|
|
map.set(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt2, HaveReadModuleMapInt2)
|
|
else
|
|
expect(unify(ModuleName, ParseTreeInt2 ^ pti2_module_name), $pred,
|
|
"ModuleName != module name in ParseTreeInt2"),
|
|
HaveReadModule = have_successfully_read_module(FileName,
|
|
MaybeTimestamp, ParseTreeInt2, Specs, Errors),
|
|
map.det_insert(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt2, HaveReadModuleMapInt2)
|
|
),
|
|
!HaveReadModuleMaps ^ hrmm_int2 := HaveReadModuleMapInt2
|
|
).
|
|
|
|
maybe_read_module_int3(Globals, Search,
|
|
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
|
|
ParseTreeInt3, Specs, Errors, !HaveReadModuleMaps, !IO) :-
|
|
OrigHaveReadModuleMapInt3 = !.HaveReadModuleMaps ^ hrmm_int3,
|
|
( if
|
|
find_read_module_int3(OrigHaveReadModuleMapInt3, ModuleName,
|
|
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
|
|
ParseTreeInt3Prime, SpecsPrime, ErrorsPrime)
|
|
then
|
|
FileName = FileNamePrime,
|
|
MaybeTimestamp = MaybeTimestampPrime,
|
|
ParseTreeInt3 = ParseTreeInt3Prime,
|
|
Specs = SpecsPrime,
|
|
Errors = ErrorsPrime
|
|
else
|
|
Descr = "Reading .int3 interface for module",
|
|
read_module_int3(Globals, Descr, do_not_ignore_errors, Search,
|
|
ModuleName, FileName,
|
|
always_read_module(ReturnTimestamp), MaybeTimestamp,
|
|
ParseTreeInt3, Specs, Errors, !IO),
|
|
( if set.member(rme_could_not_open_file, Errors) then
|
|
HaveReadModule = tried_to_read_module_failed,
|
|
map.set(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt3, HaveReadModuleMapInt3)
|
|
else
|
|
expect(unify(ModuleName, ParseTreeInt3 ^ pti3_module_name), $pred,
|
|
"ModuleName != module name in ParseTreeInt3"),
|
|
HaveReadModule = have_successfully_read_module(FileName,
|
|
MaybeTimestamp, ParseTreeInt3, Specs, Errors),
|
|
map.det_insert(ModuleName, HaveReadModule,
|
|
OrigHaveReadModuleMapInt3, HaveReadModuleMapInt3)
|
|
),
|
|
!HaveReadModuleMaps ^ hrmm_int3 := HaveReadModuleMapInt3
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
find_read_module_src(HaveReadModuleMapSrc, ModuleName, ReturnTimestamp,
|
|
FileName, MaybeTimestamp, ParseTreeSrc, Specs, Errors) :-
|
|
map.search(HaveReadModuleMapSrc, ModuleName, HaveReadModule),
|
|
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
|
|
ParseTreeSrc, Specs, Errors),
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
|
|
MaybeTimestamp).
|
|
|
|
find_read_module_int(HaveReadModuleMapInt, Key,
|
|
ReturnTimestamp, FileName, MaybeTimestamp,
|
|
ParseTreeInt, Specs, Errors) :-
|
|
map.search(HaveReadModuleMapInt, Key, HaveReadModule),
|
|
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
|
|
ParseTreeInt, Specs, Errors),
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
|
|
MaybeTimestamp).
|
|
|
|
find_read_module_int0(HaveReadModuleMapInt0, ModuleName,
|
|
ReturnTimestamp, FileName, MaybeTimestamp,
|
|
ParseTreeInt0, Specs, Errors) :-
|
|
map.search(HaveReadModuleMapInt0, ModuleName, HaveReadModule),
|
|
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
|
|
ParseTreeInt0, Specs, Errors),
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
|
|
MaybeTimestamp).
|
|
|
|
find_read_module_int1(HaveReadModuleMapInt1, ModuleName,
|
|
ReturnTimestamp, FileName, MaybeTimestamp,
|
|
ParseTreeInt1, Specs, Errors) :-
|
|
map.search(HaveReadModuleMapInt1, ModuleName, HaveReadModule),
|
|
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
|
|
ParseTreeInt1, Specs, Errors),
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
|
|
MaybeTimestamp).
|
|
|
|
find_read_module_int2(HaveReadModuleMapInt2, ModuleName,
|
|
ReturnTimestamp, FileName, MaybeTimestamp,
|
|
ParseTreeInt2, Specs, Errors) :-
|
|
map.search(HaveReadModuleMapInt2, ModuleName, HaveReadModule),
|
|
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
|
|
ParseTreeInt2, Specs, Errors),
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
|
|
MaybeTimestamp).
|
|
|
|
find_read_module_int3(HaveReadModuleMapInt3, ModuleName,
|
|
ReturnTimestamp, FileName, MaybeTimestamp,
|
|
ParseTreeInt3, Specs, Errors) :-
|
|
map.search(HaveReadModuleMapInt3, ModuleName, HaveReadModule),
|
|
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
|
|
ParseTreeInt3, Specs, Errors),
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
|
|
MaybeTimestamp).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred return_timestamp_if_needed(maybe_return_timestamp::in,
|
|
maybe(timestamp)::in, maybe(timestamp)::out) is det.
|
|
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0, MaybeTimestamp) :-
|
|
(
|
|
ReturnTimestamp = do_return_timestamp,
|
|
(
|
|
MaybeTimestamp0 = no,
|
|
% This can happen if
|
|
%
|
|
% - code that does not need a timestamp enters a parse tree
|
|
% into the have_read_module_map, and then later
|
|
%
|
|
% - code that does need a timestamp finds the parse tree there.
|
|
%
|
|
% We abort because I (zs) don't think this should happen:
|
|
% the use of have_read_module_maps in module_imports.m never
|
|
% needs timestamps, the smart recompilation modules always
|
|
% need timestamps, but the have_read_module_maps they use
|
|
% are completely separate (for now). If it turns out I am wrong,
|
|
% or we *do* want these two subsystems to use the same
|
|
% have_read_module_maps, then there are two obvious possibilities:
|
|
% either *always* store the timestamp of a file we read in, or
|
|
% get the timestamp from the OS the first time it is needed
|
|
% (have_read_module_maps entries include the filename, so this
|
|
% is possible). The first solution is simpler, the second can
|
|
% execute fewer system calls.
|
|
unexpected($pred, "do_return_timestamp but no timestamp")
|
|
;
|
|
MaybeTimestamp0 = yes(_),
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
)
|
|
;
|
|
ReturnTimestamp = dont_return_timestamp,
|
|
MaybeTimestamp = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred check_timestamp_report_if_needed_and_missing(globals::in,
|
|
file_name::in, maybe(io.res(timestamp))::in, maybe(timestamp)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
check_timestamp_report_if_needed_and_missing(Globals, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, !IO) :-
|
|
(
|
|
MaybeTimestampRes = yes(ok(Timestamp)),
|
|
MaybeTimestamp = yes(Timestamp)
|
|
;
|
|
MaybeTimestampRes = yes(error(IOError)),
|
|
MaybeTimestamp = no,
|
|
globals.lookup_bool_option(Globals, smart_recompilation,
|
|
SmartRecompilation),
|
|
% Should we print the warning if smart recompilation has already been
|
|
% disabled by an earlier error? At the moment, we do.
|
|
(
|
|
SmartRecompilation = yes,
|
|
record_and_report_missing_timestamp(Globals, FileName,
|
|
IOError, !IO)
|
|
;
|
|
SmartRecompilation = no
|
|
)
|
|
;
|
|
MaybeTimestampRes = no,
|
|
MaybeTimestamp = no
|
|
).
|
|
|
|
:- pred record_and_report_missing_timestamp(globals::in, file_name::in,
|
|
io.error::in, io::di, io::uo) is det.
|
|
|
|
record_and_report_missing_timestamp(Globals, FileName, Error, !IO) :-
|
|
io_set_disable_smart_recompilation(disable_smart_recompilation, !IO),
|
|
io_set_disable_generate_item_version_numbers(disable_item_version_numbers,
|
|
!IO),
|
|
globals.lookup_bool_option(Globals, warn_smart_recompilation, Warn),
|
|
(
|
|
Warn = yes,
|
|
io.error_message(Error, Msg),
|
|
io.format("Warning: cannot find modification time for %s:\n",
|
|
[s(FileName)], !IO),
|
|
io.format(" %s.\n", [s(Msg)], !IO),
|
|
io.format(" Smart recompilation will not work.\n", [], !IO),
|
|
globals.lookup_bool_option(Globals, halt_at_warn, HaltAtWarn),
|
|
(
|
|
HaltAtWarn = yes,
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
HaltAtWarn = no
|
|
)
|
|
;
|
|
Warn = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module parse_tree.read_modules.
|
|
%-----------------------------------------------------------------------------%
|