/
parse_error.m
274 lines (224 loc) · 10.9 KB
/
parse_error.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
%-----------------------------------------------------------------------------e
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------e
% Copyright (C) 2014 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
:- module parse_tree.parse_error.
:- interface.
:- import_module parse_tree.error_spec.
:- import_module io.
:- import_module list.
:- import_module set.
% 1. We obviously cannot process the module if we could not read it.
%
% 2. If the module contains some bad ":- module" declarations, then
% the identity of the module that contains any items following
% such declarations will be in doubt. The compiler can guess the
% identity of the module intended by the programmer only by chance,
% and any mistake will typically lead to an avalanche of misleading
% errors.
%
% 3. If the module contains some bad ":- end_module" declarations,
% then the same is true for any items following those declarations.
%
% Items follow most ":- module" declarations, but not most ":- end_module"
% declarations, and if no items follow a bad ":- end_module" declaration,
% then the badness of the declaration has no further consequences.
% It would therefore be worthwhile making a distinction between bad
% ":- end_module" declarations that have items following them, and those
% that don't. Unfortunately, since each item is processed independently,
% that is not trivial to do.
%
:- type fatal_read_module_error
---> frme_could_not_find_file
% We could not find the specified file.
; frme_could_not_open_file
% We could not open the specified file.
; frme_could_not_read_file
% We could open the specified file, but could not read its
% contents.
; frme_bad_submodule_start
% We encountered a declaration for the start of a submodule,
% but the name of the new submodule is not one that
% can be immediately nested inside the current module.
% NOTE This error can happen only for submodules of the main
% module in the file. For the main module itself, we would generate
% the rme_unexpected_module_name error.
; frme_bad_module_end.
% We encountered a declaration for the end of a module,
% but the name of the ended module is not the name of the
% until-then-current module.
% NOTE This error can happen both for the main module of the
% file and for its submodules.
% This type represents the kinds of nonfatal errors that can happen
% when we read in a Mercury module (which will be either a source file,
% or an interface file).
%
:- type nonfatal_read_module_error
---> rme_unexpected_module_name
% The file starts with a module declaration for a module
% other than the one we expected.
; rme_no_module_decl_at_start
% The file does not start with a module declaration at all.
; rme_no_section_decl_at_start
% The module does not start with either an interface or an
% implementation section marker.
; rme_end_module_not_at_end_of_src
% The source code of a module has at least one term
% after the end_module marker for the main module.
; rme_unexpected_term_in_int_or_opt
% The interface or optimization file of a module has at least one
% term that is not expected in such a file.
; rme_could_not_read_term
; rme_could_not_parse_item
% When we attempted to read an item from the file, we got a failure
% either in the first stage of parsing (reading in a term, i.e.
% converting a subsequence of the character in the file to a term),
% or the second stage of parsing (converting that term to an item).
% Since the rest of the compiler should not care whether parsing
% is done in one or two stages, it should treat both these errors
% the same. We distinguish them only for completeness.
; rme_cannot_find_modify_time
% We cannot find out the modification time of he file.
% This error is not fatal, because its only effect is
% to disable smart recompilation, whose implementation
% is not completed yet.
; rme_nec.
% A read module error that is Not Elsewhere Classified, i.e.
% is not one of the error kinds listed above.
%
% Before the change away from read_module_errors being just a set
% of read_module_error values, we used to not include these errors
% in the set at all, which could lead to an empty set of
% read_module_error values being paired with a nonempty list
% of severity_error error_specs.
% This type represents the set of errors that were encountered
% during an attempt to read in a Mercury module's source file,
% interface file, or optimization file.
%
% There are two kinds of tests that code will typically perform
% on values of this type.
%
% 1. Are there any errors?
% 2. Are there any FATAL errors?
%
% You can use there_are_no_errors and there_are_some_errors for
% the first kind of test, and a direct invocation of an emptiness test
% on the rm_fatal_errors field for the second kind.
%
:- type read_module_errors
---> read_module_errors(
% The fatal errors we have encountered, and their messages.
% All these error_specs should have severity_error.
rm_fatal_errors :: set(fatal_read_module_error),
rm_fatal_error_specs :: list(error_spec),
% The nonfatal errors we have encountered, and their messages.
% All these error_specs should have severity_error.
rm_nonfatal_errors :: set(nonfatal_read_module_error),
rm_nonfatal_error_specs :: list(error_spec),
% The warnings we have encountered. All these should have
% severity levels *below* severity_error.
rm_warning_specs :: list(error_spec)
).
%---------------------%
% Return a structure that records no errors (so far).
%
:- func init_read_module_errors = read_module_errors.
%---------------------%
% Add a fatal error, and its message.
%
:- pred add_fatal_error(fatal_read_module_error::in, list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
% Add a nonfatal error, and its message.
%
:- pred add_nonfatal_error(nonfatal_read_module_error::in,
list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
% If there are any error_specs in the input list,
% record them as representing a not-elsewhere-classified nonfatal error.
%
:- pred add_any_nec_errors(list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
% Add some warning messages.
%
:- pred add_warning(list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
%---------------------%
% Succeed if the read_module_errors structure records zero errors.
% (Though it may contain warnings.)
%
:- pred there_are_no_errors(read_module_errors::in) is semidet.
% Succeed if the read_module_errors structure records at least one error.
%
:- pred there_are_some_errors(read_module_errors::in) is semidet.
%---------------------%
% Return all the error_specs in the argument, regardless of severity.
%
:- func get_read_module_specs(read_module_errors) = list(error_spec).
%---------------------%
:- pred io_error_to_error_spec(error_phase::in, string::in, error_spec::out,
io::di, io::uo) is det.
:- pred io_error_to_read_module_errors(fatal_read_module_error::in,
error_phase::in, string::in, read_module_errors::out,
io::di, io::uo) is det.
%---------------------------------------------------------------------------%
:- implementation.
%---------------------------------------------------------------------------%
init_read_module_errors = read_module_errors(set.init, [], set.init, [], []).
%---------------------%
add_fatal_error(Error, Specs, Errors0, Errors) :-
Errors0 = read_module_errors(FatalErrors0, FatalSpecs0,
NonFatalErrors, NonFatalSpecs, WarningSpecs),
set.insert(Error, FatalErrors0, FatalErrors),
FatalSpecs = Specs ++ FatalSpecs0,
Errors = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs).
add_nonfatal_error(Error, Specs, Errors0, Errors) :-
Errors0 = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors0, NonFatalSpecs0, WarningSpecs),
set.insert(Error, NonFatalErrors0, NonFatalErrors),
NonFatalSpecs = Specs ++ NonFatalSpecs0,
Errors = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs).
add_any_nec_errors(Specs, !Errors) :-
(
Specs = []
;
Specs = [_ | _],
add_nonfatal_error(rme_nec, Specs, !Errors)
).
add_warning(Specs, Errors0, Errors) :-
Errors0 = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs0),
WarningSpecs = Specs ++ WarningSpecs0,
Errors = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs).
%---------------------%
there_are_no_errors(Errors) :-
Errors = read_module_errors(FatalErrors, _, NonFatalErrors, _, _),
set.is_empty(FatalErrors),
set.is_empty(NonFatalErrors).
there_are_some_errors(Errors) :-
Errors = read_module_errors(FatalErrors, _, NonFatalErrors, _, _),
( set.is_non_empty(FatalErrors)
; set.is_non_empty(NonFatalErrors)
).
%---------------------%
get_read_module_specs(Errors) = Specs :-
Errors = read_module_errors(_FatalErrors, FatalSpecs,
_NonFatalErrors, NonFatalSpecs, WarningSpecs),
Specs = FatalSpecs ++ NonFatalSpecs ++ WarningSpecs.
%---------------------%
io_error_to_error_spec(Phase, ErrorMsg, Spec, !IO) :-
io.progname_base("mercury_compile", ProgName, !IO),
Pieces = [fixed(ProgName), suffix(":"), words(ErrorMsg), nl],
Spec = no_ctxt_spec($pred, severity_error, Phase, Pieces).
io_error_to_read_module_errors(FatalError, Phase, ErrorMsg, Errors, !IO) :-
io_error_to_error_spec(Phase, ErrorMsg, Spec, !IO),
Errors0 = init_read_module_errors,
add_fatal_error(FatalError, [Spec], Errors0, Errors).
%---------------------------------------------------------------------------%
:- end_module parse_tree.parse_error.
%---------------------------------------------------------------------------%