1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Library used to pretty print Nit code.
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 == """
26 # \tfun toto: Int do return 5
29 # See `nitpretty` tool for more documentation.
37 # The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
39 # The main method here is `visit` that performs the pretty printing of a `ANode`.
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).
45 # Visited productions are in charges to move the token pointer using methods such as:
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
54 fun pretty
(n
: ANode): Template do
59 current_token
= n
.first_token
61 else if n
isa Token then
64 while p
!= null and not p
isa Prod do
68 current_token
= p
.first_token
72 return tpl
.as(not null)
75 # Pretty print the whole `nmodule` with comments before and after.
76 fun pretty_nmodule
(nmodule
: AModule): Template do
78 nmodule
.parentize_tokens
79 current_token
= nmodule
.location
.file
.first_token
81 catch_up nmodule
.location
.file
.last_token
82 if skip_empty
then tpl
.add
"\n"
83 return tpl
.as(not null)
86 # Prepare `self` for a new visit.
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
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
112 if current_token
isa TCpar then consume
")"
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
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
127 if n
.collect_length
+ current_length
> max_size
then return false
128 # check block is inlinable
129 return n
.is_inlinable
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
140 if from
isa TComment then res
.add from
141 from
= from
.as(Token).next_token
147 # Token under cursor.
149 # This is the next token to visit.
150 var current_token
: nullable Token = null
152 # Skip the `current_token`.
153 fun skip
do current_token
= current_token
.next_token
155 # Skip `current_token` until the end of line.
156 fun skip_line
do current_token
= current_token
.last_real_token_in_line
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 :(")
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
173 # Visit `current_token`.
174 fun consume
(token
: String) do
176 if current_token
.text
== token
then else current_token
.debug
("Got `{current_token.text}`; expected `{token}`.")
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
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
191 if target
isa Token then
193 else if target
isa Prod then
194 token
= target
.first_token
.as(not null)
198 if current_token
.location
> token
.location
then return
199 while current_token
!= token
do visit current_token
202 # Visit all tokens between `current_token` and the end of line.
204 if current_token
isa TComment then
209 while current_token
isa TEol do visit
(current_token
)
212 # The template under construction.
213 private var tpl
: nullable Template = null
215 # Current indent level.
218 # Size of a tabulation in spaces.
224 # Length of the current line.
225 var current_length
= 0
227 # Length of the previous line.
228 var previous_length
= 0
230 # Is the last line a blank line?
231 fun last_line_is_blank
: Bool do return previous_length
== 0
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
241 current_length
+= t
.length
246 if current_length
== 0 and last_line_is_blank
then return
247 previous_length
= current_length
249 if skip_empty
then wait_addn
+= 1
252 # Perform `addn` even if not `skip_empty`.
254 if current_length
== 0 and last_line_is_blank
then return
255 previous_length
= current_length
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
264 # Add `'\t' * indent`.
265 fun addt
do add
"\t" * indent
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
278 # Do we break string literals that are too long?
279 var break_strings
= false is public
writable
281 # Do we force `do` to be on the same line as the method signature?
282 var inline_do
= false is public
writable
284 # Do we force the deletion of empty lines?
285 var skip_empty
= false is public
writable
288 # Base framework redefs
290 redef class ANodes[E
]
291 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) do
293 var e_can_inline
= v
.can_inline
(e
)
295 if v
.current_token
isa TComma then v
.skip
298 if not e_can_inline
then
315 # Start visit of `self` using a `PrettyPrinterVisitor`
316 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) is abstract
318 # Collect the length (in `Char`) of the node.
319 private fun collect_length
: Int is abstract
321 # Is `self` printable in one line?
322 private fun is_inlinable
: Bool do return true
324 # Force `self` to be rendered as a block.
325 private var force_block
= false
327 # Does `self` have to be rendered as a block?
328 private fun must_be_block
: Bool do return force_block
330 # Force `self` to be rendered as a line.
331 private var force_inline
= false
333 # Does `self` have be rendered as a line?
334 private fun must_be_inline
: Bool do
335 if parent
!= null and parent
.must_be_inline
then return true
339 # Does `self` was written in one line before transformation?
340 private fun was_inline
: Bool is abstract
344 redef fun accept_pretty_printer
(v
) do
346 v
.current_token
= next_token
349 redef fun collect_length
do return text
.length
350 redef fun is_inlinable
do return true
351 redef fun was_inline
do return true
355 redef fun accept_pretty_printer
(v
) do
360 v
.current_token
= next_token
366 redef fun accept_pretty_printer
(v
) do v
.visit first_token
368 # The token where the production really start (skipping ADoc).
369 private fun start_token
: nullable Token do return first_token
371 # Collect all `TComment` contained in the production
372 # (between `start_token` and `end_token`).
373 private fun collect_comments
: Array[TComment] do
374 var res
= new Array[TComment]
375 if start_token
== null or last_token
== null then return res
376 var token
= start_token
378 while token
!= last_token
do
379 if token
isa TComment then res
.add token
380 token
= token
.next_token
386 redef fun collect_length
do
388 if start_token
== null or last_token
== null then return res
389 var token
= start_token
391 while token
!= last_token
do
392 res
+= token
.text
.length
393 token
= token
.next_token
396 res
+= token
.text
.length
400 redef fun was_inline
do
401 return start_token
.location
.line_start
== last_token
.location
.line_end
408 redef fun accept_pretty_printer
(v
) do
419 if is_last_in_group
then v
.addn
433 if next_token
isa TComment and is_first_in_group
then v
.addn
437 var prev_token
= self.prev_token
438 if prev_token
isa TComment and prev_token
.is_inline
and is_last_in_group
then v
.addn
443 if not v
.skip_empty
then v
.forcen
446 # Is `self` part of an `ADoc`?
447 private fun is_adoc
: Bool do return parent
isa ADoc and parent
.parent
!= null
449 # Is `self` part of a licence?
450 private fun is_licence
: Bool do
451 var prev_token
= self.prev_token
453 if prev_token
== null then
455 else if prev_token
isa TComment then
456 return prev_token
.is_licence
462 # Is `self` starts and ends its line?
463 private fun is_inline
: Bool do
464 return self == first_real_token_in_line
and self == last_real_token_in_line
467 # Is `self` an orphan line (blank before and after)?
468 private fun is_orphan
: Bool do
469 return prev_token
isa TEol and
470 (prev_token
.prev_token
isa TEol or prev_token
.prev_token
isa TComment) and
474 # Is `self` the first comment of a group of comment?
475 private fun is_first_in_group
: Bool do return not prev_token
isa TComment
477 # Is `self` the last comment of a group of comments?
478 private fun is_last_in_group
: Bool do return not next_token
isa TComment
482 redef fun accept_pretty_printer
(v
) do for comment
in n_comment
do v
.visit comment
483 redef fun is_inlinable
do return n_comment
.length
<= 1
488 redef class AAnnotations
489 redef fun accept_pretty_printer
(v
) do
492 if v
.can_inline
(self) then
494 for n_item
in n_items
do
496 if n_item
!= n_items
.last
then
500 if not was_inline
then
502 if v
.current_token
isa TKwend then v
.skip
507 for n_item
in n_items
do
511 if n_item
!= n_items
.last
then
523 redef fun is_inlinable
do
524 if not super then return false
525 for annot
in n_items
do if not annot
.is_inlinable
then return false
530 redef class AAnnotation
531 redef fun accept_pretty_printer
(v
) do
532 if n_visibility
!= null and not n_visibility
isa APublicVisibility then
541 redef class ATypeExpr
542 redef fun accept_pretty_printer
(v
) do v
.visit n_type
548 redef fun accept_pretty_printer
(v
) do
549 v
.catch_up start_token
552 if not n_imports
.is_empty
then
553 if v
.skip_empty
then v
.addn
555 for n_import
in n_imports
do
561 if not n_extern_code_blocks
.is_empty
then
564 for n_extern_block
in n_extern_code_blocks
do
565 v
.catch_up n_extern_block
566 v
.visit n_extern_block
568 if n_extern_block
!= n_extern_code_blocks
.last
then v
.addn
571 if not n_classdefs
.is_empty
then v
.addn
574 if not n_classdefs
.is_empty
then
575 if v
.skip_empty
then v
.addn
577 for n_classdef
in n_classdefs
do
578 v
.catch_up n_classdef
580 if n_classdef
!= n_classdefs
.last
then v
.addn
588 redef fun start_token
do
589 if n_moduledecl
!= null then return n_moduledecl
.first_token
590 if not n_imports
.is_empty
then return n_imports
.first
.first_token
591 if not n_classdefs
.is_empty
then return n_classdefs
.first
.first_token
595 redef fun is_inlinable
do return false
598 redef class AModuledecl
599 redef fun accept_pretty_printer
(v
) do
605 if n_annotations
!= null then
606 var annot_inline
= v
.can_inline
(n_annotations
)
607 v
.visit n_annotations
609 if not annot_inline
then
610 if v
.current_token
isa TKwend then
620 if v
.skip_empty
then v
.addn
624 redef class AModuleName
625 redef fun accept_pretty_printer
(v
) do
626 for path
in n_path
do
635 redef class ANoImport
636 redef fun accept_pretty_printer
(v
) do
641 if v
.skip_empty
then v
.addn
645 redef class AStdImport
646 redef fun accept_pretty_printer
(v
) do
647 if not n_visibility
isa APublicVisibility then
656 if v
.skip_empty
then v
.addn
662 redef class AClassdef
663 redef fun accept_pretty_printer
(v
) do
664 for n_propdef
in n_propdefs
do
667 if n_propdef
.n_doc
!= null or not v
.can_inline
(n_propdef
) then
668 if v
.skip_empty
and n_propdef
!= n_propdefs
.first
then v
.addn
670 if v
.skip_empty
and n_propdef
!= n_propdefs
.last
then v
.addn
678 redef class AStdClassdef
679 redef fun accept_pretty_printer
(v
) do
681 var can_inline
= v
.can_inline
(self)
683 if not n_visibility
isa APublicVisibility then
688 if n_kwredef
!= null then
697 if not n_formaldefs
.is_empty
then
699 v
.visit_list n_formaldefs
703 if n_extern_code_block
!= null then
705 v
.visit n_extern_code_block
711 if not n_propdefs
.is_empty
then
712 for n_superclass
in n_propdefs
do
719 if v
.skip_empty
then v
.addn
729 if v
.skip_empty
then v
.addn
733 redef fun is_inlinable
do
734 if not super then return false
735 # FIXME: repair pretty-printing one-liner classes
736 if n_propdefs
.length
> 0 then return false
737 #if n_propdefs.length == 1 and not n_propdefs.first isa ASuperPropdef then return false
738 if not collect_comments
.is_empty
then return false
742 redef fun start_token
do
743 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
744 if n_kwredef
!= null then return n_kwredef
745 return n_classkind
.first_token
749 redef class AAbstractClasskind
750 redef fun accept_pretty_printer
(v
) do
757 redef class AExternClasskind
758 redef fun accept_pretty_printer
(v
) do
765 redef class AFormaldef
766 redef fun accept_pretty_printer
(v
) do
769 if n_type
!= null then
778 redef fun accept_pretty_printer
(v
) do
779 if n_kwnullable
!= null then
786 if not n_types
.is_empty
then
797 redef fun accept_pretty_printer
(v
) do
801 if not n_visibility
isa nullable APublicVisibility then
806 if n_kwredef
!= null then
812 # Factorize annotations visit for all APropdef.
814 # Return true if annotations were inlined.
815 fun visit_annotations
(v
: PrettyPrinterVisitor, n_annotations
: nullable AAnnotations): Bool do
816 var res
= v
.can_inline
(n_annotations
)
817 if n_annotations
!= null then v
.visit n_annotations
821 # Factorize block visit for APropdefs.
823 # Were annotations printed inline? If so, we need to print the block differently.
824 fun visit_block
(v
: PrettyPrinterVisitor, n_block
: nullable AExpr, annot_inline
: Bool) do
825 # var can_inline = v.can_inline(n_block)
826 if n_block
== null then return
827 if n_annotations
!= null and not annot_inline
then
832 while not v
.current_token
isa TKwdo do v
.skip
834 var token
= v
.current_token
837 if token
isa TEol then
839 if not v
.can_inline
(n_block
) then
845 token
= v
.current_token
846 if token
isa TKwdo then break
848 if annot_inline
and do_inline
then v
.adds
851 if v
.can_inline
(n_block
) and do_inline
then
854 if n_block
isa ABlockExpr then
855 if n_block
.n_expr
.is_empty
then
856 v
.visit n_block
.n_kwend
858 v
.visit n_block
.n_expr
.first
859 v
.current_token
= n_block
.n_kwend
864 if v
.current_token
isa TKwend then v
.skip
875 if n_block
isa ABlockExpr then
876 n_block
.force_block
= true
878 v
.catch_up n_block
.n_kwend
887 if n_block
isa ABlockExpr then
888 v
.visit n_block
.n_kwend
896 redef fun start_token
do
897 if n_doc
== null then return super
898 return n_doc
.last_token
.next_token
902 redef class AAttrPropdef
903 redef fun accept_pretty_printer
(v
) do
909 if n_type
!= null then
915 if n_expr
!= null then
922 var annot_inline
= visit_annotations
(v
, n_annotations
)
923 visit_block
(v
, n_block
, annot_inline
)
928 redef fun first_token
do
929 if n_doc
!= null then return n_doc
.first_token
930 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
931 if n_kwredef
!= null then return n_kwredef
935 redef fun is_inlinable
do return true
938 redef class ATypePropdef
939 redef fun accept_pretty_printer
(v
) do
947 visit_annotations
(v
, n_annotations
)
952 redef fun is_inlinable
do return true
955 redef class AMethPropdef
956 redef fun accept_pretty_printer
(v
) do
957 # TODO: Handle extern annotations
959 var before
= v
.indent
961 if n_kwinit
!= null then v
.visit n_kwinit
962 if n_kwmeth
!= null then v
.visit n_kwmeth
963 if n_kwnew
!= null then v
.visit n_kwnew
965 if not n_methid
== null then
972 var annot_inline
= visit_annotations
(v
, n_annotations
)
974 if n_extern_calls
!= null or n_extern_code_block
!= null then
976 if n_extern_calls
!= null then v
.visit n_extern_calls
977 if n_extern_code_block
!= null then v
.visit n_extern_code_block
980 visit_block
(v
, n_block
, annot_inline
)
982 assert v
.indent
== before
986 # * block is empty or can be inlined
987 # * contains no comments
988 redef fun is_inlinable
do
989 if not super then return false
990 if n_annotations
!= null and not n_annotations
.is_inlinable
then return false
991 if n_block
!= null and not n_block
.is_inlinable
then return false
992 if n_extern_calls
!= null and not n_extern_calls
.is_inlinable
then return false
993 if n_extern_code_block
!= null and not n_extern_code_block
.is_inlinable
then return false
994 if not collect_comments
.is_empty
then return false
999 redef class AMainMethPropdef
1000 redef fun accept_pretty_printer
(v
) do
1002 if v
.skip_empty
then v
.addn
1006 redef class ASuperPropdef
1007 redef fun accept_pretty_printer
(v
) do
1012 visit_annotations
(v
, n_annotations
)
1017 redef fun is_inlinable
do return true
1020 redef class ASignature
1021 redef fun accept_pretty_printer
(v
) do
1022 if not n_params
.is_empty
then
1024 v
.visit_list n_params
1028 if n_type
!= null then
1037 redef fun accept_pretty_printer
(v
) do
1040 if n_type
!= null then
1046 if n_dotdotdot
!= null then v
.visit n_dotdotdot
1052 redef class AExternCalls
1053 redef fun accept_pretty_printer
(v
) do
1054 var can_inline
= v
.can_inline
(self)
1059 v
.visit_list n_extern_calls
1065 v
.visit_list n_extern_calls
1072 redef class AFullPropExternCall
1073 redef fun accept_pretty_printer
(v
) do
1080 redef class ALocalPropExternCall
1081 redef fun accept_pretty_printer
(v
) do v
.visit n_methid
1084 redef class AInitPropExternCall
1085 redef fun accept_pretty_printer
(v
) do v
.visit n_type
1088 redef class ACastAsExternCall
1089 redef fun accept_pretty_printer
(v
) do
1099 redef class AAsNullableExternCall
1100 redef fun accept_pretty_printer
(v
) do
1105 v
.visit n_kwnullable
1109 redef class AAsNotNullableExternCall
1110 redef fun accept_pretty_printer
(v
) do
1117 v
.visit n_kwnullable
1121 redef class AExternCodeBlock
1122 redef fun accept_pretty_printer
(v
) do
1123 if n_in_language
!= null then
1124 v
.visit n_in_language
1128 v
.visit n_extern_code_segment
1131 redef fun is_inlinable
do
1132 if not super then return false
1133 return n_extern_code_segment
.is_inlinable
1137 redef class AInLanguage
1138 redef fun accept_pretty_printer
(v
) do
1145 redef class TExternCodeSegment
1146 redef fun accept_pretty_printer
(v
) do
1147 var can_inline
= v
.can_inline
(self)
1152 var text
= text
.substring
(2, text
.length
- 4)
1153 var lines
= text
.r_trim
.split
("\n")
1155 if text
.is_empty
then
1160 if not lines
.first
.trim
.is_empty
then
1168 for line
in lines
do
1177 v
.current_token
= next_token
1181 redef fun is_inlinable
do
1182 if not super then return false
1183 return location
.line_start
== location
.line_end
1189 redef class ABlockExpr
1190 redef fun accept_pretty_printer
(v
) do
1191 var before
= v
.indent
1192 var can_inline
= v
.can_inline
(self)
1194 if can_inline
and not n_expr
.is_empty
then
1195 v
.visit n_expr
.first
1198 for nexpr
in n_expr
do
1199 var expr_inline
= v
.can_inline
(nexpr
)
1200 if not expr_inline
and nexpr
!= n_expr
.first
then v
.addn
1206 if not expr_inline
and nexpr
!= n_expr
.last
then v
.addn
1210 assert v
.indent
== before
1213 redef fun is_inlinable
do
1214 if not super then return false
1215 if not collect_comments
.is_empty
then return false
1217 if not n_expr
.is_empty
then
1218 if n_expr
.length
> 1 then return false
1219 if not n_expr
.first
.is_inlinable
then return false
1227 redef fun accept_pretty_printer
(v
) do
1228 var before
= v
.indent
1229 var can_inline
= v
.can_inline
(self)
1233 if v
.can_inline
(n_expr
) then
1242 # skip comments before `then` token
1243 while not v
.current_token
isa TKwthen do v
.skip
1245 var n_else
= self.n_else
1249 if n_then
!= null then v
.visit n_then
1252 n_else
.force_inline
= true
1257 else if n_then
== null then
1260 v
.skip_to last_token
.last_real_token_in_line
1265 else if not v
.skip_empty
and n_then
!= null and
1266 n_then
.was_inline
and
1267 n_then
.location
.line_end
== location
.line_start
then
1268 v
.forcen
# Xymus fucking syntax
1274 if n_then
!= null then
1275 if n_then
isa ABlockExpr then
1276 n_then
.force_block
= true
1281 if n_then
.was_inline
then
1296 if n_else
isa AIfExpr then
1297 n_else
.force_block
= true
1309 if n_else
isa ABlockExpr then
1310 n_else
.force_block
= true
1315 if n_else
.was_inline
then
1322 if last_token
isa TKwend then
1323 v
.catch_up last_token
1334 if last_token
.location
>= v
.current_token
.location
then v
.catch_up last_token
1338 if v
.current_token
isa TKwend then v
.skip
1342 assert v
.indent
== before
1345 redef fun is_inlinable
do
1346 if not super then return false
1347 if n_then
!= null and not n_then
.is_inlinable
then return false
1348 var n_else
= self.n_else
1349 if (n_else
isa ABlockExpr and not n_else
.n_expr
.is_empty
) or
1350 (not n_else
isa ABlockExpr and n_else
!= null) then
1353 if not collect_comments
.is_empty
then return false
1357 # Does this `if` statement contains a `else` part?
1358 private fun has_else
(v
: PrettyPrinterVisitor): Bool do
1360 if n_else
== null then return false
1361 var n_kwelse
= collect_kwelse
1362 if n_kwelse
== null then return false
1364 if n_else
isa ABlockExpr then
1365 var comments
: Array[TComment]
1367 if n_then
== null then
1368 comments
= v
.collect_comments
(n_expr
.last_token
, n_else
.last_token
)
1370 comments
= v
.collect_comments
(n_then
.last_token
, n_else
.last_token
)
1373 if not comments
.is_empty
then return true
1374 return not n_else
.n_expr
.is_empty
1380 # Lookup for `else` token in `self`.
1381 private fun collect_kwelse
: nullable TKwelse do
1382 var token
= first_token
1384 while token
!= last_token
do
1385 if token
isa TKwelse then return token
1386 token
= token
.next_token
1393 # Used to factorize work on loops.
1394 private class ALoopHelper
1397 fun loop_block
: nullable ANode is abstract
1398 fun loop_label
: nullable ANode is abstract
1400 fun visit_loop_block
(v
: PrettyPrinterVisitor) do
1401 var n_block
= loop_block
1406 if n_block
isa ABlockExpr then
1407 n_block
.force_block
= true
1409 v
.catch_up n_block
.n_kwend
1412 v
.visit n_block
.n_kwend
1422 if loop_label
!= null then
1428 fun visit_loop_inline
(v
: PrettyPrinterVisitor) do
1429 var n_block
= loop_block
1432 if n_block
isa ABlockExpr then
1433 if n_block
.n_expr
.is_empty
then
1434 v
.visit n_block
.n_kwend
1436 v
.visit n_block
.n_expr
.first
1437 v
.current_token
= n_block
.n_kwend
1442 if v
.current_token
isa TKwend then v
.skip
1445 if loop_label
!= null then
1451 redef fun is_inlinable
do
1452 var n_block
= loop_block
1453 if not super then return false
1454 if n_block
isa ABlockExpr and not n_block
.is_inlinable
then return false
1455 if not collect_comments
.is_empty
then return false
1460 redef class ALoopExpr
1463 redef fun loop_block
do return n_block
1464 redef fun loop_label
do return n_label
1466 redef fun accept_pretty_printer
(v
) do
1467 var can_inline
= v
.can_inline
(self)
1469 if can_inline
then visit_loop_inline v
else visit_loop_block v
1473 redef class AWhileExpr
1476 redef fun loop_block
do return n_block
1477 redef fun loop_label
do return n_label
1479 redef fun accept_pretty_printer
(v
) do
1480 var can_inline
= v
.can_inline
(self)
1486 if can_inline
then visit_loop_inline v
else visit_loop_block v
1493 redef fun loop_block
do return n_block
1494 redef fun loop_label
do return n_label
1496 redef fun accept_pretty_printer
(v
) do
1497 var can_inline
= v
.can_inline
(self)
1499 if can_inline
then visit_loop_inline v
else visit_loop_block v
1503 redef class AForExpr
1506 redef fun loop_block
do return n_block
1507 redef fun loop_label
do return n_label
1509 redef fun accept_pretty_printer
(v
) do
1510 var can_inline
= v
.can_inline
(self)
1514 for n_id
in n_ids
do
1516 if n_id
!= n_ids
.last
then v
.add
", "
1525 if can_inline
then visit_loop_inline v
else visit_loop_block v
1529 redef class ABreakExpr
1530 redef fun accept_pretty_printer
(v
) do
1533 if n_expr
!= null then
1538 if n_label
!= null then
1544 redef fun is_inlinable
do return true
1547 redef class AContinueExpr
1548 redef fun accept_pretty_printer
(v
) do
1549 v
.visit n_kwcontinue
1551 if n_expr
!= null then
1556 if n_label
!= null then
1562 redef fun is_inlinable
do return true
1567 redef class ASendExpr
1568 redef fun is_inlinable
do return true
1571 redef class ACallExpr
1572 redef fun accept_pretty_printer
(v
) do
1573 var can_inline
= v
.can_inline
(self)
1576 if not n_expr
isa AImplicitSelfExpr and not can_inline
then
1583 if not n_args
.n_exprs
.is_empty
then
1584 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1586 if v
.current_token
isa TOpar then v
.skip
1587 v
.visit n_args
.n_exprs
.first
1588 if v
.current_token
isa TCpar then v
.skip
1590 v
.visit_args n_args
.n_exprs
1595 # Is the call alone on its line?
1596 fun is_stmt
: Bool do return parent
isa ABlockExpr
1599 redef class ACallAssignExpr
1600 redef fun accept_pretty_printer
(v
) do
1604 if not n_args
.n_exprs
.is_empty
then
1606 v
.visit_list n_args
.n_exprs
1617 redef class ACallReassignExpr
1618 redef fun accept_pretty_printer
(v
) do
1622 if not n_args
.n_exprs
.is_empty
then
1624 v
.visit_list n_args
.n_exprs
1635 redef class ABraExpr
1636 redef fun accept_pretty_printer
(v
) do
1639 if not n_args
.n_exprs
.is_empty
then
1641 v
.visit_list n_args
.n_exprs
1647 redef class ABraAssignExpr
1648 redef fun accept_pretty_printer
(v
) do
1651 if not n_args
.n_exprs
.is_empty
then
1653 v
.visit_list n_args
.n_exprs
1664 redef class ABraReassignExpr
1665 redef fun accept_pretty_printer
(v
) do
1668 if not n_args
.n_exprs
.is_empty
then
1670 v
.visit_list n_args
.n_exprs
1681 redef class AAssignMethid
1682 redef fun accept_pretty_printer
(v
) do
1688 redef class ABraMethid
1689 redef fun accept_pretty_printer
(v
) do
1695 redef class ABraassignMethid
1696 redef fun accept_pretty_printer
(v
) do
1703 redef class AInitExpr
1704 redef fun accept_pretty_printer
(v
) do
1705 if not n_expr
isa AImplicitSelfExpr then
1711 v
.visit_args n_args
.n_exprs
1715 redef class ANewExpr
1716 redef fun accept_pretty_printer
(v
) do
1717 var can_inline
= v
.can_inline
(self)
1722 if n_id
!= null then
1725 if not can_inline
then
1735 v
.visit_args n_args
.n_exprs
1738 redef fun is_inlinable
do return true
1743 redef class AAttrExpr
1744 redef fun accept_pretty_printer
(v
) do
1749 redef fun is_inlinable
do return true
1752 redef class AAttrAssignExpr
1753 redef fun accept_pretty_printer
(v
) do
1763 redef class AAttrReassignExpr
1764 redef fun accept_pretty_printer
(v
) do
1776 redef class AVardeclExpr
1777 redef fun accept_pretty_printer
(v
) do
1782 if n_type
!= null then
1788 if n_expr
!= null then
1796 redef fun is_inlinable
do return true
1799 redef class AVarAssignExpr
1800 redef fun accept_pretty_printer
(v
) do
1809 redef class AAssertExpr
1810 redef fun accept_pretty_printer
(v
) do
1811 var can_inline
= v
.can_inline
(self)
1814 if n_id
!= null then
1822 var n_else
= self.n_else
1824 if n_else
!= null then
1835 if n_else
isa ABlockExpr then
1836 n_else
.force_block
= true
1840 v
.visit n_else
.n_kwend
1853 redef fun is_inlinable
do
1854 if not super then return false
1855 if n_else
!= null and not n_else
.is_inlinable
then return false
1860 redef class AReturnExpr
1861 redef fun accept_pretty_printer
(v
) do
1864 if n_expr
!= null then
1871 redef class ASuperExpr
1872 redef fun accept_pretty_printer
(v
) do
1875 if not n_args
.n_exprs
.is_empty
then
1876 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1878 if v
.current_token
isa TOpar then v
.skip
1879 v
.visit n_args
.n_exprs
.first
1880 if v
.current_token
isa TCpar then v
.skip
1882 v
.visit_args n_args
.n_exprs
1887 # Is the call alone on its line?
1888 fun is_stmt
: Bool do return self.first_token
.is_starting_line
1890 redef fun is_inlinable
do return true
1893 redef class AOnceExpr
1894 redef fun accept_pretty_printer
(v
) do
1900 redef fun is_inlinable
do return true
1903 redef class AAbortExpr
1904 redef fun accept_pretty_printer
(v
) do v
.visit n_kwabort
1905 redef fun is_inlinable
do return true
1908 redef class ANotExpr
1909 redef fun accept_pretty_printer
(v
) do
1916 redef class AAsCastExpr
1917 redef fun accept_pretty_printer
(v
) do
1927 redef class AAsNotnullExpr
1928 redef fun accept_pretty_printer
(v
) do
1942 # Used to factorize work on Or, And, Implies and Binop expressions.
1943 private class ABinOpHelper
1946 fun bin_expr1
: AExpr is abstract
1947 fun bin_expr2
: AExpr is abstract
1950 fun bin_op
: String is abstract
1952 redef fun accept_pretty_printer
(v
) do
1953 var can_inline
= v
.can_inline
(self)
1955 if not can_inline
then
1956 if (self isa ABinopExpr and bin_expr1
isa ABinopExpr) or
1957 (self isa AAndExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr)) or
1958 (self isa AOrExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr))
1960 bin_expr1
.force_block
= true
1981 redef class AAndExpr
1984 redef fun bin_expr1
do return n_expr
1985 redef fun bin_expr2
do return n_expr2
1986 redef fun bin_op
do return "and"
1992 redef fun bin_expr1
do return n_expr
1993 redef fun bin_expr2
do return n_expr2
1994 redef fun bin_op
do return "or"
1997 redef class AImpliesExpr
2000 redef fun bin_expr1
do return n_expr
2001 redef fun bin_expr2
do return n_expr2
2002 redef fun bin_op
do return "implies"
2005 redef class ABinopExpr
2008 redef fun bin_expr1
do return n_expr
2009 redef fun bin_expr2
do return n_expr2
2010 redef fun bin_op
do return operator
2013 redef class AIsaExpr
2014 redef fun accept_pretty_printer
(v
) do
2023 redef class AOrElseExpr
2024 redef fun accept_pretty_printer
(v
) do
2034 redef fun is_inlinable
do return true
2039 redef class AUplusExpr
2040 redef fun accept_pretty_printer
(v
) do
2046 redef class AUminusExpr
2047 redef fun accept_pretty_printer
(v
) do
2053 redef class ANullExpr
2054 redef fun accept_pretty_printer
(v
) do v
.visit n_kwnull
2055 redef fun is_inlinable
do return true
2058 redef class AParExpr
2059 redef fun accept_pretty_printer
(v
) do
2066 redef class AArrayExpr
2067 redef fun accept_pretty_printer
(v
) do
2069 v
.visit_list n_exprs
2074 redef class ACrangeExpr
2075 redef fun accept_pretty_printer
(v
) do
2084 redef class AOrangeExpr
2085 redef fun accept_pretty_printer
(v
) do
2096 redef class AStringFormExpr
2097 redef fun accept_pretty_printer
(v
) do
2098 if not v
.break_strings
then
2099 # n_string.force_inline = true
2103 if v
.can_inline
(self) then
2104 n_string
.force_inline
= true
2107 var text
= n_string
.text
2110 while i
< text
.length
do
2113 if v
.current_length
>= v
.max_size
and i
<= text
.length
- 3 then
2129 v
.current_token
= n_string
.next_token
2134 redef class ASuperstringExpr
2135 redef fun accept_pretty_printer
(v
) do
2136 for n_expr
in n_exprs
do
2137 if not v
.break_strings
then
2138 n_expr
.force_inline
= true
2144 redef fun must_be_inline
do
2145 if super then return true
2147 if not n_exprs
.is_empty
then
2148 var first
= n_exprs
.first
2149 return first
isa AStringFormExpr and first
.n_string
.text
.has_prefix
("\"\
"\"")