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