packages feed

phino-0.0.0.43: src/Pretty.hs

{-# LANGUAGE FlexibleInstances #-}

-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT

module Pretty
  ( prettyExpression,
    prettyExpression',
    prettyProgram,
    prettyProgram',
    prettyAttribute,
    prettySubsts,
    prettySubsts',
    prettySubst,
    prettyBinding,
    prettyBytes,
    prettyExtraArg,
    PrintMode (SWEET, SALTY),
    Encoding (ASCII, UNICODE),
  )
where

import Ast
import Data.List (intercalate)
import qualified Data.Map.Strict as Map
import Matcher
import Misc
import Prettyprinter
import Prettyprinter.Render.String (renderString)
import Yaml (ExtraArgument (..))

data PrintMode = SWEET | SALTY
  deriving (Eq)

instance Show PrintMode where
  show SWEET = "sweet"
  show SALTY = "salty"

data Encoding = ASCII | UNICODE

newtype Formatted a = Formatted {unFormatted :: (PrintMode, Encoding, a)}

formatted :: a -> Formatted a
formatted x = Formatted (SALTY, UNICODE, x)

prettyMeta :: String -> Doc ann
prettyMeta meta = pretty "!" <> pretty meta

prettyArrow :: Encoding -> Doc ann
prettyArrow UNICODE = pretty "↦"
prettyArrow ASCII = pretty "->"

prettyLsb :: Encoding -> Doc ann
prettyLsb UNICODE = pretty "⟦"
prettyLsb ASCII = pretty "[["

prettyRsb :: Encoding -> Doc ann
prettyRsb UNICODE = pretty "⟧"
prettyRsb ASCII = pretty "]]"

prettyDashedArrow :: Doc ann
prettyDashedArrow = pretty "⤍"

prettyLambda :: Encoding -> Doc ann
prettyLambda UNICODE = pretty "λ" <+> prettyDashedArrow
prettyLambda ASCII = pretty "L>"

prettyDelta :: Encoding -> Doc ann
prettyDelta UNICODE = pretty "Δ" <+> prettyDashedArrow
prettyDelta ASCII = pretty "D>"

instance Pretty ExtraArgument where
  pretty (ArgExpression expr) = pretty (Formatted (SWEET, UNICODE, expr))
  pretty (ArgBinding bd) = pretty (Formatted (SWEET, UNICODE, bd))
  pretty (ArgAttribute attr) = pretty (formatted attr)
  pretty (ArgBytes bytes) = pretty bytes

instance Pretty Bytes where
  pretty BtEmpty = pretty "--"
  pretty (BtOne bt) = pretty bt <> pretty "-"
  pretty (BtMany bts) = pretty (intercalate "-" bts)
  pretty (BtMeta meta) = prettyMeta meta

instance Pretty (Formatted Attribute) where
  pretty (Formatted (_, _, AtMeta meta)) = prettyMeta meta
  pretty (Formatted (_, ASCII, AtAlpha idx)) = pretty "~" <> pretty idx
  pretty (Formatted (_, ASCII, AtPhi)) = pretty "@"
  pretty (Formatted (_, ASCII, AtRho)) = pretty "^"
  pretty (Formatted (_, _, attr)) = pretty (show attr)

instance Pretty (Formatted Binding) where
  pretty (Formatted (SWEET, encoding, BiTau attr (ExFormation bindings))) =
    let voids' = voids bindings
     in if null voids'
          then pretty (Formatted (SWEET, encoding, attr)) <+> prettyArrow encoding <+> pretty (Formatted (SWEET, encoding, ExFormation bindings))
          else
            if length voids' == length bindings && last voids' == AtRho
              then inlineVoids (init voids') <+> prettyLsb encoding <> prettyRsb encoding
              else inlineVoids voids' <+> pretty (Formatted (SWEET, encoding, ExFormation (drop (length voids') bindings)))
    where
      voids :: [Binding] -> [Attribute]
      voids [] = []
      voids (bd : bds) = case bd of
        BiVoid attr -> attr : voids bds
        _ -> []
      inlineVoids :: [Attribute] -> Doc ann
      inlineVoids [] = pretty (Formatted (SWEET, encoding, attr)) <+> prettyArrow encoding
      inlineVoids voids' =
        pretty (Formatted (SWEET, encoding, attr))
          <> lparen
          <> hsep (punctuate comma (map (\attr -> pretty (Formatted (SWEET, encoding, attr))) voids'))
          <> rparen
          <+> prettyArrow encoding
  pretty (Formatted (mode, encoding, BiTau attr expr)) = pretty (Formatted (mode, encoding, attr)) <+> prettyArrow encoding <+> pretty (Formatted (mode, encoding, expr))
  pretty (Formatted (_, _, BiMeta meta)) = prettyMeta meta
  pretty (Formatted (_, encoding, BiDelta bytes)) = prettyDelta encoding <+> pretty bytes
  pretty (Formatted (_, encoding, BiLambda func)) = prettyLambda encoding <+> pretty func
  pretty (Formatted (_, encoding, BiMetaLambda meta)) = prettyLambda encoding <+> prettyMeta meta
  pretty (Formatted (mode, UNICODE, BiVoid attr)) = pretty (Formatted (mode, UNICODE, attr)) <+> prettyArrow UNICODE <+> pretty "∅"
  pretty (Formatted (mode, ASCII, BiVoid attr)) = pretty (Formatted (mode, ASCII, attr)) <+> prettyArrow ASCII <+> pretty "?"

-- >>> render (Formatted (SWEET, UNICODE, [BiVoid AtRho]))
-- ""
-- >>> render (Formatted (SWEET, UNICODE, [BiTau (AtLabel "x") ExGlobal, BiVoid AtPhi]))
-- "x \8614 \934,\n\966 \8614 \8709"
instance {-# OVERLAPPING #-} Pretty (Formatted [Binding]) where
  pretty (Formatted (SWEET, encoding, bds)) = vsep (punctuate comma (excludeVoidRho (\bd -> pretty (Formatted (SWEET, encoding, bd))) [] bds))
    where
      excludeVoidRho :: (Binding -> Doc ann) -> [Doc ann] -> [Binding] -> [Doc ann]
      excludeVoidRho func acc [bd] = case bd of
        BiVoid AtRho -> reverse acc
        _ -> reverse (func bd : acc)
      excludeVoidRho func acc (x : xs) = excludeVoidRho func (func x : acc) xs
      excludeVoidRho func acc [] = reverse acc
  pretty (Formatted (SALTY, encoding, bds)) = vsep (punctuate comma (map (\bd -> pretty (Formatted (SALTY, encoding, bd))) bds))

complexApplication :: Expression -> (Expression, [Binding], [Expression])
complexApplication (ExApplication (ExApplication expr tau) tau') =
  let (before, taus, exprs) = complexApplication (ExApplication expr tau)
      taus' = tau' : taus
   in if null exprs
        then (before, taus', [])
        else case tau' of
          BiTau (AtAlpha idx) expr' ->
            if idx == fromIntegral (length exprs)
              then (before, taus', expr' : exprs)
              else (before, taus', [])
          _ -> (before, taus', [])
complexApplication (ExApplication expr (BiTau (AtAlpha 0) expr')) = (expr, [BiTau (AtAlpha 0) expr'], [expr'])
complexApplication (ExApplication expr tau) = (expr, [tau], [])

-- >>> render (Formatted (SWEET, UNICODE, ExFormation [BiVoid AtRho]))
-- "\10214\10215"
instance Pretty (Formatted Expression) where
  pretty (Formatted (SWEET, UNICODE, ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang"))) = pretty "Φ̇"
  pretty (Formatted (SWEET, ASCII, ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang"))) = pretty "QQ"
  pretty (Formatted (SWEET, _, DataString bytes)) = pretty "\"" <> pretty (btsToStr bytes) <> pretty "\""
  pretty (Formatted (SWEET, _, DataNumber bytes)) = either pretty pretty (btsToNum bytes)
  pretty (Formatted (SWEET, encoding, DataObject other bytes)) = pretty (Formatted (SALTY, encoding, DataObject other bytes))
  pretty (Formatted (SWEET, UNICODE, ExFormation [BiVoid AtRho])) = pretty "⟦⟧"
  pretty (Formatted (SWEET, ASCII, ExFormation [BiVoid AtRho])) = pretty "[[]]"
  pretty (Formatted (mode, encoding, ExFormation [binding])) = case binding of
    BiTau _ _ -> vsep [prettyLsb encoding, indent 2 (pretty (Formatted (mode, encoding, binding))), prettyRsb encoding]
    _ -> prettyLsb encoding <+> pretty (Formatted (mode, encoding, binding)) <+> prettyRsb encoding
  pretty (Formatted (_, UNICODE, ExFormation [])) = pretty "⟦⟧"
  pretty (Formatted (_, ASCII, ExFormation [])) = pretty "[[]]"
  pretty (Formatted (mode, encoding, ExFormation bindings)) = vsep [prettyLsb encoding, indent 2 (pretty (Formatted (mode, encoding, bindings))), prettyRsb encoding]
  pretty (Formatted (_, UNICODE, ExThis)) = pretty "ξ"
  pretty (Formatted (_, ASCII, ExThis)) = pretty "$"
  pretty (Formatted (_, UNICODE, ExGlobal)) = pretty "Φ"
  pretty (Formatted (_, ASCII, ExGlobal)) = pretty "Q"
  pretty (Formatted (_, UNICODE, ExTermination)) = pretty "⊥"
  pretty (Formatted (_, ASCII, ExTermination)) = pretty "T"
  pretty (Formatted (_, _, ExMeta meta)) = prettyMeta meta
  pretty (Formatted (SWEET, encoding, ExApplication (ExApplication expr tau) tau')) =
    let (expr', taus, exprs) = complexApplication (ExApplication (ExApplication expr tau) tau')
        args =
          if null exprs
            then pretty (Formatted (SWEET, encoding, reverse taus))
            else vsep (punctuate comma (map (\exp -> pretty (Formatted (SWEET, encoding, exp))) (reverse exprs)))
     in pretty (Formatted (SWEET, encoding, expr')) <> vsep [lparen, indent 2 args, rparen]
  pretty (Formatted (SWEET, encoding, ExApplication expr tau)) =
    let arg = case tau of
          BiTau (AtAlpha 0) expr' -> pretty (Formatted (SWEET, encoding, expr'))
          _ -> pretty (Formatted (SWEET, encoding, tau))
     in pretty (Formatted (SWEET, encoding, expr)) <> vsep [lparen, indent 2 arg, rparen]
  pretty (Formatted (mode, encoding, ExApplication expr tau)) = pretty (Formatted (mode, encoding, expr)) <> vsep [lparen, indent 2 (pretty (Formatted (mode, encoding, tau))), rparen]
  pretty (Formatted (mode, encoding, ExDispatch expr attr)) = pretty (Formatted (mode, encoding, expr)) <> pretty "." <> pretty (Formatted (mode, encoding, attr))
  pretty (Formatted (mode, encoding, ExMetaTail expr meta)) = pretty (Formatted (mode, encoding, expr)) <+> pretty "*" <+> prettyMeta meta

instance Pretty (Formatted Program) where
  pretty (Formatted (SALTY, encoding, Program expr)) = pretty "Φ" <+> prettyArrow encoding <+> pretty (Formatted (SALTY, encoding, expr))
  pretty (Formatted (SWEET, encoding, Program expr)) = pretty "{" <> pretty (Formatted (SWEET, encoding, expr)) <> pretty "}"

instance Pretty Tail where
  pretty (TaApplication tau) = vsep [lparen, indent 2 (pretty (formatted tau)), rparen]
  pretty (TaDispatch attr) = pretty "." <> pretty (formatted attr)

instance Pretty MetaValue where
  pretty (MvAttribute attr) = pretty (formatted attr)
  pretty (MvBytes bytes) = pretty bytes
  pretty (MvBindings []) = pretty "[]"
  pretty (MvBindings bindings) = vsep [pretty "[", indent 2 (pretty (formatted bindings)), pretty "]"]
  pretty (MvFunction func) = pretty func
  pretty (MvExpression expr _) = pretty (formatted expr)
  pretty (MvTail tails) = vsep (punctuate comma (map pretty tails))

instance Pretty Subst where
  pretty (Subst mp) =
    vsep
      [ lparen,
        indent
          2
          ( vsep
              ( punctuate
                  comma
                  ( map
                      (\(key, value) -> prettyMeta key <+> pretty ">>" <+> pretty value)
                      (Map.toList mp)
                  )
              )
          ),
        rparen
      ]

instance {-# OVERLAPPING #-} Pretty (Formatted [Subst]) where
  pretty (Formatted (_, _, [])) = pretty "[]"
  pretty (Formatted (mode, _, substs)) = vsep [pretty "[", indent 2 (vsep (punctuate comma (map pretty substs))), pretty "]"]

render :: (Pretty a) => a -> String
render printable = renderString (layoutPretty defaultLayoutOptions (pretty printable))

prettyBinding :: Binding -> String
prettyBinding binding = render (formatted binding)

prettyExtraArg :: ExtraArgument -> String
prettyExtraArg = render

prettyBytes :: Bytes -> String
prettyBytes = render

prettyAttribute :: Attribute -> String
prettyAttribute attr = render (formatted attr)

prettySubst :: Subst -> String
prettySubst = render

prettySubsts :: [Subst] -> String
prettySubsts = render

prettySubsts' :: [Subst] -> PrintMode -> Encoding -> String
prettySubsts' substs mode encoding = render (Formatted (mode, encoding, substs))

prettyExpression :: Expression -> String
prettyExpression expr = render (formatted expr)

prettyExpression' :: Expression -> String
prettyExpression' expr = render (Formatted (SWEET, UNICODE, expr))

prettyProgram :: Program -> String
prettyProgram prog = render (formatted prog)

prettyProgram' :: Program -> PrintMode -> Encoding -> String
prettyProgram' prog mode encoding = render (Formatted (mode, encoding, prog))