src: remove some warnings and do some cleaning
[nit.git] / src / nitpretty.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 # `nitpretty` is a tool able to pretty print Nit files.
16 #
17 # Usage:
18 #
19 # nitpretty source.nit
20 #
21 # Main options:
22 #
23 # * `-o res.nit` output result into `res.nit`
24 # * `--diff` show diff between `source` and `res`
25 # * `--meld` open diff with `meld`
26 # * `--check` check the format of multiple source files
27 # * `--check --meld` perform `--check` and open `meld` for each difference
28 #
29 # ## Specification
30 #
31 # The specification of the pretty printing is described here.
32 #
33 # * Default indentation level is one `'\t'` character and
34 # is increased by one for each indentation level.
35 # * Default line max-size is 80.
36 #
37 # ### Comments
38 #
39 # There is many categories of comments:
40 #
41 # `Licence comments` are attached to the top of the file
42 # no blank line before, one after.
43 #
44 # # This is a licence comment
45 #
46 # # Documentation for module `foo`
47 # module foo
48 #
49 # `ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
50 #
51 # They are printed before the definition with a blank line before and no after
52 # at the same indentation level than the definition.
53 #
54 # # Documentation for module `foo`
55 # module foo
56 #
57 # # Documentation for class `Bar`
58 # class Bar
59 # # Documentation for method `baz`
60 # fun baz do end
61 # end
62 #
63 # `Block comments` are comments composed of one or more line rattached to nothing.
64 # They are displayed with one blank line before and after at current indent level.
65 #
66 # <blank>
67 # # block
68 # # comment
69 # <blank>
70 #
71 # `Attached comments` are comments attached to a production.
72 # They are printed as this.
73 #
74 # fun foo do # attached comment
75 # end
76 #
77 # `nitpretty` automatically remove multiple blanks between comments:
78 #
79 # # Licence
80 # # ...
81 # <blank>
82 # # Block comment
83 #
84 # ### Inlining
85 #
86 # Productions are automatically inlined when possible.
87 #
88 # Conditions:
89 #
90 # * the production must be syntactically inlinable
91 # * the inlined production length is less than `PrettyPrinterVisitor::max-size`
92 # * the production do not contains any comments
93 #
94 # ### Modules
95 #
96 # * There is a blank between the module declaration and its imports
97 # * There is no blank between imports and only one after
98 # * There is a blank between each extern block definition
99 # * There is a blank between each class definition
100 # * There is no blank line at the end of the module
101 #
102 # # Documentation for module `foo`
103 # module foo
104 #
105 # import a
106 # # import b
107 # import c
108 #
109 # # Documentation for class `Bar`
110 # class Bar end
111 #
112 # class Baz end # not a `ADoc` comment
113 #
114 #
115 # ### Classes
116 #
117 # * There is no blank between the class definition and its super-classes declarations
118 # * There is no blank between two inlined property definition
119 # * There is a blank between each block definition
120 # * There no blank line at the end of the class definition
121 #
122 # # Documentation for class `Bar`
123 # class Bar end
124 #
125 # class Baz
126 # super Bar
127 #
128 # fun a is abstract
129 # private fun b do end
130 #
131 # fun c do
132 # # ...
133 # end
134 # end
135 #
136 # Generic types have no espace after or before brackets and are separated by a comma and a space:
137 #
138 # class A[E: Type1, F: Type1] do end
139 #
140 # ### Blocks
141 #
142 # * Inlined productions have no blank lines between them
143 # * Block productions have a blank before and after
144 #
145 # var a = 10
146 # var b = 0
147 #
148 # if a > b then
149 # # is positive
150 # print "positive"
151 # end
152 #
153 # print "end"
154 #
155 # ### Calls and Binary Ops
156 #
157 # Arguments are always printed separated with a comma and a space:
158 #
159 # foo(a, b, c)
160 #
161 # Binary ops are always printed wrapped with spaces:
162 #
163 # var c = 1 + 2
164 #
165 # Calls and binary ops can be splitted to fit the `max-size` constraint.
166 # Breaking priority is given to arguments declaration after the comma.
167 #
168 # return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
169 # "cccccccccccccccccccccccccc")
170 #
171 # Binary ops can also be broken to fit the `max-size` limit:
172 #
173 # return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
174 # "cccccccccccccccccccccccccc"
175 #
176 module nitpretty
177
178 import template
179 import toolcontext
180 import modelbuilder
181 import astutil
182
183 # The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
184 #
185 # The main method here is `visit` that performs the pretty printing of a `ANode`.
186 #
187 # Because some tokens like `TComment` are not stored in the AST,
188 # we visit the AST like traditionnal visitor and also maintain a
189 # pointer to the `current_token` (actually the next one to be printed).
190 #
191 # Visited productions are in charges to move the token pointer using methods such as:
192 #
193 # * `consume`: print `current_token` and move to the next one
194 # * `skip`: move to the next token without printing the current one
195 # * `skip_to`: move to a specified token skipping all the tokens before
196 # * `catch_up`: consume all the tokens between `current_token` and a specified token
197 # * `finish_line`: consume all the tokens between `current_token` and the end of line
198 class PrettyPrinterVisitor
199 # Pretty print `n`.
200 fun pretty(n: ANode): Template do
201 clear
202 n.parentize_tokens
203
204 if n isa Prod then
205 current_token = n.first_token
206 visit n
207 else if n isa Token then
208 var p = n.parent
209
210 while p != null and not p isa Prod do
211 p = p.parent
212 end
213
214 current_token = p.first_token
215 visit p
216 end
217
218 return tpl.as(not null)
219 end
220
221 # Pretty print the whole `nmodule` with comments before and after.
222 fun pretty_nmodule(nmodule: AModule): Template do
223 clear
224 nmodule.parentize_tokens
225 current_token = nmodule.location.file.first_token
226 visit nmodule
227 catch_up nmodule.location.file.last_token
228 tpl.add "\n"
229 return tpl.as(not null)
230 end
231
232 # Prepare `self` for a new visit.
233 private fun clear do
234 tpl = new Template
235 current_token = null
236 indent = 0
237 current_length = 0
238 previous_length = 0
239 wait_addn = 0
240 end
241
242 # Visit `n` if not null.
243 fun visit(n: nullable ANode) do
244 if n == null then return
245 n.accept_pretty_printer self
246 end
247
248 # Visit a list of `Anode`.
249 fun visit_list(n: nullable ANodes[ANode]) do
250 if n == null then return
251 n.accept_pretty_printer self
252 end
253
254 # Is the node inlinable and can fit on the line.
255 fun can_inline(n: nullable ANode): Bool do
256 if n == null then return true
257 if n.must_be_inline then return true
258 if n.must_be_block then return false
259 # check length
260 if n.collect_length + current_length > max_size then return false
261 # check block is inlinable
262 return n.is_inlinable
263 end
264
265 # Collect all `TComment` between `from` and `to`.
266 fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
267 var res = new Array[TComment]
268 if from isa Prod then from = from.first_token
269 if to isa Prod then to = to.first_token
270 if from == null or to == null then return res
271
272 while from != to do
273 if from isa TComment then res.add from
274 from = from.as(Token).next_token
275 end
276
277 return res
278 end
279
280 # Token under cursor.
281 #
282 # This is the next token to visit.
283 var current_token: nullable Token = null
284
285 # Skip the `current_token`.
286 fun skip do current_token = current_token.next_token
287
288 # Skip `current_token` until the end of line.
289 fun skip_line do current_token = current_token.last_real_token_in_line
290
291 # Skip `current_token` until `target` is reached.
292 fun skip_to(target: nullable Token) do
293 if target == null then return
294 while current_token != target do skip
295 end
296
297 # Visit `current_token`.
298 fun consume(token: String) do
299 assert current_token.text == token
300 visit current_token
301 end
302
303 # Is there token to visit between `current_token` and `target`?
304 fun need_catch_up(target: nullable Token): Bool do
305 if target == null then return false
306 return current_token != target
307 end
308
309 # Visit all tokens between `current_token` and `target`.
310 fun catch_up(target: nullable ANode) do
311 if target == null then return
312 if current_token == null then return
313 var token: Token
314 if target isa Token then
315 token = target
316 else if target isa Prod then
317 token = target.first_token.as(not null)
318 else
319 abort
320 end
321 assert current_token.location <= token.location
322 while current_token != token do visit current_token
323 end
324
325 # Visit all tokens between `current_token` and the end of line.
326 fun finish_line do
327 if current_token isa TComment then
328 adds
329 visit current_token
330 end
331
332 while current_token isa TEol do skip
333 end
334
335 # The template under construction.
336 private var tpl: nullable Template = null
337
338 # Current indent level.
339 var indent = 0
340
341 # Size of a tabulation in spaces.
342 var tab_size = 8
343
344 # Max line size.
345 var max_size = 80
346
347 # Length of the current line.
348 var current_length = 0
349
350 # Length of the previous line.
351 var previous_length = 0
352
353 # Is the last line a blank line?
354 fun last_line_is_blank: Bool do return previous_length == 0
355
356 # Add `t` to current template.
357 fun add(t: String) do
358 if t.is_empty then return
359 while wait_addn > 0 do
360 tpl.add "\n"
361 wait_addn -= 1
362 end
363 tpl.add t
364 current_length += t.length
365 end
366
367 # Add a `'\n'`.
368 fun addn do
369 if current_length == 0 and last_line_is_blank then return
370 previous_length = current_length
371 current_length = 0
372 wait_addn += 1
373 end
374
375 # End of line chars are stored until another char is added.
376 # This avoid empty files with only a '`\n`'.
377 private var wait_addn = 0
378
379 # Add `'\t' * indent`.
380 fun addt do add "\t" * indent
381
382 # Add a space.
383 fun adds do add " "
384
385 fun visit_recv(n_expr: AExpr) do
386 if not n_expr isa AImplicitSelfExpr then
387 visit n_expr
388 consume "."
389 end
390 end
391 end
392
393 # Base framework redefs
394
395 redef class ANodes[E]
396 private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
397 for e in self do
398 var e_can_inline = v.can_inline(e)
399
400 if e != first then
401 if not e_can_inline then
402 v.add ","
403 v.addn
404 v.addt
405 v.addt
406 else
407 v.add ", "
408 end
409 end
410
411 v.visit e
412 end
413 end
414 end
415
416 redef class ANode
417 # Start visit of `self` using a `PrettyPrinterVisitor`
418 private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract
419
420 # Collect the length (in `Char`) of the node.
421 private fun collect_length: Int is abstract
422
423 # Is `self` printable in one line?
424 private fun is_inlinable: Bool do return true
425
426 # Force `self` to be rendered as a block.
427 private var force_block = false
428
429 # Does `self` have to be rendered as a block?
430 private fun must_be_block: Bool do return force_block
431
432 # Force `self` to be rendered as a line.
433 private var force_inline = false
434
435 # Does `self` have be rendered as a line?
436 private fun must_be_inline: Bool do
437 if parent != null and parent.must_be_inline then return true
438 return force_inline
439 end
440
441 # Does `self` was written in one line before transformation?
442 private fun was_inline: Bool is abstract
443 end
444
445 redef class Token
446 redef fun accept_pretty_printer(v) do
447 v.add text.trim
448 v.current_token = next_token
449 end
450
451 redef fun collect_length do return text.length
452 redef fun is_inlinable do return true
453 redef fun was_inline do return true
454 end
455
456 redef class Prod
457 redef fun accept_pretty_printer(v) do v.visit first_token
458
459 # The token where the production really start (skipping ADoc).
460 private fun start_token: nullable Token do return first_token
461
462 # Collect all `TComment` contained in the production
463 # (between `start_token` and `end_token`).
464 private fun collect_comments: Array[TComment] do
465 var res = new Array[TComment]
466 if start_token == null or last_token == null then return res
467 var token = start_token
468
469 while token != last_token do
470 if token isa TComment then res.add token
471 token = token.next_token
472 end
473
474 return res
475 end
476
477 redef fun collect_length do
478 var res = 0
479 if start_token == null or last_token == null then return res
480 var token = start_token
481
482 while token != last_token do
483 res += token.text.length
484 token = token.next_token
485 end
486
487 res += token.text.length
488 return res
489 end
490
491 redef fun was_inline do
492 return first_token.location.line_start == last_token.location.line_end
493 end
494 end
495
496 # Comments
497
498 redef class TComment
499 redef fun accept_pretty_printer(v) do
500 if is_adoc then
501 v.addt
502 super
503 v.addn
504 return
505 end
506
507 if is_licence then
508 super
509 v.addn
510 if is_last_in_group then v.addn
511 return
512 end
513
514 if is_orphan then
515 v.addn
516 v.addt
517 super
518 v.addn
519 v.addn
520 return
521 end
522
523 if is_inline then
524 if next_token isa TComment and is_first_in_group then v.addn
525 v.addt
526 super
527 v.addn
528 var prev_token = self.prev_token
529 if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
530 return
531 end
532
533 super
534 end
535
536 # Is `self` part of an `ADoc`?
537 private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null
538
539 # Is `self` part of a licence?
540 private fun is_licence: Bool do
541 var prev_token = self.prev_token
542
543 if prev_token == null then
544 return true
545 else if prev_token isa TComment then
546 return prev_token.is_licence
547 else
548 return false
549 end
550 end
551
552 # Is `self` starts and ends its line?
553 private fun is_inline: Bool do
554 return self == first_real_token_in_line and self == last_real_token_in_line
555 end
556
557 # Is `self` an orphan line (blank before and after)?
558 private fun is_orphan: Bool do
559 return prev_token isa TEol and
560 (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
561 next_token isa TEol
562 end
563
564 # Is `self` the first comment of a group of comment?
565 private fun is_first_in_group: Bool do return not prev_token isa TComment
566
567 # Is `self` the last comment of a group of comments?
568 private fun is_last_in_group: Bool do return not next_token isa TComment
569 end
570
571 redef class ADoc
572 redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
573 redef fun is_inlinable do return n_comment.length <= 1
574 end
575
576 # Annotations
577
578 redef class AAnnotations
579 redef fun accept_pretty_printer(v) do
580 v.adds
581 v.consume "is"
582
583 if v.can_inline(self) then
584 v.adds
585 for n_item in n_items do
586 v.visit n_item
587 if n_item != n_items.last then
588 v.add ", "
589 end
590 end
591 v.finish_line
592 else if n_items.length > 1 then
593 v.addn
594 v.indent += 1
595
596 for n_item in n_items do
597 v.addt
598 v.visit n_item
599 v.finish_line
600 v.addn
601 end
602
603 v.indent -= 1
604 end
605 if not was_inline and v.current_token isa TKwend then v.skip
606 end
607
608 redef fun is_inlinable do
609 if not super then return false
610 for annot in n_items do if not annot.is_inlinable then return false
611 return true
612 end
613 end
614
615 redef class AAnnotation
616 redef fun accept_pretty_printer(v) do
617 v.visit n_atid
618 if not n_args.is_empty then
619 if n_opar == null then
620 v.adds
621 else
622 v.visit n_opar
623 end
624 v.visit_list n_args
625 v.visit n_cpar
626 end
627 end
628 end
629
630 redef class ATypeAtArg
631 redef fun accept_pretty_printer(v) do v.visit n_type
632 end
633
634 redef class AExprAtArg
635 redef fun accept_pretty_printer(v) do v.visit n_expr
636 end
637
638 # Modules
639
640 redef class AModule
641 redef fun accept_pretty_printer(v) do
642 v.catch_up start_token
643 v.visit n_moduledecl
644
645 if not n_imports.is_empty then
646 v.addn
647
648 for n_import in n_imports do
649 v.catch_up n_import
650 v.visit n_import
651 end
652 end
653
654 if not n_extern_code_blocks.is_empty then
655 v.addn
656
657 for n_extern_block in n_extern_code_blocks do
658 v.catch_up n_extern_block
659 v.visit n_extern_block
660 v.addn
661 if n_extern_block != n_extern_code_blocks.last then v.addn
662 end
663
664 if not n_classdefs.is_empty then v.addn
665 end
666
667 if not n_classdefs.is_empty then
668 v.addn
669
670 for n_classdef in n_classdefs do
671 v.catch_up n_classdef
672 v.visit n_classdef
673 if n_classdef != n_classdefs.last then v.addn
674 end
675 end
676
677 assert v.indent == 0
678 end
679
680 # Skip doc if any.
681 redef fun start_token do
682 if n_moduledecl != null then return n_moduledecl.first_token
683 if not n_imports.is_empty then return n_imports.first.first_token
684 if not n_classdefs.is_empty then return n_classdefs.first.first_token
685 return first_token
686 end
687
688 redef fun is_inlinable do return false
689 end
690
691 redef class AModuledecl
692 redef fun accept_pretty_printer(v) do
693 v.visit n_doc
694 v.visit n_kwmodule
695 v.adds
696 v.visit n_name
697
698 if n_annotations != null then
699 var annot_inline = v.can_inline(n_annotations)
700 v.visit n_annotations
701
702 if not annot_inline then
703 if v.current_token isa TKwend then
704 v.consume "end"
705 v.finish_line
706 else
707 v.add "end"
708 end
709 end
710 end
711
712 v.finish_line
713 v.addn
714 end
715 end
716
717 redef class AModuleName
718 redef fun accept_pretty_printer(v) do
719 for path in n_path do
720 v.visit path
721 v.add "::"
722 end
723
724 v.visit n_id
725 end
726 end
727
728 redef class ANoImport
729 redef fun accept_pretty_printer(v) do
730 v.visit n_kwimport
731 v.adds
732 v.visit n_kwend
733 v.finish_line
734 v.addn
735 end
736 end
737
738 redef class AStdImport
739 redef fun accept_pretty_printer(v) do
740 if not n_visibility isa APublicVisibility then
741 v.visit n_visibility
742 v.adds
743 end
744
745 v.visit n_kwimport
746 v.adds
747 v.visit n_name
748 v.finish_line
749 v.addn
750 end
751 end
752
753 # Classes
754
755 redef class AClassdef
756 redef fun accept_pretty_printer(v) do
757 for n_propdef in n_propdefs do
758 v.catch_up n_propdef
759
760 if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
761 if n_propdef != n_propdefs.first then v.addn
762 v.visit n_propdef
763 if n_propdef != n_propdefs.last then v.addn
764 else
765 v.visit n_propdef
766 end
767 end
768 end
769 end
770
771 redef class AStdClassdef
772 redef fun accept_pretty_printer(v) do
773 v.visit n_doc
774 var can_inline = v.can_inline(self)
775
776 if not n_visibility isa APublicVisibility then
777 v.visit n_visibility
778 v.adds
779 end
780
781 if n_kwredef != null then
782 v.visit n_kwredef
783 v.adds
784 end
785
786 v.visit n_classkind
787 v.adds
788 v.visit n_id
789
790 if not n_formaldefs.is_empty then
791 v.consume "["
792 v.visit_list n_formaldefs
793 v.consume "]"
794 end
795
796 if n_extern_code_block != null then
797 v.adds
798 v.visit n_extern_code_block
799 end
800
801 if can_inline then
802 v.adds
803
804 if not n_superclasses.is_empty then
805 for n_superclass in n_superclasses do
806 v.visit n_superclass
807 v.adds
808 end
809 end
810 else
811 v.finish_line
812 v.addn
813 v.indent += 1
814
815 for n_superclass in n_superclasses do
816 v.catch_up n_superclass
817 v.addt
818 v.visit n_superclass
819 v.finish_line
820 v.addn
821 end
822
823 if not n_superclasses.is_empty and not n_propdefs.is_empty then
824 v.addn
825 end
826
827 super
828 v.catch_up n_kwend
829 v.indent -= 1
830 end
831
832 v.visit n_kwend
833 v.finish_line
834 v.addn
835 assert v.indent == 0
836 end
837
838 redef fun is_inlinable do
839 if not super then return false
840 if not n_propdefs.is_empty then return false
841 if n_superclasses.length > 1 then return false
842 if not collect_comments.is_empty then return false
843 return true
844 end
845
846 redef fun start_token do
847 if not n_visibility isa APublicVisibility then return n_visibility.first_token
848 if n_kwredef != null then return n_kwredef
849 return n_classkind.first_token
850 end
851 end
852
853 redef class AAbstractClasskind
854 redef fun accept_pretty_printer(v) do
855 v.visit n_kwabstract
856 v.adds
857 v.visit n_kwclass
858 end
859 end
860
861 redef class AExternClasskind
862 redef fun accept_pretty_printer(v) do
863 v.visit n_kwextern
864 v.adds
865 v.visit n_kwclass
866 end
867 end
868
869 redef class AFormaldef
870 redef fun accept_pretty_printer(v) do
871 v.visit n_id
872
873 if n_type != null then
874 v.consume ":"
875 v.adds
876 v.visit n_type
877 end
878 end
879 end
880
881 redef class AType
882 redef fun accept_pretty_printer(v) do
883 if n_kwnullable != null then
884 v.visit n_kwnullable
885 v.adds
886 end
887
888 v.visit n_id
889
890 if not n_types.is_empty then
891 v.consume "["
892 v.visit_list n_types
893 v.consume "]"
894 end
895 end
896 end
897
898 redef class ASuperclass
899 redef fun accept_pretty_printer(v) do
900 v.visit n_kwsuper
901 v.adds
902 v.visit n_type
903 end
904 end
905
906 # Properties
907
908 redef class APropdef
909 redef fun accept_pretty_printer(v) do
910 v.visit n_doc
911 v.addt
912
913 if not n_visibility isa APublicVisibility then
914 v.visit n_visibility
915 v.adds
916 end
917
918 if n_kwredef != null then
919 v.visit n_kwredef
920 v.adds
921 end
922 end
923
924 redef fun start_token do
925 if n_doc == null then return super
926 return n_doc.last_token.next_token
927 end
928 end
929
930 redef class AAttrPropdef
931 redef fun accept_pretty_printer(v) do
932 super
933 v.visit n_kwvar
934 v.adds
935 v.visit n_id2
936
937 if n_type != null then
938 v.consume ":"
939 v.adds
940 v.visit n_type
941 end
942
943 if n_expr != null then
944 v.adds
945 v.consume "="
946 v.adds
947 v.visit n_expr
948 end
949
950 if n_annotations != null then v.visit n_annotations
951 v.finish_line
952 v.addn
953 end
954
955 redef fun first_token do
956 if n_doc != null then return n_doc.first_token
957 if not n_visibility isa APublicVisibility then return n_visibility.first_token
958 if n_kwredef != null then return n_kwredef
959 return n_kwvar
960 end
961
962 redef fun is_inlinable do return true
963 end
964
965 redef class ATypePropdef
966 redef fun accept_pretty_printer(v) do
967 super
968 v.visit n_kwtype
969 v.adds
970 v.visit n_id
971 v.consume ":"
972 v.adds
973 v.visit n_type
974 v.finish_line
975 v.addn
976 end
977
978 redef fun is_inlinable do return true
979 end
980
981 redef class AMethPropdef
982 redef fun accept_pretty_printer(v) do
983 # TODO: Handle extern annotations
984
985 var before = v.indent
986 var can_inline = v.can_inline(self)
987 super
988 if n_kwinit != null then v.visit n_kwinit
989 if n_kwmeth != null then v.visit n_kwmeth
990 if n_kwnew != null then v.visit n_kwnew
991
992 if not n_methid == null then
993 v.adds
994 v.visit n_methid
995 end
996
997 v.visit n_signature
998
999 if n_annotations != null then
1000 v.visit n_annotations
1001 else
1002 v.adds
1003 end
1004
1005 if n_extern_calls != null or n_extern_code_block != null then
1006 if n_annotations != null then v.adds
1007 if n_extern_calls != null then v.visit n_extern_calls
1008 if n_extern_code_block != null then v.visit n_extern_code_block
1009 end
1010
1011 var n_block = self.n_block
1012
1013 if n_block != null then
1014 while not v.current_token isa TKwdo do v.skip
1015 if n_annotations != null then
1016 if v.can_inline(n_annotations) then
1017 v.adds
1018 else
1019 v.addt
1020 end
1021 end
1022 v.consume "do"
1023
1024 if can_inline then
1025 v.adds
1026
1027 if n_block isa ABlockExpr then
1028 if n_block.n_expr.is_empty then
1029 v.visit n_block.n_kwend
1030 else
1031 v.visit n_block.n_expr.first
1032 v.current_token = n_block.n_kwend
1033 v.skip
1034 end
1035 else
1036 v.visit n_block
1037 if v.current_token isa TKwend then v.skip
1038 end
1039 else
1040 v.finish_line
1041 v.addn
1042 v.indent += 1
1043
1044 if n_block isa ABlockExpr then
1045 n_block.force_block = true
1046 v.visit n_block
1047 v.catch_up n_block.n_kwend
1048 else
1049 v.addt
1050 v.visit n_block
1051 v.addn
1052 end
1053
1054 v.indent -= 1
1055 v.addt
1056 if n_block isa ABlockExpr then
1057 v.visit n_block.n_kwend
1058 else
1059 v.add "end"
1060 end
1061 end
1062 end
1063
1064 v.finish_line
1065 v.addn
1066 assert v.indent == before
1067 end
1068
1069 # Can be inlined if:
1070 # * block is empty or can be inlined
1071 # * contains no comments
1072 redef fun is_inlinable do
1073 if not super then return false
1074 if n_annotations != null and not n_annotations.is_inlinable then return false
1075 if n_block != null and not n_block.is_inlinable then return false
1076 if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
1077 if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
1078 if not collect_comments.is_empty then return false
1079 return true
1080 end
1081 end
1082
1083 redef class AMainMethPropdef
1084 redef fun accept_pretty_printer(v) do
1085 v.visit n_block
1086 v.addn
1087 end
1088 end
1089
1090 redef class ASignature
1091 redef fun accept_pretty_printer(v) do
1092 if not n_params.is_empty then
1093 v.consume "("
1094 v.visit_list n_params
1095 v.consume ")"
1096 end
1097
1098 if n_type != null then
1099 v.consume ":"
1100 v.adds
1101 v.visit n_type
1102 end
1103 end
1104 end
1105
1106 redef class AParam
1107 redef fun accept_pretty_printer(v) do
1108 v.visit n_id
1109
1110 if n_type != null then
1111 v.consume ":"
1112 v.adds
1113 v.visit n_type
1114 end
1115
1116 if n_dotdotdot != null then v.visit n_dotdotdot
1117 end
1118 end
1119
1120 # Extern
1121
1122 redef class AExternCalls
1123 redef fun accept_pretty_printer(v) do
1124 var can_inline = v.can_inline(self)
1125 v.visit n_kwimport
1126
1127 if can_inline then
1128 v.adds
1129 v.visit_list n_extern_calls
1130 else
1131 v.addn
1132 v.addt
1133 v.addt
1134 v.visit_list n_extern_calls
1135 end
1136
1137 v.adds
1138 end
1139 end
1140
1141 redef class AFullPropExternCall
1142 redef fun accept_pretty_printer(v) do
1143 v.visit n_type
1144 v.visit n_dot
1145 v.visit n_methid
1146 end
1147 end
1148
1149 redef class ALocalPropExternCall
1150 redef fun accept_pretty_printer(v) do v.visit n_methid
1151 end
1152
1153 redef class AInitPropExternCall
1154 redef fun accept_pretty_printer(v) do v.visit n_type
1155 end
1156
1157 redef class ACastAsExternCall
1158 redef fun accept_pretty_printer(v) do
1159 v.visit n_from_type
1160 v.visit n_dot
1161 v.visit n_kwas
1162 v.consume "("
1163 v.visit n_to_type
1164 v.consume ")"
1165 end
1166 end
1167
1168 redef class AAsNullableExternCall
1169 redef fun accept_pretty_printer(v) do
1170 v.visit n_type
1171 v.consume "."
1172 v.visit n_kwas
1173 v.adds
1174 v.visit n_kwnullable
1175 end
1176 end
1177
1178 redef class AAsNotNullableExternCall
1179 redef fun accept_pretty_printer(v) do
1180 v.visit n_type
1181 v.consume "."
1182 v.visit n_kwas
1183 v.adds
1184 v.visit n_kwnot
1185 v.adds
1186 v.visit n_kwnullable
1187 end
1188 end
1189
1190 redef class AExternCodeBlock
1191 redef fun accept_pretty_printer(v) do
1192 if n_in_language != null then
1193 v.visit n_in_language
1194 v.adds
1195 end
1196
1197 v.visit n_extern_code_segment
1198 end
1199
1200 redef fun is_inlinable do
1201 if not super then return false
1202 return n_extern_code_segment.is_inlinable
1203 end
1204 end
1205
1206 redef class AInLanguage
1207 redef fun accept_pretty_printer(v) do
1208 v.visit n_kwin
1209 v.adds
1210 v.visit n_string
1211 end
1212 end
1213
1214 redef class TExternCodeSegment
1215 redef fun accept_pretty_printer(v) do
1216 var can_inline = v.can_inline(self)
1217
1218 if can_inline then
1219 super
1220 else
1221 var text = text.substring(2, text.length - 4)
1222 var lines = text.r_trim.split("\n")
1223
1224 if text.is_empty then
1225 v.add "`\{`\}"
1226 else
1227 v.add "`\{"
1228
1229 if not lines.first.trim.is_empty then
1230 v.addn
1231 lines.first.l_trim
1232 v.indent += 1
1233 v.addt
1234 v.indent -= 1
1235 end
1236
1237 for line in lines do
1238 v.add line.r_trim
1239 v.addn
1240 end
1241
1242 v.addt
1243 v.add "`\}"
1244 end
1245
1246 v.current_token = next_token
1247 end
1248 end
1249
1250 redef fun is_inlinable do
1251 if not super then return false
1252 return location.line_start == location.line_end
1253 end
1254 end
1255
1256 # Blocks
1257
1258 redef class ABlockExpr
1259 redef fun accept_pretty_printer(v) do
1260 var before = v.indent
1261 var can_inline = v.can_inline(self)
1262
1263 if can_inline and not n_expr.is_empty then
1264 v.visit n_expr.first
1265 v.finish_line
1266 else
1267 for nexpr in n_expr do
1268 var expr_inline = v.can_inline(nexpr)
1269 if not expr_inline and nexpr != n_expr.first then v.addn
1270 v.catch_up nexpr
1271 v.addt
1272 v.visit nexpr
1273 v.finish_line
1274 v.addn
1275 if not expr_inline and nexpr != n_expr.last then v.addn
1276 end
1277 end
1278
1279 assert v.indent == before
1280 end
1281
1282 redef fun is_inlinable do
1283 if not super then return false
1284 if not collect_comments.is_empty then return false
1285
1286 if not n_expr.is_empty then
1287 if n_expr.length > 1 then return false
1288 if not n_expr.first.is_inlinable then return false
1289 end
1290
1291 return true
1292 end
1293 end
1294
1295 redef class AIfExpr
1296 redef fun accept_pretty_printer(v) do
1297 var before = v.indent
1298 var can_inline = v.can_inline(self)
1299 v.visit n_kwif
1300 v.adds
1301
1302 if v.can_inline(n_expr) then
1303 v.visit n_expr
1304 v.adds
1305 else
1306 v.visit n_expr
1307 v.addn
1308 v.addt
1309 end
1310
1311 # skip comments before `then` token
1312 while not v.current_token isa TKwthen do v.skip
1313 v.consume "then"
1314 var n_else = self.n_else
1315
1316 if can_inline then
1317 v.adds
1318 if n_then != null then v.visit n_then
1319
1320 if has_else(v) then
1321 n_else.force_inline = true
1322 v.adds
1323 v.consume "else"
1324 v.adds
1325 v.visit n_else
1326 else if n_then == null then
1327 v.add "end"
1328 end
1329
1330 v.skip_to last_token.last_real_token_in_line
1331 else
1332 v.finish_line
1333 v.addn
1334 v.indent += 1
1335
1336 if n_then != null then
1337 if n_then isa ABlockExpr then
1338 n_then.force_block = true
1339 v.visit n_then
1340 else
1341 v.addt
1342 v.visit n_then
1343 v.addn
1344 end
1345 end
1346
1347 if has_else(v) then
1348 while not v.current_token isa TKwelse do
1349 v.consume v.current_token.text
1350 end
1351
1352 v.indent -= 1
1353 v.addt
1354 v.consume "else"
1355
1356 if n_else isa AIfExpr then
1357 n_else.force_block = true
1358 v.adds
1359 v.visit n_else
1360 else
1361 v.finish_line
1362 v.addn
1363 v.indent += 1
1364
1365 if n_else isa ABlockExpr then
1366 n_else.force_block = true
1367 v.visit n_else
1368 else
1369 v.addt
1370 v.visit n_else
1371 v.addn
1372 end
1373
1374 if last_token isa TKwend then
1375 v.catch_up last_token
1376 v.indent -= 1
1377 v.addt
1378 v.consume "end"
1379 else
1380 v.indent -= 1
1381 v.addt
1382 v.add "end"
1383 end
1384 end
1385 else
1386 if last_token.location >= v.current_token.location then v.catch_up last_token
1387 v.indent -= 1
1388 v.addt
1389 v.add "end"
1390 if v.current_token isa TKwend then v.skip
1391 end
1392 end
1393
1394 assert v.indent == before
1395 end
1396
1397 redef fun is_inlinable do
1398 if not super then return false
1399 if n_then != null and not n_then.is_inlinable then return false
1400 var n_else = self.n_else
1401 if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
1402 (not n_else isa ABlockExpr and n_else != null) then
1403 return false
1404 end
1405 if not collect_comments.is_empty then return false
1406 return true
1407 end
1408
1409 # Does this `if` statement contains a `else` part?
1410 private fun has_else(v: PrettyPrinterVisitor): Bool do
1411 var n_else = n_else
1412 if n_else == null then return false
1413 var n_kwelse = collect_kwelse
1414 if n_kwelse == null then return false
1415
1416 if n_else isa ABlockExpr then
1417 var comments: Array[TComment]
1418
1419 if n_then == null then
1420 comments = v.collect_comments(n_expr.last_token, n_else.last_token)
1421 else
1422 comments = v.collect_comments(n_then.last_token, n_else.last_token)
1423 end
1424
1425 if not comments.is_empty then return true
1426 return not n_else.n_expr.is_empty
1427 end
1428
1429 return true
1430 end
1431
1432 # Lookup for `else` token in `self`.
1433 private fun collect_kwelse: nullable TKwelse do
1434 var token = first_token
1435
1436 while token != last_token do
1437 if token isa TKwelse then return token
1438 token = token.next_token
1439 end
1440
1441 return null
1442 end
1443 end
1444
1445 # Used to factorize work on loops.
1446 private class ALoopHelper
1447 super AExpr
1448
1449 fun loop_block: nullable ANode is abstract
1450 fun loop_label: nullable ANode is abstract
1451
1452 fun visit_loop_block(v: PrettyPrinterVisitor) do
1453 var n_block = loop_block
1454 v.finish_line
1455 v.addn
1456 v.indent += 1
1457
1458 if n_block isa ABlockExpr then
1459 n_block.force_block = true
1460 v.visit n_block
1461 v.catch_up n_block.n_kwend
1462 v.indent -= 1
1463 v.addt
1464 v.visit n_block.n_kwend
1465 else
1466 v.addt
1467 v.visit n_block
1468 v.addn
1469 v.indent -= 1
1470 v.addt
1471 v.add "end"
1472 end
1473
1474 if loop_label != null then
1475 v.adds
1476 v.visit loop_label
1477 end
1478 end
1479
1480 fun visit_loop_inline(v: PrettyPrinterVisitor) do
1481 var n_block = loop_block
1482 v.adds
1483
1484 if n_block isa ABlockExpr then
1485 if n_block.n_expr.is_empty then
1486 v.visit n_block.n_kwend
1487 else
1488 v.visit n_block.n_expr.first
1489 v.current_token = n_block.n_kwend
1490 v.skip
1491 end
1492 else
1493 v.visit n_block
1494 if v.current_token isa TKwend then v.skip
1495 end
1496
1497 if loop_label != null then
1498 v.adds
1499 v.visit loop_label
1500 end
1501 end
1502
1503 redef fun is_inlinable do
1504 var n_block = loop_block
1505 if not super then return false
1506 if n_block isa ABlockExpr and not n_block.is_inlinable then return false
1507 if not collect_comments.is_empty then return false
1508 return true
1509 end
1510 end
1511
1512 redef class ALoopExpr
1513 super ALoopHelper
1514
1515 redef fun loop_block do return n_block
1516 redef fun loop_label do return n_label
1517
1518 redef fun accept_pretty_printer(v) do
1519 var can_inline = v.can_inline(self)
1520 v.visit n_kwloop
1521 if can_inline then visit_loop_inline v else visit_loop_block v
1522 end
1523 end
1524
1525 redef class AWhileExpr
1526 super ALoopHelper
1527
1528 redef fun loop_block do return n_block
1529 redef fun loop_label do return n_label
1530
1531 redef fun accept_pretty_printer(v) do
1532 var can_inline = v.can_inline(self)
1533 v.visit n_kwwhile
1534 v.adds
1535 v.visit n_expr
1536 v.adds
1537 v.visit n_kwdo
1538 if can_inline then visit_loop_inline v else visit_loop_block v
1539 end
1540 end
1541
1542 redef class ADoExpr
1543 super ALoopHelper
1544
1545 redef fun loop_block do return n_block
1546 redef fun loop_label do return n_label
1547
1548 redef fun accept_pretty_printer(v) do
1549 var can_inline = v.can_inline(self)
1550 v.visit n_kwdo
1551 if can_inline then visit_loop_inline v else visit_loop_block v
1552 end
1553 end
1554
1555 redef class AForExpr
1556 super ALoopHelper
1557
1558 redef fun loop_block do return n_block
1559 redef fun loop_label do return n_label
1560
1561 redef fun accept_pretty_printer(v) do
1562 var can_inline = v.can_inline(self)
1563 v.visit n_kwfor
1564 v.adds
1565
1566 for n_id in n_ids do
1567 v.visit n_id
1568 if n_id != n_ids.last then v.add ", "
1569 end
1570
1571 v.adds
1572 v.consume "in"
1573 v.adds
1574 v.visit n_expr
1575 v.adds
1576 v.visit n_kwdo
1577 if can_inline then visit_loop_inline v else visit_loop_block v
1578 end
1579 end
1580
1581 redef class ABreakExpr
1582 redef fun accept_pretty_printer(v) do
1583 v.visit n_kwbreak
1584
1585 if n_expr != null then
1586 v.adds
1587 v.visit n_expr
1588 end
1589
1590 if n_label != null then
1591 v.adds
1592 v.visit n_label
1593 end
1594 end
1595
1596 redef fun is_inlinable do return true
1597 end
1598
1599 redef class AContinueExpr
1600 redef fun accept_pretty_printer(v) do
1601 v.visit n_kwcontinue
1602
1603 if n_expr != null then
1604 v.adds
1605 v.visit n_expr
1606 end
1607
1608 if n_label != null then
1609 v.adds
1610 v.visit n_label
1611 end
1612 end
1613
1614 redef fun is_inlinable do return true
1615 end
1616
1617 # Calls
1618
1619 redef class ASendExpr
1620 redef fun is_inlinable do return true
1621 end
1622
1623 redef class ACallExpr
1624 redef fun accept_pretty_printer(v) do
1625 var can_inline = v.can_inline(self)
1626 v.visit_recv n_expr
1627
1628 if not n_expr isa AImplicitSelfExpr and not can_inline then
1629 v.addn
1630 v.addt
1631 v.addt
1632 end
1633
1634 v.visit n_id
1635
1636 if not n_args.n_exprs.is_empty then
1637 if is_stmt and n_args.n_exprs.length == 1 then
1638 v.adds
1639 if v.current_token isa TOpar then v.skip
1640 v.visit n_args.n_exprs.first
1641 if v.current_token isa TCpar then v.skip
1642 else
1643 if v.current_token isa TOpar then
1644 v.consume "("
1645 else
1646 v.adds
1647 end
1648
1649 v.visit_list n_args.n_exprs
1650 if v.current_token isa TCpar then v.consume ")"
1651 end
1652 end
1653 end
1654
1655 # Is the call alone on its line?
1656 fun is_stmt: Bool do return parent isa ABlockExpr
1657 end
1658
1659 redef class ACallAssignExpr
1660 redef fun accept_pretty_printer(v) do
1661 v.visit_recv n_expr
1662 v.visit n_id
1663
1664 if not n_args.n_exprs.is_empty then
1665 v.consume "("
1666 v.visit_list n_args.n_exprs
1667 v.consume ")"
1668 end
1669
1670 v.adds
1671 v.consume "="
1672 v.adds
1673 v.visit n_value
1674 end
1675 end
1676
1677 redef class ACallReassignExpr
1678 redef fun accept_pretty_printer(v) do
1679 v.visit_recv n_expr
1680 v.visit n_id
1681
1682 if not n_args.n_exprs.is_empty then
1683 v.consume "("
1684 v.visit_list n_args.n_exprs
1685 v.consume ")"
1686 end
1687
1688 v.adds
1689 v.visit n_assign_op
1690 v.adds
1691 v.visit n_value
1692 end
1693 end
1694
1695 redef class ABraExpr
1696 redef fun accept_pretty_printer(v) do
1697 v.visit n_expr
1698
1699 if not n_args.n_exprs.is_empty then
1700 v.consume "["
1701 v.visit_list n_args.n_exprs
1702 v.consume "]"
1703 end
1704 end
1705 end
1706
1707 redef class ABraAssignExpr
1708 redef fun accept_pretty_printer(v) do
1709 v.visit n_expr
1710
1711 if not n_args.n_exprs.is_empty then
1712 v.consume "["
1713 v.visit_list n_args.n_exprs
1714 v.consume "]"
1715 end
1716
1717 v.adds
1718 v.visit n_assign
1719 v.adds
1720 v.visit n_value
1721 end
1722 end
1723
1724 redef class ABraReassignExpr
1725 redef fun accept_pretty_printer(v) do
1726 v.visit n_expr
1727
1728 if not n_args.n_exprs.is_empty then
1729 v.consume "["
1730 v.visit_list n_args.n_exprs
1731 v.consume "]"
1732 end
1733
1734 v.adds
1735 v.visit n_assign_op
1736 v.adds
1737 v.visit n_value
1738 end
1739 end
1740
1741 redef class AAssignMethid
1742 redef fun accept_pretty_printer(v) do
1743 v.visit n_id
1744 v.visit n_assign
1745 end
1746 end
1747
1748 redef class ABraMethid
1749 redef fun accept_pretty_printer(v) do
1750 v.visit n_obra
1751 v.visit n_cbra
1752 end
1753 end
1754
1755 redef class ABraassignMethid
1756 redef fun accept_pretty_printer(v) do
1757 v.visit n_obra
1758 v.visit n_cbra
1759 v.visit n_assign
1760 end
1761 end
1762
1763 redef class AInitExpr
1764 redef fun accept_pretty_printer(v) do
1765 if not n_expr isa AImplicitSelfExpr then
1766 v.visit n_expr
1767 v.consume "."
1768 end
1769
1770 v.visit n_kwinit
1771
1772 if not n_args.n_exprs.is_empty then
1773 v.consume "("
1774 v.visit_list n_args.n_exprs
1775 v.consume ")"
1776 end
1777 end
1778 end
1779
1780 redef class ANewExpr
1781 redef fun accept_pretty_printer(v) do
1782 var can_inline = v.can_inline(self)
1783 v.visit n_kwnew
1784 v.adds
1785 v.visit n_type
1786
1787 if n_id != null then
1788 v.consume "."
1789
1790 if not can_inline then
1791 v.addn
1792 v.addt
1793 v.addt
1794 end
1795
1796 v.visit n_id
1797 end
1798
1799 if not n_args.n_exprs.is_empty then
1800 v.consume "("
1801 v.visit_list n_args.n_exprs
1802 v.consume ")"
1803 end
1804 end
1805
1806 redef fun is_inlinable do return true
1807 end
1808
1809 # Attributes
1810
1811 redef class AAttrExpr
1812 redef fun accept_pretty_printer(v) do
1813 v.visit_recv n_expr
1814 v.visit n_id
1815 end
1816
1817 redef fun is_inlinable do return true
1818 end
1819
1820 redef class AAttrAssignExpr
1821 redef fun accept_pretty_printer(v) do
1822 v.visit_recv n_expr
1823 v.visit n_id
1824 v.adds
1825 v.visit n_assign
1826 v.adds
1827 v.visit n_value
1828 end
1829 end
1830
1831 redef class AAttrReassignExpr
1832 redef fun accept_pretty_printer(v) do
1833 v.visit_recv n_expr
1834 v.visit n_id
1835 v.adds
1836 v.visit n_assign_op
1837 v.adds
1838 v.visit n_value
1839 end
1840 end
1841
1842 # Exprs
1843
1844 redef class AVardeclExpr
1845 redef fun accept_pretty_printer(v) do
1846 v.visit n_kwvar
1847 v.adds
1848 v.visit n_id
1849
1850 if n_type != null then
1851 v.consume ":"
1852 v.adds
1853 v.visit n_type
1854 end
1855
1856 if n_expr != null then
1857 v.adds
1858 v.consume "="
1859 v.adds
1860 v.visit n_expr
1861 end
1862 end
1863
1864 redef fun is_inlinable do return true
1865 end
1866
1867 redef class AVarAssignExpr
1868 redef fun accept_pretty_printer(v) do
1869 v.visit n_id
1870 v.adds
1871 v.visit n_assign
1872 v.adds
1873 v.visit n_value
1874 end
1875 end
1876
1877 redef class AAssertExpr
1878 redef fun accept_pretty_printer(v) do
1879 var can_inline = v.can_inline(self)
1880 v.visit n_kwassert
1881
1882 if n_id != null then
1883 v.adds
1884 v.visit n_id
1885 v.consume ":"
1886 end
1887
1888 v.adds
1889 v.visit n_expr
1890 var n_else = self.n_else
1891
1892 if n_else != null then
1893 v.adds
1894 v.consume "else"
1895
1896 if can_inline then
1897 v.adds
1898 v.visit n_else
1899 else
1900 v.addn
1901
1902 if n_else isa ABlockExpr then
1903 v.indent += 1
1904 n_else.force_block = true
1905 v.visit n_else
1906 v.indent -= 1
1907 v.addt
1908 v.visit n_else.n_kwend
1909 else
1910 v.indent += 1
1911 v.addt
1912 v.visit n_else
1913 v.addn
1914 v.indent -= 1
1915 v.addt
1916 v.add "end"
1917 end
1918 end
1919 end
1920 end
1921
1922 redef fun is_inlinable do
1923 if not super then return false
1924 if n_else != null and not n_else.is_inlinable then return false
1925 return true
1926 end
1927 end
1928
1929 redef class AReturnExpr
1930 redef fun accept_pretty_printer(v) do
1931 v.visit n_kwreturn
1932
1933 if n_expr != null then
1934 v.adds
1935 v.visit n_expr
1936 end
1937 end
1938 end
1939
1940 redef class ASuperExpr
1941 redef fun accept_pretty_printer(v) do
1942 v.visit n_kwsuper
1943
1944 if not n_args.n_exprs.is_empty then
1945 if is_stmt and n_args.n_exprs.length == 1 then
1946 v.adds
1947 if v.current_token isa TOpar then v.skip
1948 v.visit n_args.n_exprs.first
1949 if v.current_token isa TCpar then v.skip
1950 else
1951 if v.current_token isa TOpar then
1952 v.consume "("
1953 else
1954 v.adds
1955 end
1956
1957 v.visit_list n_args.n_exprs
1958 if v.current_token isa TCpar then v.consume ")"
1959 end
1960 end
1961 end
1962
1963 # Is the call alone on its line?
1964 fun is_stmt: Bool do return self.first_token.is_starting_line
1965
1966 redef fun is_inlinable do return true
1967 end
1968
1969 redef class AOnceExpr
1970 redef fun accept_pretty_printer(v) do
1971 v.visit n_kwonce
1972 v.adds
1973 v.visit n_expr
1974 end
1975
1976 redef fun is_inlinable do return true
1977 end
1978
1979 redef class AAbortExpr
1980 redef fun accept_pretty_printer(v) do v.visit n_kwabort
1981 redef fun is_inlinable do return true
1982 end
1983
1984 redef class ANotExpr
1985 redef fun accept_pretty_printer(v) do
1986 v.visit n_kwnot
1987 v.adds
1988 v.visit n_expr
1989 end
1990 end
1991
1992 redef class AAsCastExpr
1993 redef fun accept_pretty_printer(v) do
1994 v.visit n_expr
1995 v.consume "."
1996 v.visit n_kwas
1997 v.visit n_opar
1998 v.visit n_type
1999 v.visit n_cpar
2000 end
2001 end
2002
2003 redef class AAsNotnullExpr
2004 redef fun accept_pretty_printer(v) do
2005 v.visit n_expr
2006 v.consume "."
2007 v.visit n_kwas
2008 v.visit n_opar
2009 v.visit n_kwnot
2010 v.adds
2011 v.visit n_kwnull
2012 v.visit n_cpar
2013 end
2014 end
2015
2016 # Binops
2017
2018 # Used to factorize work on Or, And, Implies and Binop expressions.
2019 private class ABinOpHelper
2020 super AExpr
2021
2022 fun bin_expr1: AExpr is abstract
2023 fun bin_expr2: AExpr is abstract
2024
2025 # Operator string
2026 fun bin_op: String is abstract
2027
2028 redef fun accept_pretty_printer(v) do
2029 var can_inline = v.can_inline(self)
2030
2031 if not can_inline then
2032 if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
2033 (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
2034 (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
2035 then
2036 bin_expr1.force_block = true
2037 end
2038 end
2039
2040 v.visit bin_expr1
2041 v.adds
2042 v.consume bin_op
2043
2044 if can_inline then
2045 v.adds
2046 v.visit bin_expr2
2047 else
2048 v.addn
2049 v.addt
2050 v.addt
2051 v.visit bin_expr2
2052 end
2053 end
2054 end
2055
2056 redef class AAndExpr
2057 super ABinOpHelper
2058
2059 redef fun bin_expr1 do return n_expr
2060 redef fun bin_expr2 do return n_expr2
2061 redef fun bin_op do return "and"
2062 end
2063
2064 redef class AOrExpr
2065 super ABinOpHelper
2066
2067 redef fun bin_expr1 do return n_expr
2068 redef fun bin_expr2 do return n_expr2
2069 redef fun bin_op do return "or"
2070 end
2071
2072 redef class AImpliesExpr
2073 super ABinOpHelper
2074
2075 redef fun bin_expr1 do return n_expr
2076 redef fun bin_expr2 do return n_expr2
2077 redef fun bin_op do return "implies"
2078 end
2079
2080 redef class ABinopExpr
2081 super ABinOpHelper
2082
2083 redef fun bin_expr1 do return n_expr
2084 redef fun bin_expr2 do return n_expr2
2085 end
2086
2087 redef class AEqExpr
2088 redef fun bin_op do return "=="
2089 end
2090
2091 redef class AGeExpr
2092 redef fun bin_op do return ">="
2093 end
2094
2095 redef class AGgExpr
2096 redef fun bin_op do return ">>"
2097 end
2098
2099 redef class AGtExpr
2100 redef fun bin_op do return ">"
2101 end
2102
2103 redef class ALeExpr
2104 redef fun bin_op do return "<="
2105 end
2106
2107 redef class ALlExpr
2108 redef fun bin_op do return "<<"
2109 end
2110
2111 redef class ALtExpr
2112 redef fun bin_op do return "<"
2113 end
2114
2115 redef class AMinusExpr
2116 redef fun bin_op do return "-"
2117 end
2118
2119 redef class ANeExpr
2120 redef fun bin_op do return "!="
2121 end
2122
2123 redef class APercentExpr
2124 redef fun bin_op do return "%"
2125 end
2126
2127 redef class APlusExpr
2128 redef fun bin_op do return "+"
2129 end
2130
2131 redef class ASlashExpr
2132 redef fun bin_op do return "/"
2133 end
2134
2135 redef class AStarExpr
2136 redef fun bin_op do return "*"
2137 end
2138
2139 redef class AStarstarExpr
2140 redef fun bin_op do return "**"
2141 end
2142
2143 redef class AStarshipExpr
2144 redef fun bin_op do return "<=>"
2145 end
2146
2147 redef class AIsaExpr
2148 redef fun accept_pretty_printer(v) do
2149 v.visit n_expr
2150 v.adds
2151 v.consume "isa"
2152 v.adds
2153 v.visit n_type
2154 end
2155 end
2156
2157 redef class AOrElseExpr
2158 redef fun accept_pretty_printer(v) do
2159 v.visit n_expr
2160 v.adds
2161 v.consume "or"
2162 v.adds
2163 v.consume "else"
2164 v.adds
2165 v.visit n_expr2
2166 end
2167
2168 redef fun is_inlinable do return true
2169 end
2170
2171 # Syntax
2172
2173 redef class AUminusExpr
2174 redef fun accept_pretty_printer(v) do
2175 v.consume "-"
2176 v.visit n_expr
2177 end
2178 end
2179
2180 redef class ANullExpr
2181 redef fun accept_pretty_printer(v) do v.visit n_kwnull
2182 redef fun is_inlinable do return true
2183 end
2184
2185 redef class AParExpr
2186 redef fun accept_pretty_printer(v) do
2187 v.visit n_opar
2188 v.visit n_expr
2189 v.visit n_cpar
2190 end
2191 end
2192
2193 redef class AArrayExpr
2194 redef fun accept_pretty_printer(v) do
2195 v.consume "["
2196 v.visit_list n_exprs.n_exprs
2197 v.consume "]"
2198 end
2199 end
2200
2201 redef class ACrangeExpr
2202 redef fun accept_pretty_printer(v) do
2203 v.visit n_obra
2204 v.visit n_expr
2205 v.consume ".."
2206 v.visit n_expr2
2207 v.visit n_cbra
2208 end
2209 end
2210
2211 redef class AOrangeExpr
2212 redef fun accept_pretty_printer(v) do
2213 v.visit n_obra
2214 v.visit n_expr
2215 v.consume ".."
2216 v.visit n_expr2
2217 v.visit n_cbra
2218 end
2219 end
2220
2221 # Strings
2222
2223 redef class AStringFormExpr
2224 redef fun accept_pretty_printer(v) do
2225 var can_inline = v.can_inline(self)
2226
2227 if can_inline then
2228 v.visit n_string
2229 else
2230 var text = n_string.text
2231 var i = 0
2232
2233 while i < text.length do
2234 v.add text[i].to_s
2235
2236 if v.current_length >= v.max_size and i <= text.length - 3 then
2237 v.add "\" +"
2238 v.addn
2239 v.indent += 1
2240 v.addt
2241 v.indent -= 1
2242 v.add "\""
2243 end
2244
2245 i += 1
2246 end
2247
2248 v.current_token = n_string.next_token
2249 end
2250 end
2251 end
2252
2253 redef class ASuperstringExpr
2254 redef fun accept_pretty_printer(v) do
2255 for n_expr in n_exprs do v.visit n_expr
2256 end
2257
2258 redef fun must_be_inline do
2259 if super then return true
2260
2261 if not n_exprs.is_empty then
2262 var first = n_exprs.first
2263 return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
2264 end
2265
2266 return false
2267 end
2268 end
2269
2270 redef class ToolContext
2271 var opt_dir = new OptionString("Working directory (default is '.nitpretty')", "--dir")
2272
2273 var opt_output = new OptionString("Output name (default is pretty.nit)", "-o",
2274 "--output")
2275
2276 var opt_diff = new OptionBool("Show diff between source and output", "--diff")
2277
2278 var opt_meld = new OptionBool("Show diff between source and output using meld",
2279 "--meld")
2280
2281 var opt_check = new OptionBool("Check format of Nit source files", "--check")
2282 end
2283
2284 # Return result from diff between `file1` and `file2`.
2285 private fun diff(file1, file2: String): String do
2286 var p = new IProcess("diff", "-u", file1, file2)
2287 var res = p.read_all
2288 p.wait
2289 p.close
2290 return res
2291 end
2292
2293 # process options
2294 var toolcontext = new ToolContext
2295
2296 toolcontext.option_context.
2297 add_option(toolcontext.opt_dir, toolcontext.opt_output, toolcontext.opt_diff,
2298 toolcontext.opt_meld, toolcontext.opt_check)
2299
2300 toolcontext.tooldescription = "Usage: nitpretty [OPTION]... <file.nit>\n" +
2301 "Pretty print Nit code from Nit source files."
2302
2303 toolcontext.process_options args
2304 var arguments = toolcontext.option_context.rest
2305 # build model
2306 var model = new Model
2307 var mbuilder = new ModelBuilder(model, toolcontext)
2308 var mmodules = mbuilder.parse(arguments)
2309 mbuilder.run_phases
2310
2311 if mmodules.is_empty then
2312 print "Error: no module to pretty print"
2313 return
2314 end
2315
2316 if not toolcontext.opt_check.value and mmodules.length > 1 then
2317 print "Error: only --check option allow multiple modules"
2318 return
2319 end
2320
2321 var dir = toolcontext.opt_dir.value or else ".nitpretty"
2322 if not dir.file_exists then dir.mkdir
2323 var v = new PrettyPrinterVisitor
2324
2325 for mmodule in mmodules do
2326 if not mbuilder.mmodule2nmodule.has_key(mmodule) then
2327 print " Error: no source file for module {mmodule}"
2328 return
2329 end
2330
2331 var nmodule = mbuilder.mmodule2nmodule[mmodule]
2332 var file = "{dir}/{mmodule.name}.nit"
2333 var tpl = v.pretty_nmodule(nmodule)
2334 tpl.write_to_file file
2335
2336 if toolcontext.opt_check.value then
2337 var res = diff(nmodule.location.file.filename, file)
2338
2339 if not res.is_empty then
2340 print "Wrong formating for module {nmodule.location.file.filename}"
2341 toolcontext.info(res, 1)
2342
2343 if toolcontext.opt_meld.value then
2344 sys.system "meld {nmodule.location.file.filename} {file}"
2345 end
2346 else
2347 toolcontext.info("[OK] {nmodule.location.file.filename}", 1)
2348 end
2349 else
2350 # write to file
2351 var out = toolcontext.opt_output.value
2352 if out != null then sys.system "cp {file} {out}"
2353
2354 # open in meld
2355 if toolcontext.opt_meld.value then
2356 sys.system "meld {arguments.first} {file}"
2357 return
2358 end
2359
2360 # show diff
2361 if toolcontext.opt_diff.value then
2362 var res = diff(arguments.first, file)
2363 if not res.is_empty then print res
2364 return
2365 end
2366
2367 # show pretty
2368 if not toolcontext.opt_quiet.value then tpl.write_to sys.stdout
2369 end
2370 end