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