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