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