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