Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / phase.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Phases of the processing of nit programs
16 module phase
17
18 import toolcontext
19 import parser
20 import poset
21
22 redef class ToolContext
23 # The various registered phases to performs
24 # The order in the poset is the dependence of phases
25 #
26 # While you can directly modify the poset (nodes and edges),
27 # it is often simpler to use the constructor in `Phase`
28 var phases = new POSet[Phase]
29
30 # --disable-phase
31 var opt_disable_phase = new OptionArray("Disable a specific phase; use `list` to get the list (debug)", "--disable-phase")
32
33 # --sloppy
34 var opt_sloppy = new OptionBool("Force lazy semantic analysis of the source-code (debug)", "--sloppy")
35
36 redef init
37 do
38 super
39
40 option_context.add_option(opt_disable_phase, opt_sloppy)
41 end
42
43 redef fun process_options(args)
44 do
45 super
46
47 for v in opt_disable_phase.value do
48 if v == "list" then
49 for p in phases_list do
50 var deps = p.in_hierarchy.direct_greaters
51 if deps.is_empty then
52 print p
53 else
54 print "{p} (dep: {deps.join(", ")})"
55 end
56 end
57 exit 0
58 end
59
60 var found = false
61 for p in phases do
62 if v != p.to_s then continue
63 found = true
64 p.disabled = true
65 end
66 if not found then fatal_error(null, "Error: no phase named `{v}`. Use `list` to list all phases.")
67 end
68
69 if opt_sloppy.value then semantize_is_lazy = true
70 end
71
72 # The list of registered phases in the application order.
73 var phases_list: Sequence[Phase] is lazy do
74 var phases = self.phases.to_a
75 self.phases.sort(phases)
76 return phases
77 end
78
79 # Is `phase_process_npropdef` not called automatically by `run_phases`?
80 #
81 # When set to true, it is the responsibility of the tools
82 #
83 # Is false by default.
84 var semantize_is_lazy = false is writable
85
86 # Set of already analyzed modules.
87 private var phased_modules = new HashSet[AModule]
88
89 # List of module to process according to `run_phases`
90 #
91 # This allow some new modules to be found and added while analysing the code.
92 var todo_nmodules: Sequence[AModule]
93
94 # Run all registered phases on a set of modules
95 fun run_phases(nmodules: Collection[AModule])
96 do
97 var time0 = get_time
98 self.info("*** SEMANTIC ANALYSIS ***", 1)
99 #phases.show_dot
100
101 var phases = phases_list
102
103 for phase in phases do
104 self.info(" registered phases: {phase}", 2)
105 end
106
107 var todo_nmodules = nmodules.to_a
108 self.todo_nmodules = todo_nmodules
109
110 while not todo_nmodules.is_empty do
111 var nmodule = todo_nmodules.shift
112 if phased_modules.has(nmodule) then continue
113 phased_modules.add nmodule
114
115 self.info("Semantic analysis module {nmodule.location.file.filename}", 2)
116
117 var vannot = new AnnotationPhaseVisitor
118 vannot.enter_visit(nmodule)
119
120 for phase in phases do
121 if phase.disabled then continue
122 assert phase.toolcontext == self
123 var errcount = self.error_count
124 phase.process_nmodule(nmodule)
125 if errcount != self.error_count then
126 self.check_errors
127 end
128 errcount = self.error_count
129 for nclassdef in nmodule.n_classdefs do
130 assert phase.toolcontext == self
131 phase.process_nclassdef(nclassdef)
132 if not semantize_is_lazy then for npropdef in nclassdef.n_propdefs do
133 assert phase.toolcontext == self
134 phase_process_npropdef(phase, npropdef)
135 end
136 end
137 if errcount != self.error_count then
138 self.check_errors
139 end
140 for na in vannot.annotations do
141 var p = na.parent
142 if p isa AAnnotations then p = p.parent
143 assert p != null
144 phase.process_annotated_node(p, na)
145 end
146 if errcount != self.error_count then
147 self.check_errors
148 end
149 phase.process_nmodule_after(nmodule)
150 end
151 self.check_errors
152 end
153
154 var time1 = get_time
155 self.info("*** END SEMANTIC ANALYSIS: {time1-time0} ***", 2)
156
157 self.check_errors
158 end
159
160 # Process the given `phase` on the `npropdef`
161 # Called by `run_phases`
162 protected fun phase_process_npropdef(phase: Phase, npropdef: APropdef)
163 do
164 phase.process_npropdef(npropdef)
165 end
166
167 # Run the phase on the given npropdef.
168 # Does nothing if `semantize_is_lazy` is false.
169 fun run_phases_on_npropdef(npropdef: APropdef)
170 do
171 if not semantize_is_lazy then return
172 if npropdef.is_phased then return
173 npropdef.is_phased = true
174
175 #self.info("Semantic analysis of property {npropdef.location.file.filename}", 0)
176
177 var phases = phases_list
178 for phase in phases do
179 if phase.disabled then continue
180 assert phase.toolcontext == self
181 phase_process_npropdef(phase, npropdef)
182 self.check_errors
183 end
184 end
185 end
186
187 redef class APropdef
188 # Is the propdef already analyzed by `run_phases_on_npropdef`.
189 # Unused unless `semantize_is_lazy` is true.
190 private var is_phased = false
191 end
192
193 # Collect all annotation
194 private class AnnotationPhaseVisitor
195 super Visitor
196
197 # The collected annotations
198 var annotations = new Array[AAnnotation]
199
200 redef fun visit(n)
201 do
202 n.visit_all(self)
203 if n isa AAnnotation then annotations.add n
204 end
205 end
206
207 # Abstraction of steps in the analysis/processing of Nit programs
208 # Specific phases should implements one of the `process_*` method
209 abstract class Phase
210 # The toolcontext instance attached to the phase
211 var toolcontext: ToolContext
212
213 # The dependence relation of the phase with the other phases
214 var in_hierarchy: POSetElement[Phase] is noinit
215
216 # The explicit dependences, used to initialize `in_importation`
217 var depends: nullable Collection[Phase]
218
219 # Initialize and register a phase to the toolcontext
220 init
221 do
222 in_hierarchy = toolcontext.phases.add_node(self)
223 var depends = self.depends
224 if depends != null then
225 for d in depends do
226 toolcontext.phases.add_edge(self, d)
227 end
228 end
229 end
230
231 # By default, the name is the lowercased prefix of the classname
232 redef fun to_s do return class_name.strip_extension("Phase").to_snake_case
233
234 # Is the phase globally disabled?
235 # A disabled phase is not called automatically called by `ToolContext::run_phases` and cie.
236 #
237 # Warning: disabling a phase may cause subsequent phases to work in a degraded way or to fail.
238 var disabled = false is writable
239
240 # Specific actions to execute on the whole tree of a module
241 # @toimplement
242 fun process_nmodule(nmodule: AModule) do end
243
244 # Specific actions to execute on the tree of a class definition
245 # Note that the order of the visit is the one of the file
246 # @toimplement
247 fun process_nclassdef(nclassdef: AClassdef) do end
248
249 # Specific actions to execute on the tree of a property
250 # Note that the order of the visit is the one of the file
251 # @toimplement
252 fun process_npropdef(npropdef: APropdef) do end
253
254 # Specific actions to execute on annotated nodes
255 # Note that the order of the visit is the one of the file
256 # @toimplement
257 fun process_annotated_node(node: ANode, nat: AAnnotation) do end
258
259 # Specific actions to execute on the whole tree of a module
260 # Called at the end of a phase on a module
261 # Last called hook
262 # @toimplement
263 fun process_nmodule_after(nmodule: AModule) do end
264 end