nitpretty: factorizes annotation management for properties
[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 # Factorize annotations visit for all APropdef.
776 fun visit_annotations(v: PrettyPrinterVisitor, n_annotations: nullable AAnnotations) do
777 if n_annotations != null then v.visit n_annotations
778 end
779
780 redef fun start_token do
781 if n_doc == null then return super
782 return n_doc.last_token.next_token
783 end
784 end
785
786 redef class AAttrPropdef
787 redef fun accept_pretty_printer(v) do
788 super
789 v.visit n_kwvar
790 v.adds
791 v.visit n_id2
792
793 if n_type != null then
794 v.consume ":"
795 v.adds
796 v.visit n_type
797 end
798
799 if n_expr != null then
800 v.adds
801 v.consume "="
802 v.adds
803 v.visit n_expr
804 end
805
806 visit_annotations(v, n_annotations)
807 v.finish_line
808 v.addn
809 end
810
811 redef fun first_token do
812 if n_doc != null then return n_doc.first_token
813 if not n_visibility isa APublicVisibility then return n_visibility.first_token
814 if n_kwredef != null then return n_kwredef
815 return n_kwvar
816 end
817
818 redef fun is_inlinable do return true
819 end
820
821 redef class ATypePropdef
822 redef fun accept_pretty_printer(v) do
823 super
824 v.visit n_kwtype
825 v.adds
826 v.visit n_id
827 v.consume ":"
828 v.adds
829 v.visit n_type
830 visit_annotations(v, n_annotations)
831 v.finish_line
832 v.addn
833 end
834
835 redef fun is_inlinable do return true
836 end
837
838 redef class AMethPropdef
839 redef fun accept_pretty_printer(v) do
840 # TODO: Handle extern annotations
841
842 var before = v.indent
843 super
844 if n_kwinit != null then v.visit n_kwinit
845 if n_kwmeth != null then v.visit n_kwmeth
846 if n_kwnew != null then v.visit n_kwnew
847
848 if not n_methid == null then
849 v.adds
850 v.visit n_methid
851 end
852
853 v.visit n_signature
854
855 var annot_inline = v.can_inline(n_annotations)
856 visit_annotations(v, n_annotations)
857
858 if n_extern_calls != null or n_extern_code_block != null then
859 v.adds
860 if n_extern_calls != null then v.visit n_extern_calls
861 if n_extern_code_block != null then v.visit n_extern_code_block
862 end
863
864 var n_block = self.n_block
865
866 if n_block != null then
867 while not v.current_token isa TKwdo do v.skip
868 if n_annotations != null and not annot_inline then
869 v.addt
870 else
871 v.adds
872 end
873 v.consume "do"
874
875 if v.can_inline(n_block) then
876 v.adds
877
878 if n_block isa ABlockExpr then
879 if n_block.n_expr.is_empty then
880 v.visit n_block.n_kwend
881 else
882 v.visit n_block.n_expr.first
883 v.current_token = n_block.n_kwend
884 v.skip
885 end
886 else
887 v.visit n_block
888 if v.current_token isa TKwend then v.skip
889 end
890 else
891 v.finish_line
892 v.addn
893 v.indent += 1
894
895 if n_block isa ABlockExpr then
896 n_block.force_block = true
897 v.visit n_block
898 v.catch_up n_block.n_kwend
899 else
900 v.addt
901 v.visit n_block
902 v.addn
903 end
904
905 v.indent -= 1
906 v.addt
907 if n_block isa ABlockExpr then
908 v.visit n_block.n_kwend
909 else
910 v.add "end"
911 end
912 end
913 v.finish_line
914 end
915
916 v.addn
917 assert v.indent == before
918 end
919
920 # Can be inlined if:
921 # * block is empty or can be inlined
922 # * contains no comments
923 redef fun is_inlinable do
924 if not super then return false
925 if n_annotations != null and not n_annotations.is_inlinable then return false
926 if n_block != null and not n_block.is_inlinable then return false
927 if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
928 if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
929 if not collect_comments.is_empty then return false
930 return true
931 end
932 end
933
934 redef class AMainMethPropdef
935 redef fun accept_pretty_printer(v) do
936 v.visit n_block
937 v.addn
938 end
939 end
940
941 redef class ASignature
942 redef fun accept_pretty_printer(v) do
943 if not n_params.is_empty then
944 v.consume "("
945 v.visit_list n_params
946 v.consume ")"
947 end
948
949 if n_type != null then
950 v.consume ":"
951 v.adds
952 v.visit n_type
953 end
954 end
955 end
956
957 redef class AParam
958 redef fun accept_pretty_printer(v) do
959 v.visit n_id
960
961 if n_type != null then
962 v.consume ":"
963 v.adds
964 v.visit n_type
965 end
966
967 if n_dotdotdot != null then v.visit n_dotdotdot
968 end
969 end
970
971 # Extern
972
973 redef class AExternCalls
974 redef fun accept_pretty_printer(v) do
975 var can_inline = v.can_inline(self)
976 v.visit n_kwimport
977
978 if can_inline then
979 v.adds
980 v.visit_list n_extern_calls
981 else
982 v.addn
983 v.addt
984 v.addt
985 v.visit_list n_extern_calls
986 end
987
988 v.adds
989 end
990 end
991
992 redef class AFullPropExternCall
993 redef fun accept_pretty_printer(v) do
994 v.visit n_type
995 v.visit n_dot
996 v.visit n_methid
997 end
998 end
999
1000 redef class ALocalPropExternCall
1001 redef fun accept_pretty_printer(v) do v.visit n_methid
1002 end
1003
1004 redef class AInitPropExternCall
1005 redef fun accept_pretty_printer(v) do v.visit n_type
1006 end
1007
1008 redef class ACastAsExternCall
1009 redef fun accept_pretty_printer(v) do
1010 v.visit n_from_type
1011 v.visit n_dot
1012 v.visit n_kwas
1013 v.consume "("
1014 v.visit n_to_type
1015 v.consume ")"
1016 end
1017 end
1018
1019 redef class AAsNullableExternCall
1020 redef fun accept_pretty_printer(v) do
1021 v.visit n_type
1022 v.consume "."
1023 v.visit n_kwas
1024 v.adds
1025 v.visit n_kwnullable
1026 end
1027 end
1028
1029 redef class AAsNotNullableExternCall
1030 redef fun accept_pretty_printer(v) do
1031 v.visit n_type
1032 v.consume "."
1033 v.visit n_kwas
1034 v.adds
1035 v.visit n_kwnot
1036 v.adds
1037 v.visit n_kwnullable
1038 end
1039 end
1040
1041 redef class AExternCodeBlock
1042 redef fun accept_pretty_printer(v) do
1043 if n_in_language != null then
1044 v.visit n_in_language
1045 v.adds
1046 end
1047
1048 v.visit n_extern_code_segment
1049 end
1050
1051 redef fun is_inlinable do
1052 if not super then return false
1053 return n_extern_code_segment.is_inlinable
1054 end
1055 end
1056
1057 redef class AInLanguage
1058 redef fun accept_pretty_printer(v) do
1059 v.visit n_kwin
1060 v.adds
1061 v.visit n_string
1062 end
1063 end
1064
1065 redef class TExternCodeSegment
1066 redef fun accept_pretty_printer(v) do
1067 var can_inline = v.can_inline(self)
1068
1069 if can_inline then
1070 super
1071 else
1072 var text = text.substring(2, text.length - 4)
1073 var lines = text.r_trim.split("\n")
1074
1075 if text.is_empty then
1076 v.add "`\{`\}"
1077 else
1078 v.add "`\{"
1079
1080 if not lines.first.trim.is_empty then
1081 v.addn
1082 lines.first.l_trim
1083 v.indent += 1
1084 v.addt
1085 v.indent -= 1
1086 end
1087
1088 for line in lines do
1089 v.add line.r_trim
1090 v.addn
1091 end
1092
1093 v.addt
1094 v.add "`\}"
1095 end
1096
1097 v.current_token = next_token
1098 end
1099 end
1100
1101 redef fun is_inlinable do
1102 if not super then return false
1103 return location.line_start == location.line_end
1104 end
1105 end
1106
1107 # Blocks
1108
1109 redef class ABlockExpr
1110 redef fun accept_pretty_printer(v) do
1111 var before = v.indent
1112 var can_inline = v.can_inline(self)
1113
1114 if can_inline and not n_expr.is_empty then
1115 v.visit n_expr.first
1116 v.finish_line
1117 else
1118 for nexpr in n_expr do
1119 var expr_inline = v.can_inline(nexpr)
1120 if not expr_inline and nexpr != n_expr.first then v.addn
1121 v.catch_up nexpr
1122 v.addt
1123 v.visit nexpr
1124 v.finish_line
1125 v.addn
1126 if not expr_inline and nexpr != n_expr.last then v.addn
1127 end
1128 end
1129
1130 assert v.indent == before
1131 end
1132
1133 redef fun is_inlinable do
1134 if not super then return false
1135 if not collect_comments.is_empty then return false
1136
1137 if not n_expr.is_empty then
1138 if n_expr.length > 1 then return false
1139 if not n_expr.first.is_inlinable then return false
1140 end
1141
1142 return true
1143 end
1144 end
1145
1146 redef class AIfExpr
1147 redef fun accept_pretty_printer(v) do
1148 var before = v.indent
1149 var can_inline = v.can_inline(self)
1150 v.visit n_kwif
1151 v.adds
1152
1153 if v.can_inline(n_expr) then
1154 v.visit n_expr
1155 v.adds
1156 else
1157 v.visit n_expr
1158 v.addn
1159 v.addt
1160 end
1161
1162 # skip comments before `then` token
1163 while not v.current_token isa TKwthen do v.skip
1164 v.consume "then"
1165 var n_else = self.n_else
1166
1167 if can_inline then
1168 v.adds
1169 if n_then != null then v.visit n_then
1170
1171 if has_else(v) then
1172 n_else.force_inline = true
1173 v.adds
1174 v.consume "else"
1175 v.adds
1176 v.visit n_else
1177 else if n_then == null then
1178 v.add "end"
1179 end
1180
1181 v.skip_to last_token.last_real_token_in_line
1182 else
1183 v.finish_line
1184 v.addn
1185 v.indent += 1
1186
1187 if n_then != null then
1188 if n_then isa ABlockExpr then
1189 n_then.force_block = true
1190 v.visit n_then
1191 else
1192 v.addt
1193 v.visit n_then
1194 v.addn
1195 end
1196 end
1197
1198 if has_else(v) then
1199 while not v.current_token isa TKwelse do
1200 v.consume v.current_token.text
1201 end
1202
1203 v.indent -= 1
1204 v.addt
1205 v.consume "else"
1206
1207 if n_else isa AIfExpr then
1208 n_else.force_block = true
1209 v.adds
1210 v.visit n_else
1211 else
1212 v.finish_line
1213 v.addn
1214 v.indent += 1
1215
1216 if n_else isa ABlockExpr then
1217 n_else.force_block = true
1218 v.visit n_else
1219 else
1220 v.addt
1221 v.visit n_else
1222 v.addn
1223 end
1224
1225 if last_token isa TKwend then
1226 v.catch_up last_token
1227 v.indent -= 1
1228 v.addt
1229 v.consume "end"
1230 else
1231 v.indent -= 1
1232 v.addt
1233 v.add "end"
1234 end
1235 end
1236 else
1237 if last_token.location >= v.current_token.location then v.catch_up last_token
1238 v.indent -= 1
1239 v.addt
1240 v.add "end"
1241 if v.current_token isa TKwend then v.skip
1242 end
1243 end
1244
1245 assert v.indent == before
1246 end
1247
1248 redef fun is_inlinable do
1249 if not super then return false
1250 if n_then != null and not n_then.is_inlinable then return false
1251 var n_else = self.n_else
1252 if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
1253 (not n_else isa ABlockExpr and n_else != null) then
1254 return false
1255 end
1256 if not collect_comments.is_empty then return false
1257 return true
1258 end
1259
1260 # Does this `if` statement contains a `else` part?
1261 private fun has_else(v: PrettyPrinterVisitor): Bool do
1262 var n_else = n_else
1263 if n_else == null then return false
1264 var n_kwelse = collect_kwelse
1265 if n_kwelse == null then return false
1266
1267 if n_else isa ABlockExpr then
1268 var comments: Array[TComment]
1269
1270 if n_then == null then
1271 comments = v.collect_comments(n_expr.last_token, n_else.last_token)
1272 else
1273 comments = v.collect_comments(n_then.last_token, n_else.last_token)
1274 end
1275
1276 if not comments.is_empty then return true
1277 return not n_else.n_expr.is_empty
1278 end
1279
1280 return true
1281 end
1282
1283 # Lookup for `else` token in `self`.
1284 private fun collect_kwelse: nullable TKwelse do
1285 var token = first_token
1286
1287 while token != last_token do
1288 if token isa TKwelse then return token
1289 token = token.next_token
1290 end
1291
1292 return null
1293 end
1294 end
1295
1296 # Used to factorize work on loops.
1297 private class ALoopHelper
1298 super AExpr
1299
1300 fun loop_block: nullable ANode is abstract
1301 fun loop_label: nullable ANode is abstract
1302
1303 fun visit_loop_block(v: PrettyPrinterVisitor) do
1304 var n_block = loop_block
1305 v.finish_line
1306 v.addn
1307 v.indent += 1
1308
1309 if n_block isa ABlockExpr then
1310 n_block.force_block = true
1311 v.visit n_block
1312 v.catch_up n_block.n_kwend
1313 v.indent -= 1
1314 v.addt
1315 v.visit n_block.n_kwend
1316 else
1317 v.addt
1318 v.visit n_block
1319 v.addn
1320 v.indent -= 1
1321 v.addt
1322 v.add "end"
1323 end
1324
1325 if loop_label != null then
1326 v.adds
1327 v.visit loop_label
1328 end
1329 end
1330
1331 fun visit_loop_inline(v: PrettyPrinterVisitor) do
1332 var n_block = loop_block
1333 v.adds
1334
1335 if n_block isa ABlockExpr then
1336 if n_block.n_expr.is_empty then
1337 v.visit n_block.n_kwend
1338 else
1339 v.visit n_block.n_expr.first
1340 v.current_token = n_block.n_kwend
1341 v.skip
1342 end
1343 else
1344 v.visit n_block
1345 if v.current_token isa TKwend then v.skip
1346 end
1347
1348 if loop_label != null then
1349 v.adds
1350 v.visit loop_label
1351 end
1352 end
1353
1354 redef fun is_inlinable do
1355 var n_block = loop_block
1356 if not super then return false
1357 if n_block isa ABlockExpr and not n_block.is_inlinable then return false
1358 if not collect_comments.is_empty then return false
1359 return true
1360 end
1361 end
1362
1363 redef class ALoopExpr
1364 super ALoopHelper
1365
1366 redef fun loop_block do return n_block
1367 redef fun loop_label do return n_label
1368
1369 redef fun accept_pretty_printer(v) do
1370 var can_inline = v.can_inline(self)
1371 v.visit n_kwloop
1372 if can_inline then visit_loop_inline v else visit_loop_block v
1373 end
1374 end
1375
1376 redef class AWhileExpr
1377 super ALoopHelper
1378
1379 redef fun loop_block do return n_block
1380 redef fun loop_label do return n_label
1381
1382 redef fun accept_pretty_printer(v) do
1383 var can_inline = v.can_inline(self)
1384 v.visit n_kwwhile
1385 v.adds
1386 v.visit n_expr
1387 v.adds
1388 v.visit n_kwdo
1389 if can_inline then visit_loop_inline v else visit_loop_block v
1390 end
1391 end
1392
1393 redef class ADoExpr
1394 super ALoopHelper
1395
1396 redef fun loop_block do return n_block
1397 redef fun loop_label do return n_label
1398
1399 redef fun accept_pretty_printer(v) do
1400 var can_inline = v.can_inline(self)
1401 v.visit n_kwdo
1402 if can_inline then visit_loop_inline v else visit_loop_block v
1403 end
1404 end
1405
1406 redef class AForExpr
1407 super ALoopHelper
1408
1409 redef fun loop_block do return n_block
1410 redef fun loop_label do return n_label
1411
1412 redef fun accept_pretty_printer(v) do
1413 var can_inline = v.can_inline(self)
1414 v.visit n_kwfor
1415 v.adds
1416
1417 for n_id in n_ids do
1418 v.visit n_id
1419 if n_id != n_ids.last then v.add ", "
1420 end
1421
1422 v.adds
1423 v.consume "in"
1424 v.adds
1425 v.visit n_expr
1426 v.adds
1427 v.visit n_kwdo
1428 if can_inline then visit_loop_inline v else visit_loop_block v
1429 end
1430 end
1431
1432 redef class ABreakExpr
1433 redef fun accept_pretty_printer(v) do
1434 v.visit n_kwbreak
1435
1436 if n_expr != null then
1437 v.adds
1438 v.visit n_expr
1439 end
1440
1441 if n_label != null then
1442 v.adds
1443 v.visit n_label
1444 end
1445 end
1446
1447 redef fun is_inlinable do return true
1448 end
1449
1450 redef class AContinueExpr
1451 redef fun accept_pretty_printer(v) do
1452 v.visit n_kwcontinue
1453
1454 if n_expr != null then
1455 v.adds
1456 v.visit n_expr
1457 end
1458
1459 if n_label != null then
1460 v.adds
1461 v.visit n_label
1462 end
1463 end
1464
1465 redef fun is_inlinable do return true
1466 end
1467
1468 # Calls
1469
1470 redef class ASendExpr
1471 redef fun is_inlinable do return true
1472 end
1473
1474 redef class ACallExpr
1475 redef fun accept_pretty_printer(v) do
1476 var can_inline = v.can_inline(self)
1477 v.visit_recv n_expr
1478
1479 if not n_expr isa AImplicitSelfExpr and not can_inline then
1480 v.addn
1481 v.addt
1482 v.addt
1483 end
1484
1485 v.visit n_id
1486
1487 if not n_args.n_exprs.is_empty then
1488 if is_stmt and n_args.n_exprs.length == 1 then
1489 v.adds
1490 if v.current_token isa TOpar then v.skip
1491 v.visit n_args.n_exprs.first
1492 if v.current_token isa TCpar then v.skip
1493 else
1494 if v.current_token isa TOpar then
1495 v.consume "("
1496 else
1497 v.adds
1498 end
1499
1500 v.visit_list n_args.n_exprs
1501 if v.current_token isa TCpar then v.consume ")"
1502 end
1503 end
1504 end
1505
1506 # Is the call alone on its line?
1507 fun is_stmt: Bool do return parent isa ABlockExpr
1508 end
1509
1510 redef class ACallAssignExpr
1511 redef fun accept_pretty_printer(v) do
1512 v.visit_recv n_expr
1513 v.visit n_id
1514
1515 if not n_args.n_exprs.is_empty then
1516 v.consume "("
1517 v.visit_list n_args.n_exprs
1518 v.consume ")"
1519 end
1520
1521 v.adds
1522 v.consume "="
1523 v.adds
1524 v.visit n_value
1525 end
1526 end
1527
1528 redef class ACallReassignExpr
1529 redef fun accept_pretty_printer(v) do
1530 v.visit_recv n_expr
1531 v.visit n_id
1532
1533 if not n_args.n_exprs.is_empty then
1534 v.consume "("
1535 v.visit_list n_args.n_exprs
1536 v.consume ")"
1537 end
1538
1539 v.adds
1540 v.visit n_assign_op
1541 v.adds
1542 v.visit n_value
1543 end
1544 end
1545
1546 redef class ABraExpr
1547 redef fun accept_pretty_printer(v) do
1548 v.visit n_expr
1549
1550 if not n_args.n_exprs.is_empty then
1551 v.consume "["
1552 v.visit_list n_args.n_exprs
1553 v.consume "]"
1554 end
1555 end
1556 end
1557
1558 redef class ABraAssignExpr
1559 redef fun accept_pretty_printer(v) do
1560 v.visit n_expr
1561
1562 if not n_args.n_exprs.is_empty then
1563 v.consume "["
1564 v.visit_list n_args.n_exprs
1565 v.consume "]"
1566 end
1567
1568 v.adds
1569 v.visit n_assign
1570 v.adds
1571 v.visit n_value
1572 end
1573 end
1574
1575 redef class ABraReassignExpr
1576 redef fun accept_pretty_printer(v) do
1577 v.visit n_expr
1578
1579 if not n_args.n_exprs.is_empty then
1580 v.consume "["
1581 v.visit_list n_args.n_exprs
1582 v.consume "]"
1583 end
1584
1585 v.adds
1586 v.visit n_assign_op
1587 v.adds
1588 v.visit n_value
1589 end
1590 end
1591
1592 redef class AAssignMethid
1593 redef fun accept_pretty_printer(v) do
1594 v.visit n_id
1595 v.visit n_assign
1596 end
1597 end
1598
1599 redef class ABraMethid
1600 redef fun accept_pretty_printer(v) do
1601 v.visit n_obra
1602 v.visit n_cbra
1603 end
1604 end
1605
1606 redef class ABraassignMethid
1607 redef fun accept_pretty_printer(v) do
1608 v.visit n_obra
1609 v.visit n_cbra
1610 v.visit n_assign
1611 end
1612 end
1613
1614 redef class AInitExpr
1615 redef fun accept_pretty_printer(v) do
1616 if not n_expr isa AImplicitSelfExpr then
1617 v.visit n_expr
1618 v.consume "."
1619 end
1620
1621 v.visit n_kwinit
1622
1623 if not n_args.n_exprs.is_empty then
1624 v.consume "("
1625 v.visit_list n_args.n_exprs
1626 v.consume ")"
1627 end
1628 end
1629 end
1630
1631 redef class ANewExpr
1632 redef fun accept_pretty_printer(v) do
1633 var can_inline = v.can_inline(self)
1634 v.visit n_kwnew
1635 v.adds
1636 v.visit n_type
1637
1638 if n_id != null then
1639 v.consume "."
1640
1641 if not can_inline then
1642 v.addn
1643 v.addt
1644 v.addt
1645 end
1646
1647 v.visit n_id
1648 end
1649
1650 if not n_args.n_exprs.is_empty then
1651 v.consume "("
1652 v.visit_list n_args.n_exprs
1653 v.consume ")"
1654 end
1655 end
1656
1657 redef fun is_inlinable do return true
1658 end
1659
1660 # Attributes
1661
1662 redef class AAttrExpr
1663 redef fun accept_pretty_printer(v) do
1664 v.visit_recv n_expr
1665 v.visit n_id
1666 end
1667
1668 redef fun is_inlinable do return true
1669 end
1670
1671 redef class AAttrAssignExpr
1672 redef fun accept_pretty_printer(v) do
1673 v.visit_recv n_expr
1674 v.visit n_id
1675 v.adds
1676 v.visit n_assign
1677 v.adds
1678 v.visit n_value
1679 end
1680 end
1681
1682 redef class AAttrReassignExpr
1683 redef fun accept_pretty_printer(v) do
1684 v.visit_recv n_expr
1685 v.visit n_id
1686 v.adds
1687 v.visit n_assign_op
1688 v.adds
1689 v.visit n_value
1690 end
1691 end
1692
1693 # Exprs
1694
1695 redef class AVardeclExpr
1696 redef fun accept_pretty_printer(v) do
1697 v.visit n_kwvar
1698 v.adds
1699 v.visit n_id
1700
1701 if n_type != null then
1702 v.consume ":"
1703 v.adds
1704 v.visit n_type
1705 end
1706
1707 if n_expr != null then
1708 v.adds
1709 v.consume "="
1710 v.adds
1711 v.visit n_expr
1712 end
1713 end
1714
1715 redef fun is_inlinable do return true
1716 end
1717
1718 redef class AVarAssignExpr
1719 redef fun accept_pretty_printer(v) do
1720 v.visit n_id
1721 v.adds
1722 v.visit n_assign
1723 v.adds
1724 v.visit n_value
1725 end
1726 end
1727
1728 redef class AAssertExpr
1729 redef fun accept_pretty_printer(v) do
1730 var can_inline = v.can_inline(self)
1731 v.visit n_kwassert
1732
1733 if n_id != null then
1734 v.adds
1735 v.visit n_id
1736 v.consume ":"
1737 end
1738
1739 v.adds
1740 v.visit n_expr
1741 var n_else = self.n_else
1742
1743 if n_else != null then
1744 v.adds
1745 v.consume "else"
1746
1747 if can_inline then
1748 v.adds
1749 v.visit n_else
1750 else
1751 v.addn
1752
1753 if n_else isa ABlockExpr then
1754 v.indent += 1
1755 n_else.force_block = true
1756 v.visit n_else
1757 v.indent -= 1
1758 v.addt
1759 v.visit n_else.n_kwend
1760 else
1761 v.indent += 1
1762 v.addt
1763 v.visit n_else
1764 v.addn
1765 v.indent -= 1
1766 v.addt
1767 v.add "end"
1768 end
1769 end
1770 end
1771 end
1772
1773 redef fun is_inlinable do
1774 if not super then return false
1775 if n_else != null and not n_else.is_inlinable then return false
1776 return true
1777 end
1778 end
1779
1780 redef class AReturnExpr
1781 redef fun accept_pretty_printer(v) do
1782 v.visit n_kwreturn
1783
1784 if n_expr != null then
1785 v.adds
1786 v.visit n_expr
1787 end
1788 end
1789 end
1790
1791 redef class ASuperExpr
1792 redef fun accept_pretty_printer(v) do
1793 v.visit n_kwsuper
1794
1795 if not n_args.n_exprs.is_empty then
1796 if is_stmt and n_args.n_exprs.length == 1 then
1797 v.adds
1798 if v.current_token isa TOpar then v.skip
1799 v.visit n_args.n_exprs.first
1800 if v.current_token isa TCpar then v.skip
1801 else
1802 if v.current_token isa TOpar then
1803 v.consume "("
1804 else
1805 v.adds
1806 end
1807
1808 v.visit_list n_args.n_exprs
1809 if v.current_token isa TCpar then v.consume ")"
1810 end
1811 end
1812 end
1813
1814 # Is the call alone on its line?
1815 fun is_stmt: Bool do return self.first_token.is_starting_line
1816
1817 redef fun is_inlinable do return true
1818 end
1819
1820 redef class AOnceExpr
1821 redef fun accept_pretty_printer(v) do
1822 v.visit n_kwonce
1823 v.adds
1824 v.visit n_expr
1825 end
1826
1827 redef fun is_inlinable do return true
1828 end
1829
1830 redef class AAbortExpr
1831 redef fun accept_pretty_printer(v) do v.visit n_kwabort
1832 redef fun is_inlinable do return true
1833 end
1834
1835 redef class ANotExpr
1836 redef fun accept_pretty_printer(v) do
1837 v.visit n_kwnot
1838 v.adds
1839 v.visit n_expr
1840 end
1841 end
1842
1843 redef class AAsCastExpr
1844 redef fun accept_pretty_printer(v) do
1845 v.visit n_expr
1846 v.consume "."
1847 v.visit n_kwas
1848 v.visit n_opar
1849 v.visit n_type
1850 v.visit n_cpar
1851 end
1852 end
1853
1854 redef class AAsNotnullExpr
1855 redef fun accept_pretty_printer(v) do
1856 v.visit n_expr
1857 v.consume "."
1858 v.visit n_kwas
1859 v.visit n_opar
1860 v.visit n_kwnot
1861 v.adds
1862 v.visit n_kwnull
1863 v.visit n_cpar
1864 end
1865 end
1866
1867 # Binops
1868
1869 # Used to factorize work on Or, And, Implies and Binop expressions.
1870 private class ABinOpHelper
1871 super AExpr
1872
1873 fun bin_expr1: AExpr is abstract
1874 fun bin_expr2: AExpr is abstract
1875
1876 # Operator string
1877 fun bin_op: String is abstract
1878
1879 redef fun accept_pretty_printer(v) do
1880 var can_inline = v.can_inline(self)
1881
1882 if not can_inline then
1883 if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
1884 (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
1885 (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
1886 then
1887 bin_expr1.force_block = true
1888 end
1889 end
1890
1891 v.visit bin_expr1
1892 v.adds
1893 v.consume bin_op
1894
1895 if can_inline then
1896 v.adds
1897 v.visit bin_expr2
1898 else
1899 v.addn
1900 v.addt
1901 v.addt
1902 v.visit bin_expr2
1903 end
1904 end
1905 end
1906
1907 redef class AAndExpr
1908 super ABinOpHelper
1909
1910 redef fun bin_expr1 do return n_expr
1911 redef fun bin_expr2 do return n_expr2
1912 redef fun bin_op do return "and"
1913 end
1914
1915 redef class AOrExpr
1916 super ABinOpHelper
1917
1918 redef fun bin_expr1 do return n_expr
1919 redef fun bin_expr2 do return n_expr2
1920 redef fun bin_op do return "or"
1921 end
1922
1923 redef class AImpliesExpr
1924 super ABinOpHelper
1925
1926 redef fun bin_expr1 do return n_expr
1927 redef fun bin_expr2 do return n_expr2
1928 redef fun bin_op do return "implies"
1929 end
1930
1931 redef class ABinopExpr
1932 super ABinOpHelper
1933
1934 redef fun bin_expr1 do return n_expr
1935 redef fun bin_expr2 do return n_expr2
1936 end
1937
1938 redef class AEqExpr
1939 redef fun bin_op do return "=="
1940 end
1941
1942 redef class AGeExpr
1943 redef fun bin_op do return ">="
1944 end
1945
1946 redef class AGgExpr
1947 redef fun bin_op do return ">>"
1948 end
1949
1950 redef class AGtExpr
1951 redef fun bin_op do return ">"
1952 end
1953
1954 redef class ALeExpr
1955 redef fun bin_op do return "<="
1956 end
1957
1958 redef class ALlExpr
1959 redef fun bin_op do return "<<"
1960 end
1961
1962 redef class ALtExpr
1963 redef fun bin_op do return "<"
1964 end
1965
1966 redef class AMinusExpr
1967 redef fun bin_op do return "-"
1968 end
1969
1970 redef class ANeExpr
1971 redef fun bin_op do return "!="
1972 end
1973
1974 redef class APercentExpr
1975 redef fun bin_op do return "%"
1976 end
1977
1978 redef class APlusExpr
1979 redef fun bin_op do return "+"
1980 end
1981
1982 redef class ASlashExpr
1983 redef fun bin_op do return "/"
1984 end
1985
1986 redef class AStarExpr
1987 redef fun bin_op do return "*"
1988 end
1989
1990 redef class AStarstarExpr
1991 redef fun bin_op do return "**"
1992 end
1993
1994 redef class AStarshipExpr
1995 redef fun bin_op do return "<=>"
1996 end
1997
1998 redef class AIsaExpr
1999 redef fun accept_pretty_printer(v) do
2000 v.visit n_expr
2001 v.adds
2002 v.consume "isa"
2003 v.adds
2004 v.visit n_type
2005 end
2006 end
2007
2008 redef class AOrElseExpr
2009 redef fun accept_pretty_printer(v) do
2010 v.visit n_expr
2011 v.adds
2012 v.consume "or"
2013 v.adds
2014 v.consume "else"
2015 v.adds
2016 v.visit n_expr2
2017 end
2018
2019 redef fun is_inlinable do return true
2020 end
2021
2022 # Syntax
2023
2024 redef class AUminusExpr
2025 redef fun accept_pretty_printer(v) do
2026 v.consume "-"
2027 v.visit n_expr
2028 end
2029 end
2030
2031 redef class ANullExpr
2032 redef fun accept_pretty_printer(v) do v.visit n_kwnull
2033 redef fun is_inlinable do return true
2034 end
2035
2036 redef class AParExpr
2037 redef fun accept_pretty_printer(v) do
2038 v.visit n_opar
2039 v.visit n_expr
2040 v.visit n_cpar
2041 end
2042 end
2043
2044 redef class AArrayExpr
2045 redef fun accept_pretty_printer(v) do
2046 v.consume "["
2047 v.visit_list n_exprs
2048 v.consume "]"
2049 end
2050 end
2051
2052 redef class ACrangeExpr
2053 redef fun accept_pretty_printer(v) do
2054 v.visit n_obra
2055 v.visit n_expr
2056 v.consume ".."
2057 v.visit n_expr2
2058 v.visit n_cbra
2059 end
2060 end
2061
2062 redef class AOrangeExpr
2063 redef fun accept_pretty_printer(v) do
2064 v.visit n_obra
2065 v.visit n_expr
2066 v.consume ".."
2067 v.visit n_expr2
2068 v.visit n_cbra
2069 end
2070 end
2071
2072 # Strings
2073
2074 redef class AStringFormExpr
2075 redef fun accept_pretty_printer(v) do
2076 var can_inline = v.can_inline(self)
2077
2078 if can_inline then
2079 v.visit n_string
2080 else
2081 var text = n_string.text
2082 var i = 0
2083
2084 while i < text.length do
2085 v.add text[i].to_s
2086
2087 if v.current_length >= v.max_size and i <= text.length - 3 then
2088 v.add "\" +"
2089 v.addn
2090 v.indent += 1
2091 v.addt
2092 v.indent -= 1
2093 v.add "\""
2094 end
2095
2096 i += 1
2097 end
2098
2099 v.current_token = n_string.next_token
2100 end
2101 end
2102 end
2103
2104 redef class ASuperstringExpr
2105 redef fun accept_pretty_printer(v) do
2106 for n_expr in n_exprs do v.visit n_expr
2107 end
2108
2109 redef fun must_be_inline do
2110 if super then return true
2111
2112 if not n_exprs.is_empty then
2113 var first = n_exprs.first
2114 return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
2115 end
2116
2117 return false
2118 end
2119 end