packages feed

descript-lang-0.2.0.0: src/Descript/Misc/Build/Write/Print/PrintPatch.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Descript.Misc.Build.Write.Print.PrintPatch
  ( ppatch
  , ppatchThorough
  , ppatchRec
  , ppatchF
  ) where

import Descript.Misc.Build.Write.Print.PrintText
import Descript.Misc.Build.Write.Print.Printable
import Descript.Misc.Build.Write.Print.APrint
import Descript.Misc.Build.Write.Print.Patch
import Descript.Misc.Build.Read.Parse
import Descript.Misc.Ann
import Core.Data.Proxy
import Data.Maybe
import Data.List
import Data.String

-- | A "printed" patch - a patch generated using printing. Punctuation
-- (such as delimiters) are ignored, so only 'plex' patches are actually
-- applied.
newtype PrintPatch
  = PrintPatch{ runPrintPatch :: Patch }
  deriving (Eq, Monoid)

newtype PrintPatchThorough
  = PrintPatchThorough{ runPrintPatchThorough :: Maybe Patch }
  deriving (Eq, Monoid)

instance APrint PrintPatch where
  plex = plexPatchErr

  pimp = id

  pintercal sep = mconcat . intersperse sep

instance APrint PrintPatchThorough where
  plex _ = PrintPatchThorough Nothing

  pimp _ = PrintPatchThorough $ Just mempty

  pintercal sep = mconcat . intersperse sep

instance IsString PrintPatch where
  fromString _ = mempty

instance IsString PrintPatchThorough where
  fromString _ = PrintPatchThorough $ Just mempty

-- | "Pretty patch". Converts the node into a patch which can be applied
-- to the source text, so when it's parsed again, it yields the new node.
-- This patch will affect as little as possible - e.g. if the node
-- wasn't tainted (came right from text), the patch will do absolutely
-- nothing.
ppatch :: (Printable a) => a SrcAnn -> Patch
ppatch x
  | isFullyTainted ann || (isTainted' && needsFullReprint (proxyOf x))
  = mkCPatch (srcRange ann) $ pprint x
  | isTainted' = ppatchRec ppatch x
  | otherwise = mempty
  where isTainted' = isTainted x
        ann = getAnn x

-- | Pretty patch this node, recursively patching partially tainted
-- nodes. The resulting patch should be identical to 'ppatch' but more
-- complicated. Useful only for testing.
ppatchThorough :: (Printable a) => a SrcAnn -> Patch
ppatchThorough x
  | isFullyTainted ann = fullPatch
  | otherwise = fullPatch `fromMaybe` ppatchThoroughRec ppatchThorough x
  where fullPatch = mkCPatch (srcRange ann) $ pprint x
        ann = getAnn x

-- | Converts this node into a patch which can be applied to the source
-- by converting its children into patches.
ppatchRec :: (Printable a)
          => (forall b. (Printable b) => b an -> Patch)
          -> a an
          -> Patch
ppatchRec sub = runPrintPatch . aprintRec (PrintPatch . sub)

-- | Converts this node into a patch which can be applied to the source
-- by converting its children into patches. The resulting patch should
-- be identical to 'ppatch' but more complicated. Useful only for testing.
ppatchThoroughRec :: (Printable a)
                  => (forall b. (Printable b) => b an -> Patch)
                  -> a an
                  -> Maybe Patch
ppatchThoroughRec sub
  = runPrintPatchThorough
  . aprintRec (PrintPatchThorough . Just . sub)

-- | Convert the nodes into a patch which can be applied to the source
-- text, so when it's parsed again, it yields the new node. Used for
-- lexemes.
ppatchF :: (Foldable w, Printable a) => w (a SrcAnn) -> Patch
ppatchF = foldMap ppatch

-- | An error generated when 'plex' is used to create a patch.
plexPatchErr :: a
plexPatchErr
  = error $ concat
  [ "'plex' can't be used when generating patches.\n"
  , "This was raised because an partially tainted node (not fully "
  , "tainted but with tainted children) used 'plex'. A node should "
  , "never use 'plex' unless it's a leaf (has no children), and then "
  , "it should never be partially tainted, either fully tainted or "
  , "fully untainted."
  ]