pretty: workaround a bug with a missing `else`
[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
1291 # FIXME: for some unknown reasons, has_else can be true even if
1292 # there is no `else` keyword but a `end` instead.
1293 if has_else(v) and v.current_token isa TKwelse then
1294
1295 v.indent -= 1
1296 v.addt
1297 v.consume "else"
1298
1299 if n_else isa AIfExpr then
1300 n_else.force_block = true
1301 v.adds
1302 v.visit n_else
1303 else
1304 v.finish_line
1305 if was_inline then
1306 v.forcen
1307 else
1308 v.addn
1309 end
1310 v.indent += 1
1311
1312 if n_else isa ABlockExpr then
1313 n_else.force_block = true
1314 v.visit n_else
1315 else
1316 v.addt
1317 v.visit n_else
1318 if n_else.was_inline then
1319 v.forcen
1320 else
1321 v.addn
1322 end
1323 end
1324
1325 if last_token isa TKwend then
1326 v.catch_up last_token
1327 v.indent -= 1
1328 v.addt
1329 v.consume "end"
1330 else
1331 v.indent -= 1
1332 v.addt
1333 v.add "end"
1334 end
1335 end
1336 else
1337 if last_token.location >= v.current_token.location then v.catch_up last_token
1338 v.indent -= 1
1339 v.addt
1340 v.add "end"
1341 if v.current_token isa TKwend then v.skip
1342 end
1343 end
1344
1345 assert v.indent == before
1346 end
1347
1348 redef fun is_inlinable do
1349 if not super then return false
1350 if n_then != null and not n_then.is_inlinable then return false
1351 var n_else = self.n_else
1352 if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
1353 (not n_else isa ABlockExpr and n_else != null) then
1354 return false
1355 end
1356 if not collect_comments.is_empty then return false
1357 return true
1358 end
1359
1360 # Does this `if` statement contains a `else` part?
1361 private fun has_else(v: PrettyPrinterVisitor): Bool do
1362 var n_else = n_else
1363 if n_else == null then return false
1364 var n_kwelse = collect_kwelse
1365 if n_kwelse == null then return false
1366
1367 if n_else isa ABlockExpr then
1368 var comments: Array[TComment]
1369
1370 if n_then == null then
1371 comments = v.collect_comments(n_expr.last_token, n_else.last_token)
1372 else
1373 comments = v.collect_comments(n_then.last_token, n_else.last_token)
1374 end
1375
1376 if not comments.is_empty then return true
1377 return not n_else.n_expr.is_empty
1378 end
1379
1380 return true
1381 end
1382
1383 # Lookup for `else` token in `self`.
1384 private fun collect_kwelse: nullable TKwelse do
1385 var token = first_token
1386
1387 while token != last_token do
1388 if token isa TKwelse then return token
1389 token = token.next_token
1390 end
1391
1392 return null
1393 end
1394 end
1395
1396 # Used to factorize work on loops.
1397 private class ALoopHelper
1398 super AExpr
1399
1400 fun loop_block: nullable ANode is abstract
1401 fun loop_label: nullable ANode is abstract
1402
1403 fun visit_loop_block(v: PrettyPrinterVisitor) do
1404 var n_block = loop_block
1405 v.finish_line
1406 v.addn
1407 v.indent += 1
1408
1409 if n_block isa ABlockExpr then
1410 n_block.force_block = true
1411 v.visit n_block
1412 v.catch_up n_block.n_kwend
1413 v.indent -= 1
1414 v.addt
1415 v.visit n_block.n_kwend
1416 else
1417 v.addt
1418 v.visit n_block
1419 v.addn
1420 v.indent -= 1
1421 v.addt
1422 v.add "end"
1423 end
1424
1425 if loop_label != null then
1426 v.adds
1427 v.visit loop_label
1428 end
1429 end
1430
1431 fun visit_loop_inline(v: PrettyPrinterVisitor) do
1432 var n_block = loop_block
1433 v.adds
1434
1435 if n_block isa ABlockExpr then
1436 if n_block.n_expr.is_empty then
1437 v.visit n_block.n_kwend
1438 else
1439 v.visit n_block.n_expr.first
1440 v.current_token = n_block.n_kwend
1441 v.skip
1442 end
1443 else
1444 v.visit n_block
1445 if v.current_token isa TKwend then v.skip
1446 end
1447
1448 if loop_label != null then
1449 v.adds
1450 v.visit loop_label
1451 end
1452 end
1453
1454 redef fun is_inlinable do
1455 var n_block = loop_block
1456 if not super then return false
1457 if n_block isa ABlockExpr and not n_block.is_inlinable then return false
1458 if not collect_comments.is_empty then return false
1459 return true
1460 end
1461 end
1462
1463 redef class ALoopExpr
1464 super ALoopHelper
1465
1466 redef fun loop_block do return n_block
1467 redef fun loop_label do return n_label
1468
1469 redef fun accept_pretty_printer(v) do
1470 var can_inline = v.can_inline(self)
1471 v.visit n_kwloop
1472 if can_inline then visit_loop_inline v else visit_loop_block v
1473 end
1474 end
1475
1476 redef class AWhileExpr
1477 super ALoopHelper
1478
1479 redef fun loop_block do return n_block
1480 redef fun loop_label do return n_label
1481
1482 redef fun accept_pretty_printer(v) do
1483 var can_inline = v.can_inline(self)
1484 v.visit n_kwwhile
1485 v.adds
1486 v.visit n_expr
1487 v.adds
1488 v.visit n_kwdo
1489 if can_inline then visit_loop_inline v else visit_loop_block v
1490 end
1491 end
1492
1493 redef class ADoExpr
1494 super ALoopHelper
1495
1496 redef fun loop_block do return n_block
1497 redef fun loop_label do return n_label
1498
1499 redef fun accept_pretty_printer(v) do
1500 var can_inline = v.can_inline(self)
1501 v.visit n_kwdo
1502 if can_inline then visit_loop_inline v else visit_loop_block v
1503 end
1504 end
1505
1506 redef class AForExpr
1507 super ALoopHelper
1508
1509 redef fun loop_block do return n_block
1510 redef fun loop_label do return n_label
1511
1512 redef fun accept_pretty_printer(v) do
1513 var can_inline = v.can_inline(self)
1514 v.visit n_kwfor
1515 v.adds
1516
1517 for n_id in n_ids do
1518 v.visit n_id
1519 if n_id != n_ids.last then v.add ", "
1520 end
1521
1522 v.adds
1523 v.consume "in"
1524 v.adds
1525 v.visit n_expr
1526 v.adds
1527 v.visit n_kwdo
1528 if can_inline then visit_loop_inline v else visit_loop_block v
1529 end
1530 end
1531
1532 redef class ABreakExpr
1533 redef fun accept_pretty_printer(v) do
1534 v.visit n_kwbreak
1535
1536 if n_expr != null then
1537 v.adds
1538 v.visit n_expr
1539 end
1540
1541 if n_label != null then
1542 v.adds
1543 v.visit n_label
1544 end
1545 end
1546
1547 redef fun is_inlinable do return true
1548 end
1549
1550 redef class AContinueExpr
1551 redef fun accept_pretty_printer(v) do
1552 v.visit n_kwcontinue
1553
1554 if n_expr != null then
1555 v.adds
1556 v.visit n_expr
1557 end
1558
1559 if n_label != null then
1560 v.adds
1561 v.visit n_label
1562 end
1563 end
1564
1565 redef fun is_inlinable do return true
1566 end
1567
1568 # Calls
1569
1570 redef class ASendExpr
1571 redef fun is_inlinable do return true
1572 end
1573
1574 redef class ACallExpr
1575 redef fun accept_pretty_printer(v) do
1576 var can_inline = v.can_inline(self)
1577 v.visit_recv n_expr
1578
1579 if not n_expr isa AImplicitSelfExpr and not can_inline then
1580 v.addn
1581 v.addt
1582 end
1583
1584 v.visit n_id
1585
1586 if not n_args.n_exprs.is_empty then
1587 if is_stmt and n_args.n_exprs.length == 1 then
1588 v.adds
1589 if v.current_token isa TOpar then v.skip
1590 v.visit n_args.n_exprs.first
1591 if v.current_token isa TCpar then v.skip
1592 else
1593 v.visit_args n_args.n_exprs
1594 end
1595 end
1596 end
1597
1598 # Is the call alone on its line?
1599 fun is_stmt: Bool do return parent isa ABlockExpr
1600 end
1601
1602 redef class ACallAssignExpr
1603 redef fun accept_pretty_printer(v) do
1604 v.visit_recv n_expr
1605 v.visit n_id
1606
1607 if not n_args.n_exprs.is_empty then
1608 v.consume "("
1609 v.visit_list n_args.n_exprs
1610 v.consume ")"
1611 end
1612
1613 v.adds
1614 v.consume "="
1615 v.adds
1616 v.visit n_value
1617 end
1618 end
1619
1620 redef class ACallReassignExpr
1621 redef fun accept_pretty_printer(v) do
1622 v.visit_recv n_expr
1623 v.visit n_id
1624
1625 if not n_args.n_exprs.is_empty then
1626 v.consume "("
1627 v.visit_list n_args.n_exprs
1628 v.consume ")"
1629 end
1630
1631 v.adds
1632 v.visit n_assign_op
1633 v.adds
1634 v.visit n_value
1635 end
1636 end
1637
1638 redef class ABraExpr
1639 redef fun accept_pretty_printer(v) do
1640 v.visit n_expr
1641
1642 if not n_args.n_exprs.is_empty then
1643 v.consume "["
1644 v.visit_list n_args.n_exprs
1645 v.consume "]"
1646 end
1647 end
1648 end
1649
1650 redef class ABraAssignExpr
1651 redef fun accept_pretty_printer(v) do
1652 v.visit n_expr
1653
1654 if not n_args.n_exprs.is_empty then
1655 v.consume "["
1656 v.visit_list n_args.n_exprs
1657 v.consume "]"
1658 end
1659
1660 v.adds
1661 v.visit n_assign
1662 v.adds
1663 v.visit n_value
1664 end
1665 end
1666
1667 redef class ABraReassignExpr
1668 redef fun accept_pretty_printer(v) do
1669 v.visit n_expr
1670
1671 if not n_args.n_exprs.is_empty then
1672 v.consume "["
1673 v.visit_list n_args.n_exprs
1674 v.consume "]"
1675 end
1676
1677 v.adds
1678 v.visit n_assign_op
1679 v.adds
1680 v.visit n_value
1681 end
1682 end
1683
1684 redef class AAssignMethid
1685 redef fun accept_pretty_printer(v) do
1686 v.visit n_id
1687 v.visit n_assign
1688 end
1689 end
1690
1691 redef class ABraMethid
1692 redef fun accept_pretty_printer(v) do
1693 v.visit n_obra
1694 v.visit n_cbra
1695 end
1696 end
1697
1698 redef class ABraassignMethid
1699 redef fun accept_pretty_printer(v) do
1700 v.visit n_obra
1701 v.visit n_cbra
1702 v.visit n_assign
1703 end
1704 end
1705
1706 redef class AInitExpr
1707 redef fun accept_pretty_printer(v) do
1708 if not n_expr isa AImplicitSelfExpr then
1709 v.visit n_expr
1710 v.consume "."
1711 end
1712
1713 v.visit n_kwinit
1714 v.visit_args n_args.n_exprs
1715 end
1716 end
1717
1718 redef class ANewExpr
1719 redef fun accept_pretty_printer(v) do
1720 var can_inline = v.can_inline(self)
1721 v.visit n_kwnew
1722 v.adds
1723 v.visit n_type
1724
1725 if n_id != null then
1726 v.consume "."
1727
1728 if not can_inline then
1729 v.addn
1730 v.indent += 1
1731 v.addt
1732 v.indent -= 1
1733 end
1734
1735 v.visit n_id
1736 end
1737
1738 v.visit_args n_args.n_exprs
1739 end
1740
1741 redef fun is_inlinable do return true
1742 end
1743
1744 # Attributes
1745
1746 redef class AAttrExpr
1747 redef fun accept_pretty_printer(v) do
1748 v.visit_recv n_expr
1749 v.visit n_id
1750 end
1751
1752 redef fun is_inlinable do return true
1753 end
1754
1755 redef class AAttrAssignExpr
1756 redef fun accept_pretty_printer(v) do
1757 v.visit_recv n_expr
1758 v.visit n_id
1759 v.adds
1760 v.visit n_assign
1761 v.adds
1762 v.visit n_value
1763 end
1764 end
1765
1766 redef class AAttrReassignExpr
1767 redef fun accept_pretty_printer(v) do
1768 v.visit_recv n_expr
1769 v.visit n_id
1770 v.adds
1771 v.visit n_assign_op
1772 v.adds
1773 v.visit n_value
1774 end
1775 end
1776
1777 # Exprs
1778
1779 redef class AVardeclExpr
1780 redef fun accept_pretty_printer(v) do
1781 v.visit n_kwvar
1782 v.adds
1783 v.visit n_id
1784
1785 if n_type != null then
1786 v.consume ":"
1787 v.adds
1788 v.visit n_type
1789 end
1790
1791 if n_expr != null then
1792 v.adds
1793 v.consume "="
1794 v.adds
1795 v.visit n_expr
1796 end
1797 end
1798
1799 redef fun is_inlinable do return true
1800 end
1801
1802 redef class AVarAssignExpr
1803 redef fun accept_pretty_printer(v) do
1804 v.visit n_id
1805 v.adds
1806 v.visit n_assign
1807 v.adds
1808 v.visit n_value
1809 end
1810 end
1811
1812 redef class AAssertExpr
1813 redef fun accept_pretty_printer(v) do
1814 var can_inline = v.can_inline(self)
1815 v.visit n_kwassert
1816
1817 if n_id != null then
1818 v.adds
1819 v.visit n_id
1820 v.consume ":"
1821 end
1822
1823 v.adds
1824 v.visit n_expr
1825 var n_else = self.n_else
1826
1827 if n_else != null then
1828 v.adds
1829 v.consume "else"
1830
1831 if can_inline then
1832 v.adds
1833 v.visit n_else
1834 else
1835 v.addn
1836 v.indent += 1
1837
1838 if n_else isa ABlockExpr then
1839 n_else.force_block = true
1840 v.visit n_else
1841 v.indent -= 1
1842 v.addt
1843 v.visit n_else.n_kwend
1844 else
1845 v.addt
1846 v.visit n_else
1847 v.addn
1848 v.indent -= 1
1849 v.addt
1850 v.add "end"
1851 end
1852 end
1853 end
1854 end
1855
1856 redef fun is_inlinable do
1857 if not super then return false
1858 if n_else != null and not n_else.is_inlinable then return false
1859 return true
1860 end
1861 end
1862
1863 redef class AReturnExpr
1864 redef fun accept_pretty_printer(v) do
1865 v.visit n_kwreturn
1866
1867 if n_expr != null then
1868 v.adds
1869 v.visit n_expr
1870 end
1871 end
1872 end
1873
1874 redef class ASuperExpr
1875 redef fun accept_pretty_printer(v) do
1876 v.visit n_kwsuper
1877
1878 if not n_args.n_exprs.is_empty then
1879 if is_stmt and n_args.n_exprs.length == 1 then
1880 v.adds
1881 if v.current_token isa TOpar then v.skip
1882 v.visit n_args.n_exprs.first
1883 if v.current_token isa TCpar then v.skip
1884 else
1885 v.visit_args n_args.n_exprs
1886 end
1887 end
1888 end
1889
1890 # Is the call alone on its line?
1891 fun is_stmt: Bool do return self.first_token.is_starting_line
1892
1893 redef fun is_inlinable do return true
1894 end
1895
1896 redef class AOnceExpr
1897 redef fun accept_pretty_printer(v) do
1898 v.visit n_kwonce
1899 v.adds
1900 v.visit n_expr
1901 end
1902
1903 redef fun is_inlinable do return true
1904 end
1905
1906 redef class AAbortExpr
1907 redef fun accept_pretty_printer(v) do v.visit n_kwabort
1908 redef fun is_inlinable do return true
1909 end
1910
1911 redef class ANotExpr
1912 redef fun accept_pretty_printer(v) do
1913 v.visit n_kwnot
1914 v.adds
1915 v.visit n_expr
1916 end
1917 end
1918
1919 redef class AAsCastExpr
1920 redef fun accept_pretty_printer(v) do
1921 v.visit n_expr
1922 v.consume "."
1923 v.visit n_kwas
1924 v.visit n_opar
1925 v.visit n_type
1926 v.visit n_cpar
1927 end
1928 end
1929
1930 redef class AAsNotnullExpr
1931 redef fun accept_pretty_printer(v) do
1932 v.visit n_expr
1933 v.consume "."
1934 v.visit n_kwas
1935 v.visit n_opar
1936 v.visit n_kwnot
1937 v.adds
1938 v.visit n_kwnull
1939 v.visit n_cpar
1940 end
1941 end
1942
1943 # Binops
1944
1945 # Used to factorize work on Or, And, Implies and Binop expressions.
1946 private class ABinOpHelper
1947 super AExpr
1948
1949 fun bin_expr1: AExpr is abstract
1950 fun bin_expr2: AExpr is abstract
1951
1952 # Operator string
1953 fun bin_op: String is abstract
1954
1955 redef fun accept_pretty_printer(v) do
1956 var can_inline = v.can_inline(self)
1957
1958 if not can_inline then
1959 if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
1960 (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
1961 (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
1962 then
1963 bin_expr1.force_block = true
1964 end
1965 end
1966
1967 v.visit bin_expr1
1968 v.adds
1969 v.consume bin_op
1970
1971 if can_inline then
1972 v.adds
1973 v.visit bin_expr2
1974 else
1975 v.addn
1976 v.indent += 1
1977 v.addt
1978 v.indent -= 1
1979 v.visit bin_expr2
1980 end
1981 end
1982 end
1983
1984 redef class AAndExpr
1985 super ABinOpHelper
1986
1987 redef fun bin_expr1 do return n_expr
1988 redef fun bin_expr2 do return n_expr2
1989 redef fun bin_op do return "and"
1990 end
1991
1992 redef class AOrExpr
1993 super ABinOpHelper
1994
1995 redef fun bin_expr1 do return n_expr
1996 redef fun bin_expr2 do return n_expr2
1997 redef fun bin_op do return "or"
1998 end
1999
2000 redef class AImpliesExpr
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 "implies"
2006 end
2007
2008 redef class ABinopExpr
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 operator
2014 end
2015
2016 redef class AIsaExpr
2017 redef fun accept_pretty_printer(v) do
2018 v.visit n_expr
2019 v.adds
2020 v.consume "isa"
2021 v.adds
2022 v.visit n_type
2023 end
2024 end
2025
2026 redef class AOrElseExpr
2027 redef fun accept_pretty_printer(v) do
2028 v.visit n_expr
2029 v.adds
2030 v.consume "or"
2031 v.adds
2032 v.consume "else"
2033 v.adds
2034 v.visit n_expr2
2035 end
2036
2037 redef fun is_inlinable do return true
2038 end
2039
2040 # Syntax
2041
2042 redef class AUplusExpr
2043 redef fun accept_pretty_printer(v) do
2044 v.consume "+"
2045 v.visit n_expr
2046 end
2047 end
2048
2049 redef class AUminusExpr
2050 redef fun accept_pretty_printer(v) do
2051 v.consume "-"
2052 v.visit n_expr
2053 end
2054 end
2055
2056 redef class ANullExpr
2057 redef fun accept_pretty_printer(v) do v.visit n_kwnull
2058 redef fun is_inlinable do return true
2059 end
2060
2061 redef class AParExpr
2062 redef fun accept_pretty_printer(v) do
2063 v.visit n_opar
2064 v.visit n_expr
2065 v.visit n_cpar
2066 end
2067 end
2068
2069 redef class AArrayExpr
2070 redef fun accept_pretty_printer(v) do
2071 v.consume "["
2072 v.visit_list n_exprs
2073 if n_type != null then
2074 v.consume ":"
2075 v.adds
2076 v.visit n_type
2077 end
2078 v.consume "]"
2079 end
2080 end
2081
2082 redef class ACrangeExpr
2083 redef fun accept_pretty_printer(v) do
2084 v.visit n_obra
2085 v.visit n_expr
2086 v.consume ".."
2087 v.visit n_expr2
2088 v.visit n_cbra
2089 end
2090 end
2091
2092 redef class AOrangeExpr
2093 redef fun accept_pretty_printer(v) do
2094 v.visit n_obra
2095 v.visit n_expr
2096 v.consume ".."
2097 v.visit n_expr2
2098 v.visit n_cbra
2099 end
2100 end
2101
2102 # Strings
2103
2104 redef class AStringFormExpr
2105 redef fun accept_pretty_printer(v) do
2106 if not v.break_strings then
2107 # n_string.force_inline = true
2108 v.visit n_string
2109 return
2110 end
2111 if v.can_inline(self) then
2112 n_string.force_inline = true
2113 v.visit n_string
2114 else
2115 var text = n_string.text
2116 var i = 0
2117
2118 while i < text.length do
2119 v.add text[i].to_s
2120
2121 if v.current_length >= v.max_size and i <= text.length - 3 then
2122 v.add "\" +"
2123 if was_inline then
2124 v.forcen
2125 else
2126 v.addn
2127 end
2128 v.indent += 1
2129 v.addt
2130 v.indent -= 1
2131 v.add "\""
2132 end
2133
2134 i += 1
2135 end
2136
2137 v.current_token = n_string.next_token
2138 end
2139 end
2140 end
2141
2142 redef class ASuperstringExpr
2143 redef fun accept_pretty_printer(v) do
2144 for n_expr in n_exprs do
2145 if not v.break_strings then
2146 n_expr.force_inline = true
2147 end
2148 v.visit n_expr
2149 end
2150 end
2151
2152 redef fun must_be_inline do
2153 if super then return true
2154
2155 if not n_exprs.is_empty then
2156 var first = n_exprs.first
2157 return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
2158 end
2159
2160 return false
2161 end
2162 end