Files
mercury/compiler/prog_io.m
Zoltan Somogyi a83c9b662f Move some predicates to the modules where they belong. There are no
algorithmic changes.

compiler/add_pragma.m:
compiler/hlds_code_util.m:
	Move some predicates from add_pragma.m to hlds_code_util.m;
	they are used not only when adding pragmas.

compiler/prog_io.m:
compiler/prog_mode.m:
	Move some predicates from prog_io.m to prog_mode.m;
	they do not do parsing.

compiler/add_clause.m:
compiler/prog_io_pragma.m:
	Conform to the changes above.

compiler/hlds.m:
	Fix typos.
2012-09-07 11:42:01 +00:00

2369 lines
96 KiB
Mathematica

%-----------------------------------------------------------------------------e
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------e
% Copyright (C) 1993-2012 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: prog_io.m.
% Main authors: fjh, zs.
%
% This module defines predicates for parsing Mercury programs.
%
% In some ways the representation of programs here is considerably
% more complex than is necessary for the compiler.
% The basic reason for this is that it was designed to preserve
% as much information about the source code as possible, so that
% this representation could also be used for other tools such
% as pretty-printers.
% Currently the only information that is lost is that comments and
% whitespace are stripped, any redundant parenthesization
% are lost, distinctions between different spellings of the same
% operator (eg "\+" vs "not") are lost, and DCG clauses get expanded.
% It would be a good idea to preserve all those too (well, maybe not
% the redundant parentheses), but right now it's not worth the effort.
%
% So that means that this phase of compilation is purely parsing.
% No simplifications are done (other than DCG expansion).
% The results of this phase specify
% basically the same information as is contained in the source code,
% but in a parse tree rather than a flat file.
% Simplifications are done only by make_hlds.m, which transforms
% the parse tree which we built here into the HLDS.
%
% Wishlist:
%
% 1. implement importing/exporting operators with a particular fixity
% eg. :- import_op prefix(+). % only prefix +, not infix
% (not important, but should be there for reasons of symmetry.)
% 2. improve the handling of type and inst parameters
% 3. improve the error reporting (most of the semidet preds should
% be det and should return a meaningful indication of where an
% error occurred).
%
%-----------------------------------------------------------------------------%
:- module parse_tree.prog_io.
:- interface.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module libs.timestamp.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.error_util.
:- import_module parse_tree.prog_item.
:- import_module parse_tree.prog_io_util.
:- import_module io.
:- import_module list.
:- import_module maybe.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
% This module (prog_io) exports the following predicates:
:- type module_error
---> no_module_errors % no errors
; some_module_errors % some syntax errors
; fatal_module_errors. % couldn't open the file
:- type maybe_return_timestamp
---> do_return_timestamp
; do_not_return_timestamp.
% actually_read_module(OpenFile, FileName, DefaultModuleName,
% ReturnTimestamp, MaybeFileInfo, ActualModuleName, Items,
% Specs, Error, MaybeModuleTimestamp, !IO):
%
% Reads and parses the file opened by OpenFile using the default module
% name DefaultModuleName. If ReturnTimestamp is `yes', attempt to return
% the modification timestamp in MaybeModuleTimestamp. Error is
% `fatal_module_errors' if the file coudn't be opened, `some_module_errors'
% if a syntax error was detected, and `no_module_errors' otherwise.
% MaybeFileInfo is the information about the file (usually the file or
% directory name) returned by OpenFile. ActualModuleName is the module name
% specified in the `:- module' declaration, if any, or the
% DefaultModuleName if there is no `:- module' declaration.
% Specs is a list of warning/error messages. Items is the parse tree.
%
:- pred actually_read_module(globals::in,
open_file_pred(FileInfo)::in(open_file_pred), module_name::in,
maybe_return_timestamp::in, maybe(FileInfo)::out,
module_name::out, list(item)::out, list(error_spec)::out,
module_error::out, maybe(io.res(timestamp))::out, io::di, io::uo) is det.
:- pred actually_read_module_if_changed(globals::in,
open_file_pred(FileInfo)::in(open_file_pred),
module_name::in, timestamp::in, maybe(FileInfo)::out, module_name::out,
list(item)::out, list(error_spec)::out, module_error::out,
maybe(io.res(timestamp))::out, io::di, io::uo) is det.
% Same as actually_read_module, but use intermod_directories instead of
% search_directories when searching for the file.
% Also report an error if the actual module name doesn't match
% the expected module name.
%
:- pred actually_read_opt_file(globals::in, file_name::in, module_name::in,
list(item)::out, list(error_spec)::out, module_error::out,
io::di, io::uo) is det.
% check_module_has_expected_name(FileName, ExpectedName, ActualName):
%
% Check that two module names are equal, and report an error if they
% aren't.
%
:- pred check_module_has_expected_name(file_name::in, module_name::in,
module_name::in, list(error_spec)::out) is det.
% search_for_module_source(Globals, Dirs, InterfaceDirs, ModuleName,
% FoundSourceFileName, !IO):
%
% Look for the source for ModuleName in Dirs. This will also search for
% files matching partially qualified versions of ModuleName, but only if
% a more qualified `.m' or `.int' file doesn't exist in InterfaceDirs.
% in InterfaceDirs. For example, module foo.bar.baz can be found in
% foo.bar.m, bar.baz.m or bar.m.
%
:- pred search_for_module_source(globals::in, list(dir_name)::in,
list(dir_name)::in, module_name::in, maybe_error(file_name)::out,
io::di, io::uo) is det.
% Read the first item from the given file to find the module name.
%
:- pred find_module_name(globals::in, file_name::in, maybe(module_name)::out,
io::di, io::uo) is det.
% parse_item(ModuleName, VarSet, Term, SeqNum, MaybeItem):
%
% Parse Term. If successful, bind MaybeItem to the parsed item,
% otherwise bind it to an appropriate error message. Qualify
% appropriate parts of the item, with ModuleName as the module name.
% Use SeqNum as the item's sequence number.
%
:- pred parse_item(module_name::in, varset::in, term::in, int::in,
maybe1(item)::out) is det.
% parse_decl(ModuleName, VarSet, Term, SeqNum, MaybeItem):
%
% Parse Term as a declaration. If successful, bind MaybeItem to the
% parsed item, otherwise it is bound to an appropriate error message.
% Qualify appropriate parts of the item, with ModuleName as the module
% name. Use SeqNum as the item's sequence number.
%
:- pred parse_decl(module_name::in, varset::in, term::in, int::in,
maybe1(item)::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module libs.options.
:- import_module parse_tree.file_names.
:- import_module parse_tree.mercury_to_mercury.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_io_dcg.
:- import_module parse_tree.prog_io_goal.
:- import_module parse_tree.prog_io_mode_defn.
:- import_module parse_tree.prog_io_mutable.
:- import_module parse_tree.prog_io_pragma.
:- import_module parse_tree.prog_io_sym_name.
:- import_module parse_tree.prog_io_type_defn.
:- import_module parse_tree.prog_io_typeclass.
:- import_module parse_tree.prog_io_util.
:- import_module parse_tree.prog_mode.
:- import_module recompilation.
:- import_module recompilation.version.
:- import_module assoc_list.
:- import_module bool.
:- import_module counter.
:- import_module dir.
:- import_module map.
:- import_module pair.
:- import_module parser.
:- import_module require.
:- import_module set.
:- import_module string.
:- import_module term_io.
%-----------------------------------------------------------------------------%
actually_read_module(Globals, OpenFile, DefaultModuleName, ReturnTimestamp,
FileData, ModuleName, Items, Specs, Error, MaybeModuleTimestamp,
!IO) :-
do_actually_read_module(Globals, OpenFile, DefaultModuleName,
no, ReturnTimestamp, FileData, ModuleName,
Items, Specs, Error, MaybeModuleTimestamp, !IO).
actually_read_module_if_changed(Globals, OpenFile, DefaultModuleName,
OldTimestamp, FileData, ModuleName, Items, Specs, Error,
MaybeModuleTimestamp, !IO) :-
do_actually_read_module(Globals, OpenFile, DefaultModuleName,
yes(OldTimestamp), do_return_timestamp, FileData, ModuleName,
Items, Specs, Error,MaybeModuleTimestamp, !IO).
actually_read_opt_file(Globals, FileName, DefaultModuleName,
Items, Specs, Error, !IO) :-
globals.lookup_accumulating_option(Globals, intermod_directories, Dirs),
do_actually_read_module(Globals,
search_for_file(open_file, Dirs, FileName),
DefaultModuleName, no, do_not_return_timestamp, _, ModuleName, Items,
ItemSpecs, Error, _, !IO),
check_module_has_expected_name(FileName, DefaultModuleName, ModuleName,
NameSpecs),
Specs = ItemSpecs ++ NameSpecs.
check_module_has_expected_name(FileName, ExpectedName, ActualName, Specs) :-
( ActualName \= ExpectedName ->
Pieces = [words("Error: file"), quote(FileName),
words("contains the wrong module."), nl,
words("Expected module"), sym_name(ExpectedName), suffix(","),
words("found module"), sym_name(ActualName), suffix("."), nl],
Msg = error_msg(no, treat_as_first, 0, [always(Pieces)]),
Spec = error_spec(severity_error, phase_read_files, [Msg]),
Specs = [Spec]
;
Specs = []
).
% This implementation uses io.read_term to read in the program one term
% at a time, and then converts those terms into clauses and declarations,
% checking for errors as it goes. Note that rather than using difference
% lists, we just build up the lists of items and messages in reverse order
% and then reverse them afterwards. (Using difference lists would require
% late-input modes.)
%
:- pred do_actually_read_module(globals::in,
open_file_pred(T)::in(open_file_pred), module_name::in,
maybe(timestamp)::in, maybe_return_timestamp::in,
maybe(T)::out, module_name::out, list(item)::out, list(error_spec)::out,
module_error::out, maybe(io.res(timestamp))::out, io::di, io::uo) is det.
do_actually_read_module(Globals, OpenFile, DefaultModuleName,
MaybeOldTimestamp, ReturnTimestamp, MaybeFileData, ModuleName,
Items, Specs, Error, MaybeModuleTimestamp, !IO) :-
io.input_stream(OldInputStream, !IO),
OpenFile(OpenResult, !IO),
(
OpenResult = ok(FileData),
MaybeFileData = yes(FileData),
(
ReturnTimestamp = do_return_timestamp,
io.input_stream_name(InputStreamName, !IO),
io.file_modification_time(InputStreamName, TimestampResult, !IO),
(
TimestampResult = ok(Timestamp),
MaybeModuleTimestamp = yes(ok(time_t_to_timestamp(Timestamp)))
;
TimestampResult = error(IOError),
MaybeModuleTimestamp = yes(error(IOError))
)
;
ReturnTimestamp = do_not_return_timestamp,
MaybeModuleTimestamp = no
),
(
MaybeOldTimestamp = yes(OldTimestamp),
MaybeModuleTimestamp = yes(ok(OldTimestamp))
->
% XXX Currently smart recompilation won't work
% if ModuleName \= DefaultModuleName.
% In that case, smart recompilation will be disabled
% and actually_read_module should never be passed an old timestamp.
ModuleName = DefaultModuleName,
Items = [],
Specs = [],
Error = no_module_errors
;
read_all_items(Globals, DefaultModuleName, ModuleName, Items,
Specs, Error, !IO)
),
io.set_input_stream(OldInputStream, ModuleInputStream, !IO),
io.close_input(ModuleInputStream, !IO)
;
OpenResult = error(ErrorMsg),
MaybeFileData = no,
ModuleName = DefaultModuleName,
Items = [],
MaybeModuleTimestamp = no,
io.progname_base("mercury_compile", Progname, !IO),
Pieces = [fixed(Progname), suffix(":"), words(ErrorMsg), nl],
Spec = error_spec(severity_error, phase_read_files,
[error_msg(no, treat_as_first, 0, [always(Pieces)])]),
Specs = [Spec],
Error = fatal_module_errors
).
search_for_module_source(Globals, Dirs, InterfaceDirs, ModuleName,
MaybeFileName, !IO) :-
search_for_module_source_2(Globals, Dirs, ModuleName, ModuleName,
MaybeFileName0, !IO),
(
MaybeFileName0 = ok(SourceFileName),
(
string.remove_suffix(dir.basename(SourceFileName),
".m", SourceFileBaseName),
file_name_to_module_name(SourceFileBaseName, SourceFileModuleName),
ModuleName \= SourceFileModuleName
->
% The module name doesn't match the file name. Return an error
% if there is a more qualified matching `.m' or `.int' file in
% the interface search path. This avoids having a file `read.m'
% in the current directory prevent the compiler from finding
% `bit_buffer.read.int' in the standard library.
io.input_stream(SourceStream, !IO),
search_for_module_source_2(Globals, InterfaceDirs, ModuleName,
ModuleName, MaybeFileName2, !IO),
( MaybeFileName2 = ok(_) ->
io.seen(!IO)
;
true
),
(
MaybeFileName2 = ok(SourceFileName2),
SourceFileName2 \= SourceFileName,
string.remove_suffix(dir.basename(SourceFileName2), ".m",
SourceFileBaseName2),
file_name_to_module_name(SourceFileBaseName2,
SourceFileModuleName2),
match_sym_name(SourceFileModuleName, SourceFileModuleName2)
->
io.close_input(SourceStream, !IO),
MaybeFileName = error(find_source_error(ModuleName,
Dirs, yes(SourceFileName2)))
;
module_name_to_file_name(Globals, ModuleName, ".int",
do_not_create_dirs, IntFile, !IO),
search_for_file_returning_dir(do_not_open_file, InterfaceDirs,
IntFile, MaybeIntDir, !IO),
(
MaybeIntDir = ok(IntDir),
IntDir \= dir.this_directory
->
io.close_input(SourceStream, !IO),
MaybeFileName = error(find_source_error(ModuleName,
Dirs, yes(IntDir/IntFile)))
;
io.set_input_stream(SourceStream, _, !IO),
MaybeFileName = MaybeFileName0
)
)
;
MaybeFileName = MaybeFileName0
)
;
MaybeFileName0 = error(_),
MaybeFileName = MaybeFileName0
).
:- func find_source_error(module_name, list(dir_name), maybe(file_name))
= string.
find_source_error(ModuleName, Dirs, MaybeBetterMatch) = Msg :-
ModuleNameStr = sym_name_to_string(ModuleName),
Msg0 = "cannot find source for module `" ++ ModuleNameStr ++
"' in directories " ++ string.join_list(", ", Dirs),
(
MaybeBetterMatch = no,
Msg = Msg0
;
MaybeBetterMatch = yes(BetterMatchFile),
Msg = Msg0 ++ ", but found " ++ BetterMatchFile ++
" in interface search path"
).
:- pred search_for_module_source_2(globals::in, list(dir_name)::in,
module_name::in, module_name::in, maybe_error(file_name)::out,
io::di, io::uo) is det.
search_for_module_source_2(Globals, Dirs, ModuleName, PartialModuleName,
MaybeFileName, !IO) :-
module_name_to_file_name(Globals, PartialModuleName, ".m",
do_not_create_dirs, FileName, !IO),
search_for_file(open_file, Dirs, FileName, MaybeFileName0, !IO),
(
MaybeFileName0 = ok(_),
MaybeFileName = MaybeFileName0
;
MaybeFileName0 = error(_),
( PartialModuleName1 = drop_one_qualifier(PartialModuleName) ->
search_for_module_source_2(Globals, Dirs, ModuleName,
PartialModuleName1, MaybeFileName, !IO)
;
MaybeFileName = error(find_source_error(ModuleName, Dirs, no))
)
).
:- func drop_one_qualifier(module_name) = module_name is semidet.
drop_one_qualifier(qualified(ParentQual, ChildName)) =
drop_one_qualifier_2(ParentQual, ChildName).
:- func drop_one_qualifier_2(module_name, string) = module_name.
drop_one_qualifier_2(ParentQual, ChildName) = PartialQual :-
(
ParentQual = unqualified(_ParentName),
PartialQual = unqualified(ChildName)
;
ParentQual = qualified(GrandParentQual, ParentName),
PartialGrandParentQual = drop_one_qualifier_2(GrandParentQual,
ParentName),
PartialQual = qualified(PartialGrandParentQual, ChildName)
).
%-----------------------------------------------------------------------------%
:- type module_end
---> module_end_no
; module_end_yes(module_name, prog_context).
% Extract the final `:- end_module' declaration if any.
%
:- pred get_end_module(module_name::in, list(item)::in, list(item)::out,
module_end::out) is det.
get_end_module(ModuleName, RevItems0, RevItems, EndModule) :-
(
% Note: if the module name in the end_module declaration does not match
% what we expect, given the source file name, then we assume that it is
% for a nested module, and so we leave it alone. If it is not for a
% nested module, the error will be caught by make_hlds.
RevItems0 = [Item | RevItemsPrime],
Item = item_module_end(ItemModuleEnd),
ItemModuleEnd = item_module_end_info(ModuleName, Context, _SeqNum)
->
RevItems = RevItemsPrime,
EndModule = module_end_yes(ModuleName, Context)
;
RevItems = RevItems0,
EndModule = module_end_no
).
%-----------------------------------------------------------------------------%
% Check that the module starts with a :- module declaration,
% and that the end_module declaration (if any) is correct,
% and construct the final parsing result.
%
:- pred check_end_module(module_end::in, list(item)::in, list(item)::out,
list(error_spec)::in, list(error_spec)::out,
module_error::in, module_error::out) is det.
check_end_module(EndModule, !Items, !Specs, !Error) :-
% Double-check that the first item is a `:- module ModuleName' declaration,
% and remove it from the front of the item list.
(
!.Items = [Item | !:Items],
Item = item_module_start(ItemModuleStart),
ItemModuleStart = item_module_start_info(StartModuleName, _, _)
->
% Check that the end module declaration (if any) matches
% the begin module declaration.
(
EndModule = module_end_yes(EndModuleName, EndModuleContext),
StartModuleName \= EndModuleName
->
Pieces = [words("Error:"),
quote(":- end_module"), words("declaration"),
words("does not match"),
quote(":- module"), words("declaration."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(EndModuleContext, [always(Pieces)])]),
!:Specs = [Spec | !.Specs],
!:Error = some_module_errors
;
true
)
;
% If there's no `:- module' declaration at this point, it is
% an internal error -- read_first_item should have inserted one.
unexpected($module, $pred, "no `:- module' declaration")
).
%-----------------------------------------------------------------------------%
% Create a dummy term. Used for error messages that are not associated
% with any particular term or context.
%
:- pred dummy_term(term::out) is det.
dummy_term(Term) :-
term.context_init(Context),
dummy_term_with_context(Context, Term).
% Create a dummy term with the specified context.
% Used for error messages that are associated with some specific
% context, but for which we don't want to print out the term
% (or for which the term isn't available to be printed out).
%
:- pred dummy_term_with_context(term.context::in, term::out) is det.
dummy_term_with_context(Context, Term) :-
Term = term.functor(term.atom(""), [], Context).
%-----------------------------------------------------------------------------%
find_module_name(Globals, FileName, MaybeModuleName, !IO) :-
io.open_input(FileName, OpenRes, !IO),
(
OpenRes = ok(InputStream),
io.set_input_stream(InputStream, OldInputStream, !IO),
( string.remove_suffix(FileName, ".m", PartialFileName0) ->
PartialFileName = PartialFileName0
;
PartialFileName = FileName
),
( dir.basename(PartialFileName, BaseName0) ->
BaseName = BaseName0
;
BaseName = ""
),
file_name_to_module_name(BaseName, DefaultModuleName),
counter.init(1, SeqNumCounter0),
read_first_item(DefaultModuleName, FileName, _,
ModuleName, _, _, Specs, _, SeqNumCounter0, _, !IO),
MaybeModuleName = yes(ModuleName),
% XXX _NumErrors
write_error_specs(Specs, Globals, 0, _NumWarnings, 0, _NumErrors, !IO),
io.set_input_stream(OldInputStream, _, !IO),
io.close_input(InputStream, !IO)
;
OpenRes = error(Error),
ErrorMsg = io.error_message(Error),
io.progname_base("mercury_compile", Progname, !IO),
Pieces = [fixed(Progname), suffix(":"), words("error opening"),
quote(FileName), suffix(":"), words(ErrorMsg), suffix("."), nl],
Spec = error_spec(severity_error, phase_read_files,
[error_msg(no, treat_as_first, 0, [always(Pieces)])]),
% XXX _NumErrors
write_error_spec(Spec, Globals, 0, _NumWarnings, 0, _NumErrors, !IO),
MaybeModuleName = no
).
% Read a source file from standard in, first reading in
% the input term by term and then parsing those terms and producing
% a high-level representation.
% Parsing is actually a 3-stage process instead of the
% normal two-stage process:
% lexical analysis (chars -> tokens),
% parsing stage 1 (tokens -> terms),
% parsing stage 2 (terms -> items).
% The final stage produces a list of program items, each of which
% may be a declaration or a clause.
%
% We use a continuation-passing style here.
%
:- pred read_all_items(globals::in, module_name::in, module_name::out,
list(item)::out, list(error_spec)::out, module_error::out,
io::di, io::uo) is det.
read_all_items(Globals, DefaultModuleName, ModuleName, Items,
!:Specs, !:Error, !IO) :-
some [!SeqNumCounter] (
counter.init(1, !:SeqNumCounter),
% Read all the items (the first one is handled specially).
io.input_stream(Stream, !IO),
io.input_stream_name(Stream, SourceFileName0, !IO),
read_first_item(DefaultModuleName, SourceFileName0, SourceFileName,
ModuleName, ModuleDeclItem, MaybeSecondTerm, !:Specs, !:Error,
!SeqNumCounter, !IO),
RevItems0 = [ModuleDeclItem],
(
MaybeSecondTerm = yes(SecondTerm),
% XXX Should this be SourceFileName instead of SourceFileName0?
read_term_to_item_result(ModuleName, SourceFileName0, SecondTerm,
!SeqNumCounter, MaybeSecondItem),
read_items_loop_2(Globals, MaybeSecondItem, ModuleName,
SourceFileName, RevItems0, RevItems1,
!Specs, !Error, !.SeqNumCounter, _, !IO)
;
MaybeSecondTerm = no,
read_items_loop(Globals, ModuleName, SourceFileName,
RevItems0, RevItems1, !Specs, !Error, !.SeqNumCounter, _, !IO)
),
% Get the end_module declaration (if any), check that it matches
% the initial module declaration (if any), and remove both of them
% from the final item list.
get_end_module(ModuleName, RevItems1, RevItems, EndModule),
list.reverse(RevItems, Items0),
check_end_module(EndModule, Items0, Items, !Specs, !Error)
).
% We need to jump through a few hoops when reading the first item,
% to allow us to recover from a missing initial `:- module' declaration.
% The reason is that in order to parse an item, we need to know
% which module it is defined in (because we do some module
% qualification and checking of module qualifiers at parse time),
% but the initial `:- module' declaration and the declaration
% that follows it occur in different scopes, so we need to know
% what it is that we are parsing before we can parse it!
% We solve this dilemma by first parsing it in the root scope,
% and then if it turns out to not be a `:- module' declaration
% we reparse it in the default module scope. Blecchh.
%
:- pred read_first_item(module_name::in, file_name::in, file_name::out,
module_name::out, item::out, maybe(read_term)::out,
list(error_spec)::out, module_error::out, counter::in, counter::out,
io::di, io::uo) is det.
read_first_item(DefaultModuleName, !SourceFileName, ModuleName,
ModuleDeclItem, MaybeSecondTerm, Specs, Error, !SeqNumCounter, !IO) :-
% Parse the first term, treating it as occurring within the scope
% of the special "root" module (so that any `:- module' declaration
% is taken to be a non-nested module unless explicitly qualified).
parser.read_term_filename(!.SourceFileName, MaybeFirstTerm, !IO),
read_term_to_item_result(root_module_name, !.SourceFileName,
MaybeFirstTerm, !SeqNumCounter, MaybeFirstItem),
(
% Apply and then skip `pragma source_file' decls, by calling ourselves
% recursively with the new source file name.
MaybeFirstItem = read_item_ok(FirstItem),
FirstItem = item_pragma(FirstItemPragma),
FirstItemPragma = item_pragma_info(_, Pragma, _, _),
Pragma = pragma_source_file(SFNInfo)
->
SFNInfo = pragma_info_source_file(!:SourceFileName),
read_first_item(DefaultModuleName, !SourceFileName, ModuleName,
ModuleDeclItem, MaybeSecondTerm, Specs, Error, !SeqNumCounter, !IO)
;
% Check if the first term was a `:- module' decl.
MaybeFirstItem = read_item_ok(FirstItem),
FirstItem = item_module_start(FirstItemModuleStart),
FirstItemModuleStart = item_module_start_info(StartModuleName,
FirstContext, _FirstItemSeqNum)
->
% If so, then check that it matches the expected module name,
% and if not, report a warning.
( match_sym_name(StartModuleName, DefaultModuleName) ->
ModuleName = DefaultModuleName,
Specs = [],
Error = no_module_errors
; match_sym_name(DefaultModuleName, StartModuleName) ->
ModuleName = StartModuleName,
Specs = [],
Error = no_module_errors
;
% XXX I think this should be an error, not a warning. -zs
Pieces = [words("Error: source file"), quote(!.SourceFileName),
words("contains module named"), sym_name(StartModuleName),
suffix("."), nl],
Severity = severity_conditional(warn_wrong_module_name, yes,
severity_error, no),
Msgs = [option_is_set(warn_wrong_module_name, yes,
[always(Pieces)])],
Spec = error_spec(Severity, phase_term_to_parse_tree,
[simple_msg(FirstContext, Msgs)]),
Specs = [Spec],
% Which one should we use here? We used to use the default module
% name (computed from the filename) but now we use the declared
% one.
ModuleName = StartModuleName,
Error = some_module_errors
),
make_module_decl(ModuleName, FirstContext, ModuleDeclItem),
MaybeSecondTerm = no
;
% If the first term was not a `:- module' decl, then generate an
% error message, and insert an implicit `:- module ModuleName' decl.
( MaybeFirstItem = read_item_ok(FirstItem) ->
FirstContext = get_item_context(FirstItem)
;
term.context_init(!.SourceFileName, 1, FirstContext)
),
Pieces = [words("Error: module must start with a"),
quote(":- module"), words("declaration."), nl],
Severity = severity_error,
Msgs = [always(Pieces)],
Spec = error_spec(Severity, phase_term_to_parse_tree,
[simple_msg(FirstContext, Msgs)]),
Specs = [Spec],
Error = some_module_errors,
ModuleName = DefaultModuleName,
make_module_decl(ModuleName, FirstContext, ModuleDeclItem),
% Reparse the first term, this time treating it as occuring within
% the scope of the implicit `:- module' decl rather than in the
% root module.
MaybeSecondTerm = yes(MaybeFirstTerm)
).
:- pred make_module_decl(module_name::in, term.context::in, item::out) is det.
make_module_decl(ModuleName, Context, Item) :-
ItemInfo = item_module_start_info(ModuleName, Context, -1),
Item = item_module_start(ItemInfo).
%-----------------------------------------------------------------------------%
% The code below was carefully optimized to run efficiently in NU-Prolog.
% We used to call read_item(MaybeItem) - which does all the work for
% a single item - via io.gc_call/1, which called the goal with
% garbage collection. But optimizing for NU-Prolog is no longer a concern.
:- pred read_items_loop(globals::in, module_name::in, file_name::in,
list(item)::in, list(item)::out,
list(error_spec)::in, list(error_spec)::out,
module_error::in, module_error::out, counter::in, counter::out,
io::di, io::uo) is det.
read_items_loop(Globals, ModuleName, SourceFileName, !Items,
!Specs, !Error, !SeqNumCounter, !IO) :-
read_item(ModuleName, SourceFileName, MaybeItem, !SeqNumCounter, !IO),
read_items_loop_2(Globals, MaybeItem, ModuleName, SourceFileName, !Items,
!Specs, !Error, !SeqNumCounter, !IO).
%-----------------------------------------------------------------------------%
:- pred read_items_loop_2(globals::in, read_item_result::in, module_name::in,
file_name::in, list(item)::in, list(item)::out,
list(error_spec)::in, list(error_spec)::out,
module_error::in, module_error::out, counter::in, counter::out,
io::di, io::uo) is det.
read_items_loop_2(Globals, MaybeItemOrEOF, !.ModuleName, !.SourceFileName,
!Items, !Specs, !Error, !SeqNumCounter, !IO) :-
(
MaybeItemOrEOF = read_item_eof
% If the next item was end-of-file, then we're done.
;
% If the next item had some errors, then insert them
% in the list of errors and continue looping.
MaybeItemOrEOF = read_item_errors(ItemSpecs),
!:Specs = ItemSpecs ++ !.Specs,
!:Error = some_module_errors,
read_items_loop(Globals, !.ModuleName, !.SourceFileName, !Items,
!Specs, !Error, !SeqNumCounter, !IO)
;
MaybeItemOrEOF = read_item_ok(Item),
read_items_loop_ok(Globals, Item, !ModuleName, !SourceFileName, !Items,
!Specs, !Error, !IO),
read_items_loop(Globals, !.ModuleName, !.SourceFileName, !Items,
!Specs, !Error, !SeqNumCounter, !IO)
).
:- pred read_items_loop_ok(globals::in, item::in,
module_name::in, module_name::out, file_name::in, file_name::out,
list(item)::in, list(item)::out,
list(error_spec)::in, list(error_spec)::out,
module_error::in, module_error::out, io::di, io::uo) is det.
read_items_loop_ok(Globals, Item, !ModuleName, !SourceFileName, !Items,
!Specs, !Error, !IO) :-
% If the next item was a valid item, check whether it was a declaration
% that affects the current parsing context -- i.e. either a `module' or
% `end_module' declaration, or a `pragma source_file' declaration.
% If so, set the new parsing context according. Next, unless the item
% is a `pragma source_file' declaration, insert it into the item list.
% Then continue looping.
(
Item = item_module_start(ItemModuleStart),
ItemModuleStart = item_module_start_info(NestedModuleName, _, _),
!:ModuleName = NestedModuleName,
!:Items = [Item | !.Items]
;
Item = item_module_end(ItemModuleEnd),
ItemModuleEnd = item_module_end_info(NestedModuleName, _, _),
sym_name_get_module_name_default(NestedModuleName,
root_module_name, ParentModuleName),
!:ModuleName = ParentModuleName,
!:Items = [Item | !.Items]
;
Item = item_module_defn(ItemModuleDefn),
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, SeqNum),
( ModuleDefn = md_import(Modules) ->
list.map(make_pseudo_import_module_decl(Context, SeqNum),
Modules, ImportItems),
!:Items = ImportItems ++ !.Items
; ModuleDefn = md_use(Modules) ->
list.map(make_pseudo_use_module_decl(Context, SeqNum),
Modules, UseItems),
!:Items = UseItems ++ !.Items
; ModuleDefn = md_include_module(Modules) ->
list.map(make_pseudo_include_module_decl(Context, SeqNum),
Modules, IncludeItems),
!:Items = IncludeItems ++ !.Items
;
!:Items = [Item | !.Items]
)
;
Item = item_pragma(ItemPragma),
ItemPragma = item_pragma_info(_, Pragma, _, _),
( Pragma = pragma_source_file(SFNInfo) ->
SFNInfo = pragma_info_source_file(!:SourceFileName)
;
!:Items = [Item | !.Items]
)
;
( Item = item_clause(_)
; Item = item_type_defn(_)
; Item = item_inst_defn(_)
; Item = item_mode_defn(_)
; Item = item_pred_decl(_)
; Item = item_mode_decl(_)
; Item = item_promise(_)
; Item = item_typeclass(_)
; Item = item_instance(_)
; Item = item_initialise(_)
; Item = item_finalise(_)
; Item = item_mutable(_)
),
!:Items = [Item | !.Items]
;
Item = item_nothing(ItemNothing),
ItemNothing = item_nothing_info(MaybeWarning, Context, NothingSeqNum),
(
MaybeWarning = no,
!:Items = [Item | !.Items]
;
MaybeWarning = yes(Warning),
Warning = item_warning(MaybeOption, Msg, Term),
(
MaybeOption = yes(Option),
globals.lookup_bool_option(Globals, Option, Warn)
;
MaybeOption = no,
Warn = yes
),
(
Warn = yes,
Pieces = [words("Warning: "), words(Msg), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(Term), [always(Pieces)])]),
!:Specs = [Spec | !.Specs],
globals.lookup_bool_option(Globals, halt_at_warn, Halt),
(
Halt = yes,
!:Error = some_module_errors
;
Halt = no
)
;
Warn = no
),
NoWarnItemNothing = item_nothing_info(no, Context, NothingSeqNum),
NoWarnItem = item_nothing(NoWarnItemNothing),
!:Items = [NoWarnItem | !.Items]
)
).
%-----------------------------------------------------------------------------%
:- type read_item_result
---> read_item_eof
; read_item_errors(list(error_spec))
; read_item_ok(item).
% Read_item/1 reads a single item, and if it is a valid term parses it.
%
:- pred read_item(module_name::in, file_name::in, read_item_result::out,
counter::in, counter::out, io::di, io::uo) is det.
read_item(ModuleName, SourceFileName, MaybeItem, !SeqNumCounter, !IO) :-
parser.read_term_filename(SourceFileName, MaybeTerm, !IO),
read_term_to_item_result(ModuleName, SourceFileName, MaybeTerm,
!SeqNumCounter, MaybeItem).
:- pred read_term_to_item_result(module_name::in, string::in, read_term::in,
counter::in, counter::out, read_item_result::out) is det.
read_term_to_item_result(ModuleName, FileName, ReadTermResult,
!SeqNumCounter, ReadItemResult) :-
(
ReadTermResult = eof,
ReadItemResult = read_item_eof
;
ReadTermResult = error(ErrorMsg, LineNumber),
% XXX Do we need to add an "Error:" prefix?
Pieces = [words(ErrorMsg), suffix("."), nl],
Context = term.context_init(FileName, LineNumber),
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(Context, [always(Pieces)])]),
ReadItemResult = read_item_errors([Spec])
;
ReadTermResult = term(VarSet, Term),
counter.allocate(SeqNum, !SeqNumCounter),
parse_item(ModuleName, VarSet, Term, SeqNum, MaybeItem),
(
MaybeItem = ok1(Item),
ReadItemResult = read_item_ok(Item)
;
MaybeItem = error1(Specs),
ReadItemResult = read_item_errors(Specs)
)
).
parse_item(ModuleName, VarSet, Term, SeqNum, Result) :-
(
Term = term.functor(term.atom(":-"), [DeclTerm], _DeclContext)
->
% Term is a declaration.
parse_decl(ModuleName, VarSet, DeclTerm, SeqNum, Result)
;
Term = term.functor(term.atom("-->"), [DCG_H_Term, DCG_B_Term],
DCG_Context)
->
% Term is a DCG clause.
parse_dcg_clause(ModuleName, VarSet, DCG_H_Term, DCG_B_Term,
DCG_Context, SeqNum, Result)
;
% Term is a clause; either a fact or a rule.
(
Term = term.functor(term.atom(":-"),
[HeadTermPrime, BodyTermPrime], TermContext)
->
% It's a rule.
HeadTerm = HeadTermPrime,
BodyTerm = BodyTermPrime,
ClauseContext = TermContext
;
% It's a fact.
HeadTerm = Term,
ClauseContext = get_term_context(HeadTerm),
BodyTerm = term.functor(term.atom("true"), [], ClauseContext)
),
varset.coerce(VarSet, ProgVarSet),
parse_clause(ModuleName, HeadTerm, BodyTerm, ProgVarSet,
ClauseContext, SeqNum, Result)
).
:- pred parse_clause(module_name::in, term::in, term::in,
prog_varset::in, term.context::in, int::in, maybe1(item)::out) is det.
parse_clause(ModuleName, HeadTerm, BodyTerm0, ProgVarSet0, Context,
SeqNum, MaybeItem) :-
GoalContextPieces = [],
parse_goal(BodyTerm0, GoalContextPieces, MaybeBodyGoal,
ProgVarSet0, ProgVarSet),
(
MaybeBodyGoal = ok1(BodyGoal),
varset.coerce(ProgVarSet, VarSet),
(
HeadTerm = term.functor(term.atom("="),
[FuncHeadTerm0, FuncResultTerm], _),
FuncHeadTerm = desugar_field_access(FuncHeadTerm0)
->
HeadContextPieces = [words("In equation head:")],
parse_implicitly_qualified_sym_name_and_args(ModuleName,
FuncHeadTerm, VarSet, HeadContextPieces, MaybeFunctor),
(
MaybeFunctor = ok2(Name, ArgTerms0),
list.map(term.coerce, ArgTerms0 ++ [FuncResultTerm],
ProgArgTerms),
ItemClause = item_clause_info(user, ProgVarSet, pf_function,
Name, ProgArgTerms, BodyGoal, Context, SeqNum),
Item = item_clause(ItemClause),
MaybeItem = ok1(Item)
;
MaybeFunctor = error2(Specs),
MaybeItem = error1(Specs)
)
;
HeadContextPieces = [words("In clause head:")],
parse_implicitly_qualified_sym_name_and_args(ModuleName, HeadTerm,
VarSet, HeadContextPieces, MaybeFunctor),
(
MaybeFunctor = ok2(Name, ArgTerms),
list.map(term.coerce, ArgTerms, ProgArgTerms),
ItemClause = item_clause_info(user, ProgVarSet, pf_predicate,
Name, ProgArgTerms, BodyGoal, Context, SeqNum),
Item = item_clause(ItemClause),
MaybeItem = ok1(Item)
;
MaybeFunctor = error2(Specs),
MaybeItem = error1(Specs)
)
)
;
MaybeBodyGoal = error1(Specs),
MaybeItem = error1(Specs)
).
%-----------------------------------------------------------------------------%
parse_decl(ModuleName, VarSet, Term, SeqNum, MaybeItem) :-
parse_attrs_and_decl(ModuleName, VarSet, Term, [], SeqNum, MaybeItem).
% parse_attrs_and_decl(ModuleName, VarSet, Term, Attributes, SeqNum,
% MaybeItem):
%
% Succeeds if Term is a declaration and binds Result to a representation
% of that declaration. Attributes is a list of enclosing declaration
% attributes, in the order innermost to outermost.
%
:- pred parse_attrs_and_decl(module_name::in, varset::in, term::in,
decl_attrs::in, int::in, maybe1(item)::out) is det.
parse_attrs_and_decl(ModuleName, VarSet, Term, !.Attributes, SeqNum,
MaybeItem) :-
( Term = term.functor(term.atom(Functor), Args, Context) ->
(
parse_decl_attribute(Functor, Args, Attribute, SubTerm)
->
!:Attributes = [Attribute - Context | !.Attributes],
parse_attrs_and_decl(ModuleName, VarSet, SubTerm, !.Attributes,
SeqNum, MaybeItem)
;
parse_attributed_decl(ModuleName, VarSet, Functor, Args,
!.Attributes, Context, SeqNum, MaybeItemPrime)
->
MaybeItemPrime = MaybeItem
;
TermStr = mercury_term_to_string(VarSet, no, Term),
Pieces = [words("Error: unrecognized declaration:"), nl,
words(TermStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(Context, [always(Pieces)])]),
MaybeItem = error1([Spec])
)
;
Context = get_term_context(Term),
Pieces = [words("Error: atom expected after `:-'."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(Context, [always(Pieces)])]),
MaybeItem = error1([Spec])
).
% parse_attributed_decl(ModuleName, VarSet, Functor, Args, Attributes,
% Context, SeqNum, MaybeItem):
%
% If Atom(Args) is a declaration, succeed and bind MaybeItem to a
% representation of that declaration. Attributes is a list of
% enclosing declaration attributes, in the order outermost to innermost.
%
:- pred parse_attributed_decl(module_name::in, varset::in, string::in,
list(term)::in, decl_attrs::in, prog_context::in, int::in,
maybe1(item)::out) is semidet.
parse_attributed_decl(ModuleName, VarSet, Functor, ArgTerms, Attributes,
Context, SeqNum, MaybeItem) :-
(
Functor = "type",
ArgTerms = [TypeDefnTerm],
parse_type_defn(ModuleName, VarSet, TypeDefnTerm, Attributes, Context,
SeqNum, MaybeItem)
;
Functor = "inst",
ArgTerms = [InstDeclTerm],
parse_inst_defn(ModuleName, VarSet, InstDeclTerm, Context,
SeqNum, MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "mode",
ArgTerms = [SubTerm],
( SubTerm = term.functor(term.atom("=="), [HeadTerm, BodyTerm], _) ->
% This is the definition of a mode.
parse_condition_suffix(BodyTerm, BeforeCondTerm, Condition),
parse_mode_defn(ModuleName, VarSet, HeadTerm, BeforeCondTerm,
Condition, Context, SeqNum, MaybeItem)
;
% This is the declaration of one mode of a predicate or function.
parse_mode_decl(ModuleName, VarSet, SubTerm, Attributes,
Context, SeqNum, MaybeItem)
)
;
(
Functor = "pred",
PredOrFunc = pf_predicate
;
Functor = "func",
PredOrFunc = pf_function
),
ArgTerms = [DeclTerm],
parse_pred_or_func_decl(PredOrFunc, ModuleName, VarSet, DeclTerm,
Attributes, Context, SeqNum, MaybeItem)
;
(
Functor = "import_module",
Maker = make_import
;
Functor = "use_module",
Maker = make_use
;
Functor = "export_module",
Maker = make_export
),
ArgTerms = [ModuleSpecTerm],
parse_symlist_decl(parse_module_specifier(VarSet), Maker,
ModuleSpecTerm, Attributes, Context, SeqNum, MaybeItem)
;
(
Functor = "interface",
ModuleDefn = md_interface
;
Functor = "implementation",
ModuleDefn = md_implementation
),
ArgTerms = [],
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, SeqNum),
Item = item_module_defn(ItemModuleDefn),
MaybeItem0 = ok1(Item),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "external",
(
ArgTerms = [PredSpecTerm],
MaybeBackEnd = no
;
ArgTerms = [BackEndArgTerm, PredSpecTerm],
BackEndArgTerm = term.functor(term.atom(BackEndFunctor), [], _),
(
BackEndFunctor = "high_level_backend",
BackEnd = high_level_backend
;
BackEndFunctor = "low_level_backend",
BackEnd = low_level_backend
),
MaybeBackEnd = yes(BackEnd)
),
parse_implicitly_qualified_symbol_name_specifier(ModuleName, VarSet,
PredSpecTerm, MaybeSymSpec),
process_maybe1(make_external(MaybeBackEnd, Context, SeqNum),
MaybeSymSpec, MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "module",
ArgTerms = [ModuleNameTerm],
parse_module_name(ModuleName, VarSet, ModuleNameTerm,
MaybeModuleNameSym),
(
MaybeModuleNameSym = ok1(ModuleNameSym),
ItemModuleStart =
item_module_start_info(ModuleNameSym, Context, SeqNum),
Item = item_module_start(ItemModuleStart),
MaybeItem0 = ok1(Item)
;
MaybeModuleNameSym = error1(Specs),
MaybeItem0 = error1(Specs)
),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "end_module",
ArgTerms = [ModuleNameTerm],
% The name in an `end_module' declaration not inside the scope of the
% module being ended, so the default module name here (ModuleName)
% is the parent of the previous default module name.
sym_name_get_module_name_default(ModuleName, root_module_name,
ParentOfModuleName),
parse_module_name(ParentOfModuleName, VarSet, ModuleNameTerm,
MaybeModuleNameSym),
(
MaybeModuleNameSym = ok1(ModuleNameSym),
ItemModuleEnd =
item_module_end_info(ModuleNameSym, Context, SeqNum),
Item = item_module_end(ItemModuleEnd),
MaybeItem0 = ok1(Item)
;
MaybeModuleNameSym = error1(Specs),
MaybeItem0 = error1(Specs)
),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "include_module",
ArgTerms = [ModuleNamesTerm],
parse_list(parse_module_name(ModuleName, VarSet), ModuleNamesTerm,
MaybeModuleNameSyms),
(
MaybeModuleNameSyms = ok1(ModuleNameSyms),
ModuleDefn = md_include_module(ModuleNameSyms),
ItemModuleDefn =
item_module_defn_info(ModuleDefn, Context, SeqNum),
Item = item_module_defn(ItemModuleDefn),
MaybeItem0 = ok1(Item)
;
MaybeModuleNameSyms = error1(Specs),
MaybeItem0 = error1(Specs)
),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "pragma",
parse_pragma(ModuleName, VarSet, ArgTerms, Context, SeqNum,
MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
(
Functor = "promise",
PromiseType = promise_type_true
;
Functor = "promise_exclusive",
PromiseType = promise_type_exclusive
;
Functor = "promise_exhaustive",
PromiseType = promise_type_exhaustive
;
Functor = "promise_exclusive_exhaustive",
PromiseType = promise_type_exclusive_exhaustive
),
parse_promise(ModuleName, PromiseType, VarSet, ArgTerms, Attributes,
Context, SeqNum, MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "typeclass",
parse_typeclass(ModuleName, VarSet, ArgTerms, Context, SeqNum,
MaybeItemTypeClass),
(
MaybeItemTypeClass = ok1(ItemTypeClass),
MaybeItem0 = ok1(item_typeclass(ItemTypeClass))
;
MaybeItemTypeClass = error1(Specs),
MaybeItem0 = error1(Specs)
),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "instance",
parse_instance(ModuleName, VarSet, ArgTerms, Context, SeqNum,
MaybeItemInstance),
(
MaybeItemInstance = ok1(ItemInstance),
MaybeItem0 = ok1(item_instance(ItemInstance))
;
MaybeItemInstance = error1(Specs),
MaybeItem0 = error1(Specs)
),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
( Functor = "initialise"
; Functor = "initialize"
),
ArgTerms = [SubTerm],
parse_initialise_decl(ModuleName, VarSet, SubTerm, Context,
SeqNum, MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
( Functor = "finalise"
; Functor = "finalize"
),
ArgTerms = [SubTerm],
parse_finalise_decl(ModuleName, VarSet, SubTerm, Context, SeqNum,
MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "mutable",
parse_mutable_decl(ModuleName, VarSet, ArgTerms, Context, SeqNum,
MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
Functor = "version_numbers",
process_version_numbers(ModuleName, VarSet, ArgTerms, Attributes,
Context, SeqNum, MaybeItem)
).
:- pred parse_symlist_decl(parser(module_specifier)::parser,
maker(list(module_specifier), module_defn)::maker, term::in,
decl_attrs::in, prog_context::in, int::in, maybe1(item)::out) is det.
parse_symlist_decl(ParserPred, MakeModuleDefnPred, Term, Attributes, Context,
SeqNum, MaybeItem) :-
parse_list(ParserPred, Term, MaybeModuleSpecs),
process_maybe1(make_module_defn(MakeModuleDefnPred, Context, SeqNum),
MaybeModuleSpecs, MaybeItem0),
check_no_attributes(MaybeItem0, Attributes, MaybeItem).
:- pred process_version_numbers(module_name::in, varset::in, list(term)::in,
decl_attrs::in, prog_context::in, int::in, maybe1(item)::out) is semidet.
process_version_numbers(ModuleName, _VarSet, ArgTerms, Attributes, Context,
SeqNum, MaybeItem) :-
ArgTerms = [VersionNumberTerm, ModuleNameTerm, VersionNumbersTerm],
(
VersionNumberTerm = term.functor(term.integer(VersionNumber), [], _),
VersionNumber = version_numbers_version_number
->
( try_parse_symbol_name(ModuleNameTerm, ModuleName) ->
recompilation.version.parse_version_numbers(VersionNumbersTerm,
MaybeItem0),
(
MaybeItem0 = ok1(VersionNumbers),
ModuleDefn = md_version_numbers(ModuleName, VersionNumbers),
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context,
SeqNum),
Item = item_module_defn(ItemModuleDefn),
MaybeItem1 = ok1(Item),
check_no_attributes(MaybeItem1, Attributes, MaybeItem)
;
MaybeItem0 = error1(Specs),
MaybeItem = error1(Specs)
)
;
Pieces = [words("Error: invalid module name in"),
quote(":- version_numbers"), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(ModuleNameTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
)
;
(
VersionNumberTerm = term.functor(_, _, _VersionNumberContext),
Msg = "interface file needs to be recreated, " ++
"the version numbers are out of date",
dummy_term_with_context(Context, DummyTerm),
Warning = item_warning(yes(warn_smart_recompilation),
Msg, DummyTerm),
ItemNothing = item_nothing_info(yes(Warning), Context, SeqNum),
Item = item_nothing(ItemNothing),
MaybeItem = ok1(Item)
;
VersionNumberTerm = term.variable(_, VersionNumberContext),
Pieces = [words("Error: invalid version number in"),
quote(":- version_numbers"), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(VersionNumberContext, [always(Pieces)])]),
MaybeItem = error1([Spec])
)
).
%-----------------------------------------------------------------------------%
%
% Parsing ":- pred" and ":- func" declarations.
%
% parse_pred_or_func_decl parses a predicate or function declaration.
%
:- pred parse_pred_or_func_decl(pred_or_func::in, module_name::in, varset::in,
term::in, decl_attrs::in, prog_context::in, int::in, maybe1(item)::out)
is det.
parse_pred_or_func_decl(PredOrFunc, ModuleName, VarSet, Term, Attributes,
Context, SeqNum, MaybeItem) :-
parse_condition_suffix(Term, BeforeCondTerm, Condition),
parse_determinism_suffix(VarSet, BeforeCondTerm, BeforeDetismTerm,
MaybeMaybeDetism),
parse_with_inst_suffix(BeforeDetismTerm, BeforeWithInstTerm,
MaybeWithInst),
parse_with_type_suffix(VarSet, BeforeWithInstTerm, BeforeWithTypeTerm,
MaybeWithType),
BaseTerm = BeforeWithTypeTerm,
(
MaybeMaybeDetism = ok1(MaybeDetism),
MaybeWithInst = ok1(WithInst),
MaybeWithType = ok1(WithType)
->
(
MaybeDetism = yes(_),
WithInst = yes(_)
->
Pieces = [words("Error:"), quote("with_inst"),
words("and determinism both specified."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(BaseTerm), [always(Pieces)])]),
MaybeItem = error1([Spec])
;
WithInst = yes(_),
WithType = no
->
Pieces = [words("Error:"), quote("with_inst"), words("specified"),
words("without"), quote("with_type"), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(BaseTerm), [always(Pieces)])]),
MaybeItem = error1([Spec])
;
(
% Function declarations with `with_type` annotations
% have the same form as predicate declarations.
PredOrFunc = pf_function,
WithType = no
->
parse_func_decl_base(ModuleName, VarSet, BaseTerm, Condition,
MaybeDetism, Attributes, Context, SeqNum, MaybeItem)
;
parse_pred_decl_base(PredOrFunc, ModuleName, VarSet, BaseTerm,
Condition, WithType, WithInst, MaybeDetism,
Attributes, Context, SeqNum, MaybeItem)
)
)
;
Specs = get_any_errors1(MaybeMaybeDetism)
++ get_any_errors1(MaybeWithInst)
++ get_any_errors1(MaybeWithType),
MaybeItem = error1(Specs)
).
% parse a `:- pred p(...)' declaration or a
% `:- func f(...) `with_type` t' declaration
%
:- pred parse_pred_decl_base(pred_or_func::in, module_name::in, varset::in,
term::in, condition::in, maybe(mer_type)::in, maybe(mer_inst)::in,
maybe(determinism)::in, decl_attrs::in, prog_context::in, int::in,
maybe1(item)::out) is det.
parse_pred_decl_base(PredOrFunc, ModuleName, VarSet, PredTypeTerm, Condition,
WithType, WithInst, MaybeDet, Attributes0, Context, SeqNum,
MaybeItem) :-
get_class_context_and_inst_constraints(ModuleName, VarSet,
Attributes0, Attributes1, MaybeExistClassInstContext),
(
MaybeExistClassInstContext = error3(Specs),
MaybeItem = error1(Specs)
;
MaybeExistClassInstContext =
ok3(ExistQVars, Constraints, InstConstraints),
ContextPieces = [words("In")] ++ pred_or_func_decl_pieces(PredOrFunc)
++ [suffix(":")],
parse_implicitly_qualified_sym_name_and_args(ModuleName, PredTypeTerm,
VarSet, ContextPieces, MaybePredNameAndArgs),
(
MaybePredNameAndArgs = error2(Specs),
MaybeItem = error1(Specs)
;
MaybePredNameAndArgs = ok2(Functor, ArgTerms),
( parse_type_and_mode_list(InstConstraints, ArgTerms, Args) ->
( verify_type_and_mode_list(Args) ->
(
WithInst = yes(_),
Args = [type_only(_) | _]
->
Pieces = [words("Error:"), quote("with_inst"),
words("specified without argument modes."), nl],
Spec = error_spec(severity_error,
phase_term_to_parse_tree,
[simple_msg(get_term_context(PredTypeTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
;
WithInst = no,
WithType = yes(_),
Args = [type_and_mode(_, _) | _]
->
Pieces = [words("Error: arguments have modes but"),
quote("with_inst"), words("not specified."), nl],
Spec = error_spec(severity_error,
phase_term_to_parse_tree,
[simple_msg(get_term_context(PredTypeTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
;
inst_var_constraints_types_modes_self_consistent(Args)
->
get_purity(Purity, Attributes1, Attributes),
varset.coerce(VarSet, TVarSet),
varset.coerce(VarSet, IVarSet),
Origin = user,
ItemPredDecl = item_pred_decl_info(Origin,
TVarSet, IVarSet, ExistQVars, PredOrFunc,
Functor, Args, WithType, WithInst, MaybeDet,
Condition, Purity, Constraints, Context, SeqNum),
Item = item_pred_decl(ItemPredDecl),
MaybeItem0 = ok1(Item),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
PredTypeTermStr =
describe_error_term(VarSet, PredTypeTerm),
Pieces = [words("Error: inconsistent constraints on"),
words("inst variables in")] ++
pred_or_func_decl_pieces(PredOrFunc) ++
[suffix(":"), nl,
words(PredTypeTermStr), suffix("."), nl],
Spec = error_spec(severity_error,
phase_term_to_parse_tree,
[simple_msg(get_term_context(PredTypeTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
)
;
Pieces = [words("Error: some but not all arguments"),
words("have modes."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(PredTypeTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
)
;
PredTypeTermStr = describe_error_term(VarSet, PredTypeTerm),
Pieces = [words("Error: syntax error in")] ++
pred_or_func_decl_pieces(PredOrFunc) ++
[words("at"), words(PredTypeTermStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(PredTypeTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
)
)
).
% Parse a `:- func p(...)' declaration *without* a with_type clause.
%
:- pred parse_func_decl_base(module_name::in, varset::in, term::in,
condition::in, maybe(determinism)::in, decl_attrs::in, prog_context::in,
int::in, maybe1(item)::out) is det.
parse_func_decl_base(ModuleName, VarSet, Term, Condition, MaybeDet,
Attributes0, Context, SeqNum, MaybeItem) :-
get_class_context_and_inst_constraints(ModuleName, VarSet,
Attributes0, Attributes, MaybeContext),
(
MaybeContext = error3(Specs),
MaybeItem = error1(Specs)
;
MaybeContext = ok3(ExistQVars, Constraints, InstConstraints),
(
Term = term.functor(term.atom("="),
[MaybeSugaredFuncTerm, ReturnTerm], _)
->
FuncTerm = desugar_field_access(MaybeSugaredFuncTerm),
ContextPieces = [words("In"), quote(":- func"),
words("declaration")],
parse_implicitly_qualified_sym_name_and_args(ModuleName, FuncTerm,
VarSet, ContextPieces, MaybeFuncNameAndArgs),
(
MaybeFuncNameAndArgs = error2(Specs),
MaybeItem = error1(Specs)
;
MaybeFuncNameAndArgs = ok2(FuncName, ArgTerms),
(
parse_type_and_mode_list(InstConstraints, ArgTerms,
ArgsPrime)
->
MaybeArgs = ok1(ArgsPrime)
;
FuncTermStr = describe_error_term(VarSet, FuncTerm),
ArgsPieces = [words("Error: syntax error in arguments of"),
quote(":- func"), words("declaration at"),
words(FuncTermStr), suffix("."), nl],
ArgsSpec = error_spec(severity_error,
phase_term_to_parse_tree,
[simple_msg(get_term_context(FuncTerm),
[always(ArgsPieces)])]),
MaybeArgs = error1([ArgsSpec])
),
(
parse_type_and_mode(InstConstraints, ReturnTerm,
ReturnArgPrime)
->
MaybeReturnArg = ok1(ReturnArgPrime)
;
ReturnPieces = [words("Error: syntax error"),
words("in return type of"), quote(":- func"),
words("declaration."), nl],
ReturnSpec = error_spec(severity_error,
phase_term_to_parse_tree,
[simple_msg(get_term_context(ReturnTerm),
[always(ReturnPieces)])]),
MaybeReturnArg = error1([ReturnSpec])
),
(
MaybeArgs = ok1(Args),
MaybeReturnArg = ok1(ReturnArg)
->
% We use an auxiliary predicate because the code is just
% too deeply indented here.
parse_func_decl_base_2(FuncName, Args, ReturnArg,
FuncTerm, Term, VarSet, MaybeDet, Condition,
ExistQVars, Constraints, Attributes,
Context, SeqNum, MaybeItem)
;
Specs = get_any_errors1(MaybeArgs) ++
get_any_errors1(MaybeReturnArg),
MaybeItem = error1(Specs)
)
)
;
Pieces = [words("Error:"), quote("="), words("expected in"),
quote(":- func"), words("declaration."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(Term), [always(Pieces)])]),
MaybeItem = error1([Spec])
)
).
:- pred parse_func_decl_base_2(sym_name::in, list(type_and_mode)::in,
type_and_mode::in, term::in, term::in, varset::in, maybe(determinism)::in,
condition::in, existq_tvars::in, prog_constraints::in, decl_attrs::in,
prog_context::in, int::in, maybe1(item)::out) is det.
parse_func_decl_base_2(FuncName, Args, ReturnArg, FuncTerm, Term,
VarSet, MaybeDet, Condition, ExistQVars, Constraints, Attributes0,
Context, SeqNum, MaybeItem) :-
(
verify_type_and_mode_list(Args)
->
ConsistentArgsSpecs = []
;
ConsistentPieces =
[words("Error: some but not all arguments have modes."), nl],
ConsistentSpec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(FuncTerm),
[always(ConsistentPieces)])]),
ConsistentArgsSpecs = [ConsistentSpec]
),
(
Args = [type_and_mode(_, _) | _],
ReturnArg = type_only(_)
->
ArgsOnlyPieces = [words("Error: function arguments have modes,"),
words("but function result does not."), nl],
ArgsOnlySpec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(FuncTerm),
[always(ArgsOnlyPieces)])]),
ArgsOnlySpecs = [ArgsOnlySpec]
;
ArgsOnlySpecs = []
),
(
Args = [type_only(_) | _],
ReturnArg = type_and_mode(_, _)
->
ReturnOnlyPieces = [words("Error: function result has mode,"),
words("but function arguments do not."), nl],
ReturnOnlySpec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(FuncTerm),
[always(ReturnOnlyPieces)])]),
ReturnOnlySpecs = [ReturnOnlySpec]
;
ReturnOnlySpecs = []
),
ConsistencySpecs = ConsistentArgsSpecs ++ ArgsOnlySpecs ++ ReturnOnlySpecs,
(
ConsistencySpecs = [_ | _],
MaybeItem = error1(ConsistencySpecs)
;
ConsistencySpecs = [],
get_purity(Purity, Attributes0, Attributes),
varset.coerce(VarSet, TVarSet),
varset.coerce(VarSet, IVarSet),
AllArgs = Args ++ [ReturnArg],
( inst_var_constraints_types_modes_self_consistent(AllArgs) ->
Origin = user,
ItemPredDecl = item_pred_decl_info(Origin, TVarSet, IVarSet,
ExistQVars, pf_function, FuncName, AllArgs, no, no,
MaybeDet, Condition, Purity, Constraints, Context, SeqNum),
Item = item_pred_decl(ItemPredDecl),
MaybeItem0 = ok1(Item),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
TermStr = describe_error_term(VarSet, Term),
Pieces = [words("Error: inconsistent constraints"),
words("on inst variables in function declaration:"), nl,
words(TermStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(Term), [always(Pieces)])]),
MaybeItem = error1([Spec])
)
).
:- pred parse_type_and_mode_list(inst_var_sub::in, list(term)::in,
list(type_and_mode)::out) is semidet.
parse_type_and_mode_list(_, [], []).
parse_type_and_mode_list(InstConstraints, [H0 | T0], [H | T]) :-
parse_type_and_mode(InstConstraints, H0, H),
parse_type_and_mode_list(InstConstraints, T0, T).
:- pred parse_type_and_mode(inst_var_sub::in, term::in, type_and_mode::out)
is semidet.
parse_type_and_mode(InstConstraints, Term, MaybeTypeAndMode) :-
( Term = term.functor(term.atom("::"), [TypeTerm, ModeTerm], _Context) ->
maybe_parse_type(TypeTerm, Type),
convert_mode(allow_constrained_inst_var, ModeTerm, Mode0),
constrain_inst_vars_in_mode_sub(InstConstraints, Mode0, Mode),
MaybeTypeAndMode = type_and_mode(Type, Mode)
;
maybe_parse_type(Term, Type),
MaybeTypeAndMode = type_only(Type)
).
% Verify that among the arguments of a :- pred or :- func declaration,
% either all arguments specify a mode or none of them do.
%
:- pred verify_type_and_mode_list(list(type_and_mode)::in) is semidet.
verify_type_and_mode_list([]).
verify_type_and_mode_list([First | Rest]) :-
verify_type_and_mode_list_2(Rest, First).
:- pred verify_type_and_mode_list_2(list(type_and_mode)::in, type_and_mode::in)
is semidet.
verify_type_and_mode_list_2([], _).
verify_type_and_mode_list_2([Head | Tail], First) :-
(
Head = type_only(_),
First = type_only(_)
;
Head = type_and_mode(_, _),
First = type_and_mode(_, _)
),
verify_type_and_mode_list_2(Tail, First).
%-----------------------------------------------------------------------------%
%
% Parsing mode declarations for predicates and functions.
%
:- pred parse_mode_decl(module_name::in, varset::in, term::in,
decl_attrs::in, prog_context::in, int::in, maybe1(item)::out) is det.
parse_mode_decl(ModuleName, VarSet, Term, Attributes, Context, SeqNum,
MaybeItem) :-
parse_condition_suffix(Term, BeforeCondTerm, Condition),
parse_determinism_suffix(VarSet, BeforeCondTerm, BeforeDetismTerm,
MaybeMaybeDetism),
parse_with_inst_suffix(BeforeDetismTerm, BeforeWithInstTerm,
MaybeWithInst),
BaseTerm = BeforeWithInstTerm,
(
MaybeMaybeDetism = ok1(MaybeDetism),
MaybeWithInst = ok1(WithInst)
->
(
MaybeDetism = yes(_),
WithInst = yes(_)
->
Pieces = [words("Error:"), quote("with_inst"),
words("and determinism both specified."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(BeforeCondTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
;
parse_mode_decl_base(ModuleName, VarSet, BaseTerm, Condition,
Attributes, WithInst, MaybeDetism, Context, SeqNum, MaybeItem)
)
;
Specs = get_any_errors1(MaybeMaybeDetism)
++ get_any_errors1(MaybeWithInst),
MaybeItem = error1(Specs)
).
:- pred parse_mode_decl_base(module_name::in, varset::in, term::in,
condition::in, decl_attrs::in, maybe(mer_inst)::in, maybe(determinism)::in,
prog_context::in, int::in, maybe1(item)::out) is det.
parse_mode_decl_base(ModuleName, VarSet, Term, Condition, Attributes, WithInst,
MaybeDet, Context, SeqNum, MaybeItem) :-
(
WithInst = no,
Term = term.functor(term.atom("="),
[MaybeSugaredFuncTerm, ReturnTypeTerm], _)
->
FuncTerm = desugar_field_access(MaybeSugaredFuncTerm),
ContextPieces = [words("In function"), quote(":- mode"),
words("declaration")],
parse_implicitly_qualified_sym_name_and_args(ModuleName, FuncTerm,
VarSet, ContextPieces, MaybeFunctorArgs),
(
MaybeFunctorArgs = error2(Specs),
MaybeItem = error1(Specs)
;
MaybeFunctorArgs = ok2(Functor, ArgTerms),
parse_func_mode_decl(Functor, ArgTerms, ModuleName,
FuncTerm, ReturnTypeTerm, Term, VarSet, MaybeDet, Condition,
Attributes, Context, SeqNum, MaybeItem)
)
;
ContextPieces = [words("In"), quote(":- mode"), words("declaration")],
parse_implicitly_qualified_sym_name_and_args(ModuleName, Term,
VarSet, ContextPieces, MaybeFunctorArgs),
(
MaybeFunctorArgs = error2(Specs),
MaybeItem = error1(Specs)
;
MaybeFunctorArgs = ok2(Functor, ArgTerms),
parse_pred_mode_decl(Functor, ArgTerms, ModuleName, Term,
VarSet, WithInst, MaybeDet, Condition,
Attributes, Context, SeqNum, MaybeItem)
)
).
:- pred parse_pred_mode_decl(sym_name::in, list(term)::in, module_name::in,
term::in, varset::in, maybe(mer_inst)::in, maybe(determinism)::in,
condition::in, decl_attrs::in, prog_context::in, int::in,
maybe1(item)::out) is det.
parse_pred_mode_decl(Functor, ArgTerms, ModuleName, PredModeTerm, VarSet,
WithInst, MaybeDet, Condition, Attributes0, Context, SeqNum,
MaybeItem) :-
( convert_mode_list(allow_constrained_inst_var, ArgTerms, ArgModes0) ->
get_class_context_and_inst_constraints(ModuleName, VarSet,
Attributes0, Attributes, MaybeConstraints),
(
MaybeConstraints = ok3(_, _, InstConstraints),
list.map(constrain_inst_vars_in_mode_sub(InstConstraints),
ArgModes0, ArgModes),
varset.coerce(VarSet, ProgVarSet),
( inst_var_constraints_are_self_consistent_in_modes(ArgModes) ->
(
WithInst = no,
PredOrFunc = yes(pf_predicate)
;
WithInst = yes(_),
% We don't know whether it's a predicate or a function
% until we expand out the inst.
PredOrFunc = no
),
ItemModeDecl = item_mode_decl_info(ProgVarSet, PredOrFunc,
Functor, ArgModes, WithInst, MaybeDet, Condition, Context,
SeqNum),
Item = item_mode_decl(ItemModeDecl),
MaybeItem0 = ok1(Item),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
PredModeTermStr = describe_error_term(VarSet, PredModeTerm),
Pieces = [words("Error: inconsistent constraints"),
words("on inst variables"),
words("in predicate mode declaration:"), nl,
words(PredModeTermStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(PredModeTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
)
;
MaybeConstraints = error3(Specs),
MaybeItem = error1(Specs)
)
;
PredModeTermStr = describe_error_term(VarSet, PredModeTerm),
Pieces = [words("Error: syntax error in mode declaration at"),
words(PredModeTermStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(PredModeTerm), [always(Pieces)])]),
MaybeItem = error1([Spec])
).
:- pred parse_func_mode_decl(sym_name::in, list(term)::in, module_name::in,
term::in, term::in, term::in, varset::in, maybe(determinism)::in,
condition::in, decl_attrs::in, prog_context::in, int::in,
maybe1(item)::out) is det.
parse_func_mode_decl(Functor, ArgTerms, ModuleName, FuncMode, RetModeTerm,
FullTerm, VarSet, MaybeDet, Condition, Attributes0, Context, SeqNum,
MaybeItem) :-
( convert_mode_list(allow_constrained_inst_var, ArgTerms, ArgModes0) ->
get_class_context_and_inst_constraints(ModuleName, VarSet,
Attributes0, Attributes, MaybeConstraints),
(
MaybeConstraints = ok3(_, _, InstConstraints),
list.map(constrain_inst_vars_in_mode_sub(InstConstraints),
ArgModes0, ArgModes),
(
convert_mode(allow_constrained_inst_var, RetModeTerm, RetMode0)
->
constrain_inst_vars_in_mode_sub(InstConstraints,
RetMode0, RetMode),
varset.coerce(VarSet, InstVarSet),
ArgReturnModes = ArgModes ++ [RetMode],
(
inst_var_constraints_are_self_consistent_in_modes(
ArgReturnModes)
->
ItemModeDecl = item_mode_decl_info(InstVarSet,
yes(pf_function), Functor, ArgReturnModes, no,
MaybeDet, Condition, Context, SeqNum),
Item = item_mode_decl(ItemModeDecl),
MaybeItem0 = ok1(Item),
check_no_attributes(MaybeItem0, Attributes, MaybeItem)
;
FullTermStr = describe_error_term(VarSet, FullTerm),
Pieces = [words("Error: inconsistent constraints"),
words("on inst variables"),
words("in function mode declaration:"), nl,
words(FullTermStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(FullTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
)
;
Pieces = [words("Error: syntax error in return mode"),
words("of function mode declaration."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(RetModeTerm),
[always(Pieces)])]),
MaybeItem = error1([Spec])
)
;
MaybeConstraints = error3(Specs),
MaybeItem = error1(Specs)
)
;
% XXX Should say which argument.
FuncModeStr = describe_error_term(VarSet, FuncMode),
Pieces = [words("Error: syntax error in arguments of"),
words("function mode declaration at"),
words(FuncModeStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(FuncMode), [always(Pieces)])]),
MaybeItem = error1([Spec])
).
%-----------------------------------------------------------------------------%
% We could perhaps get rid of some code duplication between here and
% prog_io_typeclass.m?
% XXX This documentation is out of date.
% get_class_context_and_inst_constraints(ModuleName, Attributes0,
% Attributes, MaybeContext, MaybeInstConstraints):
%
% Parse type quantifiers, type class constraints and inst constraints
% from the declaration attributes in Attributes0.
% MaybeContext is either bound to the correctly parsed context, or
% an appropriate error message (if there was a syntax error).
% MaybeInstConstraints is either bound to a map containing the inst
% constraints or an appropriate error message (if there was a syntax
% error).
% Attributes is bound to the remaining attributes.
%
:- pred get_class_context_and_inst_constraints(module_name::in, varset::in,
decl_attrs::in, decl_attrs::out,
maybe3(existq_tvars, prog_constraints, inst_var_sub)::out) is det.
get_class_context_and_inst_constraints(ModuleName, VarSet, RevAttributes0,
RevAttributes, MaybeExistClassInstContext) :-
% Constraints and quantifiers should occur in the following order
% (outermost to innermost):
%
% operator precedence
% -------- ----------
% 1. universal quantifiers all 950
% 2. existential quantifiers some 950
% 3. universal constraints <= 920
% 4. existential constraints => 920 [*]
% 5. the decl itself pred or func 800
%
% When we reach here, Attributes0 contains declaration attributes
% in the opposite order -- innermost to outermost -- so we reverse
% them before we start.
%
% [*] Note that the semantic meaning of `=>' is not quite the same
% as implication; logically speaking it's more like conjunction.
% Oh well, at least it has the right precedence.
%
% In theory it could make sense to allow the order of 2 & 3 to be
% swapped, or (in the case of multiple constraints & multiple
% quantifiers) to allow arbitrary interleaving of 2 & 3, but in
% practice it seems there would be little benefit in allowing that
% flexibility, so we don't.
%
% Universal quantification is the default, so we just ignore
% universal quantifiers. (XXX It might be a good idea to check
% that any universally quantified type variables do actually
% occur somewhere in the type declaration, and are not also
% existentially quantified, and if not, issue a warning or
% error message.)
list.reverse(RevAttributes0, Attributes0),
get_quant_vars(quant_type_univ, ModuleName, Attributes0, Attributes1,
[], _UnivQVars),
get_quant_vars(quant_type_exist, ModuleName, Attributes1, Attributes2,
[], ExistQVars0),
list.map(term.coerce_var, ExistQVars0, ExistQVars),
get_constraints(quant_type_univ, ModuleName, VarSet, Attributes2,
Attributes3, MaybeUnivConstraints),
get_constraints(quant_type_exist, ModuleName, VarSet, Attributes3,
Attributes, MaybeExistConstraints),
list.reverse(Attributes, RevAttributes),
(
MaybeUnivConstraints = ok2(UnivConstraints, UnivInstConstraints),
MaybeExistConstraints = ok2(ExistConstraints, ExistInstConstraints)
->
ClassConstraints = constraints(UnivConstraints, ExistConstraints),
InstConstraints =
map.old_merge(UnivInstConstraints, ExistInstConstraints),
MaybeExistClassInstContext = ok3(ExistQVars, ClassConstraints,
InstConstraints)
;
Specs = get_any_errors2(MaybeUnivConstraints) ++
get_any_errors2(MaybeExistConstraints),
MaybeExistClassInstContext = error3(Specs)
).
:- pred get_constraints(quantifier_type::in, module_name::in, varset::in,
decl_attrs::in, decl_attrs::out, maybe_class_and_inst_constraints::out)
is det.
get_constraints(QuantType, ModuleName, VarSet, !Attributes,
MaybeClassInstConstraints) :-
(
!.Attributes = [
decl_attr_constraints(QuantType, ConstraintsTerm) - _Term
| !:Attributes]
->
parse_class_and_inst_constraints(ModuleName, VarSet, ConstraintsTerm,
MaybeHeadConstraints),
% There may be more constraints of the same type;
% collect them all and combine them.
get_constraints(QuantType, ModuleName, VarSet, !Attributes,
MaybeTailConstraints),
(
MaybeHeadConstraints =
ok2(HeadClassConstraints, HeadInstConstraint),
MaybeTailConstraints =
ok2(TailClassConstraints, TailInstConstraint)
->
ClassConstraints = HeadClassConstraints ++ TailClassConstraints,
InstConstraints =
map.old_merge(HeadInstConstraint, TailInstConstraint),
MaybeClassInstConstraints = ok2(ClassConstraints, InstConstraints)
;
Specs = get_any_errors2(MaybeHeadConstraints) ++
get_any_errors2(MaybeTailConstraints),
MaybeClassInstConstraints = error2(Specs)
)
;
MaybeClassInstConstraints = ok2([], map.init)
).
%-----------------------------------------------------------------------------%
:- pred parse_promise(module_name::in, promise_type::in, varset::in,
list(term)::in, decl_attrs::in, prog_context::in, int::in,
maybe1(item)::out) is semidet.
parse_promise(ModuleName, PromiseType, VarSet, [Term], Attributes, Context,
SeqNum, MaybeItem) :-
varset.coerce(VarSet, ProgVarSet0),
ContextPieces = [],
parse_goal(Term, ContextPieces, MaybeGoal0, ProgVarSet0, ProgVarSet),
(
MaybeGoal0 = ok1(Goal0),
% Get universally quantified variables.
(
PromiseType = promise_type_true,
( Goal0 = all_expr(UnivVars0, AllGoal) - _Context ->
UnivVars0 = UnivVars,
Goal = AllGoal
;
UnivVars = [],
Goal = Goal0
)
;
( PromiseType = promise_type_exclusive
; PromiseType = promise_type_exhaustive
; PromiseType = promise_type_exclusive_exhaustive
),
get_quant_vars(quant_type_univ, ModuleName, Attributes, _,
[], UnivVars0),
list.map(term.coerce_var, UnivVars0, UnivVars),
Goal0 = Goal
),
ItemPromise = item_promise_info(PromiseType, Goal, ProgVarSet,
UnivVars, Context, SeqNum),
Item = item_promise(ItemPromise),
MaybeItem = ok1(Item)
;
MaybeGoal0 = error1(Specs),
MaybeItem = error1(Specs)
).
%-----------------------------------------------------------------------------%
% parse_determinism_suffix(VarSet, BodyTerm, BeforeDetismTerm,
% MaybeMaybeDetism):
%
% Look for a suffix of the form "is <detism>" in Term. If we find one,
% bind MaybeMaybeDetism to ok1(yes()) wrapped around the determinism,
% and bind BeforeDetismTerm to the other part of Term. If we don't
% find, one, then bind MaybeMaybeDetism to ok1(no).
%
:- pred parse_determinism_suffix(varset::in, term::in, term::out,
maybe1(maybe(determinism))::out) is det.
parse_determinism_suffix(VarSet, Term, BeforeDetismTerm, MaybeMaybeDetism) :-
(
Term = term.functor(term.atom("is"), Args, _),
Args = [BeforeDetismTermPrime, DetismTerm]
->
BeforeDetismTerm = BeforeDetismTermPrime,
(
DetismTerm = term.functor(term.atom(DetismFunctor), [], _),
standard_det(DetismFunctor, Detism)
->
MaybeMaybeDetism = ok1(yes(Detism))
;
TermStr = describe_error_term(VarSet, Term),
Pieces = [words("Error: invalid determinism category"),
words(TermStr), suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(DetismTerm), [always(Pieces)])]),
MaybeMaybeDetism = error1([Spec])
)
;
BeforeDetismTerm = Term,
MaybeMaybeDetism = ok1(no)
).
% Process the `with_type type` suffix part of a declaration.
%
:- pred parse_with_type_suffix(varset::in, term::in, term::out,
maybe1(maybe(mer_type))::out) is det.
parse_with_type_suffix(VarSet, Term, BeforeWithTypeTerm, MaybeWithType) :-
(
Term = term.functor(TypeQualifier,
[BeforeWithTypeTermPrime, TypeTerm], _),
(
TypeQualifier = term.atom("with_type")
;
TypeQualifier = term.atom(":")
)
->
BeforeWithTypeTerm = BeforeWithTypeTermPrime,
% XXX Should supply more correct ContextPieces.
ContextPieces = [],
parse_type(TypeTerm, VarSet, ContextPieces, MaybeType),
(
MaybeType = ok1(Type),
MaybeWithType = ok1(yes(Type))
;
MaybeType = error1(Specs),
MaybeWithType = error1(Specs)
)
;
BeforeWithTypeTerm = Term,
MaybeWithType = ok1(no)
).
% Process the `with_inst inst` suffix part of a declaration.
%
:- pred parse_with_inst_suffix(term::in, term::out,
maybe1(maybe(mer_inst))::out) is det.
parse_with_inst_suffix(Term, BeforeWithInstTerm, MaybeWithInst) :-
(
Term = term.functor(term.atom("with_inst"),
[BeforeWithInstTermPrime, InstTerm], _)
->
BeforeWithInstTerm = BeforeWithInstTermPrime,
( convert_inst(allow_constrained_inst_var, InstTerm, Inst) ->
MaybeWithInst = ok1(yes(Inst))
;
Pieces = [words("Error: invalid inst in"), quote("with_inst"),
suffix("."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(get_term_context(InstTerm), [always(Pieces)])]),
MaybeWithInst = error1([Spec])
)
;
BeforeWithInstTerm = Term,
MaybeWithInst = ok1(no)
).
%-----------------------------------------------------------------------------%
:- pred get_quant_vars(quantifier_type::in, module_name::in,
decl_attrs::in, decl_attrs::out, list(var)::in, list(var)::out) is det.
get_quant_vars(QuantType, ModuleName, !Attributes, !Vars) :-
(
!.Attributes = [decl_attr_quantifier(QuantType, QuantVars) - _
| !:Attributes]
->
!:Vars = !.Vars ++ QuantVars,
get_quant_vars(QuantType, ModuleName, !Attributes, !Vars)
;
true
).
%-----------------------------------------------------------------------------%
% Perform one of the following field-access syntax rewrites if possible:
%
% A ^ f(B, ...) ---> f(B, ..., A)
% (A ^ f(B, ...) := X) ---> 'f :='(B, ..., A, X)
%
:- func desugar_field_access(term) = term.
desugar_field_access(Term) = DesugaredTerm :-
(
Term = functor(atom("^"), [A, RHS], Context),
RHS = functor(atom(FieldName), Bs, _)
->
DesugaredTerm = functor(atom(FieldName), Bs ++ [A], Context)
;
Term = functor(atom(":="), [LHS, X], _),
LHS = functor(atom("^"), [A, RHS], Context),
RHS = functor(atom(FieldName), Bs, _)
->
FunctionName = FieldName ++ " :=",
DesugaredTerm = functor(atom(FunctionName), Bs ++ [A, X], Context)
;
DesugaredTerm = Term
).
%-----------------------------------------------------------------------------%
% A ModuleSpecifier is just an sym_name.
%
:- pred parse_module_specifier(varset::in, term::in,
maybe1(module_specifier)::out) is det.
parse_module_specifier(VarSet, Term, MaybeModuleSpecifier) :-
parse_symbol_name(VarSet, Term, MaybeModuleSpecifier).
% A ModuleName is an implicitly-quantified sym_name.
%
% We check for module names starting with capital letters as a special
% case, so that we can report a better error message for that case.
%
:- pred parse_module_name(module_name::in, varset::in, term::in,
maybe1(module_name)::out) is det.
parse_module_name(DefaultModuleName, VarSet, Term, MaybeModule) :-
(
Term = term.variable(_, Context),
Pieces = [words("Error: module names starting with capital letters"),
words("must be quoted using single quotes"),
words("(e.g. "":- module 'Foo'."")."), nl],
Spec = error_spec(severity_error, phase_term_to_parse_tree,
[simple_msg(Context, [always(Pieces)])]),
MaybeModule = error1([Spec])
;
Term = term.functor(_, _, _),
parse_implicitly_qualified_symbol_name(DefaultModuleName, VarSet,
Term, MaybeModule)
).
%-----------------------------------------------------------------------------%
:- pred get_purity(purity::out, decl_attrs::in, decl_attrs::out) is det.
get_purity(Purity, !Attributes) :-
( !.Attributes = [decl_attr_purity(Purity0) - _ | !:Attributes] ->
Purity = Purity0
;
Purity = purity_pure
).
%-----------------------------------------------------------------------------%
:- func pred_or_func_decl_pieces(pred_or_func) = list(format_component).
pred_or_func_decl_pieces(pf_function) =
[quote(":- func"), words("declaration")].
pred_or_func_decl_pieces(pf_predicate) =
[quote(":- pred"), words("declaration")].
%-----------------------------------------------------------------------------%
:- type maker(T1, T2) == pred(T1, T2).
:- mode maker == (pred(in, out) is det).
:- pred process_maybe1(maker(T1, T2)::maker, maybe1(T1)::in, maybe1(T2)::out)
is det.
process_maybe1(Maker, ok1(X), ok1(Y)) :-
call(Maker, X, Y).
process_maybe1(_, error1(Specs), error1(Specs)).
:- pred process_maybe1_to_t(maker(T1, maybe1(T2))::maker,
maybe1(T1)::in, maybe1(T2)::out) is det.
process_maybe1_to_t(Maker, ok1(X), Y) :-
call(Maker, X, Y).
process_maybe1_to_t(_, error1(Specs), error1(Specs)).
%-----------------------------------------------------------------------------%
:- pred make_use(list(module_specifier)::in, module_defn::out) is det.
make_use(Syms, md_use(Syms)).
:- pred make_import(list(module_specifier)::in, module_defn::out) is det.
make_import(Syms, md_import(Syms)).
:- pred make_export(list(module_specifier)::in, module_defn::out) is det.
make_export(Syms, md_export(Syms)).
%-----------------------------------------------------------------------------%
:- pred make_module_defn(maker(list(module_specifier), module_defn)::maker,
prog_context::in, int::in, list(module_specifier)::in, item::out) is det.
make_module_defn(MakeModuleDefnPred, Context, SeqNum, ModuleSpecs, Item) :-
call(MakeModuleDefnPred, ModuleSpecs, ModuleDefn),
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, SeqNum),
Item = item_module_defn(ItemModuleDefn).
:- pred make_pseudo_import_module_decl(prog_context::in, int::in,
module_specifier::in, item::out) is det.
make_pseudo_import_module_decl(Context, SeqNum, ModuleSpecifier, Item) :-
ModuleDefn = md_import([ModuleSpecifier]),
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, SeqNum),
Item = item_module_defn(ItemModuleDefn).
:- pred make_pseudo_use_module_decl(prog_context::in, int::in,
module_specifier::in, item::out) is det.
make_pseudo_use_module_decl(Context, SeqNum, ModuleSpecifier, Item) :-
ModuleDefn = md_use([ModuleSpecifier]),
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, SeqNum),
Item = item_module_defn(ItemModuleDefn).
:- pred make_pseudo_include_module_decl(prog_context::in, int::in,
module_name::in, item::out) is det.
make_pseudo_include_module_decl(Context, SeqNum, ModuleSpecifier, Item) :-
ModuleDefn = md_include_module([ModuleSpecifier]),
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, SeqNum),
Item = item_module_defn(ItemModuleDefn).
:- pred make_external(maybe(backend)::in, prog_context::in, int::in,
sym_name_specifier::in, item::out) is det.
make_external(MaybeBackend, Context, SeqNum, SymSpec, Item) :-
ModuleDefn = md_external(MaybeBackend, SymSpec),
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, SeqNum),
Item = item_module_defn(ItemModuleDefn).
%-----------------------------------------------------------------------------%
%
% You can uncomment this section for debugging.
%
% :- interface.
%
% :- pred write_item_to_stream(io.output_stream::in, item::in, io::di, io::uo)
% is det.
%
% :- pred write_item_to_stdout(item::in, io::di, io::uo) is det.
%
% :- pred write_items_to_file(string::in, list(item)::in, io::di, io::uo)
% is det.
%
% :- implementation.
%
% :- import_module pretty_printer.
%
% write_item_to_stream(Stream, Item, !IO) :-
% write_doc(Stream, format(Item), !IO),
% io.nl(Stream, !IO).
%
% write_item_to_stdout(Item, !IO) :-
% write_item_to_stream(io.stdout_stream, Item, !IO).
%
% write_items_to_file(FileName, Items, !IO) :-
% io.open_output(FileName, Result, !IO),
% (
% Result = ok(Stream),
% list.foldl(write_item_to_stream(Stream), Items, !IO)
% ;
% Result = error(_)
% ).
%-----------------------------------------------------------------------------%
:- end_module parse_tree.prog_io.
%-----------------------------------------------------------------------------%