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