nitpretty: splits nitpretty into two modules
authorAlexandre Terrasa <alexandre@moz-code.org>
Tue, 9 Dec 2014 22:47:55 +0000 (17:47 -0500)
committerAlexandre Terrasa <alexandre@moz-code.org>
Tue, 9 Dec 2014 22:47:55 +0000 (17:47 -0500)
* module `src/pretty.nit`: the lib
* module `src/nitpretty.nit`: the tool

Fixes #836

Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/nitpretty.nit
src/pretty.nit [new file with mode: 0644]

index 141ef1b..c583b53 100644 (file)
 # ~~~
 module nitpretty
 
-import template
-import toolcontext
-import modelbuilder
-import astutil
-
-# The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
-#
-# The main method here is `visit` that performs the pretty printing of a `ANode`.
-#
-# Because some tokens like `TComment` are not stored in the AST,
-# we visit the AST like traditionnal visitor and also maintain a
-# pointer to the `current_token` (actually the next one to be printed).
-#
-# Visited productions are in charges to move the token pointer using methods such as:
-#
-# * `consume`: print `current_token` and move to the next one
-# * `skip`: move to the next token without printing the current one
-# * `skip_to`: move to a specified token skipping all the tokens before
-# * `catch_up`: consume all the tokens between `current_token` and a specified token
-# * `finish_line`: consume all the tokens between `current_token` and the end of line
-class PrettyPrinterVisitor
-       # Pretty print `n`.
-       fun pretty(n: ANode): Template do
-               clear
-               n.parentize_tokens
-
-               if n isa Prod then
-                       current_token = n.first_token
-                       visit n
-               else if n isa Token then
-                       var p = n.parent
-
-                       while p != null and not p isa Prod do
-                               p = p.parent
-                       end
-
-                       current_token = p.first_token
-                       visit p
-               end
-
-               return tpl.as(not null)
-       end
-
-       # Pretty print the whole `nmodule` with comments before and after.
-       fun pretty_nmodule(nmodule: AModule): Template do
-               clear
-               nmodule.parentize_tokens
-               current_token = nmodule.location.file.first_token
-               visit nmodule
-               catch_up nmodule.location.file.last_token
-               tpl.add "\n"
-               return tpl.as(not null)
-       end
-
-       # Prepare `self` for a new visit.
-       private fun clear do
-               tpl = new Template
-               current_token = null
-               indent = 0
-               current_length = 0
-               previous_length = 0
-               wait_addn = 0
-       end
-
-       # Visit `n` if not null.
-       fun visit(n: nullable ANode) do
-               if n == null then return
-               n.accept_pretty_printer self
-       end
-
-       # Visit a list of `Anode`.
-       fun visit_list(n: nullable ANodes[ANode]) do
-               if n == null then return
-               n.accept_pretty_printer self
-       end
-
-       # Is the node inlinable and can fit on the line.
-       fun can_inline(n: nullable ANode): Bool do
-               if n == null then return true
-               if n.must_be_inline then return true
-               if n.must_be_block then return false
-               # check length
-               if n.collect_length + current_length > max_size then return false
-               # check block is inlinable
-               return n.is_inlinable
-       end
-
-       # Collect all `TComment` between `from` and `to`.
-       fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
-               var res = new Array[TComment]
-               if from isa Prod then from = from.first_token
-               if to isa Prod then to = to.first_token
-               if from == null or to == null then return res
-
-               while from != to do
-                       if from isa TComment then res.add from
-                       from = from.as(Token).next_token
-               end
-
-               return res
-       end
-
-       # Token under cursor.
-       #
-       # This is the next token to visit.
-       var current_token: nullable Token = null
-
-       # Skip the `current_token`.
-       fun skip do current_token = current_token.next_token
-
-       # Skip `current_token` until the end of line.
-       fun skip_line do current_token = current_token.last_real_token_in_line
-
-       # Skip `current_token` until `target` is reached.
-       fun skip_to(target: nullable Token) do
-               if target == null then return
-               while current_token != target do skip
-       end
-
-       # Visit `current_token`.
-       fun consume(token: String) do
-               assert current_token.text == token
-               visit current_token
-       end
-
-       # Is there token to visit between `current_token` and `target`?
-       fun need_catch_up(target: nullable Token): Bool do
-               if target == null then return false
-               return current_token != target
-       end
-
-       # Visit all tokens between `current_token` and `target`.
-       fun catch_up(target: nullable ANode) do
-               if target == null then return
-               if current_token == null then return
-               var token: Token
-               if target isa Token then
-                       token = target
-               else if target isa Prod then
-                       token = target.first_token.as(not null)
-               else
-                       abort
-               end
-               assert current_token.location <= token.location
-               while current_token != token do visit current_token
-       end
-
-       # Visit all tokens between `current_token` and the end of line.
-       fun finish_line do
-               if current_token isa TComment then
-                       adds
-                       visit current_token
-               end
-
-               while current_token isa TEol do skip
-       end
-
-       # The template under construction.
-       private var tpl: nullable Template = null
-
-       # Current indent level.
-       var indent = 0
-
-       # Size of a tabulation in spaces.
-       var tab_size = 8
-
-       # Max line size.
-       var max_size = 80
-
-       # Length of the current line.
-       var current_length = 0
-
-       # Length of the previous line.
-       var previous_length = 0
-
-       # Is the last line a blank line?
-       fun last_line_is_blank: Bool do return previous_length == 0
-
-       # Add `t` to current template.
-       fun add(t: String) do
-               if t.is_empty then return
-               while wait_addn > 0 do
-                       tpl.add "\n"
-                       wait_addn -= 1
-               end
-               tpl.add t
-               current_length += t.length
-       end
-
-       # Add a `'\n'`.
-       fun addn do
-               if current_length == 0 and last_line_is_blank then return
-               previous_length = current_length
-               current_length = 0
-               wait_addn += 1
-       end
-
-       # End of line chars are stored until another char is added.
-       # This avoid empty files with only a '`\n`'.
-       private var wait_addn = 0
-
-       # Add `'\t' * indent`.
-       fun addt do add "\t" * indent
-
-       # Add a space.
-       fun adds do add " "
-
-       fun visit_recv(n_expr: AExpr) do
-               if not n_expr isa AImplicitSelfExpr then
-                       visit n_expr
-                       consume "."
-               end
-       end
-end
-
-# Base framework redefs
-
-redef class ANodes[E]
-       private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
-               for e in self do
-                       var e_can_inline = v.can_inline(e)
-
-                       if e != first then
-                               if not e_can_inline then
-                                       v.add ","
-                                       v.addn
-                                       v.addt
-                                       v.addt
-                               else
-                                       v.add ", "
-                               end
-                       end
-
-                       v.visit e
-               end
-       end
-end
-
-redef class ANode
-       # Start visit of `self` using a `PrettyPrinterVisitor`
-       private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract
-
-       # Collect the length (in `Char`) of the node.
-       private fun collect_length: Int is abstract
-
-       # Is `self` printable in one line?
-       private fun is_inlinable: Bool do return true
-
-       # Force `self` to be rendered as a block.
-       private var force_block = false
-
-       # Does `self` have to be rendered as a block?
-       private fun must_be_block: Bool do return force_block
-
-       # Force `self` to be rendered as a line.
-       private var force_inline = false
-
-       # Does `self` have be rendered as a line?
-       private fun must_be_inline: Bool do
-               if parent != null and parent.must_be_inline then return true
-               return force_inline
-       end
-
-       # Does `self` was written in one line before transformation?
-       private fun was_inline: Bool is abstract
-end
-
-redef class Token
-       redef fun accept_pretty_printer(v) do
-               v.add text.trim
-               v.current_token = next_token
-       end
-
-       redef fun collect_length do return text.length
-       redef fun is_inlinable do return true
-       redef fun was_inline do return true
-end
-
-redef class Prod
-       redef fun accept_pretty_printer(v) do v.visit first_token
-
-       # The token where the production really start (skipping ADoc).
-       private fun start_token: nullable Token do return first_token
-
-       # Collect all `TComment` contained in the production
-       # (between `start_token` and `end_token`).
-       private fun collect_comments: Array[TComment] do
-               var res = new Array[TComment]
-               if start_token == null or last_token == null then return res
-               var token = start_token
-
-               while token != last_token do
-                       if token isa TComment then res.add token
-                       token = token.next_token
-               end
-
-               return res
-       end
-
-       redef fun collect_length do
-               var res = 0
-               if start_token == null or last_token == null then return res
-               var token = start_token
-
-               while token != last_token do
-                       res += token.text.length
-                       token = token.next_token
-               end
-
-               res += token.text.length
-               return res
-       end
-
-       redef fun was_inline do
-               return first_token.location.line_start == last_token.location.line_end
-       end
-end
-
-# Comments
-
-redef class TComment
-       redef fun accept_pretty_printer(v) do
-               if is_adoc then
-                       v.addt
-                       super
-                       v.addn
-                       return
-               end
-
-               if is_licence then
-                       super
-                       v.addn
-                       if is_last_in_group then v.addn
-                       return
-               end
-
-               if is_orphan then
-                       v.addn
-                       v.addt
-                       super
-                       v.addn
-                       v.addn
-                       return
-               end
-
-               if is_inline then
-                       if next_token isa TComment and is_first_in_group then v.addn
-                       v.addt
-                       super
-                       v.addn
-                       var prev_token = self.prev_token
-                       if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
-                       return
-               end
-
-               super
-       end
-
-       # Is `self` part of an `ADoc`?
-       private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null
-
-       # Is `self` part of a licence?
-       private fun is_licence: Bool do
-               var prev_token = self.prev_token
-
-               if prev_token == null then
-                       return true
-               else if prev_token isa TComment then
-                       return prev_token.is_licence
-               else
-                       return false
-               end
-       end
-
-       # Is `self` starts and ends its line?
-       private fun is_inline: Bool do
-               return self == first_real_token_in_line and self == last_real_token_in_line
-       end
-
-       # Is `self` an orphan line (blank before and after)?
-       private fun is_orphan: Bool do
-               return prev_token isa TEol and
-                  (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
-                  next_token isa TEol
-       end
-
-       # Is `self` the first comment of a group of comment?
-       private fun is_first_in_group: Bool do return not prev_token isa TComment
-
-       # Is `self` the last comment of a group of comments?
-       private fun is_last_in_group: Bool do return not next_token isa TComment
-end
-
-redef class ADoc
-       redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
-       redef fun is_inlinable do return n_comment.length <= 1
-end
-
-# Annotations
-
-redef class AAnnotations
-       redef fun accept_pretty_printer(v) do
-               v.adds
-               v.consume "is"
-
-               if v.can_inline(self) then
-                       v.adds
-                       for n_item in n_items do
-                               v.visit n_item
-                               if n_item != n_items.last then
-                                       v.add ", "
-                               end
-                       end
-                       v.finish_line
-               else if n_items.length > 1 then
-                       v.addn
-                       v.indent += 1
-
-                       for n_item in n_items do
-                               v.addt
-                               v.visit n_item
-                               v.finish_line
-                               v.addn
-                       end
-
-                       v.indent -= 1
-               end
-               if not was_inline and v.current_token isa TKwend then v.skip
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               for annot in n_items do if not annot.is_inlinable then return false
-               return true
-       end
-end
-
-redef class AAnnotation
-       redef fun accept_pretty_printer(v) do
-               v.visit n_atid
-               if not n_args.is_empty then
-                       if n_opar == null then
-                               v.adds
-                       else
-                               v.visit n_opar
-                       end
-                       v.visit_list n_args
-                       v.visit n_cpar
-               end
-       end
-end
-
-redef class ATypeExpr
-       redef fun accept_pretty_printer(v) do v.visit n_type
-end
-
-# Modules
-
-redef class AModule
-       redef fun accept_pretty_printer(v) do
-               v.catch_up start_token
-               v.visit n_moduledecl
-
-               if not n_imports.is_empty then
-                       v.addn
-
-                       for n_import in n_imports do
-                               v.catch_up n_import
-                               v.visit n_import
-                       end
-               end
-
-               if not n_extern_code_blocks.is_empty then
-                       v.addn
-
-                       for n_extern_block in n_extern_code_blocks do
-                               v.catch_up n_extern_block
-                               v.visit n_extern_block
-                               v.addn
-                               if n_extern_block != n_extern_code_blocks.last then v.addn
-                       end
-
-                       if not n_classdefs.is_empty then v.addn
-               end
-
-               if not n_classdefs.is_empty then
-                       v.addn
-
-                       for n_classdef in n_classdefs do
-                               v.catch_up n_classdef
-                               v.visit n_classdef
-                               if n_classdef != n_classdefs.last then v.addn
-                       end
-               end
-
-               assert v.indent == 0
-       end
-
-       # Skip doc if any.
-       redef fun start_token do
-               if n_moduledecl != null then return n_moduledecl.first_token
-               if not n_imports.is_empty then return n_imports.first.first_token
-               if not n_classdefs.is_empty then return n_classdefs.first.first_token
-               return first_token
-       end
-
-       redef fun is_inlinable do return false
-end
-
-redef class AModuledecl
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               v.visit n_kwmodule
-               v.adds
-               v.visit n_name
-
-               if n_annotations != null then
-                       var annot_inline = v.can_inline(n_annotations)
-                       v.visit n_annotations
-
-                       if not annot_inline then
-                               if v.current_token isa TKwend then
-                                       v.consume "end"
-                                       v.finish_line
-                               else
-                                       v.add "end"
-                               end
-                       end
-               end
-
-               v.finish_line
-               v.addn
-       end
-end
-
-redef class AModuleName
-       redef fun accept_pretty_printer(v) do
-               for path in n_path do
-                       v.visit path
-                       v.add "::"
-               end
-
-               v.visit n_id
-       end
-end
-
-redef class ANoImport
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwimport
-               v.adds
-               v.visit n_kwend
-               v.finish_line
-               v.addn
-       end
-end
-
-redef class AStdImport
-       redef fun accept_pretty_printer(v) do
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               v.visit n_kwimport
-               v.adds
-               v.visit n_name
-               v.finish_line
-               v.addn
-       end
-end
-
-# Classes
-
-redef class AClassdef
-       redef fun accept_pretty_printer(v) do
-               for n_propdef in n_propdefs do
-                       v.catch_up n_propdef
-
-                       if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
-                               if n_propdef != n_propdefs.first then v.addn
-                               v.visit n_propdef
-                               if n_propdef != n_propdefs.last then v.addn
-                       else
-                               v.visit n_propdef
-                       end
-               end
-       end
-end
-
-redef class AStdClassdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               var can_inline = v.can_inline(self)
-
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               if n_kwredef != null then
-                       v.visit n_kwredef
-                       v.adds
-               end
-
-               v.visit n_classkind
-               v.adds
-               v.visit n_id
-
-               if not n_formaldefs.is_empty then
-                       v.consume "["
-                       v.visit_list n_formaldefs
-                       v.consume "]"
-               end
-
-               if n_extern_code_block != null then
-                       v.adds
-                       v.visit n_extern_code_block
-               end
-
-               if can_inline then
-                       v.adds
-
-                       if not n_superclasses.is_empty then
-                               for n_superclass in n_superclasses do
-                                       v.visit n_superclass
-                                       v.adds
-                               end
-                       end
-               else
-                       v.finish_line
-                       v.addn
-                       v.indent += 1
-
-                       for n_superclass in n_superclasses do
-                               v.catch_up n_superclass
-                               v.addt
-                               v.visit n_superclass
-                               v.finish_line
-                               v.addn
-                       end
-
-                       if not n_superclasses.is_empty and not n_propdefs.is_empty then
-                               v.addn
-                       end
-
-                       super
-                       v.catch_up n_kwend
-                       v.indent -= 1
-               end
-
-               v.visit n_kwend
-               v.finish_line
-               v.addn
-               assert v.indent == 0
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if not n_propdefs.is_empty then return false
-               if n_superclasses.length > 1 then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-
-       redef fun start_token do
-               if not n_visibility isa APublicVisibility then return n_visibility.first_token
-               if n_kwredef != null then return n_kwredef
-               return n_classkind.first_token
-       end
-end
-
-redef class AAbstractClasskind
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwabstract
-               v.adds
-               v.visit n_kwclass
-       end
-end
-
-redef class AExternClasskind
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwextern
-               v.adds
-               v.visit n_kwclass
-       end
-end
-
-redef class AFormaldef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-       end
-end
-
-redef class AType
-       redef fun accept_pretty_printer(v) do
-               if n_kwnullable != null then
-                       v.visit n_kwnullable
-                       v.adds
-               end
-
-               v.visit n_id
-
-               if not n_types.is_empty then
-                       v.consume "["
-                       v.visit_list n_types
-                       v.consume "]"
-               end
-       end
-end
-
-redef class ASuperclass
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwsuper
-               v.adds
-               v.visit n_type
-       end
-end
-
-# Properties
-
-redef class APropdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               v.addt
-
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               if n_kwredef != null then
-                       v.visit n_kwredef
-                       v.adds
-               end
-       end
-
-       redef fun start_token do
-               if n_doc == null then return super
-               return n_doc.last_token.next_token
-       end
-end
-
-redef class AAttrPropdef
-       redef fun accept_pretty_printer(v) do
-               super
-               v.visit n_kwvar
-               v.adds
-               v.visit n_id2
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_expr != null then
-                       v.adds
-                       v.consume "="
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_annotations != null then v.visit n_annotations
-               v.finish_line
-               v.addn
-       end
-
-       redef fun first_token do
-               if n_doc != null then return n_doc.first_token
-               if not n_visibility isa APublicVisibility then return n_visibility.first_token
-               if n_kwredef != null then return n_kwredef
-               return n_kwvar
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class ATypePropdef
-       redef fun accept_pretty_printer(v) do
-               super
-               v.visit n_kwtype
-               v.adds
-               v.visit n_id
-               v.consume ":"
-               v.adds
-               v.visit n_type
-               v.finish_line
-               v.addn
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AMethPropdef
-       redef fun accept_pretty_printer(v) do
-               #  TODO: Handle extern annotations
-
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-               super
-               if n_kwinit != null then v.visit n_kwinit
-               if n_kwmeth != null then v.visit n_kwmeth
-               if n_kwnew != null then v.visit n_kwnew
-
-               if not n_methid == null then
-                       v.adds
-                       v.visit n_methid
-               end
-
-               v.visit n_signature
-
-               if n_annotations != null then
-                       v.visit n_annotations
-               else
-                       v.adds
-               end
-
-               if n_extern_calls != null or n_extern_code_block != null then
-                       if n_annotations != null then v.adds
-                       if n_extern_calls != null then v.visit n_extern_calls
-                       if n_extern_code_block != null then v.visit n_extern_code_block
-               end
-
-               var n_block = self.n_block
-
-               if n_block != null then
-                       while not v.current_token isa TKwdo do v.skip
-                       if n_annotations != null then
-                               if v.can_inline(n_annotations) then
-                                       v.adds
-                               else
-                                       v.addt
-                               end
-                       end
-                       v.consume "do"
-
-                       if can_inline then
-                               v.adds
-
-                               if n_block isa ABlockExpr then
-                                       if n_block.n_expr.is_empty then
-                                               v.visit n_block.n_kwend
-                                       else
-                                               v.visit n_block.n_expr.first
-                                               v.current_token = n_block.n_kwend
-                                               v.skip
-                                       end
-                               else
-                                       v.visit n_block
-                                       if v.current_token isa TKwend then v.skip
-                               end
-                       else
-                               v.finish_line
-                               v.addn
-                               v.indent += 1
-
-                               if n_block isa ABlockExpr then
-                                       n_block.force_block = true
-                                       v.visit n_block
-                                       v.catch_up n_block.n_kwend
-                               else
-                                       v.addt
-                                       v.visit n_block
-                                       v.addn
-                               end
-
-                               v.indent -= 1
-                               v.addt
-                               if n_block isa ABlockExpr then
-                                       v.visit n_block.n_kwend
-                               else
-                                       v.add "end"
-                               end
-                       end
-               end
-
-               v.finish_line
-               v.addn
-               assert v.indent == before
-       end
-
-       # Can be inlined if:
-       # * block is empty or can be inlined
-       # * contains no comments
-       redef fun is_inlinable do
-               if not super then return false
-               if n_annotations != null and not n_annotations.is_inlinable then return false
-               if n_block != null and not n_block.is_inlinable then return false
-               if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
-               if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-end
-
-redef class AMainMethPropdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_block
-               v.addn
-       end
-end
-
-redef class ASignature
-       redef fun accept_pretty_printer(v) do
-               if not n_params.is_empty then
-                       v.consume "("
-                       v.visit_list n_params
-                       v.consume ")"
-               end
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-       end
-end
-
-redef class AParam
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_dotdotdot != null then v.visit n_dotdotdot
-       end
-end
-
-# Extern
-
-redef class AExternCalls
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwimport
-
-               if can_inline then
-                       v.adds
-                       v.visit_list n_extern_calls
-               else
-                       v.addn
-                       v.addt
-                       v.addt
-                       v.visit_list n_extern_calls
-               end
-
-               v.adds
-       end
-end
-
-redef class AFullPropExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.visit n_dot
-               v.visit n_methid
-       end
-end
-
-redef class ALocalPropExternCall
-       redef fun accept_pretty_printer(v) do v.visit n_methid
-end
-
-redef class AInitPropExternCall
-       redef fun accept_pretty_printer(v) do v.visit n_type
-end
-
-redef class ACastAsExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_from_type
-               v.visit n_dot
-               v.visit n_kwas
-               v.consume "("
-               v.visit n_to_type
-               v.consume ")"
-       end
-end
-
-redef class AAsNullableExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.consume "."
-               v.visit n_kwas
-               v.adds
-               v.visit n_kwnullable
-       end
-end
-
-redef class AAsNotNullableExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.consume "."
-               v.visit n_kwas
-               v.adds
-               v.visit n_kwnot
-               v.adds
-               v.visit n_kwnullable
-       end
-end
-
-redef class AExternCodeBlock
-       redef fun accept_pretty_printer(v) do
-               if n_in_language != null then
-                       v.visit n_in_language
-                       v.adds
-               end
-
-               v.visit n_extern_code_segment
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               return n_extern_code_segment.is_inlinable
-       end
-end
-
-redef class AInLanguage
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwin
-               v.adds
-               v.visit n_string
-       end
-end
-
-redef class TExternCodeSegment
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if can_inline then
-                       super
-               else
-                       var text = text.substring(2, text.length - 4)
-                       var lines = text.r_trim.split("\n")
-
-                       if text.is_empty then
-                               v.add "`\{`\}"
-                       else
-                               v.add "`\{"
-
-                               if not lines.first.trim.is_empty then
-                                       v.addn
-                                       lines.first.l_trim
-                                       v.indent += 1
-                                       v.addt
-                                       v.indent -= 1
-                               end
-
-                               for line in lines do
-                                       v.add line.r_trim
-                                       v.addn
-                               end
-
-                               v.addt
-                               v.add "`\}"
-                       end
-
-                       v.current_token = next_token
-               end
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               return location.line_start == location.line_end
-       end
-end
-
-# Blocks
-
-redef class ABlockExpr
-       redef fun accept_pretty_printer(v) do
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-
-               if can_inline and not n_expr.is_empty then
-                       v.visit n_expr.first
-                       v.finish_line
-               else
-                       for nexpr in n_expr do
-                               var expr_inline = v.can_inline(nexpr)
-                               if not expr_inline and nexpr != n_expr.first then v.addn
-                               v.catch_up nexpr
-                               v.addt
-                               v.visit nexpr
-                               v.finish_line
-                               v.addn
-                               if not expr_inline and nexpr != n_expr.last then v.addn
-                       end
-               end
-
-               assert v.indent == before
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if not collect_comments.is_empty then return false
-
-               if not n_expr.is_empty then
-                       if n_expr.length > 1 then return false
-                       if not n_expr.first.is_inlinable then return false
-               end
-
-               return true
-       end
-end
-
-redef class AIfExpr
-       redef fun accept_pretty_printer(v) do
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-               v.visit n_kwif
-               v.adds
-
-               if v.can_inline(n_expr) then
-                       v.visit n_expr
-                       v.adds
-               else
-                       v.visit n_expr
-                       v.addn
-                       v.addt
-               end
-
-               # skip comments before `then` token
-               while not v.current_token isa TKwthen do v.skip
-               v.consume "then"
-               var n_else = self.n_else
-
-               if can_inline then
-                       v.adds
-                       if n_then != null then v.visit n_then
-
-                       if has_else(v) then
-                               n_else.force_inline = true
-                               v.adds
-                               v.consume "else"
-                               v.adds
-                               v.visit n_else
-                       else if n_then == null then
-                               v.add "end"
-                       end
-
-                       v.skip_to last_token.last_real_token_in_line
-               else
-                       v.finish_line
-                       v.addn
-                       v.indent += 1
-
-                       if n_then != null then
-                               if n_then isa ABlockExpr then
-                                       n_then.force_block = true
-                                       v.visit n_then
-                               else
-                                       v.addt
-                                       v.visit n_then
-                                       v.addn
-                               end
-                       end
-
-                       if has_else(v) then
-                               while not v.current_token isa TKwelse do
-                                       v.consume v.current_token.text
-                               end
-
-                               v.indent -= 1
-                               v.addt
-                               v.consume "else"
-
-                               if n_else isa AIfExpr then
-                                       n_else.force_block = true
-                                       v.adds
-                                       v.visit n_else
-                               else
-                                       v.finish_line
-                                       v.addn
-                                       v.indent += 1
-
-                                       if n_else isa ABlockExpr then
-                                               n_else.force_block = true
-                                               v.visit n_else
-                                       else
-                                               v.addt
-                                               v.visit n_else
-                                               v.addn
-                                       end
-
-                                       if last_token isa TKwend then
-                                               v.catch_up last_token
-                                               v.indent -= 1
-                                               v.addt
-                                               v.consume "end"
-                                       else
-                                               v.indent -= 1
-                                               v.addt
-                                               v.add "end"
-                                       end
-                               end
-                       else
-                               if last_token.location >= v.current_token.location then v.catch_up last_token
-                               v.indent -= 1
-                               v.addt
-                               v.add "end"
-                               if v.current_token isa TKwend then v.skip
-                       end
-               end
-
-               assert v.indent == before
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if n_then != null and not n_then.is_inlinable then return false
-               var n_else = self.n_else
-               if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
-                  (not n_else isa ABlockExpr and n_else != null) then
-                       return false
-               end
-               if not collect_comments.is_empty then return false
-               return true
-       end
-
-       # Does this `if` statement contains a `else` part?
-       private fun has_else(v: PrettyPrinterVisitor): Bool do
-               var n_else = n_else
-               if n_else == null then return false
-               var n_kwelse = collect_kwelse
-               if n_kwelse == null then return false
-
-               if n_else isa ABlockExpr then
-                       var comments: Array[TComment]
-
-                       if n_then == null then
-                               comments = v.collect_comments(n_expr.last_token, n_else.last_token)
-                       else
-                               comments = v.collect_comments(n_then.last_token, n_else.last_token)
-                       end
-
-                       if not comments.is_empty then return true
-                       return not n_else.n_expr.is_empty
-               end
-
-               return true
-       end
-
-       # Lookup for `else` token in `self`.
-       private fun collect_kwelse: nullable TKwelse do
-               var token = first_token
-
-               while token != last_token do
-                       if token isa TKwelse then return token
-                       token = token.next_token
-               end
-
-               return null
-       end
-end
-
-# Used to factorize work on loops.
-private class ALoopHelper
-       super AExpr
-
-       fun loop_block: nullable ANode is abstract
-       fun loop_label: nullable ANode is abstract
-
-       fun visit_loop_block(v: PrettyPrinterVisitor) do
-               var n_block = loop_block
-               v.finish_line
-               v.addn
-               v.indent += 1
-
-               if n_block isa ABlockExpr then
-                       n_block.force_block = true
-                       v.visit n_block
-                       v.catch_up n_block.n_kwend
-                       v.indent -= 1
-                       v.addt
-                       v.visit n_block.n_kwend
-               else
-                       v.addt
-                       v.visit n_block
-                       v.addn
-                       v.indent -= 1
-                       v.addt
-                       v.add "end"
-               end
-
-               if loop_label != null then
-                       v.adds
-                       v.visit loop_label
-               end
-       end
-
-       fun visit_loop_inline(v: PrettyPrinterVisitor) do
-               var n_block = loop_block
-               v.adds
-
-               if n_block isa ABlockExpr then
-                       if n_block.n_expr.is_empty then
-                               v.visit n_block.n_kwend
-                       else
-                               v.visit n_block.n_expr.first
-                               v.current_token = n_block.n_kwend
-                               v.skip
-                       end
-               else
-                       v.visit n_block
-                       if v.current_token isa TKwend then v.skip
-               end
-
-               if loop_label != null then
-                       v.adds
-                       v.visit loop_label
-               end
-       end
-
-       redef fun is_inlinable do
-               var n_block = loop_block
-               if not super then return false
-               if n_block isa ABlockExpr and not n_block.is_inlinable then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-end
-
-redef class ALoopExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwloop
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class AWhileExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwwhile
-               v.adds
-               v.visit n_expr
-               v.adds
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class ADoExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class AForExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwfor
-               v.adds
-
-               for n_id in n_ids do
-                       v.visit n_id
-                       if n_id != n_ids.last then v.add ", "
-               end
-
-               v.adds
-               v.consume "in"
-               v.adds
-               v.visit n_expr
-               v.adds
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class ABreakExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwbreak
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_label != null then
-                       v.adds
-                       v.visit n_label
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AContinueExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwcontinue
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_label != null then
-                       v.adds
-                       v.visit n_label
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Calls
-
-redef class ASendExpr
-       redef fun is_inlinable do return true
-end
-
-redef class ACallExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit_recv n_expr
-
-               if not n_expr isa AImplicitSelfExpr and not can_inline then
-                       v.addn
-                       v.addt
-                       v.addt
-               end
-
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       if is_stmt and n_args.n_exprs.length == 1 then
-                               v.adds
-                               if v.current_token isa TOpar then v.skip
-                               v.visit n_args.n_exprs.first
-                               if v.current_token isa TCpar then v.skip
-                       else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
-                       end
-               end
-       end
-
-       # Is the call alone on its line?
-       fun is_stmt: Bool do return parent isa ABlockExpr
-end
-
-redef class ACallAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-
-               v.adds
-               v.consume "="
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ACallReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ABraExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-       end
-end
-
-redef class ABraAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ABraReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAssignMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-               v.visit n_assign
-       end
-end
-
-redef class ABraMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_cbra
-       end
-end
-
-redef class ABraassignMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_cbra
-               v.visit n_assign
-       end
-end
-
-redef class AInitExpr
-       redef fun accept_pretty_printer(v) do
-               if not n_expr isa AImplicitSelfExpr then
-                       v.visit n_expr
-                       v.consume "."
-               end
-
-               v.visit n_kwinit
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-       end
-end
-
-redef class ANewExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwnew
-               v.adds
-               v.visit n_type
-
-               if n_id != null then
-                       v.consume "."
-
-                       if not can_inline then
-                               v.addn
-                               v.addt
-                               v.addt
-                       end
-
-                       v.visit n_id
-               end
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Attributes
-
-redef class AAttrExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AAttrAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAttrReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-# Exprs
-
-redef class AVardeclExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwvar
-               v.adds
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_expr != null then
-                       v.adds
-                       v.consume "="
-                       v.adds
-                       v.visit n_expr
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AVarAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAssertExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwassert
-
-               if n_id != null then
-                       v.adds
-                       v.visit n_id
-                       v.consume ":"
-               end
-
-               v.adds
-               v.visit n_expr
-               var n_else = self.n_else
-
-               if n_else != null then
-                       v.adds
-                       v.consume "else"
-
-                       if can_inline then
-                               v.adds
-                               v.visit n_else
-                       else
-                               v.addn
-
-                               if n_else isa ABlockExpr then
-                                       v.indent += 1
-                                       n_else.force_block = true
-                                       v.visit n_else
-                                       v.indent -= 1
-                                       v.addt
-                                       v.visit n_else.n_kwend
-                               else
-                                       v.indent += 1
-                                       v.addt
-                                       v.visit n_else
-                                       v.addn
-                                       v.indent -= 1
-                                       v.addt
-                                       v.add "end"
-                               end
-                       end
-               end
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if n_else != null and not n_else.is_inlinable then return false
-               return true
-       end
-end
-
-redef class AReturnExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwreturn
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-       end
-end
-
-redef class ASuperExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwsuper
-
-               if not n_args.n_exprs.is_empty then
-                       if is_stmt and n_args.n_exprs.length == 1 then
-                               v.adds
-                               if v.current_token isa TOpar then v.skip
-                               v.visit n_args.n_exprs.first
-                               if v.current_token isa TCpar then v.skip
-                       else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
-                       end
-               end
-       end
-
-       # Is the call alone on its line?
-       fun is_stmt: Bool do return self.first_token.is_starting_line
-
-       redef fun is_inlinable do return true
-end
-
-redef class AOnceExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwonce
-               v.adds
-               v.visit n_expr
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AAbortExpr
-       redef fun accept_pretty_printer(v) do v.visit n_kwabort
-       redef fun is_inlinable do return true
-end
-
-redef class ANotExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwnot
-               v.adds
-               v.visit n_expr
-       end
-end
-
-redef class AAsCastExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.consume "."
-               v.visit n_kwas
-               v.visit n_opar
-               v.visit n_type
-               v.visit n_cpar
-       end
-end
-
-redef class AAsNotnullExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.consume "."
-               v.visit n_kwas
-               v.visit n_opar
-               v.visit n_kwnot
-               v.adds
-               v.visit n_kwnull
-               v.visit n_cpar
-       end
-end
-
-# Binops
-
-# Used to factorize work on Or, And, Implies and Binop expressions.
-private class ABinOpHelper
-       super AExpr
-
-       fun bin_expr1: AExpr is abstract
-       fun bin_expr2: AExpr is abstract
-
-       # Operator string
-       fun bin_op: String is abstract
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if not can_inline then
-                       if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
-                          (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
-                          (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
-                       then
-                               bin_expr1.force_block = true
-                       end
-               end
-
-               v.visit bin_expr1
-               v.adds
-               v.consume bin_op
-
-               if can_inline then
-                       v.adds
-                       v.visit bin_expr2
-               else
-                       v.addn
-                       v.addt
-                       v.addt
-                       v.visit bin_expr2
-               end
-       end
-end
-
-redef class AAndExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "and"
-end
-
-redef class AOrExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "or"
-end
-
-redef class AImpliesExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "implies"
-end
-
-redef class ABinopExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-end
-
-redef class AEqExpr
-       redef fun bin_op do return "=="
-end
-
-redef class AGeExpr
-       redef fun bin_op do return ">="
-end
-
-redef class AGgExpr
-       redef fun bin_op do return ">>"
-end
-
-redef class AGtExpr
-       redef fun bin_op do return ">"
-end
-
-redef class ALeExpr
-       redef fun bin_op do return "<="
-end
-
-redef class ALlExpr
-       redef fun bin_op do return "<<"
-end
-
-redef class ALtExpr
-       redef fun bin_op do return "<"
-end
-
-redef class AMinusExpr
-       redef fun bin_op do return "-"
-end
-
-redef class ANeExpr
-       redef fun bin_op do return "!="
-end
-
-redef class APercentExpr
-       redef fun bin_op do return "%"
-end
-
-redef class APlusExpr
-       redef fun bin_op do return "+"
-end
-
-redef class ASlashExpr
-       redef fun bin_op do return "/"
-end
-
-redef class AStarExpr
-       redef fun bin_op do return "*"
-end
-
-redef class AStarstarExpr
-       redef fun bin_op do return "**"
-end
-
-redef class AStarshipExpr
-       redef fun bin_op do return "<=>"
-end
-
-redef class AIsaExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.adds
-               v.consume "isa"
-               v.adds
-               v.visit n_type
-       end
-end
-
-redef class AOrElseExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.adds
-               v.consume "or"
-               v.adds
-               v.consume "else"
-               v.adds
-               v.visit n_expr2
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Syntax
-
-redef class AUminusExpr
-       redef fun accept_pretty_printer(v) do
-               v.consume "-"
-               v.visit n_expr
-       end
-end
-
-redef class ANullExpr
-       redef fun accept_pretty_printer(v) do v.visit n_kwnull
-       redef fun is_inlinable do return true
-end
-
-redef class AParExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_opar
-               v.visit n_expr
-               v.visit n_cpar
-       end
-end
-
-redef class AArrayExpr
-       redef fun accept_pretty_printer(v) do
-               v.consume "["
-               v.visit_list n_exprs.n_exprs
-               v.consume "]"
-       end
-end
-
-redef class ACrangeExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_expr
-               v.consume ".."
-               v.visit n_expr2
-               v.visit n_cbra
-       end
-end
-
-redef class AOrangeExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_expr
-               v.consume ".."
-               v.visit n_expr2
-               v.visit n_cbra
-       end
-end
-
-# Strings
-
-redef class AStringFormExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if can_inline then
-                       v.visit n_string
-               else
-                       var text = n_string.text
-                       var i = 0
-
-                       while i < text.length do
-                               v.add text[i].to_s
-
-                               if v.current_length >= v.max_size and i <= text.length - 3 then
-                                       v.add "\" +"
-                                       v.addn
-                                       v.indent += 1
-                                       v.addt
-                                       v.indent -= 1
-                                       v.add "\""
-                               end
-
-                               i += 1
-                       end
-
-                       v.current_token = n_string.next_token
-               end
-       end
-end
-
-redef class ASuperstringExpr
-       redef fun accept_pretty_printer(v) do
-               for n_expr in n_exprs do v.visit n_expr
-       end
-
-       redef fun must_be_inline do
-               if super then return true
-
-               if not n_exprs.is_empty then
-                       var first = n_exprs.first
-                       return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
-               end
-
-               return false
-       end
-end
+import pretty
 
 redef class ToolContext
        var opt_dir = new OptionString("Working directory (default is '.nitpretty')", "--dir")
diff --git a/src/pretty.nit b/src/pretty.nit
new file mode 100644 (file)
index 0000000..3b3cb90
--- /dev/null
@@ -0,0 +1,2103 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module pretty
+
+import template
+import toolcontext
+import modelbuilder
+import astutil
+
+# The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
+#
+# The main method here is `visit` that performs the pretty printing of a `ANode`.
+#
+# Because some tokens like `TComment` are not stored in the AST,
+# we visit the AST like traditionnal visitor and also maintain a
+# pointer to the `current_token` (actually the next one to be printed).
+#
+# Visited productions are in charges to move the token pointer using methods such as:
+#
+# * `consume`: print `current_token` and move to the next one
+# * `skip`: move to the next token without printing the current one
+# * `skip_to`: move to a specified token skipping all the tokens before
+# * `catch_up`: consume all the tokens between `current_token` and a specified token
+# * `finish_line`: consume all the tokens between `current_token` and the end of line
+class PrettyPrinterVisitor
+       # Pretty print `n`.
+       fun pretty(n: ANode): Template do
+               clear
+               n.parentize_tokens
+
+               if n isa Prod then
+                       current_token = n.first_token
+                       visit n
+               else if n isa Token then
+                       var p = n.parent
+
+                       while p != null and not p isa Prod do
+                               p = p.parent
+                       end
+
+                       current_token = p.first_token
+                       visit p
+               end
+
+               return tpl.as(not null)
+       end
+
+       # Pretty print the whole `nmodule` with comments before and after.
+       fun pretty_nmodule(nmodule: AModule): Template do
+               clear
+               nmodule.parentize_tokens
+               current_token = nmodule.location.file.first_token
+               visit nmodule
+               catch_up nmodule.location.file.last_token
+               tpl.add "\n"
+               return tpl.as(not null)
+       end
+
+       # Prepare `self` for a new visit.
+       private fun clear do
+               tpl = new Template
+               current_token = null
+               indent = 0
+               current_length = 0
+               previous_length = 0
+               wait_addn = 0
+       end
+
+       # Visit `n` if not null.
+       fun visit(n: nullable ANode) do
+               if n == null then return
+               n.accept_pretty_printer self
+       end
+
+       # Visit a list of `Anode`.
+       fun visit_list(n: nullable ANodes[ANode]) do
+               if n == null then return
+               n.accept_pretty_printer self
+       end
+
+       # Is the node inlinable and can fit on the line.
+       fun can_inline(n: nullable ANode): Bool do
+               if n == null then return true
+               if n.must_be_inline then return true
+               if n.must_be_block then return false
+               # check length
+               if n.collect_length + current_length > max_size then return false
+               # check block is inlinable
+               return n.is_inlinable
+       end
+
+       # Collect all `TComment` between `from` and `to`.
+       fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
+               var res = new Array[TComment]
+               if from isa Prod then from = from.first_token
+               if to isa Prod then to = to.first_token
+               if from == null or to == null then return res
+
+               while from != to do
+                       if from isa TComment then res.add from
+                       from = from.as(Token).next_token
+               end
+
+               return res
+       end
+
+       # Token under cursor.
+       #
+       # This is the next token to visit.
+       var current_token: nullable Token = null
+
+       # Skip the `current_token`.
+       fun skip do current_token = current_token.next_token
+
+       # Skip `current_token` until the end of line.
+       fun skip_line do current_token = current_token.last_real_token_in_line
+
+       # Skip `current_token` until `target` is reached.
+       fun skip_to(target: nullable Token) do
+               if target == null then return
+               while current_token != target do skip
+       end
+
+       # Visit `current_token`.
+       fun consume(token: String) do
+               assert current_token.text == token
+               visit current_token
+       end
+
+       # Is there token to visit between `current_token` and `target`?
+       fun need_catch_up(target: nullable Token): Bool do
+               if target == null then return false
+               return current_token != target
+       end
+
+       # Visit all tokens between `current_token` and `target`.
+       fun catch_up(target: nullable ANode) do
+               if target == null then return
+               if current_token == null then return
+               var token: Token
+               if target isa Token then
+                       token = target
+               else if target isa Prod then
+                       token = target.first_token.as(not null)
+               else
+                       abort
+               end
+               assert current_token.location <= token.location
+               while current_token != token do visit current_token
+       end
+
+       # Visit all tokens between `current_token` and the end of line.
+       fun finish_line do
+               if current_token isa TComment then
+                       adds
+                       visit current_token
+               end
+
+               while current_token isa TEol do skip
+       end
+
+       # The template under construction.
+       private var tpl: nullable Template = null
+
+       # Current indent level.
+       var indent = 0
+
+       # Size of a tabulation in spaces.
+       var tab_size = 8
+
+       # Max line size.
+       var max_size = 80
+
+       # Length of the current line.
+       var current_length = 0
+
+       # Length of the previous line.
+       var previous_length = 0
+
+       # Is the last line a blank line?
+       fun last_line_is_blank: Bool do return previous_length == 0
+
+       # Add `t` to current template.
+       fun add(t: String) do
+               if t.is_empty then return
+               while wait_addn > 0 do
+                       tpl.add "\n"
+                       wait_addn -= 1
+               end
+               tpl.add t
+               current_length += t.length
+       end
+
+       # Add a `'\n'`.
+       fun addn do
+               if current_length == 0 and last_line_is_blank then return
+               previous_length = current_length
+               current_length = 0
+               wait_addn += 1
+       end
+
+       # End of line chars are stored until another char is added.
+       # This avoid empty files with only a '`\n`'.
+       private var wait_addn = 0
+
+       # Add `'\t' * indent`.
+       fun addt do add "\t" * indent
+
+       # Add a space.
+       fun adds do add " "
+
+       fun visit_recv(n_expr: AExpr) do
+               if not n_expr isa AImplicitSelfExpr then
+                       visit n_expr
+                       consume "."
+               end
+       end
+end
+
+# Base framework redefs
+
+redef class ANodes[E]
+       private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
+               for e in self do
+                       var e_can_inline = v.can_inline(e)
+
+                       if e != first then
+                               if not e_can_inline then
+                                       v.add ","
+                                       v.addn
+                                       v.addt
+                                       v.addt
+                               else
+                                       v.add ", "
+                               end
+                       end
+
+                       v.visit e
+               end
+       end
+end
+
+redef class ANode
+       # Start visit of `self` using a `PrettyPrinterVisitor`
+       private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract
+
+       # Collect the length (in `Char`) of the node.
+       private fun collect_length: Int is abstract
+
+       # Is `self` printable in one line?
+       private fun is_inlinable: Bool do return true
+
+       # Force `self` to be rendered as a block.
+       private var force_block = false
+
+       # Does `self` have to be rendered as a block?
+       private fun must_be_block: Bool do return force_block
+
+       # Force `self` to be rendered as a line.
+       private var force_inline = false
+
+       # Does `self` have be rendered as a line?
+       private fun must_be_inline: Bool do
+               if parent != null and parent.must_be_inline then return true
+               return force_inline
+       end
+
+       # Does `self` was written in one line before transformation?
+       private fun was_inline: Bool is abstract
+end
+
+redef class Token
+       redef fun accept_pretty_printer(v) do
+               v.add text.trim
+               v.current_token = next_token
+       end
+
+       redef fun collect_length do return text.length
+       redef fun is_inlinable do return true
+       redef fun was_inline do return true
+end
+
+redef class Prod
+       redef fun accept_pretty_printer(v) do v.visit first_token
+
+       # The token where the production really start (skipping ADoc).
+       private fun start_token: nullable Token do return first_token
+
+       # Collect all `TComment` contained in the production
+       # (between `start_token` and `end_token`).
+       private fun collect_comments: Array[TComment] do
+               var res = new Array[TComment]
+               if start_token == null or last_token == null then return res
+               var token = start_token
+
+               while token != last_token do
+                       if token isa TComment then res.add token
+                       token = token.next_token
+               end
+
+               return res
+       end
+
+       redef fun collect_length do
+               var res = 0
+               if start_token == null or last_token == null then return res
+               var token = start_token
+
+               while token != last_token do
+                       res += token.text.length
+                       token = token.next_token
+               end
+
+               res += token.text.length
+               return res
+       end
+
+       redef fun was_inline do
+               return first_token.location.line_start == last_token.location.line_end
+       end
+end
+
+# Comments
+
+redef class TComment
+       redef fun accept_pretty_printer(v) do
+               if is_adoc then
+                       v.addt
+                       super
+                       v.addn
+                       return
+               end
+
+               if is_licence then
+                       super
+                       v.addn
+                       if is_last_in_group then v.addn
+                       return
+               end
+
+               if is_orphan then
+                       v.addn
+                       v.addt
+                       super
+                       v.addn
+                       v.addn
+                       return
+               end
+
+               if is_inline then
+                       if next_token isa TComment and is_first_in_group then v.addn
+                       v.addt
+                       super
+                       v.addn
+                       var prev_token = self.prev_token
+                       if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
+                       return
+               end
+
+               super
+       end
+
+       # Is `self` part of an `ADoc`?
+       private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null
+
+       # Is `self` part of a licence?
+       private fun is_licence: Bool do
+               var prev_token = self.prev_token
+
+               if prev_token == null then
+                       return true
+               else if prev_token isa TComment then
+                       return prev_token.is_licence
+               else
+                       return false
+               end
+       end
+
+       # Is `self` starts and ends its line?
+       private fun is_inline: Bool do
+               return self == first_real_token_in_line and self == last_real_token_in_line
+       end
+
+       # Is `self` an orphan line (blank before and after)?
+       private fun is_orphan: Bool do
+               return prev_token isa TEol and
+                  (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
+                  next_token isa TEol
+       end
+
+       # Is `self` the first comment of a group of comment?
+       private fun is_first_in_group: Bool do return not prev_token isa TComment
+
+       # Is `self` the last comment of a group of comments?
+       private fun is_last_in_group: Bool do return not next_token isa TComment
+end
+
+redef class ADoc
+       redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
+       redef fun is_inlinable do return n_comment.length <= 1
+end
+
+# Annotations
+
+redef class AAnnotations
+       redef fun accept_pretty_printer(v) do
+               v.adds
+               v.consume "is"
+
+               if v.can_inline(self) then
+                       v.adds
+                       for n_item in n_items do
+                               v.visit n_item
+                               if n_item != n_items.last then
+                                       v.add ", "
+                               end
+                       end
+                       v.finish_line
+               else if n_items.length > 1 then
+                       v.addn
+                       v.indent += 1
+
+                       for n_item in n_items do
+                               v.addt
+                               v.visit n_item
+                               v.finish_line
+                               v.addn
+                       end
+
+                       v.indent -= 1
+               end
+               if not was_inline and v.current_token isa TKwend then v.skip
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               for annot in n_items do if not annot.is_inlinable then return false
+               return true
+       end
+end
+
+redef class AAnnotation
+       redef fun accept_pretty_printer(v) do
+               v.visit n_atid
+               if not n_args.is_empty then
+                       if n_opar == null then
+                               v.adds
+                       else
+                               v.visit n_opar
+                       end
+                       v.visit_list n_args
+                       v.visit n_cpar
+               end
+       end
+end
+
+redef class ATypeExpr
+       redef fun accept_pretty_printer(v) do v.visit n_type
+end
+
+# Modules
+
+redef class AModule
+       redef fun accept_pretty_printer(v) do
+               v.catch_up start_token
+               v.visit n_moduledecl
+
+               if not n_imports.is_empty then
+                       v.addn
+
+                       for n_import in n_imports do
+                               v.catch_up n_import
+                               v.visit n_import
+                       end
+               end
+
+               if not n_extern_code_blocks.is_empty then
+                       v.addn
+
+                       for n_extern_block in n_extern_code_blocks do
+                               v.catch_up n_extern_block
+                               v.visit n_extern_block
+                               v.addn
+                               if n_extern_block != n_extern_code_blocks.last then v.addn
+                       end
+
+                       if not n_classdefs.is_empty then v.addn
+               end
+
+               if not n_classdefs.is_empty then
+                       v.addn
+
+                       for n_classdef in n_classdefs do
+                               v.catch_up n_classdef
+                               v.visit n_classdef
+                               if n_classdef != n_classdefs.last then v.addn
+                       end
+               end
+
+               assert v.indent == 0
+       end
+
+       # Skip doc if any.
+       redef fun start_token do
+               if n_moduledecl != null then return n_moduledecl.first_token
+               if not n_imports.is_empty then return n_imports.first.first_token
+               if not n_classdefs.is_empty then return n_classdefs.first.first_token
+               return first_token
+       end
+
+       redef fun is_inlinable do return false
+end
+
+redef class AModuledecl
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               v.visit n_kwmodule
+               v.adds
+               v.visit n_name
+
+               if n_annotations != null then
+                       var annot_inline = v.can_inline(n_annotations)
+                       v.visit n_annotations
+
+                       if not annot_inline then
+                               if v.current_token isa TKwend then
+                                       v.consume "end"
+                                       v.finish_line
+                               else
+                                       v.add "end"
+                               end
+                       end
+               end
+
+               v.finish_line
+               v.addn
+       end
+end
+
+redef class AModuleName
+       redef fun accept_pretty_printer(v) do
+               for path in n_path do
+                       v.visit path
+                       v.add "::"
+               end
+
+               v.visit n_id
+       end
+end
+
+redef class ANoImport
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwimport
+               v.adds
+               v.visit n_kwend
+               v.finish_line
+               v.addn
+       end
+end
+
+redef class AStdImport
+       redef fun accept_pretty_printer(v) do
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               v.visit n_kwimport
+               v.adds
+               v.visit n_name
+               v.finish_line
+               v.addn
+       end
+end
+
+# Classes
+
+redef class AClassdef
+       redef fun accept_pretty_printer(v) do
+               for n_propdef in n_propdefs do
+                       v.catch_up n_propdef
+
+                       if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
+                               if n_propdef != n_propdefs.first then v.addn
+                               v.visit n_propdef
+                               if n_propdef != n_propdefs.last then v.addn
+                       else
+                               v.visit n_propdef
+                       end
+               end
+       end
+end
+
+redef class AStdClassdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               var can_inline = v.can_inline(self)
+
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               if n_kwredef != null then
+                       v.visit n_kwredef
+                       v.adds
+               end
+
+               v.visit n_classkind
+               v.adds
+               v.visit n_id
+
+               if not n_formaldefs.is_empty then
+                       v.consume "["
+                       v.visit_list n_formaldefs
+                       v.consume "]"
+               end
+
+               if n_extern_code_block != null then
+                       v.adds
+                       v.visit n_extern_code_block
+               end
+
+               if can_inline then
+                       v.adds
+
+                       if not n_superclasses.is_empty then
+                               for n_superclass in n_superclasses do
+                                       v.visit n_superclass
+                                       v.adds
+                               end
+                       end
+               else
+                       v.finish_line
+                       v.addn
+                       v.indent += 1
+
+                       for n_superclass in n_superclasses do
+                               v.catch_up n_superclass
+                               v.addt
+                               v.visit n_superclass
+                               v.finish_line
+                               v.addn
+                       end
+
+                       if not n_superclasses.is_empty and not n_propdefs.is_empty then
+                               v.addn
+                       end
+
+                       super
+                       v.catch_up n_kwend
+                       v.indent -= 1
+               end
+
+               v.visit n_kwend
+               v.finish_line
+               v.addn
+               assert v.indent == 0
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if not n_propdefs.is_empty then return false
+               if n_superclasses.length > 1 then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+
+       redef fun start_token do
+               if not n_visibility isa APublicVisibility then return n_visibility.first_token
+               if n_kwredef != null then return n_kwredef
+               return n_classkind.first_token
+       end
+end
+
+redef class AAbstractClasskind
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwabstract
+               v.adds
+               v.visit n_kwclass
+       end
+end
+
+redef class AExternClasskind
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwextern
+               v.adds
+               v.visit n_kwclass
+       end
+end
+
+redef class AFormaldef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+       end
+end
+
+redef class AType
+       redef fun accept_pretty_printer(v) do
+               if n_kwnullable != null then
+                       v.visit n_kwnullable
+                       v.adds
+               end
+
+               v.visit n_id
+
+               if not n_types.is_empty then
+                       v.consume "["
+                       v.visit_list n_types
+                       v.consume "]"
+               end
+       end
+end
+
+redef class ASuperclass
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwsuper
+               v.adds
+               v.visit n_type
+       end
+end
+
+# Properties
+
+redef class APropdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               v.addt
+
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               if n_kwredef != null then
+                       v.visit n_kwredef
+                       v.adds
+               end
+       end
+
+       redef fun start_token do
+               if n_doc == null then return super
+               return n_doc.last_token.next_token
+       end
+end
+
+redef class AAttrPropdef
+       redef fun accept_pretty_printer(v) do
+               super
+               v.visit n_kwvar
+               v.adds
+               v.visit n_id2
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_expr != null then
+                       v.adds
+                       v.consume "="
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_annotations != null then v.visit n_annotations
+               v.finish_line
+               v.addn
+       end
+
+       redef fun first_token do
+               if n_doc != null then return n_doc.first_token
+               if not n_visibility isa APublicVisibility then return n_visibility.first_token
+               if n_kwredef != null then return n_kwredef
+               return n_kwvar
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class ATypePropdef
+       redef fun accept_pretty_printer(v) do
+               super
+               v.visit n_kwtype
+               v.adds
+               v.visit n_id
+               v.consume ":"
+               v.adds
+               v.visit n_type
+               v.finish_line
+               v.addn
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AMethPropdef
+       redef fun accept_pretty_printer(v) do
+               #  TODO: Handle extern annotations
+
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+               super
+               if n_kwinit != null then v.visit n_kwinit
+               if n_kwmeth != null then v.visit n_kwmeth
+               if n_kwnew != null then v.visit n_kwnew
+
+               if not n_methid == null then
+                       v.adds
+                       v.visit n_methid
+               end
+
+               v.visit n_signature
+
+               if n_annotations != null then
+                       v.visit n_annotations
+               else
+                       v.adds
+               end
+
+               if n_extern_calls != null or n_extern_code_block != null then
+                       if n_annotations != null then v.adds
+                       if n_extern_calls != null then v.visit n_extern_calls
+                       if n_extern_code_block != null then v.visit n_extern_code_block
+               end
+
+               var n_block = self.n_block
+
+               if n_block != null then
+                       while not v.current_token isa TKwdo do v.skip
+                       if n_annotations != null then
+                               if v.can_inline(n_annotations) then
+                                       v.adds
+                               else
+                                       v.addt
+                               end
+                       end
+                       v.consume "do"
+
+                       if can_inline then
+                               v.adds
+
+                               if n_block isa ABlockExpr then
+                                       if n_block.n_expr.is_empty then
+                                               v.visit n_block.n_kwend
+                                       else
+                                               v.visit n_block.n_expr.first
+                                               v.current_token = n_block.n_kwend
+                                               v.skip
+                                       end
+                               else
+                                       v.visit n_block
+                                       if v.current_token isa TKwend then v.skip
+                               end
+                       else
+                               v.finish_line
+                               v.addn
+                               v.indent += 1
+
+                               if n_block isa ABlockExpr then
+                                       n_block.force_block = true
+                                       v.visit n_block
+                                       v.catch_up n_block.n_kwend
+                               else
+                                       v.addt
+                                       v.visit n_block
+                                       v.addn
+                               end
+
+                               v.indent -= 1
+                               v.addt
+                               if n_block isa ABlockExpr then
+                                       v.visit n_block.n_kwend
+                               else
+                                       v.add "end"
+                               end
+                       end
+               end
+
+               v.finish_line
+               v.addn
+               assert v.indent == before
+       end
+
+       # Can be inlined if:
+       # * block is empty or can be inlined
+       # * contains no comments
+       redef fun is_inlinable do
+               if not super then return false
+               if n_annotations != null and not n_annotations.is_inlinable then return false
+               if n_block != null and not n_block.is_inlinable then return false
+               if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
+               if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+end
+
+redef class AMainMethPropdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_block
+               v.addn
+       end
+end
+
+redef class ASignature
+       redef fun accept_pretty_printer(v) do
+               if not n_params.is_empty then
+                       v.consume "("
+                       v.visit_list n_params
+                       v.consume ")"
+               end
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+       end
+end
+
+redef class AParam
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_dotdotdot != null then v.visit n_dotdotdot
+       end
+end
+
+# Extern
+
+redef class AExternCalls
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwimport
+
+               if can_inline then
+                       v.adds
+                       v.visit_list n_extern_calls
+               else
+                       v.addn
+                       v.addt
+                       v.addt
+                       v.visit_list n_extern_calls
+               end
+
+               v.adds
+       end
+end
+
+redef class AFullPropExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.visit n_dot
+               v.visit n_methid
+       end
+end
+
+redef class ALocalPropExternCall
+       redef fun accept_pretty_printer(v) do v.visit n_methid
+end
+
+redef class AInitPropExternCall
+       redef fun accept_pretty_printer(v) do v.visit n_type
+end
+
+redef class ACastAsExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_from_type
+               v.visit n_dot
+               v.visit n_kwas
+               v.consume "("
+               v.visit n_to_type
+               v.consume ")"
+       end
+end
+
+redef class AAsNullableExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.consume "."
+               v.visit n_kwas
+               v.adds
+               v.visit n_kwnullable
+       end
+end
+
+redef class AAsNotNullableExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.consume "."
+               v.visit n_kwas
+               v.adds
+               v.visit n_kwnot
+               v.adds
+               v.visit n_kwnullable
+       end
+end
+
+redef class AExternCodeBlock
+       redef fun accept_pretty_printer(v) do
+               if n_in_language != null then
+                       v.visit n_in_language
+                       v.adds
+               end
+
+               v.visit n_extern_code_segment
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               return n_extern_code_segment.is_inlinable
+       end
+end
+
+redef class AInLanguage
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwin
+               v.adds
+               v.visit n_string
+       end
+end
+
+redef class TExternCodeSegment
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if can_inline then
+                       super
+               else
+                       var text = text.substring(2, text.length - 4)
+                       var lines = text.r_trim.split("\n")
+
+                       if text.is_empty then
+                               v.add "`\{`\}"
+                       else
+                               v.add "`\{"
+
+                               if not lines.first.trim.is_empty then
+                                       v.addn
+                                       lines.first.l_trim
+                                       v.indent += 1
+                                       v.addt
+                                       v.indent -= 1
+                               end
+
+                               for line in lines do
+                                       v.add line.r_trim
+                                       v.addn
+                               end
+
+                               v.addt
+                               v.add "`\}"
+                       end
+
+                       v.current_token = next_token
+               end
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               return location.line_start == location.line_end
+       end
+end
+
+# Blocks
+
+redef class ABlockExpr
+       redef fun accept_pretty_printer(v) do
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+
+               if can_inline and not n_expr.is_empty then
+                       v.visit n_expr.first
+                       v.finish_line
+               else
+                       for nexpr in n_expr do
+                               var expr_inline = v.can_inline(nexpr)
+                               if not expr_inline and nexpr != n_expr.first then v.addn
+                               v.catch_up nexpr
+                               v.addt
+                               v.visit nexpr
+                               v.finish_line
+                               v.addn
+                               if not expr_inline and nexpr != n_expr.last then v.addn
+                       end
+               end
+
+               assert v.indent == before
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if not collect_comments.is_empty then return false
+
+               if not n_expr.is_empty then
+                       if n_expr.length > 1 then return false
+                       if not n_expr.first.is_inlinable then return false
+               end
+
+               return true
+       end
+end
+
+redef class AIfExpr
+       redef fun accept_pretty_printer(v) do
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+               v.visit n_kwif
+               v.adds
+
+               if v.can_inline(n_expr) then
+                       v.visit n_expr
+                       v.adds
+               else
+                       v.visit n_expr
+                       v.addn
+                       v.addt
+               end
+
+               # skip comments before `then` token
+               while not v.current_token isa TKwthen do v.skip
+               v.consume "then"
+               var n_else = self.n_else
+
+               if can_inline then
+                       v.adds
+                       if n_then != null then v.visit n_then
+
+                       if has_else(v) then
+                               n_else.force_inline = true
+                               v.adds
+                               v.consume "else"
+                               v.adds
+                               v.visit n_else
+                       else if n_then == null then
+                               v.add "end"
+                       end
+
+                       v.skip_to last_token.last_real_token_in_line
+               else
+                       v.finish_line
+                       v.addn
+                       v.indent += 1
+
+                       if n_then != null then
+                               if n_then isa ABlockExpr then
+                                       n_then.force_block = true
+                                       v.visit n_then
+                               else
+                                       v.addt
+                                       v.visit n_then
+                                       v.addn
+                               end
+                       end
+
+                       if has_else(v) then
+                               while not v.current_token isa TKwelse do
+                                       v.consume v.current_token.text
+                               end
+
+                               v.indent -= 1
+                               v.addt
+                               v.consume "else"
+
+                               if n_else isa AIfExpr then
+                                       n_else.force_block = true
+                                       v.adds
+                                       v.visit n_else
+                               else
+                                       v.finish_line
+                                       v.addn
+                                       v.indent += 1
+
+                                       if n_else isa ABlockExpr then
+                                               n_else.force_block = true
+                                               v.visit n_else
+                                       else
+                                               v.addt
+                                               v.visit n_else
+                                               v.addn
+                                       end
+
+                                       if last_token isa TKwend then
+                                               v.catch_up last_token
+                                               v.indent -= 1
+                                               v.addt
+                                               v.consume "end"
+                                       else
+                                               v.indent -= 1
+                                               v.addt
+                                               v.add "end"
+                                       end
+                               end
+                       else
+                               if last_token.location >= v.current_token.location then v.catch_up last_token
+                               v.indent -= 1
+                               v.addt
+                               v.add "end"
+                               if v.current_token isa TKwend then v.skip
+                       end
+               end
+
+               assert v.indent == before
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if n_then != null and not n_then.is_inlinable then return false
+               var n_else = self.n_else
+               if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
+                  (not n_else isa ABlockExpr and n_else != null) then
+                       return false
+               end
+               if not collect_comments.is_empty then return false
+               return true
+       end
+
+       # Does this `if` statement contains a `else` part?
+       private fun has_else(v: PrettyPrinterVisitor): Bool do
+               var n_else = n_else
+               if n_else == null then return false
+               var n_kwelse = collect_kwelse
+               if n_kwelse == null then return false
+
+               if n_else isa ABlockExpr then
+                       var comments: Array[TComment]
+
+                       if n_then == null then
+                               comments = v.collect_comments(n_expr.last_token, n_else.last_token)
+                       else
+                               comments = v.collect_comments(n_then.last_token, n_else.last_token)
+                       end
+
+                       if not comments.is_empty then return true
+                       return not n_else.n_expr.is_empty
+               end
+
+               return true
+       end
+
+       # Lookup for `else` token in `self`.
+       private fun collect_kwelse: nullable TKwelse do
+               var token = first_token
+
+               while token != last_token do
+                       if token isa TKwelse then return token
+                       token = token.next_token
+               end
+
+               return null
+       end
+end
+
+# Used to factorize work on loops.
+private class ALoopHelper
+       super AExpr
+
+       fun loop_block: nullable ANode is abstract
+       fun loop_label: nullable ANode is abstract
+
+       fun visit_loop_block(v: PrettyPrinterVisitor) do
+               var n_block = loop_block
+               v.finish_line
+               v.addn
+               v.indent += 1
+
+               if n_block isa ABlockExpr then
+                       n_block.force_block = true
+                       v.visit n_block
+                       v.catch_up n_block.n_kwend
+                       v.indent -= 1
+                       v.addt
+                       v.visit n_block.n_kwend
+               else
+                       v.addt
+                       v.visit n_block
+                       v.addn
+                       v.indent -= 1
+                       v.addt
+                       v.add "end"
+               end
+
+               if loop_label != null then
+                       v.adds
+                       v.visit loop_label
+               end
+       end
+
+       fun visit_loop_inline(v: PrettyPrinterVisitor) do
+               var n_block = loop_block
+               v.adds
+
+               if n_block isa ABlockExpr then
+                       if n_block.n_expr.is_empty then
+                               v.visit n_block.n_kwend
+                       else
+                               v.visit n_block.n_expr.first
+                               v.current_token = n_block.n_kwend
+                               v.skip
+                       end
+               else
+                       v.visit n_block
+                       if v.current_token isa TKwend then v.skip
+               end
+
+               if loop_label != null then
+                       v.adds
+                       v.visit loop_label
+               end
+       end
+
+       redef fun is_inlinable do
+               var n_block = loop_block
+               if not super then return false
+               if n_block isa ABlockExpr and not n_block.is_inlinable then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+end
+
+redef class ALoopExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwloop
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class AWhileExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwwhile
+               v.adds
+               v.visit n_expr
+               v.adds
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class ADoExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class AForExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwfor
+               v.adds
+
+               for n_id in n_ids do
+                       v.visit n_id
+                       if n_id != n_ids.last then v.add ", "
+               end
+
+               v.adds
+               v.consume "in"
+               v.adds
+               v.visit n_expr
+               v.adds
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class ABreakExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwbreak
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_label != null then
+                       v.adds
+                       v.visit n_label
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AContinueExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwcontinue
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_label != null then
+                       v.adds
+                       v.visit n_label
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Calls
+
+redef class ASendExpr
+       redef fun is_inlinable do return true
+end
+
+redef class ACallExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit_recv n_expr
+
+               if not n_expr isa AImplicitSelfExpr and not can_inline then
+                       v.addn
+                       v.addt
+                       v.addt
+               end
+
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       if is_stmt and n_args.n_exprs.length == 1 then
+                               v.adds
+                               if v.current_token isa TOpar then v.skip
+                               v.visit n_args.n_exprs.first
+                               if v.current_token isa TCpar then v.skip
+                       else
+                               if v.current_token isa TOpar then
+                                       v.consume "("
+                               else
+                                       v.adds
+                               end
+
+                               v.visit_list n_args.n_exprs
+                               if v.current_token isa TCpar then v.consume ")"
+                       end
+               end
+       end
+
+       # Is the call alone on its line?
+       fun is_stmt: Bool do return parent isa ABlockExpr
+end
+
+redef class ACallAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+
+               v.adds
+               v.consume "="
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ACallReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ABraExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+       end
+end
+
+redef class ABraAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ABraReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAssignMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+               v.visit n_assign
+       end
+end
+
+redef class ABraMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_cbra
+       end
+end
+
+redef class ABraassignMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_cbra
+               v.visit n_assign
+       end
+end
+
+redef class AInitExpr
+       redef fun accept_pretty_printer(v) do
+               if not n_expr isa AImplicitSelfExpr then
+                       v.visit n_expr
+                       v.consume "."
+               end
+
+               v.visit n_kwinit
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+       end
+end
+
+redef class ANewExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwnew
+               v.adds
+               v.visit n_type
+
+               if n_id != null then
+                       v.consume "."
+
+                       if not can_inline then
+                               v.addn
+                               v.addt
+                               v.addt
+                       end
+
+                       v.visit n_id
+               end
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Attributes
+
+redef class AAttrExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AAttrAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAttrReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+# Exprs
+
+redef class AVardeclExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwvar
+               v.adds
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_expr != null then
+                       v.adds
+                       v.consume "="
+                       v.adds
+                       v.visit n_expr
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AVarAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAssertExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwassert
+
+               if n_id != null then
+                       v.adds
+                       v.visit n_id
+                       v.consume ":"
+               end
+
+               v.adds
+               v.visit n_expr
+               var n_else = self.n_else
+
+               if n_else != null then
+                       v.adds
+                       v.consume "else"
+
+                       if can_inline then
+                               v.adds
+                               v.visit n_else
+                       else
+                               v.addn
+
+                               if n_else isa ABlockExpr then
+                                       v.indent += 1
+                                       n_else.force_block = true
+                                       v.visit n_else
+                                       v.indent -= 1
+                                       v.addt
+                                       v.visit n_else.n_kwend
+                               else
+                                       v.indent += 1
+                                       v.addt
+                                       v.visit n_else
+                                       v.addn
+                                       v.indent -= 1
+                                       v.addt
+                                       v.add "end"
+                               end
+                       end
+               end
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if n_else != null and not n_else.is_inlinable then return false
+               return true
+       end
+end
+
+redef class AReturnExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwreturn
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+       end
+end
+
+redef class ASuperExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwsuper
+
+               if not n_args.n_exprs.is_empty then
+                       if is_stmt and n_args.n_exprs.length == 1 then
+                               v.adds
+                               if v.current_token isa TOpar then v.skip
+                               v.visit n_args.n_exprs.first
+                               if v.current_token isa TCpar then v.skip
+                       else
+                               if v.current_token isa TOpar then
+                                       v.consume "("
+                               else
+                                       v.adds
+                               end
+
+                               v.visit_list n_args.n_exprs
+                               if v.current_token isa TCpar then v.consume ")"
+                       end
+               end
+       end
+
+       # Is the call alone on its line?
+       fun is_stmt: Bool do return self.first_token.is_starting_line
+
+       redef fun is_inlinable do return true
+end
+
+redef class AOnceExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwonce
+               v.adds
+               v.visit n_expr
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AAbortExpr
+       redef fun accept_pretty_printer(v) do v.visit n_kwabort
+       redef fun is_inlinable do return true
+end
+
+redef class ANotExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwnot
+               v.adds
+               v.visit n_expr
+       end
+end
+
+redef class AAsCastExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.consume "."
+               v.visit n_kwas
+               v.visit n_opar
+               v.visit n_type
+               v.visit n_cpar
+       end
+end
+
+redef class AAsNotnullExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.consume "."
+               v.visit n_kwas
+               v.visit n_opar
+               v.visit n_kwnot
+               v.adds
+               v.visit n_kwnull
+               v.visit n_cpar
+       end
+end
+
+# Binops
+
+# Used to factorize work on Or, And, Implies and Binop expressions.
+private class ABinOpHelper
+       super AExpr
+
+       fun bin_expr1: AExpr is abstract
+       fun bin_expr2: AExpr is abstract
+
+       # Operator string
+       fun bin_op: String is abstract
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if not can_inline then
+                       if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
+                          (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
+                          (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
+                       then
+                               bin_expr1.force_block = true
+                       end
+               end
+
+               v.visit bin_expr1
+               v.adds
+               v.consume bin_op
+
+               if can_inline then
+                       v.adds
+                       v.visit bin_expr2
+               else
+                       v.addn
+                       v.addt
+                       v.addt
+                       v.visit bin_expr2
+               end
+       end
+end
+
+redef class AAndExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "and"
+end
+
+redef class AOrExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "or"
+end
+
+redef class AImpliesExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "implies"
+end
+
+redef class ABinopExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+end
+
+redef class AEqExpr
+       redef fun bin_op do return "=="
+end
+
+redef class AGeExpr
+       redef fun bin_op do return ">="
+end
+
+redef class AGgExpr
+       redef fun bin_op do return ">>"
+end
+
+redef class AGtExpr
+       redef fun bin_op do return ">"
+end
+
+redef class ALeExpr
+       redef fun bin_op do return "<="
+end
+
+redef class ALlExpr
+       redef fun bin_op do return "<<"
+end
+
+redef class ALtExpr
+       redef fun bin_op do return "<"
+end
+
+redef class AMinusExpr
+       redef fun bin_op do return "-"
+end
+
+redef class ANeExpr
+       redef fun bin_op do return "!="
+end
+
+redef class APercentExpr
+       redef fun bin_op do return "%"
+end
+
+redef class APlusExpr
+       redef fun bin_op do return "+"
+end
+
+redef class ASlashExpr
+       redef fun bin_op do return "/"
+end
+
+redef class AStarExpr
+       redef fun bin_op do return "*"
+end
+
+redef class AStarstarExpr
+       redef fun bin_op do return "**"
+end
+
+redef class AStarshipExpr
+       redef fun bin_op do return "<=>"
+end
+
+redef class AIsaExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.adds
+               v.consume "isa"
+               v.adds
+               v.visit n_type
+       end
+end
+
+redef class AOrElseExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.adds
+               v.consume "or"
+               v.adds
+               v.consume "else"
+               v.adds
+               v.visit n_expr2
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Syntax
+
+redef class AUminusExpr
+       redef fun accept_pretty_printer(v) do
+               v.consume "-"
+               v.visit n_expr
+       end
+end
+
+redef class ANullExpr
+       redef fun accept_pretty_printer(v) do v.visit n_kwnull
+       redef fun is_inlinable do return true
+end
+
+redef class AParExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_opar
+               v.visit n_expr
+               v.visit n_cpar
+       end
+end
+
+redef class AArrayExpr
+       redef fun accept_pretty_printer(v) do
+               v.consume "["
+               v.visit_list n_exprs.n_exprs
+               v.consume "]"
+       end
+end
+
+redef class ACrangeExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_expr
+               v.consume ".."
+               v.visit n_expr2
+               v.visit n_cbra
+       end
+end
+
+redef class AOrangeExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_expr
+               v.consume ".."
+               v.visit n_expr2
+               v.visit n_cbra
+       end
+end
+
+# Strings
+
+redef class AStringFormExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if can_inline then
+                       v.visit n_string
+               else
+                       var text = n_string.text
+                       var i = 0
+
+                       while i < text.length do
+                               v.add text[i].to_s
+
+                               if v.current_length >= v.max_size and i <= text.length - 3 then
+                                       v.add "\" +"
+                                       v.addn
+                                       v.indent += 1
+                                       v.addt
+                                       v.indent -= 1
+                                       v.add "\""
+                               end
+
+                               i += 1
+                       end
+
+                       v.current_token = n_string.next_token
+               end
+       end
+end
+
+redef class ASuperstringExpr
+       redef fun accept_pretty_printer(v) do
+               for n_expr in n_exprs do v.visit n_expr
+       end
+
+       redef fun must_be_inline do
+               if super then return true
+
+               if not n_exprs.is_empty then
+                       var first = n_exprs.first
+                       return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
+               end
+
+               return false
+       end
+end