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