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