content: {-# language GeneralizedNewtypeDeriving #-}
  {-# language FlexibleInstances, MultiParamTypeClasses #-}
  {-# language OverloadedStrings #-}
  
  {-|
  Module      : Language.Python.Internal.Render
  Copyright   : (C) CSIRO 2017-2019
  License     : BSD3
  Maintainer  : Isaac Elliott <isaace71295@gmail.com>
  Stability   : experimental
  Portability : non-portable
  -}
  
  module Language.Python.Internal.Render
    ( -- * Common Functions
      showModule, showStatement, showExpr
      -- * Rendering
    , RenderOutput, showRenderOutput, singleton
    , renderModule, renderStatement, renderExpr
      -- * Miscellany
    , showQuoteType, showStringPrefix, showBytesPrefix, showToken, showTokens
    , expandIndents, whitespaceTokens, commentTokens
    , parens, braces, brackets
    , renderWhitespace, renderCommaSep, renderCommaSep1, renderCommaSep1'
    , renderIdent, renderComment, renderModuleName, renderDot, renderRelativeModuleName
    , renderImportAs, renderImportTargets, renderSimpleStatement, renderCompoundStatement
    , renderBlock, renderIndent, renderIndents, renderExceptAs, renderArg, renderParam
    , renderParams, renderCompFor, renderCompIf, renderComprehension, renderBinOp, renderUnOp
    , renderSubscript, renderPyChars, escapeChars, intToHex
    )
  where
  
  import Control.Lens.Cons (_init, _last)
  import Control.Lens.Fold ((^..), folded, traverseOf_)
  import Control.Lens.Getter ((^.))
  import Control.Lens.Review ((#))
  import Control.Lens.Setter ((.~))
  import Control.Monad.Writer.Strict (Writer, execWriter, writer)
  import Control.Monad.Reader (ReaderT, runReaderT, local, ask)
  import Data.Bifoldable (bitraverse_, bitraverse_)
  import Data.Char (ord)
  import Data.Digit.Char (charHeXaDeCiMaL, charOctal)
  import Data.Digit.Hexadecimal.MixedCase (HeXDigit(..))
  import Data.DList (DList)
  import Data.Function ((&))
  import Data.Foldable (toList, traverse_)
  import Data.Maybe (isNothing)
  import Data.Semigroup (Semigroup(..))
  import Data.Text (Text)
  
  import qualified Data.DList as DList
  import qualified Data.List.NonEmpty as NonEmpty
  import qualified Data.Text as Text
  import qualified Data.Text.Lazy as Lazy
  import qualified Data.Text.Lazy.Builder as Builder
  
  import Language.Python.Internal.Render.Correction
  import Language.Python.Internal.Token (PyToken(..))
  import Language.Python.Syntax.AugAssign
  import Language.Python.Syntax.CommaSep
  import Language.Python.Syntax.Comment
  import Language.Python.Syntax.Expr
  import Language.Python.Syntax.Ident
  import Language.Python.Syntax.Import
  import Language.Python.Syntax.Module
  import Language.Python.Syntax.ModuleNames
  import Language.Python.Syntax.Numbers
  import Language.Python.Syntax.Operator.Binary
  import Language.Python.Syntax.Operator.Unary
  import Language.Python.Syntax.Punctuation
  import Language.Python.Syntax.Statement
  import Language.Python.Syntax.Strings
  import Language.Python.Syntax.Whitespace
  
  -- | A 'RenderOutput' is an intermediate form used during rendering
  -- with efficient concatenation
  newtype RenderOutput a
    = RenderOutput
    { unRenderOutput
      :: ReaderT
           -- Is the thing we're rendering followed by an optional
           -- newline? (as opposed to mandatory newline)
           --
           -- This is because the AST may be missing critical newlines
           -- and we supply them during rendering
           Bool
           (Writer (DList (PyToken ())))
           a
    } deriving (Functor, Applicative, Monad)
  
  -- | Treats the input as a terminating statement (does not cause additional newlines to
  -- be inserted)
  final :: RenderOutput a -> RenderOutput a
  final = id
  
  -- | Treats the input as a non-terminating statement (causes additional newlines to be
  -- inserted)
  notFinal :: RenderOutput a -> RenderOutput a
  notFinal (RenderOutput a) = RenderOutput $ local (const False) a
  
  -- | Are we inside a terminating or non-terminating context?
  isFinal :: RenderOutput Bool
  isFinal = RenderOutput ask
  
  -- | Render a single token as a 'RenderOutput'
  singleton :: PyToken () -> RenderOutput ()
  singleton a = RenderOutput $ writer ((), DList.singleton a)
  
  -- | Run a 'RenderOutput' to produce a final 'Text'.
  --
  -- These 'Text's should then not be appended any more. All appending should
  -- be done during the 'RenderOutput' phase.
  showRenderOutput :: RenderOutput a -> Text
  showRenderOutput =
    Lazy.toStrict .
    Builder.toLazyText .
    foldMap (Builder.fromText . showToken) .
    correctSpaces showToken .
    correctNewlines .
    DList.toList .
    execWriter .
    flip runReaderT True .
    unRenderOutput
  
  renderComment :: Comment a -> RenderOutput ()
  renderComment = traverse_ singleton . commentTokens
  
  commentTokens :: Comment a -> [PyToken ()]
  commentTokens c = [TkComment $ () <$ c]
  
  showComment :: Comment a -> Text
  showComment (MkComment _ s) = Text.pack $ "#" <> s
  
  between :: RenderOutput l -> RenderOutput r -> RenderOutput a -> RenderOutput a
  between l r m = l *> m <* r
  
  parens :: RenderOutput a -> RenderOutput a
  parens = between (singleton $ TkLeftParen ()) (singleton $ TkRightParen ())
  
  brackets :: RenderOutput a -> RenderOutput a
  brackets = between (singleton $ TkLeftBracket ()) (singleton $ TkRightBracket ())
  
  braces :: RenderOutput a -> RenderOutput a
  braces = between (singleton $ TkLeftBrace ()) (singleton $ TkRightBrace ())
  
  -- | Parenthesise a term, but put its trailing whitespace *outside* the parens
  parensDistTWS
    :: HasTrailingWhitespace s
    => (s -> RenderOutput ())
    -> s -> RenderOutput ()
  parensDistTWS f a = do
    parens $ f (a & trailingWhitespace .~ [])
    traverse_ renderWhitespace (a ^. trailingWhitespace)
  
  parensTuple :: Expr v a -> RenderOutput ()
  parensTuple e =
    case e of
      Tuple{} -> parensDistTWS renderExpr e
      _ -> renderExpr e
  
  parensGenerator :: Expr v a -> RenderOutput ()
  parensGenerator e =
    case e of
      Generator{} -> parensDistTWS renderExpr e
      _ -> renderExpr e
  
  parensTupleGenerator :: Expr v a -> RenderOutput ()
  parensTupleGenerator e =
    case e of
      Tuple{} -> parensDistTWS renderExpr e
      Generator{} -> parensDistTWS renderExpr e
      _ -> renderExpr e
  
  escapeChars :: [(Char, Char)]
  escapeChars =
    [ ('\\', '\\')
    , ('"', '"')
    , ('\a', 'a')
    , ('\b', 'b')
    , ('\f', 'f')
    , ('\n', 'n')
    , ('\r', 'r')
    , ('\t', 't')
    , ('\v', 'v')
    ]
  
  intToHex :: Int -> Text
  intToHex n = Text.pack $ go n []
    where
      go 0 = (++"0")
      go 1 = (++"1")
      go 2 = (++"2")
      go 3 = (++"3")
      go 4 = (++"4")
      go 5 = (++"5")
      go 6 = (++"6")
      go 7 = (++"7")
      go 8 = (++"8")
      go 9 = (++"9")
      go 10 = (++"A")
      go 11 = (++"B")
      go 12 = (++"C")
      go 13 = (++"D")
      go 14 = (++"E")
      go 15 = (++"F")
      go b = let (q, r) = quotRem b 16 in go r . go q
  
  intToHexH :: Int -> [HeXDigit]
  intToHexH n = go n []
    where
      go 0 = (++[HeXDigit0])
      go 1 = (++[HeXDigit1])
      go 2 = (++[HeXDigit2])
      go 3 = (++[HeXDigit3])
      go 4 = (++[HeXDigit4])
      go 5 = (++[HeXDigit5])
      go 6 = (++[HeXDigit6])
      go 7 = (++[HeXDigit7])
      go 8 = (++[HeXDigit8])
      go 9 = (++[HeXDigit9])
      go 10 = (++[HeXDigitA])
      go 11 = (++[HeXDigitB])
      go 12 = (++[HeXDigitC])
      go 13 = (++[HeXDigitD])
      go 14 = (++[HeXDigitE])
      go 15 = (++[HeXDigitF])
      go b = let (q, r) = quotRem b 16 in go r . go q
  
  renderPyCharsWithCorrection
    :: (QuoteType -> StringType -> [PyChar] -> [PyChar])
    -> QuoteType
    -> StringType
    -> [PyChar] -> Text
  renderPyCharsWithCorrection c qt st = Text.pack . go . c qt st
    where
      go s =
        case s of
          [] -> ""
          Char_newline : cs -> "\\newline" <> go cs
          Char_octal1 a : cs ->
            "\\" <>
            [charOctal # a] <>
            go cs
          Char_octal2 a b : cs ->
            "\\" <>
            [charOctal # a, charOctal # b] <>
            go cs
          Char_octal3 a b c : cs ->
            "\\" <>
            [charOctal # a, charOctal # b, charOctal # c] <>
            go cs
          Char_hex a b : cs ->
            "\\x" <> [charHeXaDeCiMaL # a, charHeXaDeCiMaL # b] <> go cs
          Char_uni16 a b c d : cs ->
            "\\u" <>
            [ charHeXaDeCiMaL # a
            , charHeXaDeCiMaL # b
            , charHeXaDeCiMaL # c
            , charHeXaDeCiMaL # d
            ] <>
            go cs
          Char_uni32 a b c d e f g h : cs ->
            "\\u" <>
            [ charHeXaDeCiMaL # a
            , charHeXaDeCiMaL # b
            , charHeXaDeCiMaL # c
            , charHeXaDeCiMaL # d
            , charHeXaDeCiMaL # e
            , charHeXaDeCiMaL # f
            , charHeXaDeCiMaL # g
            , charHeXaDeCiMaL # h
            ] <>
            go cs
          Char_esc_bslash : cs -> '\\' : '\\' : go cs
          Char_esc_singlequote : cs -> '\\' : '\'' : go cs
          Char_esc_doublequote : cs -> '\\' : '"' : go cs
          Char_esc_a : cs -> '\\' : 'a' : go cs
          Char_esc_b : cs -> '\\' : 'b' : go cs
          Char_esc_f : cs -> '\\' : 'f' : go cs
          Char_esc_n : cs -> '\\' : 'n' : go cs
          Char_esc_r : cs -> '\\' : 'r' : go cs
          Char_esc_t : cs -> '\\' : 't' : go cs
          Char_esc_v : cs -> '\\' : 'v' : go cs
          Char_lit c : cs ->
            case st of
              LongString -> c : go cs
              ShortString ->
                case c of
                  '\r' -> go $ Char_esc_r : cs
                  '\n' -> go $ Char_esc_n : cs
                  _ -> c : go cs
  
  renderPyChars :: QuoteType -> StringType -> [PyChar] -> Text
  renderPyChars =
    renderPyCharsWithCorrection $
    \qt st ->
      case st of
        LongString ->
          correctBackslashes . correctBackslashEscapes .
          correctInitialFinalQuotesLong qt
        ShortString ->
          correctBackslashes . correctBackslashEscapes .
          correctQuotes qt
  
  renderRawPyChars :: QuoteType -> StringType -> [PyChar] -> Text
  renderRawPyChars =
    renderPyCharsWithCorrection $
    \qt st ->
      case st of
        LongString ->
          correctInitialFinalQuotesLongRaw qt .
          correctBackslashEscapesRaw .
          correctBackslashesRaw
        ShortString ->
          correctBackslashEscapesRaw . correctBackslashesRaw .
          correctQuotesRaw qt
  
  renderPyCharsBytesWithCorrection
    :: (QuoteType -> StringType -> [PyChar] -> [PyChar])
    -> QuoteType
    -> StringType
    -> [PyChar] -> Text
  renderPyCharsBytesWithCorrection c qt st = Text.pack . go . c qt st
    where
      go s =
        case s of
          [] -> ""
          Char_newline : cs -> "\\newline" <> go cs
          Char_octal1 a  : cs ->
            "\\" <>
            [charOctal # a] <>
            go cs
          Char_octal2 a b : cs ->
            "\\" <>
            [charOctal # a, charOctal # b] <>
            go cs
          Char_octal3 a b c : cs ->
            "\\" <>
            [charOctal # a, charOctal # b, charOctal # c] <>
            go cs
          Char_hex a b : cs ->
            "\\x" <> [charHeXaDeCiMaL # a, charHeXaDeCiMaL # b] <> go cs
          Char_uni16 a b c d : cs ->
            "\\u" <>
            [ charHeXaDeCiMaL # a
            , charHeXaDeCiMaL # b
            , charHeXaDeCiMaL # c
            , charHeXaDeCiMaL # d
            ] <>
            go cs
          Char_uni32 a b c d e f g h : cs ->
            "\\u" <>
            [ charHeXaDeCiMaL # a
            , charHeXaDeCiMaL # b
            , charHeXaDeCiMaL # c
            , charHeXaDeCiMaL # d
            , charHeXaDeCiMaL # e
            , charHeXaDeCiMaL # f
            , charHeXaDeCiMaL # g
            , charHeXaDeCiMaL # h
            ] <>
            go cs
          Char_esc_bslash : cs -> '\\' : '\\' : go cs
          Char_esc_singlequote : cs -> '\\' : '\'' : go cs
          Char_esc_doublequote : cs -> '\\' : '"' : go cs
          Char_esc_a : cs -> '\\' : 'a' : go cs
          Char_esc_b : cs -> '\\' : 'b' : go cs
          Char_esc_f : cs -> '\\' : 'f' : go cs
          Char_esc_n : cs -> '\\' : 'n' : go cs
          Char_esc_r : cs -> '\\' : 'r' : go cs
          Char_esc_t : cs -> '\\' : 't' : go cs
          Char_esc_v : cs -> '\\' : 'v' : go cs
          Char_lit c : cs
            | o <- ord c, o > 127 ->
              let
                h = intToHexH o
              in
              case replicate (8 - length h) HeXDigit0 <> h of
                [a, b, c, d, e, f, g, h] -> go $ Char_uni32 a b c d e f g h : cs
                _ -> error $ "character " <> show c <> " out of unicode range"
            | otherwise ->
                case st of
                  LongString -> c : go cs
                  ShortString ->
                    case c of
                      '\r' -> go $ Char_esc_r : cs
                      '\n' -> go $ Char_esc_n : cs
                      _ -> c : go cs
  
  renderPyCharsBytes :: QuoteType -> StringType -> [PyChar] -> Text
  renderPyCharsBytes =
    renderPyCharsBytesWithCorrection $
    \qt st ->
    case st of
      LongString ->
        correctBackslashes . correctBackslashEscapes . correctInitialFinalQuotesLong qt
      ShortString ->
        correctBackslashes . correctBackslashEscapes . correctQuotes qt
  
  renderRawPyCharsBytes :: QuoteType -> StringType -> [PyChar] -> Text
  renderRawPyCharsBytes =
    renderPyCharsBytesWithCorrection $
    \qt st ->
      case st of
        LongString ->
          correctInitialFinalQuotesLongRaw qt .
          correctBackslashEscapesRaw .
          correctBackslashesRaw
        ShortString ->
          correctBackslashEscapesRaw . correctBackslashesRaw .
          correctQuotesRaw qt
  
  showTokens :: [PyToken a] -> Text
  showTokens =
    Lazy.toStrict .
    Builder.toLazyText .
    foldMap (Builder.fromText . showToken . (() <$)) .
    (expandIndents =<<)
  
  expandIndents :: PyToken a -> [PyToken ()]
  expandIndents (TkIndent _ i) =
    (i ^.. indentsValue.folded.indentWhitespaces.folded) >>=
    whitespaceTokens 
  expandIndents (TkLevel _ i) =
    (i ^.. indentsValue.folded.indentWhitespaces.folded) >>=
    whitespaceTokens
  expandIndents TkDedent{} = []
  expandIndents a = pure $ () <$ a
  
  showToken :: PyToken a -> Text
  showToken t =
    case t of
      TkIndent{} -> error "trying to show indent token"
      TkLevel{} -> error "trying to show level token"
      TkDedent{} -> error "trying to show dedent token"
      TkIf{} -> "if"
      TkElse{} -> "else"
      TkElif{} -> "elif"
      TkWhile{} -> "while"
      TkAssert{} -> "assert"
      TkDef{} -> "def"
      TkReturn{} -> "return"
      TkPass{} -> "pass"
      TkBreak{} -> "break"
      TkContinue{} -> "continue"
      TkTrue{} -> "True"
      TkFalse{} -> "False"
      TkNone{} -> "None"
      TkEllipsis{} -> "..."
      TkOr{} -> "or"
      TkAnd{} -> "and"
      TkIs{} -> "is"
      TkNot{} -> "not"
      TkGlobal{} -> "global"
      TkNonlocal{} -> "nonlocal"
      TkDel{} -> "del"
      TkLambda{} -> "lambda"
      TkImport{} -> "import"
      TkFrom{} -> "from"
      TkAs{} -> "as"
      TkRaise{} -> "raise"
      TkTry{} -> "try"
      TkExcept{} -> "except"
      TkFinally{} -> "finally"
      TkClass{} -> "class"
      TkRightArrow{} -> "->"
      TkWith{} -> "with"
      TkFor{} -> "for"
      TkIn{} -> "in"
      TkYield{} -> "yield"
      TkInt i -> showIntLiteral i
      TkFloat i -> showFloatLiteral i
      TkImag i -> showImagLiteral i
      TkIdent s _ -> Text.pack s
      TkString sp st qt s _ ->
        let
          quote =
            Text.pack $
            (case st of; LongString -> replicate 3; ShortString -> pure) (showQuoteType qt)
        in
          foldMap showStringPrefix sp <>
          quote <>
          renderPyChars qt st s <>
          quote
      TkBytes sp st qt s _ ->
        let
          quote =
            Text.pack $
            (case st of; LongString -> replicate 3; ShortString -> pure) (showQuoteType qt)
        in
          showBytesPrefix sp <>
          quote <>
          renderPyCharsBytes qt st s <>
          quote
      TkRawString sp st qt s _ ->
        let
          quote =
            case st of
              LongString -> Text.pack . replicate 3 $ showQuoteType qt
              ShortString -> Text.singleton $ showQuoteType qt
        in
          showRawStringPrefix sp <>
          quote <>
          renderRawPyChars qt st s <>
          quote
      TkRawBytes sp st qt s _ ->
        let
          quote =
            case st of
              LongString -> Text.pack . replicate 3 $ showQuoteType qt
              ShortString -> Text.singleton $ showQuoteType qt
        in
          showRawBytesPrefix sp <>
          quote <>
          renderRawPyCharsBytes qt st s <>
          quote
      TkSpace{} -> " "
      TkTab{} -> "\t"
      TkNewline nl _ ->
        case nl of
          CR -> "\r"
          LF -> "\n"
          CRLF -> "\r\n"
      TkLeftBracket{} -> "["
      TkRightBracket{} -> "]"
      TkLeftParen{} -> "("
      TkRightParen{} -> ")"
      TkLeftBrace{} -> "{"
      TkRightBrace{} -> "}"
      TkLt{} -> "<"
      TkLte{} -> "<="
      TkEq{} -> "="
      TkDoubleEq{}-> "=="
      TkBangEq{}-> "!="
      TkGt{} -> ">"
      TkGte{} -> ">="
      TkContinued nl _ ->
        "\\" <>
        case nl of
          CR -> "\r"
          LF -> "\n"
          CRLF -> "\r\n"
      TkColon{} -> ":"
      TkSemicolon{} -> ";"
      TkComma{} -> ","
      TkDot{} -> "."
      TkPlus{} -> "+"
      TkMinus{} -> "-"
      TkTilde{} -> "~"
      TkComment c -> showComment c
      TkStar{} -> "*"
      TkDoubleStar{} -> "**"
      TkSlash{} -> "/"
      TkDoubleSlash{} -> "//"
      TkPercent{} -> "%"
      TkShiftLeft{} -> "<<"
      TkShiftRight{} -> ">>"
      TkPlusEq{} -> "+="
      TkMinusEq{} -> "-="
      TkStarEq{} -> "*="
      TkAtEq{} -> "@="
      TkAt{} -> "@"
      TkSlashEq{} -> "/="
      TkPercentEq{} -> "%="
      TkAmpersandEq{} -> "&="
      TkPipeEq{} -> "|="
      TkCaretEq{} -> "^="
      TkAmpersand{} -> "&"
      TkPipe{} -> "|"
      TkCaret{} -> "^"
      TkShiftLeftEq{} -> "<<="
      TkShiftRightEq{} -> ">>="
      TkDoubleStarEq{} -> "**="
      TkDoubleSlashEq{} -> "//="
  
  whitespaceTokens :: Whitespace -> [PyToken ()]
  whitespaceTokens Space = [TkSpace ()]
  whitespaceTokens Tab = [TkTab ()]
  whitespaceTokens (Continued nl ws) = TkContinued nl () : (ws >>= whitespaceTokens)
  whitespaceTokens (Newline nl) = [TkNewline nl ()]
  whitespaceTokens (Comment cmt) = commentTokens cmt
  
  renderWhitespace :: Whitespace -> RenderOutput ()
  renderWhitespace = traverse_ singleton . whitespaceTokens
  
  renderNewline :: Newline -> PyToken ()
  renderNewline nl = TkNewline nl ()
  
  renderComma :: Comma -> RenderOutput ()
  renderComma (MkComma ws) = do
    singleton $ TkComma ()
    traverse_ renderWhitespace ws
  
  renderAt :: At -> RenderOutput ()
  renderAt (MkAt ws) = do
    singleton $ TkAt ()
    traverse_ renderWhitespace ws
  
  renderCommaSep :: (a -> RenderOutput ()) -> CommaSep a -> RenderOutput ()
  renderCommaSep _ CommaSepNone = pure ()
  renderCommaSep f (CommaSepOne a) = f a
  renderCommaSep f (CommaSepMany a c cs) = do
    f a
    renderComma c
    renderCommaSep f cs
  
  renderCommaSep1 :: (a -> RenderOutput ()) -> CommaSep1 a -> RenderOutput ()
  renderCommaSep1 f (CommaSepOne1 a) = f a
  renderCommaSep1 f (CommaSepMany1 a comma c) = do
    f a
    renderComma comma
    renderCommaSep1 f c
  
  renderCommaSep1' :: (a -> RenderOutput ()) -> CommaSep1' a -> RenderOutput ()
  renderCommaSep1' f (CommaSepOne1' a b) = do
    f a
    traverse_
      renderComma
      b
  renderCommaSep1' f (CommaSepMany1' a comma c) = do
    f a
    renderComma comma
    renderCommaSep1' f c
  
  renderIdent :: Ident v a -> RenderOutput ()
  renderIdent (MkIdent _ a b) = do
    singleton $ TkIdent a ()
    traverse_ renderWhitespace b
  
  parensTernaryLambda :: (Expr v a -> RenderOutput ()) -> Expr v a -> RenderOutput ()
  parensTernaryLambda _ e@Ternary{} = parensDistTWS renderExpr e
  parensTernaryLambda _ e@Lambda{} = parensDistTWS renderExpr e
  parensTernaryLambda f e = f e
  
  renderCompFor :: CompFor v a -> RenderOutput ()
  renderCompFor (CompFor _ ws1 ex1 ws2 ex2) = do
    singleton $ TkFor ()
    traverse_ renderWhitespace ws1
    (case ex1 of
       Not{} -> parensDistTWS renderExpr ex1
       _ -> parensGenerator ex1)
    singleton $ TkIn ()
    traverse_ renderWhitespace ws2
    parensTernaryLambda parensTupleGenerator ex2
  
  renderCompIf :: CompIf v a -> RenderOutput ()
  renderCompIf (CompIf _ ws ex) = do
    singleton $ TkIf ()
    traverse_ renderWhitespace ws
    parensTernaryLambda parensTupleGenerator ex
  
  renderComprehension
    :: (e v a -> RenderOutput ())
    -> Comprehension e v a
    -> RenderOutput ()
  renderComprehension f (Comprehension _ expr cf cs) = do
    f expr
    renderCompFor cf
    traverse_ (bitraverse_ renderCompFor renderCompIf) cs
  
  renderDictItem :: DictItem v a -> RenderOutput ()
  renderDictItem (DictItem _ a b c) = do
    parensTupleGenerator a
    renderColon b
    parensTupleGenerator c
  renderDictItem (DictUnpack _ a b) = do
    singleton $ TkDoubleStar ()
    traverse_ renderWhitespace a
    case b of
      BinOp _ _ BoolAnd{} _ -> parensDistTWS renderExpr b
      BinOp _ _ BoolOr{} _ -> parensDistTWS renderExpr b
      BinOp _ _ op _ | isComparison op -> parensDistTWS renderExpr b
      Not{} -> parensDistTWS renderExpr b
      _ -> parensTernaryLambda parensTupleGenerator b
  
  renderStringLiteral :: StringLiteral a -> RenderOutput ()
  renderStringLiteral (StringLiteral _ a b c d e) = do
    singleton $ TkString a b c d ()
    traverse_ renderWhitespace e
  renderStringLiteral (BytesLiteral _ a b c d e) = do
    singleton $ TkBytes a b c d ()
    traverse_ renderWhitespace e
  renderStringLiteral (RawStringLiteral _ a b c d e) = do
    singleton $ TkRawString a b c d ()
    traverse_ renderWhitespace e
  renderStringLiteral (RawBytesLiteral _ a b c d e) = do
    singleton $ TkRawBytes a b c d ()
    traverse_ renderWhitespace e
  
  renderSubscript :: Subscript v a -> RenderOutput ()
  renderSubscript (SubscriptExpr a) =
    case a of
      Await{} -> parensDistTWS renderExpr a
      _ -> parensTupleGenerator a
  renderSubscript (SubscriptSlice a b c d) = do
    traverse_ parensTupleGenerator a
    renderColon b
    traverse_ parensTupleGenerator c
    traverse_
      (bitraverse_
        renderColon
        (traverse_ parensTupleGenerator))
      d
  
  renderYield :: (Expr v a -> RenderOutput ()) -> Expr v a -> RenderOutput ()
  renderYield _ (Yield _ a b) = do
    singleton $ TkYield ()
    traverse_ renderWhitespace a
    renderCommaSep parensTupleGenerator b
  renderYield _ (YieldFrom _ a b c) = do
    singleton $ TkYield ()
    traverse_ renderWhitespace a
    singleton $ TkFrom ()
    traverse_ renderWhitespace b
    parensTupleGenerator c
  renderYield re e = re e
  
  renderUnpackTarget :: Expr v a -> RenderOutput ()
  renderUnpackTarget e =
    case e of
      BinOp _ _ BoolAnd{} _ -> parensDistTWS renderExpr e
      BinOp _ _ BoolOr{} _ -> parensDistTWS renderExpr e
      BinOp _ _ op _ | isComparison op -> parensDistTWS renderExpr e
      Not{} -> parensDistTWS renderExpr e
      _ -> parensTernaryLambda parensTupleGenerator e
  
  renderNestedParens
    :: RenderOutput ()
    -> [([Whitespace], [Whitespace])]
    -> RenderOutput ()
  renderNestedParens =
    foldr
      (\(ws1, ws2) y -> do
          singleton $ TkLeftParen ()
          traverse_ renderWhitespace ws1
          y
          singleton $ TkRightParen ()
          traverse_ renderWhitespace ws2)
  
  renderTupleItems
    :: CommaSep1' (TupleItem v a)
    -> RenderOutput ()
  renderTupleItems (CommaSepOne1' a Nothing) =
    case a of
      TupleItem _ b -> parensTupleGenerator b
      TupleUnpack _ b c d ->
        renderNestedParens
          (do
              singleton $ TkStar ()
              traverse_ renderWhitespace c
              renderUnpackTarget d)
          b
  renderTupleItems (CommaSepOne1' a (Just comma)) = do
    (case a of
       TupleItem _ b -> parensTupleGenerator b
       TupleUnpack _ [] b c ->
         parens $ do
           singleton $ TkStar ()
           traverse_ renderWhitespace b
           renderUnpackTarget c
       TupleUnpack _ b c d ->
         renderNestedParens
           (do
               singleton $ TkStar ()
               traverse_ renderWhitespace c
               renderUnpackTarget d)
           b)
    renderComma comma
  renderTupleItems (CommaSepMany1' a comma rest) = do
    (case a of
      TupleItem _ b -> parensTupleGenerator b
      TupleUnpack _ [] b c ->
        parens $ do
          singleton $ TkStar ()
          traverse_ renderWhitespace b
          renderUnpackTarget c
      TupleUnpack _ b c d ->
        renderNestedParens
          (do
              singleton $ TkStar ()
              traverse_ renderWhitespace c
              renderUnpackTarget d)
          b)
    renderComma comma
    renderTupleItems rest
  
  renderSetItem :: SetItem v a -> RenderOutput ()
  renderSetItem a =
    case a of
      SetItem _ b -> parensTupleGenerator b
      SetUnpack _ b c d ->
        renderNestedParens
          (do
              singleton $ TkStar ()
              traverse_ renderWhitespace c
              renderUnpackTarget d)
          b
  
  renderSetItems :: CommaSep1' (SetItem v a) -> RenderOutput ()
  renderSetItems (CommaSepOne1' a Nothing) =
    case a of
      SetItem _ b -> parensTupleGenerator b
      SetUnpack _ b c d ->
        renderNestedParens
          (do
              singleton $ TkStar ()
              traverse_ renderWhitespace c
              renderUnpackTarget d)
          b
  renderSetItems (CommaSepOne1' a (Just comma)) = do
    (case a of
       SetItem _ b -> parensTupleGenerator b
       SetUnpack _ [] b c -> do
         singleton $ TkStar ()
         traverse_ renderWhitespace b
         renderUnpackTarget c
       SetUnpack _ b c d ->
         renderNestedParens
           (do
               singleton $ TkStar ()
               traverse_ renderWhitespace c
               renderUnpackTarget d)
           b)
    renderComma comma
  renderSetItems (CommaSepMany1' a comma rest) = do
    (case a of
      SetItem _ b -> parensTupleGenerator b
      SetUnpack _ [] b c -> do
        singleton $ TkStar ()
        traverse_ renderWhitespace b
        renderUnpackTarget c
      SetUnpack _ b c d ->
        renderNestedParens
          (do
              singleton $ TkStar ()
              traverse_ renderWhitespace c
              renderUnpackTarget d)
          b)
    renderComma comma
    renderSetItems rest
  
  renderListItems :: CommaSep1' (ListItem v a) -> RenderOutput ()
  renderListItems (CommaSepOne1' a Nothing) =
    case a of
      ListItem _ b -> parensTupleGenerator b
      ListUnpack _ b c d ->
        renderNestedParens
          (do
              singleton $ TkStar ()
              traverse_ renderWhitespace c
              renderUnpackTarget d)
          b
  renderListItems (CommaSepOne1' a (Just comma)) = do
    (case a of
       ListItem _ b -> parensTupleGenerator b
       ListUnpack _ [] b c -> do
         singleton $ TkStar ()
         traverse_ renderWhitespace b
         renderUnpackTarget c
       ListUnpack _ b c d ->
         renderNestedParens
           (do
               singleton $ TkStar ()
               traverse_ renderWhitespace c
               renderUnpackTarget d)
           b)
    renderComma comma
  renderListItems (CommaSepMany1' a comma rest) = do
    (case a of
      ListItem _ b -> parensTupleGenerator b
      ListUnpack _ [] b c -> do
        singleton $ TkStar ()
        traverse_ renderWhitespace b
        renderUnpackTarget c
      ListUnpack _ b c d ->
        renderNestedParens
          (do
              singleton $ TkStar ()
              traverse_ renderWhitespace c
              renderUnpackTarget d)
          b)
    renderComma comma
    renderListItems rest
  
  renderExpr :: Expr v a -> RenderOutput ()
  renderExpr (Unit _ a b) = do
    singleton $ TkLeftParen ()
    traverse_ renderWhitespace a
    singleton $ TkRightParen ()
    traverse_ renderWhitespace b
  renderExpr (Lambda _ a b c d) = do
    singleton $ TkLambda ()
    traverse_ renderWhitespace a
    renderParams b
    renderColon c
    parensTupleGenerator d
  renderExpr e@Yield{} = parensDistTWS (renderYield parensTupleGenerator) e
  renderExpr e@YieldFrom{} = parensDistTWS (renderYield parensTupleGenerator) e
  renderExpr (Ternary _ a b c d e) = do
    (case a of
       Generator{} -> parensDistTWS renderExpr a
       _ -> parensTupleGenerator a)
    singleton $ TkIf ()
    traverse_ renderWhitespace b
    parensTernaryLambda parensTupleGenerator c
    singleton $ TkElse ()
    traverse_ renderWhitespace d
    parensTupleGenerator e
  renderExpr (Subscript _ a b c d) = do
    (case a of
       BinOp{} -> parensDistTWS renderExpr a
       UnOp{} -> parensDistTWS renderExpr a
       Not{} -> parensDistTWS renderExpr a
       Ternary{} -> parensDistTWS renderExpr a
       Lambda{} -> parensDistTWS renderExpr a
       Await{} -> parensDistTWS renderExpr a
       _ -> parensTupleGenerator a)
    brackets $ do
      traverse_ renderWhitespace b
      renderCommaSep1' renderSubscript c
    traverse_ renderWhitespace d
  renderExpr (Not _ ws e) = do
    singleton $ TkNot ()
    traverse_ renderWhitespace ws
    case e of
      BinOp _ _ BoolAnd{} _ -> parensDistTWS renderExpr e
      BinOp _ _ BoolOr{} _ -> parensDistTWS renderExpr e
      Ternary{} -> parensDistTWS renderExpr e
      Lambda{} -> parensDistTWS renderExpr e
      _ -> parensTupleGenerator e
  renderExpr (Parens _ ws1 e ws2) = do
    parens $ do
      traverse_ renderWhitespace ws1
      renderYield renderExpr e
    traverse_ renderWhitespace ws2
  renderExpr (Bool _ b ws) = do
    singleton $ if b then TkTrue () else TkFalse ()
    traverse_ renderWhitespace ws
  renderExpr (UnOp _ op expr) = do
    renderUnOp op
    case expr of
      BinOp _ _ Exp{} _ -> parensTupleGenerator expr
      BinOp{} -> parensDistTWS renderExpr expr
      Deref _ Int{} _ _ -> parensDistTWS renderExpr expr
      Not{} -> parensDistTWS renderExpr expr
      Ternary{} -> parensDistTWS renderExpr expr
      Lambda{} -> parensDistTWS renderExpr expr
      _ -> parensTupleGenerator expr
  renderExpr (String _ vs) =
    traverse_ renderStringLiteral $ correctAdjacentStrings vs
  renderExpr (Int _ n ws) = do
    singleton $ TkInt (() <$ n)
    traverse_ renderWhitespace ws
  renderExpr (Float _ n ws) = do
    singleton $ TkFloat (() <$ n)
    traverse_ renderWhitespace ws
  renderExpr (Imag _ n ws) = do
    singleton $ TkImag (() <$ n)
    traverse_ renderWhitespace ws
  renderExpr (Ident _ name) = renderIdent name
  renderExpr (List _ ws1 exprs ws2) = do
    brackets $ do
      traverse_ renderWhitespace ws1
      traverse_ renderListItems exprs
    traverse_ renderWhitespace ws2
  renderExpr (ListComp _ ws1 comp ws2) = do
    brackets $ do
      traverse_ renderWhitespace ws1
      renderComprehension
        (\e -> case e of
            Yield{} -> parensDistTWS renderExpr e
            YieldFrom{} -> parensDistTWS renderExpr e
            _ -> parensTupleGenerator e)
        comp
    traverse_ renderWhitespace ws2
  renderExpr (Call _ expr ws args ws2) = do
    (case expr of
       UnOp{} -> parensDistTWS renderExpr expr
       BinOp{} -> parensDistTWS renderExpr expr
       Tuple{} -> parensDistTWS renderExpr expr
       Not{} -> parensDistTWS renderExpr expr
       Ternary{} -> parensDistTWS renderExpr expr
       Lambda{} -> parensDistTWS renderExpr expr
       _ -> parensGenerator expr)
    parens $ do
      traverse_ renderWhitespace ws
      traverse_ renderArgs args
    traverse_ renderWhitespace ws2
  renderExpr (Deref _ expr ws name) = do
    (case expr of
       Int{} -> parensDistTWS renderExpr expr
       BinOp{} -> parensDistTWS renderExpr expr
       Tuple{} -> parensDistTWS renderExpr expr
       Not{} -> parensDistTWS renderExpr expr
       UnOp{} -> parensDistTWS renderExpr expr
       Ternary{} -> parensDistTWS renderExpr expr
       Lambda{} -> parensDistTWS renderExpr expr
       Await{} -> parensDistTWS renderExpr expr
       _ -> parensGenerator expr)
    singleton $ TkDot ()
    traverse_ renderWhitespace ws
    renderIdent name
  renderExpr (None _ ws) = do
    singleton $ TkNone ()
    traverse_ renderWhitespace ws
  renderExpr (Ellipsis _ ws) = do
    singleton $ TkEllipsis ()
    traverse_ renderWhitespace ws
  renderExpr (BinOp _ e1 op e2) = do
    if shouldGroupLeft op e1
      then parensDistTWS renderExpr e1
      else parensTernaryLambda parensGenerator e1
  
    renderBinOp op
  
    if shouldGroupRight op e2
      then parensDistTWS renderExpr e2
      else parensTernaryLambda parensGenerator e2
  renderExpr (Tuple _ a ws c) =
    renderTupleItems $
    case c of
      Nothing -> CommaSepOne1' a (Just ws)
      Just c' -> CommaSepMany1' a ws c'
  renderExpr (DictComp _ ws1 comp ws2) = do
    braces $ do
      traverse_ renderWhitespace ws1
      renderComprehension renderDictItem comp
    traverse_ renderWhitespace ws2
  renderExpr (Dict _ a b c) = do
    braces $ do
      traverse_ renderWhitespace a
      traverse_ (renderCommaSep1' renderDictItem) b
    traverse_ renderWhitespace c
  renderExpr (SetComp _ ws1 comp ws2) = do
    braces $ do
      traverse_ renderWhitespace ws1
      renderComprehension renderSetItem comp
    traverse_ renderWhitespace ws2
  renderExpr (Set _ a b c) = do
    braces $ do
      traverse_ renderWhitespace a
      renderSetItems b
    traverse_ renderWhitespace c
  renderExpr (Generator _ a) =
    renderComprehension
      (\e -> case e of
          Yield{} -> parensDistTWS renderExpr e
          YieldFrom{} -> parensDistTWS renderExpr e
          _ -> parensTupleGenerator e)
      a
  renderExpr (Await _ ws expr) = do
    singleton $ TkIdent "await" ()
    traverse_ renderWhitespace ws
    (case expr of
       UnOp{} -> parensDistTWS renderExpr expr
       BinOp{} -> parensDistTWS renderExpr expr
       Tuple{} -> parensDistTWS renderExpr expr
       Not{} -> parensDistTWS renderExpr expr
       Ternary{} -> parensDistTWS renderExpr expr
       Lambda{} -> parensDistTWS renderExpr expr
       Await{} -> parensDistTWS renderExpr expr
       _ -> parensGenerator expr)
  
  renderModuleName :: ModuleName v a -> RenderOutput ()
  renderModuleName (ModuleNameOne _ s) = renderIdent s
  renderModuleName (ModuleNameMany _ n dot rest) = do
    renderIdent n
    renderDot dot
    renderModuleName rest
  
  renderDot :: Dot -> RenderOutput ()
  renderDot (MkDot ws) = do
    singleton $ TkDot ()
    traverse_ renderWhitespace ws
  
  renderRelativeModuleName :: RelativeModuleName v a -> RenderOutput ()
  renderRelativeModuleName (RelativeWithName _ ds mn) = do
    traverse_ renderDot ds
    renderModuleName mn
  renderRelativeModuleName (Relative _ ds) =
    traverse_ renderDot ds
  
  renderImportAs :: (e v a -> RenderOutput ()) -> ImportAs e v a -> RenderOutput ()
  renderImportAs f (ImportAs _ ea m) = do
    f ea
    traverse_
      (\(a, b) -> do
          singleton $ TkAs ()
          traverse_ renderWhitespace a
          renderIdent b)
      m
  
  renderImportTargets :: ImportTargets v a -> RenderOutput ()
  renderImportTargets (ImportAll _ ws) = do
    singleton $ TkStar ()
    traverse_ renderWhitespace ws
  renderImportTargets (ImportSome _ ts) =
    renderCommaSep1 (renderImportAs renderIdent) ts
  renderImportTargets (ImportSomeParens _ ws1 ts ws2) = do
    parens $ do
      traverse_ renderWhitespace ws1
      renderCommaSep1' (renderImportAs renderIdent) ts
    traverse_ renderWhitespace ws2
  
  renderAugAssign :: AugAssign a -> RenderOutput ()
  renderAugAssign aa = do
    singleton $ case _augAssignType aa of
      PlusEq -> TkPlusEq ()
      MinusEq -> TkMinusEq ()
      StarEq -> TkStarEq ()
      AtEq -> TkAtEq ()
      SlashEq -> TkSlashEq ()
      PercentEq -> TkPercentEq ()
      AmpersandEq -> TkAmpersandEq ()
      PipeEq -> TkPipeEq ()
      CaretEq -> TkCaretEq ()
      ShiftLeftEq -> TkShiftLeftEq ()
      ShiftRightEq -> TkShiftRightEq ()
      DoubleStarEq -> TkDoubleStarEq ()
      DoubleSlashEq -> TkDoubleSlashEq ()
    traverse_ renderWhitespace (_augAssignWhitespace aa)
  
  renderSimpleStatement :: SimpleStatement v a -> RenderOutput ()
  renderSimpleStatement (Assert _ b c d) = do
    singleton $ TkAssert ()
    traverse_ renderWhitespace b
    parensTupleGenerator c
    traverse_
      (\(a, b) -> do
          renderComma a
          parensTupleGenerator b)
      d
  renderSimpleStatement (Raise _ ws x) = do
    singleton $ TkRaise ()
    traverse_ renderWhitespace ws
    traverse_
      (\(b, c) -> do
         parensTupleGenerator b
         traverse_
           (\(d, e) -> do
              singleton $ TkFrom ()
              traverse_ renderWhitespace d
              parensTupleGenerator e)
           c)
      x
  renderSimpleStatement (Return _ ws expr) = do
    singleton $ TkReturn ()
    traverse_ renderWhitespace ws
    traverse_ parensGenerator expr
  renderSimpleStatement (Expr _ expr) = renderYield parensGenerator expr
  renderSimpleStatement (Assign _ lvalue rvalues) = do
    renderExpr lvalue
    traverse_
      (\(ws2, rvalue) -> do
         renderEquals ws2
         renderYield parensGenerator rvalue)
      rvalues
  renderSimpleStatement (AugAssign _ lvalue as rvalue) = do
    renderExpr lvalue
    renderAugAssign as
    parensTupleGenerator rvalue
  renderSimpleStatement (Pass _ ws) = do
    singleton $ TkPass ()
    traverse_ renderWhitespace ws
  renderSimpleStatement (Continue _ ws) = do
    singleton $ TkContinue ()
    traverse_ renderWhitespace ws
  renderSimpleStatement (Break _ ws) = do
    singleton $ TkBreak ()
    traverse_ renderWhitespace ws
  renderSimpleStatement (Global _ ws ids) = do
    singleton $ TkGlobal ()
    traverse_ renderWhitespace ws
    renderCommaSep1 renderIdent ids
  renderSimpleStatement (Nonlocal _ ws ids) = do
    singleton $ TkNonlocal ()
    traverse_ renderWhitespace ws
    renderCommaSep1 renderIdent ids
  renderSimpleStatement (Del _ ws vals) = do
    singleton $ TkDel ()
    traverse_ renderWhitespace ws
    renderCommaSep1'
      (\a -> case a of
          BinOp{} -> parensDistTWS renderExpr a
          Not{} -> parensDistTWS renderExpr a
          Ternary{} -> parensDistTWS renderExpr a
          Lambda{} -> parensDistTWS renderExpr a
          _ -> parensTupleGenerator a)
      vals
  renderSimpleStatement (Import _ ws ns) = do
    singleton $ TkImport ()
    traverse_ renderWhitespace ws
    renderCommaSep1 (renderImportAs renderModuleName) ns
  renderSimpleStatement (From _ ws1 name ws3 ns) = do
    singleton $ TkFrom ()
    traverse_ renderWhitespace ws1
    renderRelativeModuleName name
    singleton $ TkImport ()
    traverse_ renderWhitespace ws3
    renderImportTargets ns
  
  renderBlank :: Blank a -> RenderOutput ()
  renderBlank (Blank _ a b) = do
    traverse_ renderWhitespace a
    traverse_ renderComment b
  
  renderBlock :: Block v a -> RenderOutput ()
  renderBlock (Block a b c) = do
    traverse_ (bitraverse_ renderBlank (singleton . renderNewline)) a
    (if null c then final else notFinal) $ renderStatement b
    traverseOf_
      (_init.traverse)
      (bitraverse_
        (bitraverse_ renderBlank (singleton . renderNewline))
        (notFinal . renderStatement))
      c
    traverseOf_
      _last
      (bitraverse_
        (bitraverse_ renderBlank (singleton . renderNewline))
        (final . renderStatement))
      c
  
  renderSemicolon :: Semicolon a -> RenderOutput ()
  renderSemicolon (MkSemicolon _ ws) = do
    singleton $ TkSemicolon ()
    traverse_ renderWhitespace ws
  
  renderEquals :: Equals -> RenderOutput ()
  renderEquals (MkEquals ws) = do
    singleton $ TkEq ()
    traverse_ renderWhitespace ws
  
  renderColon :: Colon -> RenderOutput ()
  renderColon (MkColon ws) = do
    singleton $ TkColon ()
    traverse_ renderWhitespace ws
  
  renderSuite
    :: Suite v a
    -> RenderOutput ()
  renderSuite (SuiteMany _ a b c d) = do
    renderColon a
    traverse_ renderComment b
    singleton (renderNewline c)
    renderBlock d
  renderSuite (SuiteOne _ a b) = do
    renderColon a
    fin <- isFinal
    renderSmallStatement $ correctTrailingNewline fin b
  
  renderDecorator :: Decorator v a -> RenderOutput ()
  renderDecorator (Decorator _ a b c d e f) = do
    renderIndents a
    renderAt b
    renderExpr c
    traverse_ renderComment d
    singleton (renderNewline e)
    traverse_ (bitraverse_ renderBlank (singleton . renderNewline)) f
  
  renderCompoundStatement :: CompoundStatement v a -> RenderOutput ()
  renderCompoundStatement (Fundef _ decos idnt asyncWs ws1 name ws2 params ws3 mty s) = do
    traverse_ renderDecorator decos
    renderIndents idnt
    traverse_
      (\ws -> do
          singleton $ TkIdent "async" ()
          traverse_ renderWhitespace ws)
      asyncWs
    singleton (TkDef ())
    traverse_ renderWhitespace ws1
    renderIdent name
    parens $ do
      traverse_ renderWhitespace ws2
      renderParams params
    traverse_ renderWhitespace ws3
    traverse_
      (\(ws, ty) -> do
          singleton $ TkRightArrow ()
          traverse_ renderWhitespace ws
          parensTupleGenerator ty)
      mty
    final $ renderSuite s
  renderCompoundStatement (If _ idnt ws1 expr s elifs body') = do
    renderIndents idnt
    singleton $ TkIf ()
    traverse_ renderWhitespace ws1
    parensTupleGenerator expr
    notFinal $ renderSuite s
    traverseOf_
      (_init.traverse)
      (\(idnt, ws4, ex, s) -> do
          renderIndents idnt
          singleton $ TkElif ()
          traverse_ renderWhitespace ws4
          parensTupleGenerator ex
          notFinal $ renderSuite s)
      elifs
    traverseOf_
      _last
      (\(idnt, ws4, ex, s) -> do
          renderIndents idnt
          singleton $ TkElif ()
          traverse_ renderWhitespace ws4
          parensTupleGenerator ex
          (if isNothing body' then final else notFinal) $ renderSuite s)
      elifs
    traverse_
      (\(idnt, ws4, s) -> do
          renderIndents idnt
          singleton $ TkElse ()
          traverse_ renderWhitespace ws4
          final $ renderSuite s)
      body'
  renderCompoundStatement (While _ idnt ws1 expr s els) = do
    renderIndents idnt
    singleton $ TkWhile ()
    traverse_ renderWhitespace ws1
    parensTupleGenerator expr
    (if isNothing els then final else notFinal) $ renderSuite s
    traverse_
      (\(idnt, ws4, s) -> do
          renderIndents idnt
          singleton $ TkElse ()
          traverse_ renderWhitespace ws4
          final $ renderSuite s)
      els
  renderCompoundStatement (TryExcept _ idnt a s e f g) = do
    renderIndents idnt
    singleton $ TkTry ()
    traverse_ renderWhitespace a
    notFinal $ renderSuite s
    traverse_
      (\(idnt, ws1, eas, s) -> do
         renderIndents idnt
         singleton $ TkExcept ()
         traverse_ renderWhitespace ws1
         traverse_ renderExceptAs eas
         notFinal $ renderSuite s)
      (NonEmpty.init e)
    (case NonEmpty.last e of
       (idnt, ws1, eas, s) -> do
         renderIndents idnt
         singleton $ TkExcept ()
         traverse_ renderWhitespace ws1
         traverse_ renderExceptAs eas
         (if isNothing f && isNothing g then final else notFinal) $ renderSuite s)
    traverse_
      (\(idnt, ws1, s) -> do
         renderIndents idnt
         singleton $ TkElse ()
         traverse_ renderWhitespace ws1
         (if isNothing g then final else notFinal) $ renderSuite s)
      f
    traverse_
      (\(idnt, ws1, s) -> do
         renderIndents idnt
         singleton $ TkFinally ()
         traverse_ renderWhitespace ws1
         final $ renderSuite s)
      g
  renderCompoundStatement (TryFinally _ idnt a s idnt2 e s') = do
    renderIndents idnt
    singleton $ TkTry ()
    traverse_ renderWhitespace a
    notFinal $ renderSuite s
    renderIndents idnt2
    singleton $ TkFinally ()
    traverse_ renderWhitespace e
    final $ renderSuite s'
  renderCompoundStatement (For _ idnt asyncWs a b c d s h) = do
    renderIndents idnt
    traverse_
      (\ws -> do
          singleton $ TkIdent "async" ()
          traverse_ renderWhitespace ws)
      asyncWs
    singleton $ TkFor ()
    traverse_ renderWhitespace a
    parensGenerator b
    singleton $ TkIn ()
    traverse_ renderWhitespace c
    renderCommaSep1' parensTupleGenerator d
    (if isNothing h then final else notFinal) $ renderSuite s
    traverse_
      (\(idnt, x, s) -> do
          renderIndents idnt
          singleton $ TkElse ()
          traverse_ renderWhitespace x
          final $ renderSuite s)
      h
  renderCompoundStatement (ClassDef _ decos idnt a b c s) = do
    traverse_ renderDecorator decos
    renderIndents idnt
    singleton $ TkClass ()
    traverse_ renderWhitespace a
    renderIdent b
    traverse_
      (\(x, y, z) -> do
        parens $ do
          traverse_ renderWhitespace x
          traverse_ renderArgs y
        traverse_ renderWhitespace z)
      c
    final $ renderSuite s
  renderCompoundStatement (With _ idnt asyncWs a b s) = do
    renderIndents idnt
    traverse_
      (\ws -> do
          singleton $ TkIdent "async" ()
          traverse_ renderWhitespace ws)
      asyncWs
    singleton $ TkWith ()
    traverse_ renderWhitespace a
    renderCommaSep1 renderWithItem b
    final $ renderSuite s
  
  renderWithItem :: WithItem v a -> RenderOutput ()
  renderWithItem (WithItem _ a b) = do
    parensTupleGenerator a
    traverse_
      (\(c, d) -> do
         singleton $ TkAs ()
         traverse_ renderWhitespace c
         parensTupleGenerator d)
      b
  
  renderIndent :: Indent -> RenderOutput ()
  renderIndent (MkIndent ws) = traverse_ renderWhitespace $ toList ws
  
  renderSmallStatement :: SmallStatement v a -> RenderOutput ()
  renderSmallStatement (MkSmallStatement s ss sc cmt nl) = do
    renderSimpleStatement s
    traverse_
      (\(b, c) -> do
         renderSemicolon b
         renderSimpleStatement c)
      ss
    traverse_ renderSemicolon sc
    traverse_ renderComment cmt
    traverse_ (singleton . renderNewline) nl
  
  renderStatement :: Statement v a -> RenderOutput ()
  renderStatement (CompoundStatement c) = renderCompoundStatement c
  renderStatement (SmallStatement idnts s) = do
    renderIndents idnts
    fin <- isFinal
    renderSmallStatement $ correctTrailingNewline fin s
  
  renderExceptAs :: ExceptAs v a -> RenderOutput ()
  renderExceptAs (ExceptAs _ e f) = do
    parensTupleGenerator e
    traverse_
      (\(a, b) -> do
          singleton $ TkAs ()
          traverse_ renderWhitespace a
          renderIdent b)
      f
  
  renderArgs :: CommaSep1' (Arg v a) -> RenderOutput ()
  renderArgs (CommaSepOne1' a Nothing) = renderArg parensTuple a
  renderArgs e = renderCommaSep1' (renderArg parensTupleGenerator) e
  
  renderArg :: (Expr v a -> RenderOutput ()) -> Arg v a -> RenderOutput ()
  renderArg re (PositionalArg _ expr) = re expr
  renderArg _ (KeywordArg _ name ws2 expr) = do
    renderIdent name
    singleton $ TkEq ()
    traverse_ renderWhitespace ws2
    parensTupleGenerator expr
  renderArg _ (StarArg _ ws expr) = do
    singleton $ TkStar ()
    traverse_ renderWhitespace ws
    parensTupleGenerator expr
  renderArg _ (DoubleStarArg _ ws expr) = do
    singleton $ TkDoubleStar ()
    traverse_ renderWhitespace ws
    parensTupleGenerator expr
  
  renderParams :: CommaSep (Param v a) -> RenderOutput ()
  renderParams = renderCommaSep renderParam . correctParams
  
  renderParam :: Param v a -> RenderOutput ()
  renderParam (PositionalParam _ name mty) = do
    renderIdent name
    traverse_
      (\(c, ty) -> do
          renderColon c
          parensTupleGenerator ty)
      mty
  renderParam (StarParam _ ws name mty) = do
    singleton $ TkStar ()
    traverse_ renderWhitespace ws
    renderIdent name
    traverse_
      (\(c, ty) -> do
          renderColon c
          parensTupleGenerator ty)
      mty
  renderParam (UnnamedStarParam _ ws) = do
    singleton $ TkStar ()
    traverse_ renderWhitespace ws
  renderParam (DoubleStarParam _ ws name mty) = do
    singleton $ TkDoubleStar ()
    traverse_ renderWhitespace ws
    renderIdent name
    traverse_
      (\(c, ty) -> do
          renderColon c
          parensTupleGenerator ty)
      mty
  renderParam (KeywordParam _ name mty ws2 expr) = do
    renderIdent name
    traverse_
      (\(c, ty) -> do
          renderColon c
          parensTupleGenerator ty)
      mty
    singleton $ TkEq ()
    traverse_ renderWhitespace ws2
    parensTupleGenerator expr
  
  renderUnOp :: UnOp a -> RenderOutput ()
  renderUnOp (Negate _ ws) = do
    singleton $ TkMinus ()
    traverse_ renderWhitespace ws
  renderUnOp (Positive _ ws) = do
    singleton $ TkPlus ()
    traverse_ renderWhitespace ws
  renderUnOp (Complement _ ws) = do
    singleton $ TkTilde ()
    traverse_ renderWhitespace ws
  
  renderBinOp :: BinOp a -> RenderOutput ()
  renderBinOp (Is _ ws) = do
    singleton $ TkIs ()
    traverse_ renderWhitespace ws
  renderBinOp (IsNot _ ws1 ws2) = do
    singleton $ TkIs ()
    traverse_ renderWhitespace ws1
    singleton $ TkNot ()
    traverse_ renderWhitespace ws2
  renderBinOp (In _ ws) = do
    singleton $ TkIn ()
    traverse_ renderWhitespace ws
  renderBinOp (NotIn _ ws1 ws2) = do
    singleton $ TkNot ()
    traverse_ renderWhitespace ws1
    singleton $ TkIn ()
    traverse_ renderWhitespace ws2
  renderBinOp (Plus _ ws) = do
    singleton $ TkPlus ()
    traverse_ renderWhitespace ws
  renderBinOp (Minus _ ws) = do
    singleton $ TkMinus ()
    traverse_ renderWhitespace ws
  renderBinOp (Multiply _ ws) = do
    singleton $ TkStar ()
    traverse_ renderWhitespace ws
  renderBinOp (At _ ws) = do
    singleton $ TkAt ()
    traverse_ renderWhitespace ws
  renderBinOp (Divide _ ws) = do
    singleton $ TkSlash ()
    traverse_ renderWhitespace ws
  renderBinOp (FloorDivide _ ws) = do
    singleton $ TkDoubleSlash ()
    traverse_ renderWhitespace ws
  renderBinOp (Exp _ ws) = do
    singleton $ TkDoubleStar ()
    traverse_ renderWhitespace ws
  renderBinOp (BoolAnd _ ws) = do
    singleton $ TkAnd ()
    traverse_ renderWhitespace ws
  renderBinOp (BoolOr _ ws) = do
    singleton $ TkOr ()
    traverse_ renderWhitespace ws
  renderBinOp (Eq _ ws) = do
    singleton $ TkDoubleEq ()
    traverse_ renderWhitespace ws
  renderBinOp (Lt _ ws) = do
    singleton $ TkLt ()
    traverse_ renderWhitespace ws
  renderBinOp (LtEq _ ws) = do
    singleton $ TkLte ()
    traverse_ renderWhitespace ws
  renderBinOp (Gt _ ws) = do
    singleton $ TkGt ()
    traverse_ renderWhitespace ws
  renderBinOp (GtEq _ ws) = do
    singleton $ TkGte ()
    traverse_ renderWhitespace ws
  renderBinOp (NotEq _ ws) = do
    singleton $ TkBangEq ()
    traverse_ renderWhitespace ws
  renderBinOp (Percent _ ws) = do
    singleton $ TkPercent ()
    traverse_ renderWhitespace ws
  renderBinOp (BitOr _ ws) = do
    singleton $ TkPipe ()
    traverse_ renderWhitespace ws
  renderBinOp (BitXor _ ws) = do
    singleton $ TkCaret ()
    traverse_ renderWhitespace ws
  renderBinOp (BitAnd _ ws) = do
    singleton $ TkAmpersand ()
    traverse_ renderWhitespace ws
  renderBinOp (ShiftLeft _ ws) = do
    singleton $ TkShiftLeft ()
    traverse_ renderWhitespace ws
  renderBinOp (ShiftRight _ ws) = do
    singleton $ TkShiftRight ()
    traverse_ renderWhitespace ws
  
  renderIndents :: Indents a -> RenderOutput ()
  renderIndents (Indents is _) = traverse_ renderIndent is
  
  renderModule :: Module v a -> RenderOutput ()
  renderModule ModuleEmpty = pure ()
  renderModule (ModuleBlankFinal a) = renderBlank a
  renderModule (ModuleBlank a b c) = do
    renderBlank a
    singleton $ renderNewline b
    renderModule c
  renderModule (ModuleStatement a b) = do
    renderStatement a
    renderModule b
  
  -- | Render an entire Python module to 'Text'
  showModule :: Module v a -> Text
  showModule = showRenderOutput . renderModule
  
  -- | Render a single Python statement to 'Text'
  showStatement :: Statement v a -> Text
  showStatement = showRenderOutput . renderStatement
  
  -- | Render a single Python expression to 'Text'
  showExpr :: Expr v a -> Text
  showExpr = showRenderOutput . parensGenerator
  