1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 # Copyright 2014 Alexandre Terrasa <alexandre@moz-code.org>
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
18 # Helpers for various statistics tools.
26 redef class ToolContext
29 var opt_all
= new OptionBool("Compute all metrics", "--all")
32 var opt_mmodules
= new OptionBool("Compute metrics about mmodules", "--mmodules")
34 var opt_mclasses
= new OptionBool("Compute metrics about mclasses", "--mclasses")
36 var opt_mendel
= new OptionBool("Compute mendel metrics", "--mendel")
38 var opt_inheritance
= new OptionBool("Compute metrics about inheritance usage", "--inheritance")
40 var opt_refinement
= new OptionBool("Compute metrics about refinement usage", "--refinement")
42 var opt_self
= new OptionBool("Compute metrics about the usage of explicit and implicit self", "--self")
44 var opt_ast
= new OptionBool("Compute metrics about the usage of nodes and identifiers in the AST", "--ast")
46 var opt_nullables
= new OptionBool("Compute metrics on nullables send", "--nullables")
48 var opt_static_types
= new OptionBool("Compute explicit static types metrics", "--static-types")
50 var opt_tables
= new OptionBool("Compute tables metrics", "--tables")
52 var opt_rta
= new OptionBool("Compute RTA metrics", "--rta")
54 var opt_csv
= new OptionBool("Export metrics in CSV format", "--csv")
56 var opt_generate_hyperdoc
= new OptionBool("Generate Hyperdoc", "--generate_hyperdoc")
58 var opt_poset
= new OptionBool("Complete metrics on posets", "--poset")
60 var opt_nocolors
= new OptionBool("Disable colors in console outputs", "--no-colors")
62 var opt_dir
= new OptionString("Directory where some statistics files are generated", "-d", "--dir")
64 # Output directory for metrics files.
65 var output_dir
: String = "."
70 self.option_context
.add_option
(opt_all
)
71 self.option_context
.add_option
(opt_mmodules
)
72 self.option_context
.add_option
(opt_mclasses
)
73 self.option_context
.add_option
(opt_mendel
)
74 self.option_context
.add_option
(opt_inheritance
)
75 self.option_context
.add_option
(opt_refinement
)
76 self.option_context
.add_option
(opt_self
)
77 self.option_context
.add_option
(opt_ast
)
78 self.option_context
.add_option
(opt_nullables
)
79 self.option_context
.add_option
(opt_static_types
)
80 self.option_context
.add_option
(opt_tables
)
81 self.option_context
.add_option
(opt_rta
)
82 self.option_context
.add_option
(opt_csv
)
83 self.option_context
.add_option
(opt_generate_hyperdoc
)
84 self.option_context
.add_option
(opt_poset
)
85 self.option_context
.add_option
(opt_dir
)
86 self.option_context
.add_option
(opt_nocolors
)
89 redef fun process_options
(args
)
92 var val
= self.opt_dir
.value
94 val
= val
.simplify_path
100 # Format and colorize a string heading of level 1 for console output.
102 # Default style is yellow and bold.
103 fun format_h1
(str
: String): String do
104 if opt_nocolors
.value
then return str
105 return str
.yellow
.bold
108 # Format and colorize a string heading of level 2 for console output.
110 # Default style is white and bold.
111 fun format_h2
(str
: String): String do
112 if opt_nocolors
.value
then return str
116 # Format and colorize a string heading of level 3 for console output.
118 # Default style is white and nobold.
119 fun format_h3
(str
: String): String do
120 if opt_nocolors
.value
then return str
124 # Format and colorize a string heading of level 4 for console output.
126 # Default style is green.
127 fun format_h4
(str
: String): String do
128 if opt_nocolors
.value
then return str
132 # Format and colorize a string heading of level 5 for console output.
134 # Default style is light gray.
135 fun format_p
(str
: String): String do
136 if opt_nocolors
.value
then return str
137 return str
.light_gray
143 # is the class imported from standard lib?
144 fun is_standard
: Bool do
145 return self.intro_mmodule
.mgroup
.mproject
.name
== "standard"
150 # is the module imported from standard lib?
151 fun is_standard
: Bool do
152 return self.mgroup
.mproject
.name
== "standard"
156 # A Metric is used to collect data about things
158 # The concept is reified here for a better organization and documentation
161 # Type of elements measured by this metric.
164 # Type of values used to measure elements.
167 # Type of data representation used to associate elements and values.
168 type RES: Map[ELM, VAL]
170 # The name of this metric (generally an acronym about the metric).
171 fun name
: String is abstract
173 # A long and understandable description about what is measured by this metric.
174 fun desc
: String is abstract
176 # Clear all results for this metric
177 fun clear
is abstract
179 # Values for each element
180 fun values
: RES is abstract
182 # Collect metric values on elements
183 fun collect
(elements
: Set[ELM]) is abstract
185 # The value calculated for the element
186 fun [](element
: ELM): VAL do return values
[element
]
188 # Does the element have a value for this metric?
189 fun has_element
(element
: ELM): Bool do return values
.has_key
(element
)
192 fun avg
: Float is abstract
194 # Pretty print the metric results in console
195 fun to_console
(indent
: Int, colors
: Bool) do
196 if values
.is_empty
then
198 print
"{"\t" * indent}{name}: {desc} -- nothing".green
200 print
"{"\t" * indent}{name}: {desc} -- nothing"
208 print
"{"\t" * indent}{name}: {desc}".green
209 print
"{"\t" * indent} avg: {avg}".light_gray
210 print
"{"\t" * indent} max: {max} ({self[max]})".light_gray
211 print
"{"\t" * indent} min: {min} ({self[min]})".light_gray
212 print
"{"\t" * indent} std: {std_dev}".light_gray
214 print
"{"\t" * indent}{name}: {desc}"
215 print
"{"\t" * indent} avg: {avg}"
216 print
"{"\t" * indent} max: {max} ({self[max]})"
217 print
"{"\t" * indent} min: {min} ({self[min]})"
218 print
"{"\t" * indent} std: {std_dev}"
222 # The sum of all the values.
223 fun sum
: VAL is abstract
225 # The values standard derivation
226 fun std_dev
: Float is abstract
228 # The element with the highest value
229 fun max
: ELM is abstract
231 # The element with the lowest value
232 fun min
: ELM is abstract
234 # The value threshold above what elements are considered as 'interesting'
235 fun threshold
: Float do return avg
+ std_dev
237 # The set of element above the threshold
238 fun above_threshold
: Set[ELM] is abstract
240 # Sort the metric keys by values
241 fun sort
: Array[ELM] do
242 return values
.keys_sorted_by_values
(default_reverse_comparator
)
246 # A Metric that collects integer data
248 # Used to count things
253 redef type RES: Counter[ELM]
255 # `IntMetric` uses a Counter to store values in intern.
256 protected var values_cache
= new Counter[ELM]
258 redef fun values
do return values_cache
260 redef fun clear
do values_cache
.clear
262 redef fun sum
do return values_cache
.sum
265 assert not values_cache
.is_empty
266 return values_cache
.max
.as(not null)
270 assert not values_cache
.is_empty
271 return values_cache
.min
.as(not null)
275 redef fun avg
do return values_cache
.avg
277 redef fun std_dev
do return values_cache
.std_dev
279 redef fun above_threshold
do
280 var above
= new HashSet[ELM]
281 var threshold
= threshold
282 for element
, value
in values
do
283 if value
.to_f
> threshold
then above
.add
(element
)
288 redef fun to_console
(indent
, colors
) do
291 print
"{"\t" * indent} sum: {sum}".light_gray
293 print
"{"\t" * indent} sum: {sum}"
298 # A Metric that collects float datas
300 # Used sor summarization
304 redef type VAL: Float
306 # `FloatMetric` uses a Map to store values in intern.
307 protected var values_cache
= new HashMap[ELM, VAL]
309 redef fun values
do return values_cache
311 redef fun clear
do values_cache
.clear
316 for v
in values
.values
do sum
+= v
321 assert not values
.is_empty
322 var max
: nullable Float = null
323 var elem
: nullable ELM = null
324 for e
, v
in values
do
325 if max
== null or v
> max
then
330 return elem
.as(not null)
334 assert not values
.is_empty
335 var min
: nullable Float = null
336 var elem
: nullable ELM = null
337 for e
, v
in values
do
338 if min
== null or v
< min
then
343 return elem
.as(not null)
347 if values
.is_empty
then return 0.0
348 return sum
/ values
.length
.to_f
353 for value
in values
.values
do
354 sum
+= (value
- avg
).pow
(2.to_f
)
356 return (sum
/ values
.length
.to_f
).sqrt
359 redef fun above_threshold
do
360 var above
= new HashSet[ELM]
361 var threshold
= threshold
362 for element
, value
in values
do
363 if value
> threshold
then above
.add
(element
)
368 redef fun to_console
(indent
, colors
) do
371 print
"{"\t" * indent} sum: {sum}".light_gray
373 print
"{"\t" * indent} sum: {sum}"
378 # A MetricSet is a metric holder
380 # It purpose is to be extended with a metric collect service
383 # Type of element measured by this `MetricSet`.
387 var metrics
: Set[Metric] = new HashSet[Metric]
389 # Add a metric to the set
390 fun register
(metrics
: Metric...) do for metric
in metrics
do self.metrics
.add
(metric
)
392 # Clear all results for all metrics
393 fun clear
do for metric
in metrics
do metric
.clear
395 # Collect all metrics for this set of class
396 fun collect
(elements
: Set[ELM]) do
397 for metric
in metrics
do metric
.collect
(elements
)
400 # Pretty print the resuls in console
401 fun to_console
(indent
: Int, colors
: Bool) do
402 for metric
in metrics
do metric
.to_console
(indent
, colors
)
405 # Export the metric set in CSV format
406 fun to_csv
: CsvDocument do
407 var csv
= new CsvDocument
409 csv
.format
= new CsvFormat('"', ';', "\n")
412 csv
.header
.add
("entry")
413 for metric
in metrics
do csv
.header
.add
(metric
.name
)
415 # collect all entries to merge metric results
416 var entries
= new HashSet[ELM]
417 for metric
in metrics
do
418 for entry
in metric
.values
.keys
do entries
.add
(entry
)
422 for entry
in entries
do
423 var line
= [entry
.to_s
]
424 for metric
in metrics
do
425 if metric
.has_element
(entry
) then
426 line
.add
(metric
[entry
].to_s
)
431 csv
.records
.add
(line
)