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