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