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