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